@ouro.bot/cli 0.1.0-alpha.15 → 0.1.0-alpha.151

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 (150) hide show
  1. package/AdoptionSpecialist.ouro/psyche/SOUL.md +2 -2
  2. package/AdoptionSpecialist.ouro/psyche/identities/monty.md +2 -2
  3. package/README.md +185 -191
  4. package/changelog.json +991 -0
  5. package/dist/heart/active-work.js +633 -0
  6. package/dist/heart/bridges/manager.js +358 -0
  7. package/dist/heart/bridges/state-machine.js +135 -0
  8. package/dist/heart/bridges/store.js +123 -0
  9. package/dist/heart/commitments.js +105 -0
  10. package/dist/heart/config.js +66 -21
  11. package/dist/heart/core.js +645 -112
  12. package/dist/heart/cross-chat-delivery.js +146 -0
  13. package/dist/heart/daemon/agent-discovery.js +81 -0
  14. package/dist/heart/daemon/agent-service.js +360 -0
  15. package/dist/heart/daemon/auth-flow.js +457 -0
  16. package/dist/heart/daemon/cadence.js +70 -0
  17. package/dist/heart/daemon/daemon-cli.js +2301 -205
  18. package/dist/heart/daemon/daemon-entry.js +131 -4
  19. package/dist/heart/daemon/daemon-runtime-sync.js +212 -0
  20. package/dist/heart/daemon/daemon-tombstone.js +106 -0
  21. package/dist/heart/daemon/daemon.js +372 -3
  22. package/dist/heart/daemon/habit-migration.js +181 -0
  23. package/dist/heart/daemon/habit-parser.js +119 -0
  24. package/dist/heart/daemon/habit-scheduler.js +252 -0
  25. package/dist/heart/daemon/hatch-animation.js +10 -3
  26. package/dist/heart/daemon/hatch-flow.js +28 -111
  27. package/dist/heart/daemon/hooks/bundle-meta.js +92 -0
  28. package/dist/heart/daemon/inner-status.js +89 -0
  29. package/dist/heart/daemon/launchd.js +159 -0
  30. package/dist/heart/daemon/log-tailer.js +4 -3
  31. package/dist/heart/daemon/mcp-server.js +591 -0
  32. package/dist/heart/daemon/message-router.js +17 -8
  33. package/dist/heart/daemon/os-cron-deps.js +134 -0
  34. package/dist/heart/daemon/ouro-bot-global-installer.js +128 -0
  35. package/dist/heart/daemon/ouro-path-installer.js +78 -34
  36. package/dist/heart/daemon/ouro-version-manager.js +171 -0
  37. package/dist/heart/daemon/process-manager.js +75 -0
  38. package/dist/heart/daemon/run-hooks.js +37 -0
  39. package/dist/heart/daemon/runtime-logging.js +58 -15
  40. package/dist/heart/daemon/runtime-metadata.js +219 -0
  41. package/dist/heart/daemon/runtime-mode.js +67 -0
  42. package/dist/heart/daemon/sense-manager.js +50 -2
  43. package/dist/heart/daemon/session-id-resolver.js +131 -0
  44. package/dist/heart/daemon/skill-management-installer.js +94 -0
  45. package/dist/heart/daemon/socket-client.js +202 -0
  46. package/dist/heart/daemon/specialist-orchestrator.js +2 -2
  47. package/dist/heart/daemon/specialist-prompt.js +10 -7
  48. package/dist/heart/daemon/specialist-tools.js +53 -4
  49. package/dist/heart/daemon/staged-restart.js +114 -0
  50. package/dist/heart/daemon/task-scheduler.js +3 -25
  51. package/dist/heart/daemon/thoughts.js +507 -0
  52. package/dist/heart/daemon/update-checker.js +111 -0
  53. package/dist/heart/daemon/update-hooks.js +138 -0
  54. package/dist/heart/daemon/wrapper-publish-guard.js +86 -0
  55. package/dist/heart/delegation.js +62 -0
  56. package/dist/heart/identity.js +64 -21
  57. package/dist/heart/kicks.js +2 -20
  58. package/dist/heart/model-capabilities.js +48 -0
  59. package/dist/heart/obligations.js +197 -0
  60. package/dist/heart/progress-story.js +42 -0
  61. package/dist/heart/provider-failover.js +88 -0
  62. package/dist/heart/provider-ping.js +159 -0
  63. package/dist/heart/providers/anthropic-token.js +163 -0
  64. package/dist/heart/providers/anthropic.js +195 -34
  65. package/dist/heart/providers/azure.js +115 -9
  66. package/dist/heart/providers/github-copilot.js +157 -0
  67. package/dist/heart/providers/minimax.js +33 -3
  68. package/dist/heart/providers/openai-codex.js +49 -14
  69. package/dist/heart/session-activity.js +173 -0
  70. package/dist/heart/session-recall.js +216 -0
  71. package/dist/heart/streaming.js +117 -33
  72. package/dist/heart/target-resolution.js +127 -0
  73. package/dist/heart/tool-loop.js +200 -0
  74. package/dist/heart/turn-coordinator.js +28 -0
  75. package/dist/mind/associative-recall.js +122 -32
  76. package/dist/mind/bundle-manifest.js +14 -0
  77. package/dist/mind/context.js +125 -14
  78. package/dist/mind/{memory.js → diary.js} +70 -56
  79. package/dist/mind/first-impressions.js +16 -2
  80. package/dist/mind/friends/channel.js +44 -0
  81. package/dist/mind/friends/group-context.js +144 -0
  82. package/dist/mind/friends/store-file.js +19 -0
  83. package/dist/mind/friends/trust-explanation.js +74 -0
  84. package/dist/mind/friends/types.js +8 -0
  85. package/dist/mind/journal-index.js +161 -0
  86. package/dist/mind/obligation-steering.js +221 -0
  87. package/dist/mind/obligations.js +134 -0
  88. package/dist/mind/pending.js +76 -9
  89. package/dist/mind/phrases.js +1 -0
  90. package/dist/mind/prompt.js +612 -88
  91. package/dist/mind/token-estimate.js +8 -12
  92. package/dist/nerves/cli-logging.js +15 -2
  93. package/dist/nerves/coverage/run-artifacts.js +1 -1
  94. package/dist/nerves/index.js +59 -2
  95. package/dist/nerves/runtime.js +5 -1
  96. package/dist/repertoire/ado-client.js +4 -2
  97. package/dist/repertoire/ado-semantic.js +6 -0
  98. package/dist/repertoire/coding/codex-jsonl.js +64 -0
  99. package/dist/repertoire/coding/context-pack.js +254 -0
  100. package/dist/repertoire/coding/feedback.js +301 -0
  101. package/dist/repertoire/coding/index.js +4 -1
  102. package/dist/repertoire/coding/manager.js +210 -4
  103. package/dist/repertoire/coding/spawner.js +58 -12
  104. package/dist/repertoire/coding/tools.js +176 -4
  105. package/dist/repertoire/data/ado-endpoints.json +188 -0
  106. package/dist/repertoire/guardrails.js +290 -0
  107. package/dist/repertoire/mcp-client.js +254 -0
  108. package/dist/repertoire/mcp-manager.js +198 -0
  109. package/dist/repertoire/skills.js +14 -23
  110. package/dist/repertoire/tasks/board.js +12 -0
  111. package/dist/repertoire/tasks/index.js +23 -9
  112. package/dist/repertoire/tasks/parser.js +0 -2
  113. package/dist/repertoire/tasks/transitions.js +1 -20
  114. package/dist/repertoire/tools-base.js +990 -273
  115. package/dist/repertoire/tools-bluebubbles.js +94 -0
  116. package/dist/repertoire/tools-github.js +1 -6
  117. package/dist/repertoire/tools-teams.js +65 -59
  118. package/dist/repertoire/tools.js +281 -91
  119. package/dist/scripts/claude-code-hook.js +41 -0
  120. package/dist/scripts/claude-code-stop-hook.js +47 -0
  121. package/dist/senses/attention-queue.js +97 -0
  122. package/dist/senses/bluebubbles-client.js +258 -5
  123. package/dist/senses/bluebubbles-entry.js +2 -0
  124. package/dist/senses/bluebubbles-inbound-log.js +109 -0
  125. package/dist/senses/bluebubbles-media.js +339 -0
  126. package/dist/senses/bluebubbles-model.js +12 -4
  127. package/dist/senses/bluebubbles-mutation-log.js +45 -5
  128. package/dist/senses/bluebubbles-runtime-state.js +109 -0
  129. package/dist/senses/bluebubbles-session-cleanup.js +72 -0
  130. package/dist/senses/bluebubbles.js +981 -45
  131. package/dist/senses/cli-entry.js +1 -1
  132. package/dist/senses/cli-layout.js +187 -0
  133. package/dist/senses/cli.js +374 -131
  134. package/dist/senses/contextual-heartbeat.js +106 -0
  135. package/dist/senses/continuity.js +94 -0
  136. package/dist/senses/debug-activity.js +154 -0
  137. package/dist/senses/inner-dialog-worker.js +112 -19
  138. package/dist/senses/inner-dialog.js +621 -84
  139. package/dist/senses/pipeline.js +463 -0
  140. package/dist/senses/shared-turn.js +196 -0
  141. package/dist/senses/surface-tool.js +95 -0
  142. package/dist/senses/teams.js +796 -141
  143. package/dist/senses/trust-gate.js +112 -2
  144. package/package.json +12 -4
  145. package/skills/configure-dev-tools.md +81 -0
  146. package/subagents/README.md +4 -70
  147. package/dist/heart/daemon/subagent-installer.js +0 -134
  148. package/subagents/work-doer.md +0 -233
  149. package/subagents/work-merger.md +0 -624
  150. package/subagents/work-planner.md +0 -373
@@ -1,6 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.__internal = void 0;
4
3
  exports.estimateTokensForMessage = estimateTokensForMessage;
5
4
  exports.estimateTokensForMessages = estimateTokensForMessages;
6
5
  const runtime_1 = require("../nerves/runtime");
@@ -53,7 +52,7 @@ function countCharsInContent(content) {
53
52
  return c.text.length;
54
53
  if (typeof c.content === "string")
55
54
  return c.content.length;
56
- return safeStringify(content).length;
55
+ return safeStringify(c).length;
57
56
  }
58
57
  return 0;
59
58
  }
@@ -70,12 +69,13 @@ function countCharsInToolCalls(toolCalls) {
70
69
  if (typeof t.type === "string")
71
70
  total += t.type.length;
72
71
  if (t.function && typeof t.function === "object") {
73
- if (typeof t.function.name === "string")
74
- total += t.function.name.length;
75
- if (typeof t.function.arguments === "string")
76
- total += t.function.arguments.length;
77
- else if (t.function.arguments != null)
78
- total += safeStringify(t.function.arguments).length;
72
+ const fn = t.function;
73
+ if (typeof fn.name === "string")
74
+ total += fn.name.length;
75
+ if (typeof fn.arguments === "string")
76
+ total += fn.arguments.length;
77
+ else if (fn.arguments != null)
78
+ total += safeStringify(fn.arguments).length;
79
79
  }
80
80
  }
81
81
  return total;
@@ -113,7 +113,3 @@ function estimateTokensForMessages(msgs) {
113
113
  total += estimateTokensForMessage(msg);
114
114
  return total;
115
115
  }
116
- exports.__internal = {
117
- CHARS_PER_TOKEN,
118
- PER_MESSAGE_OVERHEAD_TOKENS,
119
- };
@@ -5,20 +5,33 @@ const config_1 = require("../heart/config");
5
5
  const nerves_1 = require("../nerves");
6
6
  const runtime_1 = require("./runtime");
7
7
  const runtime_2 = require("./runtime");
8
+ const LEVEL_PRIORITY = { debug: 10, info: 20, warn: 30, error: 40 };
9
+ /** Wrap a sink so it only receives events at or above the given level. */
10
+ /* v8 ignore start -- internal filter plumbing, exercised via integration @preserve */
11
+ function filterSink(sink, minLevel) {
12
+ const minPriority = LEVEL_PRIORITY[minLevel] ?? 0;
13
+ return (entry) => {
14
+ if ((LEVEL_PRIORITY[entry.level] ?? 0) >= minPriority)
15
+ sink(entry);
16
+ };
17
+ }
8
18
  function resolveCliSinks(sinks) {
9
19
  const requested = sinks && sinks.length > 0 ? sinks : ["terminal", "ndjson"];
10
20
  return [...new Set(requested)];
11
21
  }
12
22
  function configureCliRuntimeLogger(_friendId, options = {}) {
13
23
  const sinkKinds = resolveCliSinks(options.sinks);
24
+ const level = options.level ?? "info";
14
25
  const sinks = sinkKinds.map((sinkKind) => {
15
26
  if (sinkKind === "terminal") {
16
- return (0, nerves_1.createTerminalSink)();
27
+ // Terminal only shows warnings and errors — INFO is too noisy
28
+ // for an interactive session. Full detail goes to the ndjson file.
29
+ return filterSink((0, nerves_1.createTerminalSink)(), "warn");
17
30
  }
18
31
  return (0, nerves_1.createNdjsonFileSink)((0, config_1.logPath)("cli", "runtime"));
19
32
  });
20
33
  const logger = (0, nerves_1.createLogger)({
21
- level: options.level ?? "info",
34
+ level,
22
35
  sinks,
23
36
  });
24
37
  (0, runtime_2.setRuntimeLogger)(logger);
@@ -14,7 +14,7 @@ const path_1 = require("path");
14
14
  const os_1 = require("os");
15
15
  exports.REPO_SLUG = "ouroboros-agent-harness";
16
16
  function getTestRunsRoot(repoSlug = exports.REPO_SLUG) {
17
- return (0, path_1.join)((0, os_1.homedir)(), ".agentstate", "test-runs", repoSlug);
17
+ return (0, path_1.join)((0, os_1.tmpdir)(), "ouroboros-test-runs", repoSlug);
18
18
  }
19
19
  function createRunId(now = new Date()) {
20
20
  return now.toISOString().replace(/[:.]/g, "-");
@@ -4,8 +4,10 @@ exports.createTraceId = createTraceId;
4
4
  exports.ensureTraceId = ensureTraceId;
5
5
  exports.createFanoutSink = createFanoutSink;
6
6
  exports.formatTerminalEntry = formatTerminalEntry;
7
+ exports.registerSpinnerHooks = registerSpinnerHooks;
7
8
  exports.createTerminalSink = createTerminalSink;
8
9
  exports.createStderrSink = createStderrSink;
10
+ exports.rotateIfNeeded = rotateIfNeeded;
9
11
  exports.createNdjsonFileSink = createNdjsonFileSink;
10
12
  exports.registerGlobalLogSink = registerGlobalLogSink;
11
13
  exports.createLogger = createLogger;
@@ -73,36 +75,91 @@ function formatTerminalEntry(entry) {
73
75
  const level = entry.level.toUpperCase();
74
76
  return `${formatTerminalTime(entry.ts)} ${level} [${entry.component}] ${entry.message}${formatTerminalMeta(entry.meta)}`;
75
77
  }
78
+ // Spinner coordination: the CLI sense registers these so log output
79
+ // doesn't interleave with the active spinner animation.
80
+ let _pauseSpinner = null;
81
+ let _resumeSpinner = null;
82
+ function registerSpinnerHooks(pause, resume) {
83
+ _pauseSpinner = pause;
84
+ _resumeSpinner = resume;
85
+ }
76
86
  function createTerminalSink(write = (chunk) => process.stderr.write(chunk), colorize = true) {
77
87
  return (entry) => {
88
+ _pauseSpinner?.();
78
89
  const line = formatTerminalEntry(entry);
79
90
  if (!colorize) {
80
91
  write(`${line}\n`);
92
+ _resumeSpinner?.();
81
93
  return;
82
94
  }
83
95
  const prefix = LEVEL_COLORS[entry.level];
84
96
  write(`${prefix}${line}\x1b[0m\n`);
97
+ _resumeSpinner?.();
85
98
  };
86
99
  }
87
100
  function createStderrSink(write = (chunk) => process.stderr.write(chunk)) {
88
101
  return createTerminalSink(write);
89
102
  }
90
- function createNdjsonFileSink(filePath) {
103
+ const DEFAULT_MAX_LOG_SIZE_BYTES = 50 * 1024 * 1024; // 50MB
104
+ const ROTATION_CHECK_INTERVAL_BYTES = 1024 * 1024; // ~1MB between stat checks
105
+ /**
106
+ * Rotate a log file: shift .1 -> .2, delete old .2, rename current -> .1.
107
+ * Uses the ndjson extension pattern: file.ndjson -> file.1.ndjson -> file.2.ndjson
108
+ */
109
+ function rotateIfNeeded(filePath, maxSizeBytes) {
110
+ let size;
111
+ try {
112
+ size = (0, fs_1.statSync)(filePath).size;
113
+ }
114
+ catch {
115
+ return false;
116
+ }
117
+ if (size < maxSizeBytes)
118
+ return false;
119
+ const ext = filePath.endsWith(".ndjson") ? ".ndjson" : "";
120
+ const base = ext ? filePath.slice(0, -ext.length) : filePath;
121
+ const file1 = `${base}.1${ext}`;
122
+ const file2 = `${base}.2${ext}`;
123
+ try {
124
+ (0, fs_1.unlinkSync)(file2);
125
+ }
126
+ catch { /* may not exist */ }
127
+ try {
128
+ (0, fs_1.renameSync)(file1, file2);
129
+ }
130
+ catch { /* may not exist */ }
131
+ try {
132
+ (0, fs_1.renameSync)(filePath, file1);
133
+ }
134
+ catch { /* best effort */ }
135
+ return true;
136
+ }
137
+ function createNdjsonFileSink(filePath, maxSizeBytes) {
91
138
  (0, fs_1.mkdirSync)((0, path_1.dirname)(filePath), { recursive: true });
92
139
  const queue = [];
93
140
  let flushing = false;
141
+ let bytesSinceCheck = 0;
142
+ const maxSize = maxSizeBytes ?? DEFAULT_MAX_LOG_SIZE_BYTES;
94
143
  function flush() {
95
144
  if (flushing || queue.length === 0)
96
145
  return;
97
146
  flushing = true;
98
147
  const line = queue.shift();
148
+ /* v8 ignore start — rotation triggers only after ~1MB of writes @preserve */
149
+ if (bytesSinceCheck >= ROTATION_CHECK_INTERVAL_BYTES) {
150
+ bytesSinceCheck = 0;
151
+ rotateIfNeeded(filePath, maxSize);
152
+ }
153
+ /* v8 ignore stop */
99
154
  (0, fs_1.appendFile)(filePath, line, "utf8", () => {
100
155
  flushing = false;
101
156
  flush();
102
157
  });
103
158
  }
104
159
  return (entry) => {
105
- queue.push(`${JSON.stringify(entry)}\n`);
160
+ const line = `${JSON.stringify(entry)}\n`;
161
+ bytesSinceCheck += line.length;
162
+ queue.push(line);
106
163
  flush();
107
164
  };
108
165
  }
@@ -6,7 +6,11 @@ const index_1 = require("./index");
6
6
  let runtimeLogger = null;
7
7
  function getRuntimeLogger() {
8
8
  if (!runtimeLogger) {
9
- runtimeLogger = (0, index_1.createLogger)({ level: "info" });
9
+ // Default logger has no sinks — events emitted before the real logger is
10
+ // configured (e.g. identity resolution during startup) are silently dropped.
11
+ // This prevents INFO lines from leaking to stderr and interleaving with
12
+ // the CLI spinner when the ouro subprocess hasn't configured its logger yet.
13
+ runtimeLogger = (0, index_1.createLogger)({ level: "info", sinks: [] });
10
14
  }
11
15
  return runtimeLogger;
12
16
  }
@@ -28,8 +28,10 @@ function resolveContentType(method, path) {
28
28
  : "application/json";
29
29
  }
30
30
  // Generic ADO API request. Returns response body as pretty-printed JSON string.
31
- async function adoRequest(token, method, org, path, body) {
31
+ // `host` overrides the base URL for non-standard APIs (e.g. "vsapm.dev.azure.com", "vssps.dev.azure.com").
32
+ async function adoRequest(token, method, org, path, body, host) {
32
33
  try {
34
+ const base = host ? `https://${host}/${org}` : `${ADO_BASE}/${org}`;
33
35
  (0, runtime_1.emitNervesEvent)({
34
36
  event: "client.request_start",
35
37
  component: "clients",
@@ -37,7 +39,7 @@ async function adoRequest(token, method, org, path, body) {
37
39
  meta: { client: "ado", method, org, path },
38
40
  });
39
41
  const fullPath = ensureApiVersion(path);
40
- const url = `${ADO_BASE}/${org}${fullPath}`;
42
+ const url = `${base}${fullPath}`;
41
43
  const contentType = resolveContentType(method, path);
42
44
  const opts = {
43
45
  method,
@@ -279,6 +279,7 @@ exports.adoSemanticToolDefinitions = [
279
279
  return formatForChannel(items, ctx, organization, project);
280
280
  },
281
281
  integration: "ado",
282
+ summaryKeys: ["organization", "project"],
282
283
  },
283
284
  // -- ado_create_epic --
284
285
  {
@@ -318,6 +319,7 @@ exports.adoSemanticToolDefinitions = [
318
319
  },
319
320
  integration: "ado",
320
321
  confirmationRequired: true,
322
+ summaryKeys: ["organization", "project", "title"],
321
323
  },
322
324
  // -- ado_create_issue --
323
325
  {
@@ -359,6 +361,7 @@ exports.adoSemanticToolDefinitions = [
359
361
  },
360
362
  integration: "ado",
361
363
  confirmationRequired: true,
364
+ summaryKeys: ["organization", "project", "title"],
362
365
  },
363
366
  // -- ado_move_items --
364
367
  {
@@ -413,6 +416,7 @@ exports.adoSemanticToolDefinitions = [
413
416
  },
414
417
  integration: "ado",
415
418
  confirmationRequired: true,
419
+ summaryKeys: ["organization", "project", "workItemIds"],
416
420
  },
417
421
  // -- ado_restructure_backlog --
418
422
  {
@@ -471,6 +475,7 @@ exports.adoSemanticToolDefinitions = [
471
475
  },
472
476
  integration: "ado",
473
477
  confirmationRequired: true,
478
+ summaryKeys: ["organization", "project"],
474
479
  },
475
480
  // -- ado_validate_structure --
476
481
  {
@@ -672,6 +677,7 @@ exports.adoSemanticToolDefinitions = [
672
677
  },
673
678
  integration: "ado",
674
679
  confirmationRequired: true,
680
+ summaryKeys: ["organization", "project"],
675
681
  },
676
682
  // -- ado_detect_orphans --
677
683
  {
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ /**
3
+ * Codex JSONL event parser.
4
+ * Parses typed events from Codex --json output:
5
+ * thread.started, turn.started, turn.completed, item.completed.
6
+ * Maps events to coding session status transitions.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.parseCodexJsonlEvent = parseCodexJsonlEvent;
10
+ const runtime_1 = require("../../nerves/runtime");
11
+ const KNOWN_TYPES = new Set(["thread.started", "turn.started", "turn.completed", "item.completed"]);
12
+ /**
13
+ * Parse a single JSONL line from Codex --json output.
14
+ * Returns null for invalid JSON, empty strings, or unknown event types.
15
+ */
16
+ function parseCodexJsonlEvent(line) {
17
+ const trimmed = line.trim();
18
+ if (trimmed.length === 0)
19
+ return null;
20
+ let parsed;
21
+ try {
22
+ parsed = JSON.parse(trimmed);
23
+ }
24
+ catch {
25
+ (0, runtime_1.emitNervesEvent)({
26
+ component: "repertoire",
27
+ event: "repertoire.codex_jsonl_parse_error",
28
+ message: "failed to parse codex JSONL line",
29
+ meta: { line: trimmed.slice(0, 100) },
30
+ });
31
+ return null;
32
+ }
33
+ const type = parsed.type;
34
+ if (!type || !KNOWN_TYPES.has(type))
35
+ return null;
36
+ const event = {
37
+ type: type,
38
+ threadId: typeof parsed.thread_id === "string" ? parsed.thread_id : undefined,
39
+ turnId: typeof parsed.turn_id === "string" ? parsed.turn_id : undefined,
40
+ item: typeof parsed.item === "object" && parsed.item !== null ? parsed.item : undefined,
41
+ raw: parsed,
42
+ statusHint: mapStatusHint(type),
43
+ };
44
+ (0, runtime_1.emitNervesEvent)({
45
+ component: "repertoire",
46
+ event: "repertoire.codex_jsonl_event",
47
+ message: "parsed codex JSONL event",
48
+ meta: { type, threadId: event.threadId ?? null },
49
+ });
50
+ return event;
51
+ }
52
+ function mapStatusHint(type) {
53
+ switch (type) {
54
+ case "thread.started":
55
+ case "turn.started":
56
+ return "running";
57
+ case "turn.completed":
58
+ case "item.completed":
59
+ return null; // No automatic status change for completion events
60
+ /* v8 ignore next -- defensive default for unknown event types */
61
+ default:
62
+ return null;
63
+ }
64
+ }
@@ -0,0 +1,254 @@
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.prepareCodingContextPack = prepareCodingContextPack;
37
+ const crypto = __importStar(require("crypto"));
38
+ const fs = __importStar(require("fs"));
39
+ const path = __importStar(require("path"));
40
+ const child_process_1 = require("child_process");
41
+ const active_work_1 = require("../../heart/active-work");
42
+ const identity_1 = require("../../heart/identity");
43
+ const runtime_1 = require("../../nerves/runtime");
44
+ const skills_1 = require("../skills");
45
+ const CONTEXT_FILENAMES = ["AGENTS.md", "CLAUDE.md"];
46
+ function defaultRunCommand(command, args, cwd) {
47
+ const result = (0, child_process_1.spawnSync)(command, args, {
48
+ cwd,
49
+ encoding: "utf-8",
50
+ });
51
+ return {
52
+ status: result.status ?? 1,
53
+ stdout: typeof result.stdout === "string" ? result.stdout : "",
54
+ stderr: typeof result.stderr === "string" ? result.stderr : "",
55
+ };
56
+ }
57
+ function stableContextKey(request) {
58
+ const payload = JSON.stringify({
59
+ runner: request.runner,
60
+ workdir: request.workdir,
61
+ taskRef: request.taskRef ?? "",
62
+ parentAgent: request.parentAgent ?? "",
63
+ obligationId: request.obligationId ?? "",
64
+ originSession: request.originSession ?? null,
65
+ });
66
+ return crypto.createHash("sha1").update(payload).digest("hex").slice(0, 12);
67
+ }
68
+ function collectProjectContextFiles(workdir, deps) {
69
+ const files = [];
70
+ const seen = new Set();
71
+ let current = path.resolve(workdir);
72
+ const root = path.parse(current).root;
73
+ while (true) {
74
+ for (const filename of CONTEXT_FILENAMES) {
75
+ const candidate = path.join(current, filename);
76
+ if (!deps.existsSync(candidate) || seen.has(candidate))
77
+ continue;
78
+ try {
79
+ const content = deps.readFileSync(candidate, "utf-8").trim();
80
+ if (content.length > 0) {
81
+ files.unshift({ path: candidate, content });
82
+ seen.add(candidate);
83
+ }
84
+ }
85
+ catch {
86
+ // Best-effort loading only.
87
+ }
88
+ }
89
+ if (current === root)
90
+ break;
91
+ current = path.dirname(current);
92
+ }
93
+ return files;
94
+ }
95
+ function captureRepoSnapshot(workdir, runCommand) {
96
+ const repoRoot = runCommand("git", ["rev-parse", "--show-toplevel"], workdir);
97
+ if (repoRoot.status !== 0) {
98
+ return {
99
+ available: false,
100
+ repoRoot: null,
101
+ branch: null,
102
+ head: null,
103
+ statusLines: [],
104
+ };
105
+ }
106
+ const branch = runCommand("git", ["rev-parse", "--abbrev-ref", "HEAD"], workdir);
107
+ const head = runCommand("git", ["rev-parse", "--short", "HEAD"], workdir);
108
+ const status = runCommand("git", ["status", "--short"], workdir);
109
+ return {
110
+ available: true,
111
+ repoRoot: repoRoot.stdout.trim() || null,
112
+ branch: branch.status === 0 ? branch.stdout.trim() || null : null,
113
+ head: head.status === 0 ? head.stdout.trim() || null : null,
114
+ statusLines: status.status === 0
115
+ ? status.stdout.split(/\r?\n/).map((line) => line.trimEnd()).filter(Boolean)
116
+ : [],
117
+ };
118
+ }
119
+ function formatContextFiles(files) {
120
+ if (files.length === 0)
121
+ return "(none found)";
122
+ return files.map((file) => `### ${file.path}\n${file.content}`).join("\n\n");
123
+ }
124
+ function formatSkills(skills) {
125
+ return skills.length > 0 ? skills.join(", ") : "(none found)";
126
+ }
127
+ function formatExistingSessions(sessions) {
128
+ if (sessions.length === 0)
129
+ return "activeSessions: none";
130
+ return sessions
131
+ .map((session) => {
132
+ return [
133
+ `- ${session.id}`,
134
+ `status=${session.status}`,
135
+ `lastActivityAt=${session.lastActivityAt}`,
136
+ session.taskRef ? `taskRef=${session.taskRef}` : null,
137
+ session.checkpoint ? `checkpoint=${session.checkpoint}` : null,
138
+ session.artifactPath ? `artifact=${session.artifactPath}` : null,
139
+ ].filter(Boolean).join(" ");
140
+ })
141
+ .join("\n");
142
+ }
143
+ function formatOrigin(request) {
144
+ if (!request.originSession)
145
+ return "originSession: none";
146
+ return `originSession: ${request.originSession.channel}/${request.originSession.key} (${request.originSession.friendId})`;
147
+ }
148
+ function buildScopeContent(request, contextFiles, skills, agentName) {
149
+ return [
150
+ "# Coding Session Scope",
151
+ "",
152
+ "## Request",
153
+ `runner: ${request.runner}`,
154
+ `taskRef: ${request.taskRef ?? "unassigned"}`,
155
+ `parentAgent: ${request.parentAgent ?? agentName}`,
156
+ `workdir: ${request.workdir}`,
157
+ formatOrigin(request),
158
+ `obligationId: ${request.obligationId ?? "none"}`,
159
+ "",
160
+ "## Prompt",
161
+ request.prompt,
162
+ "",
163
+ "## Session Contract",
164
+ "- This is a focused coding lane opened by the parent Ouro agent.",
165
+ "- Execute the concrete prompt in the supplied workdir directly.",
166
+ "- Do not switch into planning/doing workflows or approval gates unless the prompt explicitly asks for them.",
167
+ "- Treat the current prompt, scope file, and live world-state checkpoint in the state file as the authoritative briefing for this lane.",
168
+ "",
169
+ "## Project Context Files",
170
+ formatContextFiles(contextFiles),
171
+ "",
172
+ "## Available Bundle Skills",
173
+ formatSkills(skills),
174
+ ].join("\n");
175
+ }
176
+ function buildStateContent(request, contextKey, generatedAt, snapshot, existingSessions, agentName, activeWorkFrame) {
177
+ const gitSection = snapshot.available
178
+ ? [
179
+ `repoRoot: ${snapshot.repoRoot ?? "unknown"}`,
180
+ `branch: ${snapshot.branch ?? "unknown"}`,
181
+ `head: ${snapshot.head ?? "unknown"}`,
182
+ "status:",
183
+ snapshot.statusLines.length > 0 ? snapshot.statusLines.join("\n") : "(clean)",
184
+ ].join("\n")
185
+ : "git: unavailable";
186
+ return [
187
+ "# Coding Session State",
188
+ `generatedAt: ${generatedAt}`,
189
+ `contextKey: ${contextKey}`,
190
+ `agent: ${request.parentAgent ?? agentName}`,
191
+ formatOrigin(request),
192
+ `obligationId: ${request.obligationId ?? "none"}`,
193
+ "",
194
+ "## Workspace Snapshot",
195
+ gitSection,
196
+ ...(activeWorkFrame ? ["", (0, active_work_1.formatLiveWorldStateCheckpoint)(activeWorkFrame)] : []),
197
+ "",
198
+ "## Related Coding Sessions",
199
+ formatExistingSessions(existingSessions),
200
+ ].join("\n");
201
+ }
202
+ function relatedSessions(request, existingSessions) {
203
+ return existingSessions.filter((session) => {
204
+ return session.runner === request.runner
205
+ && session.workdir === request.workdir
206
+ && session.taskRef === request.taskRef;
207
+ });
208
+ }
209
+ function prepareCodingContextPack(input, deps = {}) {
210
+ const agentRoot = deps.agentRoot ?? (0, identity_1.getAgentRoot)();
211
+ const agentName = deps.agentName ?? (0, identity_1.getAgentName)();
212
+ const nowIso = deps.nowIso ?? (() => new Date().toISOString());
213
+ const existsSync = deps.existsSync ?? fs.existsSync;
214
+ const readFileSync = deps.readFileSync ?? fs.readFileSync;
215
+ const writeFileSync = deps.writeFileSync ?? fs.writeFileSync;
216
+ const mkdirSync = deps.mkdirSync ?? fs.mkdirSync;
217
+ const listAvailableSkills = deps.listSkills ?? skills_1.listSkills;
218
+ const runCommand = deps.runCommand ?? defaultRunCommand;
219
+ const contextKey = stableContextKey(input.request);
220
+ const contextDir = path.join(agentRoot, "state", "coding", "context");
221
+ const scopeFile = path.join(contextDir, `${contextKey}-scope.md`);
222
+ const stateFile = path.join(contextDir, `${contextKey}-state.md`);
223
+ const contextFiles = collectProjectContextFiles(input.request.workdir, { existsSync, readFileSync });
224
+ const skills = listAvailableSkills();
225
+ const existingSessions = relatedSessions(input.request, input.existingSessions ?? []);
226
+ const snapshot = captureRepoSnapshot(input.request.workdir, runCommand);
227
+ const generatedAt = nowIso();
228
+ const scopeContent = buildScopeContent(input.request, contextFiles, skills, agentName);
229
+ const stateContent = buildStateContent(input.request, contextKey, generatedAt, snapshot, existingSessions, agentName, input.activeWorkFrame);
230
+ mkdirSync(contextDir, { recursive: true });
231
+ writeFileSync(scopeFile, `${scopeContent}\n`, "utf-8");
232
+ writeFileSync(stateFile, `${stateContent}\n`, "utf-8");
233
+ (0, runtime_1.emitNervesEvent)({
234
+ component: "repertoire",
235
+ event: "repertoire.coding_context_pack_written",
236
+ message: "prepared coding session context pack",
237
+ meta: {
238
+ contextKey,
239
+ workdir: input.request.workdir,
240
+ taskRef: input.request.taskRef ?? null,
241
+ contextFiles: contextFiles.length,
242
+ skills: skills.length,
243
+ relatedSessions: existingSessions.length,
244
+ gitAvailable: snapshot.available,
245
+ },
246
+ });
247
+ return {
248
+ contextKey,
249
+ scopeFile,
250
+ stateFile,
251
+ scopeContent,
252
+ stateContent,
253
+ };
254
+ }