@neotx/core 0.1.0-alpha.0 → 0.1.0-alpha.10

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
@@ -86,14 +86,21 @@ declare const mcpServerConfigSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
86
86
  args: z.ZodOptional<z.ZodArray<z.ZodString>>;
87
87
  env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
88
88
  }, z.core.$strip>], "type">;
89
+ declare const gitStrategySchema: z.ZodDefault<z.ZodEnum<{
90
+ pr: "pr";
91
+ branch: "branch";
92
+ }>>;
93
+ type GitStrategy = z.infer<typeof gitStrategySchema>;
89
94
  declare const repoConfigSchema: z.ZodObject<{
90
95
  path: z.ZodString;
91
96
  name: z.ZodOptional<z.ZodString>;
92
97
  defaultBranch: z.ZodDefault<z.ZodString>;
93
98
  branchPrefix: z.ZodDefault<z.ZodString>;
94
99
  pushRemote: z.ZodDefault<z.ZodString>;
95
- autoCreatePr: z.ZodDefault<z.ZodBoolean>;
96
- prBaseBranch: z.ZodOptional<z.ZodString>;
100
+ gitStrategy: z.ZodDefault<z.ZodEnum<{
101
+ pr: "pr";
102
+ branch: "branch";
103
+ }>>;
97
104
  }, z.core.$strip>;
98
105
  declare const globalConfigSchema: z.ZodObject<{
99
106
  repos: z.ZodDefault<z.ZodArray<z.ZodObject<{
@@ -102,8 +109,10 @@ declare const globalConfigSchema: z.ZodObject<{
102
109
  defaultBranch: z.ZodDefault<z.ZodString>;
103
110
  branchPrefix: z.ZodDefault<z.ZodString>;
104
111
  pushRemote: z.ZodDefault<z.ZodString>;
105
- autoCreatePr: z.ZodDefault<z.ZodBoolean>;
106
- prBaseBranch: z.ZodOptional<z.ZodString>;
112
+ gitStrategy: z.ZodDefault<z.ZodEnum<{
113
+ pr: "pr";
114
+ branch: "branch";
115
+ }>>;
107
116
  }, z.core.$strip>>>;
108
117
  concurrency: z.ZodDefault<z.ZodObject<{
109
118
  maxSessions: z.ZodDefault<z.ZodNumber>;
@@ -121,6 +130,7 @@ declare const globalConfigSchema: z.ZodObject<{
121
130
  sessions: z.ZodDefault<z.ZodObject<{
122
131
  initTimeoutMs: z.ZodDefault<z.ZodNumber>;
123
132
  maxDurationMs: z.ZodDefault<z.ZodNumber>;
133
+ dir: z.ZodDefault<z.ZodString>;
124
134
  }, z.core.$strip>>;
125
135
  webhooks: z.ZodDefault<z.ZodArray<z.ZodObject<{
126
136
  url: z.ZodString;
@@ -131,13 +141,18 @@ declare const globalConfigSchema: z.ZodObject<{
131
141
  supervisor: z.ZodDefault<z.ZodObject<{
132
142
  port: z.ZodDefault<z.ZodNumber>;
133
143
  secret: z.ZodOptional<z.ZodString>;
134
- idleIntervalMs: z.ZodDefault<z.ZodNumber>;
135
144
  heartbeatTimeoutMs: z.ZodDefault<z.ZodNumber>;
136
145
  maxConsecutiveFailures: z.ZodDefault<z.ZodNumber>;
137
146
  maxEventsPerSec: z.ZodDefault<z.ZodNumber>;
138
147
  dailyCapUsd: z.ZodDefault<z.ZodNumber>;
148
+ consolidationIntervalMs: z.ZodDefault<z.ZodNumber>;
149
+ compactionIntervalMs: z.ZodDefault<z.ZodNumber>;
150
+ eventTimeoutMs: z.ZodDefault<z.ZodNumber>;
139
151
  instructions: z.ZodOptional<z.ZodString>;
140
152
  }, z.core.$strip>>;
153
+ memory: z.ZodDefault<z.ZodObject<{
154
+ embeddings: z.ZodDefault<z.ZodBoolean>;
155
+ }, z.core.$strip>>;
141
156
  mcpServers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodDiscriminatedUnion<[z.ZodObject<{
142
157
  type: z.ZodLiteral<"http">;
143
158
  url: z.ZodString;
@@ -165,8 +180,10 @@ declare const neoConfigSchema: z.ZodObject<{
165
180
  defaultBranch: z.ZodDefault<z.ZodString>;
166
181
  branchPrefix: z.ZodDefault<z.ZodString>;
167
182
  pushRemote: z.ZodDefault<z.ZodString>;
168
- autoCreatePr: z.ZodDefault<z.ZodBoolean>;
169
- prBaseBranch: z.ZodOptional<z.ZodString>;
183
+ gitStrategy: z.ZodDefault<z.ZodEnum<{
184
+ pr: "pr";
185
+ branch: "branch";
186
+ }>>;
170
187
  }, z.core.$strip>>>;
171
188
  concurrency: z.ZodDefault<z.ZodObject<{
172
189
  maxSessions: z.ZodDefault<z.ZodNumber>;
@@ -184,6 +201,7 @@ declare const neoConfigSchema: z.ZodObject<{
184
201
  sessions: z.ZodDefault<z.ZodObject<{
185
202
  initTimeoutMs: z.ZodDefault<z.ZodNumber>;
186
203
  maxDurationMs: z.ZodDefault<z.ZodNumber>;
204
+ dir: z.ZodDefault<z.ZodString>;
187
205
  }, z.core.$strip>>;
188
206
  webhooks: z.ZodDefault<z.ZodArray<z.ZodObject<{
189
207
  url: z.ZodString;
@@ -194,13 +212,18 @@ declare const neoConfigSchema: z.ZodObject<{
194
212
  supervisor: z.ZodDefault<z.ZodObject<{
195
213
  port: z.ZodDefault<z.ZodNumber>;
196
214
  secret: z.ZodOptional<z.ZodString>;
197
- idleIntervalMs: z.ZodDefault<z.ZodNumber>;
198
215
  heartbeatTimeoutMs: z.ZodDefault<z.ZodNumber>;
199
216
  maxConsecutiveFailures: z.ZodDefault<z.ZodNumber>;
200
217
  maxEventsPerSec: z.ZodDefault<z.ZodNumber>;
201
218
  dailyCapUsd: z.ZodDefault<z.ZodNumber>;
219
+ consolidationIntervalMs: z.ZodDefault<z.ZodNumber>;
220
+ compactionIntervalMs: z.ZodDefault<z.ZodNumber>;
221
+ eventTimeoutMs: z.ZodDefault<z.ZodNumber>;
202
222
  instructions: z.ZodOptional<z.ZodString>;
203
223
  }, z.core.$strip>>;
224
+ memory: z.ZodDefault<z.ZodObject<{
225
+ embeddings: z.ZodDefault<z.ZodBoolean>;
226
+ }, z.core.$strip>>;
204
227
  mcpServers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodDiscriminatedUnion<[z.ZodObject<{
205
228
  type: z.ZodLiteral<"http">;
206
229
  url: z.ZodString;
@@ -253,6 +276,7 @@ interface AgentDefinition {
253
276
  prompt: string;
254
277
  tools: string[];
255
278
  model: string;
279
+ mcpServers?: string[] | undefined;
256
280
  }
257
281
  interface ResolvedAgent {
258
282
  name: string;
@@ -293,7 +317,7 @@ interface PersistedRun {
293
317
  repo: string;
294
318
  prompt: string;
295
319
  branch?: string | undefined;
296
- worktreePath?: string | undefined;
320
+ sessionPath?: string | undefined;
297
321
  pid?: number | undefined;
298
322
  status: "running" | "paused" | "completed" | "failed";
299
323
  steps: Record<string, StepResult>;
@@ -306,6 +330,8 @@ interface StepResult {
306
330
  sessionId?: string | undefined;
307
331
  output?: unknown;
308
332
  rawOutput?: string | undefined;
333
+ prUrl?: string | undefined;
334
+ prNumber?: number | undefined;
309
335
  costUsd: number;
310
336
  durationMs: number;
311
337
  agent: string;
@@ -320,10 +346,12 @@ interface DispatchInput {
320
346
  repo: string;
321
347
  prompt: string;
322
348
  runId?: string | undefined;
349
+ branch?: string | undefined;
323
350
  step?: string | undefined;
324
351
  from?: string | undefined;
325
352
  retry?: string | undefined;
326
353
  priority?: Priority | undefined;
354
+ gitStrategy?: "pr" | "branch" | undefined;
327
355
  overrides?: {
328
356
  agents?: Record<string, string> | undefined;
329
357
  maxTurns?: number | undefined;
@@ -355,7 +383,7 @@ interface ActiveSession {
355
383
  repo: string;
356
384
  status: "running" | "queued" | "waiting_gate";
357
385
  startedAt: string;
358
- worktreePath?: string | undefined;
386
+ sessionPath?: string | undefined;
359
387
  }
360
388
  interface OrchestratorStatus {
361
389
  paused: boolean;
@@ -609,15 +637,13 @@ declare class Semaphore {
609
637
  */
610
638
  declare class CostJournal {
611
639
  private readonly dir;
612
- private dirCreated;
640
+ private readonly dirCache;
613
641
  private dayCache;
614
642
  constructor(options: {
615
643
  dir: string;
616
644
  });
617
645
  append(entry: CostEntry): Promise<void>;
618
646
  getDayTotal(date?: Date): Promise<number>;
619
- private fileForDate;
620
- private ensureDir;
621
647
  }
622
648
 
623
649
  /**
@@ -644,28 +670,31 @@ declare class NeoEventEmitter {
644
670
  */
645
671
  declare class EventJournal {
646
672
  private readonly dir;
647
- private dirCreated;
673
+ private readonly dirCache;
648
674
  constructor(options: {
649
675
  dir: string;
650
676
  });
651
677
  append(event: NeoEvent): Promise<void>;
652
- private fileForDate;
653
- private ensureDir;
654
678
  }
655
679
 
656
680
  type WebhookConfig = NeoConfig["webhooks"][number];
657
681
  /**
658
- * Fire-and-forget webhook dispatcher for NeoEvents.
682
+ * Webhook dispatcher for NeoEvents.
659
683
  *
660
684
  * - Matches events against per-webhook filters (exact or wildcard like "session:*")
661
685
  * - Excludes gate:waiting events (contain non-serializable callbacks)
662
686
  * - Signs payloads with HMAC-SHA256 when a secret is configured
663
- * - Never throws errors are silently swallowed (consistent with EventJournal)
687
+ * - Terminal events (session:complete, session:fail, budget:alert) are retried
688
+ * with exponential backoff on failure
689
+ * - Non-terminal events remain fire-and-forget
664
690
  */
665
691
  declare class WebhookDispatcher {
666
692
  private readonly webhooks;
693
+ private readonly pending;
667
694
  constructor(webhooks: WebhookConfig[]);
668
695
  dispatch(event: NeoEvent): void;
696
+ /** Wait for all pending terminal webhook deliveries to complete. */
697
+ flush(): Promise<void>;
669
698
  }
670
699
  /**
671
700
  * Check if an event type matches a filter list.
@@ -674,27 +703,48 @@ declare class WebhookDispatcher {
674
703
  */
675
704
  declare function matchesFilter(eventType: string, filters?: string[]): boolean;
676
705
 
706
+ interface SessionCloneInfo {
707
+ path: string;
708
+ branch: string;
709
+ repoPath: string;
710
+ }
711
+ /**
712
+ * Create an isolated git clone for an agent session.
713
+ * Uses `git clone --local` to hardlink objects (fast, no network).
714
+ * Then checks out the target branch (existing or new).
715
+ */
716
+ declare function createSessionClone(options: {
717
+ repoPath: string;
718
+ branch: string;
719
+ baseBranch: string;
720
+ sessionDir: string;
721
+ }): Promise<SessionCloneInfo>;
722
+ /**
723
+ * Remove a session clone directory.
724
+ * Idempotent — does not throw if the directory is already gone.
725
+ */
726
+ declare function removeSessionClone(sessionPath: string): Promise<void>;
727
+ /**
728
+ * List all session clones under a base directory.
729
+ */
730
+ declare function listSessionClones(sessionsBaseDir: string): Promise<SessionCloneInfo[]>;
731
+
677
732
  declare function createBranch(repoPath: string, branch: string, baseBranch: string): Promise<void>;
678
733
  declare function pushBranch(repoPath: string, branch: string, remote: string): Promise<void>;
679
734
  declare function fetchRemote(repoPath: string, remote: string): Promise<void>;
680
735
  declare function deleteBranch(repoPath: string, branch: string): Promise<void>;
681
736
  declare function getCurrentBranch(repoPath: string): Promise<string>;
682
737
  /**
683
- * Generate a deterministic branch name for a run.
684
- * Uses the repo's branchPrefix (default "feat") and the runId.
685
- */
686
- declare function getBranchName(config: RepoConfig, runId: string): string;
687
-
688
- /**
689
- * Per-repository in-memory mutex to serialise git operations.
690
- * Concurrent git commands on the same repo corrupt the index — this prevents that.
738
+ * Resolve the branch name for a run.
739
+ * If an explicit branch is provided, use it as-is.
740
+ * Otherwise, generate a deterministic name from the repo's branchPrefix and runId.
691
741
  */
742
+ declare function getBranchName(config: RepoConfig, runId: string, branch?: string): string;
692
743
  /**
693
- * Execute `fn` while holding an exclusive lock for `repoPath`.
694
- * Concurrent calls for the same repo are queued and executed serially.
695
- * Operations on different repos run in parallel.
744
+ * Push a branch from a session clone to a remote. Silently succeeds if
745
+ * the branch has no new commits to push.
696
746
  */
697
- declare function withGitLock<T>(repoPath: string, fn: () => Promise<T>): Promise<T>;
747
+ declare function pushSessionBranch(sessionPath: string, branch: string, remote: string): Promise<void>;
698
748
 
699
749
  /**
700
750
  * SDK-compatible sandbox configuration.
@@ -713,40 +763,10 @@ interface SandboxConfig {
713
763
  /**
714
764
  * Build an SDK-compatible sandbox configuration for an agent.
715
765
  *
716
- * - Writable agents: all their tools are allowed, write paths include the worktree
766
+ * - Writable agents: all their tools are allowed, write paths include the session clone
717
767
  * - Readonly agents: write tools are filtered out, no writable paths
718
768
  */
719
- declare function buildSandboxConfig(agent: ResolvedAgent, worktreePath?: string): SandboxConfig;
720
-
721
- interface WorktreeInfo {
722
- path: string;
723
- branch: string;
724
- repoPath: string;
725
- }
726
- /**
727
- * Create a new git worktree with an associated branch.
728
- * Creates the branch from baseBranch, then adds the worktree at worktreeDir.
729
- */
730
- declare function createWorktree(options: {
731
- repoPath: string;
732
- branch: string;
733
- baseBranch: string;
734
- worktreeDir: string;
735
- }): Promise<WorktreeInfo>;
736
- /**
737
- * Remove a worktree. Does NOT delete the branch (branch stays for the PR).
738
- * Idempotent — does not throw if the worktree is already gone.
739
- */
740
- declare function removeWorktree(worktreePath: string): Promise<void>;
741
- /**
742
- * List all worktrees for a repository.
743
- */
744
- declare function listWorktrees(repoPath: string): Promise<WorktreeInfo[]>;
745
- /**
746
- * Clean up worktrees under worktreeBaseDir that no longer have a matching run.
747
- * Removes any subdirectory that is a git worktree.
748
- */
749
- declare function cleanupOrphanedWorktrees(worktreeBaseDir: string): Promise<void>;
769
+ declare function buildSandboxConfig(agent: ResolvedAgent, sessionPath?: string): SandboxConfig;
750
770
 
751
771
  /**
752
772
  * Audit log middleware.
@@ -815,6 +835,8 @@ interface OrchestratorOptions {
815
835
  journalDir?: string | undefined;
816
836
  builtInWorkflowDir?: string | undefined;
817
837
  customWorkflowDir?: string | undefined;
838
+ /** Skip orphan recovery on start — workers should set this to true to avoid false orphan detection on concurrent launches. */
839
+ skipOrphanRecovery?: boolean | undefined;
818
840
  }
819
841
  declare class Orchestrator extends NeoEventEmitter {
820
842
  private readonly config;
@@ -826,17 +848,19 @@ declare class Orchestrator extends NeoEventEmitter {
826
848
  private readonly idempotencyCache;
827
849
  private readonly abortControllers;
828
850
  private readonly repoIndex;
829
- private readonly createdRunDirs;
851
+ private readonly runStore;
830
852
  private readonly journalDir;
831
853
  private readonly builtInWorkflowDir;
832
854
  private readonly customWorkflowDir;
833
855
  private costJournal;
834
856
  private eventJournal;
835
857
  private webhookDispatcher;
858
+ private memoryStore;
836
859
  private _paused;
837
860
  private _costToday;
838
861
  private _startedAt;
839
862
  private _drainResolve;
863
+ private readonly skipOrphanRecovery;
840
864
  constructor(config: NeoConfig, options?: OrchestratorOptions);
841
865
  registerWorkflow(definition: WorkflowDefinition): void;
842
866
  registerAgent(agent: ResolvedAgent): void;
@@ -867,8 +891,15 @@ declare class Orchestrator extends NeoEventEmitter {
867
891
  private preDispatchChecks;
868
892
  private buildDispatchContext;
869
893
  private executeStep;
894
+ /**
895
+ * Push the branch (writable only), then remove the session clone.
896
+ * Runs in `finally` so it executes on both success and failure.
897
+ */
898
+ private finalizeSession;
870
899
  private runAgentSession;
871
900
  private finalizeDispatch;
901
+ private getMemoryStore;
902
+ private loadMemoryContext;
872
903
  private emitCostEvents;
873
904
  private emitSessionComplete;
874
905
  private emitSessionFail;
@@ -878,8 +909,10 @@ declare class Orchestrator extends NeoEventEmitter {
878
909
  private getFirstStep;
879
910
  private resolveStepAgent;
880
911
  private resolveRepo;
881
- private buildMiddlewareContext;
882
912
  private computeBudgetRemainingPct;
913
+ private resolveMcpServers;
914
+ /** Discover running supervisor daemons and return webhook configs for their endpoints. */
915
+ private discoverSupervisorWebhooks;
883
916
  private persistRun;
884
917
  private recoverOrphanedRuns;
885
918
  }
@@ -923,7 +956,6 @@ declare function getSupervisorDir(name: string): string;
923
956
  * Path to a supervisor state file: ~/.neo/supervisors/<name>/state.json
924
957
  */
925
958
  declare function getSupervisorStatePath(name: string): string;
926
- declare function getSupervisorMemoryPath(name: string): string;
927
959
  declare function getSupervisorActivityPath(name: string): string;
928
960
  declare function getSupervisorInboxPath(name: string): string;
929
961
  declare function getSupervisorEventsPath(name: string): string;
@@ -933,9 +965,12 @@ interface ParsedOutput {
933
965
  rawOutput: string;
934
966
  output?: unknown;
935
967
  parseError?: string;
968
+ prUrl?: string;
969
+ prNumber?: number;
936
970
  }
937
971
  /**
938
972
  * Parse agent output, optionally validating against a Zod schema.
973
+ * Also extracts structured markers like PR_URL from the output.
939
974
  *
940
975
  * - If no schema: returns rawOutput only
941
976
  * - If schema provided: extracts JSON from output, validates with schema
@@ -947,10 +982,11 @@ interface SessionOptions {
947
982
  agent: ResolvedAgent;
948
983
  prompt: string;
949
984
  repoPath?: string;
950
- worktreePath?: string;
985
+ sessionPath?: string;
951
986
  sandboxConfig: SandboxConfig;
952
987
  hooks?: Record<string, unknown>;
953
- mcpServers?: McpServerConfig[];
988
+ mcpServers?: Record<string, McpServerConfig>;
989
+ env?: Record<string, string>;
954
990
  initTimeoutMs: number;
955
991
  maxDurationMs: number;
956
992
  resumeSessionId?: string | undefined;
@@ -1000,9 +1036,80 @@ interface RecoveryOptions extends SessionOptions {
1000
1036
  */
1001
1037
  declare function runWithRecovery(options: RecoveryOptions): Promise<SessionResult>;
1002
1038
 
1039
+ interface SessionExecutionInput {
1040
+ runId: string;
1041
+ sessionId: string;
1042
+ agent: ResolvedAgent;
1043
+ stepDef: WorkflowStepDef;
1044
+ repoConfig: RepoConfig;
1045
+ repoPath: string;
1046
+ prompt: string;
1047
+ branch?: string | undefined;
1048
+ gitStrategy: GitStrategy;
1049
+ sessionPath?: string | undefined;
1050
+ metadata?: Record<string, unknown> | undefined;
1051
+ startedAt: string;
1052
+ }
1053
+ interface SessionExecutionConfig {
1054
+ initTimeoutMs: number;
1055
+ maxDurationMs: number;
1056
+ maxRetries: number;
1057
+ backoffBaseMs: number;
1058
+ }
1059
+ interface SessionExecutionDeps {
1060
+ middleware: Middleware[];
1061
+ mcpServers?: Record<string, McpServerConfig> | undefined;
1062
+ memoryContext?: string | undefined;
1063
+ onAttempt?: (attempt: number, strategy: string) => void;
1064
+ }
1065
+ interface SessionExecutionResult extends StepResult {
1066
+ parsed: ParsedOutput;
1067
+ }
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
+ /**
1073
+ * Encapsulates session execution logic: prompt building, SDK calls, and response processing.
1074
+ * Extracted from Orchestrator for better testability and separation of concerns.
1075
+ */
1076
+ declare class SessionExecutor {
1077
+ private readonly config;
1078
+ private readonly getContextValue;
1079
+ constructor(config: SessionExecutionConfig, getContextValue: (key: string) => unknown);
1080
+ /**
1081
+ * Execute an agent session with the given input and dependencies.
1082
+ * Handles prompt building, SDK invocation via recovery wrapper, and output parsing.
1083
+ */
1084
+ execute(input: SessionExecutionInput, deps: SessionExecutionDeps): Promise<SessionExecutionResult>;
1085
+ }
1086
+
1087
+ /**
1088
+ * Checks whether a process with the given PID is currently running.
1089
+ *
1090
+ * Uses the POSIX signal 0 trick: `process.kill(pid, 0)` doesn't actually
1091
+ * send a signal but checks whether the process exists and the current
1092
+ * process has permission to signal it. If the process doesn't exist,
1093
+ * an ESRCH error is thrown.
1094
+ *
1095
+ * @param pid - The process ID to check. Must be a positive integer.
1096
+ * @returns `true` if the process is alive and accessible, `false` otherwise.
1097
+ *
1098
+ * @example
1099
+ * ```ts
1100
+ * import { isProcessAlive } from "@/shared/process";
1101
+ *
1102
+ * // Check if current process is alive (always true)
1103
+ * isProcessAlive(process.pid); // => true
1104
+ *
1105
+ * // Check if a non-existent process is alive
1106
+ * isProcessAlive(999999); // => false
1107
+ * ```
1108
+ */
1109
+ declare function isProcessAlive(pid: number): boolean;
1110
+
1003
1111
  declare const supervisorDaemonStateSchema: z.ZodObject<{
1004
1112
  pid: z.ZodNumber;
1005
- tmuxSession: z.ZodString;
1006
1113
  sessionId: z.ZodString;
1007
1114
  port: z.ZodNumber;
1008
1115
  cwd: z.ZodString;
@@ -1012,11 +1119,22 @@ declare const supervisorDaemonStateSchema: z.ZodObject<{
1012
1119
  totalCostUsd: z.ZodDefault<z.ZodNumber>;
1013
1120
  todayCostUsd: z.ZodDefault<z.ZodNumber>;
1014
1121
  costResetDate: z.ZodOptional<z.ZodString>;
1122
+ idleSkipCount: z.ZodDefault<z.ZodNumber>;
1123
+ activeWorkSkipCount: z.ZodDefault<z.ZodNumber>;
1015
1124
  status: z.ZodDefault<z.ZodEnum<{
1016
1125
  running: "running";
1017
1126
  draining: "draining";
1018
1127
  stopped: "stopped";
1019
1128
  }>>;
1129
+ lastConsolidationHeartbeat: z.ZodDefault<z.ZodNumber>;
1130
+ lastCompactionHeartbeat: z.ZodDefault<z.ZodNumber>;
1131
+ lastConsolidationTimestamp: z.ZodOptional<z.ZodString>;
1132
+ wakeReason: z.ZodOptional<z.ZodEnum<{
1133
+ events: "events";
1134
+ timer: "timer";
1135
+ active_runs: "active_runs";
1136
+ forced: "forced";
1137
+ }>>;
1020
1138
  }, z.core.$strip>;
1021
1139
  type SupervisorDaemonState = z.infer<typeof supervisorDaemonStateSchema>;
1022
1140
  declare const webhookIncomingEventSchema: z.ZodObject<{
@@ -1031,6 +1149,7 @@ type WebhookIncomingEvent = z.infer<typeof webhookIncomingEventSchema>;
1031
1149
  declare const inboxMessageSchema: z.ZodObject<{
1032
1150
  id: z.ZodString;
1033
1151
  from: z.ZodEnum<{
1152
+ agent: "agent";
1034
1153
  tui: "tui";
1035
1154
  api: "api";
1036
1155
  external: "external";
@@ -1046,19 +1165,47 @@ declare const activityEntrySchema: z.ZodObject<{
1046
1165
  error: "error";
1047
1166
  message: "message";
1048
1167
  decision: "decision";
1168
+ tool_use: "tool_use";
1049
1169
  event: "event";
1050
1170
  heartbeat: "heartbeat";
1051
1171
  action: "action";
1052
1172
  thinking: "thinking";
1053
1173
  plan: "plan";
1054
1174
  dispatch: "dispatch";
1055
- tool_use: "tool_use";
1056
1175
  }>;
1057
1176
  summary: z.ZodString;
1058
1177
  detail: z.ZodOptional<z.ZodUnknown>;
1059
1178
  timestamp: z.ZodString;
1060
1179
  }, z.core.$strip>;
1061
1180
  type ActivityEntry = z.infer<typeof activityEntrySchema>;
1181
+ declare const logBufferEntrySchema: z.ZodObject<{
1182
+ id: z.ZodString;
1183
+ type: z.ZodEnum<{
1184
+ decision: "decision";
1185
+ action: "action";
1186
+ progress: "progress";
1187
+ blocker: "blocker";
1188
+ milestone: "milestone";
1189
+ discovery: "discovery";
1190
+ }>;
1191
+ message: z.ZodString;
1192
+ agent: z.ZodOptional<z.ZodString>;
1193
+ runId: z.ZodOptional<z.ZodString>;
1194
+ repo: z.ZodOptional<z.ZodString>;
1195
+ target: z.ZodEnum<{
1196
+ memory: "memory";
1197
+ knowledge: "knowledge";
1198
+ digest: "digest";
1199
+ }>;
1200
+ timestamp: z.ZodString;
1201
+ consolidatedAt: z.ZodOptional<z.ZodString>;
1202
+ }, z.core.$strip>;
1203
+ type LogBufferEntry = z.infer<typeof logBufferEntrySchema>;
1204
+ declare const internalEventKindSchema: z.ZodEnum<{
1205
+ consolidation_timer: "consolidation_timer";
1206
+ active_run_check: "active_run_check";
1207
+ }>;
1208
+ type InternalEventKind = z.infer<typeof internalEventKindSchema>;
1062
1209
  type QueuedEvent = {
1063
1210
  kind: "webhook";
1064
1211
  data: WebhookIncomingEvent;
@@ -1069,10 +1216,14 @@ type QueuedEvent = {
1069
1216
  kind: "run_complete";
1070
1217
  runId: string;
1071
1218
  timestamp: string;
1219
+ } | {
1220
+ kind: "internal";
1221
+ eventKind: InternalEventKind;
1222
+ timestamp: string;
1072
1223
  };
1073
1224
 
1074
1225
  declare class ActivityLog {
1075
- private readonly filePath;
1226
+ readonly filePath: string;
1076
1227
  private readonly dir;
1077
1228
  constructor(dir: string);
1078
1229
  /**
@@ -1094,6 +1245,8 @@ declare class ActivityLog {
1094
1245
  interface SupervisorDaemonOptions {
1095
1246
  name: string;
1096
1247
  config: GlobalConfig;
1248
+ /** Path to bundled default SUPERVISOR.md (e.g. from @neotx/agents) */
1249
+ defaultInstructionsPath?: string | undefined;
1097
1250
  }
1098
1251
  /**
1099
1252
  * Orchestrates all supervisor components: webhook server, event queue,
@@ -1103,6 +1256,7 @@ declare class SupervisorDaemon {
1103
1256
  private readonly name;
1104
1257
  private readonly config;
1105
1258
  private readonly dir;
1259
+ private readonly defaultInstructionsPath;
1106
1260
  private webhookServer;
1107
1261
  private eventQueue;
1108
1262
  private heartbeatLoop;
@@ -1115,12 +1269,21 @@ declare class SupervisorDaemon {
1115
1269
  private readState;
1116
1270
  private writeState;
1117
1271
  private readLockPid;
1118
- private isProcessAlive;
1119
1272
  }
1120
1273
 
1121
1274
  interface EventQueueOptions {
1122
1275
  maxEventsPerSec: number;
1123
1276
  }
1277
+ interface GroupedMessage {
1278
+ text: string;
1279
+ from: string;
1280
+ count: number;
1281
+ }
1282
+ interface GroupedEvents {
1283
+ messages: GroupedMessage[];
1284
+ webhooks: QueuedEvent[];
1285
+ runCompletions: QueuedEvent[];
1286
+ }
1124
1287
  /**
1125
1288
  * In-memory event queue with deduplication, rate limiting, and file watching.
1126
1289
  *
@@ -1151,12 +1314,17 @@ declare class EventQueue {
1151
1314
  * Drain all queued events and return them. Clears the queue.
1152
1315
  */
1153
1316
  drain(): QueuedEvent[];
1317
+ /**
1318
+ * Drain and group events: deduplicates messages by content,
1319
+ * keeps webhooks and run completions separate.
1320
+ */
1321
+ drainAndGroup(): GroupedEvents;
1154
1322
  size(): number;
1155
1323
  /**
1156
1324
  * Start watching inbox.jsonl and events.jsonl for new entries.
1157
1325
  * New lines are parsed and pushed into the queue.
1158
1326
  */
1159
- startWatching(inboxPath: string, eventsPath: string): void;
1327
+ startWatching(inboxPath: string, eventsPath: string): Promise<void>;
1160
1328
  stopWatching(): void;
1161
1329
  /**
1162
1330
  * Replay unprocessed events from disk on startup.
@@ -1188,15 +1356,20 @@ interface HeartbeatLoopOptions {
1188
1356
  sessionId: string;
1189
1357
  eventQueue: EventQueue;
1190
1358
  activityLog: ActivityLog;
1359
+ /** Path to bundled default SUPERVISOR.md (e.g. from @neotx/agents) */
1360
+ defaultInstructionsPath?: string | undefined;
1361
+ memoryDbPath?: string | undefined;
1191
1362
  }
1192
1363
  /**
1193
1364
  * The core autonomous loop. At each iteration:
1194
1365
  * 1. Drain events from the queue
1195
- * 2. Build a prompt with context + memory + events
1196
- * 3. Call sdk.query() for Claude to reason and act
1197
- * 4. Extract and save updated memory
1198
- * 5. Log activity
1199
- * 6. Wait for the next event or idle timeout
1366
+ * 2. Read log buffer entries
1367
+ * 3. Determine standard vs consolidation mode
1368
+ * 4. Build the appropriate prompt
1369
+ * 5. Call sdk.query() for Claude to reason and act
1370
+ * 6. Mark entries consolidated and compact log buffer (consolidation only)
1371
+ * 7. Log activity
1372
+ * 8. Wait for the next event or idle timeout
1200
1373
  */
1201
1374
  declare class HeartbeatLoop {
1202
1375
  private stopping;
@@ -1209,22 +1382,53 @@ declare class HeartbeatLoop {
1209
1382
  private readonly eventQueue;
1210
1383
  private readonly activityLog;
1211
1384
  private customInstructions;
1385
+ private readonly defaultInstructionsPath;
1386
+ private memoryStore;
1387
+ private readonly memoryDbPath;
1212
1388
  constructor(options: HeartbeatLoopOptions);
1389
+ private getMemoryStore;
1213
1390
  start(): Promise<void>;
1214
1391
  stop(): void;
1215
1392
  private runHeartbeat;
1393
+ /**
1394
+ * Check if supervisor daily budget is exceeded.
1395
+ */
1396
+ private checkBudgetExceeded;
1397
+ /**
1398
+ * Handle skip logic for idle and active-work scenarios.
1399
+ */
1400
+ private handleSkipLogic;
1401
+ /**
1402
+ * Determine heartbeat mode: compaction > consolidation > standard.
1403
+ */
1404
+ private determineHeartbeatMode;
1405
+ /**
1406
+ * Build the state update object after heartbeat completion.
1407
+ */
1408
+ private buildStateUpdate;
1409
+ /**
1410
+ * Build the prompt for the current heartbeat mode.
1411
+ */
1412
+ private buildHeartbeatModePrompt;
1413
+ /**
1414
+ * Call the Claude SDK and stream results.
1415
+ */
1416
+ private callSdk;
1216
1417
  private readState;
1217
1418
  private updateState;
1419
+ /** Read persisted run files and return summaries of active (running/paused) runs. */
1420
+ private getActiveRuns;
1218
1421
  /**
1219
1422
  * Load custom instructions from SUPERVISOR.md.
1220
1423
  * Resolution order:
1221
1424
  * 1. Explicit path via `supervisor.instructions` in config
1222
- * 2. Default: ~/.neo/SUPERVISOR.md
1425
+ * 2. User default: ~/.neo/SUPERVISOR.md
1426
+ * 3. Bundled default from @neotx/agents (if path provided)
1223
1427
  */
1224
1428
  private loadInstructions;
1225
1429
  /** Route a single SDK stream message to the appropriate log handler. */
1226
1430
  private logStreamMessage;
1227
- /** Log thinking and plan blocks from assistant content. */
1431
+ /** Log thinking and plan blocks from assistant content — no truncation. */
1228
1432
  private logContentBlocks;
1229
1433
  /** Log tool use events — distinguish MCP tools from built-in tools. */
1230
1434
  private logToolUse;
@@ -1234,50 +1438,115 @@ declare class HeartbeatLoop {
1234
1438
  }
1235
1439
 
1236
1440
  /**
1237
- * Load the supervisor memory from disk.
1238
- * Returns empty string if no memory file exists yet.
1239
- */
1240
- declare function loadMemory(dir: string): Promise<string>;
1241
- /**
1242
- * Save the supervisor memory to disk (full overwrite).
1441
+ * Append a single entry to the log buffer file.
1243
1442
  */
1244
- declare function saveMemory(dir: string, content: string): Promise<void>;
1245
- /**
1246
- * Extract memory content from Claude's response using <memory>...</memory> tags.
1247
- * Handles both JSON and markdown content inside the tags.
1248
- * Returns null if no memory block is found.
1249
- */
1250
- declare function extractMemoryFromResponse(response: string): string | null;
1251
- /**
1252
- * Check if memory content exceeds the recommended size limit.
1253
- */
1254
- declare function checkMemorySize(content: string): {
1255
- ok: boolean;
1256
- sizeKB: number;
1257
- };
1443
+ declare function appendLogBuffer(dir: string, entry: LogBufferEntry): Promise<void>;
1258
1444
 
1259
- interface HeartbeatPromptOptions {
1260
- repos: RepoConfig[];
1261
- memory: string;
1262
- memorySizeKB: number;
1263
- events: QueuedEvent[];
1264
- budgetStatus: {
1265
- todayUsd: number;
1266
- capUsd: number;
1267
- remainingPct: number;
1268
- };
1269
- activeRuns: string[];
1270
- heartbeatCount: number;
1271
- mcpServerNames: string[];
1272
- customInstructions?: string | undefined;
1445
+ interface Embedder {
1446
+ embed(texts: string[]): Promise<number[][]>;
1447
+ readonly dimensions: number;
1448
+ }
1449
+ declare class LocalEmbedder implements Embedder {
1450
+ readonly dimensions = 384;
1451
+ embed(texts: string[]): Promise<number[][]>;
1452
+ }
1453
+
1454
+ declare const memoryTypeSchema: z.ZodEnum<{
1455
+ fact: "fact";
1456
+ procedure: "procedure";
1457
+ episode: "episode";
1458
+ focus: "focus";
1459
+ feedback: "feedback";
1460
+ task: "task";
1461
+ }>;
1462
+ type MemoryType = z.infer<typeof memoryTypeSchema>;
1463
+ declare const memoryEntrySchema: z.ZodObject<{
1464
+ id: z.ZodString;
1465
+ type: z.ZodEnum<{
1466
+ fact: "fact";
1467
+ procedure: "procedure";
1468
+ episode: "episode";
1469
+ focus: "focus";
1470
+ feedback: "feedback";
1471
+ task: "task";
1472
+ }>;
1473
+ scope: z.ZodString;
1474
+ content: z.ZodString;
1475
+ source: z.ZodString;
1476
+ tags: z.ZodDefault<z.ZodArray<z.ZodString>>;
1477
+ createdAt: z.ZodString;
1478
+ lastAccessedAt: z.ZodString;
1479
+ accessCount: z.ZodDefault<z.ZodNumber>;
1480
+ expiresAt: z.ZodOptional<z.ZodString>;
1481
+ outcome: z.ZodOptional<z.ZodString>;
1482
+ runId: z.ZodOptional<z.ZodString>;
1483
+ category: z.ZodOptional<z.ZodString>;
1484
+ severity: z.ZodOptional<z.ZodString>;
1485
+ supersedes: z.ZodOptional<z.ZodString>;
1486
+ }, z.core.$strip>;
1487
+ type MemoryEntry = z.infer<typeof memoryEntrySchema>;
1488
+ declare const memoryWriteInputSchema: z.ZodObject<{
1489
+ type: z.ZodEnum<{
1490
+ fact: "fact";
1491
+ procedure: "procedure";
1492
+ episode: "episode";
1493
+ focus: "focus";
1494
+ feedback: "feedback";
1495
+ task: "task";
1496
+ }>;
1497
+ scope: z.ZodDefault<z.ZodString>;
1498
+ content: z.ZodString;
1499
+ source: z.ZodDefault<z.ZodString>;
1500
+ tags: z.ZodDefault<z.ZodArray<z.ZodString>>;
1501
+ expiresAt: z.ZodOptional<z.ZodString>;
1502
+ outcome: z.ZodOptional<z.ZodString>;
1503
+ runId: z.ZodOptional<z.ZodString>;
1504
+ category: z.ZodOptional<z.ZodString>;
1505
+ severity: z.ZodOptional<z.ZodString>;
1506
+ supersedes: z.ZodOptional<z.ZodString>;
1507
+ }, z.core.$strip>;
1508
+ type MemoryWriteInput = z.input<typeof memoryWriteInputSchema>;
1509
+ interface MemoryQuery {
1510
+ scope?: string;
1511
+ types?: MemoryType[];
1512
+ since?: string;
1513
+ limit?: number;
1514
+ sortBy?: "relevance" | "createdAt" | "accessCount";
1515
+ tags?: string[];
1516
+ }
1517
+ interface MemoryStats {
1518
+ total: number;
1519
+ byType: Record<string, number>;
1520
+ byScope: Record<string, number>;
1521
+ }
1522
+
1523
+ declare class MemoryStore {
1524
+ private db;
1525
+ private embedder;
1526
+ private hasVec;
1527
+ constructor(dbPath: string, embedder?: Embedder | null);
1528
+ private initSchema;
1529
+ /**
1530
+ * Migrate existing tables whose CHECK constraint predates the 'task' type.
1531
+ * SQLite doesn't allow ALTER CHECK, so we recreate the table if needed.
1532
+ */
1533
+ private migrateCheckConstraint;
1534
+ write(input: MemoryWriteInput): Promise<string>;
1535
+ update(id: string, content: string): void;
1536
+ updateFields(id: string, fields: {
1537
+ content?: string;
1538
+ outcome?: string;
1539
+ runId?: string;
1540
+ }): void;
1541
+ forget(id: string): void;
1542
+ query(opts?: MemoryQuery): MemoryEntry[];
1543
+ search(text: string, opts?: MemoryQuery): Promise<MemoryEntry[]>;
1544
+ markAccessed(ids: string[]): void;
1545
+ decay(maxAgeDays?: number, minAccessCount?: number): number;
1546
+ expireEphemeral(): number;
1547
+ stats(): MemoryStats;
1548
+ close(): void;
1273
1549
  }
1274
- /**
1275
- * Build the prompt sent to Claude at each heartbeat.
1276
- *
1277
- * Includes: role definition, current memory, pending events,
1278
- * budget status, active runs, and available integrations.
1279
- */
1280
- declare function buildHeartbeatPrompt(opts: HeartbeatPromptOptions): string;
1281
1550
 
1282
1551
  interface WebhookServerOptions {
1283
1552
  port: number;
@@ -1356,4 +1625,4 @@ declare class WorkflowRegistry {
1356
1625
 
1357
1626
  declare const VERSION = "0.1.0";
1358
1627
 
1359
- 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, EventJournal, EventQueue, type GateWaitingEvent, type GlobalConfig, HeartbeatLoop, type HeartbeatLoopOptions, type HeartbeatPromptOptions, type HookEvent, type InboxMessage, type LoopDetectionMiddleware, type McpServerConfig, 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 SessionCompleteEvent, SessionError, type SessionEvent, 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, type WorktreeInfo, activityEntrySchema, addRepoToGlobalConfig, agentConfigSchema, agentModelSchema, agentSandboxSchema, agentToolEntrySchema, agentToolSchema, auditLog, budgetGuard, buildHeartbeatPrompt, buildMiddlewareChain, buildSDKHooks, buildSandboxConfig, checkMemorySize, cleanupOrphanedWorktrees, createBranch, createWorktree, deleteBranch, extractMemoryFromResponse, fetchRemote, getBranchName, getCurrentBranch, getDataDir, getJournalsDir, getRepoRunsDir, getRunDispatchPath, getRunLogPath, getRunsDir, getSupervisorActivityPath, getSupervisorDir, getSupervisorEventsPath, getSupervisorInboxPath, getSupervisorLockPath, getSupervisorMemoryPath, getSupervisorStatePath, getSupervisorsDir, globalConfigSchema, inboxMessageSchema, listReposFromGlobalConfig, listWorktrees, loadAgentFile, loadConfig, loadGlobalConfig, loadMemory, loadWorkflow, loopDetection, matchesFilter, mcpServerConfigSchema, neoConfigSchema, parseOutput, pushBranch, removeRepoFromGlobalConfig, removeWorktree, repoConfigSchema, resolveAgent, runSession, runWithRecovery, saveMemory, supervisorDaemonStateSchema, supervisorDaemonStateSchema as supervisorStateSchema, toRepoSlug, webhookIncomingEventSchema, withGitLock, workflowGateDefSchema, workflowStepDefSchema };
1628
+ 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 };