Unified activity timeline in Conversation: joins, questions, resolutions, contract, agreements
deploy / deploy (push) Canceled after 0s
deploy / deploy (push) Canceled after 0s
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import type { MessageView, ObserverView, ParticipantView, QuestionView, RoomView } from './types.js';
|
||||
import type { ContractView, MessageView, ObserverView, ParticipantView, QuestionView, RoomView } from './types.js';
|
||||
import { buildTimeline } from './timeline.js';
|
||||
|
||||
/** Read-only Markdown state for the human observer of a room. */
|
||||
export function renderObserverMarkdown(o: ObserverView, baseUrl: string): string {
|
||||
@@ -35,13 +36,22 @@ export function renderObserverMarkdown(o: ObserverView, baseUrl: string): string
|
||||
}
|
||||
parts.push('');
|
||||
}
|
||||
parts.push('## Conversation');
|
||||
if (o.conversation.length === 0) parts.push('(no messages yet)');
|
||||
parts.push('## Conversation (full activity timeline)');
|
||||
const timeline = buildTimeline({
|
||||
participants: o.participants,
|
||||
conversation: o.conversation,
|
||||
openQuestions: o.open_questions,
|
||||
resolvedQuestions: o.resolved_questions,
|
||||
contract: o.current_contract,
|
||||
});
|
||||
if (timeline.length === 0) parts.push('(no activity yet)');
|
||||
else
|
||||
for (const m of o.conversation) {
|
||||
parts.push(`**${m.role}** (${m.created_at}):`);
|
||||
parts.push('');
|
||||
parts.push(m.content);
|
||||
for (const e of timeline) {
|
||||
parts.push(`**${e.role}** — ${e.action} (${e.at})`);
|
||||
if (e.body) {
|
||||
parts.push('');
|
||||
parts.push(e.body);
|
||||
}
|
||||
parts.push('');
|
||||
}
|
||||
parts.push(`---\n_Observer link for room ${o.room.id}. Final artifact: ${baseUrl}/api/rooms/${o.room.id}/final.md (requires a participant token)._`);
|
||||
@@ -62,6 +72,8 @@ export interface MarkdownInput {
|
||||
resolvedQuestions: QuestionView[];
|
||||
contractMarkdown: string;
|
||||
contractVersion: number;
|
||||
/** Full contract view (with agreements) for the activity timeline; falls back to contractMarkdown/Version. */
|
||||
contract?: ContractView | null;
|
||||
/** Short secrets policy (links to the canonical /security.md). */
|
||||
secretsPolicyShort: string;
|
||||
}
|
||||
@@ -119,14 +131,27 @@ export function renderRoomMarkdown(i: MarkdownInput): string {
|
||||
parts.push(i.contractMarkdown.trim() ? i.contractMarkdown : `(not drafted yet, version ${i.contractVersion})`);
|
||||
parts.push('');
|
||||
|
||||
parts.push('## Conversation');
|
||||
if (i.conversation.length === 0) {
|
||||
parts.push('(no messages yet)');
|
||||
parts.push('## Conversation (full activity timeline)');
|
||||
const timeline = buildTimeline({
|
||||
participants: i.participants,
|
||||
conversation: i.conversation,
|
||||
openQuestions: i.openQuestions,
|
||||
resolvedQuestions: i.resolvedQuestions,
|
||||
contract:
|
||||
i.contract ??
|
||||
(i.contractVersion > 0
|
||||
? { version: i.contractVersion, markdown: i.contractMarkdown, updated_at: null, agreements: [] }
|
||||
: null),
|
||||
});
|
||||
if (timeline.length === 0) {
|
||||
parts.push('(no activity yet)');
|
||||
} else {
|
||||
for (const m of i.conversation) {
|
||||
parts.push(`**${m.role}** (${m.created_at}, id: ${m.id}):`);
|
||||
parts.push('');
|
||||
parts.push(m.content);
|
||||
for (const e of timeline) {
|
||||
parts.push(`**${e.role}** — ${e.action} (${e.at})`);
|
||||
if (e.body) {
|
||||
parts.push('');
|
||||
parts.push(e.body);
|
||||
}
|
||||
parts.push('');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user