@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.
- 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/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 +9 -0
- package/dist/control-plane/index.js +260 -11
- 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/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/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,7 +1,9 @@
|
|
|
1
1
|
import { createWorkflowInvocationRef } from "@tutti/shared/ids";
|
|
2
2
|
import { recordProjectTimelineEvent } from "../project-timeline/index.js";
|
|
3
|
+
import { withHostStoreTransaction } from "../store/index.js";
|
|
3
4
|
import { WorkspaceOpsError, readReferenceSummaryTargets, updateReferenceSummaries, } from "../workspace-ops/index.js";
|
|
4
5
|
import { logProcedureExecutionError } from "./procedure-logging.js";
|
|
6
|
+
import { enqueueControlPlaneIntent, readControlPlaneIntentByDedupeKey, settlePendingControlPlaneIntent, } from "./dispatch-intents.js";
|
|
5
7
|
const REFERENCE_SUMMARY_CONCURRENCY = 1;
|
|
6
8
|
const REFERENCE_SUMMARY_MAX_ATTEMPTS = 2;
|
|
7
9
|
function publishReferenceSummaryTimelineChanged(input) {
|
|
@@ -357,7 +359,27 @@ export function startReferenceSummaryRefreshProcedure(input) {
|
|
|
357
359
|
title: "Refresh Reference Summaries",
|
|
358
360
|
summary: "Summarizing reference files.",
|
|
359
361
|
now: input.now,
|
|
360
|
-
onTransition:
|
|
362
|
+
onTransition: (state) => {
|
|
363
|
+
if (state.transition_kind === "terminal" && input.recordRecoveryIntent !== false) {
|
|
364
|
+
const intent = readControlPlaneIntentByDedupeKey(input.store.db, `reference_summary_refresh:${state.activity.activity_ref}`);
|
|
365
|
+
if (intent?.state === "pending") {
|
|
366
|
+
settlePendingControlPlaneIntent(input.store.db, {
|
|
367
|
+
intentId: intent.intent_id,
|
|
368
|
+
state: state.activity.status === "failed" ? "failed" : "completed",
|
|
369
|
+
...(state.activity.status === "failed"
|
|
370
|
+
? { errorCode: "reference_summary_failed" }
|
|
371
|
+
: {}),
|
|
372
|
+
now: input.now,
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
input.publishProcedureTransition(state);
|
|
377
|
+
if (state.transition_kind === "terminal") {
|
|
378
|
+
input.onTerminal?.(state.activity.status === "failed"
|
|
379
|
+
? { state: "failed", errorCode: "reference_summary_failed" }
|
|
380
|
+
: undefined);
|
|
381
|
+
}
|
|
382
|
+
},
|
|
361
383
|
onError: ({ context, error }) => logProcedureExecutionError({
|
|
362
384
|
logger: input.logger,
|
|
363
385
|
workflowKind: "reference_summary_refresh",
|
|
@@ -366,9 +388,22 @@ export function startReferenceSummaryRefreshProcedure(input) {
|
|
|
366
388
|
error,
|
|
367
389
|
}),
|
|
368
390
|
execute: async (context) => {
|
|
369
|
-
input.
|
|
370
|
-
|
|
371
|
-
|
|
391
|
+
withHostStoreTransaction(input.store.db, (tx) => {
|
|
392
|
+
if (input.recordRecoveryIntent !== false) {
|
|
393
|
+
enqueueControlPlaneIntent(tx, {
|
|
394
|
+
kind: "reference_summary_refresh",
|
|
395
|
+
ownerKind: "reference_summary",
|
|
396
|
+
ownerRef: context.activity_ref,
|
|
397
|
+
dedupeKey: `reference_summary_refresh:${context.activity_ref}`,
|
|
398
|
+
lane: "foreground",
|
|
399
|
+
priority: 50,
|
|
400
|
+
now: input.now,
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
input.onExecuteStart?.({
|
|
404
|
+
activityRef: context.activity_ref,
|
|
405
|
+
workflowRef,
|
|
406
|
+
});
|
|
372
407
|
});
|
|
373
408
|
return runReferenceSummaryRefresh({
|
|
374
409
|
store: input.store,
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ActivityRef } from "@tutti/shared/ids";
|
|
2
|
+
import type { SqliteDatabase } from "../store/index.js";
|
|
3
|
+
export declare function enqueueRunDispatchIntent(db: SqliteDatabase, input: {
|
|
4
|
+
activityRef: ActivityRef;
|
|
5
|
+
now: () => Date;
|
|
6
|
+
}): {
|
|
7
|
+
intent: import("./dispatch-intents.js").ControlPlaneIntent;
|
|
8
|
+
created: boolean;
|
|
9
|
+
};
|
|
10
|
+
export declare function settleRunDispatchIntentAfterRecovery(db: SqliteDatabase, input: {
|
|
11
|
+
activityRef: ActivityRef;
|
|
12
|
+
errorCode: string;
|
|
13
|
+
now: () => Date;
|
|
14
|
+
}): boolean;
|
|
15
|
+
//# sourceMappingURL=run-dispatch-intents.d.ts.map
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { enqueueControlPlaneIntent, readControlPlaneIntentByDedupeKey, settleClaimedControlPlaneIntent, settlePendingControlPlaneIntent, } from "./dispatch-intents.js";
|
|
2
|
+
export function enqueueRunDispatchIntent(db, input) {
|
|
3
|
+
return enqueueControlPlaneIntent(db, {
|
|
4
|
+
kind: "run_dispatch",
|
|
5
|
+
ownerKind: "task_run",
|
|
6
|
+
ownerRef: input.activityRef,
|
|
7
|
+
dedupeKey: `run_dispatch:${input.activityRef}`,
|
|
8
|
+
lane: "run",
|
|
9
|
+
priority: 10,
|
|
10
|
+
now: input.now,
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
export function settleRunDispatchIntentAfterRecovery(db, input) {
|
|
14
|
+
const intent = readControlPlaneIntentByDedupeKey(db, `run_dispatch:${input.activityRef}`);
|
|
15
|
+
if (intent === null ||
|
|
16
|
+
intent.state === "completed" ||
|
|
17
|
+
intent.state === "failed" ||
|
|
18
|
+
intent.state === "cancelled") {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
if (intent.state === "pending") {
|
|
22
|
+
return (settlePendingControlPlaneIntent(db, {
|
|
23
|
+
intentId: intent.intent_id,
|
|
24
|
+
state: "completed",
|
|
25
|
+
errorCode: input.errorCode,
|
|
26
|
+
now: input.now,
|
|
27
|
+
}) !== null);
|
|
28
|
+
}
|
|
29
|
+
if (intent.claim_token === undefined) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
return (settleClaimedControlPlaneIntent(db, {
|
|
33
|
+
intentId: intent.intent_id,
|
|
34
|
+
claimToken: intent.claim_token,
|
|
35
|
+
state: "completed",
|
|
36
|
+
errorCode: input.errorCode,
|
|
37
|
+
now: input.now,
|
|
38
|
+
}) !== null);
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=run-dispatch-intents.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type ActivityRef, type TaskId } from "@tutti/shared/ids";
|
|
2
2
|
import type { RetryTaskDisposition, RetryTaskPayload } from "@tutti/shared/schemas/api";
|
|
3
|
-
import type
|
|
3
|
+
import { type HostProjectStore } from "../store/index.js";
|
|
4
4
|
import type { StartRunForTaskInput } from "./run-start.js";
|
|
5
5
|
import type { ControlPlaneCommandResult, RunPipelineResolver } from "./types.js";
|
|
6
6
|
type ActiveActivity = {
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { createActivityRef, } from "@tutti/shared/ids";
|
|
2
2
|
import { materializeTaskRetryRun, readNextRunnableTask, readTaskDetailProjection, readTaskRunResultsPage, } from "../collaboration-state/index.js";
|
|
3
|
+
import { withHostStoreTransaction } from "../store/index.js";
|
|
3
4
|
import { toDomainRunLineage } from "./run-lineage.js";
|
|
5
|
+
import { enqueueRunDispatchIntent } from "./run-dispatch-intents.js";
|
|
4
6
|
function retryCorrectionContext(result, detail) {
|
|
5
7
|
const resultProjection = detail.detail.result;
|
|
6
8
|
const reasonCode = result.result_kind === "failed"
|
|
7
9
|
? result.error_code
|
|
8
10
|
: resultProjection.checks === "failed"
|
|
9
11
|
? "checks_failed"
|
|
10
|
-
: resultProjection.promotion?.reason_code ?? "previous_run_failed";
|
|
12
|
+
: (resultProjection.promotion?.reason_code ?? "previous_run_failed");
|
|
11
13
|
return {
|
|
12
14
|
previous_provider_result: result.result_kind === "finished"
|
|
13
15
|
? {
|
|
@@ -58,11 +60,17 @@ export function retryControlPlaneTask(input) {
|
|
|
58
60
|
return { disposition: { kind: "task_not_failed" } };
|
|
59
61
|
}
|
|
60
62
|
const activityRef = createActivityRef();
|
|
61
|
-
const materialized =
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
const materialized = withHostStoreTransaction(input.store.db, (tx) => {
|
|
64
|
+
const retry = materializeTaskRetryRun(tx, {
|
|
65
|
+
task_id: input.taskId,
|
|
66
|
+
expected_latest_run_result_id: input.payload.expected_latest_run_result_id,
|
|
67
|
+
activity_ref: activityRef,
|
|
68
|
+
now: input.now,
|
|
69
|
+
});
|
|
70
|
+
if (retry.disposition.kind === "accepted") {
|
|
71
|
+
enqueueRunDispatchIntent(tx, { activityRef, now: input.now });
|
|
72
|
+
}
|
|
73
|
+
return retry;
|
|
66
74
|
});
|
|
67
75
|
if (materialized.disposition.kind !== "accepted") {
|
|
68
76
|
return materialized;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type ActivityRef } from "@tutti/shared/ids";
|
|
2
2
|
import type { RunSchedulerNowDisposition, RunSchedulerNowPayload, ProcedureLane } from "@tutti/shared/schemas/api";
|
|
3
3
|
import type { StartRunResult } from "../run-engine/index.js";
|
|
4
|
-
import type
|
|
4
|
+
import { type HostProjectStore } from "../store/index.js";
|
|
5
5
|
import type { ControlPlaneCommandResult, RunPipelineResolver } from "./types.js";
|
|
6
6
|
import type { StartRunForTaskInput } from "./run-start.js";
|
|
7
7
|
type ActiveActivity = {
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { createActivityRef, } from "@tutti/shared/ids";
|
|
2
2
|
import { markTaskRunStarted, readActiveClarificationProjection, readNextRunnableTask, readTaskDetailProjection, } from "../collaboration-state/index.js";
|
|
3
|
+
import { withHostStoreTransaction } from "../store/index.js";
|
|
4
|
+
import { enqueueRunDispatchIntent } from "./run-dispatch-intents.js";
|
|
3
5
|
function runControlPlaneScheduler(input) {
|
|
4
6
|
const activeActivity = input.readSchedulerBlocker();
|
|
5
7
|
if (activeActivity !== null) {
|
|
@@ -49,10 +51,16 @@ function runControlPlaneScheduler(input) {
|
|
|
49
51
|
return { disposition: { kind: "nothing_to_run" } };
|
|
50
52
|
}
|
|
51
53
|
const activityRef = createActivityRef();
|
|
52
|
-
const started =
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
const started = withHostStoreTransaction(input.store.db, (tx) => {
|
|
55
|
+
const materialized = markTaskRunStarted(tx, {
|
|
56
|
+
task_id: nextTaskId,
|
|
57
|
+
activity_ref: activityRef,
|
|
58
|
+
now: input.now,
|
|
59
|
+
});
|
|
60
|
+
if (materialized.kind === "started") {
|
|
61
|
+
enqueueRunDispatchIntent(tx, { activityRef, now: input.now });
|
|
62
|
+
}
|
|
63
|
+
return materialized;
|
|
56
64
|
});
|
|
57
65
|
if (started.kind !== "started") {
|
|
58
66
|
return { disposition: { kind: "nothing_to_run" } };
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { createWorkflowInvocationRef, type ActivityRef } from "@tutti/shared/ids";
|
|
1
|
+
import { createWorkflowInvocationRef, type ActivityRef, type ControlPlaneIntentId } from "@tutti/shared/ids";
|
|
2
2
|
import type { RefreshScratchpadDisposition, RefreshScratchpadResult, ProcedureLane } from "@tutti/shared/schemas/api";
|
|
3
3
|
import type { ScratchpadSourceCursorUpdate } from "../collaboration-state/scratchpad-source-state.js";
|
|
4
|
-
import type { ScratchpadSourceRefInput } from "../collaboration-state/types.js";
|
|
4
|
+
import type { ScratchpadSourceRefInput, TaskCompileSubmissionRecord } from "../collaboration-state/types.js";
|
|
5
5
|
import type { ProcedureEngine, ProcedureEngineState } from "../procedure-engine/index.js";
|
|
6
6
|
import type { HostProjectStore } from "../store/index.js";
|
|
7
7
|
import type { WorkspaceEventBus } from "../server-shell/http/workspace-events.js";
|
|
8
8
|
import type { ControlPlaneCommandResult, ControlPlaneLogger } from "./types.js";
|
|
9
|
+
import type { IntentRuntimeTerminal } from "./intent-dispatcher.js";
|
|
9
10
|
import type { ProcedureWorkflowRunner, ProcedureWorkflowRunnerResolver } from "./workflows/index.js";
|
|
10
11
|
type ScratchpadProjectContext = {
|
|
11
12
|
workspaceRoot: string;
|
|
@@ -18,6 +19,7 @@ export type ScratchpadSourceBatch = {
|
|
|
18
19
|
};
|
|
19
20
|
export declare function readControlPlaneScratchpadSourceBatch(store: HostProjectStore, options?: {
|
|
20
21
|
projectContext?: ScratchpadProjectContext | undefined;
|
|
22
|
+
sourceWindow?: TaskCompileSubmissionRecord["source_window"];
|
|
21
23
|
now?: () => Date;
|
|
22
24
|
}): ScratchpadSourceBatch;
|
|
23
25
|
export declare function runControlPlaneScratchpadRefreshProcedure(options: {
|
|
@@ -27,6 +29,9 @@ export declare function runControlPlaneScratchpadRefreshProcedure(options: {
|
|
|
27
29
|
store: HostProjectStore;
|
|
28
30
|
events: WorkspaceEventBus;
|
|
29
31
|
sourceBatch: ScratchpadSourceBatch;
|
|
32
|
+
lane: ProcedureLane;
|
|
33
|
+
recordRecoveryIntent?: boolean;
|
|
34
|
+
recoveryIntentId?: ControlPlaneIntentId;
|
|
30
35
|
now: () => Date;
|
|
31
36
|
}): Promise<{
|
|
32
37
|
kind: "needs_human";
|
|
@@ -57,6 +62,9 @@ export declare function startControlPlaneScratchpadRefresh(options: {
|
|
|
57
62
|
logger?: ControlPlaneLogger | undefined;
|
|
58
63
|
now: () => Date;
|
|
59
64
|
publishProcedureTransition: (state: ProcedureEngineState) => void;
|
|
65
|
+
recordRecoveryIntent?: boolean;
|
|
66
|
+
recoveryIntentId?: ControlPlaneIntentId;
|
|
67
|
+
onTerminal?: (result?: IntentRuntimeTerminal) => void;
|
|
60
68
|
}): ControlPlaneCommandResult<RefreshScratchpadDisposition, RefreshScratchpadResult>;
|
|
61
69
|
export {};
|
|
62
70
|
//# sourceMappingURL=scratchpad-refresh-start.d.ts.map
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { createWorkflowInvocationRef, } from "@tutti/shared/ids";
|
|
2
2
|
import { readActiveTaskCompileContext, readMainChatMessagesForScratchpadCycle, readScratchpadSourceState, readWorklistProjection, markScratchpadRefreshFailed, markScratchpadRefreshStarted, } from "../collaboration-state/index.js";
|
|
3
3
|
import { readProjectBriefProjection, readProjectBriefSourceDocuments, } from "../project-brief/index.js";
|
|
4
|
+
import { withHostStoreTransaction } from "../store/index.js";
|
|
4
5
|
import { logProcedureExecutionError } from "./procedure-logging.js";
|
|
6
|
+
import { enqueueControlPlaneIntent, readControlPlaneIntentByDedupeKey, settlePendingControlPlaneIntent, } from "./dispatch-intents.js";
|
|
5
7
|
import { applyScratchpadRefreshOutput } from "./scratchpad-refresh.js";
|
|
6
8
|
import { compactScratchpadSourceMessages, compactScratchpadWorklist, filterScratchpadSourceMessages, MAX_SOURCE_MESSAGES_TOTAL_CHARS, MIN_SOURCE_MESSAGE_TEXT_CHARS, ScratchpadSourceBudgetExceededError, } from "./scratchpad-source-messages.js";
|
|
7
9
|
const SCRATCHPAD_REFRESH_FAILURE_BACKOFF_MS = 60_000;
|
|
@@ -76,7 +78,14 @@ function fitScratchpadRefreshWorkflowInput(input) {
|
|
|
76
78
|
}
|
|
77
79
|
export function readControlPlaneScratchpadSourceBatch(store, options = {}) {
|
|
78
80
|
const sourceState = readScratchpadSourceState(store.db);
|
|
79
|
-
const mainChatMessages = readMainChatMessagesForScratchpadCycle(store.db, sourceState.cycle_start_cursor)
|
|
81
|
+
const mainChatMessages = readMainChatMessagesForScratchpadCycle(store.db, options.sourceWindow?.after ?? sourceState.cycle_start_cursor).filter((message) => {
|
|
82
|
+
const through = options.sourceWindow?.through;
|
|
83
|
+
if (through === undefined) {
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
const timeOrder = message.created_at.localeCompare(through.created_at);
|
|
87
|
+
return timeOrder < 0 || (timeOrder === 0 && message.id <= through.message_id);
|
|
88
|
+
});
|
|
80
89
|
const sourceMessages = filterScratchpadSourceMessages(mainChatMessages);
|
|
81
90
|
const latestSource = sourceMessages.at(-1);
|
|
82
91
|
const unrefreshedSourceCount = sourceMessages.filter((message) => {
|
|
@@ -95,9 +104,7 @@ export function readControlPlaneScratchpadSourceBatch(store, options = {}) {
|
|
|
95
104
|
});
|
|
96
105
|
const workflowInput = fitScratchpadRefreshWorkflowInput({
|
|
97
106
|
...(projectBrief === undefined ? {} : { projectBrief }),
|
|
98
|
-
...(activeContext === null
|
|
99
|
-
? {}
|
|
100
|
-
: { activeTaskCompile: activeContext.scratchpad_snapshot }),
|
|
107
|
+
...(activeContext === null ? {} : { activeTaskCompile: activeContext.scratchpad_snapshot }),
|
|
101
108
|
worklist: readWorklistProjection(store.db),
|
|
102
109
|
sourceMessages,
|
|
103
110
|
});
|
|
@@ -120,9 +127,24 @@ export function readControlPlaneScratchpadSourceBatch(store, options = {}) {
|
|
|
120
127
|
};
|
|
121
128
|
}
|
|
122
129
|
export async function runControlPlaneScratchpadRefreshProcedure(options) {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
130
|
+
withHostStoreTransaction(options.store.db, (tx) => {
|
|
131
|
+
let intentId = options.recoveryIntentId;
|
|
132
|
+
if (options.recordRecoveryIntent !== false) {
|
|
133
|
+
intentId = enqueueControlPlaneIntent(tx, {
|
|
134
|
+
kind: "scratchpad_refresh",
|
|
135
|
+
ownerKind: "scratchpad",
|
|
136
|
+
ownerRef: options.activityRef,
|
|
137
|
+
dedupeKey: `scratchpad_refresh:${options.activityRef}`,
|
|
138
|
+
lane: options.lane,
|
|
139
|
+
priority: options.lane === "scratchpad_background" ? 80 : 40,
|
|
140
|
+
now: options.now,
|
|
141
|
+
}).intent.intent_id;
|
|
142
|
+
}
|
|
143
|
+
markScratchpadRefreshStarted(tx, {
|
|
144
|
+
activityRef: options.activityRef,
|
|
145
|
+
...(intentId === undefined ? {} : { intentId }),
|
|
146
|
+
now: options.now,
|
|
147
|
+
});
|
|
126
148
|
});
|
|
127
149
|
let output;
|
|
128
150
|
try {
|
|
@@ -196,7 +218,27 @@ export function startControlPlaneScratchpadRefresh(options) {
|
|
|
196
218
|
title: "Refresh Scratchpad",
|
|
197
219
|
summary: "Refreshing Scratchpad from main chat.",
|
|
198
220
|
now: options.now,
|
|
199
|
-
onTransition:
|
|
221
|
+
onTransition: (state) => {
|
|
222
|
+
if (state.transition_kind === "terminal" && options.recordRecoveryIntent !== false) {
|
|
223
|
+
const intent = readControlPlaneIntentByDedupeKey(options.store.db, `scratchpad_refresh:${state.activity.activity_ref}`);
|
|
224
|
+
if (intent?.state === "pending") {
|
|
225
|
+
settlePendingControlPlaneIntent(options.store.db, {
|
|
226
|
+
intentId: intent.intent_id,
|
|
227
|
+
state: state.activity.status === "failed" ? "failed" : "completed",
|
|
228
|
+
...(state.activity.status === "failed"
|
|
229
|
+
? { errorCode: "scratchpad_refresh_failed" }
|
|
230
|
+
: {}),
|
|
231
|
+
now: options.now,
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
options.publishProcedureTransition(state);
|
|
236
|
+
if (state.transition_kind === "terminal") {
|
|
237
|
+
options.onTerminal?.(state.activity.status === "failed"
|
|
238
|
+
? { state: "failed", errorCode: "scratchpad_refresh_failed" }
|
|
239
|
+
: undefined);
|
|
240
|
+
}
|
|
241
|
+
},
|
|
200
242
|
onError: ({ context, error }) => logProcedureExecutionError({
|
|
201
243
|
logger: options.logger,
|
|
202
244
|
workflowKind: "scratchpad_refresh",
|
|
@@ -211,6 +253,13 @@ export function startControlPlaneScratchpadRefresh(options) {
|
|
|
211
253
|
store: options.store,
|
|
212
254
|
events: options.events,
|
|
213
255
|
sourceBatch,
|
|
256
|
+
lane: options.lane ?? "foreground",
|
|
257
|
+
...(options.recordRecoveryIntent === undefined
|
|
258
|
+
? {}
|
|
259
|
+
: { recordRecoveryIntent: options.recordRecoveryIntent }),
|
|
260
|
+
...(options.recoveryIntentId === undefined
|
|
261
|
+
? {}
|
|
262
|
+
: { recoveryIntentId: options.recoveryIntentId }),
|
|
214
263
|
now: options.now,
|
|
215
264
|
}),
|
|
216
265
|
});
|
|
@@ -5,7 +5,7 @@ import type { WorkspaceEventBus } from "../server-shell/http/workspace-events.js
|
|
|
5
5
|
import { type HostProjectStore } from "../store/index.js";
|
|
6
6
|
import type { StartupRecoveryOptions, StartupRecoveryResult } from "./types.js";
|
|
7
7
|
type LastRecoveryConclusion = NonNullable<ExecutionStatusProjection["last_conclusion"]>;
|
|
8
|
-
export type
|
|
8
|
+
export type StartupRecoveryEnqueueSealedRound = (sealed: SealedClarificationRoundWithoutSuccessor, roundId: ClarificationRoundRef) => boolean;
|
|
9
9
|
export declare function runControlPlaneStartupRecovery(input: {
|
|
10
10
|
store: HostProjectStore;
|
|
11
11
|
events: WorkspaceEventBus;
|
|
@@ -13,7 +13,7 @@ export declare function runControlPlaneStartupRecovery(input: {
|
|
|
13
13
|
now: () => Date;
|
|
14
14
|
getExecutionStatus: () => ExecutionStatusProjection;
|
|
15
15
|
setLastRecoveryConclusion: (conclusion: LastRecoveryConclusion) => void;
|
|
16
|
-
|
|
16
|
+
enqueueSealedRound: StartupRecoveryEnqueueSealedRound;
|
|
17
17
|
}): StartupRecoveryResult;
|
|
18
18
|
export {};
|
|
19
19
|
//# sourceMappingURL=startup-recovery.d.ts.map
|
|
@@ -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;
|