@neotx/core 0.1.0-alpha.6 → 0.1.0-alpha.9
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 +3 -0
- package/dist/index.js +153 -111
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -835,6 +835,8 @@ interface OrchestratorOptions {
|
|
|
835
835
|
journalDir?: string | undefined;
|
|
836
836
|
builtInWorkflowDir?: string | undefined;
|
|
837
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;
|
|
838
840
|
}
|
|
839
841
|
declare class Orchestrator extends NeoEventEmitter {
|
|
840
842
|
private readonly config;
|
|
@@ -858,6 +860,7 @@ declare class Orchestrator extends NeoEventEmitter {
|
|
|
858
860
|
private _costToday;
|
|
859
861
|
private _startedAt;
|
|
860
862
|
private _drainResolve;
|
|
863
|
+
private readonly skipOrphanRecovery;
|
|
861
864
|
constructor(config: NeoConfig, options?: OrchestratorOptions);
|
|
862
865
|
registerWorkflow(definition: WorkflowDefinition): void;
|
|
863
866
|
registerAgent(agent: ResolvedAgent): void;
|
package/dist/index.js
CHANGED
|
@@ -2371,6 +2371,7 @@ var Orchestrator = class extends NeoEventEmitter {
|
|
|
2371
2371
|
_costToday = 0;
|
|
2372
2372
|
_startedAt = 0;
|
|
2373
2373
|
_drainResolve = null;
|
|
2374
|
+
skipOrphanRecovery;
|
|
2374
2375
|
constructor(config, options = {}) {
|
|
2375
2376
|
super();
|
|
2376
2377
|
this.config = config;
|
|
@@ -2378,6 +2379,7 @@ var Orchestrator = class extends NeoEventEmitter {
|
|
|
2378
2379
|
this.journalDir = options.journalDir ?? getJournalsDir();
|
|
2379
2380
|
this.builtInWorkflowDir = options.builtInWorkflowDir;
|
|
2380
2381
|
this.customWorkflowDir = options.customWorkflowDir;
|
|
2382
|
+
this.skipOrphanRecovery = options.skipOrphanRecovery ?? false;
|
|
2381
2383
|
for (const repo of config.repos) {
|
|
2382
2384
|
const resolvedPath = path11.resolve(repo.path);
|
|
2383
2385
|
const normalizedRepo = { ...repo, path: resolvedPath };
|
|
@@ -2495,7 +2497,9 @@ var Orchestrator = class extends NeoEventEmitter {
|
|
|
2495
2497
|
this.registerWorkflow(workflow);
|
|
2496
2498
|
}
|
|
2497
2499
|
}
|
|
2498
|
-
|
|
2500
|
+
if (!this.skipOrphanRecovery) {
|
|
2501
|
+
await this.recoverOrphanedRuns();
|
|
2502
|
+
}
|
|
2499
2503
|
await mkdir6(this.config.sessions.dir, { recursive: true });
|
|
2500
2504
|
}
|
|
2501
2505
|
async shutdown() {
|
|
@@ -3573,7 +3577,16 @@ async function appendLogBuffer(dir, entry) {
|
|
|
3573
3577
|
}
|
|
3574
3578
|
|
|
3575
3579
|
// src/supervisor/prompt-builder.ts
|
|
3576
|
-
var ROLE = `You are the neo autonomous supervisor
|
|
3580
|
+
var ROLE = `You are the neo autonomous supervisor \u2014 a stateless dispatch controller.
|
|
3581
|
+
|
|
3582
|
+
You receive state (events, memory, work queue) and produce actions (tool calls).
|
|
3583
|
+
|
|
3584
|
+
<behavioral-contract>
|
|
3585
|
+
- Your ONLY visible output is \`neo log\` commands. The TUI shows these and nothing else.
|
|
3586
|
+
- Your text output is NEVER shown to anyone \u2014 every token of text is wasted cost.
|
|
3587
|
+
- Produce tool calls, not explanations. Do not narrate your reasoning.
|
|
3588
|
+
- You NEVER modify code \u2014 that is the agents' job.
|
|
3589
|
+
</behavioral-contract>`;
|
|
3577
3590
|
var COMMANDS = `### Dispatching agents
|
|
3578
3591
|
\`\`\`bash
|
|
3579
3592
|
neo run <agent> --prompt "..." --repo <path> --branch <name> [--priority critical|high|medium|low] [--meta '<json>']
|
|
@@ -3604,6 +3617,8 @@ neo agents # list available agents
|
|
|
3604
3617
|
neo memory write --type fact --scope /path "Stable fact about repo"
|
|
3605
3618
|
neo memory write --type focus --expires 2h "Current working context"
|
|
3606
3619
|
neo memory write --type procedure --scope /path "How to do X"
|
|
3620
|
+
neo memory write --type task --scope /path --severity high --category "neo runs <id>" "Task description"
|
|
3621
|
+
neo memory update <id> --outcome in_progress|done|blocked|abandoned
|
|
3607
3622
|
neo memory forget <id>
|
|
3608
3623
|
neo memory search "keyword"
|
|
3609
3624
|
neo memory list --type fact
|
|
@@ -3613,107 +3628,109 @@ neo memory list --type fact
|
|
|
3613
3628
|
\`\`\`bash
|
|
3614
3629
|
neo log <type> "<message>" # visible in TUI only
|
|
3615
3630
|
\`\`\``;
|
|
3631
|
+
var COMMANDS_COMPACT = `### Commands (reference)
|
|
3632
|
+
\`neo run <agent> --prompt "..." --repo <path> --branch <name> [--meta '<json>']\`
|
|
3633
|
+
\`neo runs [--short | <runId>]\` \xB7 \`neo cost --short\` \xB7 \`neo agents\`
|
|
3634
|
+
\`neo memory write|update|forget|search|list\` \xB7 \`neo log <type> "<msg>"\`
|
|
3635
|
+
Always read output after architect/refiner: \`neo runs <runId>\`.`;
|
|
3616
3636
|
var HEARTBEAT_RULES = `### Heartbeat lifecycle
|
|
3617
|
-
1. **Check work queue FIRST** \u2014 if you have pending tasks, work on the next one before looking for new work
|
|
3618
|
-
2. Process incoming events (messages, run completions)
|
|
3619
|
-
3. Follow up on pending work (CI checks, deferred dispatches) with \`neo runs\` or \`gh pr checks\`
|
|
3620
|
-
4. Make decisions and dispatch agents
|
|
3621
|
-
5. Update task status (\`neo memory update <id> --outcome in_progress|done|blocked\`) and log decisions
|
|
3622
|
-
6. Yield \u2014 each heartbeat should take seconds, not minutes
|
|
3623
3637
|
|
|
3624
|
-
|
|
3638
|
+
<decision-tree>
|
|
3639
|
+
1. DEDUP FIRST \u2014 check focus for PROCESSED entries. Skip any runId already processed.
|
|
3640
|
+
2. PENDING TASKS? \u2014 dispatch the next eligible task from work queue. Do not re-plan.
|
|
3641
|
+
3. EVENTS? \u2014 process run completions, messages, webhooks. Parse agent JSON output.
|
|
3642
|
+
4. FOLLOW-UPS? \u2014 check CI (\`gh pr checks\`), deferred dispatches (\`neo runs\`).
|
|
3643
|
+
5. DISPATCH \u2014 route work to agents. Mark tasks \`in_progress\`, add ACTIVE to focus.
|
|
3644
|
+
6. YIELD \u2014 log your decisions and yield. Do not poll. Completions arrive at future heartbeats.
|
|
3645
|
+
</decision-tree>
|
|
3625
3646
|
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3647
|
+
<rules>
|
|
3648
|
+
- Work queue IS your plan. Never re-plan existing tasks.
|
|
3649
|
+
- Maximize parallelism: dispatch independent tasks in the same heartbeat.
|
|
3650
|
+
- After dispatch: update focus, yield immediately. Do NOT wait for results.
|
|
3651
|
+
- Deferred work (CI pending): MUST check at next heartbeat.
|
|
3652
|
+
</rules>`;
|
|
3629
3653
|
var REPORTING_RULES = `### Reporting
|
|
3630
|
-
\`neo log\` is your ONLY visible output \u2014 the TUI shows these and nothing else.
|
|
3631
|
-
- \`neo log decision "..."\` \u2014 why you chose this route
|
|
3632
|
-
- \`neo log action "..."\` \u2014 what you dispatched/did
|
|
3633
|
-
- \`neo log discovery "..."\` \u2014 ephemeral observations
|
|
3634
|
-
- 1-3 sentences per log. Pack maximum info: ticket, agent, branch, runId, cost, PR#. No markdown.
|
|
3635
3654
|
|
|
3636
|
-
|
|
3637
|
-
var MEMORY_RULES = `### Memory \u2014 types and when to use each
|
|
3655
|
+
\`neo log\` is your ONLY visible output. Use telegraphic format.
|
|
3638
3656
|
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
| \`feedback\` | Recurring review pattern | After seeing the same reviewer complaint 3+ times | Permanent |
|
|
3645
|
-
| \`episode\` | Run outcome | Auto-created on run completion \u2014 do NOT write manually | Permanent |
|
|
3646
|
-
| \`task\` | Planned work item | After architect output or decomposition | Until done/abandoned |
|
|
3657
|
+
<log-format>
|
|
3658
|
+
neo log decision "<ticket> \u2192 <action> | <1-line reason>"
|
|
3659
|
+
neo log action "<agent> <repo>:<branch> run:<runId> | <context>"
|
|
3660
|
+
neo log discovery "<what> in <where>"
|
|
3661
|
+
</log-format>
|
|
3647
3662
|
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3663
|
+
<examples>
|
|
3664
|
+
<example type="good">
|
|
3665
|
+
neo log decision "YC-42 \u2192 developer | clear spec, complexity 3"
|
|
3666
|
+
neo log action "developer standards:feat/YC-42-auth run:5900a64a | task T1"
|
|
3667
|
+
neo log discovery "CI requires node 20 in api-service"
|
|
3668
|
+
</example>
|
|
3669
|
+
<example type="bad">
|
|
3670
|
+
neo log plan "Good! Now let me check the status and update things accordingly."
|
|
3671
|
+
neo log decision "Heartbeat #309: Idle cycle - no action required. All 4 repositories stable."
|
|
3672
|
+
neo log action "I've dispatched a developer agent to work on the authentication feature."
|
|
3673
|
+
</example>
|
|
3674
|
+
</examples>`;
|
|
3675
|
+
var MEMORY_RULES_CORE = `### Memory
|
|
3652
3676
|
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
3677
|
+
<memory-types>
|
|
3678
|
+
| Type | Store when | TTL |
|
|
3679
|
+
|------|-----------|-----|
|
|
3680
|
+
| \`fact\` | Stable truth affecting dispatch decisions | Permanent (decays) |
|
|
3681
|
+
| \`procedure\` | Same failure 3+ times | Permanent |
|
|
3682
|
+
| \`focus\` | After every dispatch/deferral | --expires required |
|
|
3683
|
+
| \`task\` | Any planned work (tickets, decompositions, follow-ups) | Until done/abandoned |
|
|
3684
|
+
| \`feedback\` | Same review complaint 3+ times | Permanent |
|
|
3685
|
+
</memory-types>
|
|
3658
3686
|
|
|
3659
|
-
|
|
3660
|
-
Focus
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
\`\`\`
|
|
3687
|
+
<memory-rules>
|
|
3688
|
+
- Focus MUST use structured format: ACTIVE/PENDING/WAITING/PROCESSED lines only.
|
|
3689
|
+
- NEVER store: file counts, line numbers, completed work details, data available via \`neo runs <id>\`.
|
|
3690
|
+
- After PR merge: forget related facts unless they are reusable architectural truths.
|
|
3691
|
+
- Pattern escalation: same failure 3+ times \u2192 write a \`procedure\`.
|
|
3692
|
+
- Every memory that references external context MUST include a retrieval command (in \`--category\` for tasks, in content for facts/procedures). You are stateless \u2014 if you can't retrieve it later, don't store it.
|
|
3693
|
+
</memory-rules>
|
|
3667
3694
|
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
neo memory write --type focus --expires 2h "ACTIVE: 5900a64a developer 'T1: schema+store' branch:feat/task-queue
|
|
3671
|
-
PENDING: T2 'CLI --outcome flag' depends:T1
|
|
3672
|
-
PENDING: T3+T4 'prompt injection' depends:T1"
|
|
3695
|
+
<task-workflow>
|
|
3696
|
+
Tasks are your work queue. The work queue section above shows them with markers (\`\u25CB\` pending, \`[ACTIVE]\` in_progress, \`[BLOCKED]\` blocked).
|
|
3673
3697
|
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3698
|
+
Create a task for any planned work: incoming tickets, architect decompositions, refiner sub-tickets, follow-up actions, CI fixes.
|
|
3699
|
+
- \`--severity critical|high|medium|low\` \u2014 dispatch highest severity first
|
|
3700
|
+
- \`--tags "initiative:<name>"\` \u2014 groups related tasks (shown as [initiative] headers in queue)
|
|
3701
|
+
- \`--tags "depends:mem_<id>"\` \u2014 task cannot start until dependency is done
|
|
3702
|
+
- \`--category\` \u2014 **MANDATORY** \u2014 the command to retrieve context for this task (shown as \`\u2192 <command>\` in queue)
|
|
3677
3703
|
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
|
|
3704
|
+
**Context retrieval rule**: every task and relevant memory MUST include a way for you to access its source context at a future heartbeat. You are stateless \u2014 without this, you lose the context.
|
|
3705
|
+
- Agent output: \`--category "neo runs <runId>"\`
|
|
3706
|
+
- Note/plan: \`--category "cat notes/plan-feature.md"\`
|
|
3707
|
+
- Notion ticket: \`--category "API-retrieve-a-page <notionPageId>"\`
|
|
3708
|
+
- Architect decomposition: \`--category "neo runs <architectRunId>"\` (contains milestones + tasks)
|
|
3681
3709
|
|
|
3682
|
-
|
|
3683
|
-
neo memory write --type feedback --scope /path/to/repo --category input_validation "Always validate user input at controller boundaries"
|
|
3710
|
+
Lifecycle: create \u2192 \`neo memory update <id> --outcome in_progress\` (on dispatch) \u2192 \`done\` (on success) / \`blocked\` (on failure, will retry) / \`abandoned\` (terminal, won't retry)
|
|
3684
3711
|
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
neo memory write --type task --scope /path/to/repo --severity medium --tags "initiative:auth-v2,depends:mem_abc" --category "cat notes/plan-auth.md" "T2: Add JWT validation"
|
|
3712
|
+
Dispatch rule: pick the highest-severity task with no unmet dependencies. Dispatch independent tasks in parallel. Before dispatching, run the \`--category\` command to retrieve task context.
|
|
3713
|
+
</task-workflow>
|
|
3688
3714
|
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3715
|
+
<focus-format>
|
|
3716
|
+
ACTIVE: <runId> <agent> "<task>" branch:<name>
|
|
3717
|
+
PENDING: <taskId> "<description>" depends:<taskId>
|
|
3718
|
+
WAITING: <what> since:HB<N>
|
|
3719
|
+
PROCESSED: <runId> \u2192 <outcome> PR#<N>
|
|
3720
|
+
</focus-format>
|
|
3694
3721
|
|
|
3695
|
-
|
|
3722
|
+
**Notes** (\`notes/\`, via Bash): use for detailed multi-page plans that span multiple heartbeats. After creating a plan, write a focus summary with \`--category "cat notes/<file>"\`. Delete notes when done.`;
|
|
3723
|
+
var MEMORY_RULES_EXAMPLES = `<memory-commands>
|
|
3724
|
+
neo memory write --type focus --expires 2h "ACTIVE: 5900a64a developer 'T1' branch:feat/x"
|
|
3725
|
+
neo memory write --type fact --scope /repo "CI requires pnpm build \u2014 discovered in run abc123"
|
|
3726
|
+
neo memory write --type procedure --scope /repo "Check gh pr view before re-dispatch"
|
|
3727
|
+
neo memory write --type task --scope /repo --severity high --category "neo runs abc123" --tags "initiative:auth-v2,depends:mem_xyz" "T1: Auth middleware"
|
|
3728
|
+
neo memory update <id> --outcome in_progress|done|blocked|abandoned
|
|
3696
3729
|
neo memory forget <id>
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
#### Work queue workflow (Tasks)
|
|
3703
|
-
After architect/refiner output, create tasks with \`neo memory write --type task\`:
|
|
3704
|
-
- Include \`--category\` with the command to retrieve context (\`neo runs <id>\` or \`cat notes/<file>\`)
|
|
3705
|
-
- Use \`--tags depends:mem_<id>\` for task dependencies
|
|
3706
|
-
- Use \`--tags initiative:<name>\` to group tasks across repos
|
|
3707
|
-
- Update status with \`neo memory update <id> --outcome in_progress|done|blocked|abandoned\`
|
|
3708
|
-
- The queue is shown at every heartbeat \u2014 you will not lose track
|
|
3709
|
-
|
|
3710
|
-
#### Pattern escalation
|
|
3711
|
-
When you encounter the same failure or issue 3+ times, ALWAYS write a \`procedure\` memory so you handle it automatically next time. Do not re-discover the same problem repeatedly.
|
|
3712
|
-
|
|
3713
|
-
#### Event deduplication
|
|
3714
|
-
After processing a run completion, record the runId as PROCESSED in your focus. If the same runId appears in future events, skip it \u2014 do not re-analyze.
|
|
3715
|
-
|
|
3716
|
-
**Notes** (\`notes/\`, via Bash): use for detailed multi-page plans, analysis, and checklists that span multiple heartbeats. After creating or reading a plan, write a focus summary: "Plan: <name> | Tasks: T1-T5 | Current: T1 | Next: T2 (depends T1) | Ref: cat notes/<file>". Delete notes when done.`;
|
|
3730
|
+
</memory-commands>`;
|
|
3731
|
+
function getCommandsSection(heartbeatCount) {
|
|
3732
|
+
return heartbeatCount <= 3 ? COMMANDS : COMMANDS_COMPACT;
|
|
3733
|
+
}
|
|
3717
3734
|
function buildContextSections(opts) {
|
|
3718
3735
|
const parts = [];
|
|
3719
3736
|
if (opts.repos.length > 0) {
|
|
@@ -3874,7 +3891,7 @@ function getBasename(scopePath) {
|
|
|
3874
3891
|
const parts = scopePath.split("/");
|
|
3875
3892
|
return parts[parts.length - 1] || scopePath;
|
|
3876
3893
|
}
|
|
3877
|
-
var SIGNIFICANT_TYPES = /* @__PURE__ */ new Set(["decision", "action", "dispatch", "error"
|
|
3894
|
+
var SIGNIFICANT_TYPES = /* @__PURE__ */ new Set(["decision", "action", "dispatch", "error"]);
|
|
3878
3895
|
function buildRecentActionsSection(entries) {
|
|
3879
3896
|
const significant = entries.filter((e) => SIGNIFICANT_TYPES.has(e.type));
|
|
3880
3897
|
if (significant.length === 0) return "";
|
|
@@ -3897,7 +3914,7 @@ function buildEventsSection(grouped) {
|
|
|
3897
3914
|
const { messages, webhooks, runCompletions } = grouped;
|
|
3898
3915
|
const totalEvents = messages.length + webhooks.length + runCompletions.length;
|
|
3899
3916
|
if (totalEvents === 0) {
|
|
3900
|
-
return "No new events.
|
|
3917
|
+
return "No new events.";
|
|
3901
3918
|
}
|
|
3902
3919
|
const parts = [];
|
|
3903
3920
|
for (const msg of messages) {
|
|
@@ -3928,15 +3945,33 @@ ${JSON.stringify(event.data.payload ?? {}, null, 2)}
|
|
|
3928
3945
|
return `Internal event: ${event.eventKind}`;
|
|
3929
3946
|
}
|
|
3930
3947
|
}
|
|
3948
|
+
function isIdleHeartbeat(opts) {
|
|
3949
|
+
const { messages, webhooks, runCompletions } = opts.grouped;
|
|
3950
|
+
const totalEvents = messages.length + webhooks.length + runCompletions.length;
|
|
3951
|
+
const hasWork = buildWorkQueueSection(opts.memories) !== "";
|
|
3952
|
+
return totalEvents === 0 && opts.activeRuns.length === 0 && !hasWork;
|
|
3953
|
+
}
|
|
3954
|
+
function buildIdlePrompt(opts) {
|
|
3955
|
+
return `<role>
|
|
3956
|
+
${ROLE}
|
|
3957
|
+
Heartbeat #${opts.heartbeatCount}
|
|
3958
|
+
</role>
|
|
3959
|
+
|
|
3960
|
+
<context>
|
|
3961
|
+
No events. No active runs. No pending tasks.
|
|
3962
|
+
Budget: $${opts.budgetStatus.todayUsd.toFixed(2)} / $${opts.budgetStatus.capUsd.toFixed(2)} (${opts.budgetStatus.remainingPct.toFixed(0)}% remaining)
|
|
3963
|
+
</context>
|
|
3964
|
+
|
|
3965
|
+
<directive>
|
|
3966
|
+
Nothing to do. Run \`neo log discovery "idle"\` and yield. Do not produce any other output.
|
|
3967
|
+
</directive>`;
|
|
3968
|
+
}
|
|
3931
3969
|
function buildStandardPrompt(opts) {
|
|
3932
3970
|
const sections = [];
|
|
3933
3971
|
sections.push(`<role>
|
|
3934
3972
|
${ROLE}
|
|
3935
3973
|
Heartbeat #${opts.heartbeatCount}
|
|
3936
3974
|
</role>`);
|
|
3937
|
-
sections.push(`<commands>
|
|
3938
|
-
${COMMANDS}
|
|
3939
|
-
</commands>`);
|
|
3940
3975
|
const contextParts = [];
|
|
3941
3976
|
const workQueue = buildWorkQueueSection(opts.memories);
|
|
3942
3977
|
if (workQueue) {
|
|
@@ -3957,16 +3992,21 @@ ${buildEventsSection(opts.grouped)}`);
|
|
|
3957
3992
|
sections.push(`<context>
|
|
3958
3993
|
${contextParts.join("\n\n")}
|
|
3959
3994
|
</context>`);
|
|
3995
|
+
sections.push(`<reference>
|
|
3996
|
+
${getCommandsSection(opts.heartbeatCount)}
|
|
3997
|
+
</reference>`);
|
|
3960
3998
|
const instructionParts = [];
|
|
3961
3999
|
instructionParts.push(HEARTBEAT_RULES);
|
|
3962
4000
|
instructionParts.push(REPORTING_RULES);
|
|
3963
|
-
instructionParts.push(
|
|
4001
|
+
instructionParts.push(MEMORY_RULES_CORE);
|
|
3964
4002
|
if (opts.customInstructions) {
|
|
3965
4003
|
instructionParts.push(`### Custom instructions
|
|
3966
4004
|
${opts.customInstructions}`);
|
|
3967
4005
|
}
|
|
4006
|
+
const { messages, webhooks, runCompletions } = opts.grouped;
|
|
4007
|
+
const hasEvents = messages.length + webhooks.length + runCompletions.length > 0;
|
|
3968
4008
|
instructionParts.push(
|
|
3969
|
-
"
|
|
4009
|
+
hasEvents ? "Process events, dispatch eligible work, yield. Each heartbeat costs ~$0.10 \u2014 be efficient." : "No events. If pending work exists, dispatch it. Otherwise yield immediately."
|
|
3970
4010
|
);
|
|
3971
4011
|
sections.push(`<instructions>
|
|
3972
4012
|
${instructionParts.join("\n\n")}
|
|
@@ -3979,9 +4019,6 @@ function buildConsolidationPrompt(opts) {
|
|
|
3979
4019
|
${ROLE}
|
|
3980
4020
|
Heartbeat #${opts.heartbeatCount} (CONSOLIDATION)
|
|
3981
4021
|
</role>`);
|
|
3982
|
-
sections.push(`<commands>
|
|
3983
|
-
${COMMANDS}
|
|
3984
|
-
</commands>`);
|
|
3985
4022
|
const contextParts = [];
|
|
3986
4023
|
const workQueueConsolidation = buildWorkQueueSection(opts.memories);
|
|
3987
4024
|
if (workQueueConsolidation) {
|
|
@@ -4002,10 +4039,14 @@ ${buildEventsSection(opts.grouped)}`);
|
|
|
4002
4039
|
sections.push(`<context>
|
|
4003
4040
|
${contextParts.join("\n\n")}
|
|
4004
4041
|
</context>`);
|
|
4042
|
+
sections.push(`<reference>
|
|
4043
|
+
${getCommandsSection(opts.heartbeatCount)}
|
|
4044
|
+
</reference>`);
|
|
4005
4045
|
const instructionParts = [];
|
|
4006
4046
|
instructionParts.push(HEARTBEAT_RULES);
|
|
4007
4047
|
instructionParts.push(REPORTING_RULES);
|
|
4008
|
-
instructionParts.push(
|
|
4048
|
+
instructionParts.push(MEMORY_RULES_CORE);
|
|
4049
|
+
instructionParts.push(MEMORY_RULES_EXAMPLES);
|
|
4009
4050
|
if (opts.customInstructions) {
|
|
4010
4051
|
instructionParts.push(`### Custom instructions
|
|
4011
4052
|
${opts.customInstructions}`);
|
|
@@ -4022,13 +4063,7 @@ If there IS active work, your job:
|
|
|
4022
4063
|
2. **Update focus** \u2014 rewrite focus using the MANDATORY structured format (ACTIVE/PENDING/WAITING/PROCESSED). Remove resolved items. Add new context.
|
|
4023
4064
|
3. **Pattern escalation** \u2014 if agents hit the same issue 3+ times (check recent actions), write a \`procedure\` to prevent recurrence.
|
|
4024
4065
|
4. **Prune completed work** \u2014 if a PR is merged or an initiative is done, forget related facts that are no longer actionable. Keep only reusable architectural truths.
|
|
4025
|
-
|
|
4026
|
-
\`\`\`bash
|
|
4027
|
-
neo memory write --type procedure --scope /repo "Before re-dispatching after orphan, check gh pr view first"
|
|
4028
|
-
neo memory write --type focus --expires 4h "ACTIVE: abc123 developer 'T3: prompt injection' branch:feat/task-queue
|
|
4029
|
-
PROCESSED: 5900a64a \u2192 PR#70 APPROVED"
|
|
4030
|
-
neo memory forget <stale-id>
|
|
4031
|
-
\`\`\``
|
|
4066
|
+
5. **Prune done tasks** \u2014 forget tasks with outcome \`done\` or \`abandoned\` older than 7 days.`
|
|
4032
4067
|
);
|
|
4033
4068
|
sections.push(`<instructions>
|
|
4034
4069
|
${instructionParts.join("\n\n")}
|
|
@@ -4041,9 +4076,6 @@ function buildCompactionPrompt(opts) {
|
|
|
4041
4076
|
${ROLE}
|
|
4042
4077
|
Heartbeat #${opts.heartbeatCount} (COMPACTION)
|
|
4043
4078
|
</role>`);
|
|
4044
|
-
sections.push(`<commands>
|
|
4045
|
-
${COMMANDS}
|
|
4046
|
-
</commands>`);
|
|
4047
4079
|
const contextParts = [];
|
|
4048
4080
|
contextParts.push(...buildContextSections(opts));
|
|
4049
4081
|
contextParts.push(buildMemorySection(opts.memories, opts.supervisorDir));
|
|
@@ -4054,10 +4086,14 @@ ${COMMANDS}
|
|
|
4054
4086
|
sections.push(`<context>
|
|
4055
4087
|
${contextParts.join("\n\n")}
|
|
4056
4088
|
</context>`);
|
|
4089
|
+
sections.push(`<reference>
|
|
4090
|
+
${getCommandsSection(opts.heartbeatCount)}
|
|
4091
|
+
</reference>`);
|
|
4057
4092
|
const instructionParts = [];
|
|
4058
4093
|
instructionParts.push(HEARTBEAT_RULES);
|
|
4059
4094
|
instructionParts.push(REPORTING_RULES);
|
|
4060
|
-
instructionParts.push(
|
|
4095
|
+
instructionParts.push(MEMORY_RULES_CORE);
|
|
4096
|
+
instructionParts.push(MEMORY_RULES_EXAMPLES);
|
|
4061
4097
|
if (opts.customInstructions) {
|
|
4062
4098
|
instructionParts.push(`### Custom instructions
|
|
4063
4099
|
${opts.customInstructions}`);
|
|
@@ -4070,8 +4106,9 @@ This is a COMPACTION heartbeat. Deep-clean your ENTIRE memory.
|
|
|
4070
4106
|
3. **Remove trivial facts** \u2014 file counts, line numbers, structural details that \`ls\` or \`cat package.json\` can answer. These waste context.
|
|
4071
4107
|
4. **Merge duplicates** \u2014 combine similar facts within the same scope into one.
|
|
4072
4108
|
5. **Clean up focus** \u2014 forget resolved items, rewrite remaining in structured format.
|
|
4073
|
-
6. **
|
|
4074
|
-
7. **
|
|
4109
|
+
6. **Prune done tasks** \u2014 forget tasks with outcome \`done\` or \`abandoned\` older than 7 days.
|
|
4110
|
+
7. **Delete completed notes** from notes/ directory.
|
|
4111
|
+
8. **Stay under 15 facts per scope** \u2014 prioritize facts that affect dispatch decisions.
|
|
4075
4112
|
|
|
4076
4113
|
Flag contradictions: if two facts contradict, keep the newer one.
|
|
4077
4114
|
|
|
@@ -4382,6 +4419,12 @@ var HeartbeatLoop = class {
|
|
|
4382
4419
|
modeLabel: "consolidation"
|
|
4383
4420
|
};
|
|
4384
4421
|
}
|
|
4422
|
+
if (isIdleHeartbeat(sharedOpts)) {
|
|
4423
|
+
return {
|
|
4424
|
+
prompt: buildIdlePrompt(sharedOpts),
|
|
4425
|
+
modeLabel: "idle"
|
|
4426
|
+
};
|
|
4427
|
+
}
|
|
4385
4428
|
return {
|
|
4386
4429
|
prompt: buildStandardPrompt(sharedOpts),
|
|
4387
4430
|
modeLabel: "standard"
|
|
@@ -4409,7 +4452,6 @@ var HeartbeatLoop = class {
|
|
|
4409
4452
|
}
|
|
4410
4453
|
const queryOptions = {
|
|
4411
4454
|
cwd: homedir2(),
|
|
4412
|
-
maxTurns: 15,
|
|
4413
4455
|
allowedTools,
|
|
4414
4456
|
permissionMode: "bypassPermissions",
|
|
4415
4457
|
allowDangerouslySkipPermissions: true,
|