@xfey/tutti 0.1.96 → 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 (49) 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 +260 -11
  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/migrations/0015_control_plane_intents.sql +115 -0
  43. package/migrations/README.md +2 -1
  44. package/node_modules/@tutti/shared/dist/ids/index.d.ts +3 -0
  45. package/node_modules/@tutti/shared/dist/ids/index.js +2 -0
  46. package/package.json +1 -1
  47. package/web/assets/{homepage-motion-scene-BFo4r4nz.js → homepage-motion-scene-lpr-1FjJ.js} +1 -1
  48. package/web/assets/{index-CNIl4TF8.js → index-CQoq2PtU.js} +2 -2
  49. package/web/index.html +1 -1
@@ -0,0 +1,276 @@
1
+ import { createControlPlaneIntentId } from "@tutti/shared/ids";
2
+ export const CONTROL_PLANE_INTENT_KINDS = [
3
+ "task_compile_start",
4
+ "task_compile_continuation",
5
+ "task_bound_follow_up",
6
+ "scratchpad_refresh",
7
+ "reference_summary_refresh",
8
+ "run_dispatch",
9
+ ];
10
+ export const CONTROL_PLANE_INTENT_OWNER_KINDS = [
11
+ "task_compile",
12
+ "clarification_round",
13
+ "scratchpad",
14
+ "reference_summary",
15
+ "task_run",
16
+ ];
17
+ const CONTROL_PLANE_INTENT_COLUMNS = `
18
+ intent_id,
19
+ kind,
20
+ owner_kind,
21
+ owner_ref,
22
+ dedupe_key,
23
+ lane,
24
+ priority,
25
+ state,
26
+ available_at,
27
+ claim_token,
28
+ lease_expires_at,
29
+ attempt_count,
30
+ last_error_code,
31
+ created_at,
32
+ updated_at,
33
+ completed_at
34
+ `;
35
+ function nowIso(now) {
36
+ return (now?.() ?? new Date()).toISOString();
37
+ }
38
+ function mapIntentRow(row) {
39
+ return {
40
+ intent_id: row.intent_id,
41
+ kind: row.kind,
42
+ owner_kind: row.owner_kind,
43
+ owner_ref: row.owner_ref,
44
+ dedupe_key: row.dedupe_key,
45
+ lane: row.lane,
46
+ priority: row.priority,
47
+ state: row.state,
48
+ available_at: row.available_at,
49
+ attempt_count: row.attempt_count,
50
+ created_at: row.created_at,
51
+ updated_at: row.updated_at,
52
+ ...(row.claim_token === null ? {} : { claim_token: row.claim_token }),
53
+ ...(row.lease_expires_at === null ? {} : { lease_expires_at: row.lease_expires_at }),
54
+ ...(row.last_error_code === null ? {} : { last_error_code: row.last_error_code }),
55
+ ...(row.completed_at === null ? {} : { completed_at: row.completed_at }),
56
+ };
57
+ }
58
+ export function readControlPlaneIntent(db, intentId) {
59
+ const row = db
60
+ .prepare(`SELECT ${CONTROL_PLANE_INTENT_COLUMNS} FROM control_plane_intents WHERE intent_id = ?`)
61
+ .get(intentId);
62
+ return row === undefined ? null : mapIntentRow(row);
63
+ }
64
+ export function readControlPlaneIntentByDedupeKey(db, dedupeKey) {
65
+ const row = db
66
+ .prepare(`SELECT ${CONTROL_PLANE_INTENT_COLUMNS} FROM control_plane_intents WHERE dedupe_key = ?`)
67
+ .get(dedupeKey);
68
+ return row === undefined ? null : mapIntentRow(row);
69
+ }
70
+ export function enqueueControlPlaneIntent(db, input) {
71
+ const createdAt = nowIso(input.now);
72
+ const intentId = createControlPlaneIntentId();
73
+ const result = db
74
+ .prepare(`
75
+ INSERT INTO control_plane_intents (
76
+ intent_id,
77
+ kind,
78
+ owner_kind,
79
+ owner_ref,
80
+ dedupe_key,
81
+ lane,
82
+ priority,
83
+ state,
84
+ available_at,
85
+ attempt_count,
86
+ created_at,
87
+ updated_at
88
+ )
89
+ VALUES (?, ?, ?, ?, ?, ?, ?, 'pending', ?, 0, ?, ?)
90
+ ON CONFLICT(dedupe_key) DO NOTHING
91
+ `)
92
+ .run(intentId, input.kind, input.ownerKind, input.ownerRef, input.dedupeKey, input.lane, input.priority ?? 100, input.availableAt ?? createdAt, createdAt, createdAt);
93
+ const intent = readControlPlaneIntentByDedupeKey(db, input.dedupeKey);
94
+ if (intent === null) {
95
+ throw new Error("Control Plane intent enqueue did not materialize a durable row");
96
+ }
97
+ return { intent, created: result.changes === 1 };
98
+ }
99
+ export function listPendingControlPlaneIntents(db, input = {}) {
100
+ const rows = db
101
+ .prepare(`
102
+ SELECT ${CONTROL_PLANE_INTENT_COLUMNS}
103
+ FROM control_plane_intents
104
+ WHERE state = 'pending' AND available_at <= ?
105
+ ORDER BY priority ASC, available_at ASC, created_at ASC, intent_id ASC
106
+ LIMIT ?
107
+ `)
108
+ .all(nowIso(input.now), input.limit ?? 100);
109
+ return rows.map(mapIntentRow);
110
+ }
111
+ export function listNonTerminalControlPlaneIntentsByKind(db, kind) {
112
+ const rows = db
113
+ .prepare(`
114
+ SELECT ${CONTROL_PLANE_INTENT_COLUMNS}
115
+ FROM control_plane_intents
116
+ WHERE kind = ? AND state IN ('pending', 'claimed', 'running')
117
+ ORDER BY created_at ASC, intent_id ASC
118
+ `)
119
+ .all(kind);
120
+ return rows.map(mapIntentRow);
121
+ }
122
+ export function listExpiredControlPlaneIntentClaims(db, input = {}) {
123
+ const rows = db
124
+ .prepare(`
125
+ SELECT ${CONTROL_PLANE_INTENT_COLUMNS}
126
+ FROM control_plane_intents
127
+ WHERE state IN ('claimed', 'running') AND lease_expires_at <= ?
128
+ ORDER BY lease_expires_at ASC, intent_id ASC
129
+ LIMIT ?
130
+ `)
131
+ .all(nowIso(input.now), input.limit ?? 100);
132
+ return rows.map(mapIntentRow);
133
+ }
134
+ export function listActiveControlPlaneIntentClaims(db) {
135
+ const rows = db
136
+ .prepare(`
137
+ SELECT ${CONTROL_PLANE_INTENT_COLUMNS}
138
+ FROM control_plane_intents
139
+ WHERE state IN ('claimed', 'running')
140
+ ORDER BY updated_at ASC, intent_id ASC
141
+ `)
142
+ .all();
143
+ return rows.map(mapIntentRow);
144
+ }
145
+ export function claimControlPlaneIntent(db, input) {
146
+ const updatedAt = nowIso(input.now);
147
+ const row = db
148
+ .prepare(`
149
+ UPDATE control_plane_intents
150
+ SET
151
+ state = 'claimed',
152
+ claim_token = ?,
153
+ lease_expires_at = ?,
154
+ attempt_count = attempt_count + 1,
155
+ last_error_code = NULL,
156
+ updated_at = ?
157
+ WHERE intent_id = ? AND state = 'pending' AND available_at <= ?
158
+ RETURNING ${CONTROL_PLANE_INTENT_COLUMNS}
159
+ `)
160
+ .get(input.claimToken, input.leaseExpiresAt, updatedAt, input.intentId, updatedAt);
161
+ return row === undefined ? null : mapIntentRow(row);
162
+ }
163
+ export function markControlPlaneIntentRunning(db, input) {
164
+ const updatedAt = nowIso(input.now);
165
+ const row = db
166
+ .prepare(`
167
+ UPDATE control_plane_intents
168
+ SET state = 'running', lease_expires_at = ?, updated_at = ?
169
+ WHERE intent_id = ? AND state = 'claimed' AND claim_token = ?
170
+ RETURNING ${CONTROL_PLANE_INTENT_COLUMNS}
171
+ `)
172
+ .get(input.leaseExpiresAt, updatedAt, input.intentId, input.claimToken);
173
+ return row === undefined ? null : mapIntentRow(row);
174
+ }
175
+ export function renewControlPlaneIntentLease(db, input) {
176
+ const result = db
177
+ .prepare(`
178
+ UPDATE control_plane_intents
179
+ SET lease_expires_at = ?, updated_at = ?
180
+ WHERE intent_id = ? AND state = 'running' AND claim_token = ?
181
+ `)
182
+ .run(input.leaseExpiresAt, nowIso(input.now), input.intentId, input.claimToken);
183
+ return result.changes === 1;
184
+ }
185
+ export function releaseControlPlaneIntentClaim(db, input) {
186
+ const updatedAt = nowIso(input.now);
187
+ const row = db
188
+ .prepare(`
189
+ UPDATE control_plane_intents
190
+ SET
191
+ state = 'pending',
192
+ available_at = ?,
193
+ claim_token = NULL,
194
+ lease_expires_at = NULL,
195
+ last_error_code = ?,
196
+ updated_at = ?
197
+ WHERE intent_id = ? AND state = 'claimed' AND claim_token = ?
198
+ RETURNING ${CONTROL_PLANE_INTENT_COLUMNS}
199
+ `)
200
+ .get(input.availableAt ?? updatedAt, input.errorCode ?? null, updatedAt, input.intentId, input.claimToken);
201
+ return row === undefined ? null : mapIntentRow(row);
202
+ }
203
+ export function requeueExpiredControlPlaneIntentClaim(db, input) {
204
+ const updatedAt = nowIso(input.now);
205
+ const row = db
206
+ .prepare(`
207
+ UPDATE control_plane_intents
208
+ SET
209
+ state = 'pending',
210
+ available_at = ?,
211
+ claim_token = NULL,
212
+ lease_expires_at = NULL,
213
+ last_error_code = ?,
214
+ updated_at = ?
215
+ WHERE
216
+ intent_id = ?
217
+ AND state IN ('claimed', 'running')
218
+ AND claim_token = ?
219
+ AND lease_expires_at <= ?
220
+ RETURNING ${CONTROL_PLANE_INTENT_COLUMNS}
221
+ `)
222
+ .get(input.availableAt ?? updatedAt, input.errorCode, updatedAt, input.intentId, input.expectedClaimToken, updatedAt);
223
+ return row === undefined ? null : mapIntentRow(row);
224
+ }
225
+ export function requeueOrphanedControlPlaneIntentClaim(db, input) {
226
+ const updatedAt = nowIso(input.now);
227
+ const row = db
228
+ .prepare(`
229
+ UPDATE control_plane_intents
230
+ SET
231
+ state = 'pending',
232
+ available_at = ?,
233
+ claim_token = NULL,
234
+ lease_expires_at = NULL,
235
+ last_error_code = ?,
236
+ updated_at = ?
237
+ WHERE
238
+ intent_id = ?
239
+ AND state IN ('claimed', 'running')
240
+ AND claim_token = ?
241
+ RETURNING ${CONTROL_PLANE_INTENT_COLUMNS}
242
+ `)
243
+ .get(updatedAt, input.errorCode, updatedAt, input.intentId, input.expectedClaimToken);
244
+ return row === undefined ? null : mapIntentRow(row);
245
+ }
246
+ export function settleClaimedControlPlaneIntent(db, input) {
247
+ const completedAt = nowIso(input.now);
248
+ const row = db
249
+ .prepare(`
250
+ UPDATE control_plane_intents
251
+ SET
252
+ state = ?,
253
+ claim_token = NULL,
254
+ lease_expires_at = NULL,
255
+ last_error_code = ?,
256
+ updated_at = ?,
257
+ completed_at = ?
258
+ WHERE intent_id = ? AND state IN ('claimed', 'running') AND claim_token = ?
259
+ RETURNING ${CONTROL_PLANE_INTENT_COLUMNS}
260
+ `)
261
+ .get(input.state, input.errorCode ?? null, completedAt, completedAt, input.intentId, input.claimToken);
262
+ return row === undefined ? null : mapIntentRow(row);
263
+ }
264
+ export function settlePendingControlPlaneIntent(db, input) {
265
+ const completedAt = nowIso(input.now);
266
+ const row = db
267
+ .prepare(`
268
+ UPDATE control_plane_intents
269
+ SET state = ?, last_error_code = ?, updated_at = ?, completed_at = ?
270
+ WHERE intent_id = ? AND state = 'pending'
271
+ RETURNING ${CONTROL_PLANE_INTENT_COLUMNS}
272
+ `)
273
+ .get(input.state, input.errorCode ?? null, completedAt, completedAt, input.intentId);
274
+ return row === undefined ? null : mapIntentRow(row);
275
+ }
276
+ //# sourceMappingURL=dispatch-intents.js.map
@@ -1,6 +1,6 @@
1
1
  import { type ClarificationRoundRef, type TaskId } from "@tutti/shared/ids";
2
2
  import type { WorkspaceEventBus } from "../server-shell/http/workspace-events.js";
3
- import type { HostProjectStore } from "../store/index.js";
3
+ import { type HostProjectStore } from "../store/index.js";
4
4
  import type { RunPipelineResolver } from "./types.js";
5
5
  import type { StartRunForTaskInput } from "./run-start.js";
6
6
  type StartRunForTask = (input: StartRunForTaskInput) => void;
@@ -1,8 +1,10 @@
1
1
  import { createActivityRef, } from "@tutti/shared/ids";
2
2
  import { materializeTaskBoundRunSuccessor, readNextRunnableTask, readTaskDetailProjection, readTaskRunResultsPage, } from "../collaboration-state/index.js";
3
+ import { withHostStoreTransaction } from "../store/index.js";
3
4
  import { readRunContinuationContextFromStore } from "./follow-up-context.js";
4
5
  import { clarificationInvalidates } from "./invalidations.js";
5
6
  import { toDomainRunLineage } from "./run-lineage.js";
7
+ import { enqueueRunDispatchIntent } from "./run-dispatch-intents.js";
6
8
  export function startRunFromFollowUp(input) {
7
9
  const pipelineResolution = input.resolveRunPipeline?.();
8
10
  if (pipelineResolution === undefined || pipelineResolution.kind !== "ready") {
@@ -27,10 +29,16 @@ export function startRunFromFollowUp(input) {
27
29
  return;
28
30
  }
29
31
  const activityRef = createActivityRef();
30
- const materialized = materializeTaskBoundRunSuccessor(input.store.db, {
31
- round_id: input.roundId,
32
- activity_ref: activityRef,
33
- now: input.now,
32
+ const materialized = withHostStoreTransaction(input.store.db, (tx) => {
33
+ const successor = materializeTaskBoundRunSuccessor(tx, {
34
+ round_id: input.roundId,
35
+ activity_ref: activityRef,
36
+ now: input.now,
37
+ });
38
+ if (successor?.kind === "materialized") {
39
+ enqueueRunDispatchIntent(tx, { activityRef, now: input.now });
40
+ }
41
+ return successor;
34
42
  });
35
43
  if (materialized === null || materialized.kind !== "materialized") {
36
44
  return;
@@ -4,6 +4,7 @@ import type { WorkspaceEventBus } from "../server-shell/http/workspace-events.js
4
4
  import type { HostProjectStore } from "../store/index.js";
5
5
  import type { StartRunForTaskInput } from "./run-start.js";
6
6
  import type { ControlPlaneLogger, Phase5ControlPlaneOptions, RunPipelineResolver } from "./types.js";
7
+ import type { IntentRuntimeStartResult, IntentRuntimeTerminal } from "./intent-dispatcher.js";
7
8
  import type { ProcedureWorkflowRunnerResolver } from "./workflows/index.js";
8
9
  export declare function startControlPlaneTaskBoundFollowUpCheck(options: {
9
10
  engine: ProcedureEngine;
@@ -23,5 +24,6 @@ export declare function startControlPlaneTaskBoundFollowUpCheck(options: {
23
24
  publishProcedureTransition: (state: ProcedureEngineState) => void;
24
25
  startRunForTask: (runInput: StartRunForTaskInput) => void;
25
26
  shouldContinue?: () => boolean;
26
- }): boolean;
27
+ onTerminal?: (result?: IntentRuntimeTerminal) => void;
28
+ }): IntentRuntimeStartResult;
27
29
  //# sourceMappingURL=follow-up-start.d.ts.map
@@ -6,7 +6,7 @@ import { logProcedureExecutionError } from "./procedure-logging.js";
6
6
  export function startControlPlaneTaskBoundFollowUpCheck(options) {
7
7
  const runnerResolution = options.resolveWorkflowRunner();
8
8
  if (runnerResolution.kind !== "ready" || runnerResolution.runner.runFollowUpCheck === undefined) {
9
- return false;
9
+ return { kind: "not_ready", errorCode: "follow_up_runner_unavailable" };
10
10
  }
11
11
  const workflowRef = createWorkflowInvocationRef();
12
12
  let output;
@@ -37,6 +37,11 @@ export function startControlPlaneTaskBoundFollowUpCheck(options) {
37
37
  now: options.now,
38
38
  });
39
39
  }
40
+ if (state.transition_kind === "terminal") {
41
+ options.onTerminal?.(state.activity.status === "failed"
42
+ ? { state: "failed", errorCode: "procedure_failed" }
43
+ : undefined);
44
+ }
40
45
  },
41
46
  onError: ({ context, error }) => logProcedureExecutionError({
42
47
  logger: options.logger,
@@ -98,6 +103,6 @@ export function startControlPlaneTaskBoundFollowUpCheck(options) {
98
103
  });
99
104
  },
100
105
  });
101
- return start.kind === "accepted";
106
+ return start.kind === "accepted" ? { kind: "accepted" } : { kind: "busy" };
102
107
  }
103
108
  //# sourceMappingURL=follow-up-start.js.map
@@ -6,5 +6,7 @@ export declare function formatStartupRecoverySummary(input: {
6
6
  replayedRoundCount: number;
7
7
  interruptedCommandCount: number;
8
8
  staleReplayHintCount: number;
9
+ recoveredTaskCompileCount: number;
10
+ recoveredScratchpadRefreshCount: number;
9
11
  }): string;
10
12
  //# sourceMappingURL=formatters.d.ts.map
@@ -34,6 +34,12 @@ export function formatStartupRecoverySummary(input) {
34
34
  if (input.staleReplayHintCount > 0) {
35
35
  parts.push(`${countNoun(input.staleReplayHintCount, "stale replay hint")} cleared`);
36
36
  }
37
+ if (input.recoveredTaskCompileCount > 0) {
38
+ parts.push(`${countNoun(input.recoveredTaskCompileCount, "Task Compile chain")} requeued`);
39
+ }
40
+ if (input.recoveredScratchpadRefreshCount > 0) {
41
+ parts.push(`${countNoun(input.recoveredScratchpadRefreshCount, "Scratchpad refresh")} requeued`);
42
+ }
37
43
  return parts.length === 0
38
44
  ? "Startup recovery completed."
39
45
  : `Startup recovery completed: ${parts.join("; ")}.`;
@@ -18,6 +18,7 @@ export declare class Phase5ControlPlane {
18
18
  private readonly runLifecycle;
19
19
  private readonly now;
20
20
  private readonly scratchpadAutoRefreshScheduler;
21
+ private readonly intentDispatcher;
21
22
  private pendingWorklistCompletionContextSyncTimer;
22
23
  private readonly pendingReferenceSummaryPaths;
23
24
  private readonly referenceSummaryWaiters;
@@ -25,6 +26,8 @@ export declare class Phase5ControlPlane {
25
26
  private referenceSummaryStartScheduled;
26
27
  private closing;
27
28
  private readonly asyncOwners;
29
+ private readonly pendingRunInputs;
30
+ private readonly runIntentTerminals;
28
31
  private lastRecoveryConclusion;
29
32
  constructor(options: Phase5ControlPlaneOptions);
30
33
  getExecutionStatus(): ExecutionStatusProjection;
@@ -73,6 +76,12 @@ export declare class Phase5ControlPlane {
73
76
  private cancelPendingWorklistCompletionContextSync;
74
77
  private startTaskCompileContinuation;
75
78
  private startTaskBoundFollowUpCheck;
79
+ private dispatchDurableIntent;
80
+ private dispatchRunIntent;
81
+ private dispatchScratchpadIntent;
82
+ private dispatchReferenceSummaryIntent;
83
+ private dispatchTaskCompileStartIntent;
84
+ private dispatchClarificationSuccessorIntent;
76
85
  private publishProcedureTransition;
77
86
  private logProcedureTransition;
78
87
  }