@telora/daemon 0.21.98 → 0.21.134
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/build-info.json +6 -3
- package/dist/audit/audit-hooks.d.ts +106 -0
- package/dist/audit/audit-hooks.d.ts.map +1 -1
- package/dist/audit/audit-hooks.js +149 -17
- package/dist/audit/audit-hooks.js.map +1 -1
- package/dist/ci/ci-sensor-loop.d.ts +6 -0
- package/dist/ci/ci-sensor-loop.d.ts.map +1 -1
- package/dist/ci/ci-sensor-loop.js +50 -3
- package/dist/ci/ci-sensor-loop.js.map +1 -1
- package/dist/completion/coverage-drift-state.d.ts +29 -2
- package/dist/completion/coverage-drift-state.d.ts.map +1 -1
- package/dist/completion/coverage-drift-state.js +42 -2
- package/dist/completion/coverage-drift-state.js.map +1 -1
- package/dist/completion/coverage-stabilized.d.ts +58 -0
- package/dist/completion/coverage-stabilized.d.ts.map +1 -0
- package/dist/completion/coverage-stabilized.js +84 -0
- package/dist/completion/coverage-stabilized.js.map +1 -0
- package/dist/completion/delivery-advancement-guard.d.ts +21 -0
- package/dist/completion/delivery-advancement-guard.d.ts.map +1 -1
- package/dist/completion/delivery-advancement-guard.js +31 -3
- package/dist/completion/delivery-advancement-guard.js.map +1 -1
- package/dist/completion/event-escalations.d.ts +15 -0
- package/dist/completion/event-escalations.d.ts.map +1 -1
- package/dist/completion/event-escalations.js +42 -4
- package/dist/completion/event-escalations.js.map +1 -1
- package/dist/completion/event-phases.d.ts.map +1 -1
- package/dist/completion/event-phases.js +5 -2
- package/dist/completion/event-phases.js.map +1 -1
- package/dist/completion/machine-remediable-demotion.d.ts +74 -0
- package/dist/completion/machine-remediable-demotion.d.ts.map +1 -0
- package/dist/completion/machine-remediable-demotion.js +147 -0
- package/dist/completion/machine-remediable-demotion.js.map +1 -0
- package/dist/completion/review-exit-phase.d.ts.map +1 -1
- package/dist/completion/review-exit-phase.js +10 -6
- package/dist/completion/review-exit-phase.js.map +1 -1
- package/dist/completion/settle-injection-sweep.d.ts +55 -0
- package/dist/completion/settle-injection-sweep.d.ts.map +1 -0
- package/dist/completion/settle-injection-sweep.js +88 -0
- package/dist/completion/settle-injection-sweep.js.map +1 -0
- package/dist/completion/state-cascade.d.ts.map +1 -1
- package/dist/completion/state-cascade.js +41 -0
- package/dist/completion/state-cascade.js.map +1 -1
- package/dist/completion/status-advance-phase.d.ts.map +1 -1
- package/dist/completion/status-advance-phase.js +18 -1
- package/dist/completion/status-advance-phase.js.map +1 -1
- package/dist/completion/team-lifecycle.d.ts +42 -0
- package/dist/completion/team-lifecycle.d.ts.map +1 -1
- package/dist/completion/team-lifecycle.js +71 -0
- package/dist/completion/team-lifecycle.js.map +1 -1
- package/dist/delivery/delivery-lifecycle.d.ts +19 -33
- package/dist/delivery/delivery-lifecycle.d.ts.map +1 -1
- package/dist/delivery/delivery-lifecycle.js +19 -147
- package/dist/delivery/delivery-lifecycle.js.map +1 -1
- package/dist/delivery/delivery-merge-ops.d.ts +46 -0
- package/dist/delivery/delivery-merge-ops.d.ts.map +1 -0
- package/dist/delivery/delivery-merge-ops.js +154 -0
- package/dist/delivery/delivery-merge-ops.js.map +1 -0
- package/dist/delivery/delivery-reconciler-apply.d.ts +28 -0
- package/dist/delivery/delivery-reconciler-apply.d.ts.map +1 -1
- package/dist/delivery/delivery-reconciler-apply.js +30 -0
- package/dist/delivery/delivery-reconciler-apply.js.map +1 -1
- package/dist/delivery/delivery-reconciler.d.ts +7 -2
- package/dist/delivery/delivery-reconciler.d.ts.map +1 -1
- package/dist/delivery/delivery-reconciler.js +34 -5
- package/dist/delivery/delivery-reconciler.js.map +1 -1
- package/dist/delivery/external-hold-surface.d.ts +32 -0
- package/dist/delivery/external-hold-surface.d.ts.map +1 -0
- package/dist/delivery/external-hold-surface.js +67 -0
- package/dist/delivery/external-hold-surface.js.map +1 -0
- package/dist/delivery/external-hold.d.ts +21 -0
- package/dist/delivery/external-hold.d.ts.map +1 -1
- package/dist/delivery/external-hold.js +40 -5
- package/dist/delivery/external-hold.js.map +1 -1
- package/dist/delivery/paused-revival.d.ts +140 -0
- package/dist/delivery/paused-revival.d.ts.map +1 -0
- package/dist/delivery/paused-revival.js +229 -0
- package/dist/delivery/paused-revival.js.map +1 -0
- package/dist/delivery/wait-reachability.d.ts.map +1 -1
- package/dist/delivery/wait-reachability.js +11 -0
- package/dist/delivery/wait-reachability.js.map +1 -1
- package/dist/directive/close-loop-dispatcher.d.ts +16 -263
- package/dist/directive/close-loop-dispatcher.d.ts.map +1 -1
- package/dist/directive/close-loop-dispatcher.js +21 -259
- package/dist/directive/close-loop-dispatcher.js.map +1 -1
- package/dist/directive/directive-dispatch.d.ts +19 -0
- package/dist/directive/directive-dispatch.d.ts.map +1 -1
- package/dist/directive/directive-dispatch.js +18 -0
- package/dist/directive/directive-dispatch.js.map +1 -1
- package/dist/directive/directive-executor.d.ts +1 -3
- package/dist/directive/directive-executor.d.ts.map +1 -1
- package/dist/directive/directive-executor.js +23 -21
- package/dist/directive/directive-executor.js.map +1 -1
- package/dist/directive/phase-sync.d.ts +28 -15
- package/dist/directive/phase-sync.d.ts.map +1 -1
- package/dist/directive/phase-sync.js +28 -36
- package/dist/directive/phase-sync.js.map +1 -1
- package/dist/directive/resolver-dispatch.d.ts +19 -92
- package/dist/directive/resolver-dispatch.d.ts.map +1 -1
- package/dist/directive/resolver-dispatch.js +19 -65
- package/dist/directive/resolver-dispatch.js.map +1 -1
- package/dist/focus/focus-engine.d.ts.map +1 -1
- package/dist/focus/focus-engine.js +26 -0
- package/dist/focus/focus-engine.js.map +1 -1
- package/dist/focus/focus-executor.d.ts.map +1 -1
- package/dist/focus/focus-executor.js +290 -256
- package/dist/focus/focus-executor.js.map +1 -1
- package/dist/focus/focus-loop-registry.d.ts.map +1 -1
- package/dist/focus/focus-loop-registry.js +43 -1
- package/dist/focus/focus-loop-registry.js.map +1 -1
- package/dist/focus/focus-respawn-guard.d.ts.map +1 -1
- package/dist/focus/focus-respawn-guard.js +11 -2
- package/dist/focus/focus-respawn-guard.js.map +1 -1
- package/dist/focus/focus-team-state.d.ts +11 -0
- package/dist/focus/focus-team-state.d.ts.map +1 -1
- package/dist/focus/focus-team-state.js +17 -0
- package/dist/focus/focus-team-state.js.map +1 -1
- package/dist/focus/listener-auto-advance.d.ts +13 -1
- package/dist/focus/listener-auto-advance.d.ts.map +1 -1
- package/dist/focus/listener-auto-advance.js +103 -22
- package/dist/focus/listener-auto-advance.js.map +1 -1
- package/dist/focus/listener.d.ts.map +1 -1
- package/dist/focus/listener.js +125 -75
- package/dist/focus/listener.js.map +1 -1
- package/dist/focus/tick-deadline.d.ts +55 -0
- package/dist/focus/tick-deadline.d.ts.map +1 -0
- package/dist/focus/tick-deadline.js +91 -0
- package/dist/focus/tick-deadline.js.map +1 -0
- package/dist/focus-stage-lifecycle/index.d.ts +18 -25
- package/dist/focus-stage-lifecycle/index.d.ts.map +1 -1
- package/dist/focus-stage-lifecycle/index.js +26 -38
- package/dist/focus-stage-lifecycle/index.js.map +1 -1
- package/dist/focus-stage-lifecycle/wedge-cause-arbiter.d.ts +52 -0
- package/dist/focus-stage-lifecycle/wedge-cause-arbiter.d.ts.map +1 -0
- package/dist/focus-stage-lifecycle/wedge-cause-arbiter.js +94 -0
- package/dist/focus-stage-lifecycle/wedge-cause-arbiter.js.map +1 -0
- package/dist/focus-stage-lifecycle/wedge-watchdog.d.ts +18 -1
- package/dist/focus-stage-lifecycle/wedge-watchdog.d.ts.map +1 -1
- package/dist/focus-stage-lifecycle/wedge-watchdog.js +57 -7
- package/dist/focus-stage-lifecycle/wedge-watchdog.js.map +1 -1
- package/dist/guard/condition-evaluators.d.ts +9 -0
- package/dist/guard/condition-evaluators.d.ts.map +1 -1
- package/dist/guard/condition-evaluators.js +15 -0
- package/dist/guard/condition-evaluators.js.map +1 -1
- package/dist/guard/guard-evaluator.d.ts +17 -1
- package/dist/guard/guard-evaluator.d.ts.map +1 -1
- package/dist/guard/guard-evaluator.js +61 -2
- package/dist/guard/guard-evaluator.js.map +1 -1
- package/dist/guard/trigger-executor.d.ts +30 -18
- package/dist/guard/trigger-executor.d.ts.map +1 -1
- package/dist/guard/trigger-executor.js +55 -31
- package/dist/guard/trigger-executor.js.map +1 -1
- package/dist/prompt/team-prompt-base.d.ts +1 -1
- package/dist/prompt/team-prompt-base.d.ts.map +1 -1
- package/dist/prompt/team-prompt-base.js +1 -1
- package/dist/prompt/team-prompt-base.js.map +1 -1
- package/dist/prompt-sections/persona-sections.d.ts +0 -32
- package/dist/prompt-sections/persona-sections.d.ts.map +1 -1
- package/dist/prompt-sections/persona-sections.js +19 -88
- package/dist/prompt-sections/persona-sections.js.map +1 -1
- package/dist/queries/deliveries.d.ts +23 -0
- package/dist/queries/deliveries.d.ts.map +1 -1
- package/dist/queries/deliveries.js +6 -0
- package/dist/queries/deliveries.js.map +1 -1
- package/dist/queries/focuses.d.ts +36 -0
- package/dist/queries/focuses.d.ts.map +1 -1
- package/dist/queries/focuses.js +45 -0
- package/dist/queries/focuses.js.map +1 -1
- package/dist/queries/guards.d.ts +10 -0
- package/dist/queries/guards.d.ts.map +1 -1
- package/dist/queries/guards.js +14 -1
- package/dist/queries/guards.js.map +1 -1
- package/dist/queries/issues.d.ts +65 -1
- package/dist/queries/issues.d.ts.map +1 -1
- package/dist/queries/issues.js +135 -2
- package/dist/queries/issues.js.map +1 -1
- package/dist/queries/schemas.d.ts +51 -0
- package/dist/queries/schemas.d.ts.map +1 -1
- package/dist/queries/schemas.js +34 -0
- package/dist/queries/schemas.js.map +1 -1
- package/dist/queries/verification.d.ts +5 -6
- package/dist/queries/verification.d.ts.map +1 -1
- package/dist/queries/verification.js +5 -6
- package/dist/queries/verification.js.map +1 -1
- package/dist/resolvers/loop-persona.d.ts +1 -11
- package/dist/resolvers/loop-persona.d.ts.map +1 -1
- package/dist/resolvers/loop-persona.js +2 -16
- package/dist/resolvers/loop-persona.js.map +1 -1
- package/dist/shell/unified-shell-provisioning.d.ts +40 -0
- package/dist/shell/unified-shell-provisioning.d.ts.map +1 -1
- package/dist/shell/unified-shell-provisioning.js +72 -0
- package/dist/shell/unified-shell-provisioning.js.map +1 -1
- package/dist/shell/unified-shell.d.ts.map +1 -1
- package/dist/shell/unified-shell.js +18 -1
- package/dist/shell/unified-shell.js.map +1 -1
- package/dist/spawn/helpers.d.ts.map +1 -1
- package/dist/spawn/helpers.js +6 -1
- package/dist/spawn/helpers.js.map +1 -1
- package/dist/spawn/process-spawn.d.ts.map +1 -1
- package/dist/spawn/process-spawn.js +7 -0
- package/dist/spawn/process-spawn.js.map +1 -1
- package/dist/spawn/spawn-cooldown.d.ts +41 -7
- package/dist/spawn/spawn-cooldown.d.ts.map +1 -1
- package/dist/spawn/spawn-cooldown.js +47 -0
- package/dist/spawn/spawn-cooldown.js.map +1 -1
- package/dist/spawn/spawn-decision.d.ts.map +1 -1
- package/dist/spawn/spawn-decision.js +33 -1
- package/dist/spawn/spawn-decision.js.map +1 -1
- package/dist/verify/stall-detectors.d.ts +20 -8
- package/dist/verify/stall-detectors.d.ts.map +1 -1
- package/dist/verify/stall-detectors.js +27 -46
- package/dist/verify/stall-detectors.js.map +1 -1
- package/dist/workflow/gate-failure-policy.d.ts +48 -0
- package/dist/workflow/gate-failure-policy.d.ts.map +1 -0
- package/dist/workflow/gate-failure-policy.js +57 -0
- package/dist/workflow/gate-failure-policy.js.map +1 -0
- package/dist/workflow/pipeline-config.d.ts +2 -2
- package/dist/workflow/pipeline-config.js +2 -2
- package/package.json +1 -1
- package/dist/directive/close-loop-stage.d.ts +0 -50
- package/dist/directive/close-loop-stage.d.ts.map +0 -1
- package/dist/directive/close-loop-stage.js +0 -210
- package/dist/directive/close-loop-stage.js.map +0 -1
- package/dist/focus-stage-lifecycle/review-convergence.d.ts +0 -133
- package/dist/focus-stage-lifecycle/review-convergence.d.ts.map +0 -1
- package/dist/focus-stage-lifecycle/review-convergence.js +0 -237
- package/dist/focus-stage-lifecycle/review-convergence.js.map +0 -1
|
@@ -1,64 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Focus injection-sweep primitives.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* Formerly the close-loop dispatcher's pure helpers. The machine-driven
|
|
5
|
+
* close_loop/done stage-writing family has been removed (the focus lifecycle
|
|
6
|
+
* collapsed to a single derived phase terminating at winding_down); these
|
|
7
|
+
* pure primitives are all that remains, retained for
|
|
8
|
+
* `completion/settle-injection-sweep.ts`, which runs the same anchored-injection
|
|
9
|
+
* sweep at the derived winding_down settle (writing NO focus stage).
|
|
8
10
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* - **Otherwise, spawn a fresh bookkeeper team** (Sonnet model, fresh
|
|
16
|
-
* session) with the close_loop assembly recipe:
|
|
17
|
-
* loop.persona(variant=close_loop)
|
|
18
|
-
* focus.context
|
|
19
|
-
* focus.anchoring_injections
|
|
20
|
-
* git.diff_against_base
|
|
21
|
-
* delivery.acceptance_criteria
|
|
22
|
-
* review.outcomes
|
|
23
|
-
* The team handles the per-injection sweep (verified | escalated, no
|
|
24
|
-
* third option), the incomplete-planning check, and the tree-drift
|
|
25
|
-
* check skeleton (D5 wires `focus.description` through into the
|
|
26
|
-
* drift-check prompt section).
|
|
27
|
-
*
|
|
28
|
-
* - **Monitor termination.** Once every anchored injection has reached a
|
|
29
|
-
* terminal state (`verified` or has an open `injection_unverified`
|
|
30
|
-
* escalation), the dispatcher advances the focus to `done`. While any
|
|
31
|
-
* injection is still non-terminal, the focus stays at `close_loop`.
|
|
32
|
-
*
|
|
33
|
-
* The dispatcher is intentionally split into small pure helpers so each
|
|
34
|
-
* deterministic check (terminal classification, short-circuit detection,
|
|
35
|
-
* directive composition) is unit-testable without DB or process spawning.
|
|
11
|
+
* The sweep realizes anchored injections whose anchoring deliveries closed
|
|
12
|
+
* successfully but whose injection status has not yet flipped to 'verified',
|
|
13
|
+
* so the reality tree settles to reflect what was built. Split into small pure
|
|
14
|
+
* helpers so each check (anchor filtering, sweep planning, sweep execution) is
|
|
15
|
+
* unit-testable without DB or process spawning.
|
|
36
16
|
*/
|
|
37
|
-
import type { DaemonConfig, StageDirective } from '../types/index.js';
|
|
38
17
|
import type { FocusDeliveryInfo } from '../types/index.js';
|
|
39
|
-
/**
|
|
40
|
-
* Model used by close_loop bookkeeper teams. Sonnet because the bookkeeper
|
|
41
|
-
* is a careful, deterministic role (sweep injections, file escalations);
|
|
42
|
-
* Opus would be overkill and slower.
|
|
43
|
-
*/
|
|
44
|
-
export declare const CLOSE_LOOP_MODEL = "claude-sonnet-4-6";
|
|
45
|
-
/**
|
|
46
|
-
* Assembly recipe for the close_loop stage. Composed in dispatcher when
|
|
47
|
-
* the close_loop directive is synthesized. Persisted nowhere -- the
|
|
48
|
-
* stage's stored `agent_directive` (if any) is ignored in favor of this
|
|
49
|
-
* canonical recipe so the bookkeeper framing is deterministic regardless
|
|
50
|
-
* of how the workflow stage row was seeded.
|
|
51
|
-
*/
|
|
52
|
-
export declare const CLOSE_LOOP_ASSEMBLY_RECIPE: readonly string[];
|
|
53
|
-
/**
|
|
54
|
-
* Terminal injection statuses for close_loop. An injection is considered
|
|
55
|
-
* terminal when it is either verified (the bookkeeper called
|
|
56
|
-
* reality_tree_injection_verify) or has an `injection_unverified`
|
|
57
|
-
* escalation filed against it. The first is detected by `injectionStatus`;
|
|
58
|
-
* the second is detected by walking the agent_escalations table -- see
|
|
59
|
-
* `isInjectionEscalated`.
|
|
60
|
-
*/
|
|
61
|
-
export declare const TERMINAL_INJECTION_STATUSES: readonly string[];
|
|
62
18
|
/**
|
|
63
19
|
* Return the deliveries in the focus that anchor an injection.
|
|
64
20
|
*
|
|
@@ -68,49 +24,6 @@ export declare const TERMINAL_INJECTION_STATUSES: readonly string[];
|
|
|
68
24
|
* reality_tree_nodes row.
|
|
69
25
|
*/
|
|
70
26
|
export declare function getAnchoringDeliveries(deliveries: FocusDeliveryInfo[]): FocusDeliveryInfo[];
|
|
71
|
-
/**
|
|
72
|
-
* Pure short-circuit predicate -- "does this focus have any anchoring
|
|
73
|
-
* injections?". If false, close_loop is a no-op and the focus can advance
|
|
74
|
-
* directly to `done`.
|
|
75
|
-
*/
|
|
76
|
-
export declare function shouldShortCircuitToDone(deliveries: FocusDeliveryInfo[]): boolean;
|
|
77
|
-
/**
|
|
78
|
-
* Per-injection summary used by termination checks. The dispatcher composes
|
|
79
|
-
* one of these per anchored injection from a snapshot of:
|
|
80
|
-
*
|
|
81
|
-
* - the injection node's `injectionStatus` (from reality_tree_nodes)
|
|
82
|
-
* - whether there is an open `injection_unverified` escalation with
|
|
83
|
-
* metadata.injection_id == this injection id (from agent_escalations)
|
|
84
|
-
*/
|
|
85
|
-
export interface InjectionTerminalState {
|
|
86
|
-
injectionId: string;
|
|
87
|
-
/** From reality_tree_nodes.injection_status (or null for non-injection nodes). */
|
|
88
|
-
injectionStatus: string | null;
|
|
89
|
-
/** Whether an open injection_unverified escalation targets this injection. */
|
|
90
|
-
hasOpenUnverifiedEscalation: boolean;
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* Pure classifier. An injection is terminal for close_loop when:
|
|
94
|
-
*
|
|
95
|
-
* - injectionStatus is `verified`, OR
|
|
96
|
-
* - there is an open `injection_unverified` escalation for it
|
|
97
|
-
* (this is the "escalated" outcome -- the bookkeeper filed the
|
|
98
|
-
* escalation and the focus is now blocked on a human).
|
|
99
|
-
*
|
|
100
|
-
* The "open escalation" case keeps the focus at close_loop (status='pending'
|
|
101
|
-
* or 'in_review') -- this function classifies the escalation as terminal
|
|
102
|
-
* for the dispatcher's perspective so the daemon does not loop forever
|
|
103
|
-
* re-spawning the bookkeeper. A human must resolve or dismiss the
|
|
104
|
-
* escalation; once resolved, the bookkeeper can be re-triggered.
|
|
105
|
-
*/
|
|
106
|
-
export declare function isInjectionTerminal(state: InjectionTerminalState): boolean;
|
|
107
|
-
/**
|
|
108
|
-
* Pure aggregate predicate -- "are ALL anchored injections terminal?".
|
|
109
|
-
* Returns true on an empty list (the short-circuit branch handles the
|
|
110
|
-
* empty case separately, but this is safe to call after the short-circuit
|
|
111
|
-
* filter).
|
|
112
|
-
*/
|
|
113
|
-
export declare function allInjectionsTerminal(states: InjectionTerminalState[]): boolean;
|
|
114
27
|
/**
|
|
115
28
|
* One entry in the planned sweep: an injection that should be verified and
|
|
116
29
|
* the evidence text to persist with the verify call.
|
|
@@ -144,8 +57,8 @@ export declare function planInjectionSweep(anchored: FocusDeliveryInfo[], status
|
|
|
144
57
|
* I/O so tests can pass a stub.
|
|
145
58
|
*
|
|
146
59
|
* `ok === true` means every entry verified; `ok === false` means the loop
|
|
147
|
-
* stopped at `failedAt.injectionId`. The
|
|
148
|
-
*
|
|
60
|
+
* stopped at `failedAt.injectionId`. The caller MUST NOT treat the sweep as
|
|
61
|
+
* complete when `ok` is false -- a partial sweep means the tree is
|
|
149
62
|
* still in an inconsistent state and the next poll cycle should retry.
|
|
150
63
|
*/
|
|
151
64
|
export interface SweepRunOutcome {
|
|
@@ -158,168 +71,8 @@ export interface SweepRunOutcome {
|
|
|
158
71
|
}
|
|
159
72
|
/**
|
|
160
73
|
* Execute a planned sweep in order. Short-circuits on the first failure --
|
|
161
|
-
* the
|
|
162
|
-
*
|
|
163
|
-
* close_loop poll cycle.
|
|
74
|
+
* the caller holds the focus and retries the failed verify on the next
|
|
75
|
+
* settle cycle.
|
|
164
76
|
*/
|
|
165
77
|
export declare function executeSweep(entries: readonly InjectionSweepEntry[], verify: (injectionId: string, evidence: string) => Promise<void>): Promise<SweepRunOutcome>;
|
|
166
|
-
/**
|
|
167
|
-
* Dependencies for advancing a focus from close_loop -> done with a
|
|
168
|
-
* preceding injection sweep. Both are owned by callers so tests can stub
|
|
169
|
-
* them without mocking the daemon's I/O layer.
|
|
170
|
-
*/
|
|
171
|
-
export interface AdvanceToDoneDeps {
|
|
172
|
-
verify: (injectionId: string, evidence: string) => Promise<void>;
|
|
173
|
-
updateFocusStage: (focusId: string, stage: 'done') => Promise<void>;
|
|
174
|
-
}
|
|
175
|
-
/**
|
|
176
|
-
* Outcome of `advanceFocusToDoneWithSweep`. The dispatcher reads this to
|
|
177
|
-
* decide whether to clear the bookkeeper-spawned flag (which is cleared
|
|
178
|
-
* only when the focus actually leaves close_loop, success or terminal
|
|
179
|
-
* failure).
|
|
180
|
-
*
|
|
181
|
-
* - `advanced=true` -- updateFocusStage was called and returned
|
|
182
|
-
* - `sweepFailure` set -- the sweep failed; updateFocusStage was NOT
|
|
183
|
-
* called; the focus is held at close_loop for the next poll cycle to
|
|
184
|
-
* retry. This is the explicit "focus does not advance" semantics from
|
|
185
|
-
* AC-6 of the focus-closure-correctness focus.
|
|
186
|
-
* - `advanceError` set -- the sweep succeeded (or was empty) but
|
|
187
|
-
* updateFocusStage threw. The focus did not advance, but the failure
|
|
188
|
-
* is on the stage-update side, not the sweep side.
|
|
189
|
-
*/
|
|
190
|
-
export interface AdvanceToDoneOutcome {
|
|
191
|
-
advanced: boolean;
|
|
192
|
-
verifiedCount: number;
|
|
193
|
-
planSize: number;
|
|
194
|
-
sweepFailure?: {
|
|
195
|
-
injectionId: string;
|
|
196
|
-
error: Error;
|
|
197
|
-
};
|
|
198
|
-
advanceError?: Error;
|
|
199
|
-
}
|
|
200
|
-
/**
|
|
201
|
-
* Run the close_loop sweep, then advance the focus to `done`. Pure with
|
|
202
|
-
* respect to side effects -- callers inject `verify` and
|
|
203
|
-
* `updateFocusStage` via `deps`.
|
|
204
|
-
*
|
|
205
|
-
* Order of operations is part of the contract:
|
|
206
|
-
* 1. Build the sweep plan from `anchored` + `statusByInjectionId`.
|
|
207
|
-
* 2. If non-empty, run the sweep. On failure: log + return early WITHOUT
|
|
208
|
-
* calling `deps.updateFocusStage`. This is the "focus does not
|
|
209
|
-
* advance" invariant — the failed verify is retried on the next poll
|
|
210
|
-
* cycle when the focus is re-evaluated.
|
|
211
|
-
* 3. On sweep success (or empty plan), call `deps.updateFocusStage`.
|
|
212
|
-
*
|
|
213
|
-
* The dispatcher (`dispatchCloseLoopStage`) delegates the entire
|
|
214
|
-
* advance_to_done branch to this function so the sweep-failure path is
|
|
215
|
-
* unit-testable without DB or process mocking.
|
|
216
|
-
*/
|
|
217
|
-
export declare function advanceFocusToDoneWithSweep(focus: {
|
|
218
|
-
focus_id: string;
|
|
219
|
-
focus_name: string;
|
|
220
|
-
}, anchored: FocusDeliveryInfo[], statusByInjectionId: ReadonlyMap<string, string | null>, deps: AdvanceToDoneDeps): Promise<AdvanceToDoneOutcome>;
|
|
221
|
-
/**
|
|
222
|
-
* Compose the synthetic close_loop StageDirective. Used by the dispatcher
|
|
223
|
-
* to seed the pending spawn directive map; pure for testability.
|
|
224
|
-
*/
|
|
225
|
-
export declare function buildCloseLoopDirective(): StageDirective;
|
|
226
|
-
/**
|
|
227
|
-
* Query: are there any pending or in_review injection_unverified escalations
|
|
228
|
-
* for the given injection ids? Returns the set of injection ids that have
|
|
229
|
-
* an open escalation.
|
|
230
|
-
*
|
|
231
|
-
* Looks at agent_escalations with:
|
|
232
|
-
* - escalation_kind = 'injection_unverified'
|
|
233
|
-
* - status IN ('pending', 'in_review')
|
|
234
|
-
* - metadata->>'injection_id' IN (...)
|
|
235
|
-
*
|
|
236
|
-
* The dispatcher uses this to classify "escalated" injections as terminal
|
|
237
|
-
* for advancement purposes.
|
|
238
|
-
*/
|
|
239
|
-
export interface OpenInjectionEscalationLookup {
|
|
240
|
-
(params: {
|
|
241
|
-
organizationId: string;
|
|
242
|
-
injectionIds: string[];
|
|
243
|
-
}): Promise<Set<string>>;
|
|
244
|
-
}
|
|
245
|
-
/**
|
|
246
|
-
* Top-level result of inspecting a focus for close_loop termination.
|
|
247
|
-
* Tells the caller exactly what to do: short-circuit, spawn, advance to
|
|
248
|
-
* done, or hold.
|
|
249
|
-
*/
|
|
250
|
-
export type CloseLoopDecision = {
|
|
251
|
-
kind: 'short_circuit_to_done';
|
|
252
|
-
} | {
|
|
253
|
-
kind: 'spawn_bookkeeper';
|
|
254
|
-
directive: StageDirective;
|
|
255
|
-
} | {
|
|
256
|
-
kind: 'advance_to_done';
|
|
257
|
-
} | {
|
|
258
|
-
kind: 'hold';
|
|
259
|
-
reason: string;
|
|
260
|
-
};
|
|
261
|
-
/**
|
|
262
|
-
* Compute the close_loop decision for a focus given a snapshot of its
|
|
263
|
-
* deliveries and the terminal state of each anchored injection.
|
|
264
|
-
*
|
|
265
|
-
* Pure -- no DB access. Tests pass snapshots in.
|
|
266
|
-
*
|
|
267
|
-
* Semantics:
|
|
268
|
-
*
|
|
269
|
-
* - No anchored injections -> short_circuit_to_done
|
|
270
|
-
* - Anchored injections, none yet sweeped -> spawn_bookkeeper
|
|
271
|
-
* - All anchored injections terminal -> advance_to_done
|
|
272
|
-
* - Some terminal, some not -> hold (waiting on bookkeeper)
|
|
273
|
-
*
|
|
274
|
-
* The "spawn_bookkeeper" branch is gated on "no sweep has happened yet"
|
|
275
|
-
* which the caller communicates via `bookkeeperHasRun`. Without this gate
|
|
276
|
-
* every poll cycle would respawn the bookkeeper while it's still working
|
|
277
|
-
* (or before a stuck injection's escalation is filed).
|
|
278
|
-
*/
|
|
279
|
-
export declare function decideCloseLoop(deliveries: FocusDeliveryInfo[], injectionStates: InjectionTerminalState[], bookkeeperHasRun: boolean): CloseLoopDecision;
|
|
280
|
-
/**
|
|
281
|
-
* Sentinel returned by the dispatcher when no action was taken (focus is
|
|
282
|
-
* not in close_loop stage, or already processed this cycle). Caller can
|
|
283
|
-
* log it but should not act on it.
|
|
284
|
-
*/
|
|
285
|
-
export interface CloseLoopDispatchOutcome {
|
|
286
|
-
decision: CloseLoopDecision;
|
|
287
|
-
/** Anchored injection ids that were inspected (empty on short-circuit). */
|
|
288
|
-
anchoredInjectionIds: string[];
|
|
289
|
-
}
|
|
290
|
-
/**
|
|
291
|
-
* Re-exported constant set so tests can assert the kind names used by the
|
|
292
|
-
* dispatcher in the agent_escalations rows. Keeps the kind string under
|
|
293
|
-
* a single source of truth in daemon-core.
|
|
294
|
-
*/
|
|
295
|
-
export declare const CLOSE_LOOP_ESCALATION_KINDS: {
|
|
296
|
-
readonly INJECTION_UNVERIFIED: "injection_unverified";
|
|
297
|
-
readonly TREE_DRIFT: "tree_drift";
|
|
298
|
-
readonly INCOMPLETE_PLANNING: "incomplete_planning";
|
|
299
|
-
};
|
|
300
|
-
/**
|
|
301
|
-
* Configuration knobs accepted by the dispatcher entry point. Exposed so
|
|
302
|
-
* tests can inject a no-op spawner.
|
|
303
|
-
*/
|
|
304
|
-
export interface CloseLoopDispatcherDeps {
|
|
305
|
-
/**
|
|
306
|
-
* Set the pending spawn directive for the focus so the next spawn cycle
|
|
307
|
-
* delivers the close_loop directive. In production this is the export
|
|
308
|
-
* from directive-executor; in tests this is a stub.
|
|
309
|
-
*/
|
|
310
|
-
setPendingSpawnDirective: (focusId: string, directive: {
|
|
311
|
-
message: string;
|
|
312
|
-
model: string | null;
|
|
313
|
-
sessionType: 'coding' | 'review';
|
|
314
|
-
contentHash: string;
|
|
315
|
-
}) => void;
|
|
316
|
-
/** Advance the focus stage on product_focuses.stage. */
|
|
317
|
-
updateFocusStage: (focusId: string, stage: string) => Promise<void>;
|
|
318
|
-
/** Lookup open injection_unverified escalations. */
|
|
319
|
-
lookupOpenInjectionEscalations: OpenInjectionEscalationLookup;
|
|
320
|
-
/** Build the directive content (assembly resolution + prompt). */
|
|
321
|
-
assembleDirectiveContent: (config: DaemonConfig, focusId: string, directive: StageDirective, worktreePath: string | null) => Promise<string>;
|
|
322
|
-
/** Compute the content hash for the synthesized directive. */
|
|
323
|
-
computeDirectiveHash: (directive: StageDirective) => string;
|
|
324
|
-
}
|
|
325
78
|
//# sourceMappingURL=close-loop-dispatcher.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"close-loop-dispatcher.d.ts","sourceRoot":"","sources":["../../src/directive/close-loop-dispatcher.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"close-loop-dispatcher.d.ts","sourceRoot":"","sources":["../../src/directive/close-loop-dispatcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAM3D;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,iBAAiB,EAAE,GAAG,iBAAiB,EAAE,CAI3F;AA2BD;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,iBAAiB,EAAE,EAC7B,mBAAmB,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,GACtD,mBAAmB,EAAE,CA6BvB;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,OAAO,CAAC;IACZ,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,CAAC,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,KAAK,CAAA;KAAE,CAAC;CAClD;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE,SAAS,mBAAmB,EAAE,EACvC,MAAM,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAC/D,OAAO,CAAC,eAAe,CAAC,CAe1B"}
|
|
@@ -1,75 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* session) with the close_loop assembly recipe:
|
|
17
|
-
* loop.persona(variant=close_loop)
|
|
18
|
-
* focus.context
|
|
19
|
-
* focus.anchoring_injections
|
|
20
|
-
* git.diff_against_base
|
|
21
|
-
* delivery.acceptance_criteria
|
|
22
|
-
* review.outcomes
|
|
23
|
-
* The team handles the per-injection sweep (verified | escalated, no
|
|
24
|
-
* third option), the incomplete-planning check, and the tree-drift
|
|
25
|
-
* check skeleton (D5 wires `focus.description` through into the
|
|
26
|
-
* drift-check prompt section).
|
|
27
|
-
*
|
|
28
|
-
* - **Monitor termination.** Once every anchored injection has reached a
|
|
29
|
-
* terminal state (`verified` or has an open `injection_unverified`
|
|
30
|
-
* escalation), the dispatcher advances the focus to `done`. While any
|
|
31
|
-
* injection is still non-terminal, the focus stays at `close_loop`.
|
|
32
|
-
*
|
|
33
|
-
* The dispatcher is intentionally split into small pure helpers so each
|
|
34
|
-
* deterministic check (terminal classification, short-circuit detection,
|
|
35
|
-
* directive composition) is unit-testable without DB or process spawning.
|
|
36
|
-
*/
|
|
37
|
-
import { ESCALATION_KINDS } from '@telora/daemon-core';
|
|
38
|
-
import { createLogger } from '../shell/log.js';
|
|
39
|
-
const log = createLogger({ module: 'close-loop-dispatcher' });
|
|
40
|
-
// ---------------------------------------------------------------------------
|
|
41
|
-
// Constants
|
|
42
|
-
// ---------------------------------------------------------------------------
|
|
43
|
-
/**
|
|
44
|
-
* Model used by close_loop bookkeeper teams. Sonnet because the bookkeeper
|
|
45
|
-
* is a careful, deterministic role (sweep injections, file escalations);
|
|
46
|
-
* Opus would be overkill and slower.
|
|
47
|
-
*/
|
|
48
|
-
export const CLOSE_LOOP_MODEL = 'claude-sonnet-4-6';
|
|
49
|
-
/**
|
|
50
|
-
* Assembly recipe for the close_loop stage. Composed in dispatcher when
|
|
51
|
-
* the close_loop directive is synthesized. Persisted nowhere -- the
|
|
52
|
-
* stage's stored `agent_directive` (if any) is ignored in favor of this
|
|
53
|
-
* canonical recipe so the bookkeeper framing is deterministic regardless
|
|
54
|
-
* of how the workflow stage row was seeded.
|
|
2
|
+
* Focus injection-sweep primitives.
|
|
3
|
+
*
|
|
4
|
+
* Formerly the close-loop dispatcher's pure helpers. The machine-driven
|
|
5
|
+
* close_loop/done stage-writing family has been removed (the focus lifecycle
|
|
6
|
+
* collapsed to a single derived phase terminating at winding_down); these
|
|
7
|
+
* pure primitives are all that remains, retained for
|
|
8
|
+
* `completion/settle-injection-sweep.ts`, which runs the same anchored-injection
|
|
9
|
+
* sweep at the derived winding_down settle (writing NO focus stage).
|
|
10
|
+
*
|
|
11
|
+
* The sweep realizes anchored injections whose anchoring deliveries closed
|
|
12
|
+
* successfully but whose injection status has not yet flipped to 'verified',
|
|
13
|
+
* so the reality tree settles to reflect what was built. Split into small pure
|
|
14
|
+
* helpers so each check (anchor filtering, sweep planning, sweep execution) is
|
|
15
|
+
* unit-testable without DB or process spawning.
|
|
55
16
|
*/
|
|
56
|
-
export const CLOSE_LOOP_ASSEMBLY_RECIPE = [
|
|
57
|
-
'loop.persona(variant=close_loop)',
|
|
58
|
-
'focus.context',
|
|
59
|
-
'focus.anchoring_injections',
|
|
60
|
-
'git.diff_against_base',
|
|
61
|
-
'delivery.acceptance_criteria',
|
|
62
|
-
'review.outcomes',
|
|
63
|
-
];
|
|
64
|
-
/**
|
|
65
|
-
* Terminal injection statuses for close_loop. An injection is considered
|
|
66
|
-
* terminal when it is either verified (the bookkeeper called
|
|
67
|
-
* reality_tree_injection_verify) or has an `injection_unverified`
|
|
68
|
-
* escalation filed against it. The first is detected by `injectionStatus`;
|
|
69
|
-
* the second is detected by walking the agent_escalations table -- see
|
|
70
|
-
* `isInjectionEscalated`.
|
|
71
|
-
*/
|
|
72
|
-
export const TERMINAL_INJECTION_STATUSES = ['verified'];
|
|
73
17
|
// ---------------------------------------------------------------------------
|
|
74
18
|
// Helpers (pure)
|
|
75
19
|
// ---------------------------------------------------------------------------
|
|
@@ -84,56 +28,13 @@ export const TERMINAL_INJECTION_STATUSES = ['verified'];
|
|
|
84
28
|
export function getAnchoringDeliveries(deliveries) {
|
|
85
29
|
return deliveries.filter((d) => typeof d.injectionId === 'string' && d.injectionId.length > 0);
|
|
86
30
|
}
|
|
87
|
-
/**
|
|
88
|
-
* Pure short-circuit predicate -- "does this focus have any anchoring
|
|
89
|
-
* injections?". If false, close_loop is a no-op and the focus can advance
|
|
90
|
-
* directly to `done`.
|
|
91
|
-
*/
|
|
92
|
-
export function shouldShortCircuitToDone(deliveries) {
|
|
93
|
-
return getAnchoringDeliveries(deliveries).length === 0;
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* Pure classifier. An injection is terminal for close_loop when:
|
|
97
|
-
*
|
|
98
|
-
* - injectionStatus is `verified`, OR
|
|
99
|
-
* - there is an open `injection_unverified` escalation for it
|
|
100
|
-
* (this is the "escalated" outcome -- the bookkeeper filed the
|
|
101
|
-
* escalation and the focus is now blocked on a human).
|
|
102
|
-
*
|
|
103
|
-
* The "open escalation" case keeps the focus at close_loop (status='pending'
|
|
104
|
-
* or 'in_review') -- this function classifies the escalation as terminal
|
|
105
|
-
* for the dispatcher's perspective so the daemon does not loop forever
|
|
106
|
-
* re-spawning the bookkeeper. A human must resolve or dismiss the
|
|
107
|
-
* escalation; once resolved, the bookkeeper can be re-triggered.
|
|
108
|
-
*/
|
|
109
|
-
export function isInjectionTerminal(state) {
|
|
110
|
-
if (state.injectionStatus && TERMINAL_INJECTION_STATUSES.includes(state.injectionStatus)) {
|
|
111
|
-
return true;
|
|
112
|
-
}
|
|
113
|
-
if (state.hasOpenUnverifiedEscalation) {
|
|
114
|
-
return true;
|
|
115
|
-
}
|
|
116
|
-
return false;
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
* Pure aggregate predicate -- "are ALL anchored injections terminal?".
|
|
120
|
-
* Returns true on an empty list (the short-circuit branch handles the
|
|
121
|
-
* empty case separately, but this is safe to call after the short-circuit
|
|
122
|
-
* filter).
|
|
123
|
-
*/
|
|
124
|
-
export function allInjectionsTerminal(states) {
|
|
125
|
-
return states.every(isInjectionTerminal);
|
|
126
|
-
}
|
|
127
31
|
// ---------------------------------------------------------------------------
|
|
128
|
-
// Focus-level injection sweep
|
|
32
|
+
// Focus-level injection sweep
|
|
129
33
|
//
|
|
130
|
-
//
|
|
131
|
-
//
|
|
132
|
-
//
|
|
133
|
-
//
|
|
134
|
-
// path other than the per-delivery verify gate -- e.g. a delivery that
|
|
135
|
-
// finished its review and routed straight to 'done') should be realized in
|
|
136
|
-
// the reality tree before the focus advances.
|
|
34
|
+
// Anchored injections whose status is not yet 'verified' but whose anchoring
|
|
35
|
+
// deliveries closed successfully (via a path other than the per-delivery verify
|
|
36
|
+
// gate -- e.g. a delivery that finished its review and routed straight to
|
|
37
|
+
// 'done') should be realized in the reality tree at focus settle.
|
|
137
38
|
//
|
|
138
39
|
// Without this sweep, the FRT overlays for those injections stay frozen in
|
|
139
40
|
// their pre-close state and the focus completes with a stale reality tree.
|
|
@@ -200,9 +101,8 @@ export function planInjectionSweep(anchored, statusByInjectionId) {
|
|
|
200
101
|
}
|
|
201
102
|
/**
|
|
202
103
|
* Execute a planned sweep in order. Short-circuits on the first failure --
|
|
203
|
-
* the
|
|
204
|
-
*
|
|
205
|
-
* close_loop poll cycle.
|
|
104
|
+
* the caller holds the focus and retries the failed verify on the next
|
|
105
|
+
* settle cycle.
|
|
206
106
|
*/
|
|
207
107
|
export async function executeSweep(entries, verify) {
|
|
208
108
|
const verified = [];
|
|
@@ -221,142 +121,4 @@ export async function executeSweep(entries, verify) {
|
|
|
221
121
|
}
|
|
222
122
|
return { ok: true, verified };
|
|
223
123
|
}
|
|
224
|
-
/**
|
|
225
|
-
* Run the close_loop sweep, then advance the focus to `done`. Pure with
|
|
226
|
-
* respect to side effects -- callers inject `verify` and
|
|
227
|
-
* `updateFocusStage` via `deps`.
|
|
228
|
-
*
|
|
229
|
-
* Order of operations is part of the contract:
|
|
230
|
-
* 1. Build the sweep plan from `anchored` + `statusByInjectionId`.
|
|
231
|
-
* 2. If non-empty, run the sweep. On failure: log + return early WITHOUT
|
|
232
|
-
* calling `deps.updateFocusStage`. This is the "focus does not
|
|
233
|
-
* advance" invariant — the failed verify is retried on the next poll
|
|
234
|
-
* cycle when the focus is re-evaluated.
|
|
235
|
-
* 3. On sweep success (or empty plan), call `deps.updateFocusStage`.
|
|
236
|
-
*
|
|
237
|
-
* The dispatcher (`dispatchCloseLoopStage`) delegates the entire
|
|
238
|
-
* advance_to_done branch to this function so the sweep-failure path is
|
|
239
|
-
* unit-testable without DB or process mocking.
|
|
240
|
-
*/
|
|
241
|
-
export async function advanceFocusToDoneWithSweep(focus, anchored, statusByInjectionId, deps) {
|
|
242
|
-
const plan = planInjectionSweep(anchored, statusByInjectionId);
|
|
243
|
-
if (plan.length > 0) {
|
|
244
|
-
const sweepResult = await executeSweep(plan, deps.verify);
|
|
245
|
-
if (!sweepResult.ok) {
|
|
246
|
-
const { injectionId, error } = sweepResult.failedAt;
|
|
247
|
-
log.warn(`[close-loop-dispatcher] Injection sweep FAILED for focus ${focus.focus_id.slice(0, 8)} ` +
|
|
248
|
-
`at injection ${injectionId.slice(0, 8)}: ${error.message}. ` +
|
|
249
|
-
`${sweepResult.verified.length}/${plan.length} injection(s) verified before failure. ` +
|
|
250
|
-
`Holding focus at close_loop -- next poll cycle will retry.`);
|
|
251
|
-
return {
|
|
252
|
-
advanced: false,
|
|
253
|
-
verifiedCount: sweepResult.verified.length,
|
|
254
|
-
planSize: plan.length,
|
|
255
|
-
sweepFailure: { injectionId, error },
|
|
256
|
-
};
|
|
257
|
-
}
|
|
258
|
-
if (sweepResult.verified.length > 0) {
|
|
259
|
-
log.info(`[close-loop-dispatcher] Focus "${focus.focus_name}" -- close-loop sweep verified ` +
|
|
260
|
-
`${sweepResult.verified.length} anchored injection(s) before advancing to done`);
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
try {
|
|
264
|
-
await deps.updateFocusStage(focus.focus_id, 'done');
|
|
265
|
-
log.info(`[close-loop-dispatcher] Focus "${focus.focus_name}" -- all anchored injections terminal; ` +
|
|
266
|
-
`advancing close_loop -> done`);
|
|
267
|
-
return { advanced: true, verifiedCount: plan.length, planSize: plan.length };
|
|
268
|
-
}
|
|
269
|
-
catch (err) {
|
|
270
|
-
log.warn(`[close-loop-dispatcher] Failed to advance focus ${focus.focus_id.slice(0, 8)} to done: ` +
|
|
271
|
-
`${err.message}`);
|
|
272
|
-
return {
|
|
273
|
-
advanced: false,
|
|
274
|
-
verifiedCount: plan.length,
|
|
275
|
-
planSize: plan.length,
|
|
276
|
-
advanceError: err,
|
|
277
|
-
};
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
/**
|
|
281
|
-
* Compose the synthetic close_loop StageDirective. Used by the dispatcher
|
|
282
|
-
* to seed the pending spawn directive map; pure for testability.
|
|
283
|
-
*/
|
|
284
|
-
export function buildCloseLoopDirective() {
|
|
285
|
-
return {
|
|
286
|
-
execution: 'spawn',
|
|
287
|
-
compact: null,
|
|
288
|
-
assembly: [...CLOSE_LOOP_ASSEMBLY_RECIPE],
|
|
289
|
-
tools: null,
|
|
290
|
-
prompt: [
|
|
291
|
-
'You are the close-loop bookkeeper for this focus. Run the three',
|
|
292
|
-
'asymmetric checks in order:',
|
|
293
|
-
'',
|
|
294
|
-
'1. **Per-injection sweep.** For each anchored injection, decide:',
|
|
295
|
-
' verified or escalated. No third option. Use',
|
|
296
|
-
' telora_reality_tree_injection_verify(injectionId, evidence) for',
|
|
297
|
-
' verified; file an `injection_unverified` escalation otherwise.',
|
|
298
|
-
'',
|
|
299
|
-
'2. **incomplete_planning check.** Walk the focus deliveries and the',
|
|
300
|
-
' git diff. Any delivery that shipped without an anchoring injection,',
|
|
301
|
-
' or any "injection-shaped" residual work the focus did not declare,',
|
|
302
|
-
' gets one `incomplete_planning` escalation per gap.',
|
|
303
|
-
'',
|
|
304
|
-
'3. **tree_drift check.** Compare residual UDEs in the focus tree to',
|
|
305
|
-
' the focus.description (the implicit apex). Drift gets a `tree_drift`',
|
|
306
|
-
' escalation; in-scope but unresolved UDEs do too. See the',
|
|
307
|
-
' "Tree-Focus Apex Drift Check" section of the role framework for',
|
|
308
|
-
' the procedure.',
|
|
309
|
-
'',
|
|
310
|
-
'You do not advance the focus stage. The daemon advances close_loop',
|
|
311
|
-
'to done automatically once every anchored injection is terminal',
|
|
312
|
-
'(verified or has an open injection_unverified escalation).',
|
|
313
|
-
].join('\n'),
|
|
314
|
-
model: CLOSE_LOOP_MODEL,
|
|
315
|
-
};
|
|
316
|
-
}
|
|
317
|
-
/**
|
|
318
|
-
* Compute the close_loop decision for a focus given a snapshot of its
|
|
319
|
-
* deliveries and the terminal state of each anchored injection.
|
|
320
|
-
*
|
|
321
|
-
* Pure -- no DB access. Tests pass snapshots in.
|
|
322
|
-
*
|
|
323
|
-
* Semantics:
|
|
324
|
-
*
|
|
325
|
-
* - No anchored injections -> short_circuit_to_done
|
|
326
|
-
* - Anchored injections, none yet sweeped -> spawn_bookkeeper
|
|
327
|
-
* - All anchored injections terminal -> advance_to_done
|
|
328
|
-
* - Some terminal, some not -> hold (waiting on bookkeeper)
|
|
329
|
-
*
|
|
330
|
-
* The "spawn_bookkeeper" branch is gated on "no sweep has happened yet"
|
|
331
|
-
* which the caller communicates via `bookkeeperHasRun`. Without this gate
|
|
332
|
-
* every poll cycle would respawn the bookkeeper while it's still working
|
|
333
|
-
* (or before a stuck injection's escalation is filed).
|
|
334
|
-
*/
|
|
335
|
-
export function decideCloseLoop(deliveries, injectionStates, bookkeeperHasRun) {
|
|
336
|
-
if (shouldShortCircuitToDone(deliveries)) {
|
|
337
|
-
return { kind: 'short_circuit_to_done' };
|
|
338
|
-
}
|
|
339
|
-
if (allInjectionsTerminal(injectionStates)) {
|
|
340
|
-
return { kind: 'advance_to_done' };
|
|
341
|
-
}
|
|
342
|
-
if (!bookkeeperHasRun) {
|
|
343
|
-
return { kind: 'spawn_bookkeeper', directive: buildCloseLoopDirective() };
|
|
344
|
-
}
|
|
345
|
-
const total = injectionStates.length;
|
|
346
|
-
const terminal = injectionStates.filter(isInjectionTerminal).length;
|
|
347
|
-
return {
|
|
348
|
-
kind: 'hold',
|
|
349
|
-
reason: `${terminal}/${total} anchored injections terminal (verified or escalated); waiting on bookkeeper`,
|
|
350
|
-
};
|
|
351
|
-
}
|
|
352
|
-
/**
|
|
353
|
-
* Re-exported constant set so tests can assert the kind names used by the
|
|
354
|
-
* dispatcher in the agent_escalations rows. Keeps the kind string under
|
|
355
|
-
* a single source of truth in daemon-core.
|
|
356
|
-
*/
|
|
357
|
-
export const CLOSE_LOOP_ESCALATION_KINDS = {
|
|
358
|
-
INJECTION_UNVERIFIED: ESCALATION_KINDS.INJECTION_UNVERIFIED,
|
|
359
|
-
TREE_DRIFT: ESCALATION_KINDS.TREE_DRIFT,
|
|
360
|
-
INCOMPLETE_PLANNING: ESCALATION_KINDS.INCOMPLETE_PLANNING,
|
|
361
|
-
};
|
|
362
124
|
//# sourceMappingURL=close-loop-dispatcher.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"close-loop-dispatcher.js","sourceRoot":"","sources":["../../src/directive/close-loop-dispatcher.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"close-loop-dispatcher.js","sourceRoot":"","sources":["../../src/directive/close-loop-dispatcher.ts"],"names":[],"mappings":"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"}
|