@ouro.bot/cli 0.1.0-alpha.674 → 0.1.0-alpha.676

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 (63) hide show
  1. package/README.md +11 -5
  2. package/changelog.json +19 -0
  3. package/dist/arc/evolution.js +13 -10
  4. package/dist/arc/obligations.js +1 -1
  5. package/dist/heart/active-work.js +3 -3
  6. package/dist/heart/agent-entry.js +8 -8
  7. package/dist/heart/commitments.js +1 -1
  8. package/dist/heart/core.js +45 -23
  9. package/dist/heart/daemon/agent-config-check.js +24 -0
  10. package/dist/heart/daemon/agent-discovery.js +28 -17
  11. package/dist/heart/daemon/agent-service.js +6 -6
  12. package/dist/heart/daemon/await-private-wake.js +59 -0
  13. package/dist/heart/daemon/cli-exec.js +180 -24
  14. package/dist/heart/daemon/cli-help.js +20 -2
  15. package/dist/heart/daemon/cli-parse.js +45 -2
  16. package/dist/heart/daemon/cli-render.js +1 -0
  17. package/dist/heart/daemon/daemon-entry.js +122 -25
  18. package/dist/heart/daemon/daemon.js +223 -20
  19. package/dist/heart/daemon/habit-private-wake.js +56 -0
  20. package/dist/heart/daemon/inner-status.js +3 -3
  21. package/dist/heart/daemon/message-router.js +39 -3
  22. package/dist/heart/daemon/pulse.js +43 -7
  23. package/dist/heart/daemon/socket-client.js +14 -2
  24. package/dist/heart/daemon/startup-tui.js +9 -1
  25. package/dist/heart/daemon/thoughts.js +39 -39
  26. package/dist/heart/habits/habit-scheduler.js +24 -4
  27. package/dist/heart/mailbox/mailbox-view.js +1 -1
  28. package/dist/heart/mailbox/readers/agent-machine.js +7 -7
  29. package/dist/heart/primitives.js +1 -1
  30. package/dist/heart/private-runtime/decision-renderer.js +156 -0
  31. package/dist/heart/private-runtime/index.js +18 -0
  32. package/dist/heart/private-runtime/ledger.js +218 -0
  33. package/dist/heart/private-runtime/policy.js +205 -0
  34. package/dist/heart/private-runtime/types.js +2 -0
  35. package/dist/heart/progress-story.js +1 -1
  36. package/dist/heart/session-transcript.js +1 -1
  37. package/dist/heart/tool-activity-callbacks.js +1 -1
  38. package/dist/heart/tool-description.js +2 -2
  39. package/dist/heart/tool-loop.js +2 -2
  40. package/dist/heart/turn-context.js +5 -5
  41. package/dist/mailbox-ui/assets/index-J8zDwHmE.js +1 -0
  42. package/dist/mailbox-ui/index.html +1 -1
  43. package/dist/mailroom/attention.js +1 -1
  44. package/dist/mind/context.js +2 -2
  45. package/dist/mind/pending.js +12 -5
  46. package/dist/mind/prompt.js +32 -25
  47. package/dist/repertoire/coding/feedback.js +25 -4
  48. package/dist/repertoire/tools-awaiting.js +2 -2
  49. package/dist/repertoire/tools-flow.js +4 -4
  50. package/dist/repertoire/tools-record.js +1 -1
  51. package/dist/repertoire/tools-session.js +33 -67
  52. package/dist/repertoire/tools-surface.js +3 -3
  53. package/dist/senses/cli.js +2 -2
  54. package/dist/senses/inner-dialog-worker.js +13 -519
  55. package/dist/senses/inner-dialog.js +10 -961
  56. package/dist/senses/mail.js +21 -5
  57. package/dist/senses/pipeline.js +1 -1
  58. package/dist/senses/private-runtime-worker.js +578 -0
  59. package/dist/senses/private-runtime.js +1145 -0
  60. package/dist/senses/shared-turn.js +1 -1
  61. package/dist/senses/trust-gate.js +2 -2
  62. package/package.json +1 -1
  63. package/dist/mailbox-ui/assets/index-CaTIFDmv.js +0 -1
package/README.md CHANGED
@@ -97,7 +97,12 @@ Task docs do not live in this repo anymore. Planning and doing docs live in the
97
97
  ## Runtime Truths
98
98
 
99
99
  - `agent.json` is the source of truth for identity, phrase pools, context settings, enabled senses, vault coordinates, and provider+model selection. It has two provider lanes: `outward` for CLI, Teams, BlueBubbles, Mail, and Voice turns, and `inner` for private agent-facing turns.
100
+ - The `inner` lane is a provider/model lane, not the private-runtime system name.
101
+ - Provider/model selection belongs to `agent.json` lanes; `privateRuntime` cannot select providers or models.
100
102
  - Legacy `humanFacing`/`agentFacing` provider fields are read only as compatibility aliases for `outward`/`inner`; they are not a second config surface.
103
+ - Starting the private runtime worker is process supervision, not a model turn.
104
+ - Denied/default private-runtime policy records or queues work with zero provider calls.
105
+ - Provider-readiness pings are explicit readiness checks, not private turns.
101
106
  - Each agent has one credential vault for provider, runtime, sense, integration, travel, and tool credentials. There is no machine-wide credential pool.
102
107
  - Vault unlock material is local machine state. Prefer macOS Keychain, Windows DPAPI, or Linux Secret Service; plaintext fallback is allowed only by explicit human choice.
103
108
  - New vault unlock secrets are confirmed before use and rejected if they do not meet the minimum strength requirements.
@@ -134,7 +139,7 @@ When you want this machine to use a provider/model for a lane, use:
134
139
  ouro use --agent <name> --lane <outward|inner> --provider <provider> --model <model>
135
140
  ```
136
141
 
137
- The outward lane handles user-facing senses. The inner lane handles the agent's private thinking. `ouro use` performs the provider/model check before committing the lane, so a broken local choice fails fast with a repair path instead of surprising the next turn.
142
+ The outward lane selects the provider/model for user-facing senses. The `inner` lane selects the provider/model for private agent-facing turns; private-runtime policy decides whether those turns may run. `ouro use` performs the provider/model check before committing the lane, so a broken local choice fails fast with a repair path instead of surprising the next turn.
138
143
 
139
144
  For the full locked auth/provider contract, including refresh, repair actors, caching, and SerpentGuide hatch bootstrap, see `docs/auth-and-providers.md`.
140
145
 
@@ -207,7 +212,8 @@ ouro poke <agent> --task <task-id>
207
212
  ouro poke <agent> --habit <habit-name>
208
213
  ouro habit list --agent <agent>
209
214
  ouro habit create --agent <agent> <name> --cadence <interval>
210
- ouro inner --agent <agent> # private turn status
215
+ ouro private status --agent <agent>
216
+ ouro private decisions --agent <agent>
211
217
  ouro attention --agent <agent> # attention queue
212
218
  ouro link <agent> --friend <id> --provider <provider> --external-id <external-id>
213
219
  ouro setup --tool <tool> --agent <name> # register MCP server + hooks with a dev tool
@@ -221,13 +227,13 @@ The generic secret primitive is a vault item / credential in the owning agent va
221
227
 
222
228
  To clone an existing agent onto a new machine (macOS, Linux, or Windows via WSL2), see **[docs/cross-machine-setup.md](docs/cross-machine-setup.md)**. The short version is bundle plus vault: `npx ouro.bot@latest`, open the home deck, choose clone, enter the bundle's git remote URL, unlock the agent vault, refresh/verify credentials, and start with `ouro up`.
223
229
 
224
- ## The Agent's Inner Life
230
+ ## The Agent's Private Runtime And Rhythms
225
231
 
226
- Agents in Ouroboros aren't just responders — they have an autonomous inner life.
232
+ Agents in Ouroboros aren't just responders — they have private agent-facing turns, recurring rhythms, durable records, and explicit spend policy.
227
233
 
228
234
  **Habits** are the agent's rhythms. A habit is an Ouro-native cron wrapper: it fires a private agent-facing session, can surface to family or the habit originator when it needs help or needs to report back, and leaves an audit receipt.
229
235
 
230
- **The inner lane** is where private sessions run. Habit runs, private returns, awaits, and self-maintenance can happen there, but the lane is not a record substrate. Anything durable leaves the lane: live continuity and audit go to Arc; work goes to Desk; learned facts and reference notes go to the Desk record.
236
+ **The private runtime** is where private agent-facing turns run. It uses the `inner` provider/model lane, but the lane is only provider selection; the runtime is governed by private-runtime policy, receipts, and attention queues. Habit runs, private returns, awaits, and self-maintenance can happen privately, but private context is not a record substrate. Anything durable leaves the turn: live continuity and audit go to Arc; work goes to Desk; learned facts and reference notes go to the Desk record.
231
237
 
232
238
  **Desk and Arc** are the durable orientation pair. Arc owns live continuity, claims, obligations, and habit run receipts. Desk owns durable work and the maintained record. The target substrate is captured in [Agent Orientation Substrate](docs/agent-orientation-substrate.md).
233
239
 
package/changelog.json CHANGED
@@ -1,6 +1,25 @@
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.676",
6
+ "changes": [
7
+ "Make npm release-channel authority explicit: current clients consume the selected latest dist-tag under trusted publishing, legacy alpha is not treated as a supported pre-stable client channel, and tests guard install surfaces from teaching stale alpha usage."
8
+ ]
9
+ },
10
+ {
11
+ "version": "0.1.0-alpha.675",
12
+ "changes": [
13
+ "Render private-runtime prompt metadata without current inner channel/sense labels: private-runtime turns now show channel private-runtime and current sense private runtime (agent-facing runtime, not a sense), with tests and guards preventing channel/current-sense inner labels from returning.",
14
+ "Render canonical private-runtime CLI commands in prompt and usage surfaces: normal command lists now show ouro private status/decisions, ouro inner is labeled as a legacy alias only, and vocabulary guards reject unqualified ouro inner outside explicit compatibility fixtures.",
15
+ "Bind private-runtime spend receipts to resolved provider lane metadata and keep legacy inner terminology out of active prompt, ponder, socket, and generic CLI usage surfaces; direct ouro inner help remains a hidden compatibility alias only.",
16
+ "Bind production private-runtime receipts to cached provider credential revisions and fail closed when the receipt revision is missing, the selected lane's credential revision is unavailable, or the revision changes before execution.",
17
+ "Route fresh-session task, habit, and await private-runtime wakes through their payload-specific prompt builders instead of spending an approved receipt on generic bootstrap text.",
18
+ "Treat non-autostart private-runtime workers as intentionally parked in daemon status and startup polling while preloading provider credential metadata for revision-bound provider readiness.",
19
+ "Refresh live provider readiness during ouro up only when daemon status has configured provider rows with unknown readiness, preserving passive private-runtime startup without repeated provider-worker wakeups.",
20
+ "Keep the private-runtime coverage gate at 100% after the passive-startup hardening by covering credential-cache misses, legacy compatibility shims, passive timeout parking, and explicit daemon initial-alive branches."
21
+ ]
22
+ },
4
23
  {
5
24
  "version": "0.1.0-alpha.674",
6
25
  "changes": [
@@ -82,19 +82,19 @@ const ALL_ACTIONS = [
82
82
  "ratify",
83
83
  ];
84
84
  const HUMAN_REQUIRED_ACTIONS = new Set([
85
+ "mutate_credentials",
86
+ ]);
87
+ const REVIEWER_REQUIRED_ACTIONS = new Set([
88
+ "merge_pr",
85
89
  "release_publish",
90
+ "install_local",
91
+ "mutate_shared_skill",
86
92
  "mutate_identity",
87
93
  "mutate_voice",
88
- "mutate_credentials",
89
94
  "mutate_provider_config",
90
95
  "send_external_message",
91
96
  "change_hosted_infra",
92
97
  ]);
93
- const ASK_BEFORE_ACTIONS = new Set([
94
- "merge_pr",
95
- "install_local",
96
- "mutate_shared_skill",
97
- ]);
98
98
  function nowIso() {
99
99
  return new Date().toISOString();
100
100
  }
@@ -135,14 +135,14 @@ function defaultAuthority(updatedAt) {
135
135
  for (const action of ALL_ACTIONS) {
136
136
  actions[action] = HUMAN_REQUIRED_ACTIONS.has(action)
137
137
  ? "human_required"
138
- : ASK_BEFORE_ACTIONS.has(action)
139
- ? "ask_before_action"
138
+ : REVIEWER_REQUIRED_ACTIONS.has(action)
139
+ ? "reviewer_required"
140
140
  : "allowed";
141
141
  }
142
142
  return {
143
143
  actions,
144
144
  updatedAt,
145
- reason: "default conservative evolution authority",
145
+ reason: "default conservative evolution authority with reviewer gates",
146
146
  };
147
147
  }
148
148
  function isEvolutionCase(value) {
@@ -227,6 +227,9 @@ function budgetExhausted(item, action) {
227
227
  }
228
228
  return false;
229
229
  }
230
+ function fallbackAuthorityForAction(action) {
231
+ return HUMAN_REQUIRED_ACTIONS.has(action) ? "human_required" : "reviewer_required";
232
+ }
230
233
  function spendBudget(item, action) {
231
234
  const budget = {
232
235
  ...item.budget,
@@ -370,7 +373,7 @@ function evaluateEvolutionAction(agentRoot, caseId, action) {
370
373
  return { allowed: false, code: "case_not_found", reason: `Evolution case not found: ${caseId}` };
371
374
  if (isTerminal(item))
372
375
  return { allowed: false, code: "terminal_case", reason: `Evolution case is ${item.status}` };
373
- const authority = item.authority.actions[action] ?? "human_required";
376
+ const authority = item.authority.actions[action] ?? fallbackAuthorityForAction(action);
374
377
  if (authority !== "allowed")
375
378
  return { allowed: false, code: authority, reason: `${action} is ${authority}` };
376
379
  if (budgetExhausted(item, action))
@@ -280,7 +280,7 @@ function advanceReturnObligation(agentName, obligationId, update) {
280
280
  });
281
281
  return updated;
282
282
  }
283
- // Inner return obligations that have been sitting in queued/running state
283
+ // Private-runtime return obligations that have been sitting in queued/running state
284
284
  // longer than this are auto-pruned from the "held work items" injection.
285
285
  // Anything older is overwhelmingly noise: the agent has had many turns to
286
286
  // resolve them and has not, and reinjecting them every turn just burns
@@ -199,7 +199,7 @@ function formatActiveLane(frame, obligation) {
199
199
  return "this same thread";
200
200
  }
201
201
  if (frame.inner?.job?.status === "running") {
202
- return "inner lane";
202
+ return "private runtime";
203
203
  }
204
204
  return null;
205
205
  }
@@ -681,7 +681,7 @@ function buildActiveWorkFrame(input) {
681
681
  }
682
682
  function formatActiveWorkFrame(frame, options) {
683
683
  const lines = ["## what i'm holding"];
684
- lines.push("this is my top-level live world-state right now. inner work, coding lanes, other sessions, and return obligations all belong inside this picture.");
684
+ 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
685
  lines.push("if older checkpoints elsewhere in the transcript disagree with this picture, this picture wins.");
686
686
  const primaryObligation = findPrimaryOpenObligation(frame);
687
687
  const currentSessionObligation = findCurrentSessionOpenObligation(frame);
@@ -847,7 +847,7 @@ function formatActiveWorkFrame(frame, options) {
847
847
  }
848
848
  if (frame.innerReturnObligations && frame.innerReturnObligations.length > 0) {
849
849
  lines.push("");
850
- lines.push("## inner return obligations");
850
+ lines.push("## private-runtime return obligations");
851
851
  for (const ob of frame.innerReturnObligations) {
852
852
  const preview = ob.delegatedContent.length > 60
853
853
  ? `${ob.delegatedContent.slice(0, 57)}...`
@@ -48,17 +48,17 @@ const runtime_1 = require("../nerves/runtime");
48
48
  (0, runtime_1.emitNervesEvent)({
49
49
  component: "senses",
50
50
  event: "senses.entry_boot",
51
- message: "booting inner-dialog entrypoint",
52
- meta: { entry: "inner-dialog", agentName },
51
+ message: "starting private-runtime process entrypoint",
52
+ meta: { entry: "private-runtime", agentName },
53
53
  });
54
- // Dynamic import: agent-entry is boot-time wiring that starts a sense process.
54
+ // Dynamic import: agent-entry is process-start wiring that starts a sense process.
55
55
  // Using dynamic import avoids a static heart/ -> senses/ dependency.
56
56
  Promise.resolve().then(() => __importStar(require("./runtime-credentials"))).then(async ({ readMachineRuntimeCredentialConfig, readRuntimeCredentialConfig, refreshMachineRuntimeCredentialConfig, refreshRuntimeCredentialConfig, waitForRuntimeCredentialBootstrap, }) => {
57
57
  await waitForRuntimeCredentialBootstrap(agentName);
58
58
  if (!readRuntimeCredentialConfig(agentName).ok) {
59
59
  void refreshRuntimeCredentialConfig(agentName, { preserveCachedOnFailure: true }).catch(() => undefined);
60
60
  }
61
- /* v8 ignore next 7 -- boot-time best-effort machine credential refresh runs in a child entrypoint and is covered operationally by daemon startup tests @preserve */
61
+ /* v8 ignore next 7 -- process-start best-effort machine credential refresh runs in a child entrypoint and is covered operationally by daemon startup tests @preserve */
62
62
  if (!readMachineRuntimeCredentialConfig(agentName).ok) {
63
63
  void Promise.resolve().then(() => __importStar(require("./machine-identity"))).then(({ loadOrCreateMachineIdentity }) => {
64
64
  const machine = loadOrCreateMachineIdentity();
@@ -66,16 +66,16 @@ Promise.resolve().then(() => __importStar(require("./runtime-credentials"))).the
66
66
  })
67
67
  .catch(() => undefined);
68
68
  }
69
- const { startInnerDialogWorker } = await Promise.resolve().then(() => __importStar(require("../senses/inner-dialog-worker")));
70
- await startInnerDialogWorker();
69
+ const { startPrivateRuntimeWorker } = await Promise.resolve().then(() => __importStar(require("../senses/private-runtime-worker")));
70
+ await startPrivateRuntimeWorker();
71
71
  })
72
72
  .catch((error) => {
73
73
  (0, runtime_1.emitNervesEvent)({
74
74
  level: "error",
75
75
  component: "senses",
76
76
  event: "senses.entry_error",
77
- message: "inner-dialog entrypoint failed",
78
- meta: { entry: "inner-dialog", agentName, error: error instanceof Error ? error.message : String(error) },
77
+ message: "private-runtime entrypoint failed",
78
+ meta: { entry: "private-runtime", agentName, error: error instanceof Error ? error.message : String(error) },
79
79
  });
80
80
  // eslint-disable-next-line no-console -- fatal startup guard for worker process
81
81
  console.error(error instanceof Error ? error.message : String(error));
@@ -72,7 +72,7 @@ function deriveCommitments(activeWorkFrame, innerJob, pendingObligations, pendin
72
72
  }
73
73
  // Safe to ignore
74
74
  if (innerJob.status === "idle" && !(activeWorkFrame.inner?.hasPending)) {
75
- safeToIgnore.push("no private thinking in progress");
75
+ safeToIgnore.push("no private-runtime work in progress");
76
76
  }
77
77
  if (activeWorkFrame.bridges.length === 0) {
78
78
  safeToIgnore.push("no shared work to coordinate");
@@ -334,7 +334,7 @@ async function habitToolBatchBlockReason(habitSession, toolCalls, delegatedOrigi
334
334
  return null;
335
335
  }
336
336
  /** Chat-style channels expose the `speak` tool — outer human-conversation channels
337
- * where mid-turn delivery is meaningful. Inner dialog has `ponder`. MCP returns
337
+ * where mid-turn delivery is meaningful. The private runtime has `ponder`. MCP returns
338
338
  * synchronously. Mail is batch. Anything else (unknown channel) treats as non-chat. */
339
339
  function isChatStyleChannel(channel) {
340
340
  return channel === "cli" || channel === "teams" || channel === "bluebubbles" || channel === "voice";
@@ -483,7 +483,7 @@ function claimsPrivateReturnQueued(answer) {
483
483
  if (!answer)
484
484
  return false;
485
485
  const normalized = answer.toLowerCase();
486
- return /\b(queued|queue|will return|return when|when .*complete|when .*completes|private pass|inner dialog completes|later)\b/.test(normalized)
486
+ return /\b(queued|queue|will return|return when|when .*complete|when .*completes|private pass|private runtime completes|inner dialog completes|later)\b/.test(normalized)
487
487
  && /\b(private|inner|return|queued|later)\b/.test(normalized);
488
488
  }
489
489
  function privateReturnMissingPonderError(input) {
@@ -493,7 +493,7 @@ function privateReturnMissingPonderError(input) {
493
493
  return null;
494
494
  if (!claimsPrivateReturnQueued(input.answer))
495
495
  return null;
496
- return "private-return acknowledgement claimed work was queued, but no ponder packet was created this turn. Call ponder(action=create, ...) first so the return has a packet, return obligation, and inner wake; then settle with only a queued acknowledgement. If you cannot create the packet, ask a blocking clarification without saying it is queued.";
496
+ return "private-return acknowledgement claimed work was queued, but no ponder packet was created this turn. Call ponder(action=create, ...) first so the return has a packet, return obligation, and private-runtime wake; then settle with only a queued acknowledgement. If you cannot create the packet, ask a blocking clarification without saying it is queued.";
497
497
  }
498
498
  function activeReturnObligationId(agentName, obligationId) {
499
499
  if (!obligationId)
@@ -501,6 +501,27 @@ function activeReturnObligationId(agentName, obligationId) {
501
501
  const obligation = (0, obligations_1.readReturnObligation)(agentName, obligationId);
502
502
  return obligation?.status === "queued" || obligation?.status === "running" ? obligationId : null;
503
503
  }
504
+ function ponderReturnSessionId(packet) {
505
+ const origin = packet.origin;
506
+ if (!origin)
507
+ return "unknown/unknown/unknown";
508
+ return `${origin.friendId}/${origin.channel}/${origin.key}`;
509
+ }
510
+ function buildPonderReturnPrivateWakeOptions(input) {
511
+ const sessionId = ponderReturnSessionId(input.packet);
512
+ return {
513
+ reason: "ponder return obligation private attention",
514
+ triggerSource: "ponder-return-obligation",
515
+ budgetClass: "interactive",
516
+ idempotencyKey: `ponder-return:${input.agentName}:${input.returnObligationId}:${input.packet.id}:${sessionId}`,
517
+ originRefs: [
518
+ { kind: "tool", id: "ponder" },
519
+ { kind: "ponder-packet", id: input.packet.id },
520
+ { kind: "return-obligation", id: input.returnObligationId },
521
+ { kind: "session", id: sessionId },
522
+ ],
523
+ };
524
+ }
504
525
  function buildPonderResult(packet, action, returnObligationId) {
505
526
  return JSON.stringify({
506
527
  ok: true,
@@ -509,7 +530,7 @@ function buildPonderResult(packet, action, returnObligationId) {
509
530
  status: packet.status,
510
531
  return_obligation_id: returnObligationId,
511
532
  private_return_contract: returnObligationId
512
- ? "queued for inner attention; do not present the requested private answer as complete in this same outward turn. if you answer now, only say the private pass is queued and will return when ready."
533
+ ? "queued for private-runtime attention; do not present the requested private answer as complete in this same outward turn. if you answer now, only say the private pass is queued and will return when ready."
513
534
  : null,
514
535
  }, null, 2);
515
536
  }
@@ -885,30 +906,30 @@ async function runAgent(messages, callbacks, channel, signal, options) {
885
906
  providerRuntime.resetTurnState(messages);
886
907
  while (!done) {
887
908
  // Channel-based tool filtering:
888
- // - Inner dialog: exclude send_message (delivery via surface), observe (no one to observe)
909
+ // - Private runtime: exclude send_message (delivery via surface), observe (no one to observe)
889
910
  // - All outward channels (1:1, group, reaction): observe available
890
911
  //
891
912
  // ponder, settle/rest, surface, and observe are always assembled based on channel context.
892
913
  // ponder is available in ALL channels (outer: think privately, inner: keep turning).
893
- // Inner dialog gets restTool instead of settleTool (rest = end turn, gated by attention queue).
914
+ // Private runtime gets restTool instead of settleTool (rest = end turn, gated by attention queue).
894
915
  // toolChoiceRequired only controls whether tool_choice: "required" is set in the API call.
895
- const isInnerDialog = channel === "inner";
896
- const innerHabitCanSendMessage = isInnerDialog
916
+ const isPrivateRuntimeChannel = channel === "inner";
917
+ const privateRuntimeHabitCanSendMessage = isPrivateRuntimeChannel
897
918
  && habitSession?.toolPolicy.outwardMessagingAllowed === true
898
919
  && habitSession.toolPolicy.grantedTools.includes("send_message");
899
- const innerHabitCanSurface = isInnerDialog
920
+ const privateRuntimeHabitCanSurface = isPrivateRuntimeChannel
900
921
  && (!habitSession || (habitSession.toolPolicy.outwardMessagingAllowed === true
901
922
  && habitSession.toolPolicy.grantedTools.includes("surface")));
902
- const filteredBaseTools = isInnerDialog
903
- ? baseTools.filter((t) => innerHabitCanSendMessage || t.function.name !== "send_message")
923
+ const filteredBaseTools = isPrivateRuntimeChannel
924
+ ? baseTools.filter((t) => privateRuntimeHabitCanSendMessage || t.function.name !== "send_message")
904
925
  : baseTools;
905
926
  const activeTools = [
906
927
  ...filteredBaseTools,
907
928
  tools_1.ponderTool,
908
- ...(isInnerDialog && innerHabitCanSurface ? [tools_2.surfaceToolDef] : []),
909
- ...(isInnerDialog ? [tools_1.restTool] : []),
910
- ...(!isInnerDialog ? [tools_1.observeTool] : []),
911
- ...(!isInnerDialog ? [tools_1.settleTool] : []),
929
+ ...(isPrivateRuntimeChannel && privateRuntimeHabitCanSurface ? [tools_2.surfaceToolDef] : []),
930
+ ...(isPrivateRuntimeChannel ? [tools_1.restTool] : []),
931
+ ...(!isPrivateRuntimeChannel ? [tools_1.observeTool] : []),
932
+ ...(!isPrivateRuntimeChannel ? [tools_1.settleTool] : []),
912
933
  ...(isChatStyleChannel(channel ?? "") ? [tools_1.speakTool] : []),
913
934
  ];
914
935
  const activeToolNames = new Set(activeTools.map((tool) => tool.function.name));
@@ -1179,7 +1200,7 @@ async function runAgent(messages, callbacks, channel, signal, options) {
1179
1200
  messages.push(msg);
1180
1201
  messages.push({
1181
1202
  role: "user",
1182
- content: isInnerDialog
1203
+ content: isPrivateRuntimeChannel
1183
1204
  ? "no tool was called this turn. you must end every turn by calling rest (or surface, ponder, observe). emit the tool call now."
1184
1205
  : "no tool was called this turn. you must end every turn by calling settle with your answer (or ponder/observe). emit the tool call now.",
1185
1206
  });
@@ -1222,9 +1243,9 @@ async function runAgent(messages, callbacks, channel, signal, options) {
1222
1243
  return {};
1223
1244
  } })();
1224
1245
  callbacks.onToolStart("settle", settleArgs);
1225
- // Inner dialog attention queue gate: reject settle if items remain
1246
+ // Private-runtime attention queue gate: reject settle if items remain
1226
1247
  const attentionQueue = (augmentedToolContext ?? options?.toolContext)?.delegatedOrigins;
1227
- if (isInnerDialog && attentionQueue && attentionQueue.length > 0) {
1248
+ if (isPrivateRuntimeChannel && attentionQueue && attentionQueue.length > 0) {
1228
1249
  callbacks.onToolEnd("settle", (0, tools_1.summarizeArgs)("settle", settleArgs), false);
1229
1250
  callbacks.onClearText?.();
1230
1251
  messages.push(msg);
@@ -1236,8 +1257,8 @@ async function runAgent(messages, callbacks, channel, signal, options) {
1236
1257
  // Extract answer from the tool call arguments.
1237
1258
  // Supports: {"answer":"text","intent":"..."} or "text" (JSON string).
1238
1259
  const { answer, intent } = parseSettlePayload(result.toolCalls[0].arguments);
1239
- // Inner dialog settle: no CompletionMetadata, "(settled)" ack
1240
- if (isInnerDialog) {
1260
+ // Private-runtime settle: no CompletionMetadata, "(settled)" ack
1261
+ if (isPrivateRuntimeChannel) {
1241
1262
  callbacks.onToolEnd("settle", (0, tools_1.summarizeArgs)("settle", settleArgs), true);
1242
1263
  messages.push(msg);
1243
1264
  const settled = "(settled)";
@@ -1407,7 +1428,7 @@ async function runAgent(messages, callbacks, channel, signal, options) {
1407
1428
  }
1408
1429
  if (tc.name === "send_message" && args.friendId === "self") {
1409
1430
  const latestUserText = latestUserMessageText(messages);
1410
- if (!isInnerDialog && looksLikePrivateReturnRequest(latestUserText)) {
1431
+ if (!isPrivateRuntimeChannel && looksLikePrivateReturnRequest(latestUserText)) {
1411
1432
  const argSummary = (0, tools_1.summarizeArgs)(tc.name, args);
1412
1433
  const rejection = "private-return requests must use ponder, not send_message(friendId=self). Create a typed ponder packet with the marker/source request preserved, then only acknowledge that the private pass is queued.";
1413
1434
  callbacks.onToolStart(tc.name, args);
@@ -1498,7 +1519,7 @@ async function runAgent(messages, callbacks, channel, signal, options) {
1498
1519
  let privateReturnSourceRequest = "";
1499
1520
  if (action === "create") {
1500
1521
  if (isInnerChannel && attentionQueue.length > 0) {
1501
- throw new Error("inner dialog already has held return work in the attention queue; surface the existing delegationId instead of creating a replacement ponder packet.");
1522
+ throw new Error("private runtime already has held return work in the attention queue; surface the existing delegationId instead of creating a replacement ponder packet.");
1502
1523
  }
1503
1524
  const kind = parsedArgs.kind;
1504
1525
  const objective = typeof parsedArgs.objective === "string" ? parsedArgs.objective.trim() : "";
@@ -1608,7 +1629,8 @@ async function runAgent(messages, callbacks, channel, signal, options) {
1608
1629
  for (const token of extractPrivateReturnHeldTokens(privateReturnSourceRequest)) {
1609
1630
  privateReturnHeldTokens.add(token);
1610
1631
  }
1611
- await (0, socket_client_1.requestInnerWake)((0, identity_2.getAgentName)(), augmentedToolContext?.daemonSocketPath).catch(() => undefined);
1632
+ const agentName = (0, identity_2.getAgentName)();
1633
+ await (0, socket_client_1.requestPrivateWake)(agentName, augmentedToolContext?.daemonSocketPath, buildPonderReturnPrivateWakeOptions({ agentName, packet, returnObligationId })).catch(() => undefined);
1612
1634
  }
1613
1635
  sawPonder = true;
1614
1636
  toolResult = buildPonderResult(packet, resultAction, returnObligationId);
@@ -101,6 +101,27 @@ function resolveFacingProvider(parsed, facing, agentName, agentJsonPath) {
101
101
  : (0, provider_models_1.getDefaultModelForProvider)(provider);
102
102
  return { ok: true, selected: { facing, lane: laneForFacing(facing), provider, model } };
103
103
  }
104
+ function validatePrivateRuntimeConfig(agentName, agentJsonPath, parsed) {
105
+ const privateRuntime = parsed.privateRuntime;
106
+ if (privateRuntime === undefined)
107
+ return null;
108
+ if (!privateRuntime || typeof privateRuntime !== "object" || Array.isArray(privateRuntime)) {
109
+ return {
110
+ ok: false,
111
+ error: `agent.json for '${agentName}' has invalid privateRuntime config; privateRuntime must be an object.`,
112
+ fix: `Use privateRuntime only for private-runtime policy such as { autoStart: false } in ${agentJsonPath}. Provider/model selection belongs to the inner lane: ouro use --agent ${agentName} --lane inner --provider <provider> --model <model>.`,
113
+ };
114
+ }
115
+ const privateRuntimeRecord = privateRuntime;
116
+ const forbidden = ["provider", "model"].filter((key) => Object.prototype.hasOwnProperty.call(privateRuntimeRecord, key));
117
+ if (forbidden.length === 0)
118
+ return null;
119
+ return {
120
+ ok: false,
121
+ error: `agent.json for '${agentName}' cannot set ${forbidden.map((key) => `privateRuntime.${key}`).join(" or ")}; privateRuntime cannot select providers or models.`,
122
+ fix: `Remove ${forbidden.map((key) => `privateRuntime.${key}`).join(" and ")} from ${agentJsonPath}. Provider/model selection belongs to the inner lane: ouro use --agent ${agentName} --lane inner --provider <provider> --model <model>.`,
123
+ };
124
+ }
104
125
  function readAgentConfigForProviderCheck(agentName, bundlesRoot) {
105
126
  const agentJsonPath = configPathFor(agentName, bundlesRoot);
106
127
  let raw;
@@ -134,6 +155,9 @@ function readAgentConfigForProviderCheck(agentName, bundlesRoot) {
134
155
  if (parsed.enabled === false) {
135
156
  return { ok: true, disabled: true, agentJsonPath, parsed };
136
157
  }
158
+ const privateRuntimeError = validatePrivateRuntimeConfig(agentName, agentJsonPath, parsed);
159
+ if (privateRuntimeError)
160
+ return { ok: false, result: privateRuntimeError };
137
161
  return { ok: true, disabled: false, agentJsonPath, parsed };
138
162
  }
139
163
  function readProviderSelectionForCheck(agentName, bundlesRoot) {
@@ -36,6 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.isLibraryKind = isLibraryKind;
37
37
  exports.listAllBundleAgents = listAllBundleAgents;
38
38
  exports.listEnabledBundleAgents = listEnabledBundleAgents;
39
+ exports.readPrivateRuntimeConfig = readPrivateRuntimeConfig;
39
40
  exports.isInnerDialogAutoStartEnabled = isInnerDialogAutoStartEnabled;
40
41
  exports.listBundleSyncRows = listBundleSyncRows;
41
42
  const fs = __importStar(require("fs"));
@@ -117,44 +118,54 @@ function listEnabledBundleAgents(options = {}) {
117
118
  .filter((row) => row.enabled && !isLibraryKind(row.kind))
118
119
  .map((row) => row.name);
119
120
  }
120
- /**
121
- * Per-agent runtime policy for the always-on inner-dialog worker. Agents
122
- * default to the historic behavior (auto-start on daemon boot), but a bundle
123
- * can opt out with:
124
- *
125
- * "innerDialog": { "autoStart": false }
126
- *
127
- * This keeps the agent enabled for sync/status/senses while preventing an
128
- * autonomous boot turn from spending model credits.
129
- */
130
- function isInnerDialogAutoStartEnabled(agent, options = {}) {
121
+ function readPrivateRuntimeConfig(agent, options = {}) {
131
122
  const bundlesRoot = options.bundlesRoot ?? (0, identity_1.getAgentBundlesRoot)();
132
123
  const readFileSync = options.readFileSync ?? fs.readFileSync;
133
124
  const configPath = path.join(bundlesRoot, `${agent}.ouro`, "agent.json");
134
125
  try {
135
126
  const raw = readFileSync(configPath, "utf-8");
136
127
  const parsed = JSON.parse(raw);
128
+ const privateRuntime = parsed.privateRuntime;
129
+ if (privateRuntime && typeof privateRuntime === "object" && !Array.isArray(privateRuntime)) {
130
+ return {
131
+ autoStart: privateRuntime.autoStart === true,
132
+ source: "privateRuntime",
133
+ };
134
+ }
137
135
  const innerDialog = parsed.innerDialog;
138
136
  if (innerDialog && typeof innerDialog === "object" && !Array.isArray(innerDialog)) {
139
- return innerDialog.autoStart !== false;
137
+ return {
138
+ autoStart: innerDialog.autoStart === true,
139
+ source: "legacy-innerDialog",
140
+ compatibilityNote: "innerDialog.autoStart is a legacy migration input; use privateRuntime.autoStart.",
141
+ };
140
142
  }
141
- return true;
143
+ return { autoStart: false, source: "default" };
142
144
  }
143
145
  catch (error) {
146
+ const message = error instanceof Error ? error.message : String(error);
144
147
  (0, runtime_1.emitNervesEvent)({
145
148
  level: "warn",
146
149
  component: "daemon",
147
- event: "daemon.inner_dialog_policy_read_failed",
148
- message: "failed to read inner-dialog auto-start policy; refusing autonomous worker start",
150
+ event: "daemon.private_runtime_policy_read_failed",
151
+ message: "failed to read private-runtime auto-start policy; refusing autonomous private-runtime start",
149
152
  meta: {
150
153
  agent,
151
154
  configPath,
152
- error: error instanceof Error ? error.message : String(error),
155
+ error: message,
153
156
  },
154
157
  });
155
- return false;
158
+ return { autoStart: false, source: "unreadable", error: message };
156
159
  }
157
160
  }
161
+ /**
162
+ * Legacy compatibility shim for older call sites. New code should use
163
+ * readPrivateRuntimeConfig() so compatibility notes and failure provenance are
164
+ * preserved.
165
+ */
166
+ function isInnerDialogAutoStartEnabled(agent, options = {}) {
167
+ return readPrivateRuntimeConfig(agent, options).autoStart;
168
+ }
158
169
  /**
159
170
  * Read the per-agent sync block from each enabled bundle's agent.json.
160
171
  * Used by the daemon (and stopped-state status renderer) to build per-agent
@@ -75,8 +75,8 @@ function readAgentFile(agent, ...segments) {
75
75
  function agentDiaryRoot(agent) {
76
76
  return (0, record_paths_1.resolveRecordDiaryRoot)((0, identity_1.getAgentRoot)(agent));
77
77
  }
78
- /** Read inner dialog runtime status. */
79
- function readInnerDialogStatus(agent) {
78
+ /** Read private-runtime status. */
79
+ function readPrivateRuntimeStatus(agent) {
80
80
  const content = readAgentFile(agent, "state", "sessions", "self", "inner", "runtime.json");
81
81
  if (!content)
82
82
  return null;
@@ -302,7 +302,7 @@ async function handleAgentStatus(params) {
302
302
  (0, runtime_1.emitNervesEvent)({ component: "daemon", event: "daemon.agent_service_start", message: "handling agent.status", meta: { agent: params.agent } });
303
303
  const diaryRoot = agentDiaryRoot(params.agent);
304
304
  const facts = (0, diary_1.readDiaryEntries)(diaryRoot);
305
- const innerStatus = readInnerDialogStatus(params.agent);
305
+ const innerStatus = readPrivateRuntimeStatus(params.agent);
306
306
  const sessions = enumerateSessions(params.agent);
307
307
  const mcpVersion = readMcpRuntimeVersion();
308
308
  const runtime = await readRuntimeStatus(params.socketPath ?? socket_client_1.DEFAULT_DAEMON_SOCKET_PATH, params.agent);
@@ -354,10 +354,10 @@ async function handleAgentCatchup(params) {
354
354
  const sessions = enumerateSessions(params.agent);
355
355
  const sorted = sessions.sort((a, b) => (b.lastUsage || "").localeCompare(a.lastUsage || ""));
356
356
  const recentSessions = sorted.slice(0, 5);
357
- const innerStatus = readInnerDialogStatus(params.agent);
357
+ const innerStatus = readPrivateRuntimeStatus(params.agent);
358
358
  const parts = [];
359
359
  if (innerStatus) {
360
- parts.push(`Inner dialog: ${innerStatus.status} (last completed: ${innerStatus.lastCompletedAt || "never"})`);
360
+ parts.push(`Private runtime: ${innerStatus.status} (last completed: ${innerStatus.lastCompletedAt || "never"})`);
361
361
  }
362
362
  if (recentSessions.length > 0) {
363
363
  parts.push(`Recent sessions (${recentSessions.length}):`);
@@ -391,7 +391,7 @@ async function handleAgentGetContext(params) {
391
391
  const query = (params.question ?? params.query ?? params.topic);
392
392
  const diaryRoot = agentDiaryRoot(params.agent);
393
393
  const facts = (0, diary_1.readDiaryEntries)(diaryRoot);
394
- const innerStatus = readInnerDialogStatus(params.agent);
394
+ const innerStatus = readPrivateRuntimeStatus(params.agent);
395
395
  const sessions = enumerateSessions(params.agent);
396
396
  const taskFiles = listTaskFiles(params.agent);
397
397
  let recordSummary = null;
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildAwaitPrivateWakeCommand = buildAwaitPrivateWakeCommand;
4
+ exports.awaitNameFromPrivateWakeCommand = awaitNameFromPrivateWakeCommand;
5
+ const runtime_1 = require("../../nerves/runtime");
6
+ const reasonPrefixByTrigger = {
7
+ "await-poke": "manual await condition check",
8
+ "await-scheduler": "scheduled await condition check",
9
+ "await-expiry": "queued await expiry alert",
10
+ };
11
+ function awaitOriginRefs(awaitName, triggerSource) {
12
+ if (triggerSource === "await-poke") {
13
+ return [
14
+ { kind: "await", id: awaitName },
15
+ { kind: "daemon-command", id: "await.poke" },
16
+ ];
17
+ }
18
+ if (triggerSource === "await-scheduler") {
19
+ return [
20
+ { kind: "await", id: awaitName },
21
+ { kind: "scheduler", id: "await-scheduler" },
22
+ ];
23
+ }
24
+ return [
25
+ { kind: "await", id: awaitName },
26
+ { kind: "await-alert", id: "expired" },
27
+ ];
28
+ }
29
+ function buildAwaitPrivateWakeCommand(options) {
30
+ const firedAt = (options.now ?? (() => new Date()))().toISOString();
31
+ (0, runtime_1.emitNervesEvent)({
32
+ component: "daemon",
33
+ event: "daemon.await_private_wake_built",
34
+ message: "built await private-runtime wake command",
35
+ meta: {
36
+ agent: options.agent,
37
+ awaitName: options.awaitName,
38
+ triggerSource: options.triggerSource,
39
+ },
40
+ });
41
+ return {
42
+ kind: "private.wake",
43
+ agent: options.agent,
44
+ reason: `${reasonPrefixByTrigger[options.triggerSource]} for ${options.awaitName}`,
45
+ triggerSource: options.triggerSource,
46
+ budgetClass: "scheduled",
47
+ idempotencyKey: `await:${options.agent}:${options.awaitName}:${options.triggerSource}:${firedAt}`,
48
+ originRefs: awaitOriginRefs(options.awaitName, options.triggerSource),
49
+ };
50
+ }
51
+ function awaitNameFromPrivateWakeCommand(command) {
52
+ if (command.kind !== "private.wake")
53
+ return null;
54
+ const awaitRef = command.originRefs?.find((ref) => ref.kind === "await" && typeof ref.id === "string");
55
+ if (!awaitRef || typeof awaitRef.id !== "string")
56
+ return null;
57
+ const trimmed = awaitRef.id.trim();
58
+ return trimmed.length > 0 ? trimmed : null;
59
+ }