@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.
- package/dist/collaboration-state/index.d.ts +5 -5
- package/dist/collaboration-state/index.js +4 -4
- package/dist/collaboration-state/scratchpad-source-state.d.ts +6 -1
- package/dist/collaboration-state/scratchpad-source-state.js +37 -6
- package/dist/collaboration-state/storage-types.d.ts +10 -0
- package/dist/collaboration-state/task-compile-context.d.ts +8 -3
- package/dist/collaboration-state/task-compile-context.js +91 -7
- package/dist/collaboration-state/task-retry.js +3 -1
- package/dist/collaboration-state/types.d.ts +12 -0
- package/dist/collaboration-state/worklist.d.ts +2 -1
- package/dist/collaboration-state/worklist.js +6 -0
- package/dist/control-plane/clarification-commands.d.ts +12 -8
- package/dist/control-plane/clarification-commands.js +29 -10
- package/dist/control-plane/dispatch-intents.d.ts +104 -0
- package/dist/control-plane/dispatch-intents.js +276 -0
- package/dist/control-plane/follow-up-run.d.ts +1 -1
- package/dist/control-plane/follow-up-run.js +12 -4
- package/dist/control-plane/follow-up-start.d.ts +3 -1
- package/dist/control-plane/follow-up-start.js +7 -2
- package/dist/control-plane/formatters.d.ts +2 -0
- package/dist/control-plane/formatters.js +6 -0
- package/dist/control-plane/index.d.ts +13 -1
- package/dist/control-plane/index.js +313 -16
- package/dist/control-plane/intent-dispatcher.d.ts +52 -0
- package/dist/control-plane/intent-dispatcher.js +184 -0
- package/dist/control-plane/reference-summary-refresh.d.ts +4 -1
- package/dist/control-plane/reference-summary-refresh.js +39 -4
- package/dist/control-plane/run-dispatch-intents.d.ts +15 -0
- package/dist/control-plane/run-dispatch-intents.js +40 -0
- package/dist/control-plane/run-retry.d.ts +1 -1
- package/dist/control-plane/run-retry.js +14 -6
- package/dist/control-plane/run-scheduler.d.ts +1 -1
- package/dist/control-plane/run-scheduler.js +12 -4
- package/dist/control-plane/scratchpad-refresh-start.d.ts +10 -2
- package/dist/control-plane/scratchpad-refresh-start.js +57 -8
- package/dist/control-plane/scratchpad-refresh.js +3 -4
- package/dist/control-plane/startup-recovery.d.ts +2 -2
- package/dist/control-plane/startup-recovery.js +73 -3
- package/dist/control-plane/task-compile-continuation.d.ts +3 -1
- package/dist/control-plane/task-compile-continuation.js +14 -9
- package/dist/control-plane/task-compile-start.d.ts +33 -1
- package/dist/control-plane/task-compile-start.js +244 -26
- package/dist/control-plane/types.d.ts +4 -2
- package/dist/providers/openai/app-server/session-store.js +2 -4
- package/migrations/0015_control_plane_intents.sql +115 -0
- package/migrations/README.md +2 -1
- package/node_modules/@tutti/shared/dist/ids/index.d.ts +3 -0
- package/node_modules/@tutti/shared/dist/ids/index.js +2 -0
- package/package.json +1 -1
- package/web/assets/{homepage-motion-scene-BFo4r4nz.js → homepage-motion-scene-lpr-1FjJ.js} +1 -1
- package/web/assets/{index-CNIl4TF8.js → index-CQoq2PtU.js} +2 -2
- package/web/index.html +1 -1
|
@@ -1,13 +1,31 @@
|
|
|
1
|
-
import { deleteStaleClarificationSubmitReplayHints, readStaleRunningTaskIdentities, readSealedClarificationRoundsWithoutSuccessor, recoverStaleRunningTasksAsFailed, recordRunResult, recordRecoverySummary, } from "../collaboration-state/index.js";
|
|
1
|
+
import { deleteStaleClarificationSubmitReplayHints, readActiveClarificationProjection, readActiveTaskCompileContext, readPendingTaskCompileSubmission, readStaleRunningTaskIdentities, readSealedClarificationRoundsWithoutSuccessor, readRunResultProjectionByActivityRef, recoverStaleRunningTasksAsFailed, recordRunResult, recordRecoverySummary, recoverStaleScratchpadRefresh, } from "../collaboration-state/index.js";
|
|
2
2
|
import { docsStatusForChangedPaths } from "../run-pipeline/result-projections.js";
|
|
3
3
|
import { recoverInProgressCommandIdempotencyRecords, } from "../store/index.js";
|
|
4
4
|
import { recoverAcceptedCheckpoint } from "../workspace-ops/index.js";
|
|
5
5
|
import { resolveControlPlaneCommandRecovery } from "./command-recovery.js";
|
|
6
6
|
import { formatStartupRecoverySummary } from "./formatters.js";
|
|
7
|
+
import { enqueueControlPlaneIntent, listActiveControlPlaneIntentClaims, listNonTerminalControlPlaneIntentsByKind, requeueOrphanedControlPlaneIntentClaim, } from "./dispatch-intents.js";
|
|
8
|
+
import { settleRunDispatchIntentAfterRecovery } from "./run-dispatch-intents.js";
|
|
7
9
|
import { EXECUTION_STATUS_INVALIDATES, STARTUP_RECOVERY_INVALIDATES, taskRunResultInvalidates, } from "./invalidations.js";
|
|
8
10
|
export function runControlPlaneStartupRecovery(input) {
|
|
9
11
|
const options = input.options ?? {};
|
|
12
|
+
for (const intent of listActiveControlPlaneIntentClaims(input.store.db)) {
|
|
13
|
+
if (intent.kind === "run_dispatch" || intent.claim_token === undefined) {
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
requeueOrphanedControlPlaneIntentClaim(input.store.db, {
|
|
17
|
+
intentId: intent.intent_id,
|
|
18
|
+
expectedClaimToken: intent.claim_token,
|
|
19
|
+
errorCode: "host_restarted",
|
|
20
|
+
now: input.now,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
10
23
|
const staleReplayHintCount = deleteStaleClarificationSubmitReplayHints(input.store.db);
|
|
24
|
+
const recoveredScratchpadRefreshCount = recoverStaleScratchpadRefresh(input.store.db, {
|
|
25
|
+
now: input.now,
|
|
26
|
+
})
|
|
27
|
+
? 1
|
|
28
|
+
: 0;
|
|
11
29
|
const checkpointRecoveredRuns = [];
|
|
12
30
|
const checkpointConflictRuns = [];
|
|
13
31
|
if (options.workspaceRoot !== undefined) {
|
|
@@ -78,6 +96,16 @@ export function runControlPlaneStartupRecovery(input) {
|
|
|
78
96
|
...checkpointConflictRuns,
|
|
79
97
|
...interrupted.recovered_runs,
|
|
80
98
|
];
|
|
99
|
+
for (const intent of listNonTerminalControlPlaneIntentsByKind(input.store.db, "run_dispatch")) {
|
|
100
|
+
const activityRef = intent.owner_ref;
|
|
101
|
+
if (readRunResultProjectionByActivityRef(input.store.db, activityRef) !== null) {
|
|
102
|
+
settleRunDispatchIntentAfterRecovery(input.store.db, {
|
|
103
|
+
activityRef,
|
|
104
|
+
errorCode: "run_startup_reconciled",
|
|
105
|
+
now: input.now,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
81
109
|
const commandRecovery = options.recoverInProgressCommands
|
|
82
110
|
? recoverInProgressCommandIdempotencyRecords(input.store.db, {
|
|
83
111
|
resolve: resolveControlPlaneCommandRecovery,
|
|
@@ -91,16 +119,54 @@ export function runControlPlaneStartupRecovery(input) {
|
|
|
91
119
|
continue;
|
|
92
120
|
}
|
|
93
121
|
const roundId = sealed.round.id;
|
|
94
|
-
if (input.
|
|
122
|
+
if (input.enqueueSealedRound(sealed, roundId)) {
|
|
95
123
|
replayedRoundIds.push(roundId);
|
|
96
124
|
}
|
|
97
125
|
}
|
|
126
|
+
const activeTaskCompileContext = readActiveTaskCompileContext(input.store.db);
|
|
127
|
+
const activeClarification = readActiveClarificationProjection(input.store.db);
|
|
128
|
+
const taskCompileWaitingForHuman = activeTaskCompileContext !== null &&
|
|
129
|
+
activeClarification.state === "active" &&
|
|
130
|
+
activeClarification.thread.owner.kind === "task_compile" &&
|
|
131
|
+
activeClarification.thread.owner.workflow_ref === activeTaskCompileContext.workflow_ref;
|
|
132
|
+
let recoveredTaskCompileCount = 0;
|
|
133
|
+
const pendingTaskCompileSubmission = readPendingTaskCompileSubmission(input.store.db);
|
|
134
|
+
if (activeTaskCompileContext === null && pendingTaskCompileSubmission !== null) {
|
|
135
|
+
const ensured = enqueueControlPlaneIntent(input.store.db, {
|
|
136
|
+
kind: "task_compile_start",
|
|
137
|
+
ownerKind: "task_compile",
|
|
138
|
+
ownerRef: pendingTaskCompileSubmission.workflow_ref,
|
|
139
|
+
dedupeKey: `task_compile_start:${pendingTaskCompileSubmission.workflow_ref}`,
|
|
140
|
+
lane: "foreground",
|
|
141
|
+
priority: 30,
|
|
142
|
+
now: input.now,
|
|
143
|
+
}).intent;
|
|
144
|
+
if (ensured.state === "pending" || ensured.state === "claimed" || ensured.state === "running") {
|
|
145
|
+
recoveredTaskCompileCount = 1;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
if (activeTaskCompileContext !== null && !taskCompileWaitingForHuman) {
|
|
149
|
+
const ensured = enqueueControlPlaneIntent(input.store.db, {
|
|
150
|
+
kind: "task_compile_start",
|
|
151
|
+
ownerKind: "task_compile",
|
|
152
|
+
ownerRef: activeTaskCompileContext.workflow_ref,
|
|
153
|
+
dedupeKey: `task_compile_start:${activeTaskCompileContext.workflow_ref}`,
|
|
154
|
+
lane: "foreground",
|
|
155
|
+
priority: 30,
|
|
156
|
+
now: input.now,
|
|
157
|
+
}).intent;
|
|
158
|
+
if (ensured.state === "pending" || ensured.state === "claimed" || ensured.state === "running") {
|
|
159
|
+
recoveredTaskCompileCount = 1;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
98
162
|
const interruptedCommandCount = (options.interruptedCommandCount ?? 0) + (commandRecovery?.interrupted_count ?? 0);
|
|
99
163
|
const affectedActivities = recoveredRuns.map((recoveredRun) => recoveredRun.activity_ref);
|
|
100
164
|
const shouldRecord = affectedActivities.length > 0 ||
|
|
101
165
|
replayedRoundIds.length > 0 ||
|
|
102
166
|
interruptedCommandCount > 0 ||
|
|
103
|
-
staleReplayHintCount > 0
|
|
167
|
+
staleReplayHintCount > 0 ||
|
|
168
|
+
recoveredTaskCompileCount > 0 ||
|
|
169
|
+
recoveredScratchpadRefreshCount > 0;
|
|
104
170
|
if (!shouldRecord) {
|
|
105
171
|
return { kind: "noop" };
|
|
106
172
|
}
|
|
@@ -110,6 +176,8 @@ export function runControlPlaneStartupRecovery(input) {
|
|
|
110
176
|
replayedRoundCount: replayedRoundIds.length,
|
|
111
177
|
interruptedCommandCount,
|
|
112
178
|
staleReplayHintCount,
|
|
179
|
+
recoveredTaskCompileCount,
|
|
180
|
+
recoveredScratchpadRefreshCount,
|
|
113
181
|
});
|
|
114
182
|
const recorded = recordRecoverySummary(input.store.db, {
|
|
115
183
|
summary,
|
|
@@ -175,6 +243,8 @@ export function runControlPlaneStartupRecovery(input) {
|
|
|
175
243
|
replayed_round_ids: replayedRoundIds,
|
|
176
244
|
interrupted_command_count: interruptedCommandCount,
|
|
177
245
|
stale_replay_hint_count: staleReplayHintCount,
|
|
246
|
+
recovered_task_compile_count: recoveredTaskCompileCount,
|
|
247
|
+
recovered_scratchpad_refresh_count: recoveredScratchpadRefreshCount,
|
|
178
248
|
};
|
|
179
249
|
}
|
|
180
250
|
//# sourceMappingURL=startup-recovery.js.map
|
|
@@ -2,6 +2,7 @@ import type { ClarificationRoundRef } from "@tutti/shared/ids";
|
|
|
2
2
|
import type { ProcedureEngine, ProcedureEngineState } from "../procedure-engine/index.js";
|
|
3
3
|
import type { WorkspaceEventBus } from "../server-shell/http/workspace-events.js";
|
|
4
4
|
import type { HostProjectStore } from "../store/index.js";
|
|
5
|
+
import type { IntentRuntimeStartResult, IntentRuntimeTerminal } from "./intent-dispatcher.js";
|
|
5
6
|
import type { ControlPlaneLogger } from "./types.js";
|
|
6
7
|
import type { Phase5ControlPlaneOptions } from "./types.js";
|
|
7
8
|
import type { ProcedureWorkflowRunnerResolver } from "./workflows/index.js";
|
|
@@ -23,5 +24,6 @@ export declare function startControlPlaneTaskCompileContinuation(options: {
|
|
|
23
24
|
now: () => Date;
|
|
24
25
|
publishProcedureTransition: (state: ProcedureEngineState) => void;
|
|
25
26
|
onTasksAdded: () => void;
|
|
26
|
-
|
|
27
|
+
onTerminal?: (result?: IntentRuntimeTerminal) => void;
|
|
28
|
+
}): IntentRuntimeStartResult;
|
|
27
29
|
//# sourceMappingURL=task-compile-continuation.d.ts.map
|
|
@@ -5,18 +5,18 @@ import { applyTaskCompileProcedureOutput } from "./task-compile-output.js";
|
|
|
5
5
|
import { readTaskCompileClarificationRounds } from "./task-compile-clarification-context.js";
|
|
6
6
|
import { materializePromptSourceMessages } from "./task-source-context.js";
|
|
7
7
|
import { logProcedureExecutionError } from "./procedure-logging.js";
|
|
8
|
-
import { materializeRecentReferences, readRecentReferenceSources
|
|
8
|
+
import { materializeRecentReferences, readRecentReferenceSources } from "./recent-references.js";
|
|
9
9
|
export function startControlPlaneTaskCompileContinuation(options) {
|
|
10
10
|
if (options.owner.kind !== "task_compile") {
|
|
11
|
-
return
|
|
11
|
+
return { kind: "obsolete", errorCode: "owner_kind_mismatch" };
|
|
12
12
|
}
|
|
13
13
|
const workflowRef = options.owner.workflow_ref;
|
|
14
14
|
if (readActiveTaskCompileContext(options.store.db, workflowRef) === null) {
|
|
15
|
-
return
|
|
15
|
+
return { kind: "obsolete", errorCode: "task_compile_context_absent" };
|
|
16
16
|
}
|
|
17
17
|
const runnerResolution = options.resolveWorkflowRunner();
|
|
18
18
|
if (runnerResolution.kind !== "ready") {
|
|
19
|
-
return
|
|
19
|
+
return { kind: "not_ready", errorCode: "workflow_runner_unavailable" };
|
|
20
20
|
}
|
|
21
21
|
const start = options.engine.startProcedure({
|
|
22
22
|
workflow_ref: workflowRef,
|
|
@@ -25,7 +25,14 @@ export function startControlPlaneTaskCompileContinuation(options) {
|
|
|
25
25
|
title: "Continue Worklist Compile",
|
|
26
26
|
summary: "Continuing task_compile from clarification input.",
|
|
27
27
|
now: options.now,
|
|
28
|
-
onTransition:
|
|
28
|
+
onTransition: (state) => {
|
|
29
|
+
options.publishProcedureTransition(state);
|
|
30
|
+
if (state.transition_kind === "terminal") {
|
|
31
|
+
options.onTerminal?.(state.activity.status === "failed"
|
|
32
|
+
? { state: "failed", errorCode: "procedure_failed" }
|
|
33
|
+
: undefined);
|
|
34
|
+
}
|
|
35
|
+
},
|
|
29
36
|
onError: ({ context, error }) => logProcedureExecutionError({
|
|
30
37
|
logger: options.logger,
|
|
31
38
|
workflowKind: "task_compile",
|
|
@@ -57,9 +64,7 @@ export function startControlPlaneTaskCompileContinuation(options) {
|
|
|
57
64
|
scratchpad: taskCompileContext.scratchpad_snapshot,
|
|
58
65
|
source_messages: materializePromptSourceMessages(options.store.db, taskCompileContext.sources),
|
|
59
66
|
worklist: readWorklistProjection(options.store.db),
|
|
60
|
-
...(recentReferences.length === 0
|
|
61
|
-
? {}
|
|
62
|
-
: { recent_references: recentReferences }),
|
|
67
|
+
...(recentReferences.length === 0 ? {} : { recent_references: recentReferences }),
|
|
63
68
|
clarification_rounds: clarificationRounds,
|
|
64
69
|
}, {
|
|
65
70
|
workflow_ref: workflowRef,
|
|
@@ -108,6 +113,6 @@ export function startControlPlaneTaskCompileContinuation(options) {
|
|
|
108
113
|
}
|
|
109
114
|
},
|
|
110
115
|
});
|
|
111
|
-
return start.kind === "accepted";
|
|
116
|
+
return start.kind === "accepted" ? { kind: "accepted" } : { kind: "busy" };
|
|
112
117
|
}
|
|
113
118
|
//# sourceMappingURL=task-compile-continuation.js.map
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { type ActivityRef, type WorkflowInvocationRef } from "@tutti/shared/ids";
|
|
1
|
+
import { type ActivityRef, type ControlPlaneIntentId, type WorkflowInvocationRef } from "@tutti/shared/ids";
|
|
2
2
|
import type { SubmitWorklistDisposition, SubmitWorklistPayload, SubmitWorklistResult } from "@tutti/shared/schemas/api";
|
|
3
3
|
import type { ProcedureEngine, ProcedureEngineState, ProcedureProgressUpdate } from "../procedure-engine/index.js";
|
|
4
4
|
import type { WorkspaceEventBus } from "../server-shell/http/workspace-events.js";
|
|
5
5
|
import type { HostProjectStore } from "../store/index.js";
|
|
6
6
|
import type { ControlPlaneCommandResult, ControlPlaneLogger, Phase5ControlPlaneOptions } from "./types.js";
|
|
7
|
+
import type { IntentRuntimeStartResult, IntentRuntimeTerminal } from "./intent-dispatcher.js";
|
|
7
8
|
import type { ProcedureWorkflowRunner, ProcedureWorkflowRunnerResolver } from "./workflows/index.js";
|
|
8
9
|
export declare function startControlPlaneTaskCompile(options: {
|
|
9
10
|
engine: ProcedureEngine;
|
|
@@ -19,6 +20,37 @@ export declare function startControlPlaneTaskCompile(options: {
|
|
|
19
20
|
onTasksAdded: () => void;
|
|
20
21
|
shouldContinue?: () => boolean;
|
|
21
22
|
}): ControlPlaneCommandResult<SubmitWorklistDisposition, SubmitWorklistResult>;
|
|
23
|
+
export declare function resumePendingTaskCompileSubmission(options: {
|
|
24
|
+
engine: ProcedureEngine;
|
|
25
|
+
store: HostProjectStore;
|
|
26
|
+
events: WorkspaceEventBus;
|
|
27
|
+
taskCompileWorkflowRef: WorkflowInvocationRef;
|
|
28
|
+
recoveryIntentId: ControlPlaneIntentId;
|
|
29
|
+
resolveWorkflowRunner: ProcedureWorkflowRunnerResolver;
|
|
30
|
+
projectContext: Phase5ControlPlaneOptions["projectContext"] | undefined;
|
|
31
|
+
logger: ControlPlaneLogger | undefined;
|
|
32
|
+
now: () => Date;
|
|
33
|
+
publishProcedureTransition: (state: ProcedureEngineState) => void;
|
|
34
|
+
registerReferenceSummaryWaiter: (activityRef: ActivityRef, waiter: (state: ProcedureEngineState) => void) => void;
|
|
35
|
+
onTasksAdded: () => void;
|
|
36
|
+
shouldContinue?: () => boolean;
|
|
37
|
+
onChainTerminal: (result?: IntentRuntimeTerminal) => void;
|
|
38
|
+
}): IntentRuntimeStartResult;
|
|
39
|
+
export declare function resumeControlPlaneTaskCompileChain(options: {
|
|
40
|
+
engine: ProcedureEngine;
|
|
41
|
+
store: HostProjectStore;
|
|
42
|
+
events: WorkspaceEventBus;
|
|
43
|
+
taskCompileWorkflowRef: WorkflowInvocationRef;
|
|
44
|
+
resolveWorkflowRunner: ProcedureWorkflowRunnerResolver;
|
|
45
|
+
projectContext: Phase5ControlPlaneOptions["projectContext"] | undefined;
|
|
46
|
+
logger: ControlPlaneLogger | undefined;
|
|
47
|
+
now: () => Date;
|
|
48
|
+
publishProcedureTransition: (state: ProcedureEngineState) => void;
|
|
49
|
+
registerReferenceSummaryWaiter: (activityRef: ActivityRef, waiter: (state: ProcedureEngineState) => void) => void;
|
|
50
|
+
onTasksAdded: () => void;
|
|
51
|
+
shouldContinue?: () => boolean;
|
|
52
|
+
onChainTerminal: (result?: IntentRuntimeTerminal) => void;
|
|
53
|
+
}): IntentRuntimeStartResult;
|
|
22
54
|
export declare function runControlPlaneTaskCompileProcedure(options: {
|
|
23
55
|
runner: ProcedureWorkflowRunner;
|
|
24
56
|
workflowRef: WorkflowInvocationRef;
|
|
@@ -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
|
-
|
|
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:
|
|
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"
|
|
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
|
-
|
|
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
|
|
198
|
-
|
|
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
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
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"
|
|
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 (
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
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") {
|