@orchestrator-claude/definitions 3.36.0 → 3.37.1

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 (69) hide show
  1. package/agents/adversarial-reviewer.md +6 -1
  2. package/agents/api-extractor.md +6 -0
  3. package/agents/azure-devops-engineer.md +1 -2
  4. package/agents/business-rule-miner.md +6 -0
  5. package/agents/cicd-platform-engineer.md +1 -2
  6. package/agents/ciclo-engineer.md +15 -2
  7. package/agents/code-archaeologist.md +6 -0
  8. package/agents/debug-sidecar.md +6 -0
  9. package/agents/doc-sidecar.md +6 -0
  10. package/agents/docs-guardian.md +9 -3
  11. package/agents/frontend-engineer.md +1 -2
  12. package/agents/{ai-architect.md → harness-architect.md} +26 -5
  13. package/agents/implementer.md +6 -1
  14. package/agents/legacy-discoverer.md +6 -0
  15. package/agents/legacy-synthesizer.md +6 -0
  16. package/agents/methodology-researcher.md +6 -1
  17. package/agents/operational-artifact-builder.md +6 -1
  18. package/agents/orchestrator.md +27 -87
  19. package/agents/planner.md +6 -1
  20. package/agents/presentation-engineer.md +5 -4
  21. package/agents/python-data-engineer.md +0 -1
  22. package/agents/researcher.md +6 -0
  23. package/agents/reviewer.md +6 -1
  24. package/agents/schema-extractor.md +6 -0
  25. package/agents/software-architect.md +30 -7
  26. package/agents/specifier.md +6 -1
  27. package/agents/task-generator.md +6 -1
  28. package/agents/test-sidecar.md +6 -0
  29. package/dist/scripts/generate-hooks-matcher.d.ts +106 -0
  30. package/dist/scripts/generate-hooks-matcher.d.ts.map +1 -0
  31. package/dist/scripts/generate-hooks-matcher.js +153 -0
  32. package/dist/scripts/generate-hooks-matcher.js.map +1 -0
  33. package/dist/scripts/generate-manifest.d.ts +22 -0
  34. package/dist/scripts/generate-manifest.d.ts.map +1 -1
  35. package/dist/scripts/generate-manifest.js +67 -2
  36. package/dist/scripts/generate-manifest.js.map +1 -1
  37. package/hooks/agent-git-guard.mjs +63 -11
  38. package/hooks/dangling-guard.mjs +42 -12
  39. package/hooks/gate-guardian.mjs +38 -8
  40. package/hooks/hooks.json +3 -9
  41. package/hooks/lib/api-client.ts +128 -9
  42. package/hooks/lib/capability-scanner.ts +391 -55
  43. package/hooks/lib/dispatch-marker.ts +214 -0
  44. package/hooks/lib/git-checkpoint.ts +34 -37
  45. package/hooks/lib/observations-helper.ts +14 -1
  46. package/hooks/mailbox-listener.mjs +69 -17
  47. package/hooks/playwright-output-guard.mjs +40 -10
  48. package/hooks/post-implement.mjs +38 -8
  49. package/hooks/pre-agent-dispatch.mjs +137 -16
  50. package/hooks/pre-agent-dispatch.ts +82 -15
  51. package/hooks/session-start.mjs +274 -103
  52. package/hooks/session-start.ts +250 -69
  53. package/hooks/subagent-start.mjs +7665 -22
  54. package/hooks/subagent-start.ts +367 -16
  55. package/hooks/subagent-stop.mjs +102 -62
  56. package/hooks/subagent-stop.ts +124 -28
  57. package/hooks/user-prompt.mjs +43 -13
  58. package/hooks/verification-before-completion.mjs +63 -11
  59. package/hooks/workflow-guard.mjs +45 -11
  60. package/hooks-settings-template.json +1 -7
  61. package/manifest.json +62 -62
  62. package/package.json +4 -2
  63. package/skills/checkpoint-protocol/SKILL.md +19 -4
  64. package/skills/ciclo-lane-dispatch/SKILL.md +24 -0
  65. package/skills/fase-1b-projecao/SKILL.md +146 -0
  66. package/skills/orchestrator/SKILL.md +11 -3
  67. package/skills/workflow-coordination/SKILL.md +3 -3
  68. package/hooks/post-compact.mjs +0 -7931
  69. package/hooks/post-compact.ts +0 -170
@@ -2,8 +2,113 @@
2
2
  * subagent-start.ts — SubagentStart hook (RFC-022 Phase 2)
3
3
  * Replaces track-agent-invocation.sh start
4
4
  * Registers agent invocation via REST API.
5
+ *
6
+ * TD-214/TD-239 TASK-004 (RF-007 core, identity point #3 of the
7
+ * consolidator — see packages/definitions/tests/IdentityNormalizationConsolidator.test.ts):
8
+ * this is the PRIMARY critical path for invocation tracking — every
9
+ * RF-002/RF-004 telemetry signal downstream depends on this hook correctly
10
+ * identifying WHICH agent was dispatched. Two defects fixed here:
11
+ *
12
+ * 1. Precedence chain used to fall back to a literal `'unknown'` string that
13
+ * was then tracked via startInvocation() as if it were a genuine agent
14
+ * name — a silent fallback promoted to a real identity. The chain is now
15
+ * `agent_type -> tool_input.subagent_type -> subagent_type ->
16
+ * tool_input.type`, with NO trailing `|| 'unknown'`; an all-absent read
17
+ * resolves to `''`, which the defensive filter below treats as a read
18
+ * FAILURE (not a legitimate agent), not a would-be lookup key.
19
+ * 2. No namespace normalization: under plugin scope the harness sends
20
+ * `orchestrator:reviewer`; PHASE_MAP only has bare keys (`reviewer`), so
21
+ * a namespaced dispatch silently fell through to
22
+ * `PHASE_MAP[agentType] || agentType` and got the WRONG phase (the raw
23
+ * namespaced string itself). `normalizeAgentName` (moved to
24
+ * `lib/api-client.ts` in TASK-001) is applied immediately after the read.
25
+ *
26
+ * Defensive filter (classifyAgentType), applied to the normalized value:
27
+ * - `''` or literal `'unknown'` -> the read genuinely failed (no field in
28
+ * the 4-deep chain produced anything usable). Logged at [ERROR] — elevated
29
+ * from [WARN] per RNF-003, because this is a signal that the upstream
30
+ * payload shape changed, not an expected/everyday case. `startInvocation`
31
+ * is never called.
32
+ * - non-empty but NOT a member of the canonical agent set -> a real Task
33
+ * dispatch that simply isn't one of THIS project's workflow agents (the
34
+ * common case: `general-purpose`, or any other ad-hoc subagent_type).
35
+ * Logged at [DEBUG] — this is normal, not a failure. `startInvocation` is
36
+ * never called (there is nothing meaningful to track invocation-wise for
37
+ * an agent this hook doesn't recognize as a workflow participant).
38
+ * - a member of the canonical set -> proceeds exactly as before (phase
39
+ * lookup, startInvocation, writeInvocationState).
40
+ *
41
+ * These two branches are checked in THIS order (read-failure first) — '' and
42
+ * 'unknown' can never be members of the canonical set, so checking set
43
+ * membership first would make the [ERROR] branch permanently unreachable.
44
+ *
45
+ * Canonical agent set (ADR-023 plan ADR-P2, measured empirically S433):
46
+ * `packages/definitions/agents/` (28 `.md` files today) — DELIBERATELY NOT
47
+ * the `AGENTS_DIR = join(PROJECT_ROOT, '.claude', 'agents')` pattern used by
48
+ * session-start.ts. In THIS repo (ADR-023 dogfood — the
49
+ * canonical source lives here, nothing is "installed" into `.claude/agents/`)
50
+ * that path resolves to a directory with 0 `.md` files, so reusing it here
51
+ * would make `classifyAgentType` reject every real agent (fail-closed) or,
52
+ * if the check were inverted defensively, accept everything (fail-open) —
53
+ * both wrong. That gap is TD-243, filed and explicitly OUT OF SCOPE for this
54
+ * task; `AGENTS_DIR`'s call site in session-start.ts is
55
+ * NOT touched here. `resolveCanonicalAgentsDir` below resolves the path
56
+ * independently, local to this file.
57
+ *
58
+ * TD-214/TD-239 TASK-005 (RF-007 instrumentation, one layer above TASK-004):
59
+ * captures the FULL raw stdin payload — not just the 4 fields the precedence
60
+ * chain above extracts — into a DEDICATED log, `hook-raw-payloads.jsonl`,
61
+ * kept SEPARATE from `hook-debug.log` (retention-cost requirement, not
62
+ * style: this log is high-volume and `hook-debug.log` is read for other
63
+ * purposes). Every line also carries `correlatedWithDispatchIntent`, sourced
64
+ * from `hasFreshDispatchIntent(workflow.id)` (TD-205) — consumed VERBATIM
65
+ * from `lib/api-client.ts` (hard restriction: that file is CONSUMED, not
66
+ * modified, here; its signature/behavior are unchanged, since
67
+ * `dangling-guard.ts` rule 6 already depends on both).
68
+ *
69
+ * Capture fires on EVERY hook trigger — read-failure, unrecognized-agent,
70
+ * no-active-workflow, and proceed branches alike — deliberately, because the
71
+ * RF-011/TASK-012 consumer this log is written for needs the raw payload
72
+ * precisely for the cases where identity extraction FAILED or produced an
73
+ * unexpected value (the bug class this whole workflow exists to diagnose),
74
+ * not only for the successful path. To support this without a duplicate
75
+ * `getActiveWorkflow()` round-trip, the workflow lookup that previously
76
+ * happened only in the `proceed` branch now happens ONCE, up front, and is
77
+ * reused both for the correlation flag and for the pre-existing
78
+ * `startInvocation` gating further down.
79
+ *
80
+ * `hook-raw-payloads.jsonl`'s location is NOT obtained from
81
+ * `lib/api-client.ts` (its `STATE_DIR`/`DEBUG_LOG` constants are not
82
+ * exported, and this task must not touch that file) — `resolveStateDir`
83
+ * below duplicates the same override-then-sentinel derivation
84
+ * (`ORCHESTRATOR_STATE_DIR` env override, else `.orchestrator/.state` under
85
+ * the resolved project root) that `lib/api-client.ts` uses internally for
86
+ * `STATE_DIR`, mirroring the `resolveCanonicalAgentsDir` "resolve locally,
87
+ * don't touch the lib" precedent from TASK-004.
88
+ *
89
+ * TD-214/TD-239 TASK-010 (RF-010 Fase 5 Parte 3 — wiring): once
90
+ * `classifyAgentType` reaches `decision.kind === 'proceed'` (RF-007
91
+ * successfully resolved `resolvedAgentType`), reads
92
+ * `<resolveAgentMemoryBaseDir(cwd)>/<resolvedAgentType>/memory-index.yaml`
93
+ * (same `resolveAgentMemoryBaseDir` already injected by
94
+ * subagent-stop.ts/TASK-006 for the identical purpose), composes via
95
+ * `filterRelevantMemories` (TASK-008, HIGH-001 quota fix) and
96
+ * `formatMemoryContext` (TASK-009, per-entry cap + aggregate budget), and
97
+ * injects the result via `outputContext('SubagentStart', ...)` — but ONLY
98
+ * when the formatted string is non-empty (an index that reads but composes
99
+ * to nothing, e.g. every entry stale and none is `feedback`, must not emit a
100
+ * hollow `[MEMORY-CONTEXT]` block).
101
+ *
102
+ * Gated SOLELY on identity resolution (the RF-007->RF-010 hard dependency —
103
+ * TASK-011 proves this at the integration level: a payload RF-007 rejects
104
+ * must produce zero `outputContext` calls anywhere in the pipeline), NOT on
105
+ * an active workflow existing: memory belongs to the agent's own identity,
106
+ * not to workflow-tracking state, so this runs BEFORE the `if (!workflow)`
107
+ * early-return below and independently of it.
5
108
  */
6
109
 
110
+ import { join, dirname } from 'node:path';
111
+ import { mkdirSync, writeFileSync } from 'node:fs';
7
112
  import {
8
113
  log,
9
114
  readStdin,
@@ -11,7 +116,20 @@ import {
11
116
  getActiveWorkflow,
12
117
  startInvocation,
13
118
  writeInvocationState,
119
+ normalizeAgentName,
120
+ resolveHookProjectRoot,
121
+ hasFreshDispatchIntent,
122
+ resolveAgentMemoryBaseDir,
123
+ outputContext,
14
124
  } from './lib/api-client.js';
125
+ import {
126
+ scanAgentDefinitions,
127
+ readMemoryIndex,
128
+ filterRelevantMemories,
129
+ formatMemoryContext,
130
+ type MemoryEntry,
131
+ type MemoryIndexDiagnostic,
132
+ } from './lib/capability-scanner.js';
15
133
 
16
134
  const HOOK = 'SUBAGENT-START';
17
135
 
@@ -24,39 +142,272 @@ const PHASE_MAP: Record<string, string> = {
24
142
  reviewer: 'review',
25
143
  };
26
144
 
27
- async function main(): Promise<void> {
28
- const stdin = await readStdin();
29
- log(HOOK, `SubagentStart triggered`);
145
+ // ─────────────────────────────────────────────────────────────
146
+ // Pure helpers (exported for testing)
147
+ // ─────────────────────────────────────────────────────────────
30
148
 
31
- const agentType =
149
+ /**
150
+ * Reads the dispatched agent's raw (un-normalized) identity from stdin via
151
+ * the 4-field precedence chain. Returns `''` when none of the fields are
152
+ * present — deliberately NOT `'unknown'`; a silent placeholder fallback is
153
+ * exactly the defect this task fixes (see module doc).
154
+ */
155
+ export function resolveRawAgentType(stdin: Record<string, unknown>): string {
156
+ return (
157
+ getField(stdin, 'agent_type') ||
32
158
  getField(stdin, 'tool_input.subagent_type') ||
33
159
  getField(stdin, 'subagent_type') ||
34
- getField(stdin, 'tool_input.type') ||
35
- 'unknown';
160
+ getField(stdin, 'tool_input.type')
161
+ );
162
+ }
163
+
164
+ export type AgentTypeDecision =
165
+ | { kind: 'skip-read-failed' }
166
+ | { kind: 'skip-unrecognized'; agentType: string }
167
+ | { kind: 'proceed'; agentType: string };
168
+
169
+ /**
170
+ * Builds one JSONL line for `hook-raw-payloads.jsonl` (TASK-005, RF-007):
171
+ * the FULL raw stdin payload (verbatim, not filtered to the 4 identity
172
+ * fields) plus the dispatch-intent correlation flag. Pure — no IO — so it is
173
+ * independently testable and reusable by whoever writes the RF-011/TASK-012
174
+ * consumer (economia deliberada per the dispatch: parse this file instead of
175
+ * re-reading stdin / re-calling `hasFreshDispatchIntent`).
176
+ */
177
+ export function buildRawPayloadLogLine(
178
+ payload: Record<string, unknown>,
179
+ correlatedWithDispatchIntent: boolean,
180
+ nowIso: string = new Date().toISOString()
181
+ ): string {
182
+ return JSON.stringify({
183
+ timestamp: nowIso,
184
+ hook: HOOK,
185
+ payload,
186
+ correlatedWithDispatchIntent,
187
+ });
188
+ }
36
189
 
37
- const phase = PHASE_MAP[agentType] || agentType;
190
+ /**
191
+ * Defensive filter over an ALREADY-NORMALIZED agent type (see module doc for
192
+ * branch ordering rationale). `getCanonicalAgentNames` is only invoked when
193
+ * `agentType` is non-empty and not the literal `'unknown'` — no need to pay
194
+ * for a directory scan on a read that already failed.
195
+ */
196
+ export function classifyAgentType(
197
+ agentType: string,
198
+ getCanonicalAgentNames: () => string[]
199
+ ): AgentTypeDecision {
200
+ if (agentType === '' || agentType === 'unknown') {
201
+ return { kind: 'skip-read-failed' };
202
+ }
203
+ const canonicalAgents = getCanonicalAgentNames();
204
+ if (!canonicalAgents.includes(agentType)) {
205
+ return { kind: 'skip-unrecognized', agentType };
206
+ }
207
+ return { kind: 'proceed', agentType };
208
+ }
38
209
 
39
- log(HOOK, `agent=${agentType} phase=${phase}`);
210
+ // ─────────────────────────────────────────────────────────────
211
+ // Dependency injection interface (enables testing without IO)
212
+ // ─────────────────────────────────────────────────────────────
213
+
214
+ export interface HookDeps {
215
+ readStdin: () => Promise<Record<string, unknown>>;
216
+ log: (prefix: string, msg: string) => void;
217
+ getActiveWorkflow: () => Promise<{ id: string } | null>;
218
+ startInvocation: (agentType: string, workflowId: string, phase: string) => Promise<string | null>;
219
+ writeInvocationState: (workflowId: string, agentType: string, invocationId: string) => boolean;
220
+ /** Returns the canonical agent-name roster (see module doc, ADR-P2). */
221
+ getCanonicalAgentNames: () => string[];
222
+ /**
223
+ * TASK-005 (RF-007, TD-205): dispatch-intent freshness check. Wired
224
+ * directly to `lib/api-client.ts`'s `hasFreshDispatchIntent` — same
225
+ * function, same signature, same behavior as the `dangling-guard.ts` rule
226
+ * 6 consumer; this hook must not (and does not) alter either.
227
+ */
228
+ hasFreshDispatchIntent: (workflowId: string) => boolean;
229
+ /**
230
+ * TASK-005 (RF-007): appends one pre-built JSONL line to
231
+ * `hook-raw-payloads.jsonl` — a channel SEPARATE from `log`/
232
+ * `hook-debug.log` (see module doc for the retention-cost rationale).
233
+ */
234
+ appendRawPayloadLog: (line: string) => void;
235
+ /**
236
+ * TASK-010 (RF-010 Parte 3): resolves the `.claude/agent-memory` base dir
237
+ * for the CURRENT hook invocation — same function/contract already
238
+ * injected by subagent-stop.ts (TASK-006).
239
+ */
240
+ resolveAgentMemoryBaseDir: (cwd: string) => string;
241
+ /** TASK-010: reads `<baseDir>/<resolvedAgentType>/memory-index.yaml` (TASK-026 dual-shape parser, fail-open). */
242
+ readMemoryIndex: (indexPath: string, diag?: MemoryIndexDiagnostic) => MemoryEntry[] | null;
243
+ /** TASK-010: composition (TASK-008, HIGH-001 quota fix — reserves slots per non-feedback type). */
244
+ filterRelevantMemories: (entries: MemoryEntry[]) => MemoryEntry[];
245
+ /** TASK-010: formatting (TASK-009 — per-entry char cap + aggregate char budget). */
246
+ formatMemoryContext: (selected: MemoryEntry[], allEntries: MemoryEntry[]) => string;
247
+ /** TASK-010: injects the formatted memory context into the dispatched sub-agent's turn. */
248
+ outputContext: (hookEvent: string, context: string) => void;
249
+ }
250
+
251
+ /** `packages/definitions/agents` under the resolved project root (module doc, ADR-P2). */
252
+ function resolveCanonicalAgentsDir(): string {
253
+ const root = resolveHookProjectRoot(process.cwd()) ?? process.cwd();
254
+ return join(root, 'packages', 'definitions', 'agents');
255
+ }
256
+
257
+ function getCanonicalAgentNamesDefault(): string[] {
258
+ return scanAgentDefinitions(resolveCanonicalAgentsDir());
259
+ }
260
+
261
+ /**
262
+ * Duplicates `lib/api-client.ts`'s internal `STATE_DIR` derivation (TD-224
263
+ * Achado 1: `ORCHESTRATOR_STATE_DIR` override wins outright, else
264
+ * `.orchestrator/.state` under the resolved project root) WITHOUT importing
265
+ * it — `STATE_DIR` is not exported, and this task's hard restriction forbids
266
+ * touching `lib/api-client.ts`. Same "resolve locally, don't touch the lib"
267
+ * precedent as `resolveCanonicalAgentsDir` above (TASK-004).
268
+ */
269
+ function resolveStateDir(): string {
270
+ const override = process.env.ORCHESTRATOR_STATE_DIR;
271
+ if (override !== undefined && override !== '') return override;
272
+ const root = resolveHookProjectRoot(process.cwd()) ?? process.cwd();
273
+ return join(root, '.orchestrator', '.state');
274
+ }
275
+
276
+ function resolveRawPayloadLogPath(): string {
277
+ return join(resolveStateDir(), 'hook-raw-payloads.jsonl');
278
+ }
279
+
280
+ /** Fail-open, mirrors `lib/api-client.ts`'s `log()` posture: never throws. */
281
+ function appendRawPayloadLogDefault(line: string): void {
282
+ try {
283
+ const path = resolveRawPayloadLogPath();
284
+ mkdirSync(dirname(path), { recursive: true });
285
+ writeFileSync(path, `${line}\n`, { flag: 'a' });
286
+ } catch {
287
+ // fail silently
288
+ }
289
+ }
290
+
291
+ const defaultDeps: HookDeps = {
292
+ readStdin,
293
+ log,
294
+ getActiveWorkflow,
295
+ startInvocation,
296
+ writeInvocationState,
297
+ getCanonicalAgentNames: getCanonicalAgentNamesDefault,
298
+ hasFreshDispatchIntent,
299
+ appendRawPayloadLog: appendRawPayloadLogDefault,
300
+ resolveAgentMemoryBaseDir,
301
+ readMemoryIndex,
302
+ filterRelevantMemories,
303
+ formatMemoryContext,
304
+ outputContext,
305
+ };
306
+
307
+ // ─────────────────────────────────────────────────────────────
308
+ // Core logic (exported for testing)
309
+ // ─────────────────────────────────────────────────────────────
310
+
311
+ export async function mainWithDeps(deps: HookDeps): Promise<void> {
312
+ const stdin = await deps.readStdin();
313
+ deps.log(HOOK, `SubagentStart triggered`);
314
+
315
+ // TASK-005 (RF-007): resolve the active workflow ONCE, up front — reused
316
+ // both for the raw-payload correlation below and for the pre-existing
317
+ // startInvocation gating further down (no duplicate getActiveWorkflow call
318
+ // introduced by this instrumentation).
319
+ const workflow = await deps.getActiveWorkflow();
320
+ const correlatedWithDispatchIntent = workflow ? deps.hasFreshDispatchIntent(workflow.id) : false;
321
+ deps.appendRawPayloadLog(buildRawPayloadLogLine(stdin, correlatedWithDispatchIntent));
322
+
323
+ const rawAgentType = resolveRawAgentType(stdin);
324
+ const agentType = normalizeAgentName(rawAgentType);
325
+
326
+ const decision = classifyAgentType(agentType, deps.getCanonicalAgentNames);
327
+
328
+ if (decision.kind === 'skip-read-failed') {
329
+ deps.log(
330
+ HOOK,
331
+ `[ERROR] could not read agent identity from stdin (checked agent_type, ` +
332
+ `tool_input.subagent_type, subagent_type, tool_input.type — all empty or ` +
333
+ `literal 'unknown'; raw="${rawAgentType}") — skipping invocation tracking ` +
334
+ `(RNF-003: elevated from WARN)`
335
+ );
336
+ return;
337
+ }
338
+
339
+ if (decision.kind === 'skip-unrecognized') {
340
+ deps.log(
341
+ HOOK,
342
+ `[DEBUG] agent="${decision.agentType}" is not in the canonical agent set — ` +
343
+ `skipping invocation tracking (not a workflow agent, e.g. a general-purpose ` +
344
+ `Task dispatch)`
345
+ );
346
+ return;
347
+ }
348
+
349
+ const resolvedAgentType = decision.agentType;
350
+ const phase = PHASE_MAP[resolvedAgentType] || resolvedAgentType;
351
+
352
+ deps.log(HOOK, `agent=${resolvedAgentType} phase=${phase}`);
353
+
354
+ // TASK-010 (RF-010 Parte 3): memory context injection. Gated ONLY on
355
+ // identity resolution (this point is only reached when decision.kind ===
356
+ // 'proceed', above) — deliberately BEFORE the `!workflow` early return,
357
+ // since memory belongs to the agent's identity, not to workflow-tracking
358
+ // state (see module doc).
359
+ const memoryIndexPath = join(
360
+ deps.resolveAgentMemoryBaseDir(process.cwd()),
361
+ resolvedAgentType,
362
+ 'memory-index.yaml'
363
+ );
364
+ // S480: o sub-agente sofria a MESMA cegueira do session-start -- mesma funcao,
365
+ // mesmos tres pontos mudos. Consertar so um lado deixaria o outro calado.
366
+ const memoryDiag: MemoryIndexDiagnostic = {};
367
+ const memoryEntries = deps.readMemoryIndex(memoryIndexPath, memoryDiag);
368
+ if (memoryDiag.reason) {
369
+ deps.log(HOOK, `Memory index: ${memoryDiag.reason}`);
370
+ if (!memoryEntries || memoryEntries.length === 0) {
371
+ deps.outputContext(
372
+ 'SubagentStart',
373
+ `[MEMORY-CONTEXT] INDISPONÍVEL — nenhuma memória injetada. ${memoryDiag.reason}`
374
+ );
375
+ }
376
+ }
377
+ if (memoryEntries) {
378
+ const selectedMemories = deps.filterRelevantMemories(memoryEntries);
379
+ const memoryContext = deps.formatMemoryContext(selectedMemories, memoryEntries);
380
+ if (memoryContext) {
381
+ deps.outputContext('SubagentStart', memoryContext);
382
+ }
383
+ }
40
384
 
41
- const workflow = await getActiveWorkflow();
42
385
  if (!workflow) {
43
- log(HOOK, 'No active workflow, skipping');
386
+ deps.log(HOOK, 'No active workflow, skipping');
44
387
  return;
45
388
  }
46
389
 
47
- const invocationId = await startInvocation(agentType, workflow.id, phase);
390
+ const invocationId = await deps.startInvocation(resolvedAgentType, workflow.id, phase);
48
391
  if (invocationId) {
49
- const written = writeInvocationState(workflow.id, agentType, invocationId);
50
- log(
392
+ const written = deps.writeInvocationState(workflow.id, resolvedAgentType, invocationId);
393
+ deps.log(
51
394
  HOOK,
52
- `START: agent=${agentType} phase=${phase} workflow=${workflow.id} invocationId=${invocationId} stateWritten=${written}`
395
+ `START: agent=${resolvedAgentType} phase=${phase} workflow=${workflow.id} invocationId=${invocationId} stateWritten=${written}`
53
396
  );
54
397
  } else {
55
- log(
398
+ deps.log(
56
399
  HOOK,
57
- `START: agent=${agentType} phase=${phase} workflow=${workflow.id} — startInvocation returned null (POST failed or no token)`
400
+ `START: agent=${resolvedAgentType} phase=${phase} workflow=${workflow.id} — startInvocation returned null (POST failed or no token)`
58
401
  );
59
402
  }
60
403
  }
61
404
 
405
+ // ─────────────────────────────────────────────────────────────
406
+ // Entry point (production use)
407
+ // ─────────────────────────────────────────────────────────────
408
+
409
+ async function main(): Promise<void> {
410
+ await mainWithDeps(defaultDeps);
411
+ }
412
+
62
413
  main().catch(() => process.exit(0));