Unified activity timeline in Conversation: joins, questions, resolutions, contract, agreements
deploy / deploy (push) Canceled after 0s

This commit is contained in:
2026-09-06 22:26:48 +03:00
parent fde1ae152f
commit 2e55a383ea
8 changed files with 198 additions and 36 deletions
+39 -14
View File
@@ -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('');
}
}