@ouro.bot/cli 0.1.0-alpha.32 → 0.1.0-alpha.321
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/README.md +188 -190
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/agent.json +3 -2
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/SOUL.md +1 -1
- package/changelog.json +1924 -0
- package/dist/arc/attention-types.js +8 -0
- package/dist/arc/cares.js +140 -0
- package/dist/arc/episodes.js +117 -0
- package/dist/arc/intentions.js +133 -0
- package/dist/arc/json-store.js +117 -0
- package/dist/arc/obligations.js +237 -0
- package/dist/arc/packets.js +193 -0
- package/dist/arc/presence.js +185 -0
- package/dist/arc/task-lifecycle.js +65 -0
- package/dist/heart/active-work.js +832 -0
- package/dist/heart/agent-entry.js +37 -2
- package/dist/heart/attachments/image-normalize.js +194 -0
- package/dist/heart/attachments/materialize.js +97 -0
- package/dist/heart/attachments/originals.js +88 -0
- package/dist/heart/attachments/render.js +29 -0
- package/dist/heart/attachments/sources/adapter.js +2 -0
- package/dist/heart/attachments/sources/bluebubbles.js +156 -0
- package/dist/heart/attachments/sources/cli-local-file.js +78 -0
- package/dist/heart/attachments/sources/index.js +16 -0
- package/dist/heart/attachments/store.js +103 -0
- package/dist/heart/attachments/types.js +93 -0
- package/dist/heart/auth/auth-flow.js +456 -0
- package/dist/heart/bridges/manager.js +358 -0
- package/dist/heart/bridges/state-machine.js +135 -0
- package/dist/heart/bridges/store.js +123 -0
- package/dist/heart/bundle-state.js +168 -0
- package/dist/heart/commitments.js +111 -0
- package/dist/heart/config-registry.js +304 -0
- package/dist/heart/config.js +63 -30
- package/dist/heart/core.js +669 -195
- package/dist/heart/cross-chat-delivery.js +131 -0
- package/dist/heart/daemon/agent-config-check.js +149 -0
- package/dist/heart/daemon/agent-discovery.js +79 -3
- package/dist/heart/daemon/agent-service.js +360 -0
- package/dist/heart/daemon/agentic-repair.js +170 -0
- package/dist/heart/daemon/cadence.js +70 -0
- package/dist/heart/daemon/cli-defaults.js +596 -0
- package/dist/heart/daemon/cli-exec.js +2238 -0
- package/dist/heart/daemon/cli-help.js +306 -0
- package/dist/heart/daemon/cli-parse.js +824 -0
- package/dist/heart/daemon/cli-render-doctor.js +57 -0
- package/dist/heart/daemon/cli-render.js +506 -0
- package/dist/heart/daemon/cli-types.js +8 -0
- package/dist/heart/daemon/daemon-cli.js +29 -1171
- package/dist/heart/daemon/daemon-entry.js +333 -3
- package/dist/heart/daemon/daemon-health.js +137 -0
- package/dist/heart/daemon/daemon-runtime-sync.js +153 -12
- package/dist/heart/daemon/daemon-tombstone.js +236 -0
- package/dist/heart/daemon/daemon.js +751 -58
- package/dist/heart/daemon/doctor-types.js +8 -0
- package/dist/heart/daemon/doctor.js +322 -0
- package/dist/heart/daemon/health-monitor.js +66 -0
- package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
- package/dist/heart/daemon/hooks/bundle-meta.js +115 -1
- package/dist/heart/daemon/http-health-probe.js +80 -0
- package/dist/heart/daemon/inner-status.js +89 -0
- package/dist/heart/daemon/interactive-repair.js +69 -0
- package/dist/heart/daemon/launchd.js +46 -9
- package/dist/heart/daemon/log-tailer.js +82 -12
- package/dist/heart/daemon/logs-prune.js +105 -0
- package/dist/heart/daemon/message-router.js +17 -8
- package/dist/heart/daemon/os-cron-deps.js +134 -0
- package/dist/heart/daemon/ouro-bot-entry.js +1 -1
- package/dist/heart/daemon/process-manager.js +201 -0
- package/dist/heart/daemon/provider-discovery.js +105 -0
- package/dist/heart/daemon/pulse.js +463 -0
- package/dist/heart/daemon/run-hooks.js +2 -0
- package/dist/heart/daemon/runtime-logging.js +67 -16
- package/dist/heart/daemon/runtime-metadata.js +101 -0
- package/dist/heart/daemon/runtime-mode.js +67 -0
- package/dist/heart/daemon/safe-mode.js +161 -0
- package/dist/heart/daemon/sense-manager.js +72 -3
- package/dist/heart/daemon/session-id-resolver.js +131 -0
- package/dist/heart/daemon/skill-management-installer.js +94 -0
- package/dist/heart/daemon/socket-client.js +307 -0
- package/dist/heart/daemon/stale-bundle-prune.js +96 -0
- package/dist/heart/daemon/startup-tui.js +227 -0
- package/dist/heart/daemon/task-scheduler.js +3 -25
- package/dist/heart/daemon/thoughts.js +510 -0
- package/dist/heart/daemon/up-progress.js +135 -0
- package/dist/heart/delegation.js +62 -0
- package/dist/heart/habits/habit-migration.js +181 -0
- package/dist/heart/habits/habit-parser.js +140 -0
- package/dist/heart/habits/habit-scheduler.js +371 -0
- package/dist/heart/{daemon → hatch}/hatch-flow.js +30 -120
- package/dist/heart/{daemon → hatch}/hatch-specialist.js +3 -3
- package/dist/heart/{daemon → hatch}/specialist-prompt.js +10 -7
- package/dist/heart/{daemon → hatch}/specialist-tools.js +49 -3
- package/dist/heart/identity.js +163 -60
- package/dist/heart/kicks.js +2 -20
- package/dist/heart/mcp/mcp-server.js +653 -0
- package/dist/heart/migrate-config.js +127 -0
- package/dist/heart/model-capabilities.js +59 -0
- package/dist/heart/outlook/outlook-http.js +439 -0
- package/dist/heart/outlook/outlook-read.js +28 -0
- package/dist/heart/outlook/outlook-render.js +1032 -0
- package/dist/heart/outlook/outlook-types.js +27 -0
- package/dist/heart/outlook/outlook-view.js +194 -0
- package/dist/heart/outlook/readers/agent-machine.js +355 -0
- package/dist/heart/outlook/readers/continuity-readers.js +332 -0
- package/dist/heart/outlook/readers/runtime-readers.js +660 -0
- package/dist/heart/outlook/readers/sessions.js +231 -0
- package/dist/heart/outlook/readers/shared.js +111 -0
- package/dist/heart/progress-story.js +42 -0
- package/dist/heart/provider-failover.js +88 -0
- package/dist/heart/provider-ping.js +162 -0
- package/dist/heart/providers/anthropic-token.js +163 -0
- package/dist/heart/providers/anthropic.js +169 -46
- package/dist/heart/providers/azure.js +98 -11
- package/dist/heart/providers/error-classification.js +63 -0
- package/dist/heart/providers/github-copilot.js +136 -0
- package/dist/heart/providers/minimax-vlm.js +189 -0
- package/dist/heart/providers/minimax.js +23 -5
- package/dist/heart/providers/openai-codex.js +33 -22
- package/dist/heart/session-activity.js +190 -0
- package/dist/heart/session-events.js +726 -0
- package/dist/heart/session-recall.js +162 -0
- package/dist/heart/start-of-turn-packet.js +341 -0
- package/dist/heart/streaming.js +36 -27
- package/dist/heart/sync.js +332 -0
- package/dist/heart/target-resolution.js +127 -0
- package/dist/heart/tempo.js +93 -0
- package/dist/heart/temporal-view.js +41 -0
- package/dist/heart/tool-activity-callbacks.js +36 -0
- package/dist/heart/tool-description.js +135 -0
- package/dist/heart/tool-friction.js +55 -0
- package/dist/heart/tool-loop.js +200 -0
- package/dist/heart/turn-context.js +358 -0
- package/dist/heart/turn-coordinator.js +28 -0
- package/dist/heart/{daemon → versioning}/ouro-bot-global-installer.js +1 -1
- package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +1 -1
- package/dist/heart/{daemon → versioning}/ouro-path-installer.js +78 -35
- package/dist/heart/versioning/ouro-version-manager.js +295 -0
- package/dist/heart/{daemon → versioning}/staged-restart.js +40 -8
- package/dist/heart/{daemon → versioning}/update-checker.js +12 -2
- package/dist/heart/{daemon → versioning}/update-hooks.js +63 -59
- package/dist/mind/associative-recall.js +137 -66
- package/dist/mind/bundle-manifest.js +8 -1
- package/dist/mind/context.js +89 -93
- package/dist/mind/diary-integrity.js +60 -0
- package/dist/mind/{memory.js → diary.js} +84 -96
- package/dist/mind/embedding-provider.js +60 -0
- package/dist/mind/file-state.js +179 -0
- package/dist/mind/first-impressions.js +14 -1
- package/dist/mind/friends/channel.js +56 -0
- package/dist/mind/friends/group-context.js +144 -0
- package/dist/mind/friends/resolver.js +37 -0
- package/dist/mind/friends/store-file.js +58 -3
- package/dist/mind/friends/trust-explanation.js +74 -0
- package/dist/mind/friends/types.js +8 -0
- package/dist/mind/journal-index.js +161 -0
- package/dist/mind/obligation-steering.js +221 -0
- package/dist/mind/pending.js +76 -9
- package/dist/mind/prompt.js +950 -113
- package/dist/mind/provenance-trust.js +26 -0
- package/dist/mind/scrutiny.js +173 -0
- package/dist/mind/token-estimate.js +8 -12
- package/dist/nerves/cli-logging.js +7 -1
- package/dist/nerves/coverage/audit.js +1 -1
- package/dist/nerves/coverage/file-completeness.js +76 -5
- package/dist/nerves/coverage/run-artifacts.js +1 -1
- package/dist/nerves/event-buffer.js +111 -0
- package/dist/nerves/index.js +224 -4
- package/dist/nerves/observation.js +20 -0
- package/dist/nerves/redact.js +79 -0
- package/dist/nerves/runtime.js +5 -1
- package/dist/outlook-ui/assets/index-IuR4F6y6.js +61 -0
- package/dist/outlook-ui/assets/index-LwChZTgL.css +1 -0
- package/dist/outlook-ui/index.html +15 -0
- package/dist/repertoire/ado-client.js +15 -56
- package/dist/repertoire/ado-semantic.js +11 -10
- package/dist/repertoire/api-client.js +97 -0
- package/dist/repertoire/bitwarden-store.js +319 -0
- package/dist/repertoire/bundle-templates.js +72 -0
- package/dist/repertoire/bw-installer.js +79 -0
- package/dist/repertoire/coding/codex-jsonl.js +64 -0
- package/dist/repertoire/coding/context-pack.js +330 -0
- package/dist/repertoire/coding/feedback.js +197 -30
- package/dist/repertoire/coding/manager.js +159 -11
- package/dist/repertoire/coding/spawner.js +55 -9
- package/dist/repertoire/coding/tools.js +170 -7
- package/dist/repertoire/commerce-errors.js +109 -0
- package/dist/repertoire/commerce-self-test.js +156 -0
- package/dist/repertoire/credential-access.js +527 -0
- package/dist/repertoire/duffel-client.js +185 -0
- package/dist/repertoire/github-client.js +14 -55
- package/dist/repertoire/graph-client.js +11 -52
- package/dist/repertoire/guardrails.js +375 -0
- package/dist/repertoire/mcp-client.js +255 -0
- package/dist/repertoire/mcp-manager.js +305 -0
- package/dist/repertoire/mcp-tools.js +63 -0
- package/dist/repertoire/shell-sessions.js +133 -0
- package/dist/repertoire/skills.js +14 -23
- package/dist/repertoire/stripe-client.js +131 -0
- package/dist/repertoire/tasks/board.js +43 -5
- package/dist/repertoire/tasks/fix.js +182 -0
- package/dist/repertoire/tasks/index.js +28 -10
- package/dist/repertoire/tasks/lifecycle.js +2 -2
- package/dist/repertoire/tasks/parser.js +3 -2
- package/dist/repertoire/tasks/scanner.js +194 -37
- package/dist/repertoire/tasks/transitions.js +16 -79
- package/dist/repertoire/tool-results.js +29 -0
- package/dist/repertoire/tools-attachments.js +316 -0
- package/dist/repertoire/tools-base.js +45 -771
- package/dist/repertoire/tools-bluebubbles.js +1 -0
- package/dist/repertoire/tools-bridge.js +141 -0
- package/dist/repertoire/tools-bundle.js +984 -0
- package/dist/repertoire/tools-config.js +185 -0
- package/dist/repertoire/tools-continuity.js +248 -0
- package/dist/repertoire/tools-credential.js +182 -0
- package/dist/repertoire/tools-files.js +342 -0
- package/dist/repertoire/tools-flight.js +224 -0
- package/dist/repertoire/tools-flow.js +105 -0
- package/dist/repertoire/tools-github.js +1 -7
- package/dist/repertoire/tools-memory.js +376 -0
- package/dist/repertoire/tools-session.js +739 -0
- package/dist/repertoire/tools-shell.js +120 -0
- package/dist/repertoire/tools-stripe.js +180 -0
- package/dist/repertoire/tools-surface.js +243 -0
- package/dist/repertoire/tools-teams.js +12 -62
- package/dist/repertoire/tools-travel.js +125 -0
- package/dist/repertoire/tools-user-profile.js +144 -0
- package/dist/repertoire/tools-vault.js +110 -0
- package/dist/repertoire/tools.js +144 -138
- package/dist/repertoire/travel-api-client.js +360 -0
- package/dist/repertoire/user-profile.js +118 -0
- package/dist/repertoire/vault-setup.js +241 -0
- package/dist/scripts/claude-code-hook.js +41 -0
- package/dist/scripts/claude-code-stop-hook.js +47 -0
- package/dist/senses/attention-queue.js +116 -0
- package/dist/senses/bluebubbles/attachment-cache.js +53 -0
- package/dist/senses/bluebubbles/attachment-download.js +137 -0
- package/dist/senses/{bluebubbles-client.js → bluebubbles/client.js} +143 -9
- package/dist/senses/bluebubbles/entry.js +13 -0
- package/dist/senses/bluebubbles/inbound-log.js +113 -0
- package/dist/senses/bluebubbles/index.js +1436 -0
- package/dist/senses/{bluebubbles-media.js → bluebubbles/media.js} +121 -70
- package/dist/senses/{bluebubbles-model.js → bluebubbles/model.js} +43 -12
- package/dist/senses/{bluebubbles-mutation-log.js → bluebubbles/mutation-log.js} +46 -6
- package/dist/senses/bluebubbles/replay.js +129 -0
- package/dist/senses/bluebubbles/runtime-state.js +109 -0
- package/dist/senses/{bluebubbles-session-cleanup.js → bluebubbles/session-cleanup.js} +1 -1
- package/dist/senses/cli/bracketed-paste.js +82 -0
- package/dist/senses/cli/image-paste.js +287 -0
- package/dist/senses/cli/image-ref-navigation.js +75 -0
- package/dist/senses/cli/ink-app.js +156 -0
- package/dist/senses/cli/inline-diff.js +64 -0
- package/dist/senses/cli/input-keys.js +174 -0
- package/dist/senses/cli/kill-ring.js +86 -0
- package/dist/senses/cli/message-list.js +51 -0
- package/dist/senses/cli/ouro-tui.js +605 -0
- package/dist/senses/cli/spinner-imperative.js +135 -0
- package/dist/senses/cli/spinner.js +101 -0
- package/dist/senses/cli/status-line.js +60 -0
- package/dist/senses/cli/streaming-markdown.js +526 -0
- package/dist/senses/cli/tool-display.js +83 -0
- package/dist/senses/cli/tool-render.js +85 -0
- package/dist/senses/cli/tui-store.js +240 -0
- package/dist/senses/cli/virtual-list.js +35 -0
- package/dist/senses/cli-entry.js +1 -1
- package/dist/senses/cli-layout.js +187 -0
- package/dist/senses/cli.js +595 -246
- package/dist/senses/commands.js +65 -1
- package/dist/senses/continuity.js +94 -0
- package/dist/senses/habit-turn-message.js +108 -0
- package/dist/senses/inner-dialog-worker.js +112 -19
- package/dist/senses/inner-dialog.js +633 -86
- package/dist/senses/pipeline.js +565 -0
- package/dist/senses/shared-turn.js +199 -0
- package/dist/senses/surface-tool.js +68 -0
- package/dist/senses/teams.js +666 -166
- package/dist/senses/trust-gate.js +112 -2
- package/package.json +27 -7
- package/skills/agent-commerce.md +106 -0
- package/skills/browser-navigation.md +110 -0
- package/skills/commerce-setup-guide.md +116 -0
- package/skills/commerce-setup.md +84 -0
- package/skills/configure-dev-tools.md +81 -0
- package/skills/travel-planning.md +138 -0
- package/dist/heart/daemon/subagent-installer.js +0 -134
- package/dist/senses/bluebubbles-entry.js +0 -11
- package/dist/senses/bluebubbles.js +0 -544
- package/dist/senses/debug-activity.js +0 -108
- package/subagents/README.md +0 -73
- package/subagents/work-doer.md +0 -235
- package/subagents/work-merger.md +0 -618
- package/subagents/work-planner.md +0 -382
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/python.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-serpent.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
- /package/dist/heart/{daemon → hatch}/hatch-animation.js +0 -0
- /package/dist/heart/{daemon → hatch}/specialist-orchestrator.js +0 -0
- /package/dist/heart/{daemon → versioning}/ouro-uti.js +0 -0
- /package/dist/heart/{daemon → versioning}/wrapper-publish-guard.js +0 -0
|
@@ -35,21 +35,15 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.CodingSessionManager = void 0;
|
|
37
37
|
const fs = __importStar(require("fs"));
|
|
38
|
-
const os = __importStar(require("os"));
|
|
39
38
|
const path = __importStar(require("path"));
|
|
40
39
|
const identity_1 = require("../../heart/identity");
|
|
41
40
|
const runtime_1 = require("../../nerves/runtime");
|
|
42
41
|
const spawner_1 = require("./spawner");
|
|
43
|
-
function safeAgentName() {
|
|
44
|
-
try {
|
|
45
|
-
return (0, identity_1.getAgentName)();
|
|
46
|
-
}
|
|
47
|
-
catch {
|
|
48
|
-
return "default";
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
42
|
function defaultStateFilePath(agentName) {
|
|
52
|
-
return path.join(
|
|
43
|
+
return path.join((0, identity_1.getAgentRoot)(agentName), "state", "coding", "sessions.json");
|
|
44
|
+
}
|
|
45
|
+
function defaultArtifactDirPath(agentName) {
|
|
46
|
+
return path.join((0, identity_1.getAgentRoot)(agentName), "state", "coding", "sessions");
|
|
53
47
|
}
|
|
54
48
|
function isPidAlive(pid) {
|
|
55
49
|
try {
|
|
@@ -63,6 +57,9 @@ function isPidAlive(pid) {
|
|
|
63
57
|
function cloneSession(session) {
|
|
64
58
|
return {
|
|
65
59
|
...session,
|
|
60
|
+
originSession: session.originSession ? { ...session.originSession } : undefined,
|
|
61
|
+
checkpoint: session.checkpoint ?? null,
|
|
62
|
+
artifactPath: session.artifactPath,
|
|
66
63
|
stdoutTail: session.stdoutTail,
|
|
67
64
|
stderrTail: session.stderrTail,
|
|
68
65
|
failure: session.failure
|
|
@@ -86,6 +83,46 @@ function appendTail(existing, nextChunk, maxLength = 2000) {
|
|
|
86
83
|
const combined = `${existing}${nextChunk}`;
|
|
87
84
|
return combined.length <= maxLength ? combined : combined.slice(combined.length - maxLength);
|
|
88
85
|
}
|
|
86
|
+
function compactText(text) {
|
|
87
|
+
return text.replace(/\s+/g, " ").trim();
|
|
88
|
+
}
|
|
89
|
+
function clipText(text, maxLength = 240) {
|
|
90
|
+
return text.length <= maxLength ? text : `${text.slice(0, maxLength - 3)}...`;
|
|
91
|
+
}
|
|
92
|
+
function latestMeaningfulLine(text) {
|
|
93
|
+
const lines = text
|
|
94
|
+
.split(/\r?\n/)
|
|
95
|
+
.map((line) => compactText(line))
|
|
96
|
+
.filter(Boolean);
|
|
97
|
+
if (lines.length === 0)
|
|
98
|
+
return null;
|
|
99
|
+
return clipText(lines.at(-1));
|
|
100
|
+
}
|
|
101
|
+
function fallbackCheckpoint(status, code, signal) {
|
|
102
|
+
switch (status) {
|
|
103
|
+
case "waiting_input":
|
|
104
|
+
return "needs input";
|
|
105
|
+
case "stalled":
|
|
106
|
+
return "no recent output";
|
|
107
|
+
case "completed":
|
|
108
|
+
return "completed";
|
|
109
|
+
case "failed":
|
|
110
|
+
if (code !== null)
|
|
111
|
+
return `exit code ${code}`;
|
|
112
|
+
if (signal)
|
|
113
|
+
return `terminated by ${signal}`;
|
|
114
|
+
return "failed";
|
|
115
|
+
case "killed":
|
|
116
|
+
return "terminated by parent agent";
|
|
117
|
+
default:
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function deriveCheckpoint(session) {
|
|
122
|
+
return (latestMeaningfulLine(session.stderrTail)
|
|
123
|
+
?? latestMeaningfulLine(session.stdoutTail)
|
|
124
|
+
?? fallbackCheckpoint(session.status, session.lastExitCode, session.lastSignal));
|
|
125
|
+
}
|
|
89
126
|
function isSpawnCodingResult(value) {
|
|
90
127
|
return typeof value === "object" && value !== null && "process" in value;
|
|
91
128
|
}
|
|
@@ -123,6 +160,7 @@ class CodingSessionManager {
|
|
|
123
160
|
maxRestarts;
|
|
124
161
|
defaultStallThresholdMs;
|
|
125
162
|
stateFilePath;
|
|
163
|
+
artifactDirPath;
|
|
126
164
|
existsSync;
|
|
127
165
|
readFileSync;
|
|
128
166
|
writeFileSync;
|
|
@@ -140,8 +178,19 @@ class CodingSessionManager {
|
|
|
140
178
|
this.writeFileSync = options.writeFileSync ?? fs.writeFileSync;
|
|
141
179
|
this.mkdirSync = options.mkdirSync ?? fs.mkdirSync;
|
|
142
180
|
this.pidAlive = options.pidAlive ?? isPidAlive;
|
|
143
|
-
|
|
181
|
+
// No silent fallback to "default" — if there's no agentName and no
|
|
182
|
+
// explicit option, getAgentName() throws. The previous `safeAgentName`
|
|
183
|
+
// helper fell back to "default" and ended up writing coding session
|
|
184
|
+
// state to `~/AgentBundles/default.ouro/state/coding/sessions.json` on
|
|
185
|
+
// every vitest run because the coding manager singleton constructs with
|
|
186
|
+
// `{}`. That leaked real-fs state into the developer's home directory
|
|
187
|
+
// on every coverage run. Production callers always pass via argv (see
|
|
188
|
+
// getAgentName); test callers must either pass `agentName` explicitly
|
|
189
|
+
// or mock `../../heart/identity`.
|
|
190
|
+
this.agentName = options.agentName ?? (0, identity_1.getAgentName)();
|
|
144
191
|
this.stateFilePath = options.stateFilePath ?? defaultStateFilePath(this.agentName);
|
|
192
|
+
this.artifactDirPath = options.artifactDirPath
|
|
193
|
+
?? (options.stateFilePath ? path.dirname(options.stateFilePath) : defaultArtifactDirPath(this.agentName));
|
|
145
194
|
this.loadPersistedState();
|
|
146
195
|
}
|
|
147
196
|
async spawnSession(request) {
|
|
@@ -158,8 +207,12 @@ class CodingSessionManager {
|
|
|
158
207
|
runner: normalizedRequest.runner,
|
|
159
208
|
workdir: normalizedRequest.workdir,
|
|
160
209
|
taskRef: normalizedRequest.taskRef,
|
|
210
|
+
originSession: normalizedRequest.originSession ? { ...normalizedRequest.originSession } : undefined,
|
|
211
|
+
obligationId: normalizedRequest.obligationId,
|
|
161
212
|
scopeFile: normalizedRequest.scopeFile,
|
|
162
213
|
stateFile: normalizedRequest.stateFile,
|
|
214
|
+
checkpoint: null,
|
|
215
|
+
artifactPath: this.artifactPathFor(id),
|
|
163
216
|
status: "spawning",
|
|
164
217
|
stdoutTail: "",
|
|
165
218
|
stderrTail: "",
|
|
@@ -246,6 +299,7 @@ class CodingSessionManager {
|
|
|
246
299
|
record.process.kill("SIGTERM");
|
|
247
300
|
record.process = null;
|
|
248
301
|
record.session.status = "killed";
|
|
302
|
+
record.session.checkpoint = "terminated by parent agent";
|
|
249
303
|
record.session.endedAt = this.nowIso();
|
|
250
304
|
(0, runtime_1.emitNervesEvent)({
|
|
251
305
|
component: "repertoire",
|
|
@@ -268,6 +322,7 @@ class CodingSessionManager {
|
|
|
268
322
|
continue;
|
|
269
323
|
stalled += 1;
|
|
270
324
|
record.session.status = "stalled";
|
|
325
|
+
record.session.checkpoint = deriveCheckpoint(record.session);
|
|
271
326
|
(0, runtime_1.emitNervesEvent)({
|
|
272
327
|
level: "warn",
|
|
273
328
|
component: "repertoire",
|
|
@@ -293,6 +348,7 @@ class CodingSessionManager {
|
|
|
293
348
|
record.process = null;
|
|
294
349
|
if (record.session.status === "running" || record.session.status === "spawning") {
|
|
295
350
|
record.session.status = "killed";
|
|
351
|
+
record.session.checkpoint = "terminated during manager shutdown";
|
|
296
352
|
record.session.endedAt = this.nowIso();
|
|
297
353
|
}
|
|
298
354
|
}
|
|
@@ -337,6 +393,13 @@ class CodingSessionManager {
|
|
|
337
393
|
record.session.endedAt = this.nowIso();
|
|
338
394
|
updateKind = "completed";
|
|
339
395
|
}
|
|
396
|
+
const checkpoint = latestMeaningfulLine(text);
|
|
397
|
+
if (checkpoint) {
|
|
398
|
+
record.session.checkpoint = checkpoint;
|
|
399
|
+
}
|
|
400
|
+
else if (!record.session.checkpoint) {
|
|
401
|
+
record.session.checkpoint = deriveCheckpoint(record.session);
|
|
402
|
+
}
|
|
340
403
|
(0, runtime_1.emitNervesEvent)({
|
|
341
404
|
component: "repertoire",
|
|
342
405
|
event: "repertoire.coding_session_output",
|
|
@@ -360,12 +423,14 @@ class CodingSessionManager {
|
|
|
360
423
|
record.session.lastSignal = signal;
|
|
361
424
|
if (record.session.status === "killed" || record.session.status === "completed") {
|
|
362
425
|
record.session.endedAt = this.nowIso();
|
|
426
|
+
record.session.checkpoint = deriveCheckpoint(record.session);
|
|
363
427
|
this.persistState();
|
|
364
428
|
return;
|
|
365
429
|
}
|
|
366
430
|
if (code === 0) {
|
|
367
431
|
record.session.status = "completed";
|
|
368
432
|
record.session.endedAt = this.nowIso();
|
|
433
|
+
record.session.checkpoint = deriveCheckpoint(record.session);
|
|
369
434
|
this.persistState();
|
|
370
435
|
this.notifyListeners(record.session.id, { kind: "completed", session: cloneSession(record.session) });
|
|
371
436
|
return;
|
|
@@ -377,6 +442,7 @@ class CodingSessionManager {
|
|
|
377
442
|
record.session.status = "failed";
|
|
378
443
|
record.session.endedAt = this.nowIso();
|
|
379
444
|
record.session.failure = defaultFailureDiagnostics(code, signal, record.command, record.args, record.stdoutTail, record.stderrTail);
|
|
445
|
+
record.session.checkpoint = deriveCheckpoint(record.session);
|
|
380
446
|
(0, runtime_1.emitNervesEvent)({
|
|
381
447
|
level: "error",
|
|
382
448
|
component: "repertoire",
|
|
@@ -402,6 +468,7 @@ class CodingSessionManager {
|
|
|
402
468
|
record.session.lastActivityAt = this.nowIso();
|
|
403
469
|
record.session.endedAt = null;
|
|
404
470
|
record.session.failure = null;
|
|
471
|
+
record.session.checkpoint = `restarted after ${reason}`;
|
|
405
472
|
this.attachProcessListeners(record);
|
|
406
473
|
(0, runtime_1.emitNervesEvent)({
|
|
407
474
|
level: "warn",
|
|
@@ -483,15 +550,21 @@ class CodingSessionManager {
|
|
|
483
550
|
}
|
|
484
551
|
const normalizedRequest = {
|
|
485
552
|
...request,
|
|
553
|
+
originSession: request.originSession ? { ...request.originSession } : undefined,
|
|
486
554
|
sessionId: request.sessionId ?? session.id,
|
|
555
|
+
obligationId: request.obligationId,
|
|
487
556
|
parentAgent: request.parentAgent ?? this.agentName,
|
|
488
557
|
};
|
|
489
558
|
const normalizedSession = {
|
|
490
559
|
...session,
|
|
491
560
|
taskRef: session.taskRef ?? normalizedRequest.taskRef,
|
|
561
|
+
originSession: session.originSession ?? normalizedRequest.originSession,
|
|
562
|
+
obligationId: session.obligationId ?? normalizedRequest.obligationId,
|
|
492
563
|
failure: session.failure ?? null,
|
|
493
564
|
stdoutTail: session.stdoutTail ?? session.failure?.stdoutTail ?? "",
|
|
494
565
|
stderrTail: session.stderrTail ?? session.failure?.stderrTail ?? "",
|
|
566
|
+
checkpoint: typeof session.checkpoint === "string" ? session.checkpoint : null,
|
|
567
|
+
artifactPath: typeof session.artifactPath === "string" ? session.artifactPath : this.artifactPathFor(session.id),
|
|
495
568
|
};
|
|
496
569
|
if (typeof normalizedSession.pid === "number") {
|
|
497
570
|
const alive = this.pidAlive(normalizedSession.pid);
|
|
@@ -504,6 +577,7 @@ class CodingSessionManager {
|
|
|
504
577
|
normalizedSession.pid = null;
|
|
505
578
|
}
|
|
506
579
|
}
|
|
580
|
+
normalizedSession.checkpoint = normalizedSession.checkpoint ?? deriveCheckpoint(normalizedSession);
|
|
507
581
|
this.records.set(normalizedSession.id, {
|
|
508
582
|
request: normalizedRequest,
|
|
509
583
|
session: normalizedSession,
|
|
@@ -543,6 +617,80 @@ class CodingSessionManager {
|
|
|
543
617
|
meta: { path: this.stateFilePath, reason: error instanceof Error ? error.message : String(error) },
|
|
544
618
|
});
|
|
545
619
|
}
|
|
620
|
+
this.persistArtifacts();
|
|
621
|
+
}
|
|
622
|
+
artifactPathFor(sessionId) {
|
|
623
|
+
return path.join(this.artifactDirPath, `${sessionId}.md`);
|
|
624
|
+
}
|
|
625
|
+
renderArtifact(record) {
|
|
626
|
+
const { request, session } = record;
|
|
627
|
+
const stdout = session.stdoutTail.trim() || "(empty)";
|
|
628
|
+
const stderr = session.stderrTail.trim() || "(empty)";
|
|
629
|
+
const lines = [
|
|
630
|
+
"# Coding Session Artifact",
|
|
631
|
+
"",
|
|
632
|
+
"## Session",
|
|
633
|
+
`id: ${session.id}`,
|
|
634
|
+
`runner: ${session.runner}`,
|
|
635
|
+
`status: ${session.status}`,
|
|
636
|
+
`taskRef: ${session.taskRef ?? "unassigned"}`,
|
|
637
|
+
`workdir: ${session.workdir}`,
|
|
638
|
+
`startedAt: ${session.startedAt}`,
|
|
639
|
+
`lastActivityAt: ${session.lastActivityAt}`,
|
|
640
|
+
`endedAt: ${session.endedAt ?? "active"}`,
|
|
641
|
+
`pid: ${session.pid ?? "none"}`,
|
|
642
|
+
`restarts: ${session.restartCount}`,
|
|
643
|
+
`checkpoint: ${session.checkpoint ?? "none"}`,
|
|
644
|
+
`scopeFile: ${session.scopeFile ?? "none"}`,
|
|
645
|
+
`stateFile: ${session.stateFile ?? "none"}`,
|
|
646
|
+
"",
|
|
647
|
+
"## Request",
|
|
648
|
+
request.prompt,
|
|
649
|
+
"",
|
|
650
|
+
"## Stdout Tail",
|
|
651
|
+
stdout,
|
|
652
|
+
"",
|
|
653
|
+
"## Stderr Tail",
|
|
654
|
+
stderr,
|
|
655
|
+
];
|
|
656
|
+
if (session.failure) {
|
|
657
|
+
lines.push("", "## Failure", `command: ${session.failure.command}`, `args: ${session.failure.args.join(" ") || "(none)"}`, `code: ${session.failure.code ?? "null"}`, `signal: ${session.failure.signal ?? "null"}`);
|
|
658
|
+
}
|
|
659
|
+
return `${lines.join("\n")}\n`;
|
|
660
|
+
}
|
|
661
|
+
persistArtifacts() {
|
|
662
|
+
try {
|
|
663
|
+
this.mkdirSync(this.artifactDirPath, { recursive: true });
|
|
664
|
+
}
|
|
665
|
+
catch (error) {
|
|
666
|
+
(0, runtime_1.emitNervesEvent)({
|
|
667
|
+
level: "warn",
|
|
668
|
+
component: "repertoire",
|
|
669
|
+
event: "repertoire.coding_artifact_persist_error",
|
|
670
|
+
message: "failed preparing coding artifact directory",
|
|
671
|
+
meta: { path: this.artifactDirPath, reason: error instanceof Error ? error.message : String(error) },
|
|
672
|
+
});
|
|
673
|
+
return;
|
|
674
|
+
}
|
|
675
|
+
for (const record of this.records.values()) {
|
|
676
|
+
try {
|
|
677
|
+
record.session.artifactPath = record.session.artifactPath ?? this.artifactPathFor(record.session.id);
|
|
678
|
+
this.writeFileSync(record.session.artifactPath, this.renderArtifact(record), "utf-8");
|
|
679
|
+
}
|
|
680
|
+
catch (error) {
|
|
681
|
+
(0, runtime_1.emitNervesEvent)({
|
|
682
|
+
level: "warn",
|
|
683
|
+
component: "repertoire",
|
|
684
|
+
event: "repertoire.coding_artifact_persist_error",
|
|
685
|
+
message: "failed writing coding session artifact",
|
|
686
|
+
meta: {
|
|
687
|
+
id: record.session.id,
|
|
688
|
+
path: record.session.artifactPath ?? this.artifactPathFor(record.session.id),
|
|
689
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
690
|
+
},
|
|
691
|
+
});
|
|
692
|
+
}
|
|
693
|
+
}
|
|
546
694
|
}
|
|
547
695
|
}
|
|
548
696
|
exports.CodingSessionManager = CodingSessionManager;
|
|
@@ -36,8 +36,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.spawnCodingProcess = spawnCodingProcess;
|
|
37
37
|
const child_process_1 = require("child_process");
|
|
38
38
|
const fs = __importStar(require("fs"));
|
|
39
|
+
const os = __importStar(require("os"));
|
|
40
|
+
const path = __importStar(require("path"));
|
|
39
41
|
const runtime_1 = require("../../nerves/runtime");
|
|
40
|
-
function buildCommandArgs(runner, workdir) {
|
|
42
|
+
function buildCommandArgs(runner, workdir, parentAgent) {
|
|
41
43
|
if (runner === "claude") {
|
|
42
44
|
return {
|
|
43
45
|
command: "claude",
|
|
@@ -53,25 +55,65 @@ function buildCommandArgs(runner, workdir) {
|
|
|
53
55
|
],
|
|
54
56
|
};
|
|
55
57
|
}
|
|
58
|
+
const agent = parentAgent ?? "unknown";
|
|
59
|
+
// Use absolute path to ouro-entry.js so MCP works in both dev and installed mode
|
|
60
|
+
// __dirname at runtime is dist/repertoire/coding/ — go up 2 levels to dist/
|
|
61
|
+
const distRoot = path.resolve(__dirname, "..", "..");
|
|
62
|
+
const ouroEntryPath = path.join(distRoot, "heart", "daemon", "ouro-entry.js");
|
|
56
63
|
return {
|
|
57
64
|
command: "codex",
|
|
58
|
-
args: [
|
|
65
|
+
args: [
|
|
66
|
+
"exec",
|
|
67
|
+
"--skip-git-repo-check",
|
|
68
|
+
"--cd",
|
|
69
|
+
workdir,
|
|
70
|
+
"--ephemeral",
|
|
71
|
+
"--json",
|
|
72
|
+
"-c",
|
|
73
|
+
`mcp_servers.ouro.command=node`,
|
|
74
|
+
"-c",
|
|
75
|
+
`mcp_servers.ouro.args=["${ouroEntryPath}","mcp-serve","--agent","${agent}"]`,
|
|
76
|
+
],
|
|
59
77
|
};
|
|
60
78
|
}
|
|
79
|
+
function buildSpawnEnv(baseEnv, homeDir) {
|
|
80
|
+
const binDir = path.join(homeDir, ".ouro-cli", "bin");
|
|
81
|
+
const existingPath = baseEnv.PATH ?? "";
|
|
82
|
+
const pathEntries = existingPath.split(path.delimiter).filter((entry) => entry.length > 0);
|
|
83
|
+
if (!pathEntries.includes(binDir)) {
|
|
84
|
+
pathEntries.unshift(binDir);
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
...baseEnv,
|
|
88
|
+
PATH: pathEntries.join(path.delimiter),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function appendFileSection(sections, label, filePath, deps) {
|
|
92
|
+
if (!filePath || !deps.existsSync(filePath))
|
|
93
|
+
return;
|
|
94
|
+
const content = deps.readFileSync(filePath, "utf-8").trim();
|
|
95
|
+
if (content.length === 0)
|
|
96
|
+
return;
|
|
97
|
+
sections.push(`${label} (${filePath}):\n${content}`);
|
|
98
|
+
}
|
|
61
99
|
function buildPrompt(request, deps) {
|
|
62
100
|
const sections = [];
|
|
101
|
+
sections.push([
|
|
102
|
+
"Execution contract:",
|
|
103
|
+
"- You are a subordinate coding session launched by a parent Ouro agent.",
|
|
104
|
+
"- Execute the concrete request in the supplied workdir directly.",
|
|
105
|
+
"- Do not switch into planning/doing workflows, approval gates, or repo-management rituals unless the request explicitly asks for them.",
|
|
106
|
+
"- Treat the request, scope file, and state file as the authoritative briefing for this session.",
|
|
107
|
+
"- Prefer direct execution and verification over narration.",
|
|
108
|
+
].join("\n"));
|
|
63
109
|
sections.push([
|
|
64
110
|
"Coding session metadata:",
|
|
65
111
|
`sessionId: ${request.sessionId ?? "pending"}`,
|
|
66
112
|
`parentAgent: ${request.parentAgent ?? "unknown"}`,
|
|
67
113
|
`taskRef: ${request.taskRef ?? "unassigned"}`,
|
|
68
114
|
].join("\n"));
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
if (stateContent.length > 0) {
|
|
72
|
-
sections.push(`State file (${request.stateFile}):\n${stateContent}`);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
115
|
+
appendFileSection(sections, "Scope file", request.scopeFile, deps);
|
|
116
|
+
appendFileSection(sections, "State file", request.stateFile, deps);
|
|
75
117
|
sections.push(request.prompt);
|
|
76
118
|
return sections.join("\n\n---\n\n");
|
|
77
119
|
}
|
|
@@ -79,8 +121,11 @@ function spawnCodingProcess(request, deps = {}) {
|
|
|
79
121
|
const spawnFn = deps.spawnFn ?? ((command, args, options) => (0, child_process_1.spawn)(command, args, options));
|
|
80
122
|
const existsSync = deps.existsSync ?? fs.existsSync;
|
|
81
123
|
const readFileSync = deps.readFileSync ?? fs.readFileSync;
|
|
124
|
+
const homeDir = deps.homeDir ?? os.homedir();
|
|
125
|
+
const baseEnv = deps.baseEnv ?? process.env;
|
|
82
126
|
const prompt = buildPrompt(request, { existsSync, readFileSync });
|
|
83
|
-
const { command, args } = buildCommandArgs(request.runner, request.workdir);
|
|
127
|
+
const { command, args } = buildCommandArgs(request.runner, request.workdir, request.parentAgent);
|
|
128
|
+
const env = buildSpawnEnv(baseEnv, homeDir);
|
|
84
129
|
(0, runtime_1.emitNervesEvent)({
|
|
85
130
|
component: "repertoire",
|
|
86
131
|
event: "repertoire.coding_spawn_start",
|
|
@@ -89,6 +134,7 @@ function spawnCodingProcess(request, deps = {}) {
|
|
|
89
134
|
});
|
|
90
135
|
const proc = spawnFn(command, args, {
|
|
91
136
|
cwd: request.workdir,
|
|
137
|
+
env,
|
|
92
138
|
stdio: ["pipe", "pipe", "pipe"],
|
|
93
139
|
});
|
|
94
140
|
proc.stdin.end(`${prompt}\n`);
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.codingToolDefinitions = void 0;
|
|
4
|
+
exports.countFilesInSessionOutput = countFilesInSessionOutput;
|
|
4
5
|
const index_1 = require("./index");
|
|
6
|
+
const context_pack_1 = require("./context-pack");
|
|
7
|
+
const identity_1 = require("../../heart/identity");
|
|
8
|
+
const obligations_1 = require("../../arc/obligations");
|
|
5
9
|
const runtime_1 = require("../../nerves/runtime");
|
|
10
|
+
const scrutiny_1 = require("../../mind/scrutiny");
|
|
6
11
|
const RUNNERS = ["claude", "codex"];
|
|
7
12
|
function requireArg(args, key) {
|
|
8
13
|
const value = args[key];
|
|
@@ -29,11 +34,112 @@ function emitCodingToolEvent(toolName) {
|
|
|
29
34
|
meta: { toolName },
|
|
30
35
|
});
|
|
31
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* Count distinct file paths mentioned in a coding session's stdout output.
|
|
39
|
+
* Looks for path-like tokens (containing / and a file extension).
|
|
40
|
+
* Returns the count of unique paths found.
|
|
41
|
+
*/
|
|
42
|
+
function countFilesInSessionOutput(session) {
|
|
43
|
+
const text = `${session.stdoutTail}\n${session.stderrTail}`;
|
|
44
|
+
// Match path-like tokens: contain at least one / and a file extension
|
|
45
|
+
const pathPattern = /(?:^|\s)((?:\/|\.\/|\.\.\/)?(?:[\w.@-]+\/)+[\w.-]+\.[\w]+)/gm;
|
|
46
|
+
const paths = new Set();
|
|
47
|
+
let match;
|
|
48
|
+
while ((match = pathPattern.exec(text)) !== null) {
|
|
49
|
+
paths.add(match[1]);
|
|
50
|
+
}
|
|
51
|
+
return paths.size;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* If a coding session is completed, append scrutiny to the result.
|
|
55
|
+
* Returns the original result with scrutiny appended, or unchanged if
|
|
56
|
+
* the session is not completed or has no file changes.
|
|
57
|
+
*/
|
|
58
|
+
function appendCompletionScrutiny(result, session) {
|
|
59
|
+
if (session.status !== "completed")
|
|
60
|
+
return result;
|
|
61
|
+
const fileCount = countFilesInSessionOutput(session);
|
|
62
|
+
const scrutiny = (0, scrutiny_1.getCodingCompletionScrutiny)(fileCount);
|
|
63
|
+
return scrutiny ? `${result}\n\n${scrutiny}` : result;
|
|
64
|
+
}
|
|
65
|
+
function sameOriginSession(left, right) {
|
|
66
|
+
if (!left && !right)
|
|
67
|
+
return true;
|
|
68
|
+
if (!left || !right)
|
|
69
|
+
return false;
|
|
70
|
+
return left.friendId === right.friendId && left.channel === right.channel && left.key === right.key;
|
|
71
|
+
}
|
|
72
|
+
function matchesReusableCodingSession(session, request) {
|
|
73
|
+
if (session.status !== "spawning" && session.status !== "running" && session.status !== "waiting_input" && session.status !== "stalled") {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
const scopeMatches = request.scopeFile ? session.scopeFile === request.scopeFile : true;
|
|
77
|
+
const stateMatches = request.stateFile ? session.stateFile === request.stateFile : true;
|
|
78
|
+
return (session.runner === request.runner &&
|
|
79
|
+
session.workdir === request.workdir &&
|
|
80
|
+
session.taskRef === request.taskRef &&
|
|
81
|
+
scopeMatches &&
|
|
82
|
+
stateMatches &&
|
|
83
|
+
session.obligationId === request.obligationId &&
|
|
84
|
+
sameOriginSession(request.originSession, session.originSession));
|
|
85
|
+
}
|
|
86
|
+
function latestSessionFirst(left, right) {
|
|
87
|
+
const lastActivityDelta = Date.parse(right.lastActivityAt) - Date.parse(left.lastActivityAt);
|
|
88
|
+
if (lastActivityDelta !== 0)
|
|
89
|
+
return lastActivityDelta;
|
|
90
|
+
return right.id.localeCompare(left.id);
|
|
91
|
+
}
|
|
92
|
+
function findReusableCodingSession(sessions, request) {
|
|
93
|
+
const matches = sessions.filter((session) => matchesReusableCodingSession(session, request)).sort(latestSessionFirst);
|
|
94
|
+
return matches[0] ?? null;
|
|
95
|
+
}
|
|
96
|
+
function isLiveCodingStatus(status) {
|
|
97
|
+
return status === "spawning" || status === "running" || status === "waiting_input" || status === "stalled";
|
|
98
|
+
}
|
|
99
|
+
function rankCodingStatusSession(session, currentSession) {
|
|
100
|
+
return sameOriginSession({
|
|
101
|
+
friendId: currentSession.friendId,
|
|
102
|
+
channel: currentSession.channel,
|
|
103
|
+
key: currentSession.key,
|
|
104
|
+
}, session.originSession)
|
|
105
|
+
? 0
|
|
106
|
+
: 1;
|
|
107
|
+
}
|
|
108
|
+
function selectCodingStatusSessions(sessions, currentSession) {
|
|
109
|
+
if (sessions.length === 0)
|
|
110
|
+
return [];
|
|
111
|
+
if (!currentSession) {
|
|
112
|
+
return sessions;
|
|
113
|
+
}
|
|
114
|
+
const activeSessions = sessions.filter((session) => isLiveCodingStatus(session.status)).sort(latestSessionFirst);
|
|
115
|
+
if (activeSessions.length > 0) {
|
|
116
|
+
return activeSessions.sort((left, right) => {
|
|
117
|
+
const rankDelta = rankCodingStatusSession(left, currentSession) - rankCodingStatusSession(right, currentSession);
|
|
118
|
+
if (rankDelta !== 0)
|
|
119
|
+
return rankDelta;
|
|
120
|
+
return latestSessionFirst(left, right);
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
const matchingClosedSessions = sessions
|
|
124
|
+
.filter((session) => sameOriginSession({
|
|
125
|
+
friendId: currentSession.friendId,
|
|
126
|
+
channel: currentSession.channel,
|
|
127
|
+
key: currentSession.key,
|
|
128
|
+
}, session.originSession))
|
|
129
|
+
.sort(latestSessionFirst);
|
|
130
|
+
if (matchingClosedSessions.length > 0) {
|
|
131
|
+
return matchingClosedSessions;
|
|
132
|
+
}
|
|
133
|
+
return [...sessions].sort(latestSessionFirst);
|
|
134
|
+
}
|
|
135
|
+
function buildCodingObligationContent(taskRef) {
|
|
136
|
+
return `finish ${taskRef} and bring the result back`;
|
|
137
|
+
}
|
|
32
138
|
const codingSpawnTool = {
|
|
33
139
|
type: "function",
|
|
34
140
|
function: {
|
|
35
141
|
name: "coding_spawn",
|
|
36
|
-
description: "
|
|
142
|
+
description: "Spawn a coding session using claude or codex with task-threaded guidance. The coding session runs as a separate process with its own context. Give it a COMPLETE, SELF-CONTAINED task description -- it cannot see this conversation, doesn't know what you've tried, doesn't understand the broader context. Include: what to do, why, what files are involved, what 'done' looks like. Never delegate understanding -- don't write 'based on the conversation, fix the bug.' Write the specific file paths, line numbers, and what to change. Include any required verification steps or tests in the task description so the coding session knows how to prove the work is done.",
|
|
37
143
|
parameters: {
|
|
38
144
|
type: "object",
|
|
39
145
|
properties: {
|
|
@@ -52,7 +158,7 @@ const codingStatusTool = {
|
|
|
52
158
|
type: "function",
|
|
53
159
|
function: {
|
|
54
160
|
name: "coding_status",
|
|
55
|
-
description: "
|
|
161
|
+
description: "Inspect coding sessions. Omit sessionId to list all active/known sessions with their status. Use this to check progress before asking the human for a status update.",
|
|
56
162
|
parameters: {
|
|
57
163
|
type: "object",
|
|
58
164
|
properties: {
|
|
@@ -65,7 +171,7 @@ const codingTailTool = {
|
|
|
65
171
|
type: "function",
|
|
66
172
|
function: {
|
|
67
173
|
name: "coding_tail",
|
|
68
|
-
description: "
|
|
174
|
+
description: "Show recent stdout/stderr output from a coding session. Use this to understand what the session is doing or why it might be stuck. Read the actual output before reporting status -- don't guess.",
|
|
69
175
|
parameters: {
|
|
70
176
|
type: "object",
|
|
71
177
|
properties: {
|
|
@@ -130,6 +236,17 @@ exports.codingToolDefinitions = [
|
|
|
130
236
|
prompt,
|
|
131
237
|
taskRef,
|
|
132
238
|
};
|
|
239
|
+
if (ctx?.currentSession && ctx.currentSession.channel !== "inner") {
|
|
240
|
+
request.originSession = {
|
|
241
|
+
friendId: ctx.currentSession.friendId,
|
|
242
|
+
channel: ctx.currentSession.channel,
|
|
243
|
+
key: ctx.currentSession.key,
|
|
244
|
+
};
|
|
245
|
+
const obligation = (0, obligations_1.findPendingObligationForOrigin)((0, identity_1.getAgentRoot)(), request.originSession);
|
|
246
|
+
if (obligation) {
|
|
247
|
+
request.obligationId = obligation.id;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
133
250
|
const scopeFile = optionalArg(args, "scopeFile");
|
|
134
251
|
if (scopeFile)
|
|
135
252
|
request.scopeFile = scopeFile;
|
|
@@ -137,7 +254,48 @@ exports.codingToolDefinitions = [
|
|
|
137
254
|
if (stateFile)
|
|
138
255
|
request.stateFile = stateFile;
|
|
139
256
|
const manager = (0, index_1.getCodingSessionManager)();
|
|
257
|
+
const existingSessions = manager.listSessions();
|
|
258
|
+
const existingSession = findReusableCodingSession(existingSessions, request);
|
|
259
|
+
if (existingSession) {
|
|
260
|
+
(0, runtime_1.emitNervesEvent)({
|
|
261
|
+
component: "repertoire",
|
|
262
|
+
event: "repertoire.coding_session_reused",
|
|
263
|
+
message: "reused active coding session",
|
|
264
|
+
meta: { id: existingSession.id, runner: existingSession.runner, taskRef: existingSession.taskRef },
|
|
265
|
+
});
|
|
266
|
+
if (ctx?.codingFeedback) {
|
|
267
|
+
(0, index_1.attachCodingSessionFeedback)(manager, existingSession, ctx.codingFeedback);
|
|
268
|
+
}
|
|
269
|
+
return JSON.stringify({ ...existingSession, reused: true });
|
|
270
|
+
}
|
|
271
|
+
if (request.originSession && !request.obligationId) {
|
|
272
|
+
const created = (0, obligations_1.createObligation)((0, identity_1.getAgentRoot)(), {
|
|
273
|
+
origin: request.originSession,
|
|
274
|
+
content: buildCodingObligationContent(taskRef),
|
|
275
|
+
});
|
|
276
|
+
request.obligationId = created.id;
|
|
277
|
+
}
|
|
278
|
+
if (!request.scopeFile || !request.stateFile) {
|
|
279
|
+
const generated = (0, context_pack_1.prepareCodingContextPack)({
|
|
280
|
+
request: { ...request },
|
|
281
|
+
existingSessions,
|
|
282
|
+
activeWorkFrame: ctx?.activeWorkFrame,
|
|
283
|
+
});
|
|
284
|
+
if (!request.scopeFile)
|
|
285
|
+
request.scopeFile = generated.scopeFile;
|
|
286
|
+
if (!request.stateFile)
|
|
287
|
+
request.stateFile = generated.stateFile;
|
|
288
|
+
}
|
|
140
289
|
const session = await manager.spawnSession(request);
|
|
290
|
+
if (session.obligationId) {
|
|
291
|
+
(0, obligations_1.advanceObligation)((0, identity_1.getAgentRoot)(), session.obligationId, {
|
|
292
|
+
status: "investigating",
|
|
293
|
+
currentSurface: { kind: "coding", label: `${session.runner} ${session.id}` },
|
|
294
|
+
latestNote: session.originSession
|
|
295
|
+
? `coding session started for ${session.originSession.channel}/${session.originSession.key}`
|
|
296
|
+
: "coding session started",
|
|
297
|
+
});
|
|
298
|
+
}
|
|
141
299
|
if (args.runner === "codex" && args.taskRef) {
|
|
142
300
|
(0, runtime_1.emitNervesEvent)({
|
|
143
301
|
component: "repertoire",
|
|
@@ -151,21 +309,23 @@ exports.codingToolDefinitions = [
|
|
|
151
309
|
}
|
|
152
310
|
return JSON.stringify(session);
|
|
153
311
|
},
|
|
312
|
+
summaryKeys: ["runner", "workdir", "taskRef"],
|
|
154
313
|
},
|
|
155
314
|
{
|
|
156
315
|
tool: codingStatusTool,
|
|
157
|
-
handler: (args) => {
|
|
316
|
+
handler: (args, ctx) => {
|
|
158
317
|
emitCodingToolEvent("coding_status");
|
|
159
318
|
const manager = (0, index_1.getCodingSessionManager)();
|
|
160
319
|
const sessionId = requireArg(args, "sessionId");
|
|
161
320
|
if (!sessionId) {
|
|
162
|
-
return JSON.stringify(manager.listSessions());
|
|
321
|
+
return JSON.stringify(selectCodingStatusSessions(manager.listSessions(), ctx?.currentSession));
|
|
163
322
|
}
|
|
164
323
|
const session = manager.getSession(sessionId);
|
|
165
324
|
if (!session)
|
|
166
325
|
return `session not found: ${sessionId}`;
|
|
167
|
-
return JSON.stringify(session);
|
|
326
|
+
return appendCompletionScrutiny(JSON.stringify(session), session);
|
|
168
327
|
},
|
|
328
|
+
summaryKeys: ["sessionId"],
|
|
169
329
|
},
|
|
170
330
|
{
|
|
171
331
|
tool: codingTailTool,
|
|
@@ -177,8 +337,9 @@ exports.codingToolDefinitions = [
|
|
|
177
337
|
const session = (0, index_1.getCodingSessionManager)().getSession(sessionId);
|
|
178
338
|
if (!session)
|
|
179
339
|
return `session not found: ${sessionId}`;
|
|
180
|
-
return (0, index_1.formatCodingTail)(session);
|
|
340
|
+
return appendCompletionScrutiny((0, index_1.formatCodingTail)(session), session);
|
|
181
341
|
},
|
|
342
|
+
summaryKeys: ["sessionId"],
|
|
182
343
|
},
|
|
183
344
|
{
|
|
184
345
|
tool: codingSendInputTool,
|
|
@@ -192,6 +353,7 @@ exports.codingToolDefinitions = [
|
|
|
192
353
|
return "input is required";
|
|
193
354
|
return JSON.stringify((0, index_1.getCodingSessionManager)().sendInput(sessionId, input));
|
|
194
355
|
},
|
|
356
|
+
summaryKeys: ["sessionId", "input"],
|
|
195
357
|
},
|
|
196
358
|
{
|
|
197
359
|
tool: codingKillTool,
|
|
@@ -202,5 +364,6 @@ exports.codingToolDefinitions = [
|
|
|
202
364
|
return "sessionId is required";
|
|
203
365
|
return JSON.stringify((0, index_1.getCodingSessionManager)().killSession(sessionId));
|
|
204
366
|
},
|
|
367
|
+
summaryKeys: ["sessionId"],
|
|
205
368
|
},
|
|
206
369
|
];
|