@ouro.bot/cli 0.1.0-alpha.686 → 0.1.0-alpha.689

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/changelog.json CHANGED
@@ -1,6 +1,29 @@
1
1
  {
2
2
  "_note": "This changelog is maintained as part of the PR/version-bump workflow. Agent-curated, not auto-generated. Agents read this file directly via read_file to understand what changed between versions.",
3
3
  "versions": [
4
+ {
5
+ "version": "0.1.0-alpha.689",
6
+ "changes": [
7
+ "Avoid self-mutating npm@latest in the trusted-publishing workflow; verify the npm toolchain floor and use a pinned fallback so publish can complete reliably after merge."
8
+ ]
9
+ },
10
+ {
11
+ "version": "0.1.0-alpha.688",
12
+ "changes": [
13
+ "Render freshest friend-facing contact timing in protected turn orientation and active-work status so private/self turns cannot mistake stale background timestamps for current contact."
14
+ ]
15
+ },
16
+ {
17
+ "version": "0.1.0-alpha.687",
18
+ "changes": [
19
+ "Route coding sessions through Workbench behind a default-off feature flag.",
20
+ "Tighten Workbench coding adapter construction and coverage.",
21
+ "Handle Workbench MCP subprocess broken pipes without leaking unhandled test/runtime errors.",
22
+ "Fail Workbench coding spawns when the initial task prompt is not confirmed delivered.",
23
+ "Remove legacy inner-dialog source shims and stale auto-start config fallback so private-runtime startup is controlled only by privateRuntime.autoStart.",
24
+ "Exclude private-runtime barrel and type-only modules from code coverage so CI enforces executable code instead of inert export surfaces."
25
+ ]
26
+ },
4
27
  {
5
28
  "version": "0.1.0-alpha.686",
6
29
  "changes": [
@@ -11,6 +11,7 @@ exports.formatActiveWorkChanges = formatActiveWorkChanges;
11
11
  const runtime_1 = require("../nerves/runtime");
12
12
  const state_machine_1 = require("./bridges/state-machine");
13
13
  const obligations_1 = require("../arc/obligations");
14
+ const session_activity_1 = require("./session-activity");
14
15
  const target_resolution_1 = require("./target-resolution");
15
16
  const config_1 = require("./config");
16
17
  const RECENT_ACTIVE_OBLIGATION_WINDOW_MS = 60 * 60 * 1000;
@@ -683,6 +684,12 @@ function formatActiveWorkFrame(frame, options) {
683
684
  const lines = ["## what i'm holding"];
684
685
  lines.push("this is my top-level live world-state right now. private-runtime work, coding lanes, other sessions, and return obligations all belong inside this picture.");
685
686
  lines.push("if older checkpoints elsewhere in the transcript disagree with this picture, this picture wins.");
687
+ const friendContactTiming = (0, session_activity_1.describeSessionActivityTiming)((0, session_activity_1.selectFreshestFriendFacingActivity)(frame.friendActivity?.allOtherLiveSessions ?? []), { prefix: "freshest friend-facing contact" });
688
+ if (friendContactTiming) {
689
+ lines.push("");
690
+ lines.push("## contact timing");
691
+ lines.push(`- ${friendContactTiming}`);
692
+ }
686
693
  const primaryObligation = findPrimaryOpenObligation(frame);
687
694
  const currentSessionObligation = findCurrentSessionOpenObligation(frame);
688
695
  const activeLane = formatActiveLane(frame, primaryObligation);
@@ -106,6 +106,9 @@ const DEFAULT_LOCAL_RUNTIME_CONFIG = {
106
106
  perplexityApiKey: "",
107
107
  openaiEmbeddingsApiKey: "",
108
108
  },
109
+ features: {
110
+ workbenchCoding: false,
111
+ },
109
112
  };
110
113
  function defaultRuntimeConfig() {
111
114
  return {
@@ -118,6 +121,7 @@ function defaultRuntimeConfig() {
118
121
  bluebubblesChannel: { ...DEFAULT_LOCAL_RUNTIME_CONFIG.bluebubblesChannel },
119
122
  vault: { ...DEFAULT_LOCAL_RUNTIME_CONFIG.vault },
120
123
  integrations: { ...DEFAULT_LOCAL_RUNTIME_CONFIG.integrations },
124
+ features: { ...DEFAULT_LOCAL_RUNTIME_CONFIG.features },
121
125
  };
122
126
  }
123
127
  let _runtimeConfigOverride = null;
@@ -37,7 +37,6 @@ exports.isLibraryKind = isLibraryKind;
37
37
  exports.listAllBundleAgents = listAllBundleAgents;
38
38
  exports.listEnabledBundleAgents = listEnabledBundleAgents;
39
39
  exports.readPrivateRuntimeConfig = readPrivateRuntimeConfig;
40
- exports.isInnerDialogAutoStartEnabled = isInnerDialogAutoStartEnabled;
41
40
  exports.listBundleSyncRows = listBundleSyncRows;
42
41
  const fs = __importStar(require("fs"));
43
42
  const path = __importStar(require("path"));
@@ -182,14 +181,6 @@ function readPrivateRuntimeConfig(agent, options = {}) {
182
181
  source: "privateRuntime",
183
182
  };
184
183
  }
185
- const innerDialog = parsed.innerDialog;
186
- if (innerDialog && typeof innerDialog === "object" && !Array.isArray(innerDialog)) {
187
- return {
188
- autoStart: innerDialog.autoStart === true,
189
- source: "legacy-innerDialog",
190
- compatibilityNote: "innerDialog.autoStart is a legacy migration input; use privateRuntime.autoStart.",
191
- };
192
- }
193
184
  return { autoStart: false, source: "default" };
194
185
  }
195
186
  catch (error) {
@@ -208,14 +199,6 @@ function readPrivateRuntimeConfig(agent, options = {}) {
208
199
  return { autoStart: false, source: "unreadable", error: message };
209
200
  }
210
201
  }
211
- /**
212
- * Legacy compatibility shim for older call sites. New code should use
213
- * readPrivateRuntimeConfig() so compatibility notes and failure provenance are
214
- * preserved.
215
- */
216
- function isInnerDialogAutoStartEnabled(agent, options = {}) {
217
- return readPrivateRuntimeConfig(agent, options).autoStart;
218
- }
219
202
  /**
220
203
  * Read the per-agent sync block from each enabled bundle's agent.json.
221
204
  * Used by the daemon (and stopped-state status renderer) to build per-agent
@@ -34,6 +34,8 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.listSessionActivity = listSessionActivity;
37
+ exports.selectFreshestFriendFacingActivity = selectFreshestFriendFacingActivity;
38
+ exports.describeSessionActivityTiming = describeSessionActivityTiming;
37
39
  exports.findFreshestFriendSession = findFreshestFriendSession;
38
40
  const fs = __importStar(require("fs"));
39
41
  const path = __importStar(require("path"));
@@ -44,6 +46,16 @@ const DEFAULT_ACTIVE_THRESHOLD_MS = 24 * 60 * 60 * 1000;
44
46
  function activityPriority(source) {
45
47
  return source === "friend-facing" ? 0 : 1;
46
48
  }
49
+ function formatElapsed(ms) {
50
+ const minutes = Math.max(0, Math.floor(ms / 60000));
51
+ if (minutes < 60)
52
+ return `${minutes}m ago`;
53
+ const hours = Math.floor(minutes / 60);
54
+ if (hours < 24)
55
+ return `${hours}h ago`;
56
+ const days = Math.floor(hours / 24);
57
+ return `${days}d ago`;
58
+ }
47
59
  function resolveFriendName(friendId, friendsDir, agentName) {
48
60
  if (friendId === "self")
49
61
  return agentName;
@@ -183,6 +195,36 @@ function listSessionActivity(query) {
183
195
  return b.lastActivityMs - a.lastActivityMs;
184
196
  });
185
197
  }
198
+ function selectFreshestFriendFacingActivity(records) {
199
+ const candidates = records
200
+ .filter((record) => record.friendId !== "self" && record.channel !== "inner" && record.activitySource === "friend-facing")
201
+ .sort((a, b) => b.lastActivityMs - a.lastActivityMs);
202
+ return candidates[0] ?? null;
203
+ }
204
+ function describeSessionActivityTiming(record, options = {}) {
205
+ if (!record)
206
+ return "";
207
+ const nowMs = options.nowMs ?? Date.now();
208
+ const parts = [];
209
+ const inboundMs = record.lastInboundAt ? Date.parse(record.lastInboundAt) : NaN;
210
+ const outboundMs = record.lastOutboundAt ? Date.parse(record.lastOutboundAt) : NaN;
211
+ if (Number.isFinite(inboundMs)) {
212
+ parts.push(`last inbound ${formatElapsed(nowMs - inboundMs)}`);
213
+ }
214
+ if (Number.isFinite(outboundMs)) {
215
+ parts.push(`i last replied ${formatElapsed(nowMs - outboundMs)}`);
216
+ }
217
+ if (record.unansweredInboundCount > 0) {
218
+ const count = record.unansweredInboundCount;
219
+ parts.push(`${count} unanswered inbound message${count === 1 ? "" : "s"}`);
220
+ }
221
+ if (parts.length === 0) {
222
+ parts.push(`last activity ${formatElapsed(nowMs - record.lastActivityMs)}`);
223
+ }
224
+ const label = `${record.friendName}/${record.channel}/${record.key}`;
225
+ const prefix = options.prefix ?? "friend-facing contact";
226
+ return `${prefix}: ${label}; ${parts.join("; ")}`;
227
+ }
186
228
  function findFreshestFriendSession(query) {
187
229
  const { activeOnly = false, activeThresholdMs = DEFAULT_ACTIVE_THRESHOLD_MS, nowMs, ...rest } = query;
188
230
  const currentSession = rest.currentSession ?? null;
@@ -197,6 +197,7 @@ function buildStartOfTurnPacket(view, opts) {
197
197
  recoverySentinel: opts?.recoverySentinel,
198
198
  resumeHint: buildResumeHint(view, opts?.canonicalObligations ? effectiveObligations : undefined),
199
199
  currentSessionTiming: opts?.currentSessionTiming,
200
+ friendContactTiming: opts?.friendContactTiming,
200
201
  tempo,
201
202
  tokenBudget,
202
203
  assembledAt: new Date().toISOString(),
@@ -238,6 +239,7 @@ function renderStartOfTurnPacket(packet) {
238
239
  { label: "arc", content: packet.arcResume ?? "", priority: 7 },
239
240
  { label: "recoverySentinel", content: packet.recoverySentinel ? (0, context_loss_sentinel_1.formatContextLossSentinelText)(packet.recoverySentinel) : "", priority: 7 },
240
241
  { label: "resume", content: packet.resumeHint, priority: 6 },
242
+ { label: "friendContact", content: packet.friendContactTiming ?? "", priority: 6 },
241
243
  { label: "sessionTiming", content: packet.currentSessionTiming ?? "", priority: 5 },
242
244
  { label: "obligations", content: packet.obligations, priority: 5 },
243
245
  { label: "cares", content: packet.cares, priority: 4 },
@@ -263,7 +265,7 @@ function renderStartOfTurnPacket(packet) {
263
265
  if (tokens <= budget.max)
264
266
  break;
265
267
  // Skip continuity sections — they are protected.
266
- if (section.label === "resume" || section.label === "arc" || section.label === "recoverySentinel" || section.label === "sessionTiming")
268
+ if (section.label === "resume" || section.label === "arc" || section.label === "recoverySentinel" || section.label === "friendContact" || section.label === "sessionTiming")
267
269
  continue;
268
270
  // Remove this section entirely
269
271
  const idx = sections.findIndex((s) => s.label === section.label);
@@ -300,6 +302,9 @@ function formatSections(sections) {
300
302
  case "sessionTiming":
301
303
  parts.push(`**Thread timing:** ${section.content}`);
302
304
  break;
305
+ case "friendContact":
306
+ parts.push(`**Recent contact:** ${section.content}`);
307
+ break;
303
308
  case "obligations":
304
309
  parts.push(`**Owed:**\n${section.content}`);
305
310
  break;
@@ -33,11 +33,10 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.INNER_DIALOG_PENDING = exports.PRIVATE_RUNTIME_PENDING = void 0;
36
+ exports.PRIVATE_RUNTIME_PENDING = void 0;
37
37
  exports.getPendingDir = getPendingDir;
38
38
  exports.getDeferredReturnDir = getDeferredReturnDir;
39
39
  exports.getPrivateRuntimePendingDir = getPrivateRuntimePendingDir;
40
- exports.getInnerDialogPendingDir = getInnerDialogPendingDir;
41
40
  exports.hasPendingMessages = hasPendingMessages;
42
41
  exports.queuePendingMessage = queuePendingMessage;
43
42
  exports.enqueueDeferredReturn = enqueueDeferredReturn;
@@ -60,12 +59,6 @@ exports.PRIVATE_RUNTIME_PENDING = { friendId: "self", channel: "inner", key: "di
60
59
  function getPrivateRuntimePendingDir(agentName) {
61
60
  return getPendingDir(agentName, exports.PRIVATE_RUNTIME_PENDING.friendId, exports.PRIVATE_RUNTIME_PENDING.channel, exports.PRIVATE_RUNTIME_PENDING.key);
62
61
  }
63
- /** Legacy alias for readers and callers that still reference the historical path name. */
64
- exports.INNER_DIALOG_PENDING = exports.PRIVATE_RUNTIME_PENDING;
65
- /** Legacy alias for readers and callers that still reference the historical path name. */
66
- function getInnerDialogPendingDir(agentName) {
67
- return getPrivateRuntimePendingDir(agentName);
68
- }
69
62
  function hasPendingMessages(pendingDir) {
70
63
  if (!fs.existsSync(pendingDir))
71
64
  return false;
@@ -1,19 +1,27 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.formatCodingTail = exports.attachCodingSessionFeedback = exports.formatCodingMonitorReport = exports.CodingSessionMonitor = exports.CodingSessionManager = void 0;
3
+ exports.formatCodingTail = exports.attachCodingSessionFeedback = exports.formatCodingMonitorReport = exports.CodingSessionMonitor = exports.WorkbenchMcpClient = exports.WorkbenchCodingSessionManager = exports.CodingSessionManager = void 0;
4
4
  exports.getCodingSessionManager = getCodingSessionManager;
5
5
  exports.resetCodingSessionManager = resetCodingSessionManager;
6
6
  const runtime_1 = require("../../nerves/runtime");
7
+ const config_1 = require("../../heart/config");
8
+ const identity_1 = require("../../heart/identity");
7
9
  const manager_1 = require("./manager");
10
+ const workbench_manager_1 = require("./workbench-manager");
11
+ const workbench_client_1 = require("./workbench-client");
8
12
  let manager = null;
9
13
  function getCodingSessionManager() {
10
14
  if (!manager) {
11
- manager = new manager_1.CodingSessionManager({});
15
+ const config = (0, config_1.loadConfig)();
16
+ const backend = config.features.workbenchCoding ? "workbench" : "process";
17
+ manager = config.features.workbenchCoding
18
+ ? new workbench_manager_1.WorkbenchCodingSessionManager({ agentName: (0, identity_1.getAgentName)(), client: new workbench_client_1.WorkbenchMcpClient() })
19
+ : new manager_1.CodingSessionManager({});
12
20
  (0, runtime_1.emitNervesEvent)({
13
21
  component: "repertoire",
14
22
  event: "repertoire.coding_manager_init",
15
23
  message: "initialized coding session manager singleton",
16
- meta: {},
24
+ meta: { backend },
17
25
  });
18
26
  }
19
27
  return manager;
@@ -30,6 +38,10 @@ function resetCodingSessionManager() {
30
38
  }
31
39
  var manager_2 = require("./manager");
32
40
  Object.defineProperty(exports, "CodingSessionManager", { enumerable: true, get: function () { return manager_2.CodingSessionManager; } });
41
+ var workbench_manager_2 = require("./workbench-manager");
42
+ Object.defineProperty(exports, "WorkbenchCodingSessionManager", { enumerable: true, get: function () { return workbench_manager_2.WorkbenchCodingSessionManager; } });
43
+ var workbench_client_2 = require("./workbench-client");
44
+ Object.defineProperty(exports, "WorkbenchMcpClient", { enumerable: true, get: function () { return workbench_client_2.WorkbenchMcpClient; } });
33
45
  var monitor_1 = require("./monitor");
34
46
  Object.defineProperty(exports, "CodingSessionMonitor", { enumerable: true, get: function () { return monitor_1.CodingSessionMonitor; } });
35
47
  var reporter_1 = require("./reporter");
@@ -33,6 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.buildCodingPrompt = buildCodingPrompt;
36
37
  exports.spawnCodingProcess = spawnCodingProcess;
37
38
  const child_process_1 = require("child_process");
38
39
  const fs = __importStar(require("fs"));
@@ -96,7 +97,7 @@ function appendFileSection(sections, label, filePath, deps) {
96
97
  return;
97
98
  sections.push(`${label} (${filePath}):\n${content}`);
98
99
  }
99
- function buildPrompt(request, deps) {
100
+ function buildCodingPrompt(request, deps) {
100
101
  const sections = [];
101
102
  sections.push([
102
103
  "Execution contract:",
@@ -123,7 +124,7 @@ function spawnCodingProcess(request, deps = {}) {
123
124
  const readFileSync = deps.readFileSync ?? fs.readFileSync;
124
125
  const homeDir = deps.homeDir ?? os.homedir();
125
126
  const baseEnv = deps.baseEnv ?? process.env;
126
- const prompt = buildPrompt(request, { existsSync, readFileSync });
127
+ const prompt = buildCodingPrompt(request, { existsSync, readFileSync });
127
128
  const { command, args } = buildCommandArgs(request.runner, request.workdir, request.parentAgent);
128
129
  const env = buildSpawnEnv(baseEnv, homeDir);
129
130
  (0, runtime_1.emitNervesEvent)({
@@ -35,6 +35,22 @@ function emitCodingToolEvent(toolName) {
35
35
  meta: { toolName },
36
36
  });
37
37
  }
38
+ function isRefreshableCodingSessionManager(manager) {
39
+ return typeof manager.refreshSessions === "function"
40
+ && typeof manager.refreshSession === "function";
41
+ }
42
+ async function listCodingSessions(manager) {
43
+ return isRefreshableCodingSessionManager(manager) ? manager.refreshSessions() : manager.listSessions();
44
+ }
45
+ async function getCodingSession(manager, sessionId) {
46
+ return isRefreshableCodingSessionManager(manager) ? manager.refreshSession(sessionId) : manager.getSession(sessionId);
47
+ }
48
+ async function sendCodingInput(manager, sessionId, input) {
49
+ return manager.sendInput(sessionId, input);
50
+ }
51
+ async function killCodingSession(manager, sessionId) {
52
+ return manager.killSession(sessionId);
53
+ }
38
54
  /**
39
55
  * Count distinct file paths mentioned in a coding session's stdout output.
40
56
  * Looks for path-like tokens (containing / and a file extension).
@@ -283,7 +299,7 @@ exports.codingToolDefinitions = [
283
299
  if (stateFile)
284
300
  request.stateFile = stateFile;
285
301
  const manager = (0, index_1.getCodingSessionManager)();
286
- const existingSessions = manager.listSessions();
302
+ const existingSessions = await listCodingSessions(manager);
287
303
  const existingSession = findReusableCodingSession(existingSessions, request);
288
304
  if (existingSession) {
289
305
  (0, runtime_1.emitNervesEvent)({
@@ -360,14 +376,14 @@ exports.codingToolDefinitions = [
360
376
  },
361
377
  {
362
378
  tool: codingStatusTool,
363
- handler: (args, ctx) => {
379
+ handler: async (args, ctx) => {
364
380
  emitCodingToolEvent("coding_status");
365
381
  const manager = (0, index_1.getCodingSessionManager)();
366
382
  const sessionId = requireArg(args, "sessionId");
367
383
  if (!sessionId) {
368
- return JSON.stringify(selectCodingStatusSessions(manager.listSessions(), ctx?.currentSession));
384
+ return JSON.stringify(selectCodingStatusSessions(await listCodingSessions(manager), ctx?.currentSession));
369
385
  }
370
- const session = manager.getSession(sessionId);
386
+ const session = await getCodingSession(manager, sessionId);
371
387
  if (!session)
372
388
  return `session not found: ${sessionId}`;
373
389
  return appendCompletionScrutiny(JSON.stringify(session), session);
@@ -376,12 +392,12 @@ exports.codingToolDefinitions = [
376
392
  },
377
393
  {
378
394
  tool: codingTailTool,
379
- handler: (args) => {
395
+ handler: async (args) => {
380
396
  emitCodingToolEvent("coding_tail");
381
397
  const sessionId = requireArg(args, "sessionId");
382
398
  if (!sessionId)
383
399
  return "sessionId is required";
384
- const session = (0, index_1.getCodingSessionManager)().getSession(sessionId);
400
+ const session = await getCodingSession((0, index_1.getCodingSessionManager)(), sessionId);
385
401
  if (!session)
386
402
  return `session not found: ${sessionId}`;
387
403
  return appendCompletionScrutiny((0, index_1.formatCodingTail)(session), session);
@@ -390,7 +406,7 @@ exports.codingToolDefinitions = [
390
406
  },
391
407
  {
392
408
  tool: codingSendInputTool,
393
- handler: (args) => {
409
+ handler: async (args) => {
394
410
  emitCodingToolEvent("coding_send_input");
395
411
  const sessionId = requireArg(args, "sessionId");
396
412
  if (!sessionId)
@@ -398,19 +414,19 @@ exports.codingToolDefinitions = [
398
414
  const input = requireArg(args, "input");
399
415
  if (!input)
400
416
  return "input is required";
401
- return JSON.stringify((0, index_1.getCodingSessionManager)().sendInput(sessionId, input));
417
+ return JSON.stringify(await sendCodingInput((0, index_1.getCodingSessionManager)(), sessionId, input));
402
418
  },
403
419
  summaryKeys: ["sessionId", "input"],
404
420
  riskProfile: { mutates: "external_side_effect", risk: "high", reason: "sends input to a live coding process" },
405
421
  },
406
422
  {
407
423
  tool: codingKillTool,
408
- handler: (args) => {
424
+ handler: async (args) => {
409
425
  emitCodingToolEvent("coding_kill");
410
426
  const sessionId = requireArg(args, "sessionId");
411
427
  if (!sessionId)
412
428
  return "sessionId is required";
413
- return JSON.stringify((0, index_1.getCodingSessionManager)().killSession(sessionId));
429
+ return JSON.stringify(await killCodingSession((0, index_1.getCodingSessionManager)(), sessionId));
414
430
  },
415
431
  summaryKeys: ["sessionId"],
416
432
  riskProfile: { mutates: "external_side_effect", risk: "high", reason: "terminates a live coding process" },
@@ -0,0 +1,272 @@
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.WorkbenchMcpClient = void 0;
37
+ exports.defaultWorkbenchMcpCandidates = defaultWorkbenchMcpCandidates;
38
+ exports.resolveWorkbenchMcpPath = resolveWorkbenchMcpPath;
39
+ const child_process_1 = require("child_process");
40
+ const fs = __importStar(require("fs"));
41
+ const os = __importStar(require("os"));
42
+ const path = __importStar(require("path"));
43
+ const runtime_1 = require("../../nerves/runtime");
44
+ function defaultWorkbenchMcpCandidates(homeDir = os.homedir()) {
45
+ return [
46
+ path.join(homeDir, "Applications", "Ouro Workbench.app", "Contents", "MacOS", "OuroWorkbenchMCP"),
47
+ path.join("/Applications", "Ouro Workbench.app", "Contents", "MacOS", "OuroWorkbenchMCP"),
48
+ ];
49
+ }
50
+ function resolveWorkbenchMcpPath(options = {}) {
51
+ const existsSync = options.existsSync ?? fs.existsSync;
52
+ const candidates = [
53
+ ...(options.executablePath ? [options.executablePath] : []),
54
+ ...defaultWorkbenchMcpCandidates(options.homeDir),
55
+ ];
56
+ return candidates.find((candidate) => existsSync(candidate)) ?? null;
57
+ }
58
+ function sleep(ms) {
59
+ return new Promise((resolve) => setTimeout(resolve, ms));
60
+ }
61
+ function parseJson(text, toolName) {
62
+ try {
63
+ return JSON.parse(text);
64
+ }
65
+ catch (error) {
66
+ throw new Error(`Workbench MCP ${toolName} returned non-JSON text: ${String(error)}`);
67
+ }
68
+ }
69
+ function responseIdMatches(response, id) {
70
+ return response.id === id || response.id === String(id);
71
+ }
72
+ function responseText(response, toolName) {
73
+ if (response.error) {
74
+ throw new Error(response.error.message ?? `Workbench MCP ${toolName} returned an RPC error`);
75
+ }
76
+ const text = response.result?.content?.map((item) => item.text ?? "").join("\n") ?? "";
77
+ if (response.result?.isError) {
78
+ throw new Error(text.trim() || `Workbench MCP ${toolName} returned an error`);
79
+ }
80
+ return text;
81
+ }
82
+ function isBrokenPipe(error) {
83
+ return error.code === "EPIPE";
84
+ }
85
+ class WorkbenchMcpClient {
86
+ executablePath;
87
+ spawnFn;
88
+ timeoutMs;
89
+ pollIntervalMs;
90
+ createTimeoutMs;
91
+ actionTimeoutMs;
92
+ constructor(options = {}) {
93
+ const executablePath = resolveWorkbenchMcpPath(options);
94
+ if (!executablePath) {
95
+ throw new Error("OuroWorkbenchMCP not found in ~/Applications or /Applications");
96
+ }
97
+ this.executablePath = executablePath;
98
+ this.spawnFn = options.spawnFn ?? ((command, args, spawnOptions) => (0, child_process_1.spawn)(command, args, spawnOptions));
99
+ this.timeoutMs = options.timeoutMs ?? 10_000;
100
+ this.pollIntervalMs = options.pollIntervalMs ?? 250;
101
+ this.createTimeoutMs = options.createTimeoutMs ?? 10_000;
102
+ this.actionTimeoutMs = options.actionTimeoutMs ?? 10_000;
103
+ }
104
+ get commandPath() {
105
+ return this.executablePath;
106
+ }
107
+ async callToolText(name, args) {
108
+ (0, runtime_1.emitNervesEvent)({
109
+ component: "repertoire",
110
+ event: "repertoire.workbench_mcp_call_start",
111
+ message: "calling Workbench MCP tool",
112
+ meta: { name },
113
+ });
114
+ const child = this.spawnFn(this.executablePath, [], {
115
+ stdio: ["pipe", "pipe", "pipe"],
116
+ });
117
+ const stderrChunks = [];
118
+ const stdoutChunks = [];
119
+ child.stderr.on("data", (chunk) => stderrChunks.push(chunk));
120
+ const result = await new Promise((resolve, reject) => {
121
+ let settled = false;
122
+ let timer;
123
+ const settle = (fn) => {
124
+ if (settled)
125
+ return;
126
+ settled = true;
127
+ clearTimeout(timer);
128
+ fn();
129
+ };
130
+ timer = setTimeout(() => {
131
+ settle(() => {
132
+ child.kill("SIGTERM");
133
+ reject(new Error(`Workbench MCP ${name} timed out after ${this.timeoutMs}ms`));
134
+ });
135
+ }, this.timeoutMs);
136
+ child.on("error", (error) => {
137
+ settle(() => reject(error));
138
+ });
139
+ child.stdin.on("error", (error) => {
140
+ if (isBrokenPipe(error))
141
+ return;
142
+ settle(() => reject(error));
143
+ });
144
+ child.on("exit", () => {
145
+ const stderr = Buffer.concat(stderrChunks).toString("utf-8").trim();
146
+ const stdout = Buffer.concat(stdoutChunks).toString("utf-8");
147
+ if (!settled) {
148
+ if (stderr.length > 0 && stdout.trim().length === 0) {
149
+ settle(() => reject(new Error(stderr)));
150
+ return;
151
+ }
152
+ settle(() => reject(new Error(`Workbench MCP ${name} exited before returning a response`)));
153
+ }
154
+ });
155
+ child.stdout.on("data", (chunk) => {
156
+ stdoutChunks.push(chunk);
157
+ for (const line of Buffer.concat(stdoutChunks).toString("utf-8").split(/\r?\n/)) {
158
+ if (!line.trim())
159
+ continue;
160
+ let response;
161
+ try {
162
+ response = JSON.parse(line);
163
+ }
164
+ catch {
165
+ continue;
166
+ }
167
+ if (!responseIdMatches(response, 2))
168
+ continue;
169
+ settle(() => {
170
+ try {
171
+ resolve(responseText(response, name));
172
+ }
173
+ catch (error) {
174
+ reject(error);
175
+ }
176
+ });
177
+ }
178
+ });
179
+ const initialize = { jsonrpc: "2.0", id: 1, method: "initialize", params: {} };
180
+ const toolCall = { jsonrpc: "2.0", id: 2, method: "tools/call", params: { name, arguments: args } };
181
+ child.stdin.write(`${JSON.stringify(initialize)}\n`);
182
+ child.stdin.write(`${JSON.stringify(toolCall)}\n`);
183
+ child.stdin.end();
184
+ }).finally(() => {
185
+ if (!child.killed) {
186
+ child.kill("SIGTERM");
187
+ }
188
+ });
189
+ (0, runtime_1.emitNervesEvent)({
190
+ component: "repertoire",
191
+ event: "repertoire.workbench_mcp_call_end",
192
+ message: "Workbench MCP tool returned",
193
+ meta: { name },
194
+ });
195
+ return result;
196
+ }
197
+ async callToolJson(name, args) {
198
+ return parseJson(await this.callToolText(name, args), name);
199
+ }
200
+ async createSession(args) {
201
+ return this.callToolJson("workbench_create_session", { ...args, format: "json" });
202
+ }
203
+ async listSessions(args = {}) {
204
+ const result = await this.callToolJson("workbench_sessions", args);
205
+ return Array.isArray(result.sessions) ? result.sessions : [];
206
+ }
207
+ async transcriptTail(entry, maxBytes = 64_000) {
208
+ return this.callToolText("workbench_transcript_tail", { entry, maxBytes });
209
+ }
210
+ async requestAction(args) {
211
+ return this.callToolJson("workbench_request_action", { ...args, format: "json" });
212
+ }
213
+ async actionResult(requestId) {
214
+ return this.callToolJson("workbench_action_result", { requestId });
215
+ }
216
+ async createCodingSession(request) {
217
+ const source = request.source ?? "ouro-coding";
218
+ const group = request.group ?? path.basename(request.workingDirectory);
219
+ const createAck = await this.createSession({
220
+ owner: request.owner,
221
+ name: request.name,
222
+ command: request.command,
223
+ workingDirectory: request.workingDirectory,
224
+ group,
225
+ createGroupIfMissing: true,
226
+ trust: request.trust ?? "trusted",
227
+ autoResume: request.autoResume ?? true,
228
+ source,
229
+ });
230
+ const session = await this.waitForSession({
231
+ owner: request.owner,
232
+ name: request.name,
233
+ timeoutMs: this.createTimeoutMs,
234
+ });
235
+ const promptAck = await this.requestAction({
236
+ source,
237
+ action: "sendInput",
238
+ entry: session.id,
239
+ text: request.prompt,
240
+ appendNewline: true,
241
+ });
242
+ const promptResult = promptAck.requestId
243
+ ? await this.waitForAction(promptAck.requestId, this.actionTimeoutMs)
244
+ : undefined;
245
+ return { session, createAck, promptAck, promptResult };
246
+ }
247
+ async waitForSession(input) {
248
+ const deadline = Date.now() + (input.timeoutMs ?? this.createTimeoutMs);
249
+ let lastSessions = [];
250
+ while (Date.now() <= deadline) {
251
+ lastSessions = await this.listSessions({ owner: input.owner, name: input.name, includeArchived: true });
252
+ const match = lastSessions.find((session) => session.name.toLowerCase() === input.name.toLowerCase()
253
+ && (session.owner?.name === input.owner || !session.owner?.name));
254
+ if (match)
255
+ return match;
256
+ await sleep(this.pollIntervalMs);
257
+ }
258
+ throw new Error(`Workbench did not create session "${input.name}" for ${input.owner}; last matches=${lastSessions.length}`);
259
+ }
260
+ async waitForAction(requestId, timeoutMs = this.actionTimeoutMs) {
261
+ const deadline = Date.now() + timeoutMs;
262
+ let latest = null;
263
+ while (Date.now() <= deadline) {
264
+ latest = await this.actionResult(requestId);
265
+ if (latest.state !== "queued")
266
+ return latest;
267
+ await sleep(this.pollIntervalMs);
268
+ }
269
+ return latest ?? { requestId, state: "queued" };
270
+ }
271
+ }
272
+ exports.WorkbenchMcpClient = WorkbenchMcpClient;
@@ -0,0 +1,346 @@
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.WorkbenchCodingSessionManager = void 0;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ const runtime_1 = require("../../nerves/runtime");
40
+ const spawner_1 = require("./spawner");
41
+ function cloneSession(session) {
42
+ return {
43
+ ...session,
44
+ originSession: session.originSession ? { ...session.originSession } : undefined,
45
+ failure: session.failure ? { ...session.failure, args: [...session.failure.args] } : null,
46
+ };
47
+ }
48
+ function sanitizeNamePart(value, fallback) {
49
+ const sanitized = (value ?? fallback)
50
+ .toLowerCase()
51
+ .replace(/[^a-z0-9]+/g, "-")
52
+ .replace(/^-+|-+$/g, "");
53
+ return sanitized.length > 0 ? sanitized : fallback;
54
+ }
55
+ function timestampPart(iso) {
56
+ return iso.replace(/[-:.]/g, "").replace("Z", "Z");
57
+ }
58
+ function sessionNameForRequest(request, nowIso) {
59
+ return [
60
+ "coding",
61
+ request.runner,
62
+ sanitizeNamePart(request.taskRef, "task"),
63
+ timestampPart(nowIso),
64
+ ].join("-");
65
+ }
66
+ function commandForRunner(runner) {
67
+ return runner === "claude" ? "claude" : "codex";
68
+ }
69
+ function runnerFromWorkbenchSession(session, fallback) {
70
+ const name = session.name.toLowerCase();
71
+ if (name.includes("claude"))
72
+ return "claude";
73
+ if (name.includes("codex"))
74
+ return "codex";
75
+ return fallback;
76
+ }
77
+ function mapWorkbenchStatus(session) {
78
+ switch (session.status) {
79
+ case "configured":
80
+ case "running":
81
+ return "running";
82
+ case "waitingForInput":
83
+ return "waiting_input";
84
+ case "exited":
85
+ return session.exitCode === 0 ? "completed" : "failed";
86
+ case "needsRecovery":
87
+ case "manualActionNeeded":
88
+ return "stalled";
89
+ default:
90
+ return "running";
91
+ }
92
+ }
93
+ function checkpointFor(session, status) {
94
+ if (session.attentionPrompt?.trim())
95
+ return session.attentionPrompt.trim();
96
+ if (session.attentionReason?.trim())
97
+ return session.attentionReason.trim();
98
+ if (session.attention?.trim())
99
+ return session.attention.trim();
100
+ if (status === "completed")
101
+ return "completed";
102
+ if (status === "failed")
103
+ return session.exitCode === undefined ? "exited" : `exit code ${session.exitCode}`;
104
+ if (status === "stalled")
105
+ return "needs Workbench recovery";
106
+ return null;
107
+ }
108
+ function failureFor(session, command) {
109
+ if (mapWorkbenchStatus(session) !== "failed")
110
+ return null;
111
+ return {
112
+ command,
113
+ args: [],
114
+ code: session.exitCode ?? null,
115
+ signal: null,
116
+ stdoutTail: "",
117
+ stderrTail: checkpointFor(session, "failed"),
118
+ };
119
+ }
120
+ function resultToActionResult(action, sessionId, result) {
121
+ if (result.state === "applied" || result.state === "appliedUnconfirmed") {
122
+ return { ok: true, message: `${action} applied for ${sessionId}` };
123
+ }
124
+ if (result.state === "queued") {
125
+ return { ok: false, message: `${action} queued for ${sessionId} but not confirmed` };
126
+ }
127
+ return { ok: false, message: result.result ?? `${action} ${result.state} for ${sessionId}` };
128
+ }
129
+ function promptDeliveryFailure(result) {
130
+ const sessionId = result.session.id;
131
+ if (result.promptAck.ok === false) {
132
+ return `Workbench denied initial prompt for ${sessionId}`;
133
+ }
134
+ switch (result.promptResult?.state) {
135
+ case "applied":
136
+ case "appliedUnconfirmed":
137
+ return null;
138
+ case "failed":
139
+ return `Workbench failed initial prompt for ${sessionId}`;
140
+ case "queued":
141
+ return `Workbench queued initial prompt for ${sessionId} but did not confirm delivery`;
142
+ case "unknown":
143
+ case undefined:
144
+ return `Workbench did not confirm initial prompt delivery for ${sessionId}`;
145
+ }
146
+ }
147
+ class WorkbenchCodingSessionManager {
148
+ records = new Map();
149
+ listeners = new Map();
150
+ client;
151
+ agentName;
152
+ nowIso;
153
+ existsSync;
154
+ readFileSync;
155
+ source;
156
+ constructor(options) {
157
+ this.client = options.client;
158
+ this.agentName = options.agentName;
159
+ this.nowIso = options.nowIso ?? (() => new Date().toISOString());
160
+ this.existsSync = options.existsSync ?? fs.existsSync;
161
+ this.readFileSync = options.readFileSync ?? fs.readFileSync;
162
+ this.source = options.source ?? "ouro-coding";
163
+ }
164
+ async spawnSession(request) {
165
+ const now = this.nowIso();
166
+ const name = sessionNameForRequest(request, now);
167
+ const normalizedRequest = {
168
+ ...request,
169
+ parentAgent: request.parentAgent ?? this.agentName,
170
+ };
171
+ const prompt = (0, spawner_1.buildCodingPrompt)(normalizedRequest, {
172
+ existsSync: this.existsSync,
173
+ readFileSync: this.readFileSync,
174
+ });
175
+ const result = await this.client.createCodingSession({
176
+ owner: normalizedRequest.parentAgent,
177
+ name,
178
+ command: commandForRunner(normalizedRequest.runner),
179
+ workingDirectory: normalizedRequest.workdir,
180
+ prompt,
181
+ group: path.basename(normalizedRequest.workdir),
182
+ trust: "trusted",
183
+ autoResume: normalizedRequest.autoRestartOnCrash !== false,
184
+ source: this.source,
185
+ });
186
+ const promptFailure = promptDeliveryFailure(result);
187
+ if (promptFailure) {
188
+ throw new Error(promptFailure);
189
+ }
190
+ const session = this.toCodingSession(result.session, normalizedRequest);
191
+ this.records.set(session.id, { request: normalizedRequest, session });
192
+ (0, runtime_1.emitNervesEvent)({
193
+ component: "repertoire",
194
+ event: "repertoire.workbench_coding_session_spawned",
195
+ message: "Workbench coding session spawned",
196
+ meta: { id: session.id, runner: session.runner, workdir: session.workdir },
197
+ });
198
+ this.notifyListeners(session.id, { kind: "spawned", session: cloneSession(session) });
199
+ return cloneSession(session);
200
+ }
201
+ async refreshSessions() {
202
+ const workbenchSessions = await this.client.listSessions({ owner: this.agentName, includeArchived: true });
203
+ for (const workbenchSession of workbenchSessions) {
204
+ if (!workbenchSession.name.toLowerCase().startsWith("coding-"))
205
+ continue;
206
+ const existing = this.records.get(workbenchSession.id);
207
+ const fallbackRequest = existing?.request ?? this.fallbackRequest(workbenchSession);
208
+ const session = this.toCodingSession(workbenchSession, fallbackRequest, existing?.session);
209
+ this.records.set(session.id, { request: fallbackRequest, session });
210
+ }
211
+ return this.listSessions();
212
+ }
213
+ async refreshSession(sessionId) {
214
+ const sessions = await this.refreshSessions();
215
+ const session = sessions.find((item) => item.id === sessionId) ?? null;
216
+ if (!session)
217
+ return null;
218
+ const tail = await this.client.transcriptTail(sessionId);
219
+ const record = this.records.get(sessionId);
220
+ record.session.stdoutTail = tail;
221
+ record.session.lastActivityAt = this.nowIso();
222
+ record.session.checkpoint = tail.trim().split(/\r?\n/).filter(Boolean).at(-1) ?? record.session.checkpoint;
223
+ return cloneSession(record.session);
224
+ }
225
+ listSessions() {
226
+ return [...this.records.values()]
227
+ .map((record) => cloneSession(record.session))
228
+ .sort((left, right) => left.id.localeCompare(right.id));
229
+ }
230
+ getSession(sessionId) {
231
+ const record = this.records.get(sessionId);
232
+ return record ? cloneSession(record.session) : null;
233
+ }
234
+ subscribe(sessionId, listener) {
235
+ const listeners = this.listeners.get(sessionId) ?? new Set();
236
+ listeners.add(listener);
237
+ this.listeners.set(sessionId, listeners);
238
+ return () => {
239
+ const current = this.listeners.get(sessionId);
240
+ if (!current)
241
+ return;
242
+ current.delete(listener);
243
+ if (current.size === 0)
244
+ this.listeners.delete(sessionId);
245
+ };
246
+ }
247
+ async sendInput(sessionId, input) {
248
+ return this.queueAction(sessionId, "sendInput", { text: input, appendNewline: true });
249
+ }
250
+ async killSession(sessionId) {
251
+ const actionResult = await this.queueAction(sessionId, "terminate", {});
252
+ if (actionResult.ok) {
253
+ const record = this.records.get(sessionId);
254
+ record.session.status = "killed";
255
+ record.session.endedAt = this.nowIso();
256
+ record.session.checkpoint = "terminated by parent agent";
257
+ this.notifyListeners(sessionId, { kind: "killed", session: cloneSession(record.session) });
258
+ }
259
+ return actionResult;
260
+ }
261
+ checkStalls() {
262
+ return 0;
263
+ }
264
+ shutdown() {
265
+ this.listeners.clear();
266
+ }
267
+ async queueAction(sessionId, action, extra) {
268
+ let record = this.records.get(sessionId);
269
+ if (!record) {
270
+ await this.refreshSessions();
271
+ record = this.records.get(sessionId);
272
+ }
273
+ if (!record) {
274
+ return { ok: false, message: `session not found: ${sessionId}` };
275
+ }
276
+ const ack = await this.client.requestAction({
277
+ source: this.source,
278
+ action,
279
+ entry: sessionId,
280
+ ...extra,
281
+ });
282
+ if (!ack.requestId) {
283
+ return { ok: ack.ok === true, message: ack.message ?? `${action} queued for ${sessionId}` };
284
+ }
285
+ const result = await this.client.waitForAction(ack.requestId);
286
+ const actionResult = resultToActionResult(action, sessionId, result);
287
+ if (actionResult.ok && action === "sendInput") {
288
+ record.session.status = "running";
289
+ record.session.lastActivityAt = this.nowIso();
290
+ this.notifyListeners(sessionId, { kind: "progress", session: cloneSession(record.session) });
291
+ }
292
+ return actionResult;
293
+ }
294
+ fallbackRequest(session) {
295
+ const runner = runnerFromWorkbenchSession(session, "codex");
296
+ return {
297
+ runner,
298
+ workdir: session.workingDirectory ?? "",
299
+ prompt: "",
300
+ taskRef: session.name,
301
+ parentAgent: this.agentName,
302
+ autoRestartOnCrash: session.autoResume,
303
+ };
304
+ }
305
+ toCodingSession(workbenchSession, request, previous) {
306
+ const runner = runnerFromWorkbenchSession(workbenchSession, request.runner);
307
+ const status = mapWorkbenchStatus(workbenchSession);
308
+ const workdir = workbenchSession.workingDirectory ?? request.workdir;
309
+ const lastActivityAt = workbenchSession.lastOutputAt ?? workbenchSession.startedAt ?? previous?.lastActivityAt ?? this.nowIso();
310
+ const startedAt = workbenchSession.startedAt ?? previous?.startedAt ?? this.nowIso();
311
+ const command = commandForRunner(runner);
312
+ return {
313
+ id: workbenchSession.id,
314
+ runner,
315
+ workdir,
316
+ taskRef: request.taskRef,
317
+ originSession: request.originSession ? { ...request.originSession } : undefined,
318
+ obligationId: request.obligationId,
319
+ evolutionCaseId: request.evolutionCaseId,
320
+ scopeFile: request.scopeFile,
321
+ stateFile: request.stateFile,
322
+ checkpoint: checkpointFor(workbenchSession, status),
323
+ artifactPath: workbenchSession.transcriptPath,
324
+ status,
325
+ stdoutTail: previous?.stdoutTail ?? "",
326
+ stderrTail: previous?.stderrTail ?? "",
327
+ pid: workbenchSession.pid ?? null,
328
+ startedAt,
329
+ lastActivityAt,
330
+ endedAt: status === "completed" || status === "failed" ? lastActivityAt : null,
331
+ restartCount: previous?.restartCount ?? 0,
332
+ lastExitCode: workbenchSession.exitCode ?? null,
333
+ lastSignal: null,
334
+ failure: failureFor(workbenchSession, command),
335
+ };
336
+ }
337
+ notifyListeners(sessionId, update) {
338
+ const listeners = this.listeners.get(sessionId);
339
+ if (!listeners || listeners.size === 0)
340
+ return;
341
+ for (const listener of listeners) {
342
+ void Promise.resolve(listener(update));
343
+ }
344
+ }
345
+ }
346
+ exports.WorkbenchCodingSessionManager = WorkbenchCodingSessionManager;
@@ -6,9 +6,8 @@
6
6
  // Failure mode is "drop and log", never queue for later delivery.
7
7
  //
8
8
  // Patterns are deliberately narrow: bracketed system markers and angle-bracket
9
- // reasoning tags. Plain prose mentioning "inner dialog" or "attention queue"
10
- // is NOT blocked, so user-facing replies that legitimately discuss those
11
- // concepts still pass.
9
+ // reasoning tags. Plain prose mentioning related concepts is NOT blocked, so
10
+ // user-facing replies that legitimately discuss those concepts still pass.
12
11
  Object.defineProperty(exports, "__esModule", { value: true });
13
12
  exports.containsInternalMetaMarkers = containsInternalMetaMarkers;
14
13
  exports.emitBluebubblesMetaBlocked = emitBluebubblesMetaBlocked;
@@ -62,6 +62,7 @@ const bundle_state_1 = require("../heart/bundle-state");
62
62
  const sync_1 = require("../heart/sync");
63
63
  const config_1 = require("../heart/config");
64
64
  const session_events_1 = require("../heart/session-events");
65
+ const session_activity_1 = require("../heart/session-activity");
65
66
  const presence_1 = require("../arc/presence");
66
67
  const episodes_1 = require("../arc/episodes");
67
68
  const turn_context_1 = require("../heart/turn-context");
@@ -660,6 +661,7 @@ async function handleInboundTurn(input) {
660
661
  targetCandidates: ctx.targetCandidates,
661
662
  innerReturnObligations: ctx.returnObligations,
662
663
  });
664
+ const friendContactTiming = (0, session_activity_1.describeSessionActivityTiming)((0, session_activity_1.selectFreshestFriendFacingActivity)(sessionActivity), { prefix: "freshest friend-facing contact" });
663
665
  const delegationDecision = (0, delegation_1.decideDelegation)({
664
666
  channel: input.channel,
665
667
  ingressTexts: input.continuityIngressTexts ?? [],
@@ -731,6 +733,7 @@ async function handleInboundTurn(input) {
731
733
  all: activeWorkFrame.pendingObligations,
732
734
  },
733
735
  currentSessionTiming,
736
+ friendContactTiming,
734
737
  flightRecorderResume: ctx.flightRecorderResume,
735
738
  recoverySentinel: ctx.recoverySentinel,
736
739
  });
@@ -8,8 +8,8 @@ const PROACTIVE_INTERNAL_CONTENT_PATTERNS = [
8
8
  // PR 447 patterns: raw meta markers
9
9
  { reason: "raw_meta_marker", pattern: /<\s*\/?\s*(think|analysis|commentary)\b[^>]*>/i },
10
10
  { reason: "raw_meta_marker", pattern: /\[\s*surfaced from inner dialog\s*\]/i },
11
- // Inner dialog / attention / obligation references
12
- { reason: "inner_dialog_reference", pattern: /\binner (dialog|dialogue)\b/i },
11
+ // Stale private-runtime vocabulary / attention / obligation references
12
+ { reason: "legacy_private_runtime_reference", pattern: /\binner (dialog|dialogue)\b/i },
13
13
  { reason: "attention_queue_reference", pattern: /\battention queues?\b/i },
14
14
  { reason: "return_obligation_reference", pattern: /\b(return|held|heart|inner)\s+obligations?\b/i },
15
15
  // Surfacing mechanics
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.686",
3
+ "version": "0.1.0-alpha.689",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",
@@ -1,61 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.HEARTBEAT_OK_REST_SUPPRESSION_MS = exports.HABIT_RECURSION_BURST_THRESHOLD = exports.HABIT_RECURSION_BURST_WINDOW_MS = exports.HABIT_RECURSION_MIN_INTERVAL_MS = exports.MAX_CONSECUTIVE_INSTINCT_TURNS = void 0;
37
- exports.createInnerDialogWorker = createInnerDialogWorker;
38
- exports.startInnerDialogWorker = startInnerDialogWorker;
39
- const runtime_1 = require("../nerves/runtime");
40
- const innerDialog = __importStar(require("./inner-dialog"));
41
- const privateRuntimeWorker = __importStar(require("./private-runtime-worker"));
42
- exports.MAX_CONSECUTIVE_INSTINCT_TURNS = 3;
43
- exports.HABIT_RECURSION_MIN_INTERVAL_MS = 5_000;
44
- exports.HABIT_RECURSION_BURST_WINDOW_MS = 60_000;
45
- exports.HABIT_RECURSION_BURST_THRESHOLD = 5;
46
- exports.HEARTBEAT_OK_REST_SUPPRESSION_MS = 20 * 60_000;
47
- function createInnerDialogWorker(runTurn = (options) => {
48
- return innerDialog.runInnerDialogTurn(options);
49
- }, hasPendingWork, nowSource) {
50
- return privateRuntimeWorker.createPrivateRuntimeWorker(runTurn, hasPendingWork, nowSource);
51
- }
52
- async function startInnerDialogWorker() {
53
- (0, runtime_1.emitNervesEvent)({
54
- level: "info",
55
- component: "senses",
56
- event: "senses.inner_dialog_worker_compat_start",
57
- message: "legacy inner-dialog worker startup delegated to private-runtime worker",
58
- meta: { target: "private-runtime-worker" },
59
- });
60
- await privateRuntimeWorker.startPrivateRuntimeWorker();
61
- }
@@ -1,23 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
- };
16
- Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.runInnerDialogTurn = exports.innerDialogSessionPath = exports.loadInnerDialogInstincts = exports.buildInnerDialogBootstrapMessage = void 0;
18
- __exportStar(require("./private-runtime"), exports);
19
- var private_runtime_1 = require("./private-runtime");
20
- Object.defineProperty(exports, "buildInnerDialogBootstrapMessage", { enumerable: true, get: function () { return private_runtime_1.buildPrivateRuntimeBootstrapMessage; } });
21
- Object.defineProperty(exports, "loadInnerDialogInstincts", { enumerable: true, get: function () { return private_runtime_1.loadPrivateRuntimeInstincts; } });
22
- Object.defineProperty(exports, "innerDialogSessionPath", { enumerable: true, get: function () { return private_runtime_1.privateRuntimeSessionPath; } });
23
- Object.defineProperty(exports, "runInnerDialogTurn", { enumerable: true, get: function () { return private_runtime_1.runPrivateRuntimeTurn; } });