@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,7 +1,7 @@
|
|
|
1
|
-
import { readActiveTaskCompileContext, readWorklistProjection, } from "../collaboration-state/index.js";
|
|
1
|
+
import { readActiveClarificationProjection, readActiveTaskCompileContext, markScratchpadRefreshSucceeded, readPendingTaskCompileSubmission, readTaskProjectionByActivityRef, readScratchpadSourceState, readWorklistProjection, } from "../collaboration-state/index.js";
|
|
2
2
|
import { ProcedureEngine } from "../procedure-engine/index.js";
|
|
3
3
|
import { RunEngine } from "../run-engine/index.js";
|
|
4
|
-
import { WorkspaceOpsError, readReferenceSummaryTargets
|
|
4
|
+
import { WorkspaceOpsError, readReferenceSummaryTargets } from "../workspace-ops/index.js";
|
|
5
5
|
import { publishProcedureTransition as publishProcedureTransitionEvent, publishRunResultRecorded as publishRunResultRecordedEvent, publishRunTransition as publishRunTransitionEvent, publishTaskRunStarted as publishTaskRunStartedEvent, } from "./event-publishers.js";
|
|
6
6
|
import { startContextSyncAnalysisProcedure } from "./context-sync.js";
|
|
7
7
|
import { refreshProjectBriefProjection, } from "./project-brief-refresh.js";
|
|
@@ -11,14 +11,16 @@ import { readControlPlaneExecutionStatus } from "./execution-status.js";
|
|
|
11
11
|
import { ScratchpadAutoRefreshScheduler } from "./scratchpad-auto-refresh.js";
|
|
12
12
|
import { startControlPlaneScratchpadRefresh } from "./scratchpad-refresh-start.js";
|
|
13
13
|
import { shouldUseMessageAsScratchpadSource } from "./scratchpad-source-messages.js";
|
|
14
|
-
import { startControlPlaneTaskCompile } from "./task-compile-start.js";
|
|
14
|
+
import { resumeControlPlaneTaskCompileChain, resumePendingTaskCompileSubmission, startControlPlaneTaskCompile, } from "./task-compile-start.js";
|
|
15
15
|
import { startControlPlaneTaskCompileContinuation } from "./task-compile-continuation.js";
|
|
16
16
|
import { startControlPlaneTaskBoundFollowUpCheck } from "./follow-up-start.js";
|
|
17
17
|
import { runControlPlaneSchedulerForTaskArrival, runControlPlaneSchedulerNow, } from "./run-scheduler.js";
|
|
18
18
|
import { startControlPlaneRunForTask } from "./run-start.js";
|
|
19
19
|
import { retryControlPlaneTask } from "./run-retry.js";
|
|
20
|
-
import { runSendClarificationRoundMessage, runSubmitClarificationRound, } from "./clarification-commands.js";
|
|
20
|
+
import { enqueueClarificationSuccessorIntent, runSendClarificationRoundMessage, runSubmitClarificationRound, } from "./clarification-commands.js";
|
|
21
|
+
import { ControlPlaneIntentDispatcher, } from "./intent-dispatcher.js";
|
|
21
22
|
import { emitTaskCompletedResultMessage, emitTaskFailedResultMessage, } from "./worklist-terminal-feedback.js";
|
|
23
|
+
const WORKLIST_COMPLETION_CONTEXT_SYNC_GRACE_MS = 30_000;
|
|
22
24
|
export class Phase5ControlPlane {
|
|
23
25
|
engine;
|
|
24
26
|
runEngine;
|
|
@@ -33,12 +35,16 @@ export class Phase5ControlPlane {
|
|
|
33
35
|
runLifecycle;
|
|
34
36
|
now;
|
|
35
37
|
scratchpadAutoRefreshScheduler;
|
|
38
|
+
intentDispatcher;
|
|
39
|
+
pendingWorklistCompletionContextSyncTimer = null;
|
|
36
40
|
pendingReferenceSummaryPaths = new Set();
|
|
37
41
|
referenceSummaryWaiters = new Map();
|
|
38
42
|
pendingAllReferenceSummaries = false;
|
|
39
43
|
referenceSummaryStartScheduled = false;
|
|
40
44
|
closing = false;
|
|
41
45
|
asyncOwners = new Set();
|
|
46
|
+
pendingRunInputs = new Map();
|
|
47
|
+
runIntentTerminals = new Map();
|
|
42
48
|
lastRecoveryConclusion = null;
|
|
43
49
|
constructor(options) {
|
|
44
50
|
this.store = options.store;
|
|
@@ -59,6 +65,11 @@ export class Phase5ControlPlane {
|
|
|
59
65
|
readScratchpadRefreshBlocker: () => this.readScratchpadRefreshBlocker(),
|
|
60
66
|
startScratchpadRefresh: () => this.startScratchpadRefresh({ lane: "scratchpad_background" }),
|
|
61
67
|
});
|
|
68
|
+
this.intentDispatcher = new ControlPlaneIntentDispatcher({
|
|
69
|
+
store: this.store,
|
|
70
|
+
now: this.now,
|
|
71
|
+
handler: (intent, runtime) => this.dispatchDurableIntent(intent, runtime),
|
|
72
|
+
});
|
|
62
73
|
}
|
|
63
74
|
getExecutionStatus() {
|
|
64
75
|
return readControlPlaneExecutionStatus({
|
|
@@ -97,7 +108,7 @@ export class Phase5ControlPlane {
|
|
|
97
108
|
});
|
|
98
109
|
}
|
|
99
110
|
runStartupRecovery(options = {}) {
|
|
100
|
-
|
|
111
|
+
const recovered = runControlPlaneStartupRecovery({
|
|
101
112
|
store: this.store,
|
|
102
113
|
events: this.events,
|
|
103
114
|
options,
|
|
@@ -106,12 +117,20 @@ export class Phase5ControlPlane {
|
|
|
106
117
|
setLastRecoveryConclusion: (conclusion) => {
|
|
107
118
|
this.lastRecoveryConclusion = conclusion;
|
|
108
119
|
},
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
120
|
+
enqueueSealedRound: (sealed, roundId) => {
|
|
121
|
+
const ensured = enqueueClarificationSuccessorIntent(this.store.db, {
|
|
122
|
+
owner: sealed.thread.owner,
|
|
123
|
+
roundId,
|
|
124
|
+
now: this.now,
|
|
125
|
+
}).intent;
|
|
126
|
+
return (ensured.state === "pending" || ensured.state === "claimed" || ensured.state === "running");
|
|
127
|
+
},
|
|
112
128
|
});
|
|
129
|
+
this.intentDispatcher.start();
|
|
130
|
+
return recovered;
|
|
113
131
|
}
|
|
114
132
|
startScratchpadRefresh(options = {}) {
|
|
133
|
+
this.cancelPendingWorklistCompletionContextSync();
|
|
115
134
|
return startControlPlaneScratchpadRefresh({
|
|
116
135
|
engine: this.engine,
|
|
117
136
|
store: this.store,
|
|
@@ -128,6 +147,7 @@ export class Phase5ControlPlane {
|
|
|
128
147
|
if (!shouldUseMessageAsScratchpadSource(message)) {
|
|
129
148
|
return;
|
|
130
149
|
}
|
|
150
|
+
this.cancelPendingWorklistCompletionContextSync();
|
|
131
151
|
this.scratchpadAutoRefreshScheduler.notifySourceChanged();
|
|
132
152
|
}
|
|
133
153
|
refreshProjectBrief(options) {
|
|
@@ -148,8 +168,10 @@ export class Phase5ControlPlane {
|
|
|
148
168
|
}
|
|
149
169
|
this.closing = true;
|
|
150
170
|
this.scratchpadAutoRefreshScheduler.dispose();
|
|
171
|
+
this.cancelPendingWorklistCompletionContextSync();
|
|
151
172
|
this.pendingAllReferenceSummaries = false;
|
|
152
173
|
this.pendingReferenceSummaryPaths.clear();
|
|
174
|
+
this.intentDispatcher.dispose();
|
|
153
175
|
}
|
|
154
176
|
dispose() {
|
|
155
177
|
this.beginShutdown();
|
|
@@ -171,6 +193,7 @@ export class Phase5ControlPlane {
|
|
|
171
193
|
}
|
|
172
194
|
}
|
|
173
195
|
startTaskCompile(payload) {
|
|
196
|
+
this.cancelPendingWorklistCompletionContextSync();
|
|
174
197
|
const activeRun = this.readActiveRun();
|
|
175
198
|
if (activeRun !== null) {
|
|
176
199
|
return {
|
|
@@ -211,6 +234,9 @@ export class Phase5ControlPlane {
|
|
|
211
234
|
});
|
|
212
235
|
}
|
|
213
236
|
startContextSyncAnalysis(trigger = { kind: "manual" }) {
|
|
237
|
+
if (trigger.kind === "manual") {
|
|
238
|
+
this.cancelPendingWorklistCompletionContextSync();
|
|
239
|
+
}
|
|
214
240
|
return startContextSyncAnalysisProcedure({
|
|
215
241
|
engine: this.engine,
|
|
216
242
|
store: this.store,
|
|
@@ -305,8 +331,7 @@ export class Phase5ControlPlane {
|
|
|
305
331
|
payload,
|
|
306
332
|
commandId,
|
|
307
333
|
now: this.now,
|
|
308
|
-
|
|
309
|
-
startTaskBoundFollowUpCheck: (owner, threadId, submittedRoundId) => this.startTaskBoundFollowUpCheck(owner, threadId, submittedRoundId),
|
|
334
|
+
wakeIntentDispatcher: () => this.intentDispatcher.wake(),
|
|
310
335
|
});
|
|
311
336
|
}
|
|
312
337
|
async waitForIdle() {
|
|
@@ -348,6 +373,7 @@ export class Phase5ControlPlane {
|
|
|
348
373
|
});
|
|
349
374
|
}
|
|
350
375
|
startRunForTask(input) {
|
|
376
|
+
this.pendingRunInputs.set(input.activity_ref, input);
|
|
351
377
|
const result = startControlPlaneRunForTask({
|
|
352
378
|
runEngine: this.runEngine,
|
|
353
379
|
store: this.store,
|
|
@@ -366,8 +392,10 @@ export class Phase5ControlPlane {
|
|
|
366
392
|
publishRunResultRecorded: (recorded) => this.publishRunResultRecorded(recorded),
|
|
367
393
|
});
|
|
368
394
|
if (result.kind === "accepted") {
|
|
395
|
+
this.cancelPendingWorklistCompletionContextSync();
|
|
369
396
|
this.runLifecycle?.started();
|
|
370
397
|
}
|
|
398
|
+
this.intentDispatcher.wake();
|
|
371
399
|
return result;
|
|
372
400
|
}
|
|
373
401
|
readActiveRun() {
|
|
@@ -450,9 +478,14 @@ export class Phase5ControlPlane {
|
|
|
450
478
|
this.scratchpadAutoRefreshScheduler.notifyActivityChanged();
|
|
451
479
|
if (state.transition_kind === "terminal") {
|
|
452
480
|
this.schedulePendingReferenceSummaryRefresh();
|
|
481
|
+
this.intentDispatcher.wake();
|
|
453
482
|
}
|
|
454
483
|
}
|
|
455
484
|
handleRunTerminalTransition(state) {
|
|
485
|
+
const activityRef = state.activity.activity_ref;
|
|
486
|
+
this.runIntentTerminals.get(activityRef)?.();
|
|
487
|
+
this.runIntentTerminals.delete(activityRef);
|
|
488
|
+
this.pendingRunInputs.delete(activityRef);
|
|
456
489
|
if (state.activity.status === "finished") {
|
|
457
490
|
if (state.activity.task_id !== undefined) {
|
|
458
491
|
emitTaskCompletedResultMessage({
|
|
@@ -497,7 +530,7 @@ export class Phase5ControlPlane {
|
|
|
497
530
|
if (this.tryRunTaskArrivalScheduler()) {
|
|
498
531
|
return;
|
|
499
532
|
}
|
|
500
|
-
this.
|
|
533
|
+
this.scheduleContextSyncAfterWorklistCompletion();
|
|
501
534
|
}
|
|
502
535
|
worklistCompletionContextSyncTrigger() {
|
|
503
536
|
const worklist = readWorklistProjection(this.store.db);
|
|
@@ -511,17 +544,47 @@ export class Phase5ControlPlane {
|
|
|
511
544
|
module_count: worklist.modules.length,
|
|
512
545
|
};
|
|
513
546
|
}
|
|
514
|
-
|
|
515
|
-
if (this.closing
|
|
547
|
+
scheduleContextSyncAfterWorklistCompletion() {
|
|
548
|
+
if (this.closing ||
|
|
549
|
+
!this.store.db.open ||
|
|
550
|
+
this.pendingWorklistCompletionContextSyncTimer !== null) {
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
const sourceState = readScratchpadSourceState(this.store.db);
|
|
554
|
+
if (this.worklistCompletionContextSyncTrigger() === undefined ||
|
|
555
|
+
sourceState.dirty_since !== undefined ||
|
|
556
|
+
sourceState.in_flight !== undefined ||
|
|
557
|
+
this.readSchedulerBlocker() !== null) {
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
this.pendingWorklistCompletionContextSyncTimer = setTimeout(() => {
|
|
561
|
+
this.attemptContextSyncAfterWorklistCompletion();
|
|
562
|
+
}, WORKLIST_COMPLETION_CONTEXT_SYNC_GRACE_MS);
|
|
563
|
+
this.pendingWorklistCompletionContextSyncTimer.unref?.();
|
|
564
|
+
}
|
|
565
|
+
attemptContextSyncAfterWorklistCompletion() {
|
|
566
|
+
this.pendingWorklistCompletionContextSyncTimer = null;
|
|
567
|
+
if (this.closing || !this.store.db.open) {
|
|
516
568
|
return;
|
|
517
569
|
}
|
|
518
570
|
const trigger = this.worklistCompletionContextSyncTrigger();
|
|
519
|
-
|
|
571
|
+
const sourceState = readScratchpadSourceState(this.store.db);
|
|
572
|
+
if (trigger === undefined ||
|
|
573
|
+
sourceState.dirty_since !== undefined ||
|
|
574
|
+
sourceState.in_flight !== undefined ||
|
|
575
|
+
this.readSchedulerBlocker() !== null) {
|
|
520
576
|
return;
|
|
521
577
|
}
|
|
522
578
|
void this.startContextSyncAnalysis(trigger);
|
|
523
579
|
}
|
|
524
|
-
|
|
580
|
+
cancelPendingWorklistCompletionContextSync() {
|
|
581
|
+
if (this.pendingWorklistCompletionContextSyncTimer === null) {
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
584
|
+
clearTimeout(this.pendingWorklistCompletionContextSyncTimer);
|
|
585
|
+
this.pendingWorklistCompletionContextSyncTimer = null;
|
|
586
|
+
}
|
|
587
|
+
startTaskCompileContinuation(owner, roundId, onTerminal) {
|
|
525
588
|
let tasksAdded = false;
|
|
526
589
|
return startControlPlaneTaskCompileContinuation({
|
|
527
590
|
engine: this.engine,
|
|
@@ -545,9 +608,10 @@ export class Phase5ControlPlane {
|
|
|
545
608
|
onTasksAdded: () => {
|
|
546
609
|
tasksAdded = true;
|
|
547
610
|
},
|
|
611
|
+
...(onTerminal === undefined ? {} : { onTerminal }),
|
|
548
612
|
});
|
|
549
613
|
}
|
|
550
|
-
startTaskBoundFollowUpCheck(owner, threadId, roundId) {
|
|
614
|
+
startTaskBoundFollowUpCheck(owner, threadId, roundId, onTerminal) {
|
|
551
615
|
return startControlPlaneTaskBoundFollowUpCheck({
|
|
552
616
|
engine: this.engine,
|
|
553
617
|
store: this.store,
|
|
@@ -567,9 +631,236 @@ export class Phase5ControlPlane {
|
|
|
567
631
|
}
|
|
568
632
|
},
|
|
569
633
|
shouldContinue: () => !this.closing,
|
|
634
|
+
...(onTerminal === undefined ? {} : { onTerminal }),
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
dispatchDurableIntent(intent, runtime) {
|
|
638
|
+
switch (intent.kind) {
|
|
639
|
+
case "run_dispatch":
|
|
640
|
+
return this.dispatchRunIntent(intent, runtime.terminal);
|
|
641
|
+
case "scratchpad_refresh":
|
|
642
|
+
return this.dispatchScratchpadIntent(intent, runtime.terminal);
|
|
643
|
+
case "reference_summary_refresh":
|
|
644
|
+
return this.dispatchReferenceSummaryIntent(runtime.terminal);
|
|
645
|
+
case "task_compile_start":
|
|
646
|
+
return this.dispatchTaskCompileStartIntent(intent, runtime.terminal);
|
|
647
|
+
case "task_compile_continuation":
|
|
648
|
+
case "task_bound_follow_up":
|
|
649
|
+
return this.dispatchClarificationSuccessorIntent(intent, runtime.terminal);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
dispatchRunIntent(intent, terminal) {
|
|
653
|
+
const activityRef = intent.owner_ref;
|
|
654
|
+
const task = readTaskProjectionByActivityRef(this.store.db, activityRef);
|
|
655
|
+
if (task === null || task.status !== "running") {
|
|
656
|
+
return { kind: "obsolete", errorCode: "run_already_terminal" };
|
|
657
|
+
}
|
|
658
|
+
const activeRun = this.readActiveRun();
|
|
659
|
+
if (activeRun !== null) {
|
|
660
|
+
if (activeRun.activity_ref !== activityRef) {
|
|
661
|
+
return { kind: "busy" };
|
|
662
|
+
}
|
|
663
|
+
this.runIntentTerminals.set(activityRef, terminal);
|
|
664
|
+
return { kind: "accepted" };
|
|
665
|
+
}
|
|
666
|
+
const runInput = this.pendingRunInputs.get(activityRef);
|
|
667
|
+
if (runInput === undefined) {
|
|
668
|
+
return { kind: "not_ready", errorCode: "run_dispatch_context_unavailable" };
|
|
669
|
+
}
|
|
670
|
+
this.runIntentTerminals.set(activityRef, terminal);
|
|
671
|
+
const start = this.startRunForTask(runInput);
|
|
672
|
+
if (start.kind === "accepted") {
|
|
673
|
+
return { kind: "accepted" };
|
|
674
|
+
}
|
|
675
|
+
this.runIntentTerminals.delete(activityRef);
|
|
676
|
+
return { kind: "busy" };
|
|
677
|
+
}
|
|
678
|
+
dispatchScratchpadIntent(intent, terminal) {
|
|
679
|
+
const sourceState = readScratchpadSourceState(this.store.db);
|
|
680
|
+
if (sourceState.in_flight !== undefined) {
|
|
681
|
+
return { kind: "busy" };
|
|
682
|
+
}
|
|
683
|
+
if (sourceState.dirty_since === undefined) {
|
|
684
|
+
return { kind: "obsolete", errorCode: "scratchpad_source_clean" };
|
|
685
|
+
}
|
|
686
|
+
if (sourceState.backoff_until !== undefined &&
|
|
687
|
+
new Date(sourceState.backoff_until).getTime() > this.now().getTime()) {
|
|
688
|
+
return {
|
|
689
|
+
kind: "not_ready",
|
|
690
|
+
errorCode: "scratchpad_backoff_active",
|
|
691
|
+
availableAt: sourceState.backoff_until,
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
const result = startControlPlaneScratchpadRefresh({
|
|
695
|
+
engine: this.engine,
|
|
696
|
+
store: this.store,
|
|
697
|
+
events: this.events,
|
|
698
|
+
resolveWorkflowRunner: this.resolveWorkflowRunner,
|
|
699
|
+
projectContext: this.projectContext,
|
|
700
|
+
lane: intent.lane === "scratchpad_background" ? "scratchpad_background" : "foreground",
|
|
701
|
+
logger: this.logger,
|
|
702
|
+
now: this.now,
|
|
703
|
+
publishProcedureTransition: (state) => this.publishProcedureTransition(state),
|
|
704
|
+
recordRecoveryIntent: false,
|
|
705
|
+
recoveryIntentId: intent.intent_id,
|
|
706
|
+
onTerminal: terminal,
|
|
707
|
+
});
|
|
708
|
+
if (result.disposition.kind === "accepted") {
|
|
709
|
+
return { kind: "accepted" };
|
|
710
|
+
}
|
|
711
|
+
if (result.disposition.kind === "already_running") {
|
|
712
|
+
return { kind: "busy" };
|
|
713
|
+
}
|
|
714
|
+
if (result.disposition.kind === "no_source_messages") {
|
|
715
|
+
markScratchpadRefreshSucceeded(this.store.db, { now: this.now });
|
|
716
|
+
return { kind: "obsolete", errorCode: "scratchpad_source_empty" };
|
|
717
|
+
}
|
|
718
|
+
if (result.disposition.kind === "provider_not_configured") {
|
|
719
|
+
return { kind: "not_ready", errorCode: "provider_not_configured" };
|
|
720
|
+
}
|
|
721
|
+
return { kind: "not_ready", errorCode: "provider_unavailable" };
|
|
722
|
+
}
|
|
723
|
+
dispatchReferenceSummaryIntent(terminal) {
|
|
724
|
+
const result = startReferenceSummaryRefreshProcedure({
|
|
725
|
+
engine: this.engine,
|
|
726
|
+
store: this.store,
|
|
727
|
+
events: this.events,
|
|
728
|
+
resolveWorkflowRunner: this.resolveWorkflowRunner,
|
|
729
|
+
readActiveActivity: () => this.readSchedulerBlocker(),
|
|
730
|
+
projectContext: this.projectContext,
|
|
731
|
+
logger: this.logger,
|
|
732
|
+
now: this.now,
|
|
733
|
+
publishProcedureTransition: (state) => this.publishProcedureTransition(state),
|
|
734
|
+
recordRecoveryIntent: false,
|
|
735
|
+
onTerminal: terminal,
|
|
736
|
+
});
|
|
737
|
+
if (result.disposition.kind === "accepted") {
|
|
738
|
+
return { kind: "accepted" };
|
|
739
|
+
}
|
|
740
|
+
if (result.disposition.kind === "already_running") {
|
|
741
|
+
return { kind: "busy" };
|
|
742
|
+
}
|
|
743
|
+
if (result.disposition.kind === "no_targets") {
|
|
744
|
+
return { kind: "obsolete", errorCode: "reference_targets_terminal" };
|
|
745
|
+
}
|
|
746
|
+
if (result.disposition.kind === "provider_not_configured") {
|
|
747
|
+
return { kind: "not_ready", errorCode: "provider_not_configured" };
|
|
748
|
+
}
|
|
749
|
+
return { kind: "not_ready", errorCode: "provider_unavailable" };
|
|
750
|
+
}
|
|
751
|
+
dispatchTaskCompileStartIntent(intent, terminal) {
|
|
752
|
+
const activeContext = readActiveTaskCompileContext(this.store.db);
|
|
753
|
+
const activeClarification = readActiveClarificationProjection(this.store.db);
|
|
754
|
+
if (activeContext === null) {
|
|
755
|
+
const submission = readPendingTaskCompileSubmission(this.store.db, intent.owner_ref);
|
|
756
|
+
if (submission === null) {
|
|
757
|
+
return { kind: "obsolete", errorCode: "task_compile_context_absent" };
|
|
758
|
+
}
|
|
759
|
+
if (activeClarification.state === "active") {
|
|
760
|
+
return { kind: "not_ready", errorCode: "clarification_active" };
|
|
761
|
+
}
|
|
762
|
+
let tasksAdded = false;
|
|
763
|
+
return resumePendingTaskCompileSubmission({
|
|
764
|
+
engine: this.engine,
|
|
765
|
+
store: this.store,
|
|
766
|
+
events: this.events,
|
|
767
|
+
taskCompileWorkflowRef: submission.workflow_ref,
|
|
768
|
+
recoveryIntentId: intent.intent_id,
|
|
769
|
+
resolveWorkflowRunner: this.resolveWorkflowRunner,
|
|
770
|
+
projectContext: this.projectContext,
|
|
771
|
+
logger: this.logger,
|
|
772
|
+
now: this.now,
|
|
773
|
+
publishProcedureTransition: (state) => {
|
|
774
|
+
this.publishProcedureTransition(state);
|
|
775
|
+
if (!this.closing &&
|
|
776
|
+
tasksAdded &&
|
|
777
|
+
state.activity.status === "finished" &&
|
|
778
|
+
state.activity.workflow_kind === "task_compile") {
|
|
779
|
+
this.tryRunTaskArrivalScheduler();
|
|
780
|
+
}
|
|
781
|
+
},
|
|
782
|
+
registerReferenceSummaryWaiter: (activityRef, waiter) => {
|
|
783
|
+
const existing = this.referenceSummaryWaiters.get(activityRef) ?? [];
|
|
784
|
+
existing.push(waiter);
|
|
785
|
+
this.referenceSummaryWaiters.set(activityRef, existing);
|
|
786
|
+
},
|
|
787
|
+
onTasksAdded: () => {
|
|
788
|
+
tasksAdded = true;
|
|
789
|
+
},
|
|
790
|
+
shouldContinue: () => !this.closing,
|
|
791
|
+
onChainTerminal: terminal,
|
|
792
|
+
});
|
|
793
|
+
}
|
|
794
|
+
if (activeContext.workflow_ref !== intent.owner_ref) {
|
|
795
|
+
return { kind: "obsolete", errorCode: "task_compile_context_absent" };
|
|
796
|
+
}
|
|
797
|
+
if (activeClarification.state === "active") {
|
|
798
|
+
if (activeClarification.thread.owner.kind === "task_compile" &&
|
|
799
|
+
activeClarification.thread.owner.workflow_ref === activeContext.workflow_ref) {
|
|
800
|
+
return { kind: "obsolete", errorCode: "task_compile_waiting_for_human" };
|
|
801
|
+
}
|
|
802
|
+
return { kind: "not_ready", errorCode: "clarification_active" };
|
|
803
|
+
}
|
|
804
|
+
const foreground = this.readActiveForegroundProcedure();
|
|
805
|
+
if (foreground?.workflow_kind === "reference_summary_refresh" &&
|
|
806
|
+
this.referenceSummaryWaiters.has(foreground.activity_ref)) {
|
|
807
|
+
return { kind: "busy" };
|
|
808
|
+
}
|
|
809
|
+
let tasksAdded = false;
|
|
810
|
+
return resumeControlPlaneTaskCompileChain({
|
|
811
|
+
engine: this.engine,
|
|
812
|
+
store: this.store,
|
|
813
|
+
events: this.events,
|
|
814
|
+
taskCompileWorkflowRef: activeContext.workflow_ref,
|
|
815
|
+
resolveWorkflowRunner: this.resolveWorkflowRunner,
|
|
816
|
+
projectContext: this.projectContext,
|
|
817
|
+
logger: this.logger,
|
|
818
|
+
now: this.now,
|
|
819
|
+
publishProcedureTransition: (state) => {
|
|
820
|
+
this.publishProcedureTransition(state);
|
|
821
|
+
if (!this.closing &&
|
|
822
|
+
tasksAdded &&
|
|
823
|
+
state.activity.status === "finished" &&
|
|
824
|
+
state.activity.workflow_kind === "task_compile") {
|
|
825
|
+
this.tryRunTaskArrivalScheduler();
|
|
826
|
+
}
|
|
827
|
+
},
|
|
828
|
+
registerReferenceSummaryWaiter: (activityRef, waiter) => {
|
|
829
|
+
const existing = this.referenceSummaryWaiters.get(activityRef) ?? [];
|
|
830
|
+
existing.push(waiter);
|
|
831
|
+
this.referenceSummaryWaiters.set(activityRef, existing);
|
|
832
|
+
},
|
|
833
|
+
onTasksAdded: () => {
|
|
834
|
+
tasksAdded = true;
|
|
835
|
+
},
|
|
836
|
+
shouldContinue: () => !this.closing,
|
|
837
|
+
onChainTerminal: terminal,
|
|
570
838
|
});
|
|
571
839
|
}
|
|
840
|
+
dispatchClarificationSuccessorIntent(intent, terminal) {
|
|
841
|
+
const active = readActiveClarificationProjection(this.store.db);
|
|
842
|
+
if (active.state === "none" ||
|
|
843
|
+
active.thread.status !== "active" ||
|
|
844
|
+
active.round.id !== intent.owner_ref ||
|
|
845
|
+
active.round.status !== "sealed" ||
|
|
846
|
+
active.round.successor_status !== "not_materialized") {
|
|
847
|
+
return { kind: "obsolete", errorCode: "clarification_successor_not_required" };
|
|
848
|
+
}
|
|
849
|
+
if (intent.kind === "task_compile_continuation") {
|
|
850
|
+
if (active.thread.owner.kind !== "task_compile") {
|
|
851
|
+
return { kind: "obsolete", errorCode: "clarification_owner_kind_changed" };
|
|
852
|
+
}
|
|
853
|
+
return this.startTaskCompileContinuation(active.thread.owner, active.round.id, terminal);
|
|
854
|
+
}
|
|
855
|
+
if (active.thread.owner.kind !== "task") {
|
|
856
|
+
return { kind: "obsolete", errorCode: "clarification_owner_kind_changed" };
|
|
857
|
+
}
|
|
858
|
+
return this.startTaskBoundFollowUpCheck(active.thread.owner, active.thread.id, active.round.id, terminal);
|
|
859
|
+
}
|
|
572
860
|
publishProcedureTransition(state) {
|
|
861
|
+
if (state.transition_kind === "started" && state.activity.lane === "foreground") {
|
|
862
|
+
this.cancelPendingWorklistCompletionContextSync();
|
|
863
|
+
}
|
|
573
864
|
this.logProcedureTransition(state);
|
|
574
865
|
publishProcedureTransitionEvent({
|
|
575
866
|
events: this.events,
|
|
@@ -595,7 +886,13 @@ export class Phase5ControlPlane {
|
|
|
595
886
|
}
|
|
596
887
|
}
|
|
597
888
|
if (!this.closing) {
|
|
889
|
+
this.intentDispatcher.wake();
|
|
598
890
|
this.schedulePendingReferenceSummaryRefresh();
|
|
891
|
+
if (state.activity.status === "finished" &&
|
|
892
|
+
(state.activity.workflow_kind === "scratchpad_refresh" ||
|
|
893
|
+
state.activity.workflow_kind === "reference_summary_refresh")) {
|
|
894
|
+
this.scheduleContextSyncAfterWorklistCompletion();
|
|
895
|
+
}
|
|
599
896
|
}
|
|
600
897
|
}
|
|
601
898
|
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { HostProjectStore } from "../store/index.js";
|
|
2
|
+
import { type ControlPlaneIntent, type ControlPlaneIntentTerminalState } from "./dispatch-intents.js";
|
|
3
|
+
export type IntentRuntimeTerminal = {
|
|
4
|
+
state: ControlPlaneIntentTerminalState;
|
|
5
|
+
errorCode?: string;
|
|
6
|
+
};
|
|
7
|
+
export type IntentRuntimeStartResult = {
|
|
8
|
+
kind: "accepted";
|
|
9
|
+
} | {
|
|
10
|
+
kind: "busy";
|
|
11
|
+
} | {
|
|
12
|
+
kind: "not_ready";
|
|
13
|
+
errorCode: string;
|
|
14
|
+
availableAt?: string;
|
|
15
|
+
} | {
|
|
16
|
+
kind: "obsolete";
|
|
17
|
+
errorCode: string;
|
|
18
|
+
};
|
|
19
|
+
export type ControlPlaneIntentRuntimeHandler = (intent: ControlPlaneIntent, runtime: {
|
|
20
|
+
terminal: (result?: IntentRuntimeTerminal) => void;
|
|
21
|
+
}) => IntentRuntimeStartResult;
|
|
22
|
+
export declare class ControlPlaneIntentDispatcher {
|
|
23
|
+
private readonly store;
|
|
24
|
+
private readonly now;
|
|
25
|
+
private readonly handler;
|
|
26
|
+
private readonly reconciliationIntervalMs;
|
|
27
|
+
private readonly claimLeaseMs;
|
|
28
|
+
private readonly busyRetryMs;
|
|
29
|
+
private readonly notReadyRetryMs;
|
|
30
|
+
private readonly activeClaims;
|
|
31
|
+
private timer;
|
|
32
|
+
private wakeScheduled;
|
|
33
|
+
private draining;
|
|
34
|
+
private closing;
|
|
35
|
+
constructor(options: {
|
|
36
|
+
store: HostProjectStore;
|
|
37
|
+
handler: ControlPlaneIntentRuntimeHandler;
|
|
38
|
+
now?: () => Date;
|
|
39
|
+
reconciliationIntervalMs?: number;
|
|
40
|
+
claimLeaseMs?: number;
|
|
41
|
+
busyRetryMs?: number;
|
|
42
|
+
notReadyRetryMs?: number;
|
|
43
|
+
});
|
|
44
|
+
start(): void;
|
|
45
|
+
wake(): void;
|
|
46
|
+
dispose(): void;
|
|
47
|
+
private drain;
|
|
48
|
+
private renewActiveClaims;
|
|
49
|
+
private reconcileExpiredClaims;
|
|
50
|
+
private after;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=intent-dispatcher.d.ts.map
|