@ouro.bot/cli 0.1.0-alpha.8 → 0.1.0-alpha.81

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/AdoptionSpecialist.ouro/agent.json +70 -9
  2. package/AdoptionSpecialist.ouro/psyche/SOUL.md +5 -2
  3. package/AdoptionSpecialist.ouro/psyche/identities/monty.md +2 -2
  4. package/README.md +147 -205
  5. package/assets/ouroboros.png +0 -0
  6. package/changelog.json +468 -0
  7. package/dist/heart/active-work.js +218 -0
  8. package/dist/heart/bridges/manager.js +358 -0
  9. package/dist/heart/bridges/state-machine.js +135 -0
  10. package/dist/heart/bridges/store.js +123 -0
  11. package/dist/heart/commitments.js +89 -0
  12. package/dist/heart/config.js +68 -23
  13. package/dist/heart/core.js +452 -93
  14. package/dist/heart/cross-chat-delivery.js +146 -0
  15. package/dist/heart/daemon/agent-discovery.js +81 -0
  16. package/dist/heart/daemon/auth-flow.js +430 -0
  17. package/dist/heart/daemon/daemon-cli.js +1779 -247
  18. package/dist/heart/daemon/daemon-entry.js +55 -6
  19. package/dist/heart/daemon/daemon-runtime-sync.js +212 -0
  20. package/dist/heart/daemon/daemon.js +216 -10
  21. package/dist/heart/daemon/hatch-animation.js +10 -3
  22. package/dist/heart/daemon/hatch-flow.js +7 -82
  23. package/dist/heart/daemon/hooks/bundle-meta.js +92 -0
  24. package/dist/heart/daemon/launchd.js +159 -0
  25. package/dist/heart/daemon/log-tailer.js +4 -3
  26. package/dist/heart/daemon/message-router.js +17 -8
  27. package/dist/heart/daemon/ouro-bot-entry.js +0 -0
  28. package/dist/heart/daemon/ouro-bot-global-installer.js +128 -0
  29. package/dist/heart/daemon/ouro-entry.js +0 -0
  30. package/dist/heart/daemon/ouro-path-installer.js +260 -0
  31. package/dist/heart/daemon/ouro-uti.js +11 -2
  32. package/dist/heart/daemon/ouro-version-manager.js +164 -0
  33. package/dist/heart/daemon/process-manager.js +14 -1
  34. package/dist/heart/daemon/run-hooks.js +37 -0
  35. package/dist/heart/daemon/runtime-logging.js +58 -15
  36. package/dist/heart/daemon/runtime-metadata.js +219 -0
  37. package/dist/heart/daemon/runtime-mode.js +67 -0
  38. package/dist/heart/daemon/sense-manager.js +307 -0
  39. package/dist/heart/daemon/skill-management-installer.js +94 -0
  40. package/dist/heart/daemon/socket-client.js +202 -0
  41. package/dist/heart/daemon/specialist-orchestrator.js +53 -84
  42. package/dist/heart/daemon/specialist-prompt.js +63 -11
  43. package/dist/heart/daemon/specialist-tools.js +211 -60
  44. package/dist/heart/daemon/staged-restart.js +114 -0
  45. package/dist/heart/daemon/thoughts.js +507 -0
  46. package/dist/heart/daemon/update-checker.js +111 -0
  47. package/dist/heart/daemon/update-hooks.js +138 -0
  48. package/dist/heart/daemon/wrapper-publish-guard.js +86 -0
  49. package/dist/heart/delegation.js +62 -0
  50. package/dist/heart/identity.js +126 -21
  51. package/dist/heart/kicks.js +1 -19
  52. package/dist/heart/model-capabilities.js +48 -0
  53. package/dist/heart/obligations.js +141 -0
  54. package/dist/heart/progress-story.js +42 -0
  55. package/dist/heart/providers/anthropic.js +74 -9
  56. package/dist/heart/providers/azure.js +86 -7
  57. package/dist/heart/providers/github-copilot.js +149 -0
  58. package/dist/heart/providers/minimax.js +4 -0
  59. package/dist/heart/providers/openai-codex.js +12 -3
  60. package/dist/heart/safe-workspace.js +228 -0
  61. package/dist/heart/sense-truth.js +61 -0
  62. package/dist/heart/session-activity.js +169 -0
  63. package/dist/heart/session-recall.js +116 -0
  64. package/dist/heart/streaming.js +100 -22
  65. package/dist/heart/target-resolution.js +123 -0
  66. package/dist/heart/turn-coordinator.js +28 -0
  67. package/dist/mind/associative-recall.js +14 -2
  68. package/dist/mind/bundle-manifest.js +70 -0
  69. package/dist/mind/context.js +27 -11
  70. package/dist/mind/first-impressions.js +16 -2
  71. package/dist/mind/friends/channel.js +35 -0
  72. package/dist/mind/friends/group-context.js +144 -0
  73. package/dist/mind/friends/store-file.js +19 -0
  74. package/dist/mind/friends/trust-explanation.js +74 -0
  75. package/dist/mind/friends/types.js +8 -0
  76. package/dist/mind/memory.js +27 -26
  77. package/dist/mind/pending.js +76 -9
  78. package/dist/mind/phrases.js +1 -0
  79. package/dist/mind/prompt.js +445 -77
  80. package/dist/mind/token-estimate.js +8 -12
  81. package/dist/nerves/cli-logging.js +15 -2
  82. package/dist/nerves/coverage/run-artifacts.js +1 -1
  83. package/dist/nerves/index.js +12 -0
  84. package/dist/repertoire/ado-client.js +4 -2
  85. package/dist/repertoire/coding/feedback.js +134 -0
  86. package/dist/repertoire/coding/index.js +4 -1
  87. package/dist/repertoire/coding/manager.js +62 -4
  88. package/dist/repertoire/coding/spawner.js +3 -3
  89. package/dist/repertoire/coding/tools.js +41 -2
  90. package/dist/repertoire/data/ado-endpoints.json +188 -0
  91. package/dist/repertoire/guardrails.js +290 -0
  92. package/dist/repertoire/mcp-client.js +254 -0
  93. package/dist/repertoire/mcp-manager.js +195 -0
  94. package/dist/repertoire/skills.js +3 -26
  95. package/dist/repertoire/tasks/board.js +12 -0
  96. package/dist/repertoire/tasks/index.js +23 -9
  97. package/dist/repertoire/tasks/transitions.js +1 -2
  98. package/dist/repertoire/tools-base.js +686 -251
  99. package/dist/repertoire/tools-bluebubbles.js +93 -0
  100. package/dist/repertoire/tools-teams.js +58 -25
  101. package/dist/repertoire/tools.js +95 -53
  102. package/dist/senses/bluebubbles-client.js +210 -5
  103. package/dist/senses/bluebubbles-entry.js +2 -0
  104. package/dist/senses/bluebubbles-inbound-log.js +109 -0
  105. package/dist/senses/bluebubbles-media.js +339 -0
  106. package/dist/senses/bluebubbles-model.js +12 -4
  107. package/dist/senses/bluebubbles-mutation-log.js +45 -5
  108. package/dist/senses/bluebubbles-runtime-state.js +109 -0
  109. package/dist/senses/bluebubbles-session-cleanup.js +72 -0
  110. package/dist/senses/bluebubbles.js +894 -45
  111. package/dist/senses/cli-layout.js +187 -0
  112. package/dist/senses/cli.js +405 -156
  113. package/dist/senses/continuity.js +94 -0
  114. package/dist/senses/debug-activity.js +154 -0
  115. package/dist/senses/inner-dialog-worker.js +47 -18
  116. package/dist/senses/inner-dialog.js +377 -83
  117. package/dist/senses/pipeline.js +307 -0
  118. package/dist/senses/teams.js +573 -129
  119. package/dist/senses/trust-gate.js +112 -2
  120. package/package.json +14 -3
  121. package/subagents/README.md +4 -70
  122. package/dist/heart/daemon/specialist-session.js +0 -142
  123. package/dist/heart/daemon/subagent-installer.js +0 -125
  124. package/dist/inner-worker-entry.js +0 -4
  125. package/subagents/work-doer.md +0 -233
  126. package/subagents/work-merger.md +0 -624
  127. package/subagents/work-planner.md +0 -373
@@ -39,6 +39,9 @@ const fs = __importStar(require("fs"));
39
39
  const path = __importStar(require("path"));
40
40
  const identity_1 = require("../heart/identity");
41
41
  const runtime_1 = require("../nerves/runtime");
42
+ const types_1 = require("../mind/friends/types");
43
+ const pending_1 = require("../mind/pending");
44
+ // Canned reply; eventually agents should compose their own first-contact message
42
45
  exports.STRANGER_AUTO_REPLY = "I'm sorry, I'm not allowed to talk to strangers";
43
46
  function buildExternalKey(provider, externalId, tenantId) {
44
47
  return `${provider}:${tenantId ?? ""}:${externalId}`;
@@ -77,16 +80,107 @@ function appendPrimaryNotification(bundleRoot, provider, externalId, tenantId, n
77
80
  fs.mkdirSync(inboxDir, { recursive: true });
78
81
  fs.appendFileSync(notificationsPath, `${JSON.stringify(payload)}\n`, "utf8");
79
82
  }
83
+ function writeInnerPendingNotice(bundleRoot, noticeContent, nowIso) {
84
+ const innerPendingDir = path.join(bundleRoot, "state", "pending", pending_1.INNER_DIALOG_PENDING.friendId, pending_1.INNER_DIALOG_PENDING.channel, pending_1.INNER_DIALOG_PENDING.key);
85
+ const fileName = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}.json`;
86
+ const filePath = path.join(innerPendingDir, fileName);
87
+ const payload = {
88
+ from: "instinct",
89
+ content: noticeContent,
90
+ timestamp: Date.now(),
91
+ at: nowIso,
92
+ };
93
+ fs.mkdirSync(innerPendingDir, { recursive: true });
94
+ fs.writeFileSync(filePath, JSON.stringify(payload), "utf-8");
95
+ }
80
96
  function enforceTrustGate(input) {
97
+ const { senseType } = input;
98
+ // Local (CLI) and internal (inner dialog) — always allow
99
+ if (senseType === "local" || senseType === "internal") {
100
+ return { allowed: true };
101
+ }
102
+ // Closed senses (Teams) — org already gates access, allow all trust levels
103
+ if (senseType === "closed") {
104
+ return { allowed: true };
105
+ }
106
+ // Open senses (BlueBubbles/iMessage) — enforce trust rules
81
107
  const trustLevel = input.friend.trustLevel ?? "friend";
82
- if (trustLevel !== "stranger") {
108
+ // Family and friend — always allow on open
109
+ if ((0, types_1.isTrustedLevel)(trustLevel)) {
83
110
  return { allowed: true };
84
111
  }
85
112
  const bundleRoot = input.bundleRoot ?? (0, identity_1.getAgentRoot)();
86
- const repliesPath = path.join(bundleRoot, "stranger-replies.json");
87
113
  const nowIso = (input.now ?? (() => new Date()))().toISOString();
114
+ // Acquaintance rules
115
+ if (trustLevel === "acquaintance") {
116
+ return handleAcquaintance(input, bundleRoot, nowIso);
117
+ }
118
+ // Stranger rules (trustLevel === "stranger")
119
+ return handleStranger(input, bundleRoot, nowIso);
120
+ }
121
+ function handleAcquaintance(input, bundleRoot, nowIso) {
122
+ const { isGroupChat, groupHasFamilyMember, hasExistingGroupWithFamily } = input;
123
+ // Group chat with family member present — allow
124
+ if (isGroupChat && groupHasFamilyMember) {
125
+ return { allowed: true };
126
+ }
127
+ let result;
128
+ let noticeDetail;
129
+ if (isGroupChat) {
130
+ // Group chat without family member — reject silently
131
+ result = { allowed: false, reason: "acquaintance_group_no_family" };
132
+ noticeDetail = `acquaintance "${input.friend.name}" messaged in a group chat without a family member present`;
133
+ }
134
+ else if (hasExistingGroupWithFamily) {
135
+ // 1:1 but shares a group with family — redirect
136
+ result = {
137
+ allowed: false,
138
+ reason: "acquaintance_1on1_has_group",
139
+ autoReply: "Hey! Reach me in our group chat instead.",
140
+ };
141
+ noticeDetail = `acquaintance "${input.friend.name}" DMed me directly — redirected to our group chat`;
142
+ }
143
+ else {
144
+ // 1:1, no shared group with family — redirect to any group
145
+ result = {
146
+ allowed: false,
147
+ reason: "acquaintance_1on1_no_group",
148
+ autoReply: "Hey! Reach me in a group chat instead.",
149
+ };
150
+ noticeDetail = `acquaintance "${input.friend.name}" DMed me directly — asked to reach me in a group chat`;
151
+ }
152
+ (0, runtime_1.emitNervesEvent)({
153
+ level: "warn",
154
+ component: "senses",
155
+ event: "senses.trust_gate",
156
+ message: "acquaintance message blocked",
157
+ meta: {
158
+ channel: input.channel,
159
+ provider: input.provider,
160
+ reason: result.reason,
161
+ },
162
+ });
163
+ try {
164
+ writeInnerPendingNotice(bundleRoot, noticeDetail, nowIso);
165
+ }
166
+ catch (error) {
167
+ (0, runtime_1.emitNervesEvent)({
168
+ level: "error",
169
+ component: "senses",
170
+ event: "senses.trust_gate_error",
171
+ message: "failed to write inner pending notice",
172
+ meta: {
173
+ reason: error instanceof Error ? error.message : String(error),
174
+ },
175
+ });
176
+ }
177
+ return result;
178
+ }
179
+ function handleStranger(input, bundleRoot, nowIso) {
180
+ const repliesPath = path.join(bundleRoot, "stranger-replies.json");
88
181
  const externalKey = buildExternalKey(input.provider, input.externalId, input.tenantId);
89
182
  const state = loadRepliesState(repliesPath);
183
+ // Subsequent contact — silent drop
90
184
  if (state[externalKey]) {
91
185
  (0, runtime_1.emitNervesEvent)({
92
186
  level: "warn",
@@ -103,6 +197,7 @@ function enforceTrustGate(input) {
103
197
  reason: "stranger_silent_drop",
104
198
  };
105
199
  }
200
+ // First contact — auto-reply, persist state, notify agent
106
201
  state[externalKey] = nowIso;
107
202
  try {
108
203
  persistRepliesState(repliesPath, state);
@@ -132,6 +227,21 @@ function enforceTrustGate(input) {
132
227
  },
133
228
  });
134
229
  }
230
+ const noticeDetail = `stranger "${input.friend.name}" tried to reach me via ${input.channel}. Auto-replied once.`;
231
+ try {
232
+ writeInnerPendingNotice(bundleRoot, noticeDetail, nowIso);
233
+ }
234
+ catch (error) {
235
+ (0, runtime_1.emitNervesEvent)({
236
+ level: "error",
237
+ component: "senses",
238
+ event: "senses.trust_gate_error",
239
+ message: "failed to write inner pending notice",
240
+ meta: {
241
+ reason: error instanceof Error ? error.message : String(error),
242
+ },
243
+ });
244
+ }
135
245
  (0, runtime_1.emitNervesEvent)({
136
246
  level: "warn",
137
247
  component: "senses",
package/package.json CHANGED
@@ -1,15 +1,18 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.8",
3
+ "version": "0.1.0-alpha.81",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
+ "cli": "dist/heart/daemon/ouro-bot-entry.js",
6
7
  "ouro": "dist/heart/daemon/ouro-entry.js",
7
8
  "ouro.bot": "dist/heart/daemon/ouro-bot-entry.js"
8
9
  },
9
10
  "files": [
10
11
  "dist/",
11
12
  "AdoptionSpecialist.ouro/",
12
- "subagents/"
13
+ "subagents/",
14
+ "assets/",
15
+ "changelog.json"
13
16
  ],
14
17
  "exports": {
15
18
  ".": "./dist/heart/daemon/daemon-cli.js",
@@ -30,11 +33,19 @@
30
33
  },
31
34
  "dependencies": {
32
35
  "@anthropic-ai/sdk": "^0.78.0",
36
+ "@azure/identity": "^4.13.0",
33
37
  "@microsoft/teams.apps": "^2.0.5",
34
38
  "@microsoft/teams.dev": "^2.0.5",
35
- "openai": "^6.27.0"
39
+ "fast-glob": "^3.3.3",
40
+ "openai": "^6.27.0",
41
+ "semver": "^7.7.4"
42
+ },
43
+ "repository": {
44
+ "type": "git",
45
+ "url": "https://github.com/ouroborosbot/ouroboros"
36
46
  },
37
47
  "devDependencies": {
48
+ "@types/semver": "^7.7.1",
38
49
  "@vitest/coverage-v8": "^4.0.18",
39
50
  "eslint": "^10.0.2",
40
51
  "typescript": "^5.7.0",
@@ -1,73 +1,7 @@
1
- # Sub-agents
1
+ # Workflow Skills (Moved)
2
2
 
3
- These are source-of-truth workflow definitions (`work-planner`, `work-doer`, `work-merger`) for planning, execution, and merge. They can be consumed either as Claude sub-agents (`.md` files with YAML frontmatter) or as Codex-style skills (`SKILL.md`).
3
+ The workflow skills (`work-planner`, `work-doer`, `work-merger`) have moved to the shared skills repository:
4
4
 
5
- ## Installation
5
+ **https://github.com/ouroborosbot/ouroboros-skills**
6
6
 
7
- ### Claude Code (sub-agents)
8
-
9
- Copy or symlink these files into Claude's sub-agent directory:
10
-
11
- ```bash
12
- # Claude Code
13
- cp subagents/*.md ~/.claude/agents/
14
- # or
15
- ln -s "$(pwd)"/subagents/*.md ~/.claude/agents/
16
- ```
17
-
18
- ### Codex / skill-based harnesses
19
-
20
- For tools that support skills but not Claude sub-agents, install these as skills:
21
-
22
- ```bash
23
- mkdir -p ~/.codex/skills/work-planner ~/.codex/skills/work-doer ~/.codex/skills/work-merger
24
-
25
- # Hard-link to keep one source of truth
26
- ln -f "$(pwd)/subagents/work-planner.md" ~/.codex/skills/work-planner/SKILL.md
27
- ln -f "$(pwd)/subagents/work-doer.md" ~/.codex/skills/work-doer/SKILL.md
28
- ln -f "$(pwd)/subagents/work-merger.md" ~/.codex/skills/work-merger/SKILL.md
29
- ```
30
-
31
- **Important:** Hard-links break when editors save by replacing the file (new inode). After editing any `subagents/*.md` file, re-run the `ln -f` command for that file to restore the link. You can verify with `stat -f '%i'` — both files should share the same inode.
32
-
33
- Optional UI metadata:
34
-
35
- ```bash
36
- mkdir -p ~/.codex/skills/work-planner/agents ~/.codex/skills/work-doer/agents ~/.codex/skills/work-merger/agents
37
- cat > ~/.codex/skills/work-planner/agents/openai.yaml << 'EOF'
38
- interface:
39
- display_name: "Work Planner"
40
- short_description: "Create and gate planning/doing task docs"
41
- default_prompt: "Use $work-planner to create or update a planning doc, then stop at NEEDS_REVIEW."
42
- EOF
43
- cat > ~/.codex/skills/work-doer/agents/openai.yaml << 'EOF'
44
- interface:
45
- display_name: "Work Doer"
46
- short_description: "Execute approved doing docs with strict TDD"
47
- default_prompt: "Use $work-doer to execute an approved doing doc unit by unit."
48
- EOF
49
- cat > ~/.codex/skills/work-merger/agents/openai.yaml << 'EOF'
50
- interface:
51
- display_name: "Work Merger"
52
- short_description: "Merge feature branch into main via PR after work-doer completes"
53
- default_prompt: "Use $work-merger to merge the current feature branch into main."
54
- EOF
55
- ```
56
-
57
- Restart the harness after install so new skills are discovered.
58
-
59
- ## Available sub-agents
60
-
61
- | File | Purpose |
62
- |------|---------|
63
- | `work-planner.md` | Interactive task planner. Generates planning docs through conversation, then converts to doing docs after human approval. |
64
- | `work-doer.md` | Task executor. Reads a doing doc and works through each unit sequentially with strict TDD. |
65
- | `work-merger.md` | Sync-and-merge agent. Merges feature branch into main via PR after work-doer completes. Handles conflicts, CI failures, and race conditions. |
66
-
67
- ## Workflow
68
-
69
- 1. Human describes a task
70
- 2. Agent invokes **work-planner** to create a planning doc → human approves → planner converts to doing doc
71
- 3. Agent invokes **work-doer** to execute the doing doc unit by unit
72
- 4. Each unit is committed independently with progress tracked in the doing doc
73
- 5. Agent invokes **work-merger** to merge the feature branch into main via PR (fetch, merge, resolve conflicts, CI gate, merge PR, cleanup)
7
+ To install or update these skills, use the **skill-management** skill from that repo. It covers browsing available skills, installing them into your agent's local skills directory, and keeping them up to date.
@@ -1,142 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.runSpecialistSession = runSpecialistSession;
4
- const runtime_1 = require("../../nerves/runtime");
5
- /**
6
- * Run the specialist conversation session loop.
7
- *
8
- * The loop:
9
- * 1. Initialize messages with system prompt
10
- * 2. Prompt user -> add to messages -> call streamTurn -> process result
11
- * 3. If result has no tool calls: push assistant message, re-prompt
12
- * 4. If result has final_answer sole call: extract answer, emit via callbacks, done
13
- * 5. If result has other tool calls: execute each, push tool results, continue loop
14
- * 6. On abort signal: clean exit
15
- * 7. Return { hatchedAgentName } -- name from hatch_agent if called
16
- */
17
- async function runSpecialistSession(deps) {
18
- const { providerRuntime, systemPrompt, tools, execTool, readline, callbacks, signal } = deps;
19
- (0, runtime_1.emitNervesEvent)({
20
- component: "daemon",
21
- event: "daemon.specialist_session_start",
22
- message: "starting specialist session loop",
23
- meta: {},
24
- });
25
- const messages = [
26
- { role: "system", content: systemPrompt },
27
- ];
28
- let hatchedAgentName = null;
29
- let done = false;
30
- try {
31
- while (!done) {
32
- if (signal?.aborted)
33
- break;
34
- // Get user input
35
- const userInput = await readline.question("> ");
36
- if (!userInput.trim())
37
- continue;
38
- messages.push({ role: "user", content: userInput });
39
- providerRuntime.resetTurnState(messages);
40
- // Inner loop: process tool calls until we get a final_answer or plain text
41
- let turnDone = false;
42
- while (!turnDone) {
43
- if (signal?.aborted) {
44
- done = true;
45
- break;
46
- }
47
- callbacks.onModelStart();
48
- const result = await providerRuntime.streamTurn({
49
- messages,
50
- activeTools: tools,
51
- callbacks,
52
- signal,
53
- });
54
- // Build assistant message
55
- const assistantMsg = {
56
- role: "assistant",
57
- };
58
- if (result.content)
59
- assistantMsg.content = result.content;
60
- if (result.toolCalls.length) {
61
- assistantMsg.tool_calls = result.toolCalls.map((tc) => ({
62
- id: tc.id,
63
- type: "function",
64
- function: { name: tc.name, arguments: tc.arguments },
65
- }));
66
- }
67
- if (!result.toolCalls.length) {
68
- // Plain text response -- push and re-prompt
69
- messages.push(assistantMsg);
70
- turnDone = true;
71
- continue;
72
- }
73
- // Check for final_answer
74
- const isSoleFinalAnswer = result.toolCalls.length === 1 && result.toolCalls[0].name === "final_answer";
75
- if (isSoleFinalAnswer) {
76
- let answer;
77
- try {
78
- const parsed = JSON.parse(result.toolCalls[0].arguments);
79
- if (typeof parsed === "string") {
80
- answer = parsed;
81
- }
82
- else if (parsed.answer != null) {
83
- answer = parsed.answer;
84
- }
85
- }
86
- catch {
87
- // malformed
88
- }
89
- if (answer != null) {
90
- callbacks.onTextChunk(answer);
91
- messages.push(assistantMsg);
92
- done = true;
93
- turnDone = true;
94
- continue;
95
- }
96
- // Malformed final_answer -- ask model to retry
97
- messages.push(assistantMsg);
98
- messages.push({
99
- role: "tool",
100
- tool_call_id: result.toolCalls[0].id,
101
- content: "your final_answer was incomplete or malformed. call final_answer again with your complete response.",
102
- });
103
- providerRuntime.appendToolOutput(result.toolCalls[0].id, "retry");
104
- continue;
105
- }
106
- // Execute tool calls
107
- messages.push(assistantMsg);
108
- for (const tc of result.toolCalls) {
109
- if (signal?.aborted)
110
- break;
111
- let args = {};
112
- try {
113
- args = JSON.parse(tc.arguments);
114
- }
115
- catch {
116
- // ignore parse error
117
- }
118
- callbacks.onToolStart(tc.name, args);
119
- let toolResult;
120
- try {
121
- toolResult = await execTool(tc.name, args);
122
- }
123
- catch (e) {
124
- toolResult = `error: ${e}`;
125
- }
126
- callbacks.onToolEnd(tc.name, tc.name, true);
127
- // Track hatchling name
128
- if (tc.name === "hatch_agent" && args.name) {
129
- hatchedAgentName = args.name;
130
- }
131
- messages.push({ role: "tool", tool_call_id: tc.id, content: toolResult });
132
- providerRuntime.appendToolOutput(tc.id, toolResult);
133
- }
134
- // After processing tool calls, continue inner loop for tool result processing
135
- }
136
- }
137
- }
138
- finally {
139
- readline.close();
140
- }
141
- return { hatchedAgentName };
142
- }
@@ -1,125 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.installSubagentsForAvailableCli = installSubagentsForAvailableCli;
37
- const fs = __importStar(require("fs"));
38
- const os = __importStar(require("os"));
39
- const path = __importStar(require("path"));
40
- const child_process_1 = require("child_process");
41
- const runtime_1 = require("../../nerves/runtime");
42
- function detectCliBinary(binary) {
43
- const result = (0, child_process_1.spawnSync)("which", [binary], { encoding: "utf-8" });
44
- if (result.status !== 0)
45
- return null;
46
- const resolved = result.stdout.trim();
47
- return resolved.length > 0 ? resolved : null;
48
- }
49
- function listSubagentSources(subagentsDir) {
50
- if (!fs.existsSync(subagentsDir))
51
- return [];
52
- return fs.readdirSync(subagentsDir)
53
- .filter((name) => name.endsWith(".md"))
54
- .filter((name) => name !== "README.md")
55
- .map((name) => path.join(subagentsDir, name))
56
- .sort((a, b) => a.localeCompare(b));
57
- }
58
- function ensureSymlink(source, target) {
59
- fs.mkdirSync(path.dirname(target), { recursive: true });
60
- if (fs.existsSync(target)) {
61
- const stats = fs.lstatSync(target);
62
- if (stats.isSymbolicLink()) {
63
- const linkedPath = fs.readlinkSync(target);
64
- if (linkedPath === source)
65
- return false;
66
- }
67
- fs.unlinkSync(target);
68
- }
69
- fs.symlinkSync(source, target);
70
- return true;
71
- }
72
- async function installSubagentsForAvailableCli(options = {}) {
73
- const repoRoot = options.repoRoot ?? path.resolve(__dirname, "..", "..", "..");
74
- const homeDir = options.homeDir ?? os.homedir();
75
- const which = options.which ?? detectCliBinary;
76
- const subagentsDir = path.join(repoRoot, "subagents");
77
- const sources = listSubagentSources(subagentsDir);
78
- const notes = [];
79
- (0, runtime_1.emitNervesEvent)({
80
- component: "daemon",
81
- event: "daemon.subagent_install_start",
82
- message: "starting subagent auto-install",
83
- meta: { sources: sources.length },
84
- });
85
- if (sources.length === 0) {
86
- notes.push(`no subagent files found at ${subagentsDir}`);
87
- return { claudeInstalled: 0, codexInstalled: 0, notes };
88
- }
89
- let claudeInstalled = 0;
90
- let codexInstalled = 0;
91
- const claudePath = which("claude");
92
- if (!claudePath) {
93
- notes.push("claude CLI not found; skipping subagent install");
94
- }
95
- else {
96
- const claudeAgentsDir = path.join(homeDir, ".claude", "agents");
97
- for (const source of sources) {
98
- const target = path.join(claudeAgentsDir, path.basename(source));
99
- if (ensureSymlink(source, target)) {
100
- claudeInstalled += 1;
101
- }
102
- }
103
- }
104
- const codexPath = which("codex");
105
- if (!codexPath) {
106
- notes.push("codex CLI not found; skipping subagent install");
107
- }
108
- else {
109
- const codexSkillsDir = path.join(homeDir, ".codex", "skills");
110
- for (const source of sources) {
111
- const skillName = path.basename(source, ".md");
112
- const target = path.join(codexSkillsDir, skillName, "SKILL.md");
113
- if (ensureSymlink(source, target)) {
114
- codexInstalled += 1;
115
- }
116
- }
117
- }
118
- (0, runtime_1.emitNervesEvent)({
119
- component: "daemon",
120
- event: "daemon.subagent_install_end",
121
- message: "completed subagent auto-install",
122
- meta: { claudeInstalled, codexInstalled, notes: notes.length },
123
- });
124
- return { claudeInstalled, codexInstalled, notes };
125
- }
@@ -1,4 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- // Backward-compatible wrapper; unified runtime now lives at heart/agent-entry.
4
- require("./heart/agent-entry");