@xfey/tutti 0.1.95 → 0.1.97

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 (52) hide show
  1. package/dist/collaboration-state/index.d.ts +5 -5
  2. package/dist/collaboration-state/index.js +4 -4
  3. package/dist/collaboration-state/scratchpad-source-state.d.ts +6 -1
  4. package/dist/collaboration-state/scratchpad-source-state.js +37 -6
  5. package/dist/collaboration-state/storage-types.d.ts +10 -0
  6. package/dist/collaboration-state/task-compile-context.d.ts +8 -3
  7. package/dist/collaboration-state/task-compile-context.js +91 -7
  8. package/dist/collaboration-state/task-retry.js +3 -1
  9. package/dist/collaboration-state/types.d.ts +12 -0
  10. package/dist/collaboration-state/worklist.d.ts +2 -1
  11. package/dist/collaboration-state/worklist.js +6 -0
  12. package/dist/control-plane/clarification-commands.d.ts +12 -8
  13. package/dist/control-plane/clarification-commands.js +29 -10
  14. package/dist/control-plane/dispatch-intents.d.ts +104 -0
  15. package/dist/control-plane/dispatch-intents.js +276 -0
  16. package/dist/control-plane/follow-up-run.d.ts +1 -1
  17. package/dist/control-plane/follow-up-run.js +12 -4
  18. package/dist/control-plane/follow-up-start.d.ts +3 -1
  19. package/dist/control-plane/follow-up-start.js +7 -2
  20. package/dist/control-plane/formatters.d.ts +2 -0
  21. package/dist/control-plane/formatters.js +6 -0
  22. package/dist/control-plane/index.d.ts +13 -1
  23. package/dist/control-plane/index.js +313 -16
  24. package/dist/control-plane/intent-dispatcher.d.ts +52 -0
  25. package/dist/control-plane/intent-dispatcher.js +184 -0
  26. package/dist/control-plane/reference-summary-refresh.d.ts +4 -1
  27. package/dist/control-plane/reference-summary-refresh.js +39 -4
  28. package/dist/control-plane/run-dispatch-intents.d.ts +15 -0
  29. package/dist/control-plane/run-dispatch-intents.js +40 -0
  30. package/dist/control-plane/run-retry.d.ts +1 -1
  31. package/dist/control-plane/run-retry.js +14 -6
  32. package/dist/control-plane/run-scheduler.d.ts +1 -1
  33. package/dist/control-plane/run-scheduler.js +12 -4
  34. package/dist/control-plane/scratchpad-refresh-start.d.ts +10 -2
  35. package/dist/control-plane/scratchpad-refresh-start.js +57 -8
  36. package/dist/control-plane/scratchpad-refresh.js +3 -4
  37. package/dist/control-plane/startup-recovery.d.ts +2 -2
  38. package/dist/control-plane/startup-recovery.js +73 -3
  39. package/dist/control-plane/task-compile-continuation.d.ts +3 -1
  40. package/dist/control-plane/task-compile-continuation.js +14 -9
  41. package/dist/control-plane/task-compile-start.d.ts +33 -1
  42. package/dist/control-plane/task-compile-start.js +244 -26
  43. package/dist/control-plane/types.d.ts +4 -2
  44. package/dist/providers/openai/app-server/session-store.js +2 -4
  45. package/migrations/0015_control_plane_intents.sql +115 -0
  46. package/migrations/README.md +2 -1
  47. package/node_modules/@tutti/shared/dist/ids/index.d.ts +3 -0
  48. package/node_modules/@tutti/shared/dist/ids/index.js +2 -0
  49. package/package.json +1 -1
  50. package/web/assets/{homepage-motion-scene-BFo4r4nz.js → homepage-motion-scene-lpr-1FjJ.js} +1 -1
  51. package/web/assets/{index-CNIl4TF8.js → index-CQoq2PtU.js} +2 -2
  52. package/web/index.html +1 -1
@@ -2,8 +2,8 @@ export { CollaborationStateCursorError } from "./serialization.js";
2
2
  export { createIdleExecutionStatusProjection } from "./execution-status.js";
3
3
  export { createMainChatAgentMessage, createMainChatMessage, createMainChatSystemMessage, ensureInitialProjectWelcomeMessage, hasMainChatArtifactPreviewMessage, hasMainChatTaskResultMessage, hasMainChatWorklistCompileMessage, hasMainChatWorklistFeedbackMessage, readCompletedWorklistFeedbackTaskIds, readMessageProjectionById, readMainChatMessageWindow, readMainChatMessagesBeforeCursor, readMainChatMessagesForScratchpadCycle, } from "./messages.js";
4
4
  export { createEmptyScratchpadProjection, readScratchpadReceiptSourceRefs, readScratchpadProjection, upsertScratchpadProjection, } from "./scratchpad.js";
5
- export { markScratchpadRefreshFailed, markScratchpadRefreshStarted, markScratchpadRefreshSucceeded, markScratchpadSourceDirty, readScratchpadSourceState, } from "./scratchpad-source-state.js";
6
- export { captureTaskCompileContext, completeTaskCompileContext, readActiveTaskCompileContext, readCurrentScratchpadSourceRefs, rollbackTaskCompileContext, taskCompileContextSnapshot, } from "./task-compile-context.js";
5
+ export { markScratchpadRefreshFailed, markScratchpadRefreshStarted, markScratchpadRefreshSucceeded, markScratchpadSourceDirty, recoverStaleScratchpadRefresh, readScratchpadSourceState, } from "./scratchpad-source-state.js";
6
+ export { captureTaskCompileContext, completeTaskCompileContext, readActiveTaskCompileContext, readCurrentScratchpadSourceRefs, readPendingTaskCompileSubmission, recordTaskCompileSubmission, rollbackTaskCompileContext, taskCompileContextSnapshot, } from "./task-compile-context.js";
7
7
  export { deleteStaleClarificationSubmitReplayHints, readActiveClarificationProjection, readClarificationRoundProjection, readClarificationRoundsForThread, readClarificationThreadResponse, readTaskCompileClarificationRoundsByWorkflowRef, readSealedClarificationRoundsWithoutSuccessor, } from "./clarification-records.js";
8
8
  export { createClarificationRoundAgentMessage, createClarificationRoundMessage, readClarificationRoundMessagesWindow, } from "./clarification-messages.js";
9
9
  export { openNextTaskBoundClarificationRound, openNextTaskCompileClarificationRound, openTaskCompileClarificationRound, } from "./clarification-rounds.js";
@@ -12,9 +12,9 @@ export { readRecoverySummariesPage, readRecoverySummaryProjection, readRunResult
12
12
  export { recordRunNeedsHumanResult, recordRunResult } from "./run-recording.js";
13
13
  export { materializeTaskRetryRun, readMaterializedTaskRetry } from "./task-retry.js";
14
14
  export type { MaterializeTaskRetryResult } from "./task-retry.js";
15
- export { readStaleRunningTaskIdentities, recoverStaleRunningTasksAsFailed, } from "./recovery.js";
15
+ export { readStaleRunningTaskIdentities, recoverStaleRunningTasksAsFailed } from "./recovery.js";
16
16
  export { markReadStateScope, readReadStateSummary, recordReadNotificationEvent, } from "./read-state.js";
17
17
  export { readTaskDetailProjection } from "./task-details.js";
18
- export { applyTaskCompileProposal, markTaskRunStarted, readNextRunnableTask, readRecoverableScheduledRun, readCompiledTaskSummariesByWorkflowRef, readTaskProjection, readWorklistProjection, } from "./worklist.js";
19
- export type { ApplyTaskCompileProposalResult, CloseTaskBoundClarificationRetaskingRequiredInput, CloseTaskBoundClarificationRetaskingRequiredResult, CreateClarificationRoundAgentMessageInput, CreateClarificationRoundMessageInput, CreateMainChatAgentMessageInput, CreateMainChatMessageInput, CreateMainChatSystemMessageInput, CursorPageOptions, MarkReadStateScopeInput, MarkReadStateScopeResult, MarkTaskRunStartedInput, MarkTaskRunStartedResult, MaterializeClarificationSuccessorInput, MaterializeTaskBoundRunSuccessorInput, MaterializeTaskBoundRunSuccessorResult, MessageAuthorInput, NextRunnableTaskResult, OpenNextTaskBoundClarificationRoundInput, OpenNextTaskBoundClarificationRoundResult, OpenNextTaskCompileClarificationRoundInput, OpenNextTaskCompileClarificationRoundResult, OpenTaskCompileClarificationRoundInput, OpenTaskCompileClarificationRoundResult, ReadMessageWindowOptions, ReadReadStateSummaryInput, RecoverStaleRunningTasksResult, RecoverableScheduledRun, RecordReadNotificationEventInput, RecordRecoverySummaryInput, RecordRecoverySummaryResult, RecordRunNeedsHumanResultInput, RecordRunNeedsHumanResultResult, RecordRunResultInput, RecordRunResultResult, RunResultPromotionProjection, ScratchpadSnapshotInput, ScratchpadContentSnapshot, ScratchpadSourceRefInput, CaptureTaskCompileContextInput, CompleteTaskCompileContextInput, RollbackTaskCompileContextInput, TaskCompileContextRecord, SealedClarificationRoundWithoutSuccessor, SubmitClarificationRoundInput, TaskCompileProposalInput, TaskCompileProposalModuleInput, TaskCompileProposalTaskInput, } from "./types.js";
18
+ export { applyTaskCompileProposal, markTaskRunStarted, readNextRunnableTask, readRecoverableScheduledRun, readCompiledTaskSummariesByWorkflowRef, readTaskProjection, readTaskProjectionByActivityRef, readWorklistProjection, } from "./worklist.js";
19
+ export type { ApplyTaskCompileProposalResult, CloseTaskBoundClarificationRetaskingRequiredInput, CloseTaskBoundClarificationRetaskingRequiredResult, CreateClarificationRoundAgentMessageInput, CreateClarificationRoundMessageInput, CreateMainChatAgentMessageInput, CreateMainChatMessageInput, CreateMainChatSystemMessageInput, CursorPageOptions, MarkReadStateScopeInput, MarkReadStateScopeResult, MarkTaskRunStartedInput, MarkTaskRunStartedResult, MaterializeClarificationSuccessorInput, MaterializeTaskBoundRunSuccessorInput, MaterializeTaskBoundRunSuccessorResult, MessageAuthorInput, NextRunnableTaskResult, OpenNextTaskBoundClarificationRoundInput, OpenNextTaskBoundClarificationRoundResult, OpenNextTaskCompileClarificationRoundInput, OpenNextTaskCompileClarificationRoundResult, OpenTaskCompileClarificationRoundInput, OpenTaskCompileClarificationRoundResult, ReadMessageWindowOptions, ReadReadStateSummaryInput, RecoverStaleRunningTasksResult, RecoverableScheduledRun, RecordReadNotificationEventInput, RecordRecoverySummaryInput, RecordRecoverySummaryResult, RecordRunNeedsHumanResultInput, RecordRunNeedsHumanResultResult, RecordRunResultInput, RecordRunResultResult, RunResultPromotionProjection, ScratchpadSnapshotInput, ScratchpadContentSnapshot, ScratchpadSourceRefInput, CaptureTaskCompileContextInput, CompleteTaskCompileContextInput, RecordTaskCompileSubmissionInput, RollbackTaskCompileContextInput, TaskCompileContextRecord, TaskCompileSubmissionRecord, SealedClarificationRoundWithoutSuccessor, SubmitClarificationRoundInput, TaskCompileProposalInput, TaskCompileProposalModuleInput, TaskCompileProposalTaskInput, } from "./types.js";
20
20
  //# sourceMappingURL=index.d.ts.map
@@ -2,8 +2,8 @@ export { CollaborationStateCursorError } from "./serialization.js";
2
2
  export { createIdleExecutionStatusProjection } from "./execution-status.js";
3
3
  export { createMainChatAgentMessage, createMainChatMessage, createMainChatSystemMessage, ensureInitialProjectWelcomeMessage, hasMainChatArtifactPreviewMessage, hasMainChatTaskResultMessage, hasMainChatWorklistCompileMessage, hasMainChatWorklistFeedbackMessage, readCompletedWorklistFeedbackTaskIds, readMessageProjectionById, readMainChatMessageWindow, readMainChatMessagesBeforeCursor, readMainChatMessagesForScratchpadCycle, } from "./messages.js";
4
4
  export { createEmptyScratchpadProjection, readScratchpadReceiptSourceRefs, readScratchpadProjection, upsertScratchpadProjection, } from "./scratchpad.js";
5
- export { markScratchpadRefreshFailed, markScratchpadRefreshStarted, markScratchpadRefreshSucceeded, markScratchpadSourceDirty, readScratchpadSourceState, } from "./scratchpad-source-state.js";
6
- export { captureTaskCompileContext, completeTaskCompileContext, readActiveTaskCompileContext, readCurrentScratchpadSourceRefs, rollbackTaskCompileContext, taskCompileContextSnapshot, } from "./task-compile-context.js";
5
+ export { markScratchpadRefreshFailed, markScratchpadRefreshStarted, markScratchpadRefreshSucceeded, markScratchpadSourceDirty, recoverStaleScratchpadRefresh, readScratchpadSourceState, } from "./scratchpad-source-state.js";
6
+ export { captureTaskCompileContext, completeTaskCompileContext, readActiveTaskCompileContext, readCurrentScratchpadSourceRefs, readPendingTaskCompileSubmission, recordTaskCompileSubmission, rollbackTaskCompileContext, taskCompileContextSnapshot, } from "./task-compile-context.js";
7
7
  export { deleteStaleClarificationSubmitReplayHints, readActiveClarificationProjection, readClarificationRoundProjection, readClarificationRoundsForThread, readClarificationThreadResponse, readTaskCompileClarificationRoundsByWorkflowRef, readSealedClarificationRoundsWithoutSuccessor, } from "./clarification-records.js";
8
8
  export { createClarificationRoundAgentMessage, createClarificationRoundMessage, readClarificationRoundMessagesWindow, } from "./clarification-messages.js";
9
9
  export { openNextTaskBoundClarificationRound, openNextTaskCompileClarificationRound, openTaskCompileClarificationRound, } from "./clarification-rounds.js";
@@ -11,8 +11,8 @@ export { closeTaskBoundClarificationAsRetaskingRequired, materializeClarificatio
11
11
  export { readRecoverySummariesPage, readRecoverySummaryProjection, readRunResultProjectionByActivityRef, readTaskRunResultsPage, recordRecoverySummary, } from "./run-results.js";
12
12
  export { recordRunNeedsHumanResult, recordRunResult } from "./run-recording.js";
13
13
  export { materializeTaskRetryRun, readMaterializedTaskRetry } from "./task-retry.js";
14
- export { readStaleRunningTaskIdentities, recoverStaleRunningTasksAsFailed, } from "./recovery.js";
14
+ export { readStaleRunningTaskIdentities, recoverStaleRunningTasksAsFailed } from "./recovery.js";
15
15
  export { markReadStateScope, readReadStateSummary, recordReadNotificationEvent, } from "./read-state.js";
16
16
  export { readTaskDetailProjection } from "./task-details.js";
17
- export { applyTaskCompileProposal, markTaskRunStarted, readNextRunnableTask, readRecoverableScheduledRun, readCompiledTaskSummariesByWorkflowRef, readTaskProjection, readWorklistProjection, } from "./worklist.js";
17
+ export { applyTaskCompileProposal, markTaskRunStarted, readNextRunnableTask, readRecoverableScheduledRun, readCompiledTaskSummariesByWorkflowRef, readTaskProjection, readTaskProjectionByActivityRef, readWorklistProjection, } from "./worklist.js";
18
18
  //# sourceMappingURL=index.js.map
@@ -1,4 +1,4 @@
1
- import type { ActivityRef, MessageId } from "@tutti/shared/ids";
1
+ import type { ActivityRef, ControlPlaneIntentId, MessageId } from "@tutti/shared/ids";
2
2
  import type { SqliteDatabase } from "../store/index.js";
3
3
  export type ScratchpadMainChatCursor = {
4
4
  created_at: string;
@@ -11,6 +11,7 @@ export type ScratchpadSourceState = {
11
11
  in_flight?: {
12
12
  activity_ref: ActivityRef;
13
13
  started_at: string;
14
+ intent_id?: ControlPlaneIntentId;
14
15
  };
15
16
  backoff_until?: string;
16
17
  last_completed_at?: string;
@@ -26,6 +27,7 @@ export declare function markScratchpadSourceDirty(db: SqliteDatabase, input?: {
26
27
  }): ScratchpadSourceState;
27
28
  export declare function markScratchpadRefreshStarted(db: SqliteDatabase, input: {
28
29
  activityRef: ActivityRef;
30
+ intentId?: ControlPlaneIntentId;
29
31
  now?: () => Date;
30
32
  }): ScratchpadSourceState;
31
33
  export declare function markScratchpadRefreshSucceeded(db: SqliteDatabase, input: ScratchpadSourceCursorUpdate & {
@@ -35,4 +37,7 @@ export declare function markScratchpadRefreshFailed(db: SqliteDatabase, input?:
35
37
  backoffUntil?: string;
36
38
  now?: () => Date;
37
39
  }): ScratchpadSourceState;
40
+ export declare function recoverStaleScratchpadRefresh(db: SqliteDatabase, input?: {
41
+ now?: () => Date;
42
+ }): boolean;
38
43
  //# sourceMappingURL=scratchpad-source-state.d.ts.map
@@ -4,8 +4,7 @@ function mapScratchpadSourceStateRow(row) {
4
4
  if (row === undefined) {
5
5
  return state;
6
6
  }
7
- if (row.cycle_start_cursor_created_at !== null &&
8
- row.cycle_start_cursor_message_id !== null) {
7
+ if (row.cycle_start_cursor_created_at !== null && row.cycle_start_cursor_message_id !== null) {
9
8
  state.cycle_start_cursor = {
10
9
  created_at: row.cycle_start_cursor_created_at,
11
10
  message_id: row.cycle_start_cursor_message_id,
@@ -25,6 +24,9 @@ function mapScratchpadSourceStateRow(row) {
25
24
  state.in_flight = {
26
25
  activity_ref: row.in_flight_activity_ref,
27
26
  started_at: row.in_flight_started_at,
27
+ ...(row.in_flight_intent_id === null
28
+ ? {}
29
+ : { intent_id: row.in_flight_intent_id }),
28
30
  };
29
31
  }
30
32
  if (row.backoff_until !== null) {
@@ -74,6 +76,7 @@ export function readScratchpadSourceState(db) {
74
76
  dirty_since,
75
77
  in_flight_activity_ref,
76
78
  in_flight_started_at,
79
+ in_flight_intent_id,
77
80
  last_completed_at,
78
81
  last_failed_at,
79
82
  backoff_until,
@@ -107,14 +110,16 @@ export function markScratchpadRefreshStarted(db, input) {
107
110
  id,
108
111
  in_flight_activity_ref,
109
112
  in_flight_started_at,
113
+ in_flight_intent_id,
110
114
  updated_at
111
115
  )
112
- VALUES ('current', ?, ?, ?)
116
+ VALUES ('current', ?, ?, ?, ?)
113
117
  ON CONFLICT(id) DO UPDATE SET
114
118
  in_flight_activity_ref = excluded.in_flight_activity_ref,
115
119
  in_flight_started_at = excluded.in_flight_started_at,
120
+ in_flight_intent_id = excluded.in_flight_intent_id,
116
121
  updated_at = excluded.updated_at
117
- `).run(input.activityRef, updatedAt, updatedAt);
122
+ `).run(input.activityRef, updatedAt, input.intentId ?? null, updatedAt);
118
123
  return readScratchpadSourceState(db);
119
124
  }
120
125
  export function markScratchpadRefreshSucceeded(db, input) {
@@ -130,17 +135,19 @@ export function markScratchpadRefreshSucceeded(db, input) {
130
135
  dirty_since,
131
136
  in_flight_activity_ref,
132
137
  in_flight_started_at,
138
+ in_flight_intent_id,
133
139
  last_completed_at,
134
140
  backoff_until,
135
141
  updated_at
136
142
  )
137
- VALUES ('current', ?, ?, ?, NULL, NULL, ?, NULL, ?)
143
+ VALUES ('current', ?, ?, ?, NULL, NULL, NULL, ?, NULL, ?)
138
144
  ON CONFLICT(id) DO UPDATE SET
139
145
  refreshed_through_cursor_created_at = excluded.refreshed_through_cursor_created_at,
140
146
  refreshed_through_cursor_message_id = excluded.refreshed_through_cursor_message_id,
141
147
  dirty_since = excluded.dirty_since,
142
148
  in_flight_activity_ref = NULL,
143
149
  in_flight_started_at = NULL,
150
+ in_flight_intent_id = NULL,
144
151
  last_completed_at = excluded.last_completed_at,
145
152
  backoff_until = NULL,
146
153
  updated_at = excluded.updated_at
@@ -155,19 +162,43 @@ export function markScratchpadRefreshFailed(db, input = {}) {
155
162
  dirty_since,
156
163
  in_flight_activity_ref,
157
164
  in_flight_started_at,
165
+ in_flight_intent_id,
158
166
  last_failed_at,
159
167
  backoff_until,
160
168
  updated_at
161
169
  )
162
- VALUES ('current', ?, NULL, NULL, ?, ?, ?)
170
+ VALUES ('current', ?, NULL, NULL, NULL, ?, ?, ?)
163
171
  ON CONFLICT(id) DO UPDATE SET
164
172
  dirty_since = COALESCE(scratchpad_source_state.dirty_since, excluded.dirty_since),
165
173
  in_flight_activity_ref = NULL,
166
174
  in_flight_started_at = NULL,
175
+ in_flight_intent_id = NULL,
167
176
  last_failed_at = excluded.last_failed_at,
168
177
  backoff_until = excluded.backoff_until,
169
178
  updated_at = excluded.updated_at
170
179
  `).run(updatedAt, updatedAt, input.backoffUntil ?? null, updatedAt);
171
180
  return readScratchpadSourceState(db);
172
181
  }
182
+ export function recoverStaleScratchpadRefresh(db, input = {}) {
183
+ const current = readScratchpadSourceState(db);
184
+ if (current.in_flight === undefined) {
185
+ return false;
186
+ }
187
+ const updatedAt = nowIso(input.now);
188
+ const result = db
189
+ .prepare(`
190
+ UPDATE scratchpad_source_state
191
+ SET
192
+ dirty_since = COALESCE(dirty_since, in_flight_started_at, ?),
193
+ in_flight_activity_ref = NULL,
194
+ in_flight_started_at = NULL,
195
+ in_flight_intent_id = NULL,
196
+ last_failed_at = ?,
197
+ backoff_until = NULL,
198
+ updated_at = ?
199
+ WHERE id = 'current' AND in_flight_activity_ref = ?
200
+ `)
201
+ .run(updatedAt, updatedAt, updatedAt, current.in_flight.activity_ref);
202
+ return result.changes === 1;
203
+ }
173
204
  //# sourceMappingURL=scratchpad-source-state.js.map
@@ -48,6 +48,7 @@ export type ScratchpadSourceStateRow = {
48
48
  dirty_since: string | null;
49
49
  in_flight_activity_ref: string | null;
50
50
  in_flight_started_at: string | null;
51
+ in_flight_intent_id: string | null;
51
52
  last_completed_at: string | null;
52
53
  last_failed_at: string | null;
53
54
  backoff_until: string | null;
@@ -73,6 +74,15 @@ export type ActiveTaskCompileContextSourceRow = {
73
74
  message_id: MessageId;
74
75
  created_at: string;
75
76
  };
77
+ export type PendingTaskCompileSubmissionRow = {
78
+ workflow_ref: string;
79
+ accepted_activity_ref: string;
80
+ requested_at: string;
81
+ source_window_after_created_at: string | null;
82
+ source_window_after_message_id: string | null;
83
+ source_window_through_created_at: string | null;
84
+ source_window_through_message_id: string | null;
85
+ };
76
86
  export type TaskModuleRow = {
77
87
  id: string;
78
88
  name: string;
@@ -1,8 +1,13 @@
1
- import type { WorkflowInvocationRef } from "@tutti/shared/ids";
1
+ import type { MessageId, WorkflowInvocationRef } from "@tutti/shared/ids";
2
2
  import { type SqliteDatabase } from "../store/index.js";
3
- import type { CaptureTaskCompileContextInput, CompleteTaskCompileContextInput, RollbackTaskCompileContextInput, ScratchpadContentSnapshot, ScratchpadSourceRefInput, TaskCompileContextRecord } from "./types.js";
3
+ import type { CaptureTaskCompileContextInput, CompleteTaskCompileContextInput, RecordTaskCompileSubmissionInput, RollbackTaskCompileContextInput, ScratchpadContentSnapshot, ScratchpadSourceRefInput, TaskCompileContextRecord, TaskCompileSubmissionRecord } from "./types.js";
4
+ export declare function readPendingTaskCompileSubmission(db: SqliteDatabase, workflowRef?: WorkflowInvocationRef): TaskCompileSubmissionRecord | null;
5
+ export declare function recordTaskCompileSubmission(db: SqliteDatabase, input: RecordTaskCompileSubmissionInput): TaskCompileSubmissionRecord;
4
6
  export declare function readActiveTaskCompileContext(db: SqliteDatabase, workflowRef?: WorkflowInvocationRef): TaskCompileContextRecord | null;
5
- export declare function readCurrentScratchpadSourceRefs(db: SqliteDatabase): ScratchpadSourceRefInput[];
7
+ export declare function readCurrentScratchpadSourceRefs(db: SqliteDatabase, through?: {
8
+ created_at: string;
9
+ message_id: MessageId;
10
+ }): ScratchpadSourceRefInput[];
6
11
  export declare function captureTaskCompileContext(db: SqliteDatabase, input: CaptureTaskCompileContextInput): TaskCompileContextRecord;
7
12
  export declare function completeTaskCompileContext(db: SqliteDatabase, input: CompleteTaskCompileContextInput): {
8
13
  updated_at?: string;
@@ -49,8 +49,7 @@ function readContextRow(db) {
49
49
  }
50
50
  function mapContextRow(row, sources) {
51
51
  const sourceWindow = {};
52
- if (row.source_window_after_created_at !== null &&
53
- row.source_window_after_message_id !== null) {
52
+ if (row.source_window_after_created_at !== null && row.source_window_after_message_id !== null) {
54
53
  sourceWindow.after = {
55
54
  created_at: row.source_window_after_created_at,
56
55
  message_id: row.source_window_after_message_id,
@@ -92,6 +91,78 @@ function readContextSources(db, workflowRef) {
92
91
  `)
93
92
  .all(workflowRef);
94
93
  }
94
+ function mapTaskCompileSubmissionRow(row) {
95
+ const sourceWindow = {};
96
+ if (row.source_window_after_created_at !== null && row.source_window_after_message_id !== null) {
97
+ sourceWindow.after = {
98
+ created_at: row.source_window_after_created_at,
99
+ message_id: row.source_window_after_message_id,
100
+ };
101
+ }
102
+ if (row.source_window_through_created_at !== null &&
103
+ row.source_window_through_message_id !== null) {
104
+ sourceWindow.through = {
105
+ created_at: row.source_window_through_created_at,
106
+ message_id: row.source_window_through_message_id,
107
+ };
108
+ }
109
+ return {
110
+ workflow_ref: row.workflow_ref,
111
+ accepted_activity_ref: row.accepted_activity_ref,
112
+ requested_at: row.requested_at,
113
+ source_window: sourceWindow,
114
+ };
115
+ }
116
+ export function readPendingTaskCompileSubmission(db, workflowRef) {
117
+ const row = db
118
+ .prepare(`
119
+ SELECT
120
+ workflow_ref,
121
+ accepted_activity_ref,
122
+ requested_at,
123
+ source_window_after_created_at,
124
+ source_window_after_message_id,
125
+ source_window_through_created_at,
126
+ source_window_through_message_id
127
+ FROM pending_task_compile_submissions
128
+ WHERE id = 'current'
129
+ `)
130
+ .get();
131
+ if (row === undefined || (workflowRef !== undefined && row.workflow_ref !== workflowRef)) {
132
+ return null;
133
+ }
134
+ return mapTaskCompileSubmissionRow(row);
135
+ }
136
+ export function recordTaskCompileSubmission(db, input) {
137
+ const existing = readPendingTaskCompileSubmission(db);
138
+ if (existing !== null) {
139
+ if (existing.workflow_ref === input.workflow_ref) {
140
+ return existing;
141
+ }
142
+ throw new Error("A pending Task Compile submission already exists");
143
+ }
144
+ if (readActiveTaskCompileContext(db) !== null) {
145
+ throw new Error("An active Task Compile Context already exists");
146
+ }
147
+ db.prepare(`
148
+ INSERT INTO pending_task_compile_submissions (
149
+ id,
150
+ workflow_ref,
151
+ accepted_activity_ref,
152
+ requested_at,
153
+ source_window_after_created_at,
154
+ source_window_after_message_id,
155
+ source_window_through_created_at,
156
+ source_window_through_message_id
157
+ )
158
+ VALUES ('current', ?, ?, ?, ?, ?, ?, ?)
159
+ `).run(input.workflow_ref, input.accepted_activity_ref, nowIso(input.now), input.source_window.after?.created_at ?? null, input.source_window.after?.message_id ?? null, input.source_window.through?.created_at ?? null, input.source_window.through?.message_id ?? null);
160
+ const recorded = readPendingTaskCompileSubmission(db, input.workflow_ref);
161
+ if (recorded === null) {
162
+ throw new Error("Failed to record pending Task Compile submission");
163
+ }
164
+ return recorded;
165
+ }
95
166
  export function readActiveTaskCompileContext(db, workflowRef) {
96
167
  const row = readContextRow(db);
97
168
  if (row === undefined || (workflowRef !== undefined && row.workflow_ref !== workflowRef)) {
@@ -99,8 +170,8 @@ export function readActiveTaskCompileContext(db, workflowRef) {
99
170
  }
100
171
  return mapContextRow(row, readContextSources(db, row.workflow_ref));
101
172
  }
102
- export function readCurrentScratchpadSourceRefs(db) {
103
- return db
173
+ export function readCurrentScratchpadSourceRefs(db, through) {
174
+ const sources = db
104
175
  .prepare(`
105
176
  SELECT message_id, created_at
106
177
  FROM scratchpad_source_refs
@@ -114,6 +185,13 @@ export function readCurrentScratchpadSourceRefs(db) {
114
185
  source_scope: "main_chat",
115
186
  created_at: source.created_at,
116
187
  }));
188
+ if (through === undefined) {
189
+ return sources;
190
+ }
191
+ return sources.filter((source) => {
192
+ const timeOrder = source.created_at.localeCompare(through.created_at);
193
+ return timeOrder < 0 || (timeOrder === 0 && source.message_id <= through.message_id);
194
+ });
117
195
  }
118
196
  export function captureTaskCompileContext(db, input) {
119
197
  return withHostStoreTransaction(db, (tx) => {
@@ -129,12 +207,15 @@ export function captureTaskCompileContext(db, input) {
129
207
  throw new Error("A ready Scratchpad is required to capture Task Compile Context");
130
208
  }
131
209
  const sourceState = readScratchpadSourceState(tx);
132
- const sources = readCurrentScratchpadSourceRefs(tx);
210
+ const submission = readPendingTaskCompileSubmission(tx, input.workflow_ref);
211
+ const sources = readCurrentScratchpadSourceRefs(tx, submission?.source_window.through);
133
212
  const lastSource = sources.at(-1);
134
- const through = sourceState.refreshed_through_cursor ??
213
+ const through = submission?.source_window.through ??
214
+ sourceState.refreshed_through_cursor ??
135
215
  (lastSource === undefined
136
216
  ? undefined
137
217
  : { created_at: lastSource.created_at, message_id: lastSource.message_id });
218
+ const after = submission?.source_window.after ?? sourceState.cycle_start_cursor;
138
219
  const submittedAt = nowIso(input.now);
139
220
  tx.prepare(`
140
221
  INSERT INTO active_task_compile_context (
@@ -154,7 +235,7 @@ export function captureTaskCompileContext(db, input) {
154
235
  source_window_through_message_id
155
236
  )
156
237
  VALUES ('current', ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
157
- `).run(input.workflow_ref, input.captured_by_activity_ref, submittedAt, scratchpad.topic, scratchpad.background_summary, stringifyJson(scratchpad.current_consensus), stringifyJson(scratchpad.open_questions), stringifyJson(scratchpad.task_changes), scratchpad.updated_at, sourceState.cycle_start_cursor?.created_at ?? null, sourceState.cycle_start_cursor?.message_id ?? null, through?.created_at ?? null, through?.message_id ?? null);
238
+ `).run(input.workflow_ref, input.captured_by_activity_ref, submittedAt, scratchpad.topic, scratchpad.background_summary, stringifyJson(scratchpad.current_consensus), stringifyJson(scratchpad.open_questions), stringifyJson(scratchpad.task_changes), scratchpad.updated_at, after?.created_at ?? null, after?.message_id ?? null, through?.created_at ?? null, through?.message_id ?? null);
158
239
  const insertSource = tx.prepare(`
159
240
  INSERT INTO active_task_compile_context_sources (
160
241
  workflow_ref,
@@ -166,6 +247,9 @@ export function captureTaskCompileContext(db, input) {
166
247
  for (const source of sources) {
167
248
  insertSource.run(input.workflow_ref, source.message_id, source.created_at);
168
249
  }
250
+ if (submission !== null) {
251
+ tx.prepare("DELETE FROM pending_task_compile_submissions WHERE workflow_ref = ?").run(input.workflow_ref);
252
+ }
169
253
  tx.prepare(`
170
254
  INSERT INTO scratchpad_source_state (
171
255
  id,
@@ -48,7 +48,9 @@ function failedResultCanRetry(row, result) {
48
48
  }
49
49
  export function materializeTaskRetryRun(db, input) {
50
50
  return withHostStoreTransaction(db, (tx) => {
51
- const taskRow = tx.prepare("SELECT * FROM tasks WHERE id = ?").get(input.task_id);
51
+ const taskRow = tx
52
+ .prepare("SELECT * FROM tasks WHERE id = ?")
53
+ .get(input.task_id);
52
54
  if (taskRow === undefined) {
53
55
  return { disposition: { kind: "task_not_failed" } };
54
56
  }
@@ -90,6 +90,18 @@ export type TaskCompileContextRecord = {
90
90
  created_at: string;
91
91
  }>;
92
92
  };
93
+ export type TaskCompileSubmissionRecord = {
94
+ workflow_ref: WorkflowInvocationRef;
95
+ accepted_activity_ref: ActivityRef;
96
+ requested_at: string;
97
+ source_window: TaskCompileContextRecord["source_window"];
98
+ };
99
+ export type RecordTaskCompileSubmissionInput = {
100
+ workflow_ref: WorkflowInvocationRef;
101
+ accepted_activity_ref: ActivityRef;
102
+ source_window: TaskCompileSubmissionRecord["source_window"];
103
+ now?: () => Date;
104
+ };
93
105
  export type CaptureTaskCompileContextInput = {
94
106
  workflow_ref: WorkflowInvocationRef;
95
107
  captured_by_activity_ref: ActivityRef;
@@ -1,8 +1,9 @@
1
- import { type TaskId, type WorkflowInvocationRef } from "@tutti/shared/ids";
1
+ import { type ActivityRef, type TaskId, type WorkflowInvocationRef } from "@tutti/shared/ids";
2
2
  import type { TaskProjection, WorklistCompiledTaskSummary, WorklistProjection } from "@tutti/shared/schemas/api";
3
3
  import type { SqliteDatabase } from "../store/index.js";
4
4
  import type { ApplyTaskCompileProposalResult, MarkTaskRunStartedInput, MarkTaskRunStartedResult, NextRunnableTaskResult, RecoverableScheduledRun, TaskCompileProposalInput } from "./types.js";
5
5
  export declare function readTaskProjection(db: SqliteDatabase, taskId: TaskId): TaskProjection | null;
6
+ export declare function readTaskProjectionByActivityRef(db: SqliteDatabase, activityRef: ActivityRef): TaskProjection | null;
6
7
  export declare function readCompiledTaskSummariesByWorkflowRef(db: SqliteDatabase, workflowRef: WorkflowInvocationRef): WorklistCompiledTaskSummary[];
7
8
  export declare function readWorklistProjection(db: SqliteDatabase): WorklistProjection;
8
9
  export declare function readNextRunnableTask(db: SqliteDatabase): NextRunnableTaskResult;
@@ -44,6 +44,12 @@ export function readTaskProjection(db, taskId) {
44
44
  const row = db.prepare("SELECT * FROM tasks WHERE id = ?").get(taskId);
45
45
  return row === undefined ? null : mapTaskRow(row);
46
46
  }
47
+ export function readTaskProjectionByActivityRef(db, activityRef) {
48
+ const row = db
49
+ .prepare("SELECT * FROM tasks WHERE current_activity_ref = ? LIMIT 1")
50
+ .get(activityRef);
51
+ return row === undefined ? null : mapTaskRow(row);
52
+ }
47
53
  export function readCompiledTaskSummariesByWorkflowRef(db, workflowRef) {
48
54
  const rows = db
49
55
  .prepare(`
@@ -1,7 +1,7 @@
1
1
  import type { ClarificationRoundRef, IdempotencyKey } from "@tutti/shared/ids";
2
2
  import type { SendClarificationRoundMessageDisposition, SendClarificationRoundMessagePayload, SendClarificationRoundMessageResult, SubmitClarificationRoundDisposition, SubmitClarificationRoundPayload, SubmitClarificationRoundResult } from "@tutti/shared/schemas/api";
3
3
  import type { WorkspaceEventBus } from "../server-shell/http/workspace-events.js";
4
- import type { HostProjectStore } from "../store/index.js";
4
+ import { type HostProjectStore } from "../store/index.js";
5
5
  import type { ControlPlaneCommandResult, TrustedHumanAuthor } from "./types.js";
6
6
  export declare function runSendClarificationRoundMessage(options: {
7
7
  store: HostProjectStore;
@@ -18,16 +18,20 @@ export declare function runSubmitClarificationRound(options: {
18
18
  payload: SubmitClarificationRoundPayload;
19
19
  commandId: IdempotencyKey;
20
20
  now: () => Date;
21
- startTaskCompileContinuation: (owner: {
21
+ wakeIntentDispatcher: () => void;
22
+ }): ControlPlaneCommandResult<SubmitClarificationRoundDisposition, SubmitClarificationRoundResult>;
23
+ export declare function enqueueClarificationSuccessorIntent(db: HostProjectStore["db"], input: {
24
+ owner: {
22
25
  kind: "task_compile";
23
26
  workflow_ref: string;
24
27
  } | {
25
28
  kind: "task";
26
29
  task_id: string;
27
- }, roundId: ClarificationRoundRef) => boolean;
28
- startTaskBoundFollowUpCheck: (owner: {
29
- kind: "task";
30
- task_id: string;
31
- }, threadId: string, roundId: ClarificationRoundRef) => boolean;
32
- }): ControlPlaneCommandResult<SubmitClarificationRoundDisposition, SubmitClarificationRoundResult>;
30
+ };
31
+ roundId: ClarificationRoundRef;
32
+ now: () => Date;
33
+ }): {
34
+ intent: import("./dispatch-intents.js").ControlPlaneIntent;
35
+ created: boolean;
36
+ };
33
37
  //# sourceMappingURL=clarification-commands.d.ts.map
@@ -1,4 +1,6 @@
1
1
  import { createClarificationRoundMessage, readActiveClarificationProjection, submitClarificationRound, } from "../collaboration-state/index.js";
2
+ import { withHostStoreTransaction } from "../store/index.js";
3
+ import { enqueueControlPlaneIntent } from "./dispatch-intents.js";
2
4
  import { clarificationInvalidates } from "./invalidations.js";
3
5
  export function runSendClarificationRoundMessage(options) {
4
6
  const active = readActiveClarificationProjection(options.store.db);
@@ -69,10 +71,20 @@ export function runSubmitClarificationRound(options) {
69
71
  result: { round: active.round },
70
72
  };
71
73
  }
72
- const sealed = submitClarificationRound(options.store.db, {
73
- round_id: options.roundId,
74
- accepted_command_id: options.commandId,
75
- now: options.now,
74
+ const sealed = withHostStoreTransaction(options.store.db, (tx) => {
75
+ const submitted = submitClarificationRound(tx, {
76
+ round_id: options.roundId,
77
+ accepted_command_id: options.commandId,
78
+ now: options.now,
79
+ });
80
+ if (submitted !== null) {
81
+ enqueueClarificationSuccessorIntent(tx, {
82
+ owner: active.thread.owner,
83
+ roundId: options.roundId,
84
+ now: options.now,
85
+ });
86
+ }
87
+ return submitted;
76
88
  });
77
89
  if (sealed === null) {
78
90
  return { disposition: { kind: "chain_closed" } };
@@ -86,12 +98,7 @@ export function runSubmitClarificationRound(options) {
86
98
  },
87
99
  invalidates: clarificationInvalidates(options.roundId, active.thread.id),
88
100
  });
89
- if (active.thread.owner.kind === "task_compile") {
90
- options.startTaskCompileContinuation(active.thread.owner, options.roundId);
91
- }
92
- else {
93
- options.startTaskBoundFollowUpCheck(active.thread.owner, active.thread.id, options.roundId);
94
- }
101
+ options.wakeIntentDispatcher();
95
102
  return {
96
103
  disposition: { kind: "accepted" },
97
104
  result: {
@@ -102,4 +109,16 @@ export function runSubmitClarificationRound(options) {
102
109
  },
103
110
  };
104
111
  }
112
+ export function enqueueClarificationSuccessorIntent(db, input) {
113
+ const kind = input.owner.kind === "task_compile" ? "task_compile_continuation" : "task_bound_follow_up";
114
+ return enqueueControlPlaneIntent(db, {
115
+ kind,
116
+ ownerKind: "clarification_round",
117
+ ownerRef: input.roundId,
118
+ dedupeKey: `${kind}:${input.roundId}`,
119
+ lane: "foreground",
120
+ priority: 20,
121
+ now: input.now,
122
+ });
123
+ }
105
124
  //# sourceMappingURL=clarification-commands.js.map
@@ -0,0 +1,104 @@
1
+ import { type ControlPlaneIntentId } from "@tutti/shared/ids";
2
+ import type { SqliteDatabase } from "../store/index.js";
3
+ export declare const CONTROL_PLANE_INTENT_KINDS: readonly ["task_compile_start", "task_compile_continuation", "task_bound_follow_up", "scratchpad_refresh", "reference_summary_refresh", "run_dispatch"];
4
+ export type ControlPlaneIntentKind = (typeof CONTROL_PLANE_INTENT_KINDS)[number];
5
+ export declare const CONTROL_PLANE_INTENT_OWNER_KINDS: readonly ["task_compile", "clarification_round", "scratchpad", "reference_summary", "task_run"];
6
+ export type ControlPlaneIntentOwnerKind = (typeof CONTROL_PLANE_INTENT_OWNER_KINDS)[number];
7
+ export type ControlPlaneIntentLane = "foreground" | "scratchpad_background" | "run";
8
+ export type ControlPlaneIntentState = "pending" | "claimed" | "running" | "completed" | "failed" | "cancelled";
9
+ export type ControlPlaneIntentTerminalState = Extract<ControlPlaneIntentState, "completed" | "failed" | "cancelled">;
10
+ export type ControlPlaneIntent = {
11
+ intent_id: ControlPlaneIntentId;
12
+ kind: ControlPlaneIntentKind;
13
+ owner_kind: ControlPlaneIntentOwnerKind;
14
+ owner_ref: string;
15
+ dedupe_key: string;
16
+ lane: ControlPlaneIntentLane;
17
+ priority: number;
18
+ state: ControlPlaneIntentState;
19
+ available_at: string;
20
+ claim_token?: string;
21
+ lease_expires_at?: string;
22
+ attempt_count: number;
23
+ last_error_code?: string;
24
+ created_at: string;
25
+ updated_at: string;
26
+ completed_at?: string;
27
+ };
28
+ export declare function readControlPlaneIntent(db: SqliteDatabase, intentId: ControlPlaneIntentId): ControlPlaneIntent | null;
29
+ export declare function readControlPlaneIntentByDedupeKey(db: SqliteDatabase, dedupeKey: string): ControlPlaneIntent | null;
30
+ export declare function enqueueControlPlaneIntent(db: SqliteDatabase, input: {
31
+ kind: ControlPlaneIntentKind;
32
+ ownerKind: ControlPlaneIntentOwnerKind;
33
+ ownerRef: string;
34
+ dedupeKey: string;
35
+ lane: ControlPlaneIntentLane;
36
+ priority?: number;
37
+ availableAt?: string;
38
+ now?: () => Date;
39
+ }): {
40
+ intent: ControlPlaneIntent;
41
+ created: boolean;
42
+ };
43
+ export declare function listPendingControlPlaneIntents(db: SqliteDatabase, input?: {
44
+ now?: () => Date;
45
+ limit?: number;
46
+ }): ControlPlaneIntent[];
47
+ export declare function listNonTerminalControlPlaneIntentsByKind(db: SqliteDatabase, kind: ControlPlaneIntentKind): ControlPlaneIntent[];
48
+ export declare function listExpiredControlPlaneIntentClaims(db: SqliteDatabase, input?: {
49
+ now?: () => Date;
50
+ limit?: number;
51
+ }): ControlPlaneIntent[];
52
+ export declare function listActiveControlPlaneIntentClaims(db: SqliteDatabase): ControlPlaneIntent[];
53
+ export declare function claimControlPlaneIntent(db: SqliteDatabase, input: {
54
+ intentId: ControlPlaneIntentId;
55
+ claimToken: string;
56
+ leaseExpiresAt: string;
57
+ now?: () => Date;
58
+ }): ControlPlaneIntent | null;
59
+ export declare function markControlPlaneIntentRunning(db: SqliteDatabase, input: {
60
+ intentId: ControlPlaneIntentId;
61
+ claimToken: string;
62
+ leaseExpiresAt: string;
63
+ now?: () => Date;
64
+ }): ControlPlaneIntent | null;
65
+ export declare function renewControlPlaneIntentLease(db: SqliteDatabase, input: {
66
+ intentId: ControlPlaneIntentId;
67
+ claimToken: string;
68
+ leaseExpiresAt: string;
69
+ now?: () => Date;
70
+ }): boolean;
71
+ export declare function releaseControlPlaneIntentClaim(db: SqliteDatabase, input: {
72
+ intentId: ControlPlaneIntentId;
73
+ claimToken: string;
74
+ availableAt?: string;
75
+ errorCode?: string;
76
+ now?: () => Date;
77
+ }): ControlPlaneIntent | null;
78
+ export declare function requeueExpiredControlPlaneIntentClaim(db: SqliteDatabase, input: {
79
+ intentId: ControlPlaneIntentId;
80
+ expectedClaimToken: string;
81
+ availableAt?: string;
82
+ errorCode: string;
83
+ now?: () => Date;
84
+ }): ControlPlaneIntent | null;
85
+ export declare function requeueOrphanedControlPlaneIntentClaim(db: SqliteDatabase, input: {
86
+ intentId: ControlPlaneIntentId;
87
+ expectedClaimToken: string;
88
+ errorCode: string;
89
+ now?: () => Date;
90
+ }): ControlPlaneIntent | null;
91
+ export declare function settleClaimedControlPlaneIntent(db: SqliteDatabase, input: {
92
+ intentId: ControlPlaneIntentId;
93
+ claimToken: string;
94
+ state: ControlPlaneIntentTerminalState;
95
+ errorCode?: string;
96
+ now?: () => Date;
97
+ }): ControlPlaneIntent | null;
98
+ export declare function settlePendingControlPlaneIntent(db: SqliteDatabase, input: {
99
+ intentId: ControlPlaneIntentId;
100
+ state: ControlPlaneIntentTerminalState;
101
+ errorCode?: string;
102
+ now?: () => Date;
103
+ }): ControlPlaneIntent | null;
104
+ //# sourceMappingURL=dispatch-intents.d.ts.map