@ouro.bot/cli 0.1.0-alpha.18 → 0.1.0-alpha.181

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 (185) hide show
  1. package/AdoptionSpecialist.ouro/agent.json +3 -2
  2. package/AdoptionSpecialist.ouro/psyche/SOUL.md +3 -3
  3. package/AdoptionSpecialist.ouro/psyche/identities/monty.md +2 -2
  4. package/README.md +187 -191
  5. package/changelog.json +1219 -0
  6. package/dist/heart/active-work.js +633 -0
  7. package/dist/heart/bridges/manager.js +358 -0
  8. package/dist/heart/bridges/state-machine.js +135 -0
  9. package/dist/heart/bridges/store.js +123 -0
  10. package/dist/heart/commitments.js +105 -0
  11. package/dist/heart/config.js +40 -30
  12. package/dist/heart/core.js +620 -124
  13. package/dist/heart/cross-chat-delivery.js +146 -0
  14. package/dist/heart/daemon/agent-discovery.js +81 -0
  15. package/dist/heart/daemon/agent-service.js +360 -0
  16. package/dist/heart/daemon/auth-flow.js +477 -0
  17. package/dist/heart/daemon/cadence.js +70 -0
  18. package/dist/heart/daemon/daemon-cli.js +2476 -206
  19. package/dist/heart/daemon/daemon-entry.js +157 -4
  20. package/dist/heart/daemon/daemon-health.js +136 -0
  21. package/dist/heart/daemon/daemon-runtime-sync.js +212 -0
  22. package/dist/heart/daemon/daemon-tombstone.js +123 -0
  23. package/dist/heart/daemon/daemon.js +449 -17
  24. package/dist/heart/daemon/habit-migration.js +181 -0
  25. package/dist/heart/daemon/habit-parser.js +119 -0
  26. package/dist/heart/daemon/habit-scheduler.js +369 -0
  27. package/dist/heart/daemon/hatch-animation.js +10 -3
  28. package/dist/heart/daemon/hatch-flow.js +30 -111
  29. package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
  30. package/dist/heart/daemon/hooks/bundle-meta.js +92 -0
  31. package/dist/heart/daemon/inner-status.js +89 -0
  32. package/dist/heart/daemon/launchd.js +165 -0
  33. package/dist/heart/daemon/log-tailer.js +4 -3
  34. package/dist/heart/daemon/mcp-server.js +591 -0
  35. package/dist/heart/daemon/message-router.js +17 -8
  36. package/dist/heart/daemon/os-cron-deps.js +134 -0
  37. package/dist/heart/daemon/ouro-bot-global-installer.js +128 -0
  38. package/dist/heart/daemon/ouro-path-installer.js +78 -34
  39. package/dist/heart/daemon/ouro-version-manager.js +171 -0
  40. package/dist/heart/daemon/outlook-http.js +399 -0
  41. package/dist/heart/daemon/outlook-read.js +1291 -0
  42. package/dist/heart/daemon/outlook-render.js +1032 -0
  43. package/dist/heart/daemon/outlook-types.js +7 -0
  44. package/dist/heart/daemon/outlook-view.js +194 -0
  45. package/dist/heart/daemon/process-manager.js +75 -0
  46. package/dist/heart/daemon/run-hooks.js +39 -0
  47. package/dist/heart/daemon/runtime-logging.js +58 -15
  48. package/dist/heart/daemon/runtime-metadata.js +219 -0
  49. package/dist/heart/daemon/runtime-mode.js +67 -0
  50. package/dist/heart/daemon/safe-mode.js +161 -0
  51. package/dist/heart/daemon/sense-manager.js +50 -2
  52. package/dist/heart/daemon/session-id-resolver.js +131 -0
  53. package/dist/heart/daemon/skill-management-installer.js +94 -0
  54. package/dist/heart/daemon/socket-client.js +202 -0
  55. package/dist/heart/daemon/specialist-orchestrator.js +2 -2
  56. package/dist/heart/daemon/specialist-prompt.js +13 -9
  57. package/dist/heart/daemon/specialist-tools.js +78 -9
  58. package/dist/heart/daemon/staged-restart.js +114 -0
  59. package/dist/heart/daemon/task-scheduler.js +3 -25
  60. package/dist/heart/daemon/thoughts.js +507 -0
  61. package/dist/heart/daemon/update-checker.js +111 -0
  62. package/dist/heart/daemon/update-hooks.js +138 -0
  63. package/dist/heart/daemon/wrapper-publish-guard.js +86 -0
  64. package/dist/heart/delegation.js +62 -0
  65. package/dist/heart/identity.js +136 -55
  66. package/dist/heart/kicks.js +2 -20
  67. package/dist/heart/migrate-config.js +127 -0
  68. package/dist/heart/model-capabilities.js +48 -0
  69. package/dist/heart/obligations.js +197 -0
  70. package/dist/heart/progress-story.js +42 -0
  71. package/dist/heart/provider-failover.js +88 -0
  72. package/dist/heart/provider-ping.js +172 -0
  73. package/dist/heart/providers/anthropic-token.js +163 -0
  74. package/dist/heart/providers/anthropic.js +196 -36
  75. package/dist/heart/providers/azure.js +116 -10
  76. package/dist/heart/providers/github-copilot.js +157 -0
  77. package/dist/heart/providers/minimax.js +33 -3
  78. package/dist/heart/providers/openai-codex.js +51 -16
  79. package/dist/heart/session-activity.js +172 -0
  80. package/dist/heart/session-recall.js +216 -0
  81. package/dist/heart/streaming.js +62 -32
  82. package/dist/heart/target-resolution.js +127 -0
  83. package/dist/heart/tool-activity-callbacks.js +36 -0
  84. package/dist/heart/tool-description.js +135 -0
  85. package/dist/heart/tool-loop.js +200 -0
  86. package/dist/heart/turn-coordinator.js +28 -0
  87. package/dist/mind/associative-recall.js +122 -32
  88. package/dist/mind/bundle-manifest.js +14 -0
  89. package/dist/mind/context.js +125 -14
  90. package/dist/mind/{memory.js → diary.js} +70 -56
  91. package/dist/mind/file-state.js +179 -0
  92. package/dist/mind/first-impressions.js +16 -2
  93. package/dist/mind/friends/channel.js +56 -0
  94. package/dist/mind/friends/group-context.js +144 -0
  95. package/dist/mind/friends/store-file.js +19 -0
  96. package/dist/mind/friends/trust-explanation.js +74 -0
  97. package/dist/mind/friends/types.js +8 -0
  98. package/dist/mind/journal-index.js +161 -0
  99. package/dist/mind/obligation-steering.js +221 -0
  100. package/dist/mind/obligations.js +134 -0
  101. package/dist/mind/pending.js +76 -9
  102. package/dist/mind/phrases.js +1 -0
  103. package/dist/mind/prompt.js +721 -102
  104. package/dist/mind/scrutiny.js +173 -0
  105. package/dist/mind/token-estimate.js +8 -12
  106. package/dist/nerves/cli-logging.js +15 -2
  107. package/dist/nerves/coverage/audit.js +1 -1
  108. package/dist/nerves/coverage/run-artifacts.js +1 -1
  109. package/dist/nerves/event-buffer.js +111 -0
  110. package/dist/nerves/index.js +63 -3
  111. package/dist/nerves/observation.js +20 -0
  112. package/dist/nerves/runtime.js +5 -1
  113. package/dist/outlook-ui/assets/index-DN6vVYPm.css +1 -0
  114. package/dist/outlook-ui/assets/index-M1l1tDTX.js +61 -0
  115. package/dist/outlook-ui/index.html +15 -0
  116. package/dist/repertoire/ado-semantic.js +11 -5
  117. package/dist/repertoire/coding/codex-jsonl.js +64 -0
  118. package/dist/repertoire/coding/context-pack.js +254 -0
  119. package/dist/repertoire/coding/feedback.js +197 -30
  120. package/dist/repertoire/coding/manager.js +149 -2
  121. package/dist/repertoire/coding/spawner.js +55 -9
  122. package/dist/repertoire/coding/tools.js +170 -7
  123. package/dist/repertoire/guardrails.js +291 -0
  124. package/dist/repertoire/mcp-client.js +254 -0
  125. package/dist/repertoire/mcp-manager.js +198 -0
  126. package/dist/repertoire/shell-sessions.js +133 -0
  127. package/dist/repertoire/skills.js +14 -23
  128. package/dist/repertoire/tasks/board.js +43 -5
  129. package/dist/repertoire/tasks/fix.js +182 -0
  130. package/dist/repertoire/tasks/index.js +28 -10
  131. package/dist/repertoire/tasks/lifecycle.js +2 -2
  132. package/dist/repertoire/tasks/parser.js +3 -2
  133. package/dist/repertoire/tasks/scanner.js +194 -37
  134. package/dist/repertoire/tasks/transitions.js +11 -28
  135. package/dist/repertoire/tools-base.js +1111 -327
  136. package/dist/repertoire/tools-bluebubbles.js +94 -0
  137. package/dist/repertoire/tools-github.js +1 -6
  138. package/dist/repertoire/tools-teams.js +12 -59
  139. package/dist/repertoire/tools.js +318 -124
  140. package/dist/scripts/claude-code-hook.js +41 -0
  141. package/dist/scripts/claude-code-stop-hook.js +47 -0
  142. package/dist/senses/attention-queue.js +97 -0
  143. package/dist/senses/bluebubbles-client.js +102 -3
  144. package/dist/senses/bluebubbles-entry.js +2 -0
  145. package/dist/senses/bluebubbles-inbound-log.js +109 -0
  146. package/dist/senses/bluebubbles-media.js +118 -23
  147. package/dist/senses/bluebubbles-model.js +12 -4
  148. package/dist/senses/bluebubbles-mutation-log.js +45 -5
  149. package/dist/senses/bluebubbles-runtime-state.js +109 -0
  150. package/dist/senses/bluebubbles-session-cleanup.js +72 -0
  151. package/dist/senses/bluebubbles.js +1008 -102
  152. package/dist/senses/cli/adapter.js +274 -0
  153. package/dist/senses/cli/ink-app.js +156 -0
  154. package/dist/senses/cli/inline-diff.js +64 -0
  155. package/dist/senses/cli/message-list.js +51 -0
  156. package/dist/senses/cli/ouro-tui.js +323 -0
  157. package/dist/senses/cli/spinner-imperative.js +135 -0
  158. package/dist/senses/cli/spinner.js +101 -0
  159. package/dist/senses/cli/status-line.js +60 -0
  160. package/dist/senses/cli/streaming-markdown.js +525 -0
  161. package/dist/senses/cli/tool-display.js +83 -0
  162. package/dist/senses/cli/tool-render.js +85 -0
  163. package/dist/senses/cli/tui-store.js +198 -0
  164. package/dist/senses/cli/virtual-list.js +35 -0
  165. package/dist/senses/cli-entry.js +1 -1
  166. package/dist/senses/cli-layout.js +187 -0
  167. package/dist/senses/cli.js +593 -240
  168. package/dist/senses/commands.js +38 -1
  169. package/dist/senses/continuity.js +94 -0
  170. package/dist/senses/habit-turn-message.js +108 -0
  171. package/dist/senses/inner-dialog-worker.js +112 -19
  172. package/dist/senses/inner-dialog.js +592 -84
  173. package/dist/senses/pipeline.js +545 -0
  174. package/dist/senses/shared-turn.js +196 -0
  175. package/dist/senses/surface-tool.js +95 -0
  176. package/dist/senses/teams.js +667 -120
  177. package/dist/senses/trust-gate.js +112 -2
  178. package/package.json +16 -5
  179. package/skills/configure-dev-tools.md +81 -0
  180. package/subagents/README.md +4 -70
  181. package/dist/heart/daemon/subagent-installer.js +0 -134
  182. package/dist/senses/debug-activity.js +0 -107
  183. package/subagents/work-doer.md +0 -235
  184. package/subagents/work-merger.md +0 -618
  185. package/subagents/work-planner.md +0 -382
@@ -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("../../heart/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: "spawn a coding session using claude/codex and explicit task-threaded guidance",
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: "inspect coding sessions; omit sessionId to list all active/known sessions",
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: "show recent stdout/stderr tail for a coding session in a readable format",
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
  ];
@@ -0,0 +1,291 @@
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.OURO_CLI_TRUST_MANIFEST = void 0;
37
+ exports.guardInvocation = guardInvocation;
38
+ const fs = __importStar(require("node:fs"));
39
+ const os = __importStar(require("node:os"));
40
+ const types_1 = require("../mind/friends/types");
41
+ const runtime_1 = require("../nerves/runtime");
42
+ const deny = (reason) => ({ allowed: false, reason });
43
+ const allow = { allowed: true };
44
+ // --- reason templates ---
45
+ // Structural reasons (always-on, apply to everyone)
46
+ const REASONS = {
47
+ readBeforeEdit: "i need to read that file first before i can edit it.",
48
+ readBeforeOverwrite: "i need to read that file first before i can overwrite it.",
49
+ protectedPath: "that path is protected — i can read it but not modify it.",
50
+ destructiveCommand: "that command is too dangerous to run — it could cause irreversible damage.",
51
+ // Trust reasons (vary by relationship)
52
+ needsTrust: "i'd need a closer friend to vouch for you before i can do that.",
53
+ needsTrustForWrite: "i'd need a closer friend to vouch for you before i can write files outside my home.",
54
+ };
55
+ // --- read-only tools that never need guardrails ---
56
+ const READ_ONLY_TOOLS = new Set(["read_file", "glob", "grep"]);
57
+ // --- protected path detection ---
58
+ const PROTECTED_PATH_SEGMENTS = [".git/"];
59
+ function getProtectedAbsolutePrefixes() {
60
+ return [`${os.homedir()}/.agentsecrets/`];
61
+ }
62
+ function isProtectedPath(filePath) {
63
+ for (const segment of PROTECTED_PATH_SEGMENTS) {
64
+ if (filePath.includes(`/${segment}`) || filePath.startsWith(segment))
65
+ return true;
66
+ }
67
+ for (const prefix of getProtectedAbsolutePrefixes()) {
68
+ if (filePath.startsWith(prefix))
69
+ return true;
70
+ }
71
+ return false;
72
+ }
73
+ // --- destructive shell patterns ---
74
+ const DESTRUCTIVE_PATTERNS = [
75
+ /\brm\s+(-\w*\s+)*-\w*r\w*\s+(-\w+\s+)*[/~]/, // rm -rf / or rm -rf ~
76
+ /\bchmod\s+(-\w*\s+)*-\w*R\w*\s+\d+\s+\//, // chmod -R 777 /
77
+ /\bmkfs\b/, // mkfs.*
78
+ /\bdd\s+if=/, // dd if=
79
+ ];
80
+ function isDestructiveShellCommand(command) {
81
+ return DESTRUCTIVE_PATTERNS.some((p) => p.test(command));
82
+ }
83
+ // --- compound command splitting ---
84
+ // Shell operators that chain commands: &&, ||, ;, |, $(), backticks
85
+ const COMPOUND_SEPARATORS = /\s*(?:&&|\|\||;|\|)\s*/;
86
+ const SUBSHELL_PATTERN = /\$\(|`/;
87
+ function splitShellCommands(command) {
88
+ if (SUBSHELL_PATTERN.test(command))
89
+ return [command];
90
+ return command.split(COMPOUND_SEPARATORS).filter(Boolean);
91
+ }
92
+ // --- shell commands that write to protected paths ---
93
+ function shellWritesToProtectedPath(command) {
94
+ const redirectMatch = command.match(/>\s*(\S+)/);
95
+ if (redirectMatch && isProtectedPath(redirectMatch[1]))
96
+ return true;
97
+ const teeMatch = command.match(/tee\s+(?:-\w+\s+)*(\S+)/);
98
+ if (teeMatch && isProtectedPath(teeMatch[1]))
99
+ return true;
100
+ return false;
101
+ }
102
+ // --- structural guardrail checks (always on, all trust levels) ---
103
+ function checkReadBeforeWrite(toolName, args, context) {
104
+ if (toolName === "edit_file") {
105
+ const filePath = args.path || "";
106
+ if (!context.readPaths.has(filePath))
107
+ return deny(REASONS.readBeforeEdit);
108
+ }
109
+ if (toolName === "write_file") {
110
+ const filePath = args.path || "";
111
+ if (context.readPaths.has(filePath))
112
+ return allow;
113
+ if (!fs.existsSync(filePath))
114
+ return allow;
115
+ return deny(REASONS.readBeforeOverwrite);
116
+ }
117
+ return allow;
118
+ }
119
+ function checkDestructiveShellPatterns(toolName, args) {
120
+ if (toolName !== "shell")
121
+ return allow;
122
+ const command = args.command || "";
123
+ // Check each subcommand in compound commands for destructive patterns
124
+ for (const sub of splitShellCommands(command)) {
125
+ if (isDestructiveShellCommand(sub))
126
+ return deny(REASONS.destructiveCommand);
127
+ }
128
+ return allow;
129
+ }
130
+ function checkProtectedPaths(toolName, args) {
131
+ if (toolName === "write_file" || toolName === "edit_file") {
132
+ const filePath = args.path || "";
133
+ if (isProtectedPath(filePath))
134
+ return deny(REASONS.protectedPath);
135
+ }
136
+ if (toolName === "shell") {
137
+ const command = args.command || "";
138
+ if (shellWritesToProtectedPath(command))
139
+ return deny(REASONS.protectedPath);
140
+ }
141
+ return allow;
142
+ }
143
+ function checkStructuralGuardrails(toolName, args, context) {
144
+ const protectedResult = checkProtectedPaths(toolName, args);
145
+ if (!protectedResult.allowed)
146
+ return protectedResult;
147
+ const destructiveResult = checkDestructiveShellPatterns(toolName, args);
148
+ if (!destructiveResult.allowed)
149
+ return destructiveResult;
150
+ return checkReadBeforeWrite(toolName, args, context);
151
+ }
152
+ // --- ouro CLI trust manifest ---
153
+ /** Minimum trust level required for each ouro CLI subcommand. */
154
+ exports.OURO_CLI_TRUST_MANIFEST = {
155
+ whoami: "acquaintance",
156
+ changelog: "acquaintance",
157
+ "session list": "acquaintance",
158
+ "task board": "friend",
159
+ "task create": "friend",
160
+ "task update": "friend",
161
+ "task show": "friend",
162
+ "task actionable": "friend",
163
+ "task deps": "friend",
164
+ "task sessions": "friend",
165
+ "task fix": "friend",
166
+ "friend list": "friend",
167
+ "friend show": "friend",
168
+ "friend create": "friend",
169
+ "friend update": "family",
170
+ "reminder create": "friend",
171
+ "config model": "friend",
172
+ "config models": "friend",
173
+ "mcp list": "acquaintance",
174
+ "mcp call": "friend",
175
+ auth: "family",
176
+ "auth verify": "family",
177
+ "auth switch": "family",
178
+ rollback: "family",
179
+ versions: "acquaintance",
180
+ };
181
+ // --- trust level comparison ---
182
+ const LEVEL_ORDER = {
183
+ stranger: 0,
184
+ acquaintance: 1,
185
+ friend: 2,
186
+ family: 3,
187
+ };
188
+ function trustLevelSatisfied(required, actual) {
189
+ return LEVEL_ORDER[actual] >= LEVEL_ORDER[required];
190
+ }
191
+ // --- general CLI allowlists for acquaintance ---
192
+ const ACQUAINTANCE_SHELL_ALLOWLIST = new Set([
193
+ "cat", "ls", "head", "tail", "wc", "file", "stat", "which", "echo",
194
+ "pwd", "env", "printenv", "whoami", "date", "uname",
195
+ ]);
196
+ const ACQUAINTANCE_GIT_ALLOWLIST = new Set([
197
+ "status", "log", "show", "diff", "branch",
198
+ ]);
199
+ // --- trust-level shell guardrails ---
200
+ function resolveOuroSubcommand(command) {
201
+ const afterOuro = command.replace(/^ouro\s+/, "").trim();
202
+ /* v8 ignore next -- bare "ouro" is caught upstream by checkShellTrustGuardrails @preserve */
203
+ if (!afterOuro)
204
+ return null;
205
+ const tokens = afterOuro.split(/\s+/);
206
+ const twoWord = tokens.length >= 2 ? `${tokens[0]} ${tokens[1]}` : null;
207
+ // Two-word match first (e.g. "task board"), then one-word (e.g. "whoami")
208
+ if (twoWord && exports.OURO_CLI_TRUST_MANIFEST[twoWord])
209
+ return twoWord;
210
+ if (exports.OURO_CLI_TRUST_MANIFEST[tokens[0]])
211
+ return tokens[0];
212
+ return null;
213
+ }
214
+ function checkSingleShellCommandTrust(command, trustLevel) {
215
+ const trimmed = command.trim();
216
+ const tokens = trimmed.split(/\s+/);
217
+ const firstToken = tokens[0] || "";
218
+ // ouro CLI — check per-subcommand trust manifest
219
+ if (firstToken === "ouro") {
220
+ const subcommand = resolveOuroSubcommand(trimmed);
221
+ const requiredLevel = subcommand ? exports.OURO_CLI_TRUST_MANIFEST[subcommand] : "friend";
222
+ if (trustLevelSatisfied(requiredLevel, trustLevel))
223
+ return allow;
224
+ return deny(REASONS.needsTrust);
225
+ }
226
+ // git — check subcommand allowlist
227
+ if (firstToken === "git") {
228
+ const gitSub = tokens[1] || "";
229
+ if (ACQUAINTANCE_GIT_ALLOWLIST.has(gitSub))
230
+ return allow;
231
+ return deny(REASONS.needsTrust);
232
+ }
233
+ // General CLI — check allowlist
234
+ if (ACQUAINTANCE_SHELL_ALLOWLIST.has(firstToken))
235
+ return allow;
236
+ return deny(REASONS.needsTrust);
237
+ }
238
+ function checkShellTrustGuardrails(command, trustLevel) {
239
+ // Subshell patterns ($(), backticks) can't be reliably split — check as single command
240
+ /* v8 ignore next -- subshell branch: tested via guardrails.test.ts @preserve */
241
+ if (SUBSHELL_PATTERN.test(command)) {
242
+ return checkSingleShellCommandTrust(command, trustLevel);
243
+ }
244
+ // Compound commands: check each subcommand individually
245
+ const subcommands = splitShellCommands(command);
246
+ if (subcommands.length === 0)
247
+ return checkSingleShellCommandTrust(command, trustLevel);
248
+ for (const sub of subcommands) {
249
+ const result = checkSingleShellCommandTrust(sub, trustLevel);
250
+ if (!result.allowed)
251
+ return result;
252
+ }
253
+ return allow;
254
+ }
255
+ function checkWriteTrustGuardrails(toolName, args, context) {
256
+ if (toolName !== "write_file" && toolName !== "edit_file")
257
+ return allow;
258
+ const filePath = args.path || "";
259
+ if (context.agentRoot && filePath.startsWith(context.agentRoot))
260
+ return allow;
261
+ if (!context.agentRoot)
262
+ return allow;
263
+ return deny(REASONS.needsTrustForWrite);
264
+ }
265
+ function checkTrustLevelGuardrails(toolName, args, context) {
266
+ // Trusted levels (family/friend) — no trust guardrails. Undefined defaults to friend.
267
+ if ((0, types_1.isTrustedLevel)(context.trustLevel))
268
+ return allow;
269
+ if (toolName === "shell") {
270
+ return checkShellTrustGuardrails(args.command || "", context.trustLevel);
271
+ }
272
+ return checkWriteTrustGuardrails(toolName, args, context);
273
+ }
274
+ // --- main entry point ---
275
+ function guardInvocation(toolName, args, context) {
276
+ (0, runtime_1.emitNervesEvent)({
277
+ component: "tools",
278
+ event: "tools.guard_check",
279
+ message: "guardrail check",
280
+ meta: { toolName },
281
+ });
282
+ // Read-only tools are always allowed (no structural or trust guardrails)
283
+ if (READ_ONLY_TOOLS.has(toolName))
284
+ return allow;
285
+ // Layer 1: structural guardrails (always on)
286
+ const structuralResult = checkStructuralGuardrails(toolName, args, context);
287
+ if (!structuralResult.allowed)
288
+ return structuralResult;
289
+ // Layer 2: trust-level guardrails (varies by friend's trust)
290
+ return checkTrustLevelGuardrails(toolName, args, context);
291
+ }