@ouro.bot/cli 0.1.0-alpha.673 → 0.1.0-alpha.675
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/README.md +11 -5
- package/changelog.json +19 -0
- package/dist/arc/evolution.js +13 -10
- package/dist/arc/obligations.js +1 -1
- package/dist/heart/active-work.js +3 -3
- package/dist/heart/agent-entry.js +8 -8
- package/dist/heart/commitments.js +1 -1
- package/dist/heart/core.js +45 -23
- package/dist/heart/daemon/agent-config-check.js +24 -0
- package/dist/heart/daemon/agent-discovery.js +50 -0
- package/dist/heart/daemon/agent-service.js +6 -6
- package/dist/heart/daemon/await-private-wake.js +59 -0
- package/dist/heart/daemon/cli-exec.js +180 -24
- package/dist/heart/daemon/cli-help.js +20 -2
- package/dist/heart/daemon/cli-parse.js +45 -2
- package/dist/heart/daemon/cli-render.js +1 -0
- package/dist/heart/daemon/daemon-entry.js +126 -19
- package/dist/heart/daemon/daemon.js +223 -20
- package/dist/heart/daemon/habit-private-wake.js +56 -0
- package/dist/heart/daemon/health-monitor.js +6 -2
- package/dist/heart/daemon/inner-status.js +3 -3
- package/dist/heart/daemon/message-router.js +39 -3
- package/dist/heart/daemon/process-manager.js +1 -0
- package/dist/heart/daemon/pulse.js +43 -7
- package/dist/heart/daemon/socket-client.js +14 -2
- package/dist/heart/daemon/startup-tui.js +9 -1
- package/dist/heart/daemon/thoughts.js +39 -39
- package/dist/heart/habits/habit-scheduler.js +24 -4
- package/dist/heart/mailbox/mailbox-view.js +1 -1
- package/dist/heart/mailbox/readers/agent-machine.js +7 -7
- package/dist/heart/primitives.js +1 -1
- package/dist/heart/private-runtime/decision-renderer.js +156 -0
- package/dist/heart/private-runtime/index.js +18 -0
- package/dist/heart/private-runtime/ledger.js +218 -0
- package/dist/heart/private-runtime/policy.js +205 -0
- package/dist/heart/private-runtime/types.js +2 -0
- package/dist/heart/progress-story.js +1 -1
- package/dist/heart/session-transcript.js +1 -1
- package/dist/heart/tool-activity-callbacks.js +1 -1
- package/dist/heart/tool-description.js +2 -2
- package/dist/heart/tool-loop.js +2 -2
- package/dist/heart/turn-context.js +5 -5
- package/dist/mailbox-ui/assets/index-J8zDwHmE.js +1 -0
- package/dist/mailbox-ui/index.html +1 -1
- package/dist/mailroom/attention.js +1 -1
- package/dist/mind/context.js +2 -2
- package/dist/mind/pending.js +12 -5
- package/dist/mind/prompt.js +32 -25
- package/dist/repertoire/coding/feedback.js +25 -4
- package/dist/repertoire/tools-awaiting.js +2 -2
- package/dist/repertoire/tools-flow.js +4 -4
- package/dist/repertoire/tools-record.js +1 -1
- package/dist/repertoire/tools-session.js +33 -58
- package/dist/repertoire/tools-surface.js +3 -3
- package/dist/senses/cli.js +2 -2
- package/dist/senses/inner-dialog-worker.js +13 -519
- package/dist/senses/inner-dialog.js +10 -961
- package/dist/senses/mail.js +21 -5
- package/dist/senses/pipeline.js +1 -1
- package/dist/senses/private-runtime-worker.js +578 -0
- package/dist/senses/private-runtime.js +1145 -0
- package/dist/senses/shared-turn.js +1 -1
- package/dist/senses/trust-gate.js +2 -2
- package/package.json +1 -1
- 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
|
|
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
|
|
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
|
|
230
|
+
## The Agent's Private Runtime And Rhythms
|
|
225
231
|
|
|
226
|
-
Agents in Ouroboros aren't just responders — they have
|
|
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
|
|
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.675",
|
|
6
|
+
"changes": [
|
|
7
|
+
"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.",
|
|
8
|
+
"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.",
|
|
9
|
+
"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.",
|
|
10
|
+
"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.",
|
|
11
|
+
"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.",
|
|
12
|
+
"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.",
|
|
13
|
+
"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.",
|
|
14
|
+
"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."
|
|
15
|
+
]
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"version": "0.1.0-alpha.674",
|
|
19
|
+
"changes": [
|
|
20
|
+
"Add an inner-dialog auto-start guard so enabled agents can stay online without autonomous model-spending boot turns, including client-side self-message fallback paths and daemon health/status surfaces that respect intentionally parked agents."
|
|
21
|
+
]
|
|
22
|
+
},
|
|
4
23
|
{
|
|
5
24
|
"version": "0.1.0-alpha.673",
|
|
6
25
|
"changes": [
|
package/dist/arc/evolution.js
CHANGED
|
@@ -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
|
-
:
|
|
139
|
-
? "
|
|
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] ??
|
|
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))
|
package/dist/arc/obligations.js
CHANGED
|
@@ -280,7 +280,7 @@ function advanceReturnObligation(agentName, obligationId, update) {
|
|
|
280
280
|
});
|
|
281
281
|
return updated;
|
|
282
282
|
}
|
|
283
|
-
//
|
|
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 "
|
|
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.
|
|
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("##
|
|
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: "
|
|
52
|
-
meta: { entry: "
|
|
51
|
+
message: "starting private-runtime process entrypoint",
|
|
52
|
+
meta: { entry: "private-runtime", agentName },
|
|
53
53
|
});
|
|
54
|
-
// Dynamic import: agent-entry is
|
|
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 --
|
|
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 {
|
|
70
|
-
await
|
|
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: "
|
|
78
|
-
meta: { entry: "
|
|
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
|
|
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");
|
package/dist/heart/core.js
CHANGED
|
@@ -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.
|
|
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
|
|
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
|
|
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
|
-
// -
|
|
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
|
-
//
|
|
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
|
|
896
|
-
const
|
|
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
|
|
920
|
+
const privateRuntimeHabitCanSurface = isPrivateRuntimeChannel
|
|
900
921
|
&& (!habitSession || (habitSession.toolPolicy.outwardMessagingAllowed === true
|
|
901
922
|
&& habitSession.toolPolicy.grantedTools.includes("surface")));
|
|
902
|
-
const filteredBaseTools =
|
|
903
|
-
? baseTools.filter((t) =>
|
|
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
|
-
...(
|
|
909
|
-
...(
|
|
910
|
-
...(!
|
|
911
|
-
...(!
|
|
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:
|
|
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
|
-
//
|
|
1246
|
+
// Private-runtime attention queue gate: reject settle if items remain
|
|
1226
1247
|
const attentionQueue = (augmentedToolContext ?? options?.toolContext)?.delegatedOrigins;
|
|
1227
|
-
if (
|
|
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
|
-
//
|
|
1240
|
-
if (
|
|
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 (!
|
|
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("
|
|
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
|
-
|
|
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,8 @@ 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;
|
|
40
|
+
exports.isInnerDialogAutoStartEnabled = isInnerDialogAutoStartEnabled;
|
|
39
41
|
exports.listBundleSyncRows = listBundleSyncRows;
|
|
40
42
|
const fs = __importStar(require("fs"));
|
|
41
43
|
const path = __importStar(require("path"));
|
|
@@ -116,6 +118,54 @@ function listEnabledBundleAgents(options = {}) {
|
|
|
116
118
|
.filter((row) => row.enabled && !isLibraryKind(row.kind))
|
|
117
119
|
.map((row) => row.name);
|
|
118
120
|
}
|
|
121
|
+
function readPrivateRuntimeConfig(agent, options = {}) {
|
|
122
|
+
const bundlesRoot = options.bundlesRoot ?? (0, identity_1.getAgentBundlesRoot)();
|
|
123
|
+
const readFileSync = options.readFileSync ?? fs.readFileSync;
|
|
124
|
+
const configPath = path.join(bundlesRoot, `${agent}.ouro`, "agent.json");
|
|
125
|
+
try {
|
|
126
|
+
const raw = readFileSync(configPath, "utf-8");
|
|
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
|
+
}
|
|
135
|
+
const innerDialog = parsed.innerDialog;
|
|
136
|
+
if (innerDialog && typeof innerDialog === "object" && !Array.isArray(innerDialog)) {
|
|
137
|
+
return {
|
|
138
|
+
autoStart: innerDialog.autoStart === true,
|
|
139
|
+
source: "legacy-innerDialog",
|
|
140
|
+
compatibilityNote: "innerDialog.autoStart is a legacy migration input; use privateRuntime.autoStart.",
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
return { autoStart: false, source: "default" };
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
147
|
+
(0, runtime_1.emitNervesEvent)({
|
|
148
|
+
level: "warn",
|
|
149
|
+
component: "daemon",
|
|
150
|
+
event: "daemon.private_runtime_policy_read_failed",
|
|
151
|
+
message: "failed to read private-runtime auto-start policy; refusing autonomous private-runtime start",
|
|
152
|
+
meta: {
|
|
153
|
+
agent,
|
|
154
|
+
configPath,
|
|
155
|
+
error: message,
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
return { autoStart: false, source: "unreadable", error: message };
|
|
159
|
+
}
|
|
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
|
+
}
|
|
119
169
|
/**
|
|
120
170
|
* Read the per-agent sync block from each enabled bundle's agent.json.
|
|
121
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
|
|
79
|
-
function
|
|
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 =
|
|
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 =
|
|
357
|
+
const innerStatus = readPrivateRuntimeStatus(params.agent);
|
|
358
358
|
const parts = [];
|
|
359
359
|
if (innerStatus) {
|
|
360
|
-
parts.push(`
|
|
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 =
|
|
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
|
+
}
|