@xfey/tutti 0.1.60 → 0.1.62

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 (90) hide show
  1. package/README.md +2 -2
  2. package/dist/collaboration-state/clarification-records.d.ts +0 -9
  3. package/dist/collaboration-state/clarification-records.js +0 -57
  4. package/dist/collaboration-state/index.d.ts +6 -5
  5. package/dist/collaboration-state/index.js +5 -4
  6. package/dist/collaboration-state/messages.d.ts +4 -6
  7. package/dist/collaboration-state/messages.js +22 -29
  8. package/dist/collaboration-state/scratchpad-source-state.d.ts +4 -12
  9. package/dist/collaboration-state/scratchpad-source-state.js +24 -58
  10. package/dist/collaboration-state/scratchpad.d.ts +1 -2
  11. package/dist/collaboration-state/scratchpad.js +0 -59
  12. package/dist/collaboration-state/storage-types.d.ts +25 -5
  13. package/dist/collaboration-state/task-compile-context.d.ts +40 -0
  14. package/dist/collaboration-state/task-compile-context.js +273 -0
  15. package/dist/collaboration-state/types.d.ts +39 -9
  16. package/dist/control-plane/clarification-commands.d.ts +0 -1
  17. package/dist/control-plane/clarification-commands.js +0 -1
  18. package/dist/control-plane/index.js +16 -3
  19. package/dist/control-plane/project-context-bootstrap.d.ts +4 -4
  20. package/dist/control-plane/project-context-bootstrap.js +7 -12
  21. package/dist/control-plane/scratchpad-auto-refresh.js +12 -1
  22. package/dist/control-plane/scratchpad-refresh-start.js +77 -94
  23. package/dist/control-plane/scratchpad-refresh.js +3 -1
  24. package/dist/control-plane/scratchpad-source-messages.d.ts +15 -7
  25. package/dist/control-plane/scratchpad-source-messages.js +53 -69
  26. package/dist/control-plane/task-compile-clarification-context.d.ts +9 -0
  27. package/dist/control-plane/task-compile-clarification-context.js +70 -0
  28. package/dist/control-plane/task-compile-continuation.js +28 -56
  29. package/dist/control-plane/task-compile-output.js +50 -14
  30. package/dist/control-plane/task-compile-start.js +119 -55
  31. package/dist/control-plane/workflows/openai.d.ts +2 -1
  32. package/dist/control-plane/workflows/openai.js +13 -27
  33. package/dist/control-plane/workflows/types.d.ts +27 -17
  34. package/dist/server-shell/cli/args.d.ts +4 -0
  35. package/dist/server-shell/cli/args.js +11 -0
  36. package/dist/server-shell/cli/cli.js +35 -11
  37. package/dist/server-shell/cli/host-server-runtime.d.ts +2 -0
  38. package/dist/server-shell/cli/host-server-runtime.js +12 -5
  39. package/dist/server-shell/cli/local-control-client.d.ts +5 -1
  40. package/dist/server-shell/cli/local-control-client.js +19 -0
  41. package/dist/server-shell/desktop-integration/manager.d.ts +2 -0
  42. package/dist/server-shell/desktop-integration/manager.js +19 -31
  43. package/dist/server-shell/http/routes/local-control.d.ts +2 -0
  44. package/dist/server-shell/http/routes/local-control.js +24 -0
  45. package/dist/server-shell/local-console/invocation-context.js +10 -1
  46. package/dist/server-shell/local-console/operation-coordinator.d.ts +16 -0
  47. package/dist/server-shell/local-console/operation-coordinator.js +50 -0
  48. package/dist/server-shell/local-console/package-update-completion.d.ts +25 -0
  49. package/dist/server-shell/local-console/package-update-completion.js +91 -0
  50. package/dist/server-shell/local-console/package-update-hosts.d.ts +17 -0
  51. package/dist/server-shell/local-console/package-update-hosts.js +92 -0
  52. package/dist/server-shell/local-console/package-update-lock.d.ts +46 -0
  53. package/dist/server-shell/local-console/package-update-lock.js +223 -0
  54. package/dist/server-shell/local-console/package-update-log.d.ts +20 -0
  55. package/dist/server-shell/local-console/package-update-log.js +75 -0
  56. package/dist/server-shell/local-console/package-update-process.d.ts +22 -0
  57. package/dist/server-shell/local-console/package-update-process.js +252 -0
  58. package/dist/server-shell/local-console/package-update-record.d.ts +53 -0
  59. package/dist/server-shell/local-console/package-update-record.js +180 -0
  60. package/dist/server-shell/local-console/package-update-recovery.d.ts +34 -0
  61. package/dist/server-shell/local-console/package-update-recovery.js +140 -0
  62. package/dist/server-shell/local-console/package-update-service.d.ts +72 -0
  63. package/dist/server-shell/local-console/package-update-service.js +400 -0
  64. package/dist/server-shell/local-console/package-update-worker.d.ts +55 -0
  65. package/dist/server-shell/local-console/package-update-worker.js +297 -0
  66. package/dist/server-shell/local-console/project-service.d.ts +2 -0
  67. package/dist/server-shell/local-console/project-service.js +16 -12
  68. package/dist/server-shell/local-console/server.d.ts +9 -0
  69. package/dist/server-shell/local-console/server.js +92 -4
  70. package/dist/server-shell/package-installation/command.d.ts +36 -0
  71. package/dist/server-shell/package-installation/command.js +162 -0
  72. package/dist/server-shell/package-installation/discovery.d.ts +51 -0
  73. package/dist/server-shell/package-installation/discovery.js +137 -0
  74. package/dist/server-shell/package-installation/index.d.ts +5 -0
  75. package/dist/server-shell/package-installation/index.js +5 -0
  76. package/dist/server-shell/package-installation/installation.d.ts +34 -0
  77. package/dist/server-shell/package-installation/installation.js +123 -0
  78. package/dist/server-shell/package-installation/semver.d.ts +9 -0
  79. package/dist/server-shell/package-installation/semver.js +84 -0
  80. package/migrations/0014_scratchpad_task_compile_context.sql +147 -0
  81. package/migrations/README.md +2 -1
  82. package/package.json +1 -1
  83. package/prompts/procedures/README.md +3 -3
  84. package/prompts/procedures/scratchpad-refresh.md +17 -9
  85. package/prompts/procedures/task-compile.md +6 -3
  86. package/prompts/prompt-flow-map.md +21 -14
  87. package/web/assets/{homepage-motion-scene-CY7o4hnR.js → homepage-motion-scene-CRmrkkEH.js} +1 -1
  88. package/web/assets/{index-B08r3x8o.js → index-C9JrplHV.js} +14 -14
  89. package/web/assets/{index-DpabiRgp.css → index-IZKcmU_g.css} +1 -1
  90. package/web/index.html +2 -2
@@ -0,0 +1,273 @@
1
+ import { withHostStoreTransaction } from "../store/index.js";
2
+ import { readScratchpadProjection } from "./scratchpad.js";
3
+ import { readScratchpadSourceState } from "./scratchpad-source-state.js";
4
+ import { nowIso, stringifyJson } from "./serialization.js";
5
+ function parseStringArray(value, field) {
6
+ let parsed;
7
+ try {
8
+ parsed = JSON.parse(value);
9
+ }
10
+ catch {
11
+ throw new Error(`Invalid Task Compile Context ${field} JSON`);
12
+ }
13
+ if (!Array.isArray(parsed) || !parsed.every((entry) => typeof entry === "string")) {
14
+ throw new Error(`Invalid Task Compile Context ${field}`);
15
+ }
16
+ return parsed;
17
+ }
18
+ function readContextRow(db) {
19
+ return db
20
+ .prepare(`
21
+ SELECT
22
+ workflow_ref,
23
+ captured_by_activity_ref,
24
+ submitted_at,
25
+ topic,
26
+ background_summary,
27
+ current_consensus_json,
28
+ open_questions_json,
29
+ task_changes_json,
30
+ scratchpad_updated_at,
31
+ source_window_after_created_at,
32
+ source_window_after_message_id,
33
+ source_window_through_created_at,
34
+ source_window_through_message_id
35
+ FROM active_task_compile_context
36
+ WHERE id = 'current'
37
+ `)
38
+ .get();
39
+ }
40
+ function mapContextRow(row, sources) {
41
+ const sourceWindow = {};
42
+ if (row.source_window_after_created_at !== null &&
43
+ row.source_window_after_message_id !== null) {
44
+ sourceWindow.after = {
45
+ created_at: row.source_window_after_created_at,
46
+ message_id: row.source_window_after_message_id,
47
+ };
48
+ }
49
+ if (row.source_window_through_created_at !== null &&
50
+ row.source_window_through_message_id !== null) {
51
+ sourceWindow.through = {
52
+ created_at: row.source_window_through_created_at,
53
+ message_id: row.source_window_through_message_id,
54
+ };
55
+ }
56
+ return {
57
+ workflow_ref: row.workflow_ref,
58
+ captured_by_activity_ref: row.captured_by_activity_ref,
59
+ submitted_at: row.submitted_at,
60
+ scratchpad_snapshot: {
61
+ topic: row.topic,
62
+ background_summary: row.background_summary,
63
+ current_consensus: parseStringArray(row.current_consensus_json, "current_consensus"),
64
+ open_questions: parseStringArray(row.open_questions_json, "open_questions"),
65
+ task_changes: parseStringArray(row.task_changes_json, "task_changes"),
66
+ },
67
+ scratchpad_updated_at: row.scratchpad_updated_at,
68
+ source_window: sourceWindow,
69
+ sources: sources.map((source) => ({
70
+ message_id: source.message_id,
71
+ created_at: source.created_at,
72
+ })),
73
+ };
74
+ }
75
+ function readContextSources(db, workflowRef) {
76
+ return db
77
+ .prepare(`
78
+ SELECT workflow_ref, message_id, created_at
79
+ FROM active_task_compile_context_sources
80
+ WHERE workflow_ref = ?
81
+ ORDER BY created_at ASC, message_id ASC
82
+ `)
83
+ .all(workflowRef);
84
+ }
85
+ export function readActiveTaskCompileContext(db, workflowRef) {
86
+ const row = readContextRow(db);
87
+ if (row === undefined || (workflowRef !== undefined && row.workflow_ref !== workflowRef)) {
88
+ return null;
89
+ }
90
+ return mapContextRow(row, readContextSources(db, row.workflow_ref));
91
+ }
92
+ function currentScratchpadSources(db) {
93
+ return db
94
+ .prepare(`
95
+ SELECT message_id, created_at
96
+ FROM scratchpad_source_refs
97
+ WHERE scratchpad_id = 'current'
98
+ AND source_scope = 'main_chat'
99
+ ORDER BY created_at ASC, message_id ASC
100
+ `)
101
+ .all();
102
+ }
103
+ export function captureTaskCompileContext(db, input) {
104
+ return withHostStoreTransaction(db, (tx) => {
105
+ const existing = readActiveTaskCompileContext(tx);
106
+ if (existing !== null) {
107
+ if (existing.workflow_ref === input.workflow_ref) {
108
+ return existing;
109
+ }
110
+ throw new Error("An active Task Compile Context already exists");
111
+ }
112
+ const scratchpad = readScratchpadProjection(tx);
113
+ if (scratchpad.state !== "ready" || scratchpad.updated_at === undefined) {
114
+ throw new Error("A ready Scratchpad is required to capture Task Compile Context");
115
+ }
116
+ const sourceState = readScratchpadSourceState(tx);
117
+ const sources = currentScratchpadSources(tx);
118
+ const lastSource = sources.at(-1);
119
+ const through = sourceState.refreshed_through_cursor ??
120
+ (lastSource === undefined
121
+ ? undefined
122
+ : { created_at: lastSource.created_at, message_id: lastSource.message_id });
123
+ const submittedAt = nowIso(input.now);
124
+ tx.prepare(`
125
+ INSERT INTO active_task_compile_context (
126
+ id,
127
+ workflow_ref,
128
+ captured_by_activity_ref,
129
+ submitted_at,
130
+ topic,
131
+ background_summary,
132
+ current_consensus_json,
133
+ open_questions_json,
134
+ task_changes_json,
135
+ scratchpad_updated_at,
136
+ source_window_after_created_at,
137
+ source_window_after_message_id,
138
+ source_window_through_created_at,
139
+ source_window_through_message_id
140
+ )
141
+ VALUES ('current', ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
142
+ `).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);
143
+ const insertSource = tx.prepare(`
144
+ INSERT INTO active_task_compile_context_sources (
145
+ workflow_ref,
146
+ message_id,
147
+ created_at
148
+ )
149
+ VALUES (?, ?, ?)
150
+ `);
151
+ for (const source of sources) {
152
+ insertSource.run(input.workflow_ref, source.message_id, source.created_at);
153
+ }
154
+ tx.prepare(`
155
+ INSERT INTO scratchpad_source_state (
156
+ id,
157
+ cycle_start_cursor_created_at,
158
+ cycle_start_cursor_message_id,
159
+ updated_at
160
+ )
161
+ VALUES ('current', ?, ?, ?)
162
+ ON CONFLICT(id) DO UPDATE SET
163
+ cycle_start_cursor_created_at = excluded.cycle_start_cursor_created_at,
164
+ cycle_start_cursor_message_id = excluded.cycle_start_cursor_message_id,
165
+ updated_at = excluded.updated_at
166
+ `).run(through?.created_at ?? null, through?.message_id ?? null, submittedAt);
167
+ const captured = readActiveTaskCompileContext(tx, input.workflow_ref);
168
+ if (captured === null) {
169
+ throw new Error("Failed to capture Task Compile Context");
170
+ }
171
+ return captured;
172
+ });
173
+ }
174
+ export function completeTaskCompileContext(db, input) {
175
+ return withHostStoreTransaction(db, (tx) => {
176
+ const context = readActiveTaskCompileContext(tx, input.workflow_ref);
177
+ if (context === null) {
178
+ throw new Error("Active Task Compile Context is unavailable");
179
+ }
180
+ const completedAt = nowIso(input.now);
181
+ const snapshot = context.scratchpad_snapshot;
182
+ tx.prepare(`
183
+ INSERT INTO scratchpad_receipts (
184
+ workflow_ref,
185
+ activity_ref,
186
+ topic,
187
+ background_summary,
188
+ current_consensus_json,
189
+ open_questions_json,
190
+ task_changes_json,
191
+ submitted_at
192
+ )
193
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
194
+ `).run(input.workflow_ref, input.activity_ref, snapshot.topic, snapshot.background_summary, stringifyJson(snapshot.current_consensus), stringifyJson(snapshot.open_questions), stringifyJson(snapshot.task_changes), context.submitted_at);
195
+ const insertReceiptSource = tx.prepare(`
196
+ INSERT INTO scratchpad_receipt_sources (
197
+ workflow_ref,
198
+ message_id,
199
+ source_scope,
200
+ created_at
201
+ )
202
+ VALUES (?, ?, 'main_chat', ?)
203
+ `);
204
+ for (const source of context.sources) {
205
+ insertReceiptSource.run(input.workflow_ref, source.message_id, source.created_at);
206
+ }
207
+ tx.prepare(`
208
+ UPDATE scratchpad
209
+ SET
210
+ state = 'submitted',
211
+ submitted_by_activity_ref = ?,
212
+ submitted_by_workflow_ref = ?,
213
+ submitted_at = ?,
214
+ updated_at = ?
215
+ WHERE id = 'current'
216
+ AND state = 'ready'
217
+ AND updated_at = ?
218
+ AND topic = ?
219
+ AND background_summary = ?
220
+ AND current_consensus_json = ?
221
+ AND open_questions_json = ?
222
+ AND task_changes_json = ?
223
+ `).run(input.activity_ref, input.workflow_ref, context.submitted_at, completedAt, context.scratchpad_updated_at, snapshot.topic, snapshot.background_summary, stringifyJson(snapshot.current_consensus), stringifyJson(snapshot.open_questions), stringifyJson(snapshot.task_changes));
224
+ tx.prepare("DELETE FROM active_task_compile_context WHERE workflow_ref = ?").run(input.workflow_ref);
225
+ return readScratchpadProjection(tx);
226
+ });
227
+ }
228
+ export function rollbackTaskCompileContext(db, input) {
229
+ return withHostStoreTransaction(db, (tx) => {
230
+ const context = readActiveTaskCompileContext(tx, input.workflow_ref);
231
+ if (context === null) {
232
+ return false;
233
+ }
234
+ const rolledBackAt = nowIso(input.now);
235
+ const sourceState = readScratchpadSourceState(tx);
236
+ const dirtySince = sourceState.in_flight !== undefined &&
237
+ new Date(rolledBackAt).getTime() <= new Date(sourceState.in_flight.started_at).getTime()
238
+ ? new Date(new Date(sourceState.in_flight.started_at).getTime() + 1).toISOString()
239
+ : rolledBackAt;
240
+ const after = context.source_window.after;
241
+ tx.prepare(`
242
+ INSERT INTO scratchpad_source_state (
243
+ id,
244
+ cycle_start_cursor_created_at,
245
+ cycle_start_cursor_message_id,
246
+ refreshed_through_cursor_created_at,
247
+ refreshed_through_cursor_message_id,
248
+ dirty_since,
249
+ updated_at
250
+ )
251
+ VALUES ('current', ?, ?, ?, ?, ?, ?)
252
+ ON CONFLICT(id) DO UPDATE SET
253
+ cycle_start_cursor_created_at = excluded.cycle_start_cursor_created_at,
254
+ cycle_start_cursor_message_id = excluded.cycle_start_cursor_message_id,
255
+ refreshed_through_cursor_created_at = excluded.refreshed_through_cursor_created_at,
256
+ refreshed_through_cursor_message_id = excluded.refreshed_through_cursor_message_id,
257
+ dirty_since = excluded.dirty_since,
258
+ updated_at = excluded.updated_at
259
+ `).run(after?.created_at ?? null, after?.message_id ?? null, after?.created_at ?? null, after?.message_id ?? null, dirtySince, rolledBackAt);
260
+ tx.prepare("DELETE FROM active_task_compile_context WHERE workflow_ref = ?").run(input.workflow_ref);
261
+ return true;
262
+ });
263
+ }
264
+ export function taskCompileContextSnapshot(context) {
265
+ return {
266
+ topic: context.scratchpad_snapshot.topic,
267
+ background_summary: context.scratchpad_snapshot.background_summary,
268
+ current_consensus: [...context.scratchpad_snapshot.current_consensus],
269
+ open_questions: [...context.scratchpad_snapshot.open_questions],
270
+ task_changes: [...context.scratchpad_snapshot.task_changes],
271
+ };
272
+ }
273
+ //# sourceMappingURL=task-compile-context.js.map
@@ -1,7 +1,7 @@
1
1
  import type { RunLineage } from "@tutti/shared/domain";
2
2
  import type { AccountAvatarProjection } from "@tutti/shared/domain";
3
3
  import type { ActivityRef, ClarificationRoundRef, MessageId, TaskId, TaskModuleId, WorkflowInvocationRef } from "@tutti/shared/ids";
4
- import type { ClarificationRequestPayload, ClarificationRoundProjection, ClarificationSuccessorRef, ClarificationThreadProjection, MessageProjection, ReadStateScope, ReadStateSummaryProjection, RecoverySummaryProjection, RunResultProjection, ScratchpadProjection, TaskContractProjection, TaskDetailProjection, TaskDetailResultProjection, TaskProjection, WorklistProjection } from "@tutti/shared/schemas/api";
4
+ import type { ClarificationRequestPayload, ClarificationRoundProjection, ClarificationSuccessorRef, ClarificationThreadProjection, MessageProjection, ReadStateScope, ReadStateSummaryProjection, RecoverySummaryProjection, RunResultProjection, TaskContractProjection, TaskDetailProjection, TaskDetailResultProjection, TaskProjection, WorklistProjection } from "@tutti/shared/schemas/api";
5
5
  export type MessageAuthorInput = {
6
6
  kind: "human" | "system" | "agent";
7
7
  ref?: string;
@@ -57,18 +57,52 @@ export type ScratchpadSnapshotInput = {
57
57
  updated_by_activity_ref?: ActivityRef;
58
58
  now?: () => Date;
59
59
  };
60
+ export type ScratchpadContentSnapshot = {
61
+ topic: string;
62
+ background_summary: string;
63
+ current_consensus: string[];
64
+ open_questions: string[];
65
+ task_changes: string[];
66
+ };
60
67
  export type ScratchpadSourceRefInput = {
61
68
  message_id: MessageId;
62
- source_scope: "main_chat" | "clarification_round";
69
+ source_scope: "main_chat";
63
70
  created_at: string;
64
71
  };
65
- export type SubmitScratchpadInput = {
72
+ export type TaskCompileContextRecord = {
73
+ workflow_ref: WorkflowInvocationRef;
74
+ captured_by_activity_ref: ActivityRef;
75
+ submitted_at: string;
76
+ scratchpad_snapshot: ScratchpadContentSnapshot;
77
+ scratchpad_updated_at: string;
78
+ source_window: {
79
+ after?: {
80
+ created_at: string;
81
+ message_id: MessageId;
82
+ };
83
+ through?: {
84
+ created_at: string;
85
+ message_id: MessageId;
86
+ };
87
+ };
88
+ sources: Array<{
89
+ message_id: MessageId;
90
+ created_at: string;
91
+ }>;
92
+ };
93
+ export type CaptureTaskCompileContextInput = {
94
+ workflow_ref: WorkflowInvocationRef;
95
+ captured_by_activity_ref: ActivityRef;
96
+ now?: () => Date;
97
+ };
98
+ export type CompleteTaskCompileContextInput = {
66
99
  workflow_ref: WorkflowInvocationRef;
67
100
  activity_ref: ActivityRef;
68
101
  now?: () => Date;
69
102
  };
70
- export type SubmitScratchpadResult = {
71
- scratchpad: ScratchpadProjection;
103
+ export type RollbackTaskCompileContextInput = {
104
+ workflow_ref: WorkflowInvocationRef;
105
+ now?: () => Date;
72
106
  };
73
107
  export type TaskCompileProposalInput = {
74
108
  workflow_ref: WorkflowInvocationRef;
@@ -299,10 +333,6 @@ export type ReadMessageWindowOptions = {
299
333
  after?: string;
300
334
  around?: string;
301
335
  };
302
- export type ReadMainChatMessagesAfterOptions = {
303
- after_created_at: string;
304
- limit?: number;
305
- };
306
336
  export type CursorPageOptions = {
307
337
  cursor?: string;
308
338
  limit?: number;
@@ -29,6 +29,5 @@ export declare function runSubmitClarificationRound(options: {
29
29
  kind: "task";
30
30
  task_id: string;
31
31
  }, threadId: string, roundId: ClarificationRoundRef) => boolean;
32
- notifyScratchpadSourceChanged?: () => void;
33
32
  }): ControlPlaneCommandResult<SubmitClarificationRoundDisposition, SubmitClarificationRoundResult>;
34
33
  //# sourceMappingURL=clarification-commands.d.ts.map
@@ -87,7 +87,6 @@ export function runSubmitClarificationRound(options) {
87
87
  invalidates: clarificationInvalidates(options.roundId, active.thread.id),
88
88
  });
89
89
  if (active.thread.owner.kind === "task_compile") {
90
- options.notifyScratchpadSourceChanged?.();
91
90
  options.startTaskCompileContinuation(active.thread.owner, options.roundId);
92
91
  }
93
92
  else {
@@ -1,4 +1,4 @@
1
- import { readWorklistProjection } from "../collaboration-state/index.js";
1
+ import { readActiveTaskCompileContext, readWorklistProjection, } from "../collaboration-state/index.js";
2
2
  import { ProcedureEngine } from "../procedure-engine/index.js";
3
3
  import { RunEngine } from "../run-engine/index.js";
4
4
  import { publishProcedureTransition as publishProcedureTransitionEvent, publishRunResultRecorded as publishRunResultRecordedEvent, publishRunTransition as publishRunTransitionEvent, publishTaskRunStarted as publishTaskRunStartedEvent, } from "./event-publishers.js";
@@ -192,7 +192,6 @@ export class Phase5ControlPlane {
192
192
  now: this.now,
193
193
  startTaskCompileContinuation: (owner, submittedRoundId) => this.startTaskCompileContinuation(owner, submittedRoundId),
194
194
  startTaskBoundFollowUpCheck: (owner, threadId, submittedRoundId) => this.startTaskBoundFollowUpCheck(owner, threadId, submittedRoundId),
195
- notifyScratchpadSourceChanged: () => this.notifyScratchpadSourceChanged(),
196
195
  });
197
196
  }
198
197
  async waitForIdle() {
@@ -243,6 +242,9 @@ export class Phase5ControlPlane {
243
242
  activity_kind: "procedure",
244
243
  lane: activity.lane ?? "foreground",
245
244
  ...(activity.workflow_kind === undefined ? {} : { workflow_kind: activity.workflow_kind }),
245
+ ...(activity.workflow_ref === undefined
246
+ ? {}
247
+ : { workflow_ref: activity.workflow_ref }),
246
248
  }));
247
249
  }
248
250
  readActiveForegroundProcedure() {
@@ -274,7 +276,18 @@ export class Phase5ControlPlane {
274
276
  if (activeScratchpadRefresh !== null) {
275
277
  return activeScratchpadRefresh;
276
278
  }
277
- return this.readActiveForegroundProcedure();
279
+ const foreground = this.readActiveForegroundProcedure();
280
+ if (foreground === null) {
281
+ return null;
282
+ }
283
+ const activeContext = readActiveTaskCompileContext(this.store.db);
284
+ if (activeContext !== null &&
285
+ (foreground.workflow_kind === "project_context_bootstrap" ||
286
+ (foreground.workflow_kind === "task_compile" &&
287
+ foreground.workflow_ref === activeContext.workflow_ref))) {
288
+ return null;
289
+ }
290
+ return foreground;
278
291
  }
279
292
  publishTaskRunStarted(task, activityRef) {
280
293
  publishTaskRunStartedEvent(this.events, task, activityRef);
@@ -1,14 +1,14 @@
1
1
  import type { ActivityRef, WorkflowInvocationRef } from "@tutti/shared/ids";
2
- import type { ScratchpadProjection } from "@tutti/shared/schemas/api";
2
+ import type { ScratchpadContentSnapshot } from "../collaboration-state/index.js";
3
3
  import type { ProcedureProgressUpdate } from "../procedure-engine/index.js";
4
4
  import type { WorkspaceEventBus } from "../server-shell/http/workspace-events.js";
5
5
  import type { InitializeProjectDocsResult, ProjectDocsInitializationContext, ProjectDocsInitializationDocument } from "../workspace-ops/index.js";
6
6
  import type { ControlPlaneLogger, Phase5ControlPlaneOptions } from "./types.js";
7
7
  import type { ProcedureWorkflowRunner, ProjectContextBootstrapWorkflowInput, ProjectContextBootstrapWorkflowOutput } from "./workflows/index.js";
8
- export declare function buildProjectDocsInitializationContext(scratchpad: ScratchpadProjection): ProjectDocsInitializationContext;
8
+ export declare function buildProjectDocsInitializationContext(scratchpad: ScratchpadContentSnapshot): ProjectDocsInitializationContext;
9
9
  export declare function buildProjectContextBootstrapWorkflowInput(options: {
10
10
  projectContext: Phase5ControlPlaneOptions["projectContext"];
11
- scratchpad: ScratchpadProjection;
11
+ scratchpad: ScratchpadContentSnapshot;
12
12
  projectName: string;
13
13
  now: () => Date;
14
14
  }): ProjectContextBootstrapWorkflowInput | undefined;
@@ -16,7 +16,7 @@ export declare function projectContextBootstrapDocuments(output: ProjectContextB
16
16
  export declare function tryProjectContextBootstrapDocuments(input: {
17
17
  runner: ProcedureWorkflowRunner;
18
18
  projectContext: Phase5ControlPlaneOptions["projectContext"];
19
- scratchpad: ScratchpadProjection;
19
+ scratchpad: ScratchpadContentSnapshot;
20
20
  projectName: string;
21
21
  workflowRef: WorkflowInvocationRef;
22
22
  activityRef: ActivityRef;
@@ -1,9 +1,6 @@
1
1
  import { projectDocPath, readProjectDocsSummaries } from "../project-docs/context.js";
2
2
  import { projectContextBootstrapInvalidates } from "./invalidations.js";
3
3
  export function buildProjectDocsInitializationContext(scratchpad) {
4
- if (scratchpad.state !== "ready") {
5
- return { source: "scratchpad_submit" };
6
- }
7
4
  return {
8
5
  source: "scratchpad_submit",
9
6
  scratchpad: {
@@ -31,15 +28,13 @@ export function buildProjectContextBootstrapWorkflowInput(options) {
31
28
  if (missingDocs.length > 0) {
32
29
  input.missing_docs = missingDocs;
33
30
  }
34
- if (options.scratchpad.state === "ready") {
35
- input.scratchpad = {
36
- topic: options.scratchpad.topic,
37
- background_summary: options.scratchpad.background_summary,
38
- current_consensus: options.scratchpad.current_consensus,
39
- open_questions: options.scratchpad.open_questions,
40
- task_changes: options.scratchpad.task_changes,
41
- };
42
- }
31
+ input.scratchpad = {
32
+ topic: options.scratchpad.topic,
33
+ background_summary: options.scratchpad.background_summary,
34
+ current_consensus: options.scratchpad.current_consensus,
35
+ open_questions: options.scratchpad.open_questions,
36
+ task_changes: options.scratchpad.task_changes,
37
+ };
43
38
  return input;
44
39
  }
45
40
  export function projectContextBootstrapDocuments(output) {
@@ -110,7 +110,18 @@ export class ScratchpadAutoRefreshScheduler {
110
110
  if (this.options.readScratchpadRefreshBlocker() !== null) {
111
111
  return;
112
112
  }
113
- const sourceBatch = readControlPlaneScratchpadSourceBatch(this.options.store);
113
+ let sourceBatch;
114
+ try {
115
+ sourceBatch = readControlPlaneScratchpadSourceBatch(this.options.store);
116
+ }
117
+ catch {
118
+ markScratchpadRefreshFailed(this.options.store.db, {
119
+ backoffUntil: addMilliseconds(now, SCRATCHPAD_AUTO_REFRESH_FAILURE_BACKOFF_MS).toISOString(),
120
+ now: this.options.now,
121
+ });
122
+ this.scheduleNextAttempt();
123
+ return;
124
+ }
114
125
  if (sourceBatch.source_message_count === 0) {
115
126
  markScratchpadRefreshSucceeded(this.options.store.db, { now: this.options.now });
116
127
  this.scheduleNextAttempt();