@xfey/tutti 0.1.96 → 0.1.98

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 (74) 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/types.d.ts +12 -0
  9. package/dist/collaboration-state/worklist.d.ts +2 -1
  10. package/dist/collaboration-state/worklist.js +6 -0
  11. package/dist/control-plane/clarification-commands.d.ts +12 -8
  12. package/dist/control-plane/clarification-commands.js +29 -10
  13. package/dist/control-plane/dispatch-intents.d.ts +104 -0
  14. package/dist/control-plane/dispatch-intents.js +276 -0
  15. package/dist/control-plane/follow-up-run.d.ts +1 -1
  16. package/dist/control-plane/follow-up-run.js +12 -4
  17. package/dist/control-plane/follow-up-start.d.ts +3 -1
  18. package/dist/control-plane/follow-up-start.js +7 -2
  19. package/dist/control-plane/formatters.d.ts +2 -0
  20. package/dist/control-plane/formatters.js +6 -0
  21. package/dist/control-plane/index.d.ts +9 -0
  22. package/dist/control-plane/index.js +274 -13
  23. package/dist/control-plane/intent-dispatcher.d.ts +52 -0
  24. package/dist/control-plane/intent-dispatcher.js +184 -0
  25. package/dist/control-plane/reference-summary-refresh.d.ts +4 -1
  26. package/dist/control-plane/reference-summary-refresh.js +39 -4
  27. package/dist/control-plane/run-dispatch-intents.d.ts +15 -0
  28. package/dist/control-plane/run-dispatch-intents.js +40 -0
  29. package/dist/control-plane/run-retry.d.ts +1 -1
  30. package/dist/control-plane/run-retry.js +14 -6
  31. package/dist/control-plane/run-scheduler.d.ts +1 -1
  32. package/dist/control-plane/run-scheduler.js +12 -4
  33. package/dist/control-plane/scratchpad-refresh-start.d.ts +10 -2
  34. package/dist/control-plane/scratchpad-refresh-start.js +57 -8
  35. package/dist/control-plane/startup-recovery.d.ts +2 -2
  36. package/dist/control-plane/startup-recovery.js +73 -3
  37. package/dist/control-plane/task-compile-continuation.d.ts +3 -1
  38. package/dist/control-plane/task-compile-continuation.js +14 -9
  39. package/dist/control-plane/task-compile-start.d.ts +33 -1
  40. package/dist/control-plane/task-compile-start.js +244 -26
  41. package/dist/control-plane/types.d.ts +4 -2
  42. package/dist/control-plane/worklist-terminal-feedback.js +4 -4
  43. package/dist/server-shell/http/routes/agent-context-references.js +8 -14
  44. package/dist/server-shell/http/routes/project-api/openapi-viewer-routes.d.ts +0 -2
  45. package/dist/server-shell/http/routes/project-api/reference-files.js +6 -17
  46. package/dist/workspace-ops/constants.d.ts +0 -1
  47. package/dist/workspace-ops/constants.js +0 -1
  48. package/dist/workspace-ops/index.d.ts +3 -2
  49. package/dist/workspace-ops/index.js +3 -2
  50. package/dist/workspace-ops/project-doc-templates.js +1 -1
  51. package/dist/workspace-ops/reference-files.js +3 -2
  52. package/dist/workspace-ops/reference-metadata.d.ts +2 -2
  53. package/dist/workspace-ops/reference-metadata.js +8 -22
  54. package/dist/workspace-ops/reference-summaries.js +11 -17
  55. package/dist/workspace-ops/reference-tree.d.ts +11 -0
  56. package/dist/workspace-ops/reference-tree.js +34 -0
  57. package/dist/workspace-ops/viewer-git.d.ts +1 -0
  58. package/dist/workspace-ops/viewer-git.js +11 -0
  59. package/migrations/0015_control_plane_intents.sql +115 -0
  60. package/migrations/README.md +2 -1
  61. package/node_modules/@tutti/shared/dist/ids/index.d.ts +3 -0
  62. package/node_modules/@tutti/shared/dist/ids/index.js +2 -0
  63. package/node_modules/@tutti/shared/dist/schemas/api/viewer-reference.d.ts +0 -5
  64. package/node_modules/@tutti/shared/dist/schemas/api/viewer-reference.js +0 -2
  65. package/package.json +1 -1
  66. package/prompts/procedures/context-sync.md +3 -3
  67. package/prompts/procedures/reference-file-summary.md +1 -1
  68. package/prompts/procedures/task-compile.md +2 -2
  69. package/prompts/prompt-flow-map.md +1 -1
  70. package/prompts/runs/task-execute.md +2 -2
  71. package/prompts/skills/read-references/SKILL.md +2 -2
  72. package/web/assets/{homepage-motion-scene-BFo4r4nz.js → homepage-motion-scene-_nsq44Ph.js} +1 -1
  73. package/web/assets/{index-CNIl4TF8.js → index-BtTlbKEP.js} +3 -3
  74. package/web/index.html +1 -1
@@ -1,11 +1,13 @@
1
1
  import { createWorkflowInvocationRef, } from "@tutti/shared/ids";
2
- import { captureTaskCompileContext, readActiveTaskCompileContext, readCurrentScratchpadSourceRefs, readScratchpadProjection, readWorklistProjection, rollbackTaskCompileContext, } from "../collaboration-state/index.js";
2
+ import { captureTaskCompileContext, readActiveTaskCompileContext, readCurrentScratchpadSourceRefs, readPendingTaskCompileSubmission, readScratchpadProjection, readScratchpadSourceState, readWorklistProjection, recordTaskCompileSubmission, rollbackTaskCompileContext, } from "../collaboration-state/index.js";
3
+ import { withHostStoreTransaction } from "../store/index.js";
3
4
  import { initializeProjectDocs, readProjectDocsInitializationStatus, } from "../workspace-ops/index.js";
4
5
  import { logProcedureExecutionError } from "./procedure-logging.js";
5
6
  import { projectBriefShouldRefreshForUpdatedPaths, refreshProjectBriefProjection, } from "./project-brief-refresh.js";
6
7
  import { applyProjectDocsInitializationResult, buildProjectDocsInitializationContext, tryProjectContextBootstrapDocuments, } from "./project-context-bootstrap.js";
7
8
  import { readActiveScratchpadRefreshActivityRef, readControlPlaneScratchpadSourceBatch, runControlPlaneScratchpadRefreshProcedure, } from "./scratchpad-refresh-start.js";
8
9
  import { applyTaskCompileProcedureOutput } from "./task-compile-output.js";
10
+ import { enqueueControlPlaneIntent } from "./dispatch-intents.js";
9
11
  import { materializePromptSourceMessages } from "./task-source-context.js";
10
12
  import { materializeRecentReferences, readPendingRecentReferenceSummaryPaths, readRecentReferenceSources, } from "./recent-references.js";
11
13
  import { startReferenceSummaryRefreshProcedure } from "./reference-summary-refresh.js";
@@ -19,6 +21,15 @@ export function startControlPlaneTaskCompile(options) {
19
21
  },
20
22
  };
21
23
  }
24
+ const pendingSubmission = readPendingTaskCompileSubmission(options.store.db);
25
+ if (pendingSubmission !== null) {
26
+ return {
27
+ disposition: {
28
+ kind: "already_running",
29
+ activity_ref: pendingSubmission.accepted_activity_ref,
30
+ },
31
+ };
32
+ }
22
33
  const scratchpad = readScratchpadProjection(options.store.db);
23
34
  if (options.payload.expected_scratchpad_updated_at !== undefined &&
24
35
  options.payload.expected_scratchpad_updated_at !== scratchpad.updated_at) {
@@ -43,7 +54,10 @@ export function startControlPlaneTaskCompile(options) {
43
54
  };
44
55
  }
45
56
  const taskCompileWorkflowRef = createWorkflowInvocationRef();
46
- const sourceBatch = readControlPlaneScratchpadSourceBatch(options.store);
57
+ const sourceBatch = readControlPlaneScratchpadSourceBatch(options.store, {
58
+ ...(options.projectContext === undefined ? {} : { projectContext: options.projectContext }),
59
+ now: options.now,
60
+ });
47
61
  if (sourceBatch.source_message_count > 0) {
48
62
  const activeScratchpadRefresh = readActiveScratchpadRefreshActivityRef({
49
63
  engine: options.engine,
@@ -62,22 +76,70 @@ export function startControlPlaneTaskCompile(options) {
62
76
  runner: runnerResolution.runner,
63
77
  sourceBatch,
64
78
  taskCompileWorkflowRef,
79
+ recordRecoveryIntent: false,
80
+ onAccepted: (activityRef) => recordTaskCompileSubmissionIntent({
81
+ store: options.store,
82
+ workflowRef: taskCompileWorkflowRef,
83
+ acceptedActivityRef: activityRef,
84
+ sourceBatch,
85
+ now: options.now,
86
+ }),
65
87
  });
66
88
  }
67
89
  if (!scratchpadHasSubmittableContent(scratchpad)) {
68
90
  return { disposition: { kind: "scratchpad_empty" } };
69
91
  }
70
- return startReferenceSummaryGateOrNextStage({
92
+ const started = startReferenceSummaryGateOrNextStage({
71
93
  ...options,
72
94
  runner: runnerResolution.runner,
73
95
  taskCompileWorkflowRef,
74
96
  });
97
+ if (started.disposition.kind === "accepted") {
98
+ const activityRef = started.result?.activity_ref;
99
+ if (activityRef === undefined) {
100
+ throw new Error("Accepted Task Compile chain is missing activity identity");
101
+ }
102
+ recordTaskCompileSubmissionIntent({
103
+ store: options.store,
104
+ workflowRef: taskCompileWorkflowRef,
105
+ acceptedActivityRef: activityRef,
106
+ sourceBatch,
107
+ now: options.now,
108
+ });
109
+ }
110
+ return started;
111
+ }
112
+ function recordTaskCompileSubmissionIntent(options) {
113
+ return withHostStoreTransaction(options.store.db, (tx) => {
114
+ const sourceState = readScratchpadSourceState(tx);
115
+ const through = options.sourceBatch.cursor_update.refreshed_through_cursor ??
116
+ sourceState.refreshed_through_cursor;
117
+ recordTaskCompileSubmission(tx, {
118
+ workflow_ref: options.workflowRef,
119
+ accepted_activity_ref: options.acceptedActivityRef,
120
+ source_window: {
121
+ ...(sourceState.cycle_start_cursor === undefined
122
+ ? {}
123
+ : { after: sourceState.cycle_start_cursor }),
124
+ ...(through === undefined ? {} : { through }),
125
+ },
126
+ now: options.now,
127
+ });
128
+ return enqueueControlPlaneIntent(tx, {
129
+ kind: "task_compile_start",
130
+ ownerKind: "task_compile",
131
+ ownerRef: options.workflowRef,
132
+ dedupeKey: `task_compile_start:${options.workflowRef}`,
133
+ lane: "foreground",
134
+ priority: 30,
135
+ now: options.now,
136
+ }).intent.intent_id;
137
+ });
75
138
  }
76
139
  function startControlPlaneTaskCompileActivity(options) {
77
140
  const activeContext = readActiveTaskCompileContext(options.store.db, options.taskCompileWorkflowRef);
78
141
  const scratchpad = readScratchpadProjection(options.store.db);
79
- if (activeContext === null &&
80
- !scratchpadHasSubmittableContent(scratchpad)) {
142
+ if (activeContext === null && !scratchpadHasSubmittableContent(scratchpad)) {
81
143
  return { disposition: { kind: "scratchpad_empty" } };
82
144
  }
83
145
  const workflowRef = options.taskCompileWorkflowRef;
@@ -88,7 +150,14 @@ function startControlPlaneTaskCompileActivity(options) {
88
150
  title: "Compile Worklist",
89
151
  summary: "Compiling Scratchpad into Worklist tasks.",
90
152
  now: options.now,
91
- onTransition: options.publishProcedureTransition,
153
+ onTransition: (state) => {
154
+ options.publishProcedureTransition(state);
155
+ if (state.transition_kind === "terminal") {
156
+ options.onChainTerminal?.(state.activity.status === "failed"
157
+ ? { state: "failed", errorCode: "procedure_failed" }
158
+ : undefined);
159
+ }
160
+ },
92
161
  onError: ({ context, error }) => logProcedureExecutionError({
93
162
  logger: options.logger,
94
163
  workflowKind: "task_compile",
@@ -126,6 +195,7 @@ function startControlPlaneTaskCompileActivity(options) {
126
195
  }
127
196
  function startScratchpadRefreshBeforeTaskCompileActivity(options) {
128
197
  const workflowRef = createWorkflowInvocationRef();
198
+ let recoveryIntentId = options.recoveryIntentId;
129
199
  const start = options.engine.startProcedure({
130
200
  workflow_ref: workflowRef,
131
201
  workflow_kind: "scratchpad_refresh",
@@ -135,7 +205,14 @@ function startScratchpadRefreshBeforeTaskCompileActivity(options) {
135
205
  now: options.now,
136
206
  onTransition: (state) => {
137
207
  options.publishProcedureTransition(state);
138
- if (state.transition_kind !== "terminal" || state.activity.status !== "finished") {
208
+ if (state.transition_kind !== "terminal") {
209
+ return;
210
+ }
211
+ if (state.activity.status !== "finished") {
212
+ options.onChainTerminal?.({
213
+ state: "failed",
214
+ errorCode: "scratchpad_refresh_failed",
215
+ });
139
216
  return;
140
217
  }
141
218
  if (options.shouldContinue?.() === false) {
@@ -143,9 +220,19 @@ function startScratchpadRefreshBeforeTaskCompileActivity(options) {
143
220
  }
144
221
  const scratchpad = readScratchpadProjection(options.store.db);
145
222
  if (!scratchpadHasSubmittableContent(scratchpad)) {
223
+ options.onChainTerminal?.({
224
+ state: "failed",
225
+ errorCode: "scratchpad_empty_after_refresh",
226
+ });
146
227
  return;
147
228
  }
148
- void startReferenceSummaryGateOrNextStage(options);
229
+ const next = startReferenceSummaryGateOrNextStage(options);
230
+ if (next.disposition.kind !== "accepted" && next.disposition.kind !== "already_running") {
231
+ options.onChainTerminal?.({
232
+ state: "failed",
233
+ errorCode: "task_compile_stage_unavailable",
234
+ });
235
+ }
149
236
  },
150
237
  onError: ({ context, error }) => logProcedureExecutionError({
151
238
  logger: options.logger,
@@ -161,6 +248,11 @@ function startScratchpadRefreshBeforeTaskCompileActivity(options) {
161
248
  workflowRef,
162
249
  activityRef: context.activity_ref,
163
250
  sourceBatch: options.sourceBatch,
251
+ lane: "foreground",
252
+ ...(options.recordRecoveryIntent === undefined
253
+ ? {}
254
+ : { recordRecoveryIntent: options.recordRecoveryIntent }),
255
+ ...(recoveryIntentId === undefined ? {} : { recoveryIntentId }),
164
256
  now: options.now,
165
257
  }),
166
258
  });
@@ -172,6 +264,8 @@ function startScratchpadRefreshBeforeTaskCompileActivity(options) {
172
264
  },
173
265
  };
174
266
  }
267
+ recoveryIntentId =
268
+ options.onAccepted?.(start.activity.activity_ref) ?? recoveryIntentId;
175
269
  return {
176
270
  disposition: { kind: "accepted" },
177
271
  result: {
@@ -180,6 +274,116 @@ function startScratchpadRefreshBeforeTaskCompileActivity(options) {
180
274
  },
181
275
  };
182
276
  }
277
+ function cursorIncludes(current, required) {
278
+ if (required === undefined) {
279
+ return true;
280
+ }
281
+ if (current === undefined) {
282
+ return false;
283
+ }
284
+ const timeOrder = current.created_at.localeCompare(required.created_at);
285
+ return timeOrder > 0 || (timeOrder === 0 && current.message_id >= required.message_id);
286
+ }
287
+ export function resumePendingTaskCompileSubmission(options) {
288
+ const submission = readPendingTaskCompileSubmission(options.store.db, options.taskCompileWorkflowRef);
289
+ if (submission === null) {
290
+ return { kind: "obsolete", errorCode: "task_compile_submission_absent" };
291
+ }
292
+ const runnerResolution = options.resolveWorkflowRunner();
293
+ if (runnerResolution.kind !== "ready") {
294
+ return {
295
+ kind: "not_ready",
296
+ errorCode: runnerResolution.kind === "not_configured"
297
+ ? "provider_not_configured"
298
+ : "provider_unavailable",
299
+ };
300
+ }
301
+ const sourceState = readScratchpadSourceState(options.store.db);
302
+ if (cursorIncludes(sourceState.refreshed_through_cursor, submission.source_window.through)) {
303
+ const result = startReferenceSummaryGateOrNextStage({
304
+ ...options,
305
+ runner: runnerResolution.runner,
306
+ });
307
+ if (result.disposition.kind === "accepted") {
308
+ return { kind: "accepted" };
309
+ }
310
+ if (result.disposition.kind === "already_running") {
311
+ const activityRef = result.disposition.activity_ref;
312
+ const attached = options.engine
313
+ .getActiveProcedures()
314
+ .some((activity) => activity.activity_ref === activityRef &&
315
+ activity.workflow_kind === "reference_summary_refresh");
316
+ return attached ? { kind: "accepted" } : { kind: "busy" };
317
+ }
318
+ return { kind: "not_ready", errorCode: "task_compile_stage_unavailable" };
319
+ }
320
+ if (readActiveScratchpadRefreshActivityRef(options) !== undefined) {
321
+ return { kind: "busy" };
322
+ }
323
+ if (sourceState.backoff_until !== undefined &&
324
+ sourceState.backoff_until > options.now().toISOString()) {
325
+ return {
326
+ kind: "not_ready",
327
+ errorCode: "scratchpad_refresh_backoff",
328
+ availableAt: sourceState.backoff_until,
329
+ };
330
+ }
331
+ const sourceBatch = readControlPlaneScratchpadSourceBatch(options.store, {
332
+ ...(options.projectContext === undefined ? {} : { projectContext: options.projectContext }),
333
+ sourceWindow: submission.source_window,
334
+ now: options.now,
335
+ });
336
+ if (sourceBatch.source_message_count === 0) {
337
+ return {
338
+ kind: "not_ready",
339
+ errorCode: "task_compile_source_boundary_unavailable",
340
+ };
341
+ }
342
+ const result = startScratchpadRefreshBeforeTaskCompileActivity({
343
+ ...options,
344
+ runner: runnerResolution.runner,
345
+ sourceBatch,
346
+ taskCompileWorkflowRef: submission.workflow_ref,
347
+ recordRecoveryIntent: false,
348
+ recoveryIntentId: options.recoveryIntentId,
349
+ });
350
+ return result.disposition.kind === "accepted"
351
+ ? { kind: "accepted" }
352
+ : result.disposition.kind === "already_running"
353
+ ? { kind: "busy" }
354
+ : { kind: "not_ready", errorCode: "scratchpad_refresh_unavailable" };
355
+ }
356
+ export function resumeControlPlaneTaskCompileChain(options) {
357
+ if (readActiveTaskCompileContext(options.store.db, options.taskCompileWorkflowRef) === null) {
358
+ return { kind: "obsolete", errorCode: "task_compile_context_absent" };
359
+ }
360
+ const runnerResolution = options.resolveWorkflowRunner();
361
+ if (runnerResolution.kind !== "ready") {
362
+ return { kind: "not_ready", errorCode: "workflow_runner_unavailable" };
363
+ }
364
+ const result = startReferenceSummaryGateOrNextStage({
365
+ ...options,
366
+ runner: runnerResolution.runner,
367
+ });
368
+ if (result.disposition.kind === "accepted") {
369
+ return { kind: "accepted" };
370
+ }
371
+ if (result.disposition.kind === "already_running") {
372
+ const activityRef = result.disposition.activity_ref;
373
+ const attached = options.engine
374
+ .getActiveProcedures()
375
+ .some((activity) => activity.activity_ref === activityRef &&
376
+ activity.workflow_kind === "reference_summary_refresh");
377
+ return attached ? { kind: "accepted" } : { kind: "busy" };
378
+ }
379
+ if (result.disposition.kind === "provider_not_configured") {
380
+ return { kind: "not_ready", errorCode: "provider_not_configured" };
381
+ }
382
+ if (result.disposition.kind === "provider_unavailable") {
383
+ return { kind: "not_ready", errorCode: "provider_unavailable" };
384
+ }
385
+ return { kind: "not_ready", errorCode: "task_compile_stage_unavailable" };
386
+ }
183
387
  function startReferenceSummaryGateOrNextStage(options) {
184
388
  const sourceRefs = readActiveTaskCompileContext(options.store.db, options.taskCompileWorkflowRef)?.sources ??
185
389
  readCurrentScratchpadSourceRefs(options.store.db);
@@ -194,8 +398,9 @@ function startReferenceSummaryGateOrNextStage(options) {
194
398
  if (pendingPaths.length === 0) {
195
399
  return startNextTaskCompileStage(options);
196
400
  }
197
- const activeReferenceSummary = options.engine.getActiveProcedures().find((activity) => activity.lane === "foreground" &&
198
- activity.workflow_kind === "reference_summary_refresh");
401
+ const activeReferenceSummary = options.engine
402
+ .getActiveProcedures()
403
+ .find((activity) => activity.lane === "foreground" && activity.workflow_kind === "reference_summary_refresh");
199
404
  if (activeReferenceSummary !== undefined) {
200
405
  readOrCaptureTaskCompileContext({
201
406
  store: options.store,
@@ -218,10 +423,10 @@ function startReferenceSummaryGateOrNextStage(options) {
218
423
  paths: pendingPaths,
219
424
  resolveWorkflowRunner: () => ({ kind: "ready", runner: options.runner }),
220
425
  readActiveActivity: () => {
221
- const active = options.engine.getActiveProcedures().find((activity) => activity.lane === "foreground");
222
- return active === undefined
223
- ? null
224
- : { activity_ref: active.activity_ref };
426
+ const active = options.engine
427
+ .getActiveProcedures()
428
+ .find((activity) => activity.lane === "foreground");
429
+ return active === undefined ? null : { activity_ref: active.activity_ref };
225
430
  },
226
431
  projectContext: options.projectContext,
227
432
  logger: options.logger,
@@ -266,6 +471,7 @@ function registerReferenceSummaryGateWaiter(options, activityRef) {
266
471
  workflow_ref: options.taskCompileWorkflowRef,
267
472
  now: options.now,
268
473
  });
474
+ options.onChainTerminal?.({ state: "failed", errorCode: "reference_summary_failed" });
269
475
  return;
270
476
  }
271
477
  void startReferenceSummaryGateOrNextStage(options);
@@ -301,7 +507,11 @@ function startProjectContextBootstrapActivity(options) {
301
507
  now: options.now,
302
508
  onTransition: (state) => {
303
509
  options.publishProcedureTransition(state);
304
- if (state.transition_kind !== "terminal" || state.activity.status !== "finished") {
510
+ if (state.transition_kind !== "terminal") {
511
+ return;
512
+ }
513
+ if (state.activity.status !== "finished") {
514
+ options.onChainTerminal?.({ state: "failed", errorCode: "project_context_failed" });
305
515
  return;
306
516
  }
307
517
  if (options.shouldContinue?.() === false) {
@@ -434,15 +644,11 @@ export async function runControlPlaneTaskCompileProcedure(options) {
434
644
  scratchpad: context.scratchpad_snapshot,
435
645
  source_messages: materializePromptSourceMessages(options.store.db, context.sources),
436
646
  worklist: readWorklistProjection(options.store.db),
437
- ...(recentReferences.length === 0
438
- ? {}
439
- : { recent_references: recentReferences }),
647
+ ...(recentReferences.length === 0 ? {} : { recent_references: recentReferences }),
440
648
  }, {
441
649
  workflow_ref: options.workflowRef,
442
650
  activity_ref: options.activityRef,
443
- ...(options.reportProgress === undefined
444
- ? {}
445
- : { reportProgress: options.reportProgress }),
651
+ ...(options.reportProgress === undefined ? {} : { reportProgress: options.reportProgress }),
446
652
  });
447
653
  return applyTaskCompileProcedureOutput({
448
654
  store: options.store,
@@ -463,12 +669,24 @@ export async function runControlPlaneTaskCompileProcedure(options) {
463
669
  }
464
670
  }
465
671
  function readOrCaptureTaskCompileContext(input) {
466
- return (readActiveTaskCompileContext(input.store.db, input.workflowRef) ??
467
- captureTaskCompileContext(input.store.db, {
468
- workflow_ref: input.workflowRef,
469
- captured_by_activity_ref: input.activityRef,
672
+ return withHostStoreTransaction(input.store.db, (tx) => {
673
+ const context = readActiveTaskCompileContext(tx, input.workflowRef) ??
674
+ captureTaskCompileContext(tx, {
675
+ workflow_ref: input.workflowRef,
676
+ captured_by_activity_ref: input.activityRef,
677
+ now: input.now,
678
+ });
679
+ enqueueControlPlaneIntent(tx, {
680
+ kind: "task_compile_start",
681
+ ownerKind: "task_compile",
682
+ ownerRef: input.workflowRef,
683
+ dedupeKey: `task_compile_start:${input.workflowRef}`,
684
+ lane: "foreground",
685
+ priority: 30,
470
686
  now: input.now,
471
- }));
687
+ });
688
+ return context;
689
+ });
472
690
  }
473
691
  function scratchpadHasSubmittableContent(scratchpad) {
474
692
  if (scratchpad.state !== "ready") {
@@ -52,6 +52,8 @@ export type StartupRecoveryResult = {
52
52
  replayed_round_ids: ClarificationRoundRef[];
53
53
  interrupted_command_count: number;
54
54
  stale_replay_hint_count: number;
55
+ recovered_task_compile_count: number;
56
+ recovered_scratchpad_refresh_count: number;
55
57
  };
56
58
  export type RunDispatchTrigger = "auto_idle" | "task_arrival" | "manual_run_now" | "continuation" | "recovery";
57
59
  export type RunDispatchContext = {
@@ -128,7 +130,7 @@ export type RunPipelineStageProgress = {
128
130
  summary: string;
129
131
  runtime?: ExecutionRuntimeSnapshot;
130
132
  };
131
- export type RunPipelineResult = ({
133
+ export type RunPipelineResult = {
132
134
  result_kind: "needs_human";
133
135
  summary: string;
134
136
  clarification_request_payload: ClarificationRequestPayload;
@@ -152,7 +154,7 @@ export type RunPipelineResult = ({
152
154
  retryable: boolean;
153
155
  changed_paths?: string[];
154
156
  promotion?: NonNullable<TaskDetailProjection["detail"]["result"]["promotion"]>;
155
- });
157
+ };
156
158
  export type RunPipelineRunner = {
157
159
  runTask: (input: RunPipelineInput) => Promise<RunPipelineResult>;
158
160
  };
@@ -1,4 +1,4 @@
1
- import { readViewerFile, referenceFileSourceForPath, referenceMediaTypeForPath, referenceTuttiGroupForPath, } from "../workspace-ops/index.js";
1
+ import { isReferenceFilePath, readViewerFile, referenceFolderGroupForPath, referenceMediaTypeForPath, } from "../workspace-ops/index.js";
2
2
  import { createMainChatAgentMessage, hasMainChatTaskResultMessage, hasMainChatWorklistFeedbackMessage, readCompletedWorklistFeedbackTaskIds, readTaskDetailProjection, readWorklistProjection, } from "../collaboration-state/index.js";
3
3
  import { mainChatInvalidates } from "./invalidations.js";
4
4
  const BODY_ITEM_LIMIT = 12;
@@ -62,7 +62,7 @@ function generatedFileProjection(file) {
62
62
  name: file.name,
63
63
  size_bytes: file.size_bytes,
64
64
  };
65
- const group = referenceTuttiGroupForPath(file.path);
65
+ const group = referenceFolderGroupForPath(file.path);
66
66
  if (group !== undefined) {
67
67
  projection.group = group;
68
68
  }
@@ -73,7 +73,7 @@ function generatedFileProjection(file) {
73
73
  return projection;
74
74
  }
75
75
  function readGeneratedFile(input) {
76
- if (referenceFileSourceForPath(input.path) !== "tutti_output") {
76
+ if (!isReferenceFilePath(input.path)) {
77
77
  return null;
78
78
  }
79
79
  try {
@@ -100,7 +100,7 @@ function generatedFilesForCompletedEntries(input, entries) {
100
100
  continue;
101
101
  }
102
102
  for (const path of entry.detail?.detail.result.changed_paths ?? []) {
103
- if (referenceFileSourceForPath(path) === "tutti_output") {
103
+ if (isReferenceFilePath(path)) {
104
104
  paths.add(path);
105
105
  }
106
106
  }
@@ -1,4 +1,4 @@
1
- import { REFERENCE_DIRECTORY_PATH, REFERENCE_INDEX_PATH, WorkspaceOpsError, readReferenceSummaryIndex, readViewerFile, readViewerTree, referenceFileCategoryForPath, referenceMediaTypeForPath, referenceSummaryProjectionForEntry, } from "../../../workspace-ops/index.js";
1
+ import { REFERENCE_INDEX_PATH, WorkspaceOpsError, readReferenceFileEntries, readReferenceSummaryIndex, readViewerFile, referenceFileCategoryForPath, referenceMediaTypeForPath, referenceSummaryProjectionForEntry, } from "../../../workspace-ops/index.js";
2
2
  import { AgentContextBadRequestError } from "./agent-context-errors.js";
3
3
  function requireWorkspaceRoot(options) {
4
4
  if (options.workspaceOps === undefined) {
@@ -46,14 +46,11 @@ function unavailableReasonForContent(content) {
46
46
  }
47
47
  function referenceEntries(options) {
48
48
  const workspaceRoot = requireWorkspaceRoot(options);
49
- let tree;
50
49
  try {
51
- tree = readViewerTree({
52
- workspaceRoot,
53
- path: REFERENCE_DIRECTORY_PATH,
54
- depth: 2,
55
- ...(options.now === undefined ? {} : { now: options.now }),
56
- });
50
+ return readReferenceFileEntries({ workspaceRoot })
51
+ .filter((entry) => entry.path !== REFERENCE_INDEX_PATH)
52
+ .filter((entry) => referenceFileCategoryForPath(entry.path) !== undefined)
53
+ .sort((left, right) => left.path.localeCompare(right.path, "en", { sensitivity: "base" }));
57
54
  }
58
55
  catch (error) {
59
56
  if (error instanceof WorkspaceOpsError && error.code === "not_found") {
@@ -61,11 +58,6 @@ function referenceEntries(options) {
61
58
  }
62
59
  throw error;
63
60
  }
64
- return tree.items
65
- .filter((entry) => entry.kind === "file")
66
- .filter((entry) => entry.path !== REFERENCE_INDEX_PATH)
67
- .filter((entry) => referenceFileCategoryForPath(entry.path) !== undefined)
68
- .sort((left, right) => left.path.localeCompare(right.path, "en", { sensitivity: "base" }));
69
61
  }
70
62
  function canReadReferenceText(options, path) {
71
63
  try {
@@ -76,7 +68,9 @@ function canReadReferenceText(options, path) {
76
68
  ...(options.now === undefined ? {} : { now: options.now }),
77
69
  });
78
70
  const reason = unavailableReasonForContent(file.content);
79
- return reason === undefined ? { can_read_text: true } : { can_read_text: false, blocked_reason: reason };
71
+ return reason === undefined
72
+ ? { can_read_text: true }
73
+ : { can_read_text: false, blocked_reason: reason };
80
74
  }
81
75
  catch {
82
76
  return { can_read_text: false, blocked_reason: "restricted_path" };
@@ -283,14 +283,12 @@ export declare const HOST_PROJECT_VIEWER_OPENAPI_ROUTES: ({
283
283
  index_path: import("@sinclair/typebox").TLiteral<"docs/reference/README.md">;
284
284
  files_path: import("@sinclair/typebox").TLiteral<"docs/reference/files">;
285
285
  images_path: import("@sinclair/typebox").TLiteral<"docs/reference/images">;
286
- tutti_path: import("@sinclair/typebox").TLiteral<"docs/reference/tutti">;
287
286
  index_status: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"available">, import("@sinclair/typebox").TLiteral<"missing">, import("@sinclair/typebox").TLiteral<"unreadable">]>;
288
287
  index_unreadable_reason: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"restricted_path">, import("@sinclair/typebox").TLiteral<"secret_like">, import("@sinclair/typebox").TLiteral<"binary">, import("@sinclair/typebox").TLiteral<"unsupported_encoding">, import("@sinclair/typebox").TLiteral<"too_large">]>>;
289
288
  }>;
290
289
  items: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
291
290
  path: import("@sinclair/typebox").TString;
292
291
  name: import("@sinclair/typebox").TString;
293
- source: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"human_upload">, import("@sinclair/typebox").TLiteral<"tutti_output">]>;
294
292
  category: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"file">, import("@sinclair/typebox").TLiteral<"image">]>;
295
293
  size_bytes: import("@sinclair/typebox").TInteger;
296
294
  media_type: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
@@ -1,4 +1,4 @@
1
- import { REFERENCE_DIRECTORY_PATH, REFERENCE_FILES_DIRECTORY_PATH, REFERENCE_IMAGES_DIRECTORY_PATH, REFERENCE_INDEX_PATH, REFERENCE_TUTTI_DIRECTORY_PATH, VIEWER_HARD_READ_LIMIT_BYTES, WorkspaceOpsError, readViewerFile, readViewerSnapshot, readViewerTree, readReferenceSummaryIndex, referenceFileCategoryForPath, referenceFileSourceForPath, referenceMediaTypeForPath, referenceSummaryProjectionForEntry, } from "../../../../workspace-ops/index.js";
1
+ import { REFERENCE_DIRECTORY_PATH, REFERENCE_FILES_DIRECTORY_PATH, REFERENCE_IMAGES_DIRECTORY_PATH, REFERENCE_INDEX_PATH, VIEWER_HARD_READ_LIMIT_BYTES, WorkspaceOpsError, readReferenceFileEntries, readReferenceSummaryIndex, readViewerFile, readViewerSnapshot, referenceFileCategoryForPath, referenceMediaTypeForPath, referenceSummaryProjectionForEntry, } from "../../../../workspace-ops/index.js";
2
2
  import { paginateViewerItems, viewerPageLimit } from "./viewer-projections.js";
3
3
  function unreadableReasonForReferenceContent(content) {
4
4
  if (content.kind === "text") {
@@ -34,7 +34,6 @@ export function toReferenceFileProjection(options) {
34
34
  path: options.entry.path,
35
35
  name: options.entry.name,
36
36
  category,
37
- source: referenceFileSourceForPath(options.entry.path) ?? "human_upload",
38
37
  size_bytes: options.entry.size_bytes ?? 0,
39
38
  text_read: referenceTextReadProjection(options.workspaceRoot, options.entry, options.now),
40
39
  summary: referenceSummaryProjectionForEntry({
@@ -65,7 +64,6 @@ function referenceDirectoryProjection(options) {
65
64
  index_path: REFERENCE_INDEX_PATH,
66
65
  files_path: REFERENCE_FILES_DIRECTORY_PATH,
67
66
  images_path: REFERENCE_IMAGES_DIRECTORY_PATH,
68
- tutti_path: REFERENCE_TUTTI_DIRECTORY_PATH,
69
67
  index_status: "missing",
70
68
  };
71
69
  }
@@ -82,7 +80,6 @@ function referenceDirectoryProjection(options) {
82
80
  index_path: REFERENCE_INDEX_PATH,
83
81
  files_path: REFERENCE_FILES_DIRECTORY_PATH,
84
82
  images_path: REFERENCE_IMAGES_DIRECTORY_PATH,
85
- tutti_path: REFERENCE_TUTTI_DIRECTORY_PATH,
86
83
  index_status: "available",
87
84
  };
88
85
  }
@@ -92,7 +89,6 @@ function referenceDirectoryProjection(options) {
92
89
  index_path: REFERENCE_INDEX_PATH,
93
90
  files_path: REFERENCE_FILES_DIRECTORY_PATH,
94
91
  images_path: REFERENCE_IMAGES_DIRECTORY_PATH,
95
- tutti_path: REFERENCE_TUTTI_DIRECTORY_PATH,
96
92
  index_status: "unreadable",
97
93
  index_unreadable_reason: file.content.reason,
98
94
  };
@@ -105,7 +101,6 @@ function referenceDirectoryProjection(options) {
105
101
  index_path: REFERENCE_INDEX_PATH,
106
102
  files_path: REFERENCE_FILES_DIRECTORY_PATH,
107
103
  images_path: REFERENCE_IMAGES_DIRECTORY_PATH,
108
- tutti_path: REFERENCE_TUTTI_DIRECTORY_PATH,
109
104
  index_status: "missing",
110
105
  };
111
106
  }
@@ -119,14 +114,9 @@ export function readReferenceFilesResponse(options, query) {
119
114
  workspaceRoot,
120
115
  ...(now === undefined ? {} : { now }),
121
116
  });
122
- let tree;
117
+ let entries;
123
118
  try {
124
- tree = readViewerTree({
125
- workspaceRoot,
126
- path: REFERENCE_DIRECTORY_PATH,
127
- depth: 3,
128
- ...(now === undefined ? {} : { now }),
129
- });
119
+ entries = readReferenceFileEntries({ workspaceRoot });
130
120
  }
131
121
  catch (error) {
132
122
  if (error instanceof WorkspaceOpsError && error.code === "not_found") {
@@ -148,11 +138,10 @@ export function readReferenceFilesResponse(options, query) {
148
138
  }
149
139
  const includeIndex = query.include_index ?? false;
150
140
  const summaryIndex = readReferenceSummaryIndex({ workspaceRoot });
151
- const referenceItems = tree.items
152
- .filter((entry) => entry.kind === "file")
141
+ const referenceItems = entries
153
142
  .filter((entry) => includeIndex || entry.path !== REFERENCE_INDEX_PATH)
154
143
  .filter((entry) => entry.path === REFERENCE_INDEX_PATH ||
155
- referenceFileSourceForPath(entry.path) !== undefined)
144
+ referenceFileCategoryForPath(entry.path) !== undefined)
156
145
  .sort((left, right) => {
157
146
  const leftCategory = referenceFileCategoryForPath(left.path) ?? "file";
158
147
  const rightCategory = referenceFileCategoryForPath(right.path) ?? "file";
@@ -202,7 +191,7 @@ export function referenceFileMessageRef(file) {
202
191
  const ref = {
203
192
  path: file.path,
204
193
  name: file.name,
205
- source: file.source,
194
+ source: "human_upload",
206
195
  category: file.category,
207
196
  size_bytes: file.size_bytes,
208
197
  text_read: file.text_read,
@@ -5,7 +5,6 @@ export declare const VIEWER_HARD_READ_LIMIT_BYTES: number;
5
5
  export declare const REFERENCE_DIRECTORY_PATH: "docs/reference";
6
6
  export declare const REFERENCE_FILES_DIRECTORY_PATH: "docs/reference/files";
7
7
  export declare const REFERENCE_IMAGES_DIRECTORY_PATH: "docs/reference/images";
8
- export declare const REFERENCE_TUTTI_DIRECTORY_PATH: "docs/reference/tutti";
9
8
  export declare const REFERENCE_INDEX_PATH: "docs/reference/README.md";
10
9
  export declare const MAX_REFERENCE_UPLOAD_BYTES: number;
11
10
  export declare const VIEWER_ASSET_READ_LIMIT_BYTES: number;
@@ -5,7 +5,6 @@ export const VIEWER_HARD_READ_LIMIT_BYTES = 5 * 1024 * 1024;
5
5
  export const REFERENCE_DIRECTORY_PATH = "docs/reference";
6
6
  export const REFERENCE_FILES_DIRECTORY_PATH = "docs/reference/files";
7
7
  export const REFERENCE_IMAGES_DIRECTORY_PATH = "docs/reference/images";
8
- export const REFERENCE_TUTTI_DIRECTORY_PATH = "docs/reference/tutti";
9
8
  export const REFERENCE_INDEX_PATH = "docs/reference/README.md";
10
9
  export const MAX_REFERENCE_UPLOAD_BYTES = 10 * 1024 * 1024;
11
10
  export const VIEWER_ASSET_READ_LIMIT_BYTES = MAX_REFERENCE_UPLOAD_BYTES;
@@ -1,10 +1,11 @@
1
- export { DEFAULT_VIEWER_MAX_BYTES, MAX_REFERENCE_UPLOAD_BYTES, MAX_VIEWER_MAX_BYTES, PROJECT_DOC_PATHS, REFERENCE_IMAGE_ASSET_CACHE_VERSION, REFERENCE_IMAGE_PREVIEW_MAX_EDGE_PX, REFERENCE_IMAGE_THUMBNAIL_MAX_EDGE_PX, REFERENCE_DIRECTORY_PATH, REFERENCE_FILES_DIRECTORY_PATH, REFERENCE_IMAGES_DIRECTORY_PATH, REFERENCE_INDEX_PATH, REFERENCE_TUTTI_DIRECTORY_PATH, VIEWER_ASSET_READ_LIMIT_BYTES, VIEWER_HARD_READ_LIMIT_BYTES, WORKSPACE_OPS_MAINLINE_BRANCH, } from "./constants.js";
1
+ export { DEFAULT_VIEWER_MAX_BYTES, MAX_REFERENCE_UPLOAD_BYTES, MAX_VIEWER_MAX_BYTES, PROJECT_DOC_PATHS, REFERENCE_IMAGE_ASSET_CACHE_VERSION, REFERENCE_IMAGE_PREVIEW_MAX_EDGE_PX, REFERENCE_IMAGE_THUMBNAIL_MAX_EDGE_PX, REFERENCE_DIRECTORY_PATH, REFERENCE_FILES_DIRECTORY_PATH, REFERENCE_IMAGES_DIRECTORY_PATH, REFERENCE_INDEX_PATH, VIEWER_ASSET_READ_LIMIT_BYTES, VIEWER_HARD_READ_LIMIT_BYTES, WORKSPACE_OPS_MAINLINE_BRANCH, } from "./constants.js";
2
2
  export { WorkspaceOpsError } from "./errors.js";
3
3
  export { commitOwnedPaths, requireOwnedPathsWriteReady } from "./system-commits.js";
4
4
  export { collectProjectSourceDiff, createAcceptedCheckpoint, inspectProjectWorkspace, recoverAcceptedCheckpoint, } from "./project-workspace.js";
5
5
  export { initializeProjectDocs, readProjectDocsInitializationStatus, syncProjectDocs, } from "./project-docs.js";
6
6
  export { uploadReferenceFile } from "./reference-files.js";
7
- export { referenceFileCategoryForPath, referenceFileSourceForPath, referenceMediaTypeForPath, referenceTuttiGroupForPath, } from "./reference-metadata.js";
7
+ export { isReferenceFilePath, referenceFileCategoryForPath, referenceFolderGroupForPath, referenceMediaTypeForPath, } from "./reference-metadata.js";
8
+ export { readReferenceFileEntries } from "./reference-tree.js";
8
9
  export { readReferenceSummaryEntries, readReferenceSummaryIndex, readReferenceSummaryTargets, referenceSummaryProjectionForEntry, updateReferenceSummaries, } from "./reference-summaries.js";
9
10
  export { readViewerAsset, readViewerFile, readViewerSnapshot, readViewerTree } from "./viewer.js";
10
11
  export type { WorkspaceOpsErrorCode } from "./errors.js";