@ouro.bot/cli 0.1.0-alpha.935 → 0.1.0-alpha.937
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 +21 -0
- package/deploy/unraid/sanctuary.ouro/bundle-meta.json +1 -1
- package/deploy/unraid/sanctuary.xml +1 -1
- package/dist/heart/daemon/cli-defaults.js +5 -0
- package/dist/heart/daemon/cli-exec.js +9 -0
- package/dist/heart/daemon/cli-help.js +2 -2
- package/dist/heart/daemon/cli-parse.js +2 -1
- package/dist/heart/daemon/daemon.js +14 -0
- package/dist/heart/daemon/ouro-entry.js +4 -1
- package/dist/heart/daemon/runtime-logging.js +1 -1
- package/dist/heart/daemon/sense-manager.js +53 -0
- package/dist/heart/mailbox/mailbox-http-hooks.js +1 -0
- package/dist/heart/mailbox/mailbox-http-routes.js +8 -0
- package/dist/heart/mailbox/mailbox-read.js +3 -1
- package/dist/heart/mailbox/mailbox-supervision.js +131 -0
- package/dist/heart/shepherd/custody-types.js +2 -0
- package/dist/heart/shepherd/mandates.js +352 -0
- package/dist/heart/shepherd/operation-commands.js +19 -0
- package/dist/heart/shepherd/operations.js +135 -0
- package/dist/heart/shepherd/supervision.js +114 -0
- package/dist/heart/shepherd/types.js +2 -0
- package/dist/mailbox-ui/assets/index-Dbska1tZ.js +1 -0
- package/dist/mailbox-ui/assets/index-DsiKaPyH.css +1 -0
- package/dist/mailbox-ui/index.html +2 -2
- package/dist/nerves/coverage/file-completeness.js +4 -0
- package/dist/senses/shepherd/checkin-store.js +209 -41
- package/dist/senses/shepherd/cmux.js +69 -23
- package/dist/senses/shepherd/guidance.js +5 -2
- package/dist/senses/shepherd/herdr.js +84 -2
- package/dist/senses/shepherd/judge-evaluation-command.js +180 -0
- package/dist/senses/shepherd/judge-evaluation-entry.js +13 -0
- package/dist/senses/shepherd/judge-evaluation.js +150 -0
- package/dist/senses/shepherd/judge.js +12 -5
- package/dist/senses/shepherd/observation-publisher.js +320 -0
- package/dist/senses/shepherd/operations-defaults.js +86 -0
- package/dist/senses/shepherd/operations.js +159 -0
- package/dist/senses/shepherd/recovery.js +543 -0
- package/dist/senses/shepherd/sense.js +14 -9
- package/dist/senses/shepherd/shepherd-judge-corpus-v1.json +167 -0
- package/npm-shrinkwrap.json +2 -2
- package/package.json +2 -1
- package/dist/mailbox-ui/assets/index-Cyv4mw3Y.js +0 -1
- package/dist/mailbox-ui/assets/index-Du_9G9WO.css +0 -1
package/changelog.json
CHANGED
|
@@ -1,6 +1,27 @@
|
|
|
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.937",
|
|
6
|
+
"changes": [
|
|
7
|
+
"Add an explicit tool-free Shepherd judge evaluation command with a frozen 160-case corpus, three repetitions, unique-case metrics and private progress reports.",
|
|
8
|
+
"Reject malformed judge answers from classification credit, require safe text for continuation recall, and report output failures without claiming successful persistence.",
|
|
9
|
+
"Refuse model-selected menu, navigation and control keys before custody on every automatic trigger; keep explicit owner-requested keys separate and use a text-only judge rubric.",
|
|
10
|
+
"Keep remaining legacy text automation visibly unqualified: stored mandates do not yet govern every trigger, and native guarded input and full Shepherd v1 remain open gates.",
|
|
11
|
+
"Preserve meaningful sanitized error context for evaluation argument, output and entry failures."
|
|
12
|
+
]
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"version": "0.1.0-alpha.936",
|
|
16
|
+
"changes": [
|
|
17
|
+
"Add live Ouro Mailbox Supervision with aged terminal observations, explicit source failures, historical judgments and held-input visibility.",
|
|
18
|
+
"Add existing-owner local commands for mandate revisions, session pauses, exact custody inspection, evidence-derived retirement and recoverable offline archives.",
|
|
19
|
+
"Preserve original uncertain outcomes and replay barriers through reconciliation, migration, archive interruption and retry.",
|
|
20
|
+
"Trace unavailable supervision observations and verified maintenance quiescence without leaking source contents, and preserve CLI startup telemetry when command parsing fails.",
|
|
21
|
+
"Keep Herdr observation watchers stopped during callback-driven shutdown and prevent obsolete inventory refreshes from creating duplicate polling timers.",
|
|
22
|
+
"Explicitly label retained legacy automation and receipt-less sends as unqualified; stored mandates do not yet govern every trigger, and Shepherd v1 remains unqualified."
|
|
23
|
+
]
|
|
24
|
+
},
|
|
4
25
|
{
|
|
5
26
|
"version": "0.1.0-alpha.935",
|
|
6
27
|
"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.937</Repository>
|
|
5
5
|
<Registry>https://github.com/ourostack/ouroboros/pkgs/container/ouroboros-butler</Registry>
|
|
6
6
|
<Network>host</Network>
|
|
7
7
|
<Shell>sh</Shell>
|
|
@@ -733,6 +733,11 @@ async function defaultRunSerpentGuide() {
|
|
|
733
733
|
function createDefaultOuroCliDeps(socketPath = socket_client_1.DEFAULT_DAEMON_SOCKET_PATH) {
|
|
734
734
|
return {
|
|
735
735
|
socketPath,
|
|
736
|
+
runShepherdOperation: async (command) => {
|
|
737
|
+
const { runShepherdOperation } = await Promise.resolve().then(() => __importStar(require("../../senses/shepherd/operations")));
|
|
738
|
+
const { createDefaultShepherdOperationDeps } = await Promise.resolve().then(() => __importStar(require("../../senses/shepherd/operations-defaults")));
|
|
739
|
+
return runShepherdOperation(command, createDefaultShepherdOperationDeps(async (agent) => (0, socket_client_1.sendDaemonCommand)(socketPath, { kind: "daemon.shepherd_quiesce", agent }, { timeoutMs: socket_client_1.DEFAULT_DAEMON_COMMAND_TIMEOUT_MS })));
|
|
740
|
+
},
|
|
736
741
|
sendCommand: (commandSocketPath, command) => (0, socket_client_1.sendDaemonCommand)(commandSocketPath, command, {
|
|
737
742
|
timeoutMs: command.kind === "daemon.status"
|
|
738
743
|
? socket_client_1.DEFAULT_DAEMON_STATUS_TIMEOUT_MS
|
|
@@ -7897,6 +7897,15 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
|
|
|
7897
7897
|
await runMcpServeCliLifecycle(server, mcpInput);
|
|
7898
7898
|
return "";
|
|
7899
7899
|
}
|
|
7900
|
+
if (command.kind === "shepherd.status" || command.kind === "shepherd.inspect" || command.kind === "shepherd.pause"
|
|
7901
|
+
|| command.kind === "shepherd.resume" || command.kind === "shepherd.mandate.grant" || command.kind === "shepherd.mandate.revoke"
|
|
7902
|
+
|| command.kind === "shepherd.reconcile" || command.kind === "shepherd.maintenance") {
|
|
7903
|
+
if (!deps.runShepherdOperation)
|
|
7904
|
+
throw new Error("Shepherd local administration service is unavailable");
|
|
7905
|
+
const text = JSON.stringify(await deps.runShepherdOperation(command), null, 2);
|
|
7906
|
+
deps.writeStdout(text);
|
|
7907
|
+
return text;
|
|
7908
|
+
}
|
|
7900
7909
|
if (command.kind === "shepherd.chat") {
|
|
7901
7910
|
const daemonResult = deps.ensureDaemonForFrontend
|
|
7902
7911
|
? await deps.ensureDaemonForFrontend(command.agent)
|
|
@@ -120,10 +120,10 @@ exports.COMMAND_REGISTRY = {
|
|
|
120
120
|
},
|
|
121
121
|
shepherd: {
|
|
122
122
|
category: "Agents",
|
|
123
|
-
description: "Open an independent Ouro composer
|
|
123
|
+
description: "Open an independent Ouro composer or administer Shepherd as the existing local owner. Administration is same-UID authority, not proof of human presence. status/inspect are read-only; mutations require --expected-revision and --request-id. pause/resume require --host and --session; resume never releases held custody. mandate grant requires --project, --task, --expires-at; revoke requires --id. reconcile --id --resolution retired derives host evidence; delivered is unsupported until native receipt lookup is qualified. maintenance --destination requires fresh disabled-and-stopped daemon proof and retired terminals. --web opens the compact graphical view in cmux. Not an input hook in the coding agent.",
|
|
124
124
|
usage: "ouro shepherd chat --agent <name> [--session <UUID>] [--web]",
|
|
125
125
|
example: "ouro shepherd chat --agent ouroboros",
|
|
126
|
-
subcommands: ["chat"],
|
|
126
|
+
subcommands: ["chat", "status", "inspect", "pause", "resume", "mandate", "reconcile", "maintenance"],
|
|
127
127
|
},
|
|
128
128
|
config: {
|
|
129
129
|
category: "Agents",
|
|
@@ -53,6 +53,7 @@ const path = __importStar(require("path"));
|
|
|
53
53
|
const friends_1 = require("@ouro.bot/friends");
|
|
54
54
|
const cli_help_1 = require("./cli-help");
|
|
55
55
|
const surface_1 = require("../../senses/shepherd/surface");
|
|
56
|
+
const operations_1 = require("../shepherd/operations");
|
|
56
57
|
const local_audio_control_1 = require("../../senses/voice/local-audio-control");
|
|
57
58
|
const cli_desk_1 = require("./cli-desk");
|
|
58
59
|
const flight_recorder_1 = require("../../arc/flight-recorder");
|
|
@@ -3124,7 +3125,7 @@ function parseOuroCommand(args) {
|
|
|
3124
3125
|
if (head === "shepherd") {
|
|
3125
3126
|
const usage = "Usage: ouro shepherd chat --agent <name> [--session <UUID>] [--web]";
|
|
3126
3127
|
if (args[1] !== "chat")
|
|
3127
|
-
|
|
3128
|
+
return (0, operations_1.parseShepherdOperation)(args.slice(1));
|
|
3128
3129
|
const values = new Map();
|
|
3129
3130
|
for (let i = 2; i < args.length; i += 1) {
|
|
3130
3131
|
const flag = args[i];
|
|
@@ -1931,6 +1931,20 @@ class OuroDaemon {
|
|
|
1931
1931
|
data,
|
|
1932
1932
|
};
|
|
1933
1933
|
}
|
|
1934
|
+
case "daemon.shepherd_stopped": {
|
|
1935
|
+
if (typeof command.agent !== "string" || !this.senseManager?.shepherdStoppedProof) {
|
|
1936
|
+
return { ok: false, error: "Shepherd lifecycle proof is unavailable" };
|
|
1937
|
+
}
|
|
1938
|
+
const data = this.senseManager.shepherdStoppedProof(command.agent);
|
|
1939
|
+
return { ok: true, data };
|
|
1940
|
+
}
|
|
1941
|
+
case "daemon.shepherd_quiesce": {
|
|
1942
|
+
if (typeof command.agent !== "string" || !this.senseManager?.quiesceShepherd) {
|
|
1943
|
+
return { ok: false, error: "Shepherd lifecycle control is unavailable" };
|
|
1944
|
+
}
|
|
1945
|
+
const data = await this.senseManager.quiesceShepherd(command.agent);
|
|
1946
|
+
return { ok: true, data };
|
|
1947
|
+
}
|
|
1934
1948
|
case "daemon.health": {
|
|
1935
1949
|
const checks = await this.healthMonitor.runChecks();
|
|
1936
1950
|
const summary = checks.map((check) => `${check.name}:${check.status}:${check.message}`).join("\n");
|
|
@@ -5,7 +5,10 @@ const runtime_1 = require("../../nerves/runtime");
|
|
|
5
5
|
const cli_arg_redaction_1 = require("./cli-arg-redaction");
|
|
6
6
|
const daemon_cli_1 = require("./daemon-cli");
|
|
7
7
|
const runtime_logging_1 = require("./runtime-logging");
|
|
8
|
-
(
|
|
8
|
+
if (process.argv[2] === "shepherd" && process.argv[3] !== "chat")
|
|
9
|
+
(0, runtime_logging_1.configureDaemonRuntimeLogger)("ouro", { machine: true });
|
|
10
|
+
else
|
|
11
|
+
(0, runtime_logging_1.configureDaemonRuntimeLogger)("ouro");
|
|
9
12
|
(0, runtime_1.emitNervesEvent)({
|
|
10
13
|
component: "daemon",
|
|
11
14
|
event: "daemon.cli_entry_start",
|
|
@@ -125,7 +125,7 @@ function resolveLoggingPathTarget(explicit) {
|
|
|
125
125
|
}
|
|
126
126
|
}
|
|
127
127
|
function configureDaemonRuntimeLogger(processName, options = {}) {
|
|
128
|
-
const pathTarget = resolveLoggingPathTarget(options.agentName);
|
|
128
|
+
const pathTarget = options.machine ? { kind: "machine" } : resolveLoggingPathTarget(options.agentName);
|
|
129
129
|
const configPath = options.configPath
|
|
130
130
|
?? (pathTarget.kind === "machine"
|
|
131
131
|
? machineDaemonLoggingConfigPath(options.homeDir)
|
|
@@ -593,6 +593,8 @@ class DaemonSenseManager {
|
|
|
593
593
|
configRefreshRetryTimers = new Map();
|
|
594
594
|
configRefreshRetryAttempts = new Map();
|
|
595
595
|
bundlesRoot;
|
|
596
|
+
quiescedShepherdOwners = new Set();
|
|
597
|
+
initiallyUnmanagedShepherdOwners;
|
|
596
598
|
constructor(options) {
|
|
597
599
|
const bundlesRoot = options.bundlesRoot ?? path.join(os.homedir(), "AgentBundles");
|
|
598
600
|
this.bundlesRoot = bundlesRoot;
|
|
@@ -601,6 +603,7 @@ class DaemonSenseManager {
|
|
|
601
603
|
const facts = senseFactsFromRuntimeConfig(agent, senses, (0, runtime_credentials_1.readRuntimeCredentialConfig)(agent), (0, runtime_credentials_1.readMachineRuntimeCredentialConfig)(agent), workbenchHasStaleBundleEntry);
|
|
602
604
|
return [agent, { senses, workbenchHasStaleBundleEntry, facts }];
|
|
603
605
|
}));
|
|
606
|
+
this.initiallyUnmanagedShepherdOwners = new Set([...this.contexts.entries()].filter(([, context]) => !context.senses.shepherd.enabled).map(([agent]) => agent));
|
|
604
607
|
const managedSenseAgents = [...this.contexts.entries()].flatMap(([agent, context]) => {
|
|
605
608
|
return ["teams", "bluebubbles", "mail", "voice", "a2a", "telegram", "shepherd"]
|
|
606
609
|
.filter((sense) => context.senses[sense].enabled)
|
|
@@ -620,6 +623,8 @@ class DaemonSenseManager {
|
|
|
620
623
|
const parsed = parseSenseSnapshotName(name);
|
|
621
624
|
if (!parsed)
|
|
622
625
|
return { ok: true };
|
|
626
|
+
if (parsed.sense === "shepherd" && this.quiescedShepherdOwners.has(parsed.agent))
|
|
627
|
+
return { ok: false, skip: true, error: "Shepherd is quiesced for maintenance until daemon restart" };
|
|
623
628
|
const context = this.contexts.get(parsed.agent);
|
|
624
629
|
if (!context)
|
|
625
630
|
return { ok: true };
|
|
@@ -711,6 +716,8 @@ class DaemonSenseManager {
|
|
|
711
716
|
/* v8 ignore next -- defensive: config refreshes are only scheduled for known agent contexts @preserve */
|
|
712
717
|
if (!context)
|
|
713
718
|
return;
|
|
719
|
+
if (parsed.sense === "shepherd" && this.quiescedShepherdOwners.has(parsed.agent))
|
|
720
|
+
return;
|
|
714
721
|
context.facts = senseFactsFromRuntimeConfig(parsed.agent, context.senses, refreshed, machineRefreshed, context.workbenchHasStaleBundleEntry);
|
|
715
722
|
if (!context.facts[parsed.sense].configured) {
|
|
716
723
|
if (this.shouldRetryConfigRefresh(parsed.agent, parsed.sense, refreshed, machineRefreshed)) {
|
|
@@ -723,6 +730,8 @@ class DaemonSenseManager {
|
|
|
723
730
|
}
|
|
724
731
|
this.clearConfigRetryState(name);
|
|
725
732
|
setTimeout(() => {
|
|
733
|
+
if (parsed.sense === "shepherd" && this.quiescedShepherdOwners.has(parsed.agent))
|
|
734
|
+
return;
|
|
726
735
|
void this.processManager.startAgent?.(name).catch((error) => {
|
|
727
736
|
(0, runtime_1.emitNervesEvent)({
|
|
728
737
|
level: "error",
|
|
@@ -814,6 +823,47 @@ class DaemonSenseManager {
|
|
|
814
823
|
.filter((pid) => pid !== null && pid !== undefined);
|
|
815
824
|
}
|
|
816
825
|
/* v8 ignore stop */
|
|
826
|
+
assertShepherdDisabled(agent) {
|
|
827
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(agent) || !this.contexts.has(agent))
|
|
828
|
+
throw new Error("unknown Shepherd owner");
|
|
829
|
+
const config = JSON.parse(fs.readFileSync(path.join(this.bundlesRoot, `${agent}.ouro`, "agent.json"), "utf8"));
|
|
830
|
+
const object = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
831
|
+
if (!object(config) || !object(config.senses) || !object(config.senses.shepherd) || config.senses.shepherd.enabled !== false) {
|
|
832
|
+
throw new Error("maintenance requires explicitly disabled Shepherd in fresh agent.json");
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
async quiesceShepherd(agent) {
|
|
836
|
+
this.assertShepherdDisabled(agent);
|
|
837
|
+
this.quiescedShepherdOwners.add(agent);
|
|
838
|
+
this.contexts.get(agent).senses.shepherd.enabled = false;
|
|
839
|
+
const managedName = `${agent}:shepherd`;
|
|
840
|
+
const snapshot = this.processManager.listAgentSnapshots().find(row => row.name === managedName);
|
|
841
|
+
if (snapshot && (snapshot.status !== "stopped" || snapshot.pid !== null)) {
|
|
842
|
+
if (!this.processManager.stopAgent)
|
|
843
|
+
throw new Error("managed Shepherd stop is unavailable");
|
|
844
|
+
const retry = this.configRefreshRetryTimers.get(managedName);
|
|
845
|
+
if (retry)
|
|
846
|
+
clearTimeout(retry);
|
|
847
|
+
this.configRefreshRetryTimers.delete(managedName);
|
|
848
|
+
this.configRefreshRetryAttempts.delete(managedName);
|
|
849
|
+
await this.processManager.stopAgent(managedName);
|
|
850
|
+
}
|
|
851
|
+
return this.shepherdStoppedProof(agent);
|
|
852
|
+
}
|
|
853
|
+
shepherdStoppedProof(agent) {
|
|
854
|
+
this.assertShepherdDisabled(agent);
|
|
855
|
+
const snapshots = this.processManager.listAgentSnapshots().filter(row => row.name === `${agent}:shepherd`);
|
|
856
|
+
// Disabled-at-startup senses are never registered with this process manager.
|
|
857
|
+
if (snapshots.length === 0 && this.initiallyUnmanagedShepherdOwners.has(agent)) {
|
|
858
|
+
return { kind: "daemon_lifecycle", checkedAt: new Date().toISOString(), enabled: false, processState: "stopped" };
|
|
859
|
+
}
|
|
860
|
+
if (snapshots.length !== 1)
|
|
861
|
+
throw new Error("actual managed Shepherd process snapshot is unavailable");
|
|
862
|
+
const snapshot = snapshots[0];
|
|
863
|
+
if (snapshot.status !== "stopped" || snapshot.pid !== null)
|
|
864
|
+
throw new Error("maintenance requires stopped Shepherd with no active PID");
|
|
865
|
+
return { kind: "daemon_lifecycle", checkedAt: new Date().toISOString(), enabled: false, processState: "stopped" };
|
|
866
|
+
}
|
|
817
867
|
listHealthProbes() {
|
|
818
868
|
const probes = [];
|
|
819
869
|
for (const [agent, context] of this.contexts.entries()) {
|
|
@@ -854,6 +904,9 @@ class DaemonSenseManager {
|
|
|
854
904
|
return probes;
|
|
855
905
|
}
|
|
856
906
|
async restartSense(managedName) {
|
|
907
|
+
const parsed = parseSenseSnapshotName(managedName);
|
|
908
|
+
if (parsed?.sense === "shepherd" && this.quiescedShepherdOwners.has(parsed.agent))
|
|
909
|
+
throw new Error("Shepherd is quiesced for maintenance until daemon restart");
|
|
857
910
|
if (this.processManager.restartAgent) {
|
|
858
911
|
await this.processManager.restartAgent(managedName);
|
|
859
912
|
return;
|
|
@@ -66,6 +66,7 @@ function createMailboxHttpReadHooks(options) {
|
|
|
66
66
|
const agentRoot = (agentName) => resolveAgentRoot(bundlesRoot, agentName);
|
|
67
67
|
return {
|
|
68
68
|
agentRoot,
|
|
69
|
+
readAgentSupervision: options.readAgentSupervision ?? ((agentName) => (0, mailbox_read_1.readMailboxSupervision)(agentName, { agentRoot: agentRoot(agentName) })),
|
|
69
70
|
readAgentSessions: options.readAgentSessions ?? ((agentName) => (0, mailbox_read_1.readSessionInventory)(agentName, readOptions)),
|
|
70
71
|
readAgentTranscript: options.readAgentTranscript ?? ((agentName, friendId, channel, key) => (0, mailbox_read_1.readSessionTranscript)(agentName, friendId, channel, key, readOptions)),
|
|
71
72
|
readAgentCoding: options.readAgentCoding ?? ((agentName) => (0, mailbox_read_1.readCodingDeep)(agentRoot(agentName))),
|
|
@@ -173,6 +173,14 @@ function createMailboxHttpRequestHandler(options) {
|
|
|
173
173
|
}
|
|
174
174
|
async function handleAgentRoute(request, response, context) {
|
|
175
175
|
const { agent, surface, options } = context;
|
|
176
|
+
if (surface === "supervision") {
|
|
177
|
+
if (request.method !== "GET") {
|
|
178
|
+
(0, mailbox_http_response_1.writeJson)(response, 405, { ok: false, error: "Supervision is read-only; use the local owner CLI for administration" });
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
(0, mailbox_http_response_1.writeJson)(response, 200, options.hooks.readAgentSupervision(agent));
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
176
184
|
if (!surface) {
|
|
177
185
|
const view = options.readAgentView?.(agent);
|
|
178
186
|
if (view) {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.readSelfFixView = exports.readMailboxContinuity = exports.readOrientationView = exports.readObligationDetailView = exports.readNoteDecisionView = exports.readSentinelView = exports.readContextLossGauntletView = exports.readChangesView = exports.readNeedsMeView = exports.readNotesView = exports.readLogView = exports.readContextPacketView = exports.readContextPacketListView = exports.readHabitView = exports.readHabitRunView = exports.readHabitRunReceiptView = exports.readHabitSessionSummaryView = exports.readHabitSessionSummaryListView = exports.readFriendView = exports.readDeskPrefs = exports.readDaemonHealthDeep = exports.readCodingDeep = exports.readBridgeInventory = exports.readAttentionView = exports.readMailView = exports.readMailMessageView = exports.readSessionTranscript = exports.readSessionInventory = exports.readMailboxMachineState = exports.readMailboxAgentState = exports.readObligationSummary = void 0;
|
|
3
|
+
exports.readSelfFixView = exports.readMailboxContinuity = exports.readOrientationView = exports.readObligationDetailView = exports.readNoteDecisionView = exports.readSentinelView = exports.readContextLossGauntletView = exports.readChangesView = exports.readNeedsMeView = exports.readNotesView = exports.readLogView = exports.readContextPacketView = exports.readContextPacketListView = exports.readHabitView = exports.readHabitRunView = exports.readHabitRunReceiptView = exports.readHabitSessionSummaryView = exports.readHabitSessionSummaryListView = exports.readFriendView = exports.readDeskPrefs = exports.readDaemonHealthDeep = exports.readCodingDeep = exports.readBridgeInventory = exports.readAttentionView = exports.readMailView = exports.readMailMessageView = exports.readSessionTranscript = exports.readSessionInventory = exports.readMailboxMachineState = exports.readMailboxAgentState = exports.readObligationSummary = exports.readMailboxSupervision = void 0;
|
|
4
|
+
var mailbox_supervision_1 = require("./mailbox-supervision");
|
|
5
|
+
Object.defineProperty(exports, "readMailboxSupervision", { enumerable: true, get: function () { return mailbox_supervision_1.readMailboxSupervision; } });
|
|
4
6
|
var agent_machine_1 = require("./readers/agent-machine");
|
|
5
7
|
Object.defineProperty(exports, "readObligationSummary", { enumerable: true, get: function () { return agent_machine_1.readObligationSummary; } });
|
|
6
8
|
Object.defineProperty(exports, "readMailboxAgentState", { enumerable: true, get: function () { return agent_machine_1.readMailboxAgentState; } });
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.readMailboxSupervision = readMailboxSupervision;
|
|
37
|
+
const fs = __importStar(require("node:fs"));
|
|
38
|
+
const path = __importStar(require("node:path"));
|
|
39
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
40
|
+
const supervision_1 = require("../shepherd/supervision");
|
|
41
|
+
function owned(metadata) {
|
|
42
|
+
if (!process.getuid || metadata.uid !== process.getuid())
|
|
43
|
+
throw new Error("Observation requires current-user ownership");
|
|
44
|
+
}
|
|
45
|
+
function directory(candidate, privateMode) {
|
|
46
|
+
const metadata = fs.lstatSync(candidate, { throwIfNoEntry: false });
|
|
47
|
+
if (!metadata)
|
|
48
|
+
return false;
|
|
49
|
+
owned(metadata);
|
|
50
|
+
if (!metadata.isDirectory() || (privateMode ? (metadata.mode & 0o7777) !== 0o700 : (metadata.mode & 0o022) !== 0)) {
|
|
51
|
+
throw new Error("Observation requires unlinked private directories and non-writable ancestors");
|
|
52
|
+
}
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
function regular(metadata) {
|
|
56
|
+
owned(metadata);
|
|
57
|
+
if (!metadata.isFile() || (metadata.mode & 0o7777) !== 0o600 || metadata.nlink !== 1
|
|
58
|
+
|| metadata.size > supervision_1.SHEPHERD_OBSERVATION_MAX_BYTES)
|
|
59
|
+
throw new Error("Observation requires a bounded private regular file");
|
|
60
|
+
}
|
|
61
|
+
function unavailable(agentName, nowMs, code) {
|
|
62
|
+
(0, runtime_1.emitNervesEvent)({
|
|
63
|
+
component: "heart", level: "warn", event: "heart.shepherd_observation_unavailable",
|
|
64
|
+
message: "Supervision observation is unavailable; no current terminal counts can be established", meta: { code },
|
|
65
|
+
});
|
|
66
|
+
const view = (0, supervision_1.buildShepherdSupervisionView)({
|
|
67
|
+
schemaVersion: 1, agentName, observedAt: new Date(nowMs).toISOString(),
|
|
68
|
+
sourceHealth: { host: "missing", guidance: "missing", mandates: "missing", returns: "missing", custody: "missing", notices: "missing" },
|
|
69
|
+
sessions: { totalCount: null, truncated: true, items: [] },
|
|
70
|
+
timeline: { totalCount: null, truncated: true, items: [] },
|
|
71
|
+
custody: { revision: null, bytes: null, capacity: "unknown", claims: { totalCount: null, truncated: true, items: [] } },
|
|
72
|
+
sourceErrors: [{ source: "replica", code, detail: code === "observation_missing"
|
|
73
|
+
? "No Shepherd observation is available. Check that the Shepherd sense is running."
|
|
74
|
+
: "The Shepherd observation could not be read safely or has an invalid schema." }],
|
|
75
|
+
}, nowMs);
|
|
76
|
+
view.freshness = { status: "unknown", observedAt: null, ageMs: null };
|
|
77
|
+
return view;
|
|
78
|
+
}
|
|
79
|
+
/** A bounded read-only replica, never an authority file or a live terminal access path. */
|
|
80
|
+
function readMailboxSupervision(agentName, options) {
|
|
81
|
+
const nowMs = options.nowMs ?? Date.now();
|
|
82
|
+
try {
|
|
83
|
+
let current = path.resolve(options.agentRoot);
|
|
84
|
+
if (!directory(current, false))
|
|
85
|
+
return unavailable(agentName, nowMs, "observation_missing");
|
|
86
|
+
for (const name of ["state", "senses", "shepherd"]) {
|
|
87
|
+
current = path.join(current, name);
|
|
88
|
+
if (!directory(current, name === "shepherd"))
|
|
89
|
+
return unavailable(agentName, nowMs, "observation_missing");
|
|
90
|
+
}
|
|
91
|
+
const file = path.join(current, supervision_1.SHEPHERD_OBSERVATION_FILE);
|
|
92
|
+
const before = fs.lstatSync(file, { throwIfNoEntry: false });
|
|
93
|
+
if (!before)
|
|
94
|
+
return unavailable(agentName, nowMs, "observation_missing");
|
|
95
|
+
regular(before);
|
|
96
|
+
const fd = fs.openSync(file, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW | fs.constants.O_NONBLOCK);
|
|
97
|
+
let raw;
|
|
98
|
+
try {
|
|
99
|
+
const opened = fs.fstatSync(fd);
|
|
100
|
+
regular(opened);
|
|
101
|
+
if (opened.ino !== before.ino || opened.dev !== before.dev)
|
|
102
|
+
throw new Error("Observation changed during read");
|
|
103
|
+
const buffer = Buffer.alloc(supervision_1.SHEPHERD_OBSERVATION_MAX_BYTES + 1);
|
|
104
|
+
let size = 0;
|
|
105
|
+
while (size < buffer.length) {
|
|
106
|
+
const received = fs.readSync(fd, buffer, size, buffer.length - size, null);
|
|
107
|
+
if (!received)
|
|
108
|
+
break;
|
|
109
|
+
size += received;
|
|
110
|
+
}
|
|
111
|
+
if (size > supervision_1.SHEPHERD_OBSERVATION_MAX_BYTES)
|
|
112
|
+
throw new Error("Observation exceeds read limit");
|
|
113
|
+
const after = fs.fstatSync(fd);
|
|
114
|
+
if (after.size !== opened.size || after.mtimeMs !== opened.mtimeMs || after.ctimeMs !== opened.ctimeMs) {
|
|
115
|
+
throw new Error("Observation changed during read");
|
|
116
|
+
}
|
|
117
|
+
raw = buffer.toString("utf8", 0, size);
|
|
118
|
+
}
|
|
119
|
+
finally {
|
|
120
|
+
fs.closeSync(fd);
|
|
121
|
+
}
|
|
122
|
+
const snapshot = JSON.parse(raw);
|
|
123
|
+
if (!(0, supervision_1.isShepherdSupervisionSnapshot)(snapshot) || snapshot.agentName !== agentName)
|
|
124
|
+
throw new Error("Invalid observation identity or schema");
|
|
125
|
+
return (0, supervision_1.buildShepherdSupervisionView)(snapshot, nowMs);
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
// Do not echo untrusted file contents, paths or operating-system errors into the browser.
|
|
129
|
+
return unavailable(agentName, nowMs, "observation_invalid");
|
|
130
|
+
}
|
|
131
|
+
}
|