@neotx/core 0.1.0-alpha.15 → 0.1.0-alpha.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -244,11 +244,94 @@ declare const neoConfigSchema: z.ZodObject<{
244
244
  ttlMs: z.ZodDefault<z.ZodNumber>;
245
245
  }, z.core.$strip>>;
246
246
  }, z.core.$strip>;
247
+ declare const repoOverrideConfigSchema: z.ZodObject<{
248
+ concurrency: z.ZodOptional<z.ZodOptional<z.ZodObject<{
249
+ maxSessions: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
250
+ maxPerRepo: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
251
+ queueMax: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
252
+ }, z.core.$strip>>>;
253
+ budget: z.ZodOptional<z.ZodOptional<z.ZodObject<{
254
+ dailyCapUsd: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
255
+ alertThresholdPct: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
256
+ }, z.core.$strip>>>;
257
+ recovery: z.ZodOptional<z.ZodOptional<z.ZodObject<{
258
+ maxRetries: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
259
+ backoffBaseMs: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
260
+ }, z.core.$strip>>>;
261
+ sessions: z.ZodOptional<z.ZodOptional<z.ZodObject<{
262
+ initTimeoutMs: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
263
+ maxDurationMs: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
264
+ dir: z.ZodOptional<z.ZodDefault<z.ZodString>>;
265
+ }, z.core.$strip>>>;
266
+ }, z.core.$strip>;
247
267
  type NeoConfig = z.infer<typeof neoConfigSchema>;
248
268
  type GlobalConfig = NeoConfig;
249
269
  type RepoConfig = z.infer<typeof repoConfigSchema>;
250
270
  type RepoConfigInput = z.input<typeof repoConfigSchema>;
251
271
  type McpServerConfig = z.infer<typeof mcpServerConfigSchema>;
272
+
273
+ /**
274
+ * Configuration store that loads and merges config files.
275
+ *
276
+ * Config precedence (highest to lowest):
277
+ * 1. Repo config: <repoPath>/.neo/config.yml
278
+ * 2. Global config: ~/.neo/config.yml
279
+ * 3. Default values (hardcoded)
280
+ *
281
+ * @example
282
+ * const store = new ConfigStore('/path/to/repo');
283
+ * await store.load();
284
+ * const dailyCap = store.get<number>('budget.dailyCapUsd');
285
+ */
286
+ declare class ConfigStore {
287
+ private repoPath;
288
+ private config;
289
+ constructor(repoPath?: string);
290
+ /**
291
+ * Loads and merges config files from all locations.
292
+ * Must be called before get() or getAll().
293
+ */
294
+ load(): Promise<void>;
295
+ /**
296
+ * Gets a config value using dot notation.
297
+ *
298
+ * @param key - Dot-separated path (e.g., "budget.dailyCapUsd")
299
+ * @returns The value at the path
300
+ * @throws Error if load() has not been called
301
+ *
302
+ * @example
303
+ * store.get<number>('budget.dailyCapUsd') // 500
304
+ * store.get<string>('sessions.dir') // '/tmp/neo-sessions'
305
+ */
306
+ get<T>(key: string): T;
307
+ /**
308
+ * Returns the full merged configuration.
309
+ *
310
+ * @throws Error if load() has not been called
311
+ */
312
+ getAll(): NeoConfig;
313
+ /**
314
+ * Returns the repository path, if one was provided.
315
+ * Used by ConfigWatcher to determine which files to watch.
316
+ */
317
+ getRepoPath(): string | undefined;
318
+ /**
319
+ * Loads global config from ~/.neo/config.yml
320
+ */
321
+ private loadGlobalConfig;
322
+ /**
323
+ * Loads repo-level overrides from <repoPath>/.neo/config.yml
324
+ */
325
+ private loadRepoConfig;
326
+ /**
327
+ * Loads and parses a YAML config file.
328
+ *
329
+ * @param filePath - Absolute path to the config file
330
+ * @returns Parsed YAML content or null if file doesn't exist
331
+ */
332
+ private loadFile;
333
+ }
334
+
252
335
  /**
253
336
  * Load NeoConfig from a single file (legacy compatibility).
254
337
  */
@@ -285,35 +368,10 @@ interface ResolvedAgent {
285
368
  maxTurns?: number | undefined;
286
369
  source: "built-in" | "custom" | "extended";
287
370
  }
288
- interface WorkflowDefinition {
289
- name: string;
290
- description?: string | undefined;
291
- steps: Record<string, WorkflowStepDef | WorkflowGateDef>;
292
- }
293
- interface WorkflowStepDef {
294
- type?: "step" | undefined;
295
- agent: string;
296
- dependsOn?: string[] | undefined;
297
- prompt?: string | undefined;
298
- sandbox?: "writable" | "readonly" | undefined;
299
- maxTurns?: number | undefined;
300
- mcpServers?: string[] | undefined;
301
- recovery?: {
302
- maxRetries?: number | undefined;
303
- nonRetryable?: string[] | undefined;
304
- } | undefined;
305
- }
306
- interface WorkflowGateDef {
307
- type: "gate";
308
- dependsOn?: string[] | undefined;
309
- description: string;
310
- timeout?: string | undefined;
311
- autoApprove?: boolean | undefined;
312
- }
313
371
  interface PersistedRun {
314
372
  version: 1;
315
373
  runId: string;
316
- workflow: string;
374
+ agent: string;
317
375
  repo: string;
318
376
  prompt: string;
319
377
  branch?: string | undefined;
@@ -342,7 +400,7 @@ interface StepResult {
342
400
  }
343
401
  type Priority = "critical" | "high" | "medium" | "low";
344
402
  interface DispatchInput {
345
- workflow: string;
403
+ agent: string;
346
404
  repo: string;
347
405
  prompt: string;
348
406
  runId?: string | undefined;
@@ -361,7 +419,7 @@ interface DispatchInput {
361
419
  }
362
420
  interface TaskResult {
363
421
  runId: string;
364
- workflow: string;
422
+ agent: string;
365
423
  repo: string;
366
424
  status: "success" | "failure" | "timeout" | "cancelled";
367
425
  steps: Record<string, StepResult>;
@@ -377,7 +435,6 @@ interface TaskResult {
377
435
  interface ActiveSession {
378
436
  sessionId: string;
379
437
  runId: string;
380
- workflow: string;
381
438
  step: string;
382
439
  agent: string;
383
440
  repo: string;
@@ -394,9 +451,9 @@ interface OrchestratorStatus {
394
451
  budgetRemainingPct: number;
395
452
  uptime: number;
396
453
  }
397
- interface WorkflowContext {
454
+ interface RunContext {
398
455
  runId: string;
399
- workflow: string;
456
+ agent: string;
400
457
  repo: string;
401
458
  prompt: string;
402
459
  steps: Record<string, StepResult>;
@@ -406,7 +463,6 @@ interface SessionStartEvent {
406
463
  type: "session:start";
407
464
  sessionId: string;
408
465
  runId: string;
409
- workflow: string;
410
466
  step: string;
411
467
  agent: string;
412
468
  repo: string;
@@ -451,16 +507,16 @@ interface AgentMessageEvent {
451
507
  text: string;
452
508
  timestamp: string;
453
509
  }
454
- interface WorkflowStepStartEvent {
455
- type: "workflow:step_start";
510
+ interface StepStartEvent {
511
+ type: "step:start";
456
512
  runId: string;
457
513
  step: string;
458
514
  agent: string;
459
515
  metadata?: Record<string, unknown> | undefined;
460
516
  timestamp: string;
461
517
  }
462
- interface WorkflowStepCompleteEvent {
463
- type: "workflow:step_complete";
518
+ interface StepCompleteEvent {
519
+ type: "step:complete";
464
520
  runId: string;
465
521
  step: string;
466
522
  status: "success" | "failure" | "skipped";
@@ -474,7 +530,7 @@ interface GateWaitingEvent {
474
530
  runId: string;
475
531
  gate: string;
476
532
  description: string;
477
- context: WorkflowContext;
533
+ context: RunContext;
478
534
  approve: () => void;
479
535
  reject: (reason: string) => void;
480
536
  metadata?: Record<string, unknown> | undefined;
@@ -513,7 +569,7 @@ interface OrchestratorShutdownEvent {
513
569
  type: "orchestrator:shutdown";
514
570
  timestamp: string;
515
571
  }
516
- type NeoEvent = SessionStartEvent | SessionCompleteEvent | SessionFailEvent | AgentToolUseEvent | AgentMessageEvent | WorkflowStepStartEvent | WorkflowStepCompleteEvent | GateWaitingEvent | CostUpdateEvent | BudgetAlertEvent | QueueEnqueueEvent | QueueDequeueEvent | OrchestratorShutdownEvent;
572
+ type NeoEvent = SessionStartEvent | SessionCompleteEvent | SessionFailEvent | AgentToolUseEvent | AgentMessageEvent | StepStartEvent | StepCompleteEvent | GateWaitingEvent | CostUpdateEvent | BudgetAlertEvent | QueueEnqueueEvent | QueueDequeueEvent | OrchestratorShutdownEvent;
517
573
  type HookEvent = "PreToolUse" | "PostToolUse" | "Notification";
518
574
  interface Middleware {
519
575
  name: string;
@@ -538,7 +594,6 @@ interface MiddlewareContextMap {
538
594
  }
539
595
  interface MiddlewareContext {
540
596
  runId: string;
541
- workflow: string;
542
597
  step: string;
543
598
  agent: string;
544
599
  repo: string;
@@ -557,7 +612,6 @@ type MiddlewareResult = {
557
612
  interface CostEntry {
558
613
  timestamp: string;
559
614
  runId: string;
560
- workflow: string;
561
615
  step: string;
562
616
  sessionId: string;
563
617
  agent: string;
@@ -833,8 +887,6 @@ declare function loopDetection(options: {
833
887
  interface OrchestratorOptions {
834
888
  middleware?: Middleware[] | undefined;
835
889
  journalDir?: string | undefined;
836
- builtInWorkflowDir?: string | undefined;
837
- customWorkflowDir?: string | undefined;
838
890
  /** Skip orphan recovery on start — workers should set this to true to avoid false orphan detection on concurrent launches. */
839
891
  skipOrphanRecovery?: boolean | undefined;
840
892
  }
@@ -842,7 +894,6 @@ declare class Orchestrator extends NeoEventEmitter {
842
894
  private readonly config;
843
895
  private readonly semaphore;
844
896
  private readonly userMiddleware;
845
- private readonly workflows;
846
897
  private readonly registeredAgents;
847
898
  private readonly _activeSessions;
848
899
  private readonly idempotencyCache;
@@ -850,8 +901,6 @@ declare class Orchestrator extends NeoEventEmitter {
850
901
  private readonly repoIndex;
851
902
  private readonly runStore;
852
903
  private readonly journalDir;
853
- private readonly builtInWorkflowDir;
854
- private readonly customWorkflowDir;
855
904
  private costJournal;
856
905
  private eventJournal;
857
906
  private webhookDispatcher;
@@ -862,7 +911,6 @@ declare class Orchestrator extends NeoEventEmitter {
862
911
  private _drainResolve;
863
912
  private readonly skipOrphanRecovery;
864
913
  constructor(config: NeoConfig, options?: OrchestratorOptions);
865
- registerWorkflow(definition: WorkflowDefinition): void;
866
914
  registerAgent(agent: ResolvedAgent): void;
867
915
  dispatch(input: DispatchInput): Promise<TaskResult>;
868
916
  pause(): void;
@@ -906,8 +954,6 @@ declare class Orchestrator extends NeoEventEmitter {
906
954
  private validateInput;
907
955
  private evictExpiredIdempotencyEntries;
908
956
  private computeIdempotencyKey;
909
- private getFirstStep;
910
- private resolveStepAgent;
911
957
  private resolveRepo;
912
958
  private computeBudgetRemainingPct;
913
959
  private resolveMcpServers;
@@ -960,6 +1006,7 @@ declare function getSupervisorActivityPath(name: string): string;
960
1006
  declare function getSupervisorInboxPath(name: string): string;
961
1007
  declare function getSupervisorEventsPath(name: string): string;
962
1008
  declare function getSupervisorLockPath(name: string): string;
1009
+ declare function getSupervisorDecisionsPath(name: string): string;
963
1010
 
964
1011
  interface ParsedOutput {
965
1012
  rawOutput: string;
@@ -1036,11 +1083,15 @@ interface RecoveryOptions extends SessionOptions {
1036
1083
  */
1037
1084
  declare function runWithRecovery(options: RecoveryOptions): Promise<SessionResult>;
1038
1085
 
1086
+ declare function loadRepoInstructions(repoPath: string): Promise<string | undefined>;
1087
+ declare function buildGitStrategyInstructions(strategy: GitStrategy, agent: ResolvedAgent, branch: string, baseBranch: string, remote: string, metadata?: Record<string, unknown>): string | null;
1088
+ declare function buildReportingInstructions(_runId: string): string;
1089
+ declare function buildFullPrompt(agentPrompt: string | undefined, repoInstructions: string | undefined, gitInstructions: string | null, taskPrompt: string, memoryContext?: string | undefined, cwdInstructions?: string | undefined, reportingInstructions?: string | undefined): string;
1090
+
1039
1091
  interface SessionExecutionInput {
1040
1092
  runId: string;
1041
1093
  sessionId: string;
1042
1094
  agent: ResolvedAgent;
1043
- stepDef: WorkflowStepDef;
1044
1095
  repoConfig: RepoConfig;
1045
1096
  repoPath: string;
1046
1097
  prompt: string;
@@ -1065,10 +1116,6 @@ interface SessionExecutionDeps {
1065
1116
  interface SessionExecutionResult extends StepResult {
1066
1117
  parsed: ParsedOutput;
1067
1118
  }
1068
- declare function loadRepoInstructions(repoPath: string): Promise<string | undefined>;
1069
- declare function buildGitStrategyInstructions(strategy: GitStrategy, agent: ResolvedAgent, branch: string, baseBranch: string, remote: string, metadata?: Record<string, unknown>): string | null;
1070
- declare function buildReportingInstructions(_runId: string): string;
1071
- declare function buildFullPrompt(agentPrompt: string | undefined, repoInstructions: string | undefined, gitInstructions: string | null, taskPrompt: string, memoryContext?: string | undefined, cwdInstructions?: string | undefined, reportingInstructions?: string | undefined): string;
1072
1119
  /**
1073
1120
  * Encapsulates session execution logic: prompt building, SDK calls, and response processing.
1074
1121
  * Extracted from Orchestrator for better testability and separation of concerns.
@@ -1108,12 +1155,81 @@ declare class SessionExecutor {
1108
1155
  */
1109
1156
  declare function isProcessAlive(pid: number): boolean;
1110
1157
 
1158
+ declare const decisionOptionSchema: z.ZodObject<{
1159
+ key: z.ZodString;
1160
+ label: z.ZodString;
1161
+ description: z.ZodOptional<z.ZodString>;
1162
+ }, z.core.$strip>;
1163
+ declare const decisionSchema: z.ZodObject<{
1164
+ id: z.ZodString;
1165
+ question: z.ZodString;
1166
+ context: z.ZodOptional<z.ZodString>;
1167
+ options: z.ZodOptional<z.ZodArray<z.ZodObject<{
1168
+ key: z.ZodString;
1169
+ label: z.ZodString;
1170
+ description: z.ZodOptional<z.ZodString>;
1171
+ }, z.core.$strip>>>;
1172
+ type: z.ZodDefault<z.ZodString>;
1173
+ source: z.ZodString;
1174
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
1175
+ createdAt: z.ZodString;
1176
+ expiresAt: z.ZodOptional<z.ZodString>;
1177
+ defaultAnswer: z.ZodOptional<z.ZodString>;
1178
+ answeredAt: z.ZodOptional<z.ZodString>;
1179
+ answer: z.ZodOptional<z.ZodString>;
1180
+ expiredAt: z.ZodOptional<z.ZodString>;
1181
+ }, z.core.$strip>;
1182
+ type DecisionOption = z.infer<typeof decisionOptionSchema>;
1183
+ type Decision = z.infer<typeof decisionSchema>;
1184
+ type DecisionInput = Omit<Decision, "id" | "createdAt" | "answeredAt" | "answer" | "expiredAt">;
1185
+ /**
1186
+ * JSONL-backed store for decisions.
1187
+ * Append-only with in-place updates for answers and expiration.
1188
+ */
1189
+ declare class DecisionStore {
1190
+ private readonly filePath;
1191
+ private readonly dir;
1192
+ private readonly dirCache;
1193
+ constructor(filePath: string);
1194
+ /**
1195
+ * Create a new decision and persist it.
1196
+ * @returns The generated decision ID
1197
+ */
1198
+ create(input: DecisionInput): Promise<string>;
1199
+ /**
1200
+ * Answer a decision by ID.
1201
+ * Reads all entries, updates the matching one, and rewrites the file.
1202
+ */
1203
+ answer(id: string, answer: string): Promise<void>;
1204
+ /**
1205
+ * Get all pending decisions (unanswered, not expired, not timed out).
1206
+ */
1207
+ pending(): Promise<Decision[]>;
1208
+ /**
1209
+ * Get answered decisions, optionally filtered by timestamp.
1210
+ * @param since - ISO timestamp to filter decisions answered after this time
1211
+ */
1212
+ answered(since?: string): Promise<Decision[]>;
1213
+ /**
1214
+ * Get a specific decision by ID.
1215
+ */
1216
+ get(id: string): Promise<Decision | null>;
1217
+ /**
1218
+ * Auto-answer expired decisions with their defaultAnswer.
1219
+ * Decisions without defaultAnswer are marked as expired (expiredAt).
1220
+ * @returns The decisions that were auto-answered or marked expired
1221
+ */
1222
+ expire(): Promise<Decision[]>;
1223
+ private readAll;
1224
+ private writeAll;
1225
+ }
1226
+
1111
1227
  declare const supervisorDaemonStateSchema: z.ZodObject<{
1112
1228
  pid: z.ZodNumber;
1113
1229
  sessionId: z.ZodString;
1230
+ startedAt: z.ZodString;
1114
1231
  port: z.ZodNumber;
1115
1232
  cwd: z.ZodString;
1116
- startedAt: z.ZodString;
1117
1233
  lastHeartbeat: z.ZodOptional<z.ZodString>;
1118
1234
  heartbeatCount: z.ZodDefault<z.ZodNumber>;
1119
1235
  totalCostUsd: z.ZodDefault<z.ZodNumber>;
@@ -1169,6 +1285,7 @@ declare const activityEntrySchema: z.ZodObject<{
1169
1285
  event: "event";
1170
1286
  heartbeat: "heartbeat";
1171
1287
  action: "action";
1288
+ warning: "warning";
1172
1289
  thinking: "thinking";
1173
1290
  plan: "plan";
1174
1291
  dispatch: "dispatch";
@@ -1206,6 +1323,39 @@ declare const internalEventKindSchema: z.ZodEnum<{
1206
1323
  active_run_check: "active_run_check";
1207
1324
  }>;
1208
1325
  type InternalEventKind = z.infer<typeof internalEventKindSchema>;
1326
+ declare const supervisorStatusSchema: z.ZodObject<{
1327
+ pid: z.ZodNumber;
1328
+ sessionId: z.ZodString;
1329
+ startedAt: z.ZodString;
1330
+ heartbeatCount: z.ZodNumber;
1331
+ totalCostUsd: z.ZodNumber;
1332
+ todayCostUsd: z.ZodNumber;
1333
+ status: z.ZodEnum<{
1334
+ running: "running";
1335
+ idle: "idle";
1336
+ stopping: "stopping";
1337
+ }>;
1338
+ lastHeartbeat: z.ZodString;
1339
+ activeRunCount: z.ZodNumber;
1340
+ recentActivitySummary: z.ZodArray<z.ZodString>;
1341
+ }, z.core.$strip>;
1342
+ type SupervisorStatus = z.infer<typeof supervisorStatusSchema>;
1343
+ declare const activityQueryOptionsSchema: z.ZodObject<{
1344
+ limit: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
1345
+ offset: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
1346
+ type: z.ZodOptional<z.ZodEnum<{
1347
+ error: "error";
1348
+ message: "message";
1349
+ decision: "decision";
1350
+ event: "event";
1351
+ action: "action";
1352
+ plan: "plan";
1353
+ dispatch: "dispatch";
1354
+ }>>;
1355
+ since: z.ZodOptional<z.ZodString>;
1356
+ until: z.ZodOptional<z.ZodString>;
1357
+ }, z.core.$strip>;
1358
+ type ActivityQueryOptions = z.infer<typeof activityQueryOptionsSchema>;
1209
1359
  type QueuedEvent = {
1210
1360
  kind: "webhook";
1211
1361
  data: WebhookIncomingEvent;
@@ -1261,6 +1411,7 @@ declare class SupervisorDaemon {
1261
1411
  private eventQueue;
1262
1412
  private heartbeatLoop;
1263
1413
  private activityLog;
1414
+ private decisionStore;
1264
1415
  private sessionId;
1265
1416
  constructor(options: SupervisorDaemonOptions);
1266
1417
  start(): Promise<void>;
@@ -1269,6 +1420,11 @@ declare class SupervisorDaemon {
1269
1420
  private readState;
1270
1421
  private writeState;
1271
1422
  private readLockPid;
1423
+ /**
1424
+ * Handle decision:answer webhook events.
1425
+ * Extracts decisionId and answer from the payload and records the answer.
1426
+ */
1427
+ private handleDecisionAnswer;
1272
1428
  }
1273
1429
 
1274
1430
  interface EventQueueOptions {
@@ -1354,6 +1510,55 @@ declare class EventQueue {
1354
1510
  private markInFile;
1355
1511
  }
1356
1512
 
1513
+ declare const supervisorWebhookEventSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
1514
+ type: z.ZodLiteral<"supervisor_started">;
1515
+ supervisorId: z.ZodString;
1516
+ startedAt: z.ZodString;
1517
+ }, z.core.$strip>, z.ZodObject<{
1518
+ type: z.ZodLiteral<"heartbeat">;
1519
+ supervisorId: z.ZodString;
1520
+ heartbeatNumber: z.ZodNumber;
1521
+ timestamp: z.ZodString;
1522
+ runsActive: z.ZodNumber;
1523
+ budget: z.ZodObject<{
1524
+ todayUsd: z.ZodNumber;
1525
+ limitUsd: z.ZodNumber;
1526
+ }, z.core.$strip>;
1527
+ }, z.core.$strip>, z.ZodObject<{
1528
+ type: z.ZodLiteral<"run_dispatched">;
1529
+ supervisorId: z.ZodString;
1530
+ runId: z.ZodString;
1531
+ agent: z.ZodString;
1532
+ repo: z.ZodString;
1533
+ branch: z.ZodString;
1534
+ prompt: z.ZodString;
1535
+ }, z.core.$strip>, z.ZodObject<{
1536
+ type: z.ZodLiteral<"run_completed">;
1537
+ supervisorId: z.ZodString;
1538
+ runId: z.ZodString;
1539
+ status: z.ZodEnum<{
1540
+ completed: "completed";
1541
+ failed: "failed";
1542
+ cancelled: "cancelled";
1543
+ }>;
1544
+ output: z.ZodOptional<z.ZodString>;
1545
+ costUsd: z.ZodNumber;
1546
+ durationMs: z.ZodNumber;
1547
+ }, z.core.$strip>, z.ZodObject<{
1548
+ type: z.ZodLiteral<"supervisor_stopped">;
1549
+ supervisorId: z.ZodString;
1550
+ stoppedAt: z.ZodString;
1551
+ reason: z.ZodEnum<{
1552
+ error: "error";
1553
+ budget_exceeded: "budget_exceeded";
1554
+ shutdown: "shutdown";
1555
+ manual: "manual";
1556
+ }>;
1557
+ }, z.core.$strip>], "type">;
1558
+ type SupervisorWebhookEvent = z.infer<typeof supervisorWebhookEventSchema>;
1559
+
1560
+ /** Callback for emitting webhook events */
1561
+ type WebhookEventEmitter = (event: SupervisorWebhookEvent) => void | Promise<void>;
1357
1562
  interface HeartbeatLoopOptions {
1358
1563
  config: GlobalConfig;
1359
1564
  supervisorDir: string;
@@ -1366,6 +1571,12 @@ interface HeartbeatLoopOptions {
1366
1571
  /** Path to bundled default SUPERVISOR.md (e.g. from @neotx/agents) */
1367
1572
  defaultInstructionsPath?: string | undefined;
1368
1573
  memoryDbPath?: string | undefined;
1574
+ /** Optional callback to emit webhook events at lifecycle points */
1575
+ onWebhookEvent?: WebhookEventEmitter | undefined;
1576
+ /** Repository path for config watching (enables hot-reload) */
1577
+ repoPath?: string | undefined;
1578
+ /** Debounce time in ms for config file changes (default: 500) */
1579
+ configWatcherDebounceMs?: number | undefined;
1369
1580
  }
1370
1581
  /**
1371
1582
  * The core autonomous loop. At each iteration:
@@ -1382,7 +1593,7 @@ declare class HeartbeatLoop {
1382
1593
  private stopping;
1383
1594
  private consecutiveFailures;
1384
1595
  private activeAbort;
1385
- private readonly config;
1596
+ private config;
1386
1597
  private readonly supervisorDir;
1387
1598
  private readonly statePath;
1388
1599
  private sessionId;
@@ -1393,12 +1604,30 @@ declare class HeartbeatLoop {
1393
1604
  private readonly defaultInstructionsPath;
1394
1605
  private memoryStore;
1395
1606
  private readonly memoryDbPath;
1607
+ private readonly onWebhookEvent;
1608
+ private decisionStore;
1609
+ /** ConfigWatcher for hot-reload support */
1610
+ private configWatcher;
1611
+ private configStore;
1612
+ private readonly repoPath;
1613
+ private readonly configWatcherDebounceMs;
1396
1614
  constructor(options: HeartbeatLoopOptions);
1397
1615
  /** Path to the inbox/events directory for markProcessed() calls */
1398
1616
  get eventsPath(): string;
1399
1617
  private getMemoryStore;
1618
+ private getDecisionStore;
1400
1619
  start(): Promise<void>;
1401
1620
  stop(): void;
1621
+ /**
1622
+ * Initialize and start the ConfigWatcher for hot-reload support.
1623
+ * Subscribes to config file changes and logs reload events.
1624
+ */
1625
+ private initConfigWatcher;
1626
+ /**
1627
+ * Handle config file changes. Propagates reloaded config to the running
1628
+ * loop and triggers an immediate heartbeat.
1629
+ */
1630
+ private handleConfigChange;
1402
1631
  private runHeartbeat;
1403
1632
  /**
1404
1633
  * Check if supervisor daily budget is exceeded.
@@ -1422,11 +1651,21 @@ declare class HeartbeatLoop {
1422
1651
  private buildHeartbeatModePrompt;
1423
1652
  /**
1424
1653
  * Call the Claude SDK and stream results.
1654
+ *
1655
+ * Uses Promise.race to enable non-blocking abort detection. The standard
1656
+ * `for await (const message of stream)` pattern only checks the abort signal
1657
+ * AFTER each yield — if the SDK hangs (no messages), the abort never executes.
1658
+ * This implementation races each iterator.next() against an abort promise,
1659
+ * allowing immediate response to shutdown/timeout signals.
1425
1660
  */
1426
1661
  private callSdk;
1427
1662
  private readState;
1428
1663
  private updateState;
1429
- /** Read persisted run files and return summaries of active (running/paused) runs. */
1664
+ /**
1665
+ * Read persisted run files and return summaries of active (running/paused) runs.
1666
+ * Validates that "running" runs are actually alive by checking their PID.
1667
+ * Stale runs (dead PID past grace period) are filtered out to prevent ghost runs.
1668
+ */
1430
1669
  private getActiveRuns;
1431
1670
  /**
1432
1671
  * Load custom instructions from SUPERVISOR.md.
@@ -1445,6 +1684,31 @@ declare class HeartbeatLoop {
1445
1684
  /** Detect agent dispatches from bash tool results. */
1446
1685
  private logToolResult;
1447
1686
  private sleep;
1687
+ /**
1688
+ * Process decision:answer events from inbox messages.
1689
+ * Expected format: "decision:answer <decisionId> <answer>"
1690
+ */
1691
+ private processDecisionAnswers;
1692
+ /**
1693
+ * Read persisted run data to extract actual status, cost, and duration.
1694
+ * Returns null if the run file cannot be found or parsed.
1695
+ */
1696
+ private readPersistedRun;
1697
+ /**
1698
+ * Emit a webhook event if a callback is configured.
1699
+ * Validates the event against its schema before emission.
1700
+ */
1701
+ private emitWebhookEvent;
1702
+ /** Emit SupervisorStartedEvent */
1703
+ private emitSupervisorStarted;
1704
+ /** Emit SupervisorStoppedEvent */
1705
+ private emitSupervisorStopped;
1706
+ /** Emit HeartbeatEvent */
1707
+ private emitHeartbeatCompleted;
1708
+ /** Emit RunDispatchedEvent from tool result detection */
1709
+ private emitRunDispatched;
1710
+ /** Emit RunCompletedEvent when processing run_complete events */
1711
+ private emitRunCompleted;
1448
1712
  }
1449
1713
 
1450
1714
  /**
@@ -1558,6 +1822,27 @@ declare class MemoryStore {
1558
1822
  close(): void;
1559
1823
  }
1560
1824
 
1825
+ /**
1826
+ * Reads supervisor status from the daemon state file.
1827
+ * Returns null if the supervisor is not running or state file doesn't exist.
1828
+ */
1829
+ declare class StatusReader {
1830
+ readonly dataDir: string;
1831
+ private readonly statePath;
1832
+ private readonly activityPath;
1833
+ constructor(dataDir: string);
1834
+ /**
1835
+ * Read and parse supervisor status from disk.
1836
+ * Returns null if the state file doesn't exist (supervisor not running).
1837
+ */
1838
+ getStatus(): Promise<SupervisorStatus | null>;
1839
+ /**
1840
+ * Query activity entries with optional filtering.
1841
+ * Returns empty array if the activity file doesn't exist or is empty.
1842
+ */
1843
+ queryActivity(options?: ActivityQueryOptions): ActivityEntry[];
1844
+ }
1845
+
1561
1846
  interface WebhookServerOptions {
1562
1847
  port: number;
1563
1848
  secret?: string | undefined;
@@ -1590,49 +1875,49 @@ declare class WebhookServer {
1590
1875
  private sendJson;
1591
1876
  }
1592
1877
 
1593
- declare const workflowStepDefSchema: z.ZodObject<{
1594
- type: z.ZodDefault<z.ZodOptional<z.ZodLiteral<"step">>>;
1595
- agent: z.ZodString;
1596
- dependsOn: z.ZodOptional<z.ZodArray<z.ZodString>>;
1597
- prompt: z.ZodOptional<z.ZodString>;
1598
- sandbox: z.ZodOptional<z.ZodEnum<{
1599
- readonly: "readonly";
1600
- writable: "writable";
1601
- }>>;
1602
- maxTurns: z.ZodOptional<z.ZodNumber>;
1603
- mcpServers: z.ZodOptional<z.ZodArray<z.ZodString>>;
1604
- recovery: z.ZodOptional<z.ZodObject<{
1605
- maxRetries: z.ZodOptional<z.ZodNumber>;
1606
- nonRetryable: z.ZodOptional<z.ZodArray<z.ZodString>>;
1607
- }, z.core.$strip>>;
1608
- condition: z.ZodOptional<z.ZodString>;
1609
- }, z.core.$strip>;
1610
- declare const workflowGateDefSchema: z.ZodObject<{
1611
- type: z.ZodLiteral<"gate">;
1612
- dependsOn: z.ZodOptional<z.ZodArray<z.ZodString>>;
1613
- description: z.ZodString;
1614
- timeout: z.ZodOptional<z.ZodString>;
1615
- autoApprove: z.ZodOptional<z.ZodBoolean>;
1878
+ declare const webhookEntrySchema: z.ZodObject<{
1879
+ url: z.ZodString;
1880
+ events: z.ZodOptional<z.ZodArray<z.ZodString>>;
1881
+ secret: z.ZodOptional<z.ZodString>;
1882
+ timeoutMs: z.ZodDefault<z.ZodNumber>;
1883
+ createdAt: z.ZodDefault<z.ZodString>;
1616
1884
  }, z.core.$strip>;
1617
- declare function loadWorkflow(filePath: string): Promise<WorkflowDefinition>;
1618
-
1885
+ type WebhookEntry = z.infer<typeof webhookEntrySchema>;
1886
+ type WebhookEntryInput = z.input<typeof webhookEntrySchema>;
1619
1887
  /**
1620
- * Registry for workflow definitions.
1621
- * Loads built-in workflows from a directory and optional custom workflows.
1622
- * Custom workflows with the same name override built-in ones.
1888
+ * Add a webhook endpoint to ~/.neo/webhooks.json.
1889
+ * Deduplicates by URL.
1623
1890
  */
1624
- declare class WorkflowRegistry {
1625
- private readonly builtInDir;
1626
- private readonly customDir;
1627
- private readonly workflows;
1628
- constructor(builtInDir: string, customDir?: string);
1629
- load(): Promise<void>;
1630
- get(name: string): WorkflowDefinition | undefined;
1631
- list(): WorkflowDefinition[];
1632
- has(name: string): boolean;
1633
- private loadFromDir;
1891
+ declare function addWebhook(input: WebhookEntryInput): Promise<WebhookEntry>;
1892
+ /**
1893
+ * Remove a webhook endpoint by URL.
1894
+ * Returns true if removed, false if not found.
1895
+ */
1896
+ declare function removeWebhook(url: string): Promise<boolean>;
1897
+ /**
1898
+ * List all configured webhooks.
1899
+ */
1900
+ declare function listWebhooks(): Promise<WebhookEntry[]>;
1901
+ interface WebhookTestPayload {
1902
+ type: "test";
1903
+ timestamp: string;
1904
+ runId: string;
1905
+ status: "test";
1906
+ summary: string;
1907
+ }
1908
+ interface WebhookTestResult {
1909
+ url: string;
1910
+ success: boolean;
1911
+ statusCode?: number;
1912
+ error?: string;
1913
+ durationMs: number;
1634
1914
  }
1915
+ /**
1916
+ * Send a test payload to all configured webhooks.
1917
+ * Returns results for each endpoint.
1918
+ */
1919
+ declare function testWebhooks(): Promise<WebhookTestResult[]>;
1635
1920
 
1636
1921
  declare const VERSION = "0.1.0";
1637
1922
 
1638
- export { type ActiveSession, type ActivityEntry, ActivityLog, type AgentConfig, type AgentDefinition, type AgentMessageEvent, type AgentModel, AgentRegistry, type AgentTool, type AgentToolEntry, type AgentToolUseEvent, type AuditLogMiddleware, type BudgetAlertEvent, type CostEntry, CostJournal, type CostUpdateEvent, type DispatchInput, type Embedder, EventJournal, EventQueue, type GateWaitingEvent, type GitStrategy, type GlobalConfig, HeartbeatLoop, type HeartbeatLoopOptions, type HookEvent, type InboxMessage, LocalEmbedder, type LoopDetectionMiddleware, type McpServerConfig, type MemoryEntry, type MemoryQuery, type MemoryStats, MemoryStore, type MemoryType, type MemoryWriteInput, type Middleware, type MiddlewareChain, type MiddlewareContext, type MiddlewareContextMap, type MiddlewareEvent, type MiddlewareHandler, type MiddlewareResult, type NeoConfig, type NeoEvent, NeoEventEmitter, Orchestrator, type OrchestratorOptions, type OrchestratorShutdownEvent, type OrchestratorStatus, type ParsedOutput, type PersistedRun, type Priority, type QueueDequeueEvent, type QueueEnqueueEvent, type QueuedEvent, type RecoveryOptions, type RepoConfig, type RepoConfigInput, type ResolvedAgent, type SDKHooks, type SandboxConfig, Semaphore, type SemaphoreCallbacks, type SemaphoreConfig, type SessionCloneInfo, type SessionCompleteEvent, SessionError, type SessionEvent, type SessionExecutionConfig, type SessionExecutionDeps, type SessionExecutionInput, type SessionExecutionResult, SessionExecutor, type SessionFailEvent, type SessionOptions, type SessionResult, type SessionStartEvent, type StepResult, SupervisorDaemon, type SupervisorDaemonOptions, type SupervisorDaemonState, type SupervisorDaemonState as SupervisorState, type TaskResult, VERSION, WebhookDispatcher, type WebhookIncomingEvent, WebhookServer, type WorkflowContext, type WorkflowDefinition, type WorkflowGateDef, WorkflowRegistry, type WorkflowStepCompleteEvent, type WorkflowStepDef, type WorkflowStepStartEvent, activityEntrySchema, addRepoToGlobalConfig, agentConfigSchema, agentModelSchema, agentSandboxSchema, agentToolEntrySchema, agentToolSchema, appendLogBuffer, auditLog, budgetGuard, buildFullPrompt, buildGitStrategyInstructions, buildMiddlewareChain, buildReportingInstructions, buildSDKHooks, buildSandboxConfig, createBranch, createSessionClone, deleteBranch, fetchRemote, getBranchName, getCurrentBranch, getDataDir, getJournalsDir, getRepoRunsDir, getRunDispatchPath, getRunLogPath, getRunsDir, getSupervisorActivityPath, getSupervisorDir, getSupervisorEventsPath, getSupervisorInboxPath, getSupervisorLockPath, getSupervisorStatePath, getSupervisorsDir, globalConfigSchema, inboxMessageSchema, isProcessAlive, listReposFromGlobalConfig, listSessionClones, loadAgentFile, loadConfig, loadGlobalConfig, loadRepoInstructions, loadWorkflow, loopDetection, matchesFilter, mcpServerConfigSchema, neoConfigSchema, parseOutput, pushBranch, pushSessionBranch, removeRepoFromGlobalConfig, removeSessionClone, repoConfigSchema, resolveAgent, runSession, runWithRecovery, supervisorDaemonStateSchema, supervisorDaemonStateSchema as supervisorStateSchema, toRepoSlug, webhookIncomingEventSchema, workflowGateDefSchema, workflowStepDefSchema };
1923
+ export { type ActiveSession, type ActivityEntry, ActivityLog, type ActivityQueryOptions, type AgentConfig, type AgentDefinition, type AgentMessageEvent, type AgentModel, AgentRegistry, type AgentTool, type AgentToolEntry, type AgentToolUseEvent, type AuditLogMiddleware, type BudgetAlertEvent, ConfigStore, type CostEntry, CostJournal, type CostUpdateEvent, type Decision, type DecisionInput, type DecisionOption, DecisionStore, type DispatchInput, type Embedder, EventJournal, EventQueue, type GateWaitingEvent, type GitStrategy, type GlobalConfig, HeartbeatLoop, type HeartbeatLoopOptions, type HookEvent, type InboxMessage, LocalEmbedder, type LoopDetectionMiddleware, type McpServerConfig, type MemoryEntry, type MemoryQuery, type MemoryStats, MemoryStore, type MemoryType, type MemoryWriteInput, type Middleware, type MiddlewareChain, type MiddlewareContext, type MiddlewareContextMap, type MiddlewareEvent, type MiddlewareHandler, type MiddlewareResult, type NeoConfig, type NeoEvent, NeoEventEmitter, Orchestrator, type OrchestratorOptions, type OrchestratorShutdownEvent, type OrchestratorStatus, type ParsedOutput, type PersistedRun, type Priority, type QueueDequeueEvent, type QueueEnqueueEvent, type QueuedEvent, type RecoveryOptions, type RepoConfig, type RepoConfigInput, type ResolvedAgent, type RunContext, type SDKHooks, type SandboxConfig, Semaphore, type SemaphoreCallbacks, type SemaphoreConfig, type SessionCloneInfo, type SessionCompleteEvent, SessionError, type SessionEvent, type SessionExecutionConfig, type SessionExecutionDeps, type SessionExecutionInput, type SessionExecutionResult, SessionExecutor, type SessionFailEvent, type SessionOptions, type SessionResult, type SessionStartEvent, StatusReader, type StepCompleteEvent, type StepResult, type StepStartEvent, SupervisorDaemon, type SupervisorDaemonOptions, type SupervisorDaemonState, type SupervisorDaemonState as SupervisorState, type SupervisorStatus, type TaskResult, VERSION, WebhookDispatcher, type WebhookEntry, type WebhookEntryInput, type WebhookIncomingEvent, WebhookServer, type WebhookTestPayload, type WebhookTestResult, activityEntrySchema, addRepoToGlobalConfig, addWebhook, agentConfigSchema, agentModelSchema, agentSandboxSchema, agentToolEntrySchema, agentToolSchema, appendLogBuffer, auditLog, budgetGuard, buildFullPrompt, buildGitStrategyInstructions, buildMiddlewareChain, buildReportingInstructions, buildSDKHooks, buildSandboxConfig, createBranch, createSessionClone, decisionOptionSchema, decisionSchema, deleteBranch, fetchRemote, getBranchName, getCurrentBranch, getDataDir, getJournalsDir, getRepoRunsDir, getRunDispatchPath, getRunLogPath, getRunsDir, getSupervisorActivityPath, getSupervisorDecisionsPath, getSupervisorDir, getSupervisorEventsPath, getSupervisorInboxPath, getSupervisorLockPath, getSupervisorStatePath, getSupervisorsDir, globalConfigSchema, inboxMessageSchema, isProcessAlive, listReposFromGlobalConfig, listSessionClones, listWebhooks, loadAgentFile, loadConfig, loadGlobalConfig, loadRepoInstructions, loopDetection, matchesFilter, mcpServerConfigSchema, neoConfigSchema, parseOutput, pushBranch, pushSessionBranch, removeRepoFromGlobalConfig, removeSessionClone, removeWebhook, repoConfigSchema, repoOverrideConfigSchema, resolveAgent, runSession, runWithRecovery, supervisorDaemonStateSchema, supervisorDaemonStateSchema as supervisorStateSchema, supervisorStatusSchema, testWebhooks, toRepoSlug, webhookEntrySchema, webhookIncomingEventSchema };