@xfey/tutti 0.1.111 → 0.1.113

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/dist/approvals/decision-command.d.ts +2 -2
  2. package/dist/chat-assistant/index.d.ts +1 -1
  3. package/dist/chat-assistant/index.js +19 -6
  4. package/dist/collaboration-ingestion/clarification.d.ts +25 -0
  5. package/dist/collaboration-ingestion/clarification.js +63 -0
  6. package/dist/collaboration-ingestion/external-source.d.ts +2 -0
  7. package/dist/collaboration-ingestion/external-source.js +35 -0
  8. package/dist/collaboration-ingestion/index.d.ts +2 -0
  9. package/dist/collaboration-ingestion/index.js +2 -0
  10. package/dist/collaboration-ingestion/platform-clarification.d.ts +10 -0
  11. package/dist/collaboration-ingestion/platform-clarification.js +42 -0
  12. package/dist/collaboration-state/clarification-messages.js +3 -2
  13. package/dist/collaboration-state/clarification-records.d.ts +1 -1
  14. package/dist/collaboration-state/clarification-records.js +7 -5
  15. package/dist/collaboration-state/clarification-rounds.js +5 -5
  16. package/dist/collaboration-state/clarification-snapshot.d.ts +228 -0
  17. package/dist/collaboration-state/clarification-snapshot.js +29 -0
  18. package/dist/collaboration-state/clarification-successors.js +6 -6
  19. package/dist/collaboration-state/index.d.ts +1 -0
  20. package/dist/collaboration-state/index.js +1 -0
  21. package/dist/collaboration-state/run-recording.js +1 -1
  22. package/dist/collaboration-state/task-compile-context.d.ts +1 -1
  23. package/dist/collaboration-state/types.d.ts +1 -0
  24. package/dist/control-plane/clarification-commands.d.ts +3 -2
  25. package/dist/control-plane/clarification-commands.js +13 -4
  26. package/dist/control-plane/follow-up-context.js +8 -14
  27. package/dist/control-plane/index.d.ts +3 -2
  28. package/dist/control-plane/task-compile-clarification-context.js +7 -2
  29. package/dist/control-plane/types.d.ts +1 -0
  30. package/dist/control-plane/workflows/openai.js +5 -4
  31. package/dist/platform-outbound/repository.js +83 -4
  32. package/dist/server-shell/cli/host-lifecycle.js +1 -0
  33. package/dist/server-shell/cli/host-server-runtime.d.ts +2 -1
  34. package/dist/server-shell/cli/host-server-runtime.js +4 -2
  35. package/dist/server-shell/cli/relay-registration.js +1 -0
  36. package/dist/server-shell/http/host-tunnel.d.ts +2 -1
  37. package/dist/server-shell/http/host-tunnel.js +4 -1
  38. package/dist/server-shell/http/integration-clarification-dispatch.d.ts +6 -0
  39. package/dist/server-shell/http/integration-clarification-dispatch.js +43 -0
  40. package/dist/server-shell/http/routes/project-api/clarification-routes.js +16 -42
  41. package/dist/server-shell/http/routes/project-api/openapi-collaboration-routes.d.ts +2 -0
  42. package/dist/server-shell/http/routes/project-api/openapi-workspace-routes.d.ts +1 -0
  43. package/dist/server-shell/http/routes/project-api/openapi.d.ts +3 -0
  44. package/dist/server-shell/http/routes/project-api/schemas.d.ts +1 -0
  45. package/migrations/0022_clarification_projection_cursors.sql +7 -0
  46. package/node_modules/@tutti/relay-client/dist/host-control.d.ts +1 -0
  47. package/node_modules/@tutti/relay-client/dist/host-control.js +3 -0
  48. package/node_modules/@tutti/shared/dist/schemas/api/clarifications.d.ts +13 -1
  49. package/node_modules/@tutti/shared/dist/schemas/api/clarifications.js +9 -2
  50. package/node_modules/@tutti/shared/dist/schemas/internal/index.d.ts +2 -0
  51. package/node_modules/@tutti/shared/dist/schemas/internal/index.js +2 -0
  52. package/node_modules/@tutti/shared/dist/schemas/internal/platform-clarification-content.d.ts +15 -0
  53. package/node_modules/@tutti/shared/dist/schemas/internal/platform-clarification-content.js +24 -0
  54. package/node_modules/@tutti/shared/dist/schemas/internal/platform-clarification.d.ts +354 -0
  55. package/node_modules/@tutti/shared/dist/schemas/internal/platform-clarification.js +94 -0
  56. package/node_modules/@tutti/shared/dist/schemas/internal/platform-integration.d.ts +89 -3
  57. package/node_modules/@tutti/shared/dist/schemas/internal/platform-integration.js +15 -3
  58. package/node_modules/@tutti/shared/dist/utils/clarification-participants.d.ts +4 -0
  59. package/node_modules/@tutti/shared/dist/utils/clarification-participants.js +11 -0
  60. package/node_modules/@tutti/shared/dist/utils/index.d.ts +1 -0
  61. package/node_modules/@tutti/shared/dist/utils/index.js +1 -0
  62. package/package.json +1 -1
  63. package/prompts/procedures/follow-up-check.md +4 -4
  64. package/web/assets/{homepage-motion-scene-TmkKf8wQ.js → homepage-motion-scene-BwdH6Iss.js} +1 -1
  65. package/web/assets/index-B29RuNsd.js +69 -0
  66. package/web/index.html +1 -1
  67. package/web/assets/index-B1oL1p9_.js +0 -69
@@ -22,6 +22,7 @@ export declare function executeApprovalDecisionCommand(input: {
22
22
  approval?: {
23
23
  state: "none";
24
24
  } | {
25
+ state: "pending";
25
26
  request: {
26
27
  task_id?: string;
27
28
  activity_ref?: string;
@@ -64,8 +65,8 @@ export declare function executeApprovalDecisionCommand(input: {
64
65
  request_kind: "command_execution" | "file_change" | "permissions" | "legacy_exec_command" | "legacy_apply_patch";
65
66
  requested_at: string;
66
67
  };
67
- state: "pending";
68
68
  } | {
69
+ state: "resolved";
69
70
  request: {
70
71
  task_id?: string;
71
72
  activity_ref?: string;
@@ -108,7 +109,6 @@ export declare function executeApprovalDecisionCommand(input: {
108
109
  request_kind: "command_execution" | "file_change" | "permissions" | "legacy_exec_command" | "legacy_apply_patch";
109
110
  requested_at: string;
110
111
  };
111
- state: "resolved";
112
112
  resolution: {
113
113
  decided_by?: {
114
114
  display_name: string;
@@ -49,7 +49,7 @@ export type ReadOnlyChatAssistantOptions = {
49
49
  model: ReadOnlyChatAssistantModel;
50
50
  promptsRoot?: string;
51
51
  onScratchpadSourceChanged?: (message: MessageProjection) => void;
52
- onMainChatAgentMessageCreated?: (input: {
52
+ onAgentMessageCreated?: (input: {
53
53
  tx: SqliteDatabase;
54
54
  triggerMessage: MessageProjection;
55
55
  responseMessage: MessageProjection;
@@ -262,7 +262,7 @@ export class ReadOnlyChatAssistant {
262
262
  ...(refs === undefined ? {} : { refs }),
263
263
  ...(this.options.now === undefined ? {} : { now: this.options.now }),
264
264
  });
265
- const callback = this.options.onMainChatAgentMessageCreated?.({
265
+ const callback = this.options.onAgentMessageCreated?.({
266
266
  tx,
267
267
  triggerMessage,
268
268
  responseMessage,
@@ -286,12 +286,24 @@ export class ReadOnlyChatAssistant {
286
286
  }
287
287
  appendClarificationRoundAssistantMessage(input) {
288
288
  const round = readClarificationRoundProjection(this.options.store.db, input.roundId);
289
- const message = createClarificationRoundAgentMessage(this.options.store.db, {
290
- round_id: input.roundId,
291
- body: input.body,
292
- ...(input.refs === undefined ? {} : { refs: input.refs }),
293
- ...(this.options.now === undefined ? {} : { now: this.options.now }),
289
+ let afterCommit;
290
+ const message = withHostStoreTransaction(this.options.store.db, (tx) => {
291
+ const responseMessage = createClarificationRoundAgentMessage(tx, {
292
+ round_id: input.roundId,
293
+ body: input.body,
294
+ ...(input.refs === undefined ? {} : { refs: input.refs }),
295
+ ...(this.options.now === undefined ? {} : { now: this.options.now }),
296
+ });
297
+ const callback = this.options.onAgentMessageCreated?.({
298
+ tx,
299
+ triggerMessage: input.triggerMessage,
300
+ responseMessage,
301
+ });
302
+ if (typeof callback === "function")
303
+ afterCommit = callback;
304
+ return responseMessage;
294
305
  });
306
+ afterCommit?.();
295
307
  const invalidates = [
296
308
  {
297
309
  kind: "clarification",
@@ -314,6 +326,7 @@ export class ReadOnlyChatAssistant {
314
326
  appendAssistantMessageForTrigger(triggerMessage, body, refs) {
315
327
  if (triggerMessage.scope.kind === "clarification_round") {
316
328
  this.appendClarificationRoundAssistantMessage({
329
+ triggerMessage,
317
330
  roundId: triggerMessage.scope.clarification_round_ref,
318
331
  body,
319
332
  ...(refs === undefined ? {} : { refs }),
@@ -0,0 +1,25 @@
1
+ import type { ClarificationRoundRef, IdempotencyKey } from "@tutti/shared/ids";
2
+ import type { MessageProjection, SendClarificationRoundMessagePayload, SendClarificationRoundMessageResponse, SubmitClarificationRoundPayload, SubmitClarificationRoundResponse } from "@tutti/shared/schemas/api";
3
+ import type { Phase5ControlPlane } from "../control-plane/index.js";
4
+ import type { HostProjectStore, SqliteDatabase } from "../store/index.js";
5
+ import type { ChatAssistantEnqueuer, TrustedCollaborationAuthor } from "./types.js";
6
+ type RoundCommandOptions = {
7
+ store: HostProjectStore;
8
+ controlPlane: Pick<Phase5ControlPlane, "sendClarificationRoundMessage" | "submitClarificationRound">;
9
+ roundId: ClarificationRoundRef;
10
+ commandId: IdempotencyKey;
11
+ now?: () => Date;
12
+ };
13
+ /** Browser and trusted platform input share command identity, mutation and write-once effects. */
14
+ export declare function ingestClarificationMessage(options: RoundCommandOptions & {
15
+ payload: SendClarificationRoundMessagePayload;
16
+ author: TrustedCollaborationAuthor;
17
+ chatAssistant?: ChatAssistantEnqueuer;
18
+ idempotencyContext?: unknown;
19
+ afterCreate?: (tx: SqliteDatabase, message: MessageProjection) => void;
20
+ }): SendClarificationRoundMessageResponse;
21
+ export declare function submitClarificationAnswers(options: RoundCommandOptions & {
22
+ payload: SubmitClarificationRoundPayload;
23
+ }): SubmitClarificationRoundResponse;
24
+ export {};
25
+ //# sourceMappingURL=clarification.d.ts.map
@@ -0,0 +1,63 @@
1
+ import { readClarificationRoundProjection, readMessageProjectionById, } from "../collaboration-state/index.js";
2
+ import { clarificationInvalidates } from "../control-plane/invalidations.js";
3
+ import { executeIdempotentCommand } from "../server-shell/command-idempotency/index.js";
4
+ /** Browser and trusted platform input share command identity, mutation and write-once effects. */
5
+ export function ingestClarificationMessage(options) {
6
+ const execution = executeIdempotentCommand({
7
+ db: options.store.db,
8
+ command_id: options.commandId,
9
+ command_name: "clarification.message.create",
10
+ target_ref: options.roundId,
11
+ payload: {
12
+ body: options.payload.body,
13
+ author_ref: options.author.account_ref,
14
+ ...(options.author.kind === "development_operator"
15
+ ? { author_kind: "development_operator" }
16
+ : {}),
17
+ ...(options.idempotencyContext === undefined
18
+ ? {}
19
+ : { ingestion_context: options.idempotencyContext }),
20
+ },
21
+ ...(options.now === undefined ? {} : { now: options.now }),
22
+ execute: (tx) => {
23
+ const result = options.controlPlane.sendClarificationRoundMessage(options.roundId, options.payload, options.author);
24
+ if (result.result?.message !== undefined)
25
+ options.afterCreate?.(tx, result.result.message);
26
+ return result;
27
+ },
28
+ });
29
+ const replayedMessage = execution.disposition.kind === "created" && execution.disposition.message_id !== undefined
30
+ ? readMessageProjectionById(options.store.db, execution.disposition.message_id)
31
+ : null;
32
+ const result = execution.result ?? (replayedMessage === null ? undefined : { message: replayedMessage });
33
+ if (!execution.replayed && result?.message !== undefined)
34
+ options.chatAssistant?.enqueue({ message: result.message });
35
+ return {
36
+ command_id: execution.command_id,
37
+ disposition: execution.disposition,
38
+ invalidates: clarificationInvalidates(options.roundId),
39
+ ...(result === undefined ? {} : { result }),
40
+ };
41
+ }
42
+ export function submitClarificationAnswers(options) {
43
+ const execution = executeIdempotentCommand({
44
+ db: options.store.db,
45
+ command_id: options.commandId,
46
+ command_name: "clarification.round.submit",
47
+ target_ref: options.roundId,
48
+ payload: options.payload,
49
+ ...(options.now === undefined ? {} : { now: options.now }),
50
+ execute: () => options.controlPlane.submitClarificationRound(options.roundId, options.payload, options.commandId),
51
+ });
52
+ const round = execution.disposition.kind === "accepted" || execution.disposition.kind === "already_submitted"
53
+ ? readClarificationRoundProjection(options.store.db, options.roundId)
54
+ : null;
55
+ const result = execution.result ?? (round === null ? undefined : { round });
56
+ return {
57
+ command_id: execution.command_id,
58
+ disposition: execution.disposition,
59
+ invalidates: clarificationInvalidates(options.roundId),
60
+ ...(result === undefined ? {} : { result }),
61
+ };
62
+ }
63
+ //# sourceMappingURL=clarification.js.map
@@ -1,12 +1,14 @@
1
1
  import type { IntegrationDeliveryDisposition, TrustedConversationSourceIngestionCommand } from "@tutti/shared/schemas/internal";
2
2
  import type { HostProjectStore } from "../store/index.js";
3
3
  import type { WorkspaceEventBus } from "../server-shell/http/workspace-events.js";
4
+ import type { Phase5ControlPlane } from "../control-plane/index.js";
4
5
  import type { ChatAssistantEnqueuer, ScratchpadSourceNotifier } from "./types.js";
5
6
  export declare const TRUSTED_INTEGRATION_INGESTION_PATH = "/internal/integration/v1/conversation-sources";
6
7
  export type IngestTrustedConversationSourceOptions = {
7
8
  store: HostProjectStore;
8
9
  events: WorkspaceEventBus;
9
10
  command: TrustedConversationSourceIngestionCommand;
11
+ controlPlane?: Pick<Phase5ControlPlane, "sendClarificationRoundMessage" | "submitClarificationRound">;
10
12
  scratchpadSource?: ScratchpadSourceNotifier;
11
13
  chatAssistant?: ChatAssistantEnqueuer;
12
14
  now?: () => Date;
@@ -1,6 +1,7 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { INTEGRATION_CONVERSATION_SOURCE_TUNNEL_PATH, isTrustedConversationSourceIngestionCommand, } from "@tutti/shared/schemas/internal";
3
3
  import { ingestMainChatMessage } from "./main-chat.js";
4
+ import { ingestClarificationMessage } from "./clarification.js";
4
5
  export const TRUSTED_INTEGRATION_INGESTION_PATH = INTEGRATION_CONVERSATION_SOURCE_TUNNEL_PATH;
5
6
  function sha256Text(value) {
6
7
  return `sha256:${createHash("sha256").update(value, "utf8").digest("hex")}`;
@@ -77,6 +78,40 @@ export function ingestTrustedConversationSource(options) {
77
78
  if (source.content.text.length > 8_000) {
78
79
  return { kind: "unsupported", reason_code: "message_too_long" };
79
80
  }
81
+ if (options.command.clarification_round_ref !== undefined) {
82
+ if (options.controlPlane === undefined)
83
+ return {
84
+ kind: "host_incompatible",
85
+ retryable: true,
86
+ reason_code: "integration_clarification_unsupported",
87
+ };
88
+ // Feishu replies may automatically mention Tutti. Directed round input is an
89
+ // answer, not an implicit Q&A request; retain its text but omit the assistant.
90
+ // Browser round composers and ordinary main-chat mentions keep their own path.
91
+ const response = ingestClarificationMessage({
92
+ store: options.store,
93
+ controlPlane: options.controlPlane,
94
+ commandId: options.command.command_id,
95
+ roundId: options.command.clarification_round_ref,
96
+ payload: { body: source.content.text },
97
+ author: {
98
+ ...(options.command.actor.kind === "bot" ? { kind: "development_operator" } : {}),
99
+ account_ref: options.command.actor.account_ref,
100
+ display_name: options.command.actor.display_name,
101
+ },
102
+ idempotencyContext: {
103
+ binding_ref: options.command.binding_ref,
104
+ external_message_ref: source.locator.message_ref,
105
+ },
106
+ afterCreate: (tx, message) => insertExternalSourceEvidence(tx, options.command, message),
107
+ ...(options.now === undefined ? {} : { now: options.now }),
108
+ });
109
+ if (response.disposition.kind === "created" && response.result?.message !== undefined)
110
+ return { kind: "created", message_id: response.result.message.id };
111
+ if (response.disposition.kind === "interrupted")
112
+ return { kind: "host_unavailable", retryable: true, reason_code: "command_interrupted" };
113
+ return { kind: "invalid", reason_code: `clarification_${response.disposition.kind}` };
114
+ }
80
115
  const response = ingestMainChatMessage({
81
116
  store: options.store,
82
117
  events: options.events,
@@ -5,4 +5,6 @@ export { ingestTrustedManagedReference, readManagedReferenceSourcePaths, type In
5
5
  export { ingestReferenceFile, REFERENCE_FILE_INGESTION_COMMAND_NAME, REFERENCE_FILE_INGESTION_TARGET_REF, toReferenceFileProjection, type IngestReferenceFileOptions, } from "./reference-files.js";
6
6
  export { ingestTrustedReferenceAttachment, TRUSTED_INTEGRATION_REFERENCE_ATTACHMENT_PATH, type IngestTrustedReferenceAttachmentOptions, type ResolvedReferenceAttachmentContent, } from "./reference-attachments.js";
7
7
  export type { ChatAssistantEnqueuer, ReferenceSummaryStarter, ScratchpadSourceNotifier, TrustedCollaborationAuthor, } from "./types.js";
8
+ export { ingestClarificationMessage, submitClarificationAnswers } from "./clarification.js";
9
+ export { executeTrustedClarification } from "./platform-clarification.js";
8
10
  //# sourceMappingURL=index.d.ts.map
@@ -4,4 +4,6 @@ export { ingestManagedReferenceSnapshot, publishManagedReferenceSnapshotChanged,
4
4
  export { ingestTrustedManagedReference, readManagedReferenceSourcePaths, } from "./managed-reference-sources.js";
5
5
  export { ingestReferenceFile, REFERENCE_FILE_INGESTION_COMMAND_NAME, REFERENCE_FILE_INGESTION_TARGET_REF, toReferenceFileProjection, } from "./reference-files.js";
6
6
  export { ingestTrustedReferenceAttachment, TRUSTED_INTEGRATION_REFERENCE_ATTACHMENT_PATH, } from "./reference-attachments.js";
7
+ export { ingestClarificationMessage, submitClarificationAnswers } from "./clarification.js";
8
+ export { executeTrustedClarification } from "./platform-clarification.js";
7
9
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,10 @@
1
+ import { type TrustedClarificationCommand, type IntegrationClarificationResponse } from "@tutti/shared/schemas/internal";
2
+ import type { Phase5ControlPlane } from "../control-plane/index.js";
3
+ import type { HostProjectStore } from "../store/index.js";
4
+ export declare function executeTrustedClarification(options: {
5
+ store: HostProjectStore;
6
+ controlPlane: Phase5ControlPlane;
7
+ command: TrustedClarificationCommand;
8
+ now?: () => Date;
9
+ }): IntegrationClarificationResponse;
10
+ //# sourceMappingURL=platform-clarification.d.ts.map
@@ -0,0 +1,42 @@
1
+ import { isTrustedClarificationCommand, } from "@tutti/shared/schemas/internal";
2
+ import { readActiveClarificationProjection, readClarificationRoundProjection, readClarificationThreadResponse, } from "../collaboration-state/index.js";
3
+ import { submitClarificationAnswers } from "./clarification.js";
4
+ export function executeTrustedClarification(options) {
5
+ const { command, store } = options;
6
+ if (!isTrustedClarificationCommand(command))
7
+ return {
8
+ protocol_version: 1,
9
+ disposition: { kind: "invalid", reason_code: "trusted_clarification_invalid" },
10
+ };
11
+ if (command.operation.kind === "submit")
12
+ return {
13
+ protocol_version: 1,
14
+ disposition: {
15
+ kind: "submitted",
16
+ response: submitClarificationAnswers({
17
+ store,
18
+ controlPlane: options.controlPlane,
19
+ commandId: command.command_id,
20
+ roundId: command.operation.round_id,
21
+ payload: { expected_answer_revision: command.operation.expected_answer_revision },
22
+ ...(options.now === undefined ? {} : { now: options.now }),
23
+ }),
24
+ },
25
+ };
26
+ const round = command.operation.round_id === undefined
27
+ ? null
28
+ : readClarificationRoundProjection(store.db, command.operation.round_id);
29
+ const thread = round === null
30
+ ? null
31
+ : readClarificationThreadResponse(store.db, round.thread_id)
32
+ ?.thread;
33
+ return {
34
+ protocol_version: 1,
35
+ disposition: {
36
+ kind: "queried",
37
+ active: readActiveClarificationProjection(store.db),
38
+ ...(round === null || thread == null ? {} : { selected: { round, thread } }),
39
+ },
40
+ };
41
+ }
42
+ //# sourceMappingURL=platform-clarification.js.map
@@ -1,5 +1,5 @@
1
- import { createMessageId, } from "@tutti/shared/ids";
2
- import { clampLimit, decodeCursor, messageCursor, nowIso, stringifyJson, } from "./serialization.js";
1
+ import { createMessageId } from "@tutti/shared/ids";
2
+ import { clampLimit, decodeCursor, messageCursor, nowIso, stringifyJson } from "./serialization.js";
3
3
  import { readClarificationRoundRow } from "./clarification-records.js";
4
4
  import { mapMessageRow } from "./messages.js";
5
5
  import { recordReadNotificationEvent } from "./read-state.js";
@@ -7,6 +7,7 @@ export function createClarificationRoundMessage(db, input) {
7
7
  const createdAt = nowIso(input.now);
8
8
  const id = createMessageId();
9
9
  const refs = {
10
+ ...(input.refs ?? {}),
10
11
  clarification_round_ref: input.round_id,
11
12
  };
12
13
  db.prepare(`
@@ -4,7 +4,7 @@ import type { SqliteDatabase } from "../store/index.js";
4
4
  import type { SealedClarificationRoundWithoutSuccessor } from "./types.js";
5
5
  import type { ClarificationRoundRow, ClarificationThreadRow } from "./storage-types.js";
6
6
  export declare function mapClarificationThreadRow(row: ClarificationThreadRow): ClarificationThreadProjection;
7
- export declare function mapClarificationRoundRow(row: ClarificationRoundRow): ClarificationRoundProjection;
7
+ export declare function mapClarificationRoundRow(row: ClarificationRoundRow, db: SqliteDatabase): ClarificationRoundProjection;
8
8
  export declare function readClarificationThreadRow(db: SqliteDatabase, threadId: ClarificationThreadRef): ClarificationThreadRow | null;
9
9
  export declare function readClarificationRoundRow(db: SqliteDatabase, roundId: ClarificationRoundRef): ClarificationRoundRow | null;
10
10
  export declare function readActiveClarificationProjection(db: SqliteDatabase): ActiveClarificationProjection;
@@ -1,4 +1,5 @@
1
1
  import { parseJson } from "./serialization.js";
2
+ import { readClarificationAnswerRevision } from "./clarification-snapshot.js";
2
3
  export function mapClarificationThreadRow(row) {
3
4
  const thread = {
4
5
  id: row.id,
@@ -26,7 +27,7 @@ export function mapClarificationThreadRow(row) {
26
27
  }
27
28
  return thread;
28
29
  }
29
- export function mapClarificationRoundRow(row) {
30
+ export function mapClarificationRoundRow(row, db) {
30
31
  const round = {
31
32
  id: row.id,
32
33
  thread_id: row.clarification_thread_id,
@@ -38,6 +39,7 @@ export function mapClarificationRoundRow(row) {
38
39
  }),
39
40
  card_message_id: row.card_message_id,
40
41
  opening_message_id: row.opening_message_id,
42
+ answer_revision: readClarificationAnswerRevision(db, row.id),
41
43
  successor_status: row.successor_status,
42
44
  capabilities: {
43
45
  send_message: row.status === "writable",
@@ -99,7 +101,7 @@ export function readActiveClarificationProjection(db) {
99
101
  return {
100
102
  state: "active",
101
103
  thread: mapClarificationThreadRow(row),
102
- round: mapClarificationRoundRow(round),
104
+ round: mapClarificationRoundRow(round, db),
103
105
  };
104
106
  }
105
107
  export function readSealedClarificationRoundsWithoutSuccessor(db) {
@@ -120,7 +122,7 @@ export function readSealedClarificationRoundsWithoutSuccessor(db) {
120
122
  return [
121
123
  {
122
124
  thread: mapClarificationThreadRow(thread),
123
- round: mapClarificationRoundRow(roundRow),
125
+ round: mapClarificationRoundRow(roundRow, db),
124
126
  },
125
127
  ];
126
128
  });
@@ -142,7 +144,7 @@ export function deleteStaleClarificationSubmitReplayHints(db) {
142
144
  }
143
145
  export function readClarificationRoundProjection(db, roundId) {
144
146
  const row = readClarificationRoundRow(db, roundId);
145
- return row === null ? null : mapClarificationRoundRow(row);
147
+ return row === null ? null : mapClarificationRoundRow(row, db);
146
148
  }
147
149
  export function readClarificationRoundsForThread(db, threadId, options = {}) {
148
150
  const rows = options.throughRoundIndex === undefined
@@ -163,7 +165,7 @@ export function readClarificationRoundsForThread(db, threadId, options = {}) {
163
165
  ORDER BY round_index ASC
164
166
  `)
165
167
  .all(threadId, options.throughRoundIndex);
166
- return rows.map((row) => mapClarificationRoundRow(row));
168
+ return rows.map((row) => mapClarificationRoundRow(row, db));
167
169
  }
168
170
  export function readTaskCompileClarificationRoundsByWorkflowRef(db, workflowRef) {
169
171
  const threads = db
@@ -114,7 +114,7 @@ export function openTaskCompileClarificationRound(db, input) {
114
114
  }
115
115
  return {
116
116
  thread: mapClarificationThreadRow(thread),
117
- round: mapClarificationRoundRow(round),
117
+ round: mapClarificationRoundRow(round, db),
118
118
  card_message: mapMessageRow(cardMessage, db),
119
119
  opening_message: mapMessageRow(openingMessage),
120
120
  };
@@ -219,8 +219,8 @@ export function openNextTaskCompileClarificationRound(db, input) {
219
219
  }
220
220
  return {
221
221
  thread: mapClarificationThreadRow(thread),
222
- previous_round: mapClarificationRoundRow(previousRound),
223
- round: mapClarificationRoundRow(round),
222
+ previous_round: mapClarificationRoundRow(previousRound, db),
223
+ round: mapClarificationRoundRow(round, db),
224
224
  card_message: mapMessageRow(cardMessage, db),
225
225
  opening_message: mapMessageRow(openingMessage),
226
226
  };
@@ -337,8 +337,8 @@ export function openNextTaskBoundClarificationRound(db, input) {
337
337
  }
338
338
  return {
339
339
  thread: mapClarificationThreadRow(thread),
340
- previous_round: mapClarificationRoundRow(previousRound),
341
- round: mapClarificationRoundRow(round),
340
+ previous_round: mapClarificationRoundRow(previousRound, db),
341
+ round: mapClarificationRoundRow(round, db),
342
342
  task,
343
343
  card_message: mapMessageRow(cardMessage, db),
344
344
  opening_message: mapMessageRow(openingMessage),
@@ -0,0 +1,228 @@
1
+ import type { ClarificationRoundRef } from "@tutti/shared/ids";
2
+ import type { SqliteDatabase } from "../store/index.js";
3
+ /** Round messages are append-only; the eligible answer count is a stable CAS revision. */
4
+ export declare function readClarificationAnswerRevision(db: SqliteDatabase, roundId: ClarificationRoundRef): number;
5
+ /** Read the immutable submitted refs, not a first page or messages appended after sealing. */
6
+ export declare function readSubmittedClarificationMessages(db: SqliteDatabase, roundId: ClarificationRoundRef): {
7
+ clarification_card?: {
8
+ title?: string;
9
+ status: "writable" | "sealed";
10
+ round_id: string;
11
+ thread_id: string;
12
+ summary: string;
13
+ is_current_round: boolean;
14
+ };
15
+ refs?: {
16
+ task_id?: string;
17
+ activity_ref?: string;
18
+ artifact_feedback?: {
19
+ summary?: string;
20
+ kind: "artifact_feedback";
21
+ version: 1;
22
+ preview_generation: string;
23
+ annotations: {
24
+ document_url: string;
25
+ comment: string;
26
+ target: {
27
+ fallback_context?: {
28
+ snapshot: {
29
+ role?: string;
30
+ accessible_name?: string;
31
+ text_excerpt?: string;
32
+ state?: {
33
+ expanded?: boolean;
34
+ selected?: boolean;
35
+ checked?: boolean;
36
+ pressed?: boolean;
37
+ disabled?: boolean;
38
+ };
39
+ tag_name: string;
40
+ element_kind: "content" | "control" | "container";
41
+ context: {
42
+ role?: string;
43
+ accessible_name?: string;
44
+ tag_name: string;
45
+ }[];
46
+ };
47
+ anchor: {
48
+ explicit_anchor?: string;
49
+ stable_attributes?: {
50
+ id?: string;
51
+ test_id?: string;
52
+ };
53
+ version: 1;
54
+ semantic_path: {
55
+ role?: string;
56
+ accessible_name?: string;
57
+ }[];
58
+ selector_path: {
59
+ root: "document" | "open_shadow_root";
60
+ selector: string;
61
+ }[];
62
+ };
63
+ };
64
+ snapshot: {
65
+ role?: string;
66
+ accessible_name?: string;
67
+ text_excerpt?: string;
68
+ state?: {
69
+ expanded?: boolean;
70
+ selected?: boolean;
71
+ checked?: boolean;
72
+ pressed?: boolean;
73
+ disabled?: boolean;
74
+ };
75
+ tag_name: string;
76
+ element_kind: "content" | "control" | "container";
77
+ context: {
78
+ role?: string;
79
+ accessible_name?: string;
80
+ tag_name: string;
81
+ }[];
82
+ };
83
+ anchor: {
84
+ explicit_anchor?: string;
85
+ stable_attributes?: {
86
+ id?: string;
87
+ test_id?: string;
88
+ };
89
+ version: 1;
90
+ semantic_path: {
91
+ role?: string;
92
+ accessible_name?: string;
93
+ }[];
94
+ selector_path: {
95
+ root: "document" | "open_shadow_root";
96
+ selector: string;
97
+ }[];
98
+ };
99
+ };
100
+ }[];
101
+ artifact_title: string;
102
+ inspector_version: 1;
103
+ };
104
+ reference_file?: {
105
+ media_type?: string;
106
+ blob_oid?: string;
107
+ path: string;
108
+ name: string;
109
+ source: "human_upload" | "tutti_output";
110
+ category: "file" | "image";
111
+ size_bytes: number;
112
+ text_read: {
113
+ blocked_reason?: "unknown" | "binary" | "too_large" | "unsupported_encoding" | "restricted_path" | "secret_like";
114
+ can_read: boolean;
115
+ };
116
+ };
117
+ workflow_ref?: string;
118
+ clarification_round_ref?: string;
119
+ clarification_thread_ref?: string;
120
+ task_result?: {
121
+ error_code?: string;
122
+ activity_ref?: string;
123
+ generated_files?: {
124
+ media_type?: string;
125
+ group?: string;
126
+ path: string;
127
+ name: string;
128
+ size_bytes: number;
129
+ }[];
130
+ module_name?: string;
131
+ latest_run_result_id?: string;
132
+ retry_action?: "retryable" | "retried";
133
+ final_note?: string;
134
+ status: "completed" | "failed";
135
+ kind: "task_result";
136
+ task_id: string;
137
+ title: string;
138
+ summary: string;
139
+ task_title: string;
140
+ result_key: string;
141
+ };
142
+ worklist_compile?: {
143
+ kind: "worklist_compile";
144
+ title: string;
145
+ workflow_ref: string;
146
+ tasks: {
147
+ status: "failed" | "done" | "pending" | "todo" | "running";
148
+ task_id: string;
149
+ title: string;
150
+ summary: string;
151
+ module_id: string;
152
+ module_name: string;
153
+ }[];
154
+ task_count: number;
155
+ };
156
+ artifact_preview?: {
157
+ task_id?: string;
158
+ run_result_id?: string;
159
+ status: "available" | "updated";
160
+ kind: "artifact_preview";
161
+ title: string;
162
+ };
163
+ development_operator?: true;
164
+ external_reference_reads?: string[];
165
+ external_references?: {
166
+ name: string;
167
+ kind: "feishu_docx" | "feishu_drive_file" | "feishu_file" | "feishu_image";
168
+ created_at: string;
169
+ source_ref: string;
170
+ }[];
171
+ scratchpad_source?: "include" | "exclude";
172
+ worklist_feedback?: {
173
+ generated_files?: {
174
+ media_type?: string;
175
+ group?: string;
176
+ path: string;
177
+ name: string;
178
+ size_bytes: number;
179
+ }[];
180
+ final_note?: string;
181
+ primary_task_id?: string;
182
+ status: "completed" | "failed";
183
+ kind: "worklist_terminal";
184
+ items: {
185
+ module_name?: string;
186
+ latest_run_result_id?: string;
187
+ status: "failed" | "done";
188
+ task_id: string;
189
+ title: string;
190
+ summary: string;
191
+ }[];
192
+ title: string;
193
+ reason: "all_tasks_done" | "run_failed";
194
+ counters: {
195
+ completed: number;
196
+ failed: number;
197
+ remaining: number;
198
+ };
199
+ feedback_key: string;
200
+ };
201
+ };
202
+ scope: {
203
+ kind: "main_chat";
204
+ } | {
205
+ kind: "clarification_round";
206
+ clarification_round_ref: string;
207
+ };
208
+ id: string;
209
+ author: {
210
+ ref?: string;
211
+ display_name?: string;
212
+ avatar?: {
213
+ kind: "initials";
214
+ initials: string;
215
+ color: "neutral" | "green" | "blue" | "yellow";
216
+ } | {
217
+ kind: "image";
218
+ initials: string;
219
+ color: "neutral" | "green" | "blue" | "yellow";
220
+ image_url: string;
221
+ };
222
+ kind: "human" | "system" | "agent";
223
+ };
224
+ body: string;
225
+ created_at: string;
226
+ message_kind: "user_text" | "agent_text" | "system_notice" | "clarification_request" | "clarification_card";
227
+ }[];
228
+ //# sourceMappingURL=clarification-snapshot.d.ts.map