@ouro.bot/cli 0.1.0-alpha.16 → 0.1.0-alpha.161

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 (168) 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 +1088 -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 +608 -123
  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 +2467 -205
  19. package/dist/heart/daemon/daemon-entry.js +131 -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 +445 -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 +159 -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 +369 -0
  41. package/dist/heart/daemon/outlook-read.js +1281 -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 -8
  57. package/dist/heart/daemon/specialist-tools.js +53 -4
  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 +117 -33
  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/first-impressions.js +16 -2
  92. package/dist/mind/friends/channel.js +56 -0
  93. package/dist/mind/friends/group-context.js +144 -0
  94. package/dist/mind/friends/store-file.js +19 -0
  95. package/dist/mind/friends/trust-explanation.js +74 -0
  96. package/dist/mind/friends/types.js +8 -0
  97. package/dist/mind/journal-index.js +161 -0
  98. package/dist/mind/obligation-steering.js +221 -0
  99. package/dist/mind/obligations.js +134 -0
  100. package/dist/mind/pending.js +76 -9
  101. package/dist/mind/phrases.js +1 -0
  102. package/dist/mind/prompt.js +651 -91
  103. package/dist/mind/token-estimate.js +8 -12
  104. package/dist/nerves/cli-logging.js +15 -2
  105. package/dist/nerves/coverage/run-artifacts.js +1 -1
  106. package/dist/nerves/event-buffer.js +111 -0
  107. package/dist/nerves/index.js +63 -3
  108. package/dist/nerves/observation.js +20 -0
  109. package/dist/nerves/runtime.js +5 -1
  110. package/dist/outlook-ui/assets/index-CXQG0mVx.js +61 -0
  111. package/dist/outlook-ui/assets/index-Gq7EdfkO.css +1 -0
  112. package/dist/outlook-ui/index.html +15 -0
  113. package/dist/repertoire/ado-semantic.js +6 -0
  114. package/dist/repertoire/coding/codex-jsonl.js +64 -0
  115. package/dist/repertoire/coding/context-pack.js +254 -0
  116. package/dist/repertoire/coding/feedback.js +301 -0
  117. package/dist/repertoire/coding/index.js +4 -1
  118. package/dist/repertoire/coding/manager.js +210 -4
  119. package/dist/repertoire/coding/spawner.js +58 -12
  120. package/dist/repertoire/coding/tools.js +176 -4
  121. package/dist/repertoire/guardrails.js +291 -0
  122. package/dist/repertoire/mcp-client.js +254 -0
  123. package/dist/repertoire/mcp-manager.js +198 -0
  124. package/dist/repertoire/skills.js +14 -23
  125. package/dist/repertoire/tasks/board.js +43 -5
  126. package/dist/repertoire/tasks/fix.js +182 -0
  127. package/dist/repertoire/tasks/index.js +28 -10
  128. package/dist/repertoire/tasks/lifecycle.js +2 -2
  129. package/dist/repertoire/tasks/parser.js +3 -2
  130. package/dist/repertoire/tasks/scanner.js +194 -37
  131. package/dist/repertoire/tasks/transitions.js +11 -28
  132. package/dist/repertoire/tools-base.js +992 -273
  133. package/dist/repertoire/tools-bluebubbles.js +94 -0
  134. package/dist/repertoire/tools-github.js +1 -6
  135. package/dist/repertoire/tools-teams.js +12 -59
  136. package/dist/repertoire/tools.js +281 -101
  137. package/dist/scripts/claude-code-hook.js +41 -0
  138. package/dist/scripts/claude-code-stop-hook.js +47 -0
  139. package/dist/senses/attention-queue.js +97 -0
  140. package/dist/senses/bluebubbles-client.js +258 -5
  141. package/dist/senses/bluebubbles-entry.js +2 -0
  142. package/dist/senses/bluebubbles-inbound-log.js +109 -0
  143. package/dist/senses/bluebubbles-media.js +339 -0
  144. package/dist/senses/bluebubbles-model.js +12 -4
  145. package/dist/senses/bluebubbles-mutation-log.js +45 -5
  146. package/dist/senses/bluebubbles-runtime-state.js +109 -0
  147. package/dist/senses/bluebubbles-session-cleanup.js +72 -0
  148. package/dist/senses/bluebubbles.js +1040 -45
  149. package/dist/senses/cli-entry.js +1 -1
  150. package/dist/senses/cli-layout.js +187 -0
  151. package/dist/senses/cli.js +419 -152
  152. package/dist/senses/commands.js +38 -1
  153. package/dist/senses/continuity.js +94 -0
  154. package/dist/senses/habit-turn-message.js +108 -0
  155. package/dist/senses/inner-dialog-worker.js +112 -19
  156. package/dist/senses/inner-dialog.js +592 -84
  157. package/dist/senses/pipeline.js +502 -0
  158. package/dist/senses/shared-turn.js +196 -0
  159. package/dist/senses/surface-tool.js +95 -0
  160. package/dist/senses/teams.js +667 -120
  161. package/dist/senses/trust-gate.js +112 -2
  162. package/package.json +13 -5
  163. package/skills/configure-dev-tools.md +81 -0
  164. package/subagents/README.md +4 -70
  165. package/dist/heart/daemon/subagent-installer.js +0 -134
  166. package/subagents/work-doer.md +0 -233
  167. package/subagents/work-merger.md +0 -624
  168. package/subagents/work-planner.md +0 -373
@@ -2,6 +2,9 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.codingToolDefinitions = void 0;
4
4
  const index_1 = require("./index");
5
+ const context_pack_1 = require("./context-pack");
6
+ const identity_1 = require("../../heart/identity");
7
+ const obligations_1 = require("../../heart/obligations");
5
8
  const runtime_1 = require("../../nerves/runtime");
6
9
  const RUNNERS = ["claude", "codex"];
7
10
  function requireArg(args, key) {
@@ -29,6 +32,79 @@ function emitCodingToolEvent(toolName) {
29
32
  meta: { toolName },
30
33
  });
31
34
  }
35
+ function sameOriginSession(left, right) {
36
+ if (!left && !right)
37
+ return true;
38
+ if (!left || !right)
39
+ return false;
40
+ return left.friendId === right.friendId && left.channel === right.channel && left.key === right.key;
41
+ }
42
+ function matchesReusableCodingSession(session, request) {
43
+ if (session.status !== "spawning" && session.status !== "running" && session.status !== "waiting_input" && session.status !== "stalled") {
44
+ return false;
45
+ }
46
+ const scopeMatches = request.scopeFile ? session.scopeFile === request.scopeFile : true;
47
+ const stateMatches = request.stateFile ? session.stateFile === request.stateFile : true;
48
+ return (session.runner === request.runner &&
49
+ session.workdir === request.workdir &&
50
+ session.taskRef === request.taskRef &&
51
+ scopeMatches &&
52
+ stateMatches &&
53
+ session.obligationId === request.obligationId &&
54
+ sameOriginSession(request.originSession, session.originSession));
55
+ }
56
+ function latestSessionFirst(left, right) {
57
+ const lastActivityDelta = Date.parse(right.lastActivityAt) - Date.parse(left.lastActivityAt);
58
+ if (lastActivityDelta !== 0)
59
+ return lastActivityDelta;
60
+ return right.id.localeCompare(left.id);
61
+ }
62
+ function findReusableCodingSession(sessions, request) {
63
+ const matches = sessions.filter((session) => matchesReusableCodingSession(session, request)).sort(latestSessionFirst);
64
+ return matches[0] ?? null;
65
+ }
66
+ function isLiveCodingStatus(status) {
67
+ return status === "spawning" || status === "running" || status === "waiting_input" || status === "stalled";
68
+ }
69
+ function rankCodingStatusSession(session, currentSession) {
70
+ return sameOriginSession({
71
+ friendId: currentSession.friendId,
72
+ channel: currentSession.channel,
73
+ key: currentSession.key,
74
+ }, session.originSession)
75
+ ? 0
76
+ : 1;
77
+ }
78
+ function selectCodingStatusSessions(sessions, currentSession) {
79
+ if (sessions.length === 0)
80
+ return [];
81
+ if (!currentSession) {
82
+ return sessions;
83
+ }
84
+ const activeSessions = sessions.filter((session) => isLiveCodingStatus(session.status)).sort(latestSessionFirst);
85
+ if (activeSessions.length > 0) {
86
+ return activeSessions.sort((left, right) => {
87
+ const rankDelta = rankCodingStatusSession(left, currentSession) - rankCodingStatusSession(right, currentSession);
88
+ if (rankDelta !== 0)
89
+ return rankDelta;
90
+ return latestSessionFirst(left, right);
91
+ });
92
+ }
93
+ const matchingClosedSessions = sessions
94
+ .filter((session) => sameOriginSession({
95
+ friendId: currentSession.friendId,
96
+ channel: currentSession.channel,
97
+ key: currentSession.key,
98
+ }, session.originSession))
99
+ .sort(latestSessionFirst);
100
+ if (matchingClosedSessions.length > 0) {
101
+ return matchingClosedSessions;
102
+ }
103
+ return [...sessions].sort(latestSessionFirst);
104
+ }
105
+ function buildCodingObligationContent(taskRef) {
106
+ return `finish ${taskRef} and bring the result back`;
107
+ }
32
108
  const codingSpawnTool = {
33
109
  type: "function",
34
110
  function: {
@@ -61,6 +137,20 @@ const codingStatusTool = {
61
137
  },
62
138
  },
63
139
  };
140
+ const codingTailTool = {
141
+ type: "function",
142
+ function: {
143
+ name: "coding_tail",
144
+ description: "show recent stdout/stderr tail for a coding session in a readable format",
145
+ parameters: {
146
+ type: "object",
147
+ properties: {
148
+ sessionId: { type: "string" },
149
+ },
150
+ required: ["sessionId"],
151
+ },
152
+ },
153
+ };
64
154
  const codingSendInputTool = {
65
155
  type: "function",
66
156
  function: {
@@ -93,7 +183,7 @@ const codingKillTool = {
93
183
  exports.codingToolDefinitions = [
94
184
  {
95
185
  tool: codingSpawnTool,
96
- handler: async (args) => {
186
+ handler: async (args, ctx) => {
97
187
  emitCodingToolEvent("coding_spawn");
98
188
  const rawRunner = requireArg(args, "runner");
99
189
  if (!rawRunner)
@@ -116,30 +206,110 @@ exports.codingToolDefinitions = [
116
206
  prompt,
117
207
  taskRef,
118
208
  };
209
+ if (ctx?.currentSession && ctx.currentSession.channel !== "inner") {
210
+ request.originSession = {
211
+ friendId: ctx.currentSession.friendId,
212
+ channel: ctx.currentSession.channel,
213
+ key: ctx.currentSession.key,
214
+ };
215
+ const obligation = (0, obligations_1.findPendingObligationForOrigin)((0, identity_1.getAgentRoot)(), request.originSession);
216
+ if (obligation) {
217
+ request.obligationId = obligation.id;
218
+ }
219
+ }
119
220
  const scopeFile = optionalArg(args, "scopeFile");
120
221
  if (scopeFile)
121
222
  request.scopeFile = scopeFile;
122
223
  const stateFile = optionalArg(args, "stateFile");
123
224
  if (stateFile)
124
225
  request.stateFile = stateFile;
125
- const session = await (0, index_1.getCodingSessionManager)().spawnSession(request);
226
+ const manager = (0, index_1.getCodingSessionManager)();
227
+ const existingSessions = manager.listSessions();
228
+ const existingSession = findReusableCodingSession(existingSessions, request);
229
+ if (existingSession) {
230
+ (0, runtime_1.emitNervesEvent)({
231
+ component: "repertoire",
232
+ event: "repertoire.coding_session_reused",
233
+ message: "reused active coding session",
234
+ meta: { id: existingSession.id, runner: existingSession.runner, taskRef: existingSession.taskRef },
235
+ });
236
+ if (ctx?.codingFeedback) {
237
+ (0, index_1.attachCodingSessionFeedback)(manager, existingSession, ctx.codingFeedback);
238
+ }
239
+ return JSON.stringify({ ...existingSession, reused: true });
240
+ }
241
+ if (request.originSession && !request.obligationId) {
242
+ const created = (0, obligations_1.createObligation)((0, identity_1.getAgentRoot)(), {
243
+ origin: request.originSession,
244
+ content: buildCodingObligationContent(taskRef),
245
+ });
246
+ request.obligationId = created.id;
247
+ }
248
+ if (!request.scopeFile || !request.stateFile) {
249
+ const generated = (0, context_pack_1.prepareCodingContextPack)({
250
+ request: { ...request },
251
+ existingSessions,
252
+ activeWorkFrame: ctx?.activeWorkFrame,
253
+ });
254
+ if (!request.scopeFile)
255
+ request.scopeFile = generated.scopeFile;
256
+ if (!request.stateFile)
257
+ request.stateFile = generated.stateFile;
258
+ }
259
+ const session = await manager.spawnSession(request);
260
+ if (session.obligationId) {
261
+ (0, obligations_1.advanceObligation)((0, identity_1.getAgentRoot)(), session.obligationId, {
262
+ status: "investigating",
263
+ currentSurface: { kind: "coding", label: `${session.runner} ${session.id}` },
264
+ latestNote: session.originSession
265
+ ? `coding session started for ${session.originSession.channel}/${session.originSession.key}`
266
+ : "coding session started",
267
+ });
268
+ }
269
+ if (args.runner === "codex" && args.taskRef) {
270
+ (0, runtime_1.emitNervesEvent)({
271
+ component: "repertoire",
272
+ event: "repertoire.coding_codex_spawned",
273
+ message: "spawned codex coding session",
274
+ meta: { sessionId: session.id, taskRef: args.taskRef },
275
+ });
276
+ }
277
+ if (ctx?.codingFeedback) {
278
+ (0, index_1.attachCodingSessionFeedback)(manager, session, ctx.codingFeedback);
279
+ }
126
280
  return JSON.stringify(session);
127
281
  },
282
+ summaryKeys: ["runner", "workdir", "taskRef"],
128
283
  },
129
284
  {
130
285
  tool: codingStatusTool,
131
- handler: (args) => {
286
+ handler: (args, ctx) => {
132
287
  emitCodingToolEvent("coding_status");
133
288
  const manager = (0, index_1.getCodingSessionManager)();
134
289
  const sessionId = requireArg(args, "sessionId");
135
290
  if (!sessionId) {
136
- return JSON.stringify(manager.listSessions());
291
+ return JSON.stringify(selectCodingStatusSessions(manager.listSessions(), ctx?.currentSession));
137
292
  }
138
293
  const session = manager.getSession(sessionId);
139
294
  if (!session)
140
295
  return `session not found: ${sessionId}`;
141
296
  return JSON.stringify(session);
142
297
  },
298
+ summaryKeys: ["sessionId"],
299
+ },
300
+ {
301
+ tool: codingTailTool,
302
+ handler: (args) => {
303
+ emitCodingToolEvent("coding_tail");
304
+ const sessionId = requireArg(args, "sessionId");
305
+ if (!sessionId)
306
+ return "sessionId is required";
307
+ const session = (0, index_1.getCodingSessionManager)().getSession(sessionId);
308
+ if (!session)
309
+ return `session not found: ${sessionId}`;
310
+ return (0, index_1.formatCodingTail)(session);
311
+ },
312
+ summaryKeys: ["sessionId"],
143
313
  },
144
314
  {
145
315
  tool: codingSendInputTool,
@@ -153,6 +323,7 @@ exports.codingToolDefinitions = [
153
323
  return "input is required";
154
324
  return JSON.stringify((0, index_1.getCodingSessionManager)().sendInput(sessionId, input));
155
325
  },
326
+ summaryKeys: ["sessionId", "input"],
156
327
  },
157
328
  {
158
329
  tool: codingKillTool,
@@ -163,5 +334,6 @@ exports.codingToolDefinitions = [
163
334
  return "sessionId is required";
164
335
  return JSON.stringify((0, index_1.getCodingSessionManager)().killSession(sessionId));
165
336
  },
337
+ summaryKeys: ["sessionId"],
166
338
  },
167
339
  ];
@@ -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
+ }