@ouro.bot/cli 0.1.0-alpha.894 → 0.1.0-alpha.897
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 +18 -0
- package/deploy/unraid/sanctuary.ouro/bundle-meta.json +1 -1
- package/deploy/unraid/sanctuary.xml +1 -1
- package/dist/heart/daemon/runtime-logging.js +1 -1
- package/dist/heart/daemon/sense-manager.js +20 -20
- package/dist/heart/identity.js +4 -4
- package/dist/heart/private-runtime/policy.js +4 -4
- package/dist/heart/sense-truth.js +2 -2
- package/dist/heart/{cmux-config.js → shepherd-config.js} +24 -24
- package/dist/heart/steward-policy.js +0 -26
- package/dist/heart/turn-context.js +5 -5
- package/dist/mind/prompt.js +5 -5
- package/dist/repertoire/bitwarden-store.js +153 -7
- package/dist/repertoire/credential-access.js +27 -23
- package/dist/repertoire/guardrails.js +4 -4
- package/dist/repertoire/tools-shepherd.js +181 -0
- package/dist/repertoire/tools.js +4 -4
- package/dist/repertoire/vault-unlock.js +59 -1
- package/dist/senses/{cmux → shepherd}/client.js +3 -3
- package/dist/senses/shepherd/cmux.js +332 -0
- package/dist/senses/shepherd/host.js +2 -0
- package/dist/senses/shepherd/judge.js +98 -0
- package/dist/senses/{cmux → shepherd}/redact.js +3 -3
- package/dist/senses/shepherd/returns.js +79 -0
- package/dist/senses/shepherd/sense.js +288 -0
- package/dist/senses/{cmux-entry.js → shepherd-entry.js} +10 -9
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/dist/repertoire/tools-cmux.js +0 -396
- package/dist/senses/cmux/answer.js +0 -191
- package/dist/senses/cmux/attention.js +0 -265
- package/dist/senses/cmux/casebook.js +0 -416
- package/dist/senses/cmux/floor.js +0 -407
- package/dist/senses/cmux/principles.js +0 -72
- package/dist/senses/cmux/sense.js +0 -287
package/changelog.json
CHANGED
|
@@ -1,6 +1,24 @@
|
|
|
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.897",
|
|
6
|
+
"changes": [
|
|
7
|
+
"The cmux sense is now Ouro Shepherd (sense 'shepherd', tools shepherd_overview, shepherd_read and shepherd_signal; agent.json key senses.shepherd). Shepherd is agent-agnostic: every time a coding agent in a terminal hands control back, a cheap judge on the agent's own agent lane decides whether it should have, using the same rubric for Claude Code, Codex, Copilot CLI, Agency or any other agent.",
|
|
8
|
+
"A return is the terminal host's own stop signal: cmux's agent.hook.Stop after a 3-second grace period (dropped when the agent resumed), keyed by cmux's event id, with the agent's last message from cmux's session store (keyed by the agent's own session id; hook events name it '<agent>-<id>'). Terminals no cmux hook speaks for are polled; output that changed and then held still for 8 seconds is one return.",
|
|
9
|
+
"A premature return is answered visibly: one line typed as '[Ouro for <human>] <reply>' (pasted, then Enter, never starting with / ! or #, with no @, backslash or line break, at most 240 characters) or one menu key, and the status line says so. Shepherd first re-reads the screen and skips a session the human focused in the last minute. A loop guard stops after three answers in a row without a human focus, or ten in an hour, and escalates.",
|
|
10
|
+
"A stop while the agent's own background work runs (subagents, background shells, watchers) is not a return of control. The judge's new 'working' kind, and cmux's hadPendingBackgroundWorkAtStop (which also earns the stop a 30-second longer wait), mean Shepherd never answers it and never escalates it as a gate: it logs the last message as a progress update and sets the status line to '<agent> waiting on its background work: ...'. Only an update the judge flags as news reaches the Ouro agent, queued with no wake. The session is judged afresh at its next real stop.",
|
|
11
|
+
"Gates, finished work and loop-guard stops are escalated to the Ouro agent through a pending message and a private wake (trigger 'shepherd'); unclear returns and errors are only logged. Every judgment is one line in state/senses/shepherd/returns.jsonl with latency and input tokens.",
|
|
12
|
+
"Removed: the cmux Feed polling, the permission-prompt floor, casebook, standing-grant check (inspectStandingActionGrant), cmux_reply_once and cmux_correct, and the Claude-specific decision constants. The cmux machine config record (cmux.socketCapability and friends) is unchanged.",
|
|
13
|
+
"Review fixes: Shepherd never types into the terminal the human is in, read from cmux's system.tree just before sending (not only from focus events); the loop guard escalates once per trip and stops calling the judge until the human focuses the terminal; with background work reported every verdict is 'working' (a gate becomes news); a repeated background stop with the same last message is not judged again; a quiet terminal judged working or not an agent is checked less often (doubling up to 5 minutes); Shepherd's status is one cmux entry per terminal, cleared when the agent resumes; replies may not contain shell syntax (; $ ` | < > &) or invisible formatting characters."
|
|
14
|
+
]
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"version": "0.1.0-alpha.895",
|
|
18
|
+
"changes": [
|
|
19
|
+
"The vault unlocks once per machine. Each agent's folder under ~/.ouro-cli/bitwarden/ now keeps one disposable bw profile per installed bw version (bw-<version>/), so machines with several bw installs never migrate or share one profile. When a local step fails on an authenticated profile (a null KdfConfig or missing master-password unlock data after a newer bw half-migrated it, or a missing session), Ouro moves the profile aside as data.json.discarded and logs in fresh once with the saved unlock secret, without asking the human. Server steps (login, sync), network errors, rate limits (429), gateway errors (502/504), connection resets, DNS and unreachable-network failures, lock contention and rejected secrets never discard a profile, and those network failures now count as transient. Ouro no longer deletes the saved Keychain/DPAPI/Secret Service/plaintext unlock entry when the vault rejects it: it keeps the entry and writes a private rejection record (mode 0600) in the agent's bw folder that every process honors, so no process retries a rejected secret against the server; `ouro vault unlock` (even with the same secret) or any successful login clears it. A store's terminal login failure is retried after 15 minutes instead of being held until restart. The locked-vault message also names a macOS password reset, which replaces the login keychain, as a cause. The Unraid template and the Sanctuary bundle runtime move to this version."
|
|
20
|
+
]
|
|
21
|
+
},
|
|
4
22
|
{
|
|
5
23
|
"version": "0.1.0-alpha.894",
|
|
6
24
|
"changes": [
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<?xml version="1.0"?>
|
|
2
2
|
<Container version="2">
|
|
3
3
|
<Name>ouro-butler</Name>
|
|
4
|
-
<Repository>ghcr.io/ourostack/ouroboros-butler:0.1.0-alpha.
|
|
4
|
+
<Repository>ghcr.io/ourostack/ouroboros-butler:0.1.0-alpha.897</Repository>
|
|
5
5
|
<Registry>https://github.com/ourostack/ouroboros/pkgs/container/ouroboros-butler</Registry>
|
|
6
6
|
<Network>host</Network>
|
|
7
7
|
<Shell>sh</Shell>
|
|
@@ -58,7 +58,7 @@ function defaultLoggingForProcess(processName) {
|
|
|
58
58
|
sinks: ["terminal", "ndjson"],
|
|
59
59
|
};
|
|
60
60
|
}
|
|
61
|
-
if (processName === "bluebubbles" || processName === "mail" || processName === "voice" || processName === "a2a" || processName === "
|
|
61
|
+
if (processName === "bluebubbles" || processName === "mail" || processName === "voice" || processName === "a2a" || processName === "shepherd") {
|
|
62
62
|
return {
|
|
63
63
|
level: "warn",
|
|
64
64
|
sinks: ["terminal", "ndjson"],
|
|
@@ -43,7 +43,7 @@ const runtime_credentials_1 = require("../runtime-credentials");
|
|
|
43
43
|
const provider_credentials_1 = require("../provider-credentials");
|
|
44
44
|
const sense_truth_1 = require("../sense-truth");
|
|
45
45
|
const machine_identity_1 = require("../machine-identity");
|
|
46
|
-
const
|
|
46
|
+
const shepherd_config_1 = require("../shepherd-config");
|
|
47
47
|
const process_manager_1 = require("./process-manager");
|
|
48
48
|
const config_1 = require("../../a2a/config");
|
|
49
49
|
const DEFAULT_TEAMS_PORT = 3978;
|
|
@@ -63,7 +63,7 @@ function defaultSenses() {
|
|
|
63
63
|
a2a: { ...identity_1.DEFAULT_AGENT_SENSES.a2a },
|
|
64
64
|
telegram: { ...identity_1.DEFAULT_AGENT_SENSES.telegram },
|
|
65
65
|
workbench: { ...identity_1.DEFAULT_AGENT_SENSES.workbench },
|
|
66
|
-
|
|
66
|
+
shepherd: { ...identity_1.DEFAULT_AGENT_SENSES.shepherd },
|
|
67
67
|
};
|
|
68
68
|
}
|
|
69
69
|
function readAgentSenses(agentJsonPath) {
|
|
@@ -103,7 +103,7 @@ function readAgentSenses(agentJsonPath) {
|
|
|
103
103
|
&& !Array.isArray(rawMcpServers)
|
|
104
104
|
&& Object.prototype.hasOwnProperty.call(rawMcpServers, "ouro_workbench");
|
|
105
105
|
const workbenchHasStaleBundleEntry = workbenchSensePresent || workbenchMcpPresent;
|
|
106
|
-
for (const sense of ["cli", "teams", "bluebubbles", "mail", "voice", "a2a", "telegram", "workbench", "
|
|
106
|
+
for (const sense of ["cli", "teams", "bluebubbles", "mail", "voice", "a2a", "telegram", "workbench", "shepherd"]) {
|
|
107
107
|
const rawSense = rawSenses[sense];
|
|
108
108
|
if (!rawSense || typeof rawSense !== "object" || Array.isArray(rawSense)) {
|
|
109
109
|
continue;
|
|
@@ -164,7 +164,7 @@ function runtimeConfigUnavailableDetail(agent, runtimeConfig) {
|
|
|
164
164
|
return `vault ${itemName} unavailable (${compactRuntimeConfigError(agent, runtimeConfig.error)})`;
|
|
165
165
|
}
|
|
166
166
|
function needsMachineRuntimeConfig(agent, sense) {
|
|
167
|
-
return sense === "bluebubbles" || sense === "voice" || sense === "a2a" || sense === "
|
|
167
|
+
return sense === "bluebubbles" || sense === "voice" || sense === "a2a" || sense === "shepherd" || (agent === "sanctuary" && sense === "telegram");
|
|
168
168
|
}
|
|
169
169
|
function senseFactsFromRuntimeConfig(agent, senses, runtimeConfig, machineRuntimeConfig = (0, runtime_credentials_1.readMachineRuntimeCredentialConfig)(agent), workbenchHasStaleBundleEntry = false) {
|
|
170
170
|
const base = {
|
|
@@ -176,7 +176,7 @@ function senseFactsFromRuntimeConfig(agent, senses, runtimeConfig, machineRuntim
|
|
|
176
176
|
a2a: { configured: false, detail: "not enabled in agent.json" },
|
|
177
177
|
telegram: { configured: false, detail: "not enabled in agent.json" },
|
|
178
178
|
workbench: { configured: false, detail: "not enabled in agent.json" },
|
|
179
|
-
|
|
179
|
+
shepherd: { configured: false, detail: "not enabled in agent.json" },
|
|
180
180
|
};
|
|
181
181
|
const payload = runtimeConfig.ok ? runtimeConfig.config : {};
|
|
182
182
|
const unavailableDetail = runtimeConfigUnavailableDetail(agent, runtimeConfig);
|
|
@@ -345,10 +345,10 @@ function senseFactsFromRuntimeConfig(agent, senses, runtimeConfig, machineRuntim
|
|
|
345
345
|
detail: publicUrl ? `${publicUrl}${endpointPath}` : `:${port} ${endpointPath}`,
|
|
346
346
|
};
|
|
347
347
|
}
|
|
348
|
-
if (senses.
|
|
349
|
-
base.
|
|
348
|
+
if (senses.shepherd.enabled) {
|
|
349
|
+
base.shepherd = !machineRuntimeConfig.ok && machineRuntimeConfig.reason !== "missing"
|
|
350
350
|
? { configured: false, detail: runtimeConfigUnavailableDetail(agent, machineRuntimeConfig) }
|
|
351
|
-
: (0,
|
|
351
|
+
: (0, shepherd_config_1.shepherdConfigFacts)(machinePayload);
|
|
352
352
|
}
|
|
353
353
|
if (workbenchHasStaleBundleEntry || senses.workbench.enabled) {
|
|
354
354
|
base.workbench = {
|
|
@@ -377,8 +377,8 @@ function senseRepairHint(agent, sense) {
|
|
|
377
377
|
? "Agent-runnable: complete or recover the root authority migration; do not restore a resident Telegram token."
|
|
378
378
|
: `Agent-runnable: store Telegram bot/user/chat coordinates with 'ouro connect telegram --agent ${agent}', then restart with 'ouro up'.`;
|
|
379
379
|
}
|
|
380
|
-
if (sense === "
|
|
381
|
-
return (0,
|
|
380
|
+
if (sense === "shepherd")
|
|
381
|
+
return (0, shepherd_config_1.shepherdRepairHint)(agent);
|
|
382
382
|
/* v8 ignore next -- Workbench is deliberately not daemon-managed, so getSenseInventory never asks the daemon manager for a repair hint @preserve */
|
|
383
383
|
if (sense === "workbench") {
|
|
384
384
|
return `Agent-runnable: install Ouro Workbench.app, then launch the boss through Workbench or run 'ouro acp-serve --agent ${agent} --workbench-mcp'; 'ouro connect workbench --agent ${agent}' only cleans stale bundle entries.`;
|
|
@@ -393,7 +393,7 @@ function parseSenseSnapshotName(name) {
|
|
|
393
393
|
if (parts.length !== 2)
|
|
394
394
|
return null;
|
|
395
395
|
const [agent, sense] = parts;
|
|
396
|
-
if (sense !== "teams" && sense !== "bluebubbles" && sense !== "mail" && sense !== "voice" && sense !== "a2a" && sense !== "telegram" && sense !== "
|
|
396
|
+
if (sense !== "teams" && sense !== "bluebubbles" && sense !== "mail" && sense !== "voice" && sense !== "a2a" && sense !== "telegram" && sense !== "shepherd")
|
|
397
397
|
return null;
|
|
398
398
|
return { agent, sense };
|
|
399
399
|
}
|
|
@@ -413,13 +413,13 @@ function managedSenseEntry(sense) {
|
|
|
413
413
|
return "senses/a2a-entry.js";
|
|
414
414
|
if (sense === "telegram")
|
|
415
415
|
return "senses/telegram-entry.js";
|
|
416
|
-
if (sense === "
|
|
417
|
-
return "senses/
|
|
416
|
+
if (sense === "shepherd")
|
|
417
|
+
return "senses/shepherd-entry.js";
|
|
418
418
|
return "senses/mail-entry.js";
|
|
419
419
|
}
|
|
420
420
|
function runtimeCredentialBootstrapFor(agent, sense) {
|
|
421
421
|
const runtime = (0, runtime_credentials_1.readRuntimeCredentialConfig)(agent);
|
|
422
|
-
const usesMachineConfig = sense === "bluebubbles" || sense === "voice" || sense === "a2a" || sense === "telegram" || sense === "
|
|
422
|
+
const usesMachineConfig = sense === "bluebubbles" || sense === "voice" || sense === "a2a" || sense === "telegram" || sense === "shepherd";
|
|
423
423
|
const machineId = usesMachineConfig ? currentMachineId() : undefined;
|
|
424
424
|
const machine = usesMachineConfig ? (0, runtime_credentials_1.readMachineRuntimeCredentialConfig)(agent) : null;
|
|
425
425
|
const providerPool = (0, provider_credentials_1.readProviderCredentialPool)(agent);
|
|
@@ -590,7 +590,7 @@ class DaemonSenseManager {
|
|
|
590
590
|
return [agent, { senses, workbenchHasStaleBundleEntry, facts }];
|
|
591
591
|
}));
|
|
592
592
|
const managedSenseAgents = [...this.contexts.entries()].flatMap(([agent, context]) => {
|
|
593
|
-
return ["teams", "bluebubbles", "mail", "voice", "a2a", "telegram", "
|
|
593
|
+
return ["teams", "bluebubbles", "mail", "voice", "a2a", "telegram", "shepherd"]
|
|
594
594
|
.filter((sense) => context.senses[sense].enabled)
|
|
595
595
|
.map((sense) => ({
|
|
596
596
|
name: `${agent}:${sense}`,
|
|
@@ -742,7 +742,7 @@ class DaemonSenseManager {
|
|
|
742
742
|
}
|
|
743
743
|
async refreshEnabledSenseConfigs() {
|
|
744
744
|
const refreshes = [...this.contexts.entries()].map(async ([agent, context]) => {
|
|
745
|
-
const enabledManagedSenses = ["teams", "bluebubbles", "mail", "voice", "a2a", "telegram", "
|
|
745
|
+
const enabledManagedSenses = ["teams", "bluebubbles", "mail", "voice", "a2a", "telegram", "shepherd"]
|
|
746
746
|
.filter((sense) => context.senses[sense].enabled);
|
|
747
747
|
/* v8 ignore next -- periodic refresh work only exists when a managed background sense is enabled @preserve */
|
|
748
748
|
if (enabledManagedSenses.length === 0)
|
|
@@ -916,10 +916,10 @@ class DaemonSenseManager {
|
|
|
916
916
|
workbench: {
|
|
917
917
|
configured: context.facts.workbench.configured,
|
|
918
918
|
},
|
|
919
|
-
|
|
920
|
-
configured: context.facts.
|
|
921
|
-
optional: context.facts.
|
|
922
|
-
...(runtime.get(agent)?.
|
|
919
|
+
shepherd: {
|
|
920
|
+
configured: context.facts.shepherd.configured,
|
|
921
|
+
optional: context.facts.shepherd.optional,
|
|
922
|
+
...(runtime.get(agent)?.shepherd ?? {}),
|
|
923
923
|
},
|
|
924
924
|
};
|
|
925
925
|
const inventory = (0, sense_truth_1.getSenseInventory)({ senses: context.senses }, runtimeInfo);
|
package/dist/heart/identity.js
CHANGED
|
@@ -145,7 +145,7 @@ exports.DEFAULT_AGENT_SENSES = {
|
|
|
145
145
|
a2a: { enabled: false },
|
|
146
146
|
telegram: { enabled: false },
|
|
147
147
|
workbench: { enabled: false },
|
|
148
|
-
|
|
148
|
+
shepherd: { enabled: false },
|
|
149
149
|
};
|
|
150
150
|
function normalizeSenses(value, configFile) {
|
|
151
151
|
const defaults = {
|
|
@@ -157,7 +157,7 @@ function normalizeSenses(value, configFile) {
|
|
|
157
157
|
a2a: { ...exports.DEFAULT_AGENT_SENSES.a2a },
|
|
158
158
|
telegram: { ...exports.DEFAULT_AGENT_SENSES.telegram },
|
|
159
159
|
workbench: { ...exports.DEFAULT_AGENT_SENSES.workbench },
|
|
160
|
-
|
|
160
|
+
shepherd: { ...exports.DEFAULT_AGENT_SENSES.shepherd },
|
|
161
161
|
};
|
|
162
162
|
if (value === undefined) {
|
|
163
163
|
return defaults;
|
|
@@ -173,7 +173,7 @@ function normalizeSenses(value, configFile) {
|
|
|
173
173
|
throw new Error(`agent.json at ${configFile} must include senses as an object when present.`);
|
|
174
174
|
}
|
|
175
175
|
const raw = value;
|
|
176
|
-
const senseNames = ["cli", "teams", "bluebubbles", "mail", "voice", "a2a", "telegram", "workbench", "
|
|
176
|
+
const senseNames = ["cli", "teams", "bluebubbles", "mail", "voice", "a2a", "telegram", "workbench", "shepherd"];
|
|
177
177
|
for (const senseName of senseNames) {
|
|
178
178
|
const rawSense = raw[senseName];
|
|
179
179
|
if (rawSense === undefined) {
|
|
@@ -221,7 +221,7 @@ function buildDefaultAgentTemplate(_agentName) {
|
|
|
221
221
|
a2a: { ...exports.DEFAULT_AGENT_SENSES.a2a },
|
|
222
222
|
telegram: { ...exports.DEFAULT_AGENT_SENSES.telegram },
|
|
223
223
|
workbench: { ...exports.DEFAULT_AGENT_SENSES.workbench },
|
|
224
|
-
|
|
224
|
+
shepherd: { ...exports.DEFAULT_AGENT_SENSES.shepherd },
|
|
225
225
|
},
|
|
226
226
|
phrases: {
|
|
227
227
|
thinking: [...exports.DEFAULT_AGENT_PHRASES.thinking],
|
|
@@ -150,12 +150,12 @@ async function evaluatePolicy(request, context, deps) {
|
|
|
150
150
|
reason: "verified daemon external event",
|
|
151
151
|
};
|
|
152
152
|
}
|
|
153
|
-
if (request.triggerSource === "
|
|
154
|
-
&& hasNonEmptyOriginRef(originRefs, "
|
|
155
|
-
&& hasOriginRef(originRefs, "sense", "
|
|
153
|
+
if (request.triggerSource === "shepherd"
|
|
154
|
+
&& hasNonEmptyOriginRef(originRefs, "shepherd-return")
|
|
155
|
+
&& hasOriginRef(originRefs, "sense", "shepherd")) {
|
|
156
156
|
return {
|
|
157
157
|
result: "allow",
|
|
158
|
-
reason: "
|
|
158
|
+
reason: "Shepherd escalation",
|
|
159
159
|
};
|
|
160
160
|
}
|
|
161
161
|
if (request.triggerSource === "operator-cli"
|
|
@@ -12,7 +12,7 @@ const SENSES = [
|
|
|
12
12
|
{ sense: "a2a", label: "A2A", daemonManaged: true },
|
|
13
13
|
{ sense: "telegram", label: "Telegram", daemonManaged: true },
|
|
14
14
|
{ sense: "workbench", label: "Workbench", daemonManaged: false },
|
|
15
|
-
{ sense: "
|
|
15
|
+
{ sense: "shepherd", label: "shepherd", daemonManaged: true },
|
|
16
16
|
];
|
|
17
17
|
function configuredSenses(senses) {
|
|
18
18
|
const configured = senses ?? {};
|
|
@@ -26,7 +26,7 @@ function configuredSenses(senses) {
|
|
|
26
26
|
a2a: configured.a2a ?? { ...identity_1.DEFAULT_AGENT_SENSES.a2a },
|
|
27
27
|
telegram: configured.telegram ?? { ...identity_1.DEFAULT_AGENT_SENSES.telegram },
|
|
28
28
|
workbench: configured.workbench ?? { ...identity_1.DEFAULT_AGENT_SENSES.workbench },
|
|
29
|
-
|
|
29
|
+
shepherd: configured.shepherd ?? { ...identity_1.DEFAULT_AGENT_SENSES.shepherd },
|
|
30
30
|
};
|
|
31
31
|
}
|
|
32
32
|
function resolveStatus(enabled, daemonManaged, runtimeInfo) {
|
|
@@ -33,27 +33,27 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.
|
|
37
|
-
exports.
|
|
38
|
-
exports.
|
|
36
|
+
exports.shepherdRepairHint = shepherdRepairHint;
|
|
37
|
+
exports.shepherdConfigFacts = shepherdConfigFacts;
|
|
38
|
+
exports.shepherdSenseStatus = shepherdSenseStatus;
|
|
39
39
|
exports.defaultCmuxSocketPath = defaultCmuxSocketPath;
|
|
40
|
-
exports.
|
|
40
|
+
exports.resolveShepherdConnection = resolveShepherdConnection;
|
|
41
41
|
const fs = __importStar(require("node:fs"));
|
|
42
42
|
const os = __importStar(require("node:os"));
|
|
43
43
|
const path = __importStar(require("node:path"));
|
|
44
44
|
const runtime_1 = require("../nerves/runtime");
|
|
45
|
-
function
|
|
45
|
+
function shepherdRepairHint(agent) {
|
|
46
46
|
return `human-required: in a cmux terminal run 'ouro vault config set --agent ${agent} --scope machine --key cmux.socketCapability --value "$CMUX_SOCKET_CAPABILITY"' (or set cmux.socketPassword when cmux is in password mode), then 'ouro up'.`;
|
|
47
47
|
}
|
|
48
48
|
function text(record, key) {
|
|
49
49
|
const value = record?.[key];
|
|
50
50
|
return typeof value === "string" ? value.trim() : "";
|
|
51
51
|
}
|
|
52
|
-
function
|
|
53
|
-
const value = machinePayload
|
|
52
|
+
function hostRecord(machinePayload, key) {
|
|
53
|
+
const value = machinePayload[key];
|
|
54
54
|
return value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
|
|
55
55
|
}
|
|
56
|
-
function
|
|
56
|
+
function cmuxAuth(cmux) {
|
|
57
57
|
const token = text(cmux, "socketCapability");
|
|
58
58
|
if (token)
|
|
59
59
|
return { kind: "capability", token };
|
|
@@ -64,18 +64,18 @@ function authFrom(cmux) {
|
|
|
64
64
|
return { kind: "none" };
|
|
65
65
|
return null;
|
|
66
66
|
}
|
|
67
|
-
function
|
|
68
|
-
const cmux =
|
|
69
|
-
const auth =
|
|
67
|
+
function shepherdConfigFacts(machinePayload) {
|
|
68
|
+
const cmux = hostRecord(machinePayload, "cmux");
|
|
69
|
+
const auth = cmuxAuth(cmux);
|
|
70
70
|
if (!auth)
|
|
71
|
-
return cmux ? { configured: false, detail: "missing cmux.socketCapability" } : { configured: false, optional: true, detail: "
|
|
72
|
-
return { configured: true, detail: `socket auth: ${auth.kind === "none" ? "automation mode" : auth.kind}` };
|
|
71
|
+
return cmux ? { configured: false, detail: "missing cmux.socketCapability" } : { configured: false, optional: true, detail: "no terminal host attached on this machine" };
|
|
72
|
+
return { configured: true, detail: `cmux socket auth: ${auth.kind === "none" ? "automation mode" : auth.kind}` };
|
|
73
73
|
}
|
|
74
|
-
/** The one-word status the prompt and turn context show for
|
|
75
|
-
function
|
|
74
|
+
/** The one-word status the prompt and turn context show for Shepherd, matching the daemon's sense rows. */
|
|
75
|
+
function shepherdSenseStatus(enabled, machinePayload) {
|
|
76
76
|
if (!enabled)
|
|
77
77
|
return "disabled";
|
|
78
|
-
const facts =
|
|
78
|
+
const facts = shepherdConfigFacts(machinePayload);
|
|
79
79
|
if (facts.configured)
|
|
80
80
|
return "ready";
|
|
81
81
|
return facts.optional ? "not_attached" : "needs_config";
|
|
@@ -87,16 +87,16 @@ function defaultCmuxSocketPath(homeDir = os.homedir(), uid = process.getuid?.()
|
|
|
87
87
|
const unscoped = path.join(dir, "cmux.sock");
|
|
88
88
|
return !exists(scoped) && exists(unscoped) ? unscoped : scoped;
|
|
89
89
|
}
|
|
90
|
-
function
|
|
91
|
-
const cmux =
|
|
92
|
-
const auth =
|
|
90
|
+
function resolveShepherdConnection(agent, machinePayload, defaultSocketPath = defaultCmuxSocketPath) {
|
|
91
|
+
const cmux = hostRecord(machinePayload, "cmux");
|
|
92
|
+
const auth = cmuxAuth(cmux);
|
|
93
93
|
(0, runtime_1.emitNervesEvent)({
|
|
94
94
|
component: "senses",
|
|
95
|
-
event: "senses.
|
|
96
|
-
message: "resolved
|
|
97
|
-
meta: { agent, auth: auth?.kind ?? "missing" },
|
|
95
|
+
event: "senses.shepherd_connection_resolved",
|
|
96
|
+
message: "resolved Shepherd terminal host connection settings",
|
|
97
|
+
meta: { agent, host: auth ? "cmux" : "none", auth: auth?.kind ?? "missing" },
|
|
98
98
|
});
|
|
99
99
|
if (!auth)
|
|
100
|
-
return { ok: false, error: `
|
|
101
|
-
return { ok: true, connection: { socketPath: text(cmux, "socketPath") || defaultSocketPath(), auth } };
|
|
100
|
+
return { ok: false, error: `no terminal host is attached on this machine; ${shepherdRepairHint(agent)}` };
|
|
101
|
+
return { ok: true, connection: { host: "cmux", socketPath: text(cmux, "socketPath") || defaultSocketPath(), auth } };
|
|
102
102
|
}
|
|
@@ -38,7 +38,6 @@ exports.assertNotReplayIdentity = assertNotReplayIdentity;
|
|
|
38
38
|
exports.updateStewardPolicy = updateStewardPolicy;
|
|
39
39
|
exports.readRoutineActionReceipts = readRoutineActionReceipts;
|
|
40
40
|
exports.inspectRoutineActionGrant = inspectRoutineActionGrant;
|
|
41
|
-
exports.inspectStandingActionGrant = inspectStandingActionGrant;
|
|
42
41
|
exports.consumeRoutineActionGrant = consumeRoutineActionGrant;
|
|
43
42
|
exports.withStewardPolicyLease = withStewardPolicyLease;
|
|
44
43
|
exports.withRoutineActionAttempt = withRoutineActionAttempt;
|
|
@@ -525,31 +524,6 @@ function inspectRoutineActionGrant(agentRoot, input) {
|
|
|
525
524
|
return { allowed: false, reason: error instanceof Error ? error.message : "routine action policy is unavailable" };
|
|
526
525
|
}
|
|
527
526
|
}
|
|
528
|
-
function inspectStandingActionGrant(agentRoot, input) {
|
|
529
|
-
try {
|
|
530
|
-
const { policy, rows } = readPolicyState(agentRoot);
|
|
531
|
-
const now = input.now ?? new Date().toISOString();
|
|
532
|
-
const grant = policy.routineActionGrants[input.key];
|
|
533
|
-
if (!grant)
|
|
534
|
-
return { allowed: false, reason: "standing grant is missing" };
|
|
535
|
-
if (grant.provenance !== "stated")
|
|
536
|
-
return { allowed: false, reason: "standing grant must be owner-stated" };
|
|
537
|
-
if (!appliedEntry(rows, ["grant_routine_action"], input.key, grant))
|
|
538
|
-
return { allowed: false, reason: "standing grant has no applied owner authorization" };
|
|
539
|
-
if (grant.action !== input.action)
|
|
540
|
-
return { allowed: false, reason: "action does not match the standing grant" };
|
|
541
|
-
if (!grant.targets.includes(input.target) || grant.exclusions.includes(input.target))
|
|
542
|
-
return { allowed: false, reason: "target is not covered by the standing grant" };
|
|
543
|
-
if (grant.expiresAt && Date.parse(grant.expiresAt) <= Date.parse(now))
|
|
544
|
-
return { allowed: false, reason: "standing grant expired" };
|
|
545
|
-
if (input.usesInWindow(grant.windowMs) >= grant.maxCount)
|
|
546
|
-
return { allowed: false, reason: "standing grant count cap reached" };
|
|
547
|
-
return { allowed: true, grantVersion: grant.version, maxCount: grant.maxCount, windowMs: grant.windowMs };
|
|
548
|
-
}
|
|
549
|
-
catch (error) {
|
|
550
|
-
return { allowed: false, reason: error instanceof Error ? error.message : "steward policy is unavailable" };
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
527
|
function consumeRoutineActionGrant(agentRoot, input) {
|
|
554
528
|
if (!text(input.authorizationReceiptId))
|
|
555
529
|
throw new Error("routine action requires a current authorization receipt");
|
|
@@ -63,7 +63,7 @@ const provider_visibility_1 = require("./provider-visibility");
|
|
|
63
63
|
const mail_import_discovery_1 = require("./mail-import-discovery");
|
|
64
64
|
const flight_recorder_1 = require("../arc/flight-recorder");
|
|
65
65
|
const context_loss_sentinel_1 = require("./context-loss-sentinel");
|
|
66
|
-
const
|
|
66
|
+
const shepherd_config_1 = require("./shepherd-config");
|
|
67
67
|
// ── Helpers ─────────────────────────────────────────────────────────
|
|
68
68
|
const DAEMON_SOCKET_PATH = "/tmp/ouroboros-daemon.sock";
|
|
69
69
|
function isLiveCodingSessionStatus(status) {
|
|
@@ -168,7 +168,7 @@ function readSenseStatusLines() {
|
|
|
168
168
|
a2a: configuredSenses.a2a ?? { enabled: false },
|
|
169
169
|
telegram: configuredSenses.telegram ?? { enabled: false },
|
|
170
170
|
workbench: configuredSenses.workbench ?? { enabled: false },
|
|
171
|
-
|
|
171
|
+
shepherd: configuredSenses.shepherd ?? { enabled: false },
|
|
172
172
|
};
|
|
173
173
|
const payload = (0, config_1.loadConfig)();
|
|
174
174
|
const agentName = (0, identity_1.getAgentName)();
|
|
@@ -223,7 +223,7 @@ function readSenseStatusLines() {
|
|
|
223
223
|
&& hasTextField(runtimePayload, "telegramAuthorizedUserId")
|
|
224
224
|
&& hasTextField(runtimePayload, "telegramAuthorizedChatId"),
|
|
225
225
|
workbench: false,
|
|
226
|
-
|
|
226
|
+
shepherd: (0, shepherd_config_1.shepherdSenseStatus)(true, machinePayload) === "ready",
|
|
227
227
|
};
|
|
228
228
|
const rows = [
|
|
229
229
|
{ label: "CLI", status: "interactive" },
|
|
@@ -252,8 +252,8 @@ function readSenseStatusLines() {
|
|
|
252
252
|
status: !senses.telegram.enabled ? "disabled" : configured.telegram ? "ready" : "needs_config",
|
|
253
253
|
},
|
|
254
254
|
{
|
|
255
|
-
label: "
|
|
256
|
-
status: (0,
|
|
255
|
+
label: "shepherd",
|
|
256
|
+
status: (0, shepherd_config_1.shepherdSenseStatus)(senses.shepherd.enabled, machinePayload),
|
|
257
257
|
},
|
|
258
258
|
{
|
|
259
259
|
label: "Workbench",
|
package/dist/mind/prompt.js
CHANGED
|
@@ -86,7 +86,7 @@ const daemon_health_1 = require("../heart/daemon/daemon-health");
|
|
|
86
86
|
const scrutiny_1 = require("./scrutiny");
|
|
87
87
|
const pulse_1 = require("../heart/daemon/pulse");
|
|
88
88
|
const provider_visibility_1 = require("../heart/provider-visibility");
|
|
89
|
-
const
|
|
89
|
+
const shepherd_config_1 = require("../heart/shepherd-config");
|
|
90
90
|
const store_1 = require("../trips/store");
|
|
91
91
|
const orientation_frame_1 = require("../heart/orientation-frame");
|
|
92
92
|
const flight_recorder_1 = require("../arc/flight-recorder");
|
|
@@ -479,7 +479,7 @@ function localSenseStatusLines() {
|
|
|
479
479
|
a2a: configuredSenses.a2a ?? { enabled: false },
|
|
480
480
|
telegram: configuredSenses.telegram ?? { enabled: false },
|
|
481
481
|
workbench: configuredSenses.workbench ?? { enabled: false },
|
|
482
|
-
|
|
482
|
+
shepherd: configuredSenses.shepherd ?? { enabled: false },
|
|
483
483
|
};
|
|
484
484
|
const payload = (0, config_1.loadConfig)();
|
|
485
485
|
const runtimeConfig = (0, runtime_credentials_1.readRuntimeCredentialConfig)((0, identity_1.getAgentName)());
|
|
@@ -508,7 +508,7 @@ function localSenseStatusLines() {
|
|
|
508
508
|
&& hasTextField(runtimePayload, "telegramAuthorizedUserId")
|
|
509
509
|
&& hasTextField(runtimePayload, "telegramAuthorizedChatId"),
|
|
510
510
|
workbench: false,
|
|
511
|
-
|
|
511
|
+
shepherd: (0, shepherd_config_1.shepherdSenseStatus)(true, machinePayload) === "ready",
|
|
512
512
|
};
|
|
513
513
|
const rows = [
|
|
514
514
|
{ label: "CLI", status: "interactive" },
|
|
@@ -537,8 +537,8 @@ function localSenseStatusLines() {
|
|
|
537
537
|
status: !senses.telegram.enabled ? "disabled" : configured.telegram ? "ready" : "needs_config",
|
|
538
538
|
},
|
|
539
539
|
{
|
|
540
|
-
label: "
|
|
541
|
-
status: (0,
|
|
540
|
+
label: "shepherd",
|
|
541
|
+
status: (0, shepherd_config_1.shepherdSenseStatus)(senses.shepherd.enabled, machinePayload),
|
|
542
542
|
},
|
|
543
543
|
{
|
|
544
544
|
label: "Workbench",
|