@telora/daemon 0.16.42 → 0.16.44

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/build-info.json +2 -2
  2. package/dist/directive/directive-queue.d.ts +117 -0
  3. package/dist/directive/directive-queue.d.ts.map +1 -0
  4. package/dist/directive/directive-queue.js +114 -0
  5. package/dist/directive/directive-queue.js.map +1 -0
  6. package/dist/directive/stage-tracker.d.ts +23 -0
  7. package/dist/directive/stage-tracker.d.ts.map +1 -0
  8. package/dist/directive/stage-tracker.js +27 -0
  9. package/dist/directive/stage-tracker.js.map +1 -0
  10. package/dist/directive-executor.d.ts +3 -102
  11. package/dist/directive-executor.d.ts.map +1 -1
  12. package/dist/directive-executor.js +14 -112
  13. package/dist/directive-executor.js.map +1 -1
  14. package/dist/focus-executor.d.ts.map +1 -1
  15. package/dist/focus-executor.js +1 -0
  16. package/dist/focus-executor.js.map +1 -1
  17. package/dist/prompt-sections/context-sections.d.ts +32 -0
  18. package/dist/prompt-sections/context-sections.d.ts.map +1 -0
  19. package/dist/prompt-sections/context-sections.js +86 -0
  20. package/dist/prompt-sections/context-sections.js.map +1 -0
  21. package/dist/prompt-sections/execution-sections.d.ts +82 -0
  22. package/dist/prompt-sections/execution-sections.d.ts.map +1 -0
  23. package/dist/prompt-sections/execution-sections.js +601 -0
  24. package/dist/prompt-sections/execution-sections.js.map +1 -0
  25. package/dist/prompt-sections/persona-sections.d.ts +133 -0
  26. package/dist/prompt-sections/persona-sections.d.ts.map +1 -0
  27. package/dist/prompt-sections/persona-sections.js +317 -0
  28. package/dist/prompt-sections/persona-sections.js.map +1 -0
  29. package/dist/prompt-sections/role-sections.d.ts +24 -0
  30. package/dist/prompt-sections/role-sections.d.ts.map +1 -0
  31. package/dist/prompt-sections/role-sections.js +78 -0
  32. package/dist/prompt-sections/role-sections.js.map +1 -0
  33. package/dist/prompt-sections/shared.d.ts +66 -0
  34. package/dist/prompt-sections/shared.d.ts.map +1 -0
  35. package/dist/prompt-sections/shared.js +33 -0
  36. package/dist/prompt-sections/shared.js.map +1 -0
  37. package/dist/team-prompt-base.d.ts +16 -287
  38. package/dist/team-prompt-base.d.ts.map +1 -1
  39. package/dist/team-prompt-base.js +20 -1089
  40. package/dist/team-prompt-base.js.map +1 -1
  41. package/dist/templates/claude-md.d.ts.map +1 -1
  42. package/dist/templates/claude-md.js +0 -1
  43. package/dist/templates/claude-md.js.map +1 -1
  44. package/package.json +1 -1
package/build-info.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
- "commitSha": "ad40d005",
3
- "builtAt": "2026-05-28T21:55:15.882Z",
2
+ "commitSha": "9f4d5bf8",
3
+ "builtAt": "2026-05-29T18:59:42.051Z",
4
4
  "expectedMigrations": [
5
5
  "20250829113330_create_org_nodes_table.sql",
6
6
  "20250829113402_fix_function_security_search_path.sql",
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Directive queue: the pending spawn-directive store + its identity/guard logic.
3
+ *
4
+ * Holds the single shared `pendingSpawnDirectives` map (focusId ->
5
+ * PendingSpawnDirective) that executeSpawnDirective sets and spawnFocusTeam
6
+ * consumes, plus the pure helpers that compute a directive's identity hash and
7
+ * decide spawn-skip / re-fire suppression. Extracted from directive-executor.ts
8
+ * so the queue state and its accessors live in one focused module; the executor
9
+ * imports the map binding + helpers it calls in-line and re-exports the full
10
+ * surface (no duplicate instances).
11
+ */
12
+ import type { StageDirective } from '../types.js';
13
+ import type { SessionContinuity } from '../session-lineage.js';
14
+ /**
15
+ * Pending spawn directive content, keyed by focusId.
16
+ * Set by executeSpawnDirective, consumed by spawnFocusTeam.
17
+ */
18
+ export interface PendingSpawnDirective {
19
+ message: string;
20
+ model: string | null;
21
+ sessionType: 'coding' | 'review';
22
+ /** Session-id map slot this spawn reads/writes (INJ-B). */
23
+ lineage: string;
24
+ /** Resume-vs-fresh policy for this spawn's lineage (INJ-B). */
25
+ continuity: SessionContinuity;
26
+ /**
27
+ * SHA-256 hash of the directive's identity ({ prompt, assembly, model }).
28
+ * Recorded on FocusTeamState.lastConsumedDirectiveHash when the team
29
+ * consumes this directive, so the spawn-guard in executeSpawnDirective
30
+ * can detect divergent content within the 60s skip window.
31
+ */
32
+ contentHash: string;
33
+ }
34
+ /**
35
+ * Single shared instance of the pending spawn directive store. The executor
36
+ * sets entries directly via this binding; external callers use the accessor
37
+ * functions below.
38
+ */
39
+ export declare const pendingSpawnDirectives: Map<string, PendingSpawnDirective>;
40
+ /**
41
+ * Compute a stable hash for a stage directive's identity. Hashes only the
42
+ * fields that determine "what the agent is being told to do" -- prompt,
43
+ * assembly recipe, model -- not the assembled output, since that varies
44
+ * with git state and time.
45
+ */
46
+ export declare function computeDirectiveHash(directive: StageDirective): string;
47
+ /** Window during which a freshly-spawned team is assumed to have consumed
48
+ * the directive that produced it. */
49
+ export declare const SPAWN_GUARD_WINDOW_MS = 60000;
50
+ /**
51
+ * Decide whether executeSpawnDirective should skip the terminate+respawn
52
+ * cycle for a recently-spawned team. Pure function so the divergence rule
53
+ * is unit-testable without mocking the active teams map.
54
+ *
55
+ * - skip: team is within the spawn window AND its last-consumed directive
56
+ * hash matches the new directive's hash. The team is already running
57
+ * under the same intent.
58
+ * - respawn: team is older than the window OR the hashes diverge. Both
59
+ * cases require terminate+respawn so the team picks up fresh intent.
60
+ */
61
+ export declare function shouldSkipSpawnDirective(args: {
62
+ team: {
63
+ startedAt: Date;
64
+ lastConsumedDirectiveHash: string | null;
65
+ } | undefined;
66
+ contentHash: string;
67
+ now: number;
68
+ }): 'skip' | 'respawn';
69
+ /**
70
+ * Consume a pending spawn directive for a focus.
71
+ * Returns the directive content and removes it from the map.
72
+ * Called by spawnFocusTeam after spawning to send the directive.
73
+ */
74
+ export declare function consumePendingSpawnDirective(focusId: string): PendingSpawnDirective | undefined;
75
+ /**
76
+ * Test-only helper: prime a pending spawn directive without going through
77
+ * executeSpawnDirective. Tests use this to verify consume semantics with
78
+ * a known contentHash.
79
+ */
80
+ export declare function __setPendingSpawnDirectiveForTesting(focusId: string, directive: PendingSpawnDirective): void;
81
+ /**
82
+ * Check if a focus has a pending spawn directive.
83
+ * Used by the spawn guard to skip actionable-delivery checks.
84
+ */
85
+ export declare function hasPendingSpawnDirective(focusId: string): boolean;
86
+ /**
87
+ * Non-destructive read of a focus's pending spawn directive (unlike
88
+ * consumePendingSpawnDirective, this does NOT delete it). Used by the
89
+ * stranded-review re-fire bound to compare the queued directive's contentHash
90
+ * against the candidate about to be re-fired.
91
+ */
92
+ export declare function getPendingSpawnDirective(focusId: string): PendingSpawnDirective | undefined;
93
+ export declare function getPendingSpawnFocusIds(): string[];
94
+ /**
95
+ * Decide whether to SUPPRESS a stranded-review re-fire because an equivalent
96
+ * spawn directive is already queued and unconsumed.
97
+ *
98
+ * The stranded-review recovery re-fires a review directive when the focus is
99
+ * stuck in the review stage with no active team. But executeSpawnDirective
100
+ * re-stores the directive into the pending map on every call, and when there is
101
+ * no team to consume it the directive simply sits there. Without this bound,
102
+ * each poll re-fires, re-stores the same directive, and holds the pending-spawn
103
+ * condition true forever (root cause #18 -- the per-poll spin). When a pending
104
+ * directive with the SAME contentHash already exists, the queued directive will
105
+ * be picked up by the next spawn, so re-firing is a no-op churn: suppress it.
106
+ * A diverged contentHash (an updated stage directive) is NOT suppressed -- the
107
+ * fresh intent must replace the stale queued directive.
108
+ *
109
+ * Pure and exported for unit testing.
110
+ */
111
+ export declare function shouldSuppressRefireForPendingSpawn(input: {
112
+ pending: {
113
+ contentHash: string;
114
+ } | undefined;
115
+ candidateHash: string;
116
+ }): boolean;
117
+ //# sourceMappingURL=directive-queue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"directive-queue.d.ts","sourceRoot":"","sources":["../../src/directive/directive-queue.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAI/D;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,WAAW,EAAE,QAAQ,GAAG,QAAQ,CAAC;IACjC,2DAA2D;IAC3D,OAAO,EAAE,MAAM,CAAC;IAChB,+DAA+D;IAC/D,UAAU,EAAE,iBAAiB,CAAC;IAC9B;;;;;OAKG;IACH,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,oCAA2C,CAAC;AAE/E;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,cAAc,GAAG,MAAM,CAMtE;AAED;sCACsC;AACtC,eAAO,MAAM,qBAAqB,QAAQ,CAAC;AAE3C;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE;IAC7C,IAAI,EAAE;QAAE,SAAS,EAAE,IAAI,CAAC;QAAC,yBAAyB,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,SAAS,CAAC;IAChF,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;CACb,GAAG,MAAM,GAAG,SAAS,CAMrB;AAED;;;;GAIG;AACH,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,MAAM,GAAG,qBAAqB,GAAG,SAAS,CAM/F;AAED;;;;GAIG;AACH,wBAAgB,oCAAoC,CAClD,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,qBAAqB,GAC/B,IAAI,CAEN;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEjE;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,qBAAqB,GAAG,SAAS,CAE3F;AAED,wBAAgB,uBAAuB,IAAI,MAAM,EAAE,CAElD;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,mCAAmC,CAAC,KAAK,EAAE;IACzD,OAAO,EAAE;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC;IAC7C,aAAa,EAAE,MAAM,CAAC;CACvB,GAAG,OAAO,CAEV"}
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Directive queue: the pending spawn-directive store + its identity/guard logic.
3
+ *
4
+ * Holds the single shared `pendingSpawnDirectives` map (focusId ->
5
+ * PendingSpawnDirective) that executeSpawnDirective sets and spawnFocusTeam
6
+ * consumes, plus the pure helpers that compute a directive's identity hash and
7
+ * decide spawn-skip / re-fire suppression. Extracted from directive-executor.ts
8
+ * so the queue state and its accessors live in one focused module; the executor
9
+ * imports the map binding + helpers it calls in-line and re-exports the full
10
+ * surface (no duplicate instances).
11
+ */
12
+ import { createHash } from 'node:crypto';
13
+ /**
14
+ * Single shared instance of the pending spawn directive store. The executor
15
+ * sets entries directly via this binding; external callers use the accessor
16
+ * functions below.
17
+ */
18
+ export const pendingSpawnDirectives = new Map();
19
+ /**
20
+ * Compute a stable hash for a stage directive's identity. Hashes only the
21
+ * fields that determine "what the agent is being told to do" -- prompt,
22
+ * assembly recipe, model -- not the assembled output, since that varies
23
+ * with git state and time.
24
+ */
25
+ export function computeDirectiveHash(directive) {
26
+ return createHash('sha256').update(JSON.stringify({
27
+ prompt: directive.prompt ?? null,
28
+ assembly: directive.assembly ?? [],
29
+ model: directive.model ?? null,
30
+ })).digest('hex');
31
+ }
32
+ /** Window during which a freshly-spawned team is assumed to have consumed
33
+ * the directive that produced it. */
34
+ export const SPAWN_GUARD_WINDOW_MS = 60000;
35
+ /**
36
+ * Decide whether executeSpawnDirective should skip the terminate+respawn
37
+ * cycle for a recently-spawned team. Pure function so the divergence rule
38
+ * is unit-testable without mocking the active teams map.
39
+ *
40
+ * - skip: team is within the spawn window AND its last-consumed directive
41
+ * hash matches the new directive's hash. The team is already running
42
+ * under the same intent.
43
+ * - respawn: team is older than the window OR the hashes diverge. Both
44
+ * cases require terminate+respawn so the team picks up fresh intent.
45
+ */
46
+ export function shouldSkipSpawnDirective(args) {
47
+ const { team, contentHash, now } = args;
48
+ if (!team)
49
+ return 'respawn';
50
+ const age = now - team.startedAt.getTime();
51
+ if (age >= SPAWN_GUARD_WINDOW_MS)
52
+ return 'respawn';
53
+ return team.lastConsumedDirectiveHash === contentHash ? 'skip' : 'respawn';
54
+ }
55
+ /**
56
+ * Consume a pending spawn directive for a focus.
57
+ * Returns the directive content and removes it from the map.
58
+ * Called by spawnFocusTeam after spawning to send the directive.
59
+ */
60
+ export function consumePendingSpawnDirective(focusId) {
61
+ const directive = pendingSpawnDirectives.get(focusId);
62
+ if (directive) {
63
+ pendingSpawnDirectives.delete(focusId);
64
+ }
65
+ return directive;
66
+ }
67
+ /**
68
+ * Test-only helper: prime a pending spawn directive without going through
69
+ * executeSpawnDirective. Tests use this to verify consume semantics with
70
+ * a known contentHash.
71
+ */
72
+ export function __setPendingSpawnDirectiveForTesting(focusId, directive) {
73
+ pendingSpawnDirectives.set(focusId, directive);
74
+ }
75
+ /**
76
+ * Check if a focus has a pending spawn directive.
77
+ * Used by the spawn guard to skip actionable-delivery checks.
78
+ */
79
+ export function hasPendingSpawnDirective(focusId) {
80
+ return pendingSpawnDirectives.has(focusId);
81
+ }
82
+ /**
83
+ * Non-destructive read of a focus's pending spawn directive (unlike
84
+ * consumePendingSpawnDirective, this does NOT delete it). Used by the
85
+ * stranded-review re-fire bound to compare the queued directive's contentHash
86
+ * against the candidate about to be re-fired.
87
+ */
88
+ export function getPendingSpawnDirective(focusId) {
89
+ return pendingSpawnDirectives.get(focusId);
90
+ }
91
+ export function getPendingSpawnFocusIds() {
92
+ return [...pendingSpawnDirectives.keys()];
93
+ }
94
+ /**
95
+ * Decide whether to SUPPRESS a stranded-review re-fire because an equivalent
96
+ * spawn directive is already queued and unconsumed.
97
+ *
98
+ * The stranded-review recovery re-fires a review directive when the focus is
99
+ * stuck in the review stage with no active team. But executeSpawnDirective
100
+ * re-stores the directive into the pending map on every call, and when there is
101
+ * no team to consume it the directive simply sits there. Without this bound,
102
+ * each poll re-fires, re-stores the same directive, and holds the pending-spawn
103
+ * condition true forever (root cause #18 -- the per-poll spin). When a pending
104
+ * directive with the SAME contentHash already exists, the queued directive will
105
+ * be picked up by the next spawn, so re-firing is a no-op churn: suppress it.
106
+ * A diverged contentHash (an updated stage directive) is NOT suppressed -- the
107
+ * fresh intent must replace the stale queued directive.
108
+ *
109
+ * Pure and exported for unit testing.
110
+ */
111
+ export function shouldSuppressRefireForPendingSpawn(input) {
112
+ return input.pending !== undefined && input.pending.contentHash === input.candidateHash;
113
+ }
114
+ //# sourceMappingURL=directive-queue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"directive-queue.js","sourceRoot":"","sources":["../../src/directive/directive-queue.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AA2BzC;;;;GAIG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,IAAI,GAAG,EAAiC,CAAC;AAE/E;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,SAAyB;IAC5D,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC;QAChD,MAAM,EAAE,SAAS,CAAC,MAAM,IAAI,IAAI;QAChC,QAAQ,EAAE,SAAS,CAAC,QAAQ,IAAI,EAAE;QAClC,KAAK,EAAE,SAAS,CAAC,KAAK,IAAI,IAAI;KAC/B,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACpB,CAAC;AAED;sCACsC;AACtC,MAAM,CAAC,MAAM,qBAAqB,GAAG,KAAK,CAAC;AAE3C;;;;;;;;;;GAUG;AACH,MAAM,UAAU,wBAAwB,CAAC,IAIxC;IACC,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IACxC,IAAI,CAAC,IAAI;QAAE,OAAO,SAAS,CAAC;IAC5B,MAAM,GAAG,GAAG,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC;IAC3C,IAAI,GAAG,IAAI,qBAAqB;QAAE,OAAO,SAAS,CAAC;IACnD,OAAO,IAAI,CAAC,yBAAyB,KAAK,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;AAC7E,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,4BAA4B,CAAC,OAAe;IAC1D,MAAM,SAAS,GAAG,sBAAsB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACtD,IAAI,SAAS,EAAE,CAAC;QACd,sBAAsB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACzC,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,oCAAoC,CAClD,OAAe,EACf,SAAgC;IAEhC,sBAAsB,CAAC,GAAG,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;AACjD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,wBAAwB,CAAC,OAAe;IACtD,OAAO,sBAAsB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AAC7C,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,wBAAwB,CAAC,OAAe;IACtD,OAAO,sBAAsB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,uBAAuB;IACrC,OAAO,CAAC,GAAG,sBAAsB,CAAC,IAAI,EAAE,CAAC,CAAC;AAC5C,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,mCAAmC,CAAC,KAGnD;IACC,OAAO,KAAK,CAAC,OAAO,KAAK,SAAS,IAAI,KAAK,CAAC,OAAO,CAAC,WAAW,KAAK,KAAK,CAAC,aAAa,CAAC;AAC1F,CAAC"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Stage tracker: per-focus last-processed workflow stage state.
3
+ *
4
+ * Holds the single shared `lastProcessedStages` map (focusId -> stageId) that
5
+ * the directive edge-trigger uses to fire a stage directive exactly once per
6
+ * stage entry. Extracted from directive-executor.ts so the mutable state and
7
+ * its accessors live in one focused module; directive-executor imports the map
8
+ * binding for its in-loop reads/writes and re-exports the accessors.
9
+ */
10
+ /**
11
+ * Track the last-processed stage ID per focus to prevent
12
+ * double-injection on daemon restart or rapid poll cycles.
13
+ *
14
+ * This is the single shared instance. directive-executor's checkDirectives
15
+ * reads/writes it directly via this binding; external callers use the
16
+ * accessor functions below.
17
+ */
18
+ export declare const lastProcessedStages: Map<string, string>;
19
+ /** Seed the last-processed stage for a focus (used on daemon startup). */
20
+ export declare function seedLastProcessedStage(focusId: string, stageId: string): void;
21
+ /** Get the last-processed stages map (for persistence). */
22
+ export declare function getLastProcessedStages(): ReadonlyMap<string, string>;
23
+ //# sourceMappingURL=stage-tracker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stage-tracker.d.ts","sourceRoot":"","sources":["../../src/directive/stage-tracker.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,mBAAmB,qBAA4B,CAAC;AAE7D,0EAA0E;AAC1E,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAE7E;AAED,2DAA2D;AAC3D,wBAAgB,sBAAsB,IAAI,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAEpE"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Stage tracker: per-focus last-processed workflow stage state.
3
+ *
4
+ * Holds the single shared `lastProcessedStages` map (focusId -> stageId) that
5
+ * the directive edge-trigger uses to fire a stage directive exactly once per
6
+ * stage entry. Extracted from directive-executor.ts so the mutable state and
7
+ * its accessors live in one focused module; directive-executor imports the map
8
+ * binding for its in-loop reads/writes and re-exports the accessors.
9
+ */
10
+ /**
11
+ * Track the last-processed stage ID per focus to prevent
12
+ * double-injection on daemon restart or rapid poll cycles.
13
+ *
14
+ * This is the single shared instance. directive-executor's checkDirectives
15
+ * reads/writes it directly via this binding; external callers use the
16
+ * accessor functions below.
17
+ */
18
+ export const lastProcessedStages = new Map();
19
+ /** Seed the last-processed stage for a focus (used on daemon startup). */
20
+ export function seedLastProcessedStage(focusId, stageId) {
21
+ lastProcessedStages.set(focusId, stageId);
22
+ }
23
+ /** Get the last-processed stages map (for persistence). */
24
+ export function getLastProcessedStages() {
25
+ return lastProcessedStages;
26
+ }
27
+ //# sourceMappingURL=stage-tracker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stage-tracker.js","sourceRoot":"","sources":["../../src/directive/stage-tracker.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,GAAG,EAAkB,CAAC;AAE7D,0EAA0E;AAC1E,MAAM,UAAU,sBAAsB,CAAC,OAAe,EAAE,OAAe;IACrE,mBAAmB,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;AAC5C,CAAC;AAED,2DAA2D;AAC3D,MAAM,UAAU,sBAAsB;IACpC,OAAO,mBAAmB,CAAC;AAC7B,CAAC"}
@@ -9,110 +9,11 @@
9
9
  import type { DaemonConfig, StageDirective, DirectiveExecutionMode } from './types.js';
10
10
  import { type AssemblyManifestEntry } from './assembly-engine.js';
11
11
  import './assembly-resolvers.js';
12
- import { type SessionContinuity, type LineageSpec } from './session-lineage.js';
12
+ import { type LineageSpec } from './session-lineage.js';
13
13
  import { type CloseLoopDecision } from './close-loop-dispatcher.js';
14
14
  import { createEscalation } from './queries/issues.js';
15
- /** Seed the last-processed stage for a focus (used on daemon startup). */
16
- export declare function seedLastProcessedStage(focusId: string, stageId: string): void;
17
- /** Get the last-processed stages map (for persistence). */
18
- export declare function getLastProcessedStages(): ReadonlyMap<string, string>;
19
- /**
20
- * Pending spawn directive content, keyed by focusId.
21
- * Set by executeSpawnDirective, consumed by spawnFocusTeam.
22
- */
23
- export interface PendingSpawnDirective {
24
- message: string;
25
- model: string | null;
26
- sessionType: 'coding' | 'review';
27
- /** Session-id map slot this spawn reads/writes (INJ-B). */
28
- lineage: string;
29
- /** Resume-vs-fresh policy for this spawn's lineage (INJ-B). */
30
- continuity: SessionContinuity;
31
- /**
32
- * SHA-256 hash of the directive's identity ({ prompt, assembly, model }).
33
- * Recorded on FocusTeamState.lastConsumedDirectiveHash when the team
34
- * consumes this directive, so the spawn-guard in executeSpawnDirective
35
- * can detect divergent content within the 60s skip window.
36
- */
37
- contentHash: string;
38
- }
39
- /**
40
- * Compute a stable hash for a stage directive's identity. Hashes only the
41
- * fields that determine "what the agent is being told to do" -- prompt,
42
- * assembly recipe, model -- not the assembled output, since that varies
43
- * with git state and time.
44
- */
45
- export declare function computeDirectiveHash(directive: StageDirective): string;
46
- /** Window during which a freshly-spawned team is assumed to have consumed
47
- * the directive that produced it. */
48
- export declare const SPAWN_GUARD_WINDOW_MS = 60000;
49
- /**
50
- * Decide whether executeSpawnDirective should skip the terminate+respawn
51
- * cycle for a recently-spawned team. Pure function so the divergence rule
52
- * is unit-testable without mocking the active teams map.
53
- *
54
- * - skip: team is within the spawn window AND its last-consumed directive
55
- * hash matches the new directive's hash. The team is already running
56
- * under the same intent.
57
- * - respawn: team is older than the window OR the hashes diverge. Both
58
- * cases require terminate+respawn so the team picks up fresh intent.
59
- */
60
- export declare function shouldSkipSpawnDirective(args: {
61
- team: {
62
- startedAt: Date;
63
- lastConsumedDirectiveHash: string | null;
64
- } | undefined;
65
- contentHash: string;
66
- now: number;
67
- }): 'skip' | 'respawn';
68
- /**
69
- * Consume a pending spawn directive for a focus.
70
- * Returns the directive content and removes it from the map.
71
- * Called by spawnFocusTeam after spawning to send the directive.
72
- */
73
- export declare function consumePendingSpawnDirective(focusId: string): PendingSpawnDirective | undefined;
74
- /**
75
- * Test-only helper: prime a pending spawn directive without going through
76
- * executeSpawnDirective. Tests use this to verify consume semantics with
77
- * a known contentHash.
78
- */
79
- export declare function __setPendingSpawnDirectiveForTesting(focusId: string, directive: PendingSpawnDirective): void;
80
- /**
81
- * Check if a focus has a pending spawn directive.
82
- * Used by the spawn guard to skip actionable-delivery checks.
83
- */
84
- export declare function hasPendingSpawnDirective(focusId: string): boolean;
85
- /**
86
- * Non-destructive read of a focus's pending spawn directive (unlike
87
- * consumePendingSpawnDirective, this does NOT delete it). Used by the
88
- * stranded-review re-fire bound to compare the queued directive's contentHash
89
- * against the candidate about to be re-fired.
90
- */
91
- export declare function getPendingSpawnDirective(focusId: string): PendingSpawnDirective | undefined;
92
- export declare function getPendingSpawnFocusIds(): string[];
93
- /**
94
- * Decide whether to SUPPRESS a stranded-review re-fire because an equivalent
95
- * spawn directive is already queued and unconsumed.
96
- *
97
- * The stranded-review recovery re-fires a review directive when the focus is
98
- * stuck in the review stage with no active team. But executeSpawnDirective
99
- * re-stores the directive into the pending map on every call, and when there is
100
- * no team to consume it the directive simply sits there. Without this bound,
101
- * each poll re-fires, re-stores the same directive, and holds the pending-spawn
102
- * condition true forever (root cause #18 -- the per-poll spin). When a pending
103
- * directive with the SAME contentHash already exists, the queued directive will
104
- * be picked up by the next spawn, so re-firing is a no-op churn: suppress it.
105
- * A diverged contentHash (an updated stage directive) is NOT suppressed -- the
106
- * fresh intent must replace the stale queued directive.
107
- *
108
- * Pure and exported for unit testing.
109
- */
110
- export declare function shouldSuppressRefireForPendingSpawn(input: {
111
- pending: {
112
- contentHash: string;
113
- } | undefined;
114
- candidateHash: string;
115
- }): boolean;
15
+ export { seedLastProcessedStage, getLastProcessedStages } from './directive/stage-tracker.js';
16
+ export { computeDirectiveHash, SPAWN_GUARD_WINDOW_MS, shouldSkipSpawnDirective, consumePendingSpawnDirective, __setPendingSpawnDirectiveForTesting, hasPendingSpawnDirective, getPendingSpawnDirective, getPendingSpawnFocusIds, shouldSuppressRefireForPendingSpawn, type PendingSpawnDirective, } from './directive/directive-queue.js';
116
17
  /** Assembled directive content paired with its per-source attribution manifest. */
117
18
  export interface AssembledDirective {
118
19
  content: string;
@@ -1 +1 @@
1
- {"version":3,"file":"directive-executor.d.ts","sourceRoot":"","sources":["../src/directive-executor.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"directive-executor.d.ts","sourceRoot":"","sources":["../src/directive-executor.ts"],"names":[],"mappings":"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"}
@@ -6,10 +6,9 @@
6
6
  * For spawn mode: terminates current session, assembles directive content,
7
7
  * and stores it for the next spawn cycle to deliver.
8
8
  */
9
- import { createHash } from 'node:crypto';
10
9
  import { getActiveTeams } from './focus-team-state.js';
11
10
  import { resolveAssemblyRecipeWithManifest, } from './assembly-engine.js';
12
- // Import resolvers to ensure they're registered
11
+ // Side-effect import: registers assembly resolvers with assembly-engine at module load. Required - do not remove.
13
12
  import './assembly-resolvers.js';
14
13
  import { resolveLineageSpec } from './session-lineage.js';
15
14
  import { fetchFocusWorkflow, fetchFocusWorkflowWithTransitions, getFocusDeliveries, updateFocusStage, updateFocusWorkflowStage, } from './queries/focuses.js';
@@ -28,116 +27,19 @@ import { executeAdvanceLinkedInjections } from './trigger-executor.js';
28
27
  import { createEscalation } from './queries/issues.js';
29
28
  import { ESCALATION_REASONS } from '@telora/daemon-core';
30
29
  import { emitLoopTrigger } from './loop-event-bus.js';
31
- // ── Stage tracking ───────────────────────────────────────────────
32
- /**
33
- * Track the last-processed stage ID per focus to prevent
34
- * double-injection on daemon restart or rapid poll cycles.
35
- */
36
- const lastProcessedStages = new Map();
37
- /** Seed the last-processed stage for a focus (used on daemon startup). */
38
- export function seedLastProcessedStage(focusId, stageId) {
39
- lastProcessedStages.set(focusId, stageId);
40
- }
41
- /** Get the last-processed stages map (for persistence). */
42
- export function getLastProcessedStages() {
43
- return lastProcessedStages;
44
- }
45
- const pendingSpawnDirectives = new Map();
46
- /**
47
- * Compute a stable hash for a stage directive's identity. Hashes only the
48
- * fields that determine "what the agent is being told to do" -- prompt,
49
- * assembly recipe, model -- not the assembled output, since that varies
50
- * with git state and time.
51
- */
52
- export function computeDirectiveHash(directive) {
53
- return createHash('sha256').update(JSON.stringify({
54
- prompt: directive.prompt ?? null,
55
- assembly: directive.assembly ?? [],
56
- model: directive.model ?? null,
57
- })).digest('hex');
58
- }
59
- /** Window during which a freshly-spawned team is assumed to have consumed
60
- * the directive that produced it. */
61
- export const SPAWN_GUARD_WINDOW_MS = 60000;
62
- /**
63
- * Decide whether executeSpawnDirective should skip the terminate+respawn
64
- * cycle for a recently-spawned team. Pure function so the divergence rule
65
- * is unit-testable without mocking the active teams map.
66
- *
67
- * - skip: team is within the spawn window AND its last-consumed directive
68
- * hash matches the new directive's hash. The team is already running
69
- * under the same intent.
70
- * - respawn: team is older than the window OR the hashes diverge. Both
71
- * cases require terminate+respawn so the team picks up fresh intent.
72
- */
73
- export function shouldSkipSpawnDirective(args) {
74
- const { team, contentHash, now } = args;
75
- if (!team)
76
- return 'respawn';
77
- const age = now - team.startedAt.getTime();
78
- if (age >= SPAWN_GUARD_WINDOW_MS)
79
- return 'respawn';
80
- return team.lastConsumedDirectiveHash === contentHash ? 'skip' : 'respawn';
81
- }
82
- /**
83
- * Consume a pending spawn directive for a focus.
84
- * Returns the directive content and removes it from the map.
85
- * Called by spawnFocusTeam after spawning to send the directive.
86
- */
87
- export function consumePendingSpawnDirective(focusId) {
88
- const directive = pendingSpawnDirectives.get(focusId);
89
- if (directive) {
90
- pendingSpawnDirectives.delete(focusId);
91
- }
92
- return directive;
93
- }
94
- /**
95
- * Test-only helper: prime a pending spawn directive without going through
96
- * executeSpawnDirective. Tests use this to verify consume semantics with
97
- * a known contentHash.
98
- */
99
- export function __setPendingSpawnDirectiveForTesting(focusId, directive) {
100
- pendingSpawnDirectives.set(focusId, directive);
101
- }
102
- /**
103
- * Check if a focus has a pending spawn directive.
104
- * Used by the spawn guard to skip actionable-delivery checks.
105
- */
106
- export function hasPendingSpawnDirective(focusId) {
107
- return pendingSpawnDirectives.has(focusId);
108
- }
109
- /**
110
- * Non-destructive read of a focus's pending spawn directive (unlike
111
- * consumePendingSpawnDirective, this does NOT delete it). Used by the
112
- * stranded-review re-fire bound to compare the queued directive's contentHash
113
- * against the candidate about to be re-fired.
114
- */
115
- export function getPendingSpawnDirective(focusId) {
116
- return pendingSpawnDirectives.get(focusId);
117
- }
118
- export function getPendingSpawnFocusIds() {
119
- return [...pendingSpawnDirectives.keys()];
120
- }
121
- /**
122
- * Decide whether to SUPPRESS a stranded-review re-fire because an equivalent
123
- * spawn directive is already queued and unconsumed.
124
- *
125
- * The stranded-review recovery re-fires a review directive when the focus is
126
- * stuck in the review stage with no active team. But executeSpawnDirective
127
- * re-stores the directive into the pending map on every call, and when there is
128
- * no team to consume it the directive simply sits there. Without this bound,
129
- * each poll re-fires, re-stores the same directive, and holds the pending-spawn
130
- * condition true forever (root cause #18 -- the per-poll spin). When a pending
131
- * directive with the SAME contentHash already exists, the queued directive will
132
- * be picked up by the next spawn, so re-firing is a no-op churn: suppress it.
133
- * A diverged contentHash (an updated stage directive) is NOT suppressed -- the
134
- * fresh intent must replace the stale queued directive.
135
- *
136
- * Pure and exported for unit testing.
137
- */
138
- export function shouldSuppressRefireForPendingSpawn(input) {
139
- return input.pending !== undefined && input.pending.contentHash === input.candidateHash;
140
- }
30
+ // ── Stage tracking (extracted to directive/stage-tracker.ts) ─────
31
+ // The lastProcessedStages map + its accessors now live in a focused module.
32
+ // directive-executor imports the map binding for its in-loop reads/writes and
33
+ // RE-EXPORTS the accessors so existing importers are unaffected.
34
+ import { lastProcessedStages } from './directive/stage-tracker.js';
35
+ export { seedLastProcessedStage, getLastProcessedStages } from './directive/stage-tracker.js';
36
+ // ── Pending spawn directives (extracted to directive/directive-queue.ts) ──
37
+ // The pendingSpawnDirectives map + its accessors and the directive identity /
38
+ // spawn-guard helpers now live in a focused module. directive-executor imports
39
+ // the map binding + helpers it calls in-line and RE-EXPORTS the full surface so
40
+ // existing importers (focus-executor, listener, tests) are unaffected.
41
+ import { pendingSpawnDirectives, computeDirectiveHash, SPAWN_GUARD_WINDOW_MS, shouldSkipSpawnDirective, getPendingSpawnDirective, shouldSuppressRefireForPendingSpawn, } from './directive/directive-queue.js';
42
+ export { computeDirectiveHash, SPAWN_GUARD_WINDOW_MS, shouldSkipSpawnDirective, consumePendingSpawnDirective, __setPendingSpawnDirectiveForTesting, hasPendingSpawnDirective, getPendingSpawnDirective, getPendingSpawnFocusIds, shouldSuppressRefireForPendingSpawn, } from './directive/directive-queue.js';
141
43
  /**
142
44
  * Assemble directive content from a StageDirective's recipe and prompt.
143
45
  * Shared by both inject and spawn modes, and by focus-executor for