@ouro.bot/cli 0.1.0-alpha.662 → 0.1.0-alpha.664
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 +15 -0
- package/dist/arc/flight-recorder.js +8 -3
- package/dist/heart/context-loss-gauntlet.js +1 -1
- package/dist/heart/context-loss-sentinel.js +861 -0
- package/dist/heart/daemon/agent-config-check.js +24 -16
- package/dist/heart/daemon/cli-exec.js +77 -1
- package/dist/heart/daemon/cli-help.js +11 -4
- package/dist/heart/daemon/cli-parse.js +29 -13
- package/dist/heart/daemon/daemon-entry.js +29 -1
- package/dist/heart/daemon/health-monitor.js +3 -0
- package/dist/heart/hatch/hatch-flow.js +2 -0
- package/dist/heart/mailbox/mailbox-http-hooks.js +1 -0
- package/dist/heart/mailbox/mailbox-http-routes.js +4 -0
- package/dist/heart/mailbox/mailbox-read.js +2 -1
- package/dist/heart/mailbox/readers/continuity-readers.js +5 -0
- package/dist/heart/provider-binding-resolver.js +3 -2
- package/dist/heart/provider-readiness-cache.js +88 -9
- package/dist/heart/start-of-turn-packet.js +7 -1
- package/dist/heart/turn-context.js +20 -0
- package/dist/mailbox-ui/assets/{index-B-V9vRQ0.js → index-BZ60na8O.js} +15 -15
- package/dist/mailbox-ui/assets/index-DG6Xf5uL.css +1 -0
- package/dist/mailbox-ui/index.html +2 -2
- package/dist/mind/bundle-manifest.js +3 -0
- package/dist/senses/pipeline.js +25 -2
- package/package.json +1 -1
- package/dist/mailbox-ui/assets/index-BOZbGbkL.css +0 -1
|
@@ -318,6 +318,8 @@ async function checkAgentConfigWithProviderHealth(agentName, bundlesRoot, deps =
|
|
|
318
318
|
return selectionResult.result;
|
|
319
319
|
if (selectionResult.disabled)
|
|
320
320
|
return { ok: true };
|
|
321
|
+
const agentRoot = agentRootFor(agentName, bundlesRoot);
|
|
322
|
+
const shouldRecordReadiness = deps.recordReadiness !== false;
|
|
321
323
|
deps.onProgress?.(selectedProviderPlan(agentName, selectionResult.bindings));
|
|
322
324
|
const ping = deps.pingProvider ?? (await Promise.resolve().then(() => __importStar(require("../provider-ping")))).pingProvider;
|
|
323
325
|
const providers = selectedProvidersForBindings(selectionResult.bindings);
|
|
@@ -375,32 +377,38 @@ async function checkAgentConfigWithProviderHealth(agentName, bundlesRoot, deps =
|
|
|
375
377
|
for (const { group, result } of pingResults) {
|
|
376
378
|
if (!result.ok) {
|
|
377
379
|
for (const lane of group.lanes) {
|
|
380
|
+
if (shouldRecordReadiness) {
|
|
381
|
+
(0, provider_readiness_cache_1.recordProviderLaneReadiness)({
|
|
382
|
+
agentRoot,
|
|
383
|
+
agentName,
|
|
384
|
+
lane,
|
|
385
|
+
provider: group.provider,
|
|
386
|
+
model: group.model,
|
|
387
|
+
credentialRevision: group.record.revision,
|
|
388
|
+
status: "failed",
|
|
389
|
+
checkedAt: new Date().toISOString(),
|
|
390
|
+
error: result.message,
|
|
391
|
+
attempts: pingAttemptCount(result),
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
firstFailure ??= failedPingResult(agentName, group.lanes[0], group.provider, group.model, result);
|
|
396
|
+
continue;
|
|
397
|
+
}
|
|
398
|
+
for (const lane of group.lanes) {
|
|
399
|
+
if (shouldRecordReadiness) {
|
|
378
400
|
(0, provider_readiness_cache_1.recordProviderLaneReadiness)({
|
|
401
|
+
agentRoot,
|
|
379
402
|
agentName,
|
|
380
403
|
lane,
|
|
381
404
|
provider: group.provider,
|
|
382
405
|
model: group.model,
|
|
383
406
|
credentialRevision: group.record.revision,
|
|
384
|
-
status: "
|
|
407
|
+
status: "ready",
|
|
385
408
|
checkedAt: new Date().toISOString(),
|
|
386
|
-
error: result.message,
|
|
387
409
|
attempts: pingAttemptCount(result),
|
|
388
410
|
});
|
|
389
411
|
}
|
|
390
|
-
firstFailure ??= failedPingResult(agentName, group.lanes[0], group.provider, group.model, result);
|
|
391
|
-
continue;
|
|
392
|
-
}
|
|
393
|
-
for (const lane of group.lanes) {
|
|
394
|
-
(0, provider_readiness_cache_1.recordProviderLaneReadiness)({
|
|
395
|
-
agentName,
|
|
396
|
-
lane,
|
|
397
|
-
provider: group.provider,
|
|
398
|
-
model: group.model,
|
|
399
|
-
credentialRevision: group.record.revision,
|
|
400
|
-
status: "ready",
|
|
401
|
-
checkedAt: new Date().toISOString(),
|
|
402
|
-
attempts: pingAttemptCount(result),
|
|
403
|
-
});
|
|
404
412
|
}
|
|
405
413
|
}
|
|
406
414
|
if (firstFailure)
|
|
@@ -76,6 +76,7 @@ const provider_credentials_1 = require("../provider-credentials");
|
|
|
76
76
|
const runtime_credentials_1 = require("../runtime-credentials");
|
|
77
77
|
const provider_binding_resolver_1 = require("../provider-binding-resolver");
|
|
78
78
|
const provider_visibility_1 = require("../provider-visibility");
|
|
79
|
+
const context_loss_sentinel_1 = require("../context-loss-sentinel");
|
|
79
80
|
const machine_identity_1 = require("../machine-identity");
|
|
80
81
|
const provider_models_1 = require("../provider-models");
|
|
81
82
|
const ouro_version_manager_1 = require("../versioning/ouro-version-manager");
|
|
@@ -110,6 +111,7 @@ const stale_bundle_prune_1 = require("./stale-bundle-prune");
|
|
|
110
111
|
const up_progress_1 = require("./up-progress");
|
|
111
112
|
const provider_ping_progress_1 = require("./provider-ping-progress");
|
|
112
113
|
const provider_ping_1 = require("../provider-ping");
|
|
114
|
+
const provider_readiness_cache_1 = require("../provider-readiness-cache");
|
|
113
115
|
const agent_discovery_1 = require("./agent-discovery");
|
|
114
116
|
const boot_sync_probe_1 = require("./boot-sync-probe");
|
|
115
117
|
const connect_bay_1 = require("./connect-bay");
|
|
@@ -368,6 +370,8 @@ function agentResolutionFailureMode(command) {
|
|
|
368
370
|
case "attention.history":
|
|
369
371
|
case "work.card":
|
|
370
372
|
case "work.gauntlet":
|
|
373
|
+
case "work.sentinel":
|
|
374
|
+
case "work.sentinel.refresh":
|
|
371
375
|
case "inner.status":
|
|
372
376
|
case "session.list":
|
|
373
377
|
return "return-message";
|
|
@@ -4628,6 +4632,11 @@ function credentialPingConfig(record) {
|
|
|
4628
4632
|
...record.config,
|
|
4629
4633
|
};
|
|
4630
4634
|
}
|
|
4635
|
+
function pingAttemptCount(result) {
|
|
4636
|
+
if (Array.isArray(result.attempts))
|
|
4637
|
+
return result.attempts.length;
|
|
4638
|
+
return undefined;
|
|
4639
|
+
}
|
|
4631
4640
|
async function readProviderCredentialRecord(agent, provider, _deps, options = {}) {
|
|
4632
4641
|
const poolResult = await (0, provider_credentials_1.refreshProviderCredentialPool)(agent, { ...options, providers: [provider] });
|
|
4633
4642
|
if (poolResult.ok) {
|
|
@@ -4764,7 +4773,8 @@ async function executeProviderCheck(command, deps) {
|
|
|
4764
4773
|
return message;
|
|
4765
4774
|
};
|
|
4766
4775
|
try {
|
|
4767
|
-
const { config } = (0, auth_flow_1.readAgentConfigForAgent)(command.agent, deps.bundlesRoot);
|
|
4776
|
+
const { config, configPath } = (0, auth_flow_1.readAgentConfigForAgent)(command.agent, deps.bundlesRoot);
|
|
4777
|
+
const agentRoot = path.dirname(configPath);
|
|
4768
4778
|
const binding = providerConfigBinding(config, command.lane);
|
|
4769
4779
|
progress.startPhase(`reading ${binding.provider} credentials`);
|
|
4770
4780
|
const credential = await readProviderCredentialRecord(command.agent, binding.provider, deps, {
|
|
@@ -4788,6 +4798,19 @@ async function executeProviderCheck(command, deps) {
|
|
|
4788
4798
|
});
|
|
4789
4799
|
const status = pingResult.ok ? "ready" : `failed (${pingResult.message})`;
|
|
4790
4800
|
progress.completePhase(`checking ${binding.provider} / ${binding.model}`, status);
|
|
4801
|
+
const attempts = pingAttemptCount(pingResult);
|
|
4802
|
+
(0, provider_readiness_cache_1.recordProviderLaneReadiness)({
|
|
4803
|
+
agentRoot,
|
|
4804
|
+
agentName: command.agent,
|
|
4805
|
+
lane: command.lane,
|
|
4806
|
+
provider: binding.provider,
|
|
4807
|
+
model: binding.model,
|
|
4808
|
+
credentialRevision: credential.record.revision,
|
|
4809
|
+
status: pingResult.ok ? "ready" : "failed",
|
|
4810
|
+
checkedAt: new Date().toISOString(),
|
|
4811
|
+
...(pingResult.ok ? {} : { error: pingResult.message }),
|
|
4812
|
+
...(attempts === undefined ? {} : { attempts }),
|
|
4813
|
+
});
|
|
4791
4814
|
const message = `${command.agent} ${command.lane} ${binding.provider} / ${binding.model}: ${status}`;
|
|
4792
4815
|
(0, runtime_1.emitNervesEvent)({
|
|
4793
4816
|
component: "daemon",
|
|
@@ -5573,6 +5596,40 @@ function resolveClonePath(options, checkExists, deps) {
|
|
|
5573
5596
|
return cloneTarget;
|
|
5574
5597
|
}
|
|
5575
5598
|
/* v8 ignore stop */
|
|
5599
|
+
const HOOK_SENTINEL_LOCK_TIMEOUT_MS = 500;
|
|
5600
|
+
function hookSentinelGitStatus() {
|
|
5601
|
+
return {
|
|
5602
|
+
ok: false,
|
|
5603
|
+
error: "skipped during session-start hook to keep lifecycle hook bounded; run `ouro work sentinel refresh --agent <agent>` for full bundle git status",
|
|
5604
|
+
};
|
|
5605
|
+
}
|
|
5606
|
+
async function refreshHookSentinel(command, deps) {
|
|
5607
|
+
if (command.event !== "session-start")
|
|
5608
|
+
return;
|
|
5609
|
+
const bundlesRoot = deps.bundlesRoot ?? (0, identity_1.getAgentBundlesRoot)();
|
|
5610
|
+
const bundleRoot = path.join(bundlesRoot, `${command.agent}.ouro`);
|
|
5611
|
+
try {
|
|
5612
|
+
await (0, context_loss_sentinel_1.refreshContextLossSentinel)(command.agent, bundleRoot, {
|
|
5613
|
+
trigger: "session_start",
|
|
5614
|
+
lockTimeoutMs: HOOK_SENTINEL_LOCK_TIMEOUT_MS,
|
|
5615
|
+
gitStatus: hookSentinelGitStatus,
|
|
5616
|
+
});
|
|
5617
|
+
}
|
|
5618
|
+
catch (error) {
|
|
5619
|
+
(0, runtime_1.emitNervesEvent)({
|
|
5620
|
+
level: "warn",
|
|
5621
|
+
component: "daemon",
|
|
5622
|
+
event: "daemon.hook_sentinel_refresh_error",
|
|
5623
|
+
message: "claude code hook Sentinel refresh failed",
|
|
5624
|
+
meta: {
|
|
5625
|
+
agent: command.agent,
|
|
5626
|
+
eventType: command.event,
|
|
5627
|
+
lockTimeoutMs: HOOK_SENTINEL_LOCK_TIMEOUT_MS,
|
|
5628
|
+
error: error instanceof Error ? error.message : String(error),
|
|
5629
|
+
},
|
|
5630
|
+
});
|
|
5631
|
+
}
|
|
5632
|
+
}
|
|
5576
5633
|
// ── Main CLI execution ──
|
|
5577
5634
|
async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDeps)()) {
|
|
5578
5635
|
if (args.length === 1 && (args[0] === "--help" || args[0] === "-h")) {
|
|
@@ -5622,6 +5679,9 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
|
|
|
5622
5679
|
throw new Error(resolvedCommand.message);
|
|
5623
5680
|
}
|
|
5624
5681
|
let command = resolvedCommand.command;
|
|
5682
|
+
if (command.kind === "hook") {
|
|
5683
|
+
await refreshHookSentinel(command, deps);
|
|
5684
|
+
}
|
|
5625
5685
|
if (args.length === 0) {
|
|
5626
5686
|
const discovered = await Promise.resolve(deps.listDiscoveredAgents ? deps.listDiscoveredAgents() : (0, cli_defaults_1.defaultListDiscoveredAgents)());
|
|
5627
5687
|
/* v8 ignore start -- the interactive home shell is exercised extensively in daemon-cli tests; V8 miscounts this orchestrator because it chains through recursive command handoffs and early chat health exits @preserve */
|
|
@@ -7343,6 +7403,22 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
|
|
|
7343
7403
|
deps.writeStdout(message);
|
|
7344
7404
|
return message;
|
|
7345
7405
|
}
|
|
7406
|
+
// ── context-loss Sentinel (local, no daemon socket needed) ──
|
|
7407
|
+
if (command.kind === "work.sentinel" || command.kind === "work.sentinel.refresh") {
|
|
7408
|
+
const { formatContextLossSentinelJson, formatContextLossSentinelText, readContextLossSentinelView, refreshContextLossSentinel, } = await Promise.resolve().then(() => __importStar(require("../context-loss-sentinel")));
|
|
7409
|
+
if (!command.agent)
|
|
7410
|
+
throw new Error("work sentinel requires --agent <name>");
|
|
7411
|
+
const bundlesRoot = deps.bundlesRoot ?? (0, identity_1.getAgentBundlesRoot)();
|
|
7412
|
+
const agentRoot = deps.agentBundleRoot ?? path.join(bundlesRoot, `${command.agent}.ouro`);
|
|
7413
|
+
const sentinel = command.kind === "work.sentinel.refresh"
|
|
7414
|
+
? await refreshContextLossSentinel(command.agent, agentRoot, { trigger: "manual_cli", homeDir: deps.homeDir })
|
|
7415
|
+
: readContextLossSentinelView(agentRoot, { limit: 20 });
|
|
7416
|
+
const message = command.format === "json"
|
|
7417
|
+
? formatContextLossSentinelJson(sentinel)
|
|
7418
|
+
: formatContextLossSentinelText(sentinel);
|
|
7419
|
+
deps.writeStdout(message);
|
|
7420
|
+
return message;
|
|
7421
|
+
}
|
|
7346
7422
|
// ── inner dialog status (local, no daemon socket needed) ──
|
|
7347
7423
|
/* v8 ignore start -- inner status handler: requires real agent state on disk @preserve */
|
|
7348
7424
|
if (command.kind === "inner.status") {
|
|
@@ -153,10 +153,10 @@ exports.COMMAND_REGISTRY = {
|
|
|
153
153
|
},
|
|
154
154
|
work: {
|
|
155
155
|
category: "Tasks",
|
|
156
|
-
description: "Show durable Arc work state or run the context-loss gauntlet.",
|
|
157
|
-
usage: "ouro work card|gauntlet [--agent <name>] [--format text|json|--json]",
|
|
158
|
-
example: "ouro work
|
|
159
|
-
subcommands: ["card", "gauntlet"],
|
|
156
|
+
description: "Show durable Arc work state, recovery Sentinel state, or run the context-loss gauntlet.",
|
|
157
|
+
usage: "ouro work card|gauntlet|sentinel [refresh] [--agent <name>] [--format text|json|--json]",
|
|
158
|
+
example: "ouro work sentinel --agent slugger --format json",
|
|
159
|
+
subcommands: ["card", "gauntlet", "sentinel"],
|
|
160
160
|
},
|
|
161
161
|
"work card": {
|
|
162
162
|
category: "Tasks",
|
|
@@ -172,6 +172,13 @@ exports.COMMAND_REGISTRY = {
|
|
|
172
172
|
example: "ouro work gauntlet --agent slugger --format json",
|
|
173
173
|
hidden: true,
|
|
174
174
|
},
|
|
175
|
+
"work sentinel": {
|
|
176
|
+
category: "Tasks",
|
|
177
|
+
description: "Show read-only Arc Sentinel recovery state, or explicitly refresh it.",
|
|
178
|
+
usage: "ouro work sentinel [refresh] [--agent <name>] [--format text|json|--json]",
|
|
179
|
+
example: "ouro work sentinel refresh --agent slugger --format json",
|
|
180
|
+
hidden: true,
|
|
181
|
+
},
|
|
175
182
|
"migrate-to-desk": {
|
|
176
183
|
category: "Tasks",
|
|
177
184
|
description: "Migrate a legacy `tasks/` tree into the new `desk/` shape (copy semantics — source untouched).",
|
|
@@ -114,7 +114,7 @@ function usage() {
|
|
|
114
114
|
" ouro friend create --name <name> [--trust <level>] [--agent <name>]",
|
|
115
115
|
" ouro friend update <id> --trust <level> [--agent <name>]",
|
|
116
116
|
" ouro thoughts [--last <n>] [--json] [--follow] [--agent <name>]",
|
|
117
|
-
" ouro work card|gauntlet [--agent <name>] [--format text|json|--json]",
|
|
117
|
+
" ouro work card|gauntlet|sentinel [refresh] [--agent <name>] [--format text|json|--json]",
|
|
118
118
|
" ouro inner [--agent <name>]",
|
|
119
119
|
" ouro friend link <agent> --friend <id> --provider <p> --external-id <eid>",
|
|
120
120
|
" ouro friend unlink <agent> --friend <id> --provider <p> --external-id <eid>",
|
|
@@ -985,11 +985,13 @@ function parseProviderCheckCommand(args) {
|
|
|
985
985
|
}
|
|
986
986
|
function parseProviderCommand(args) {
|
|
987
987
|
const sub = args[0];
|
|
988
|
+
if (sub === "check")
|
|
989
|
+
return parseProviderCheckCommand(args.slice(1));
|
|
988
990
|
const { agent, rest } = extractAgentFlag(args.slice(1));
|
|
989
991
|
if (sub === "refresh" && rest.length === 0) {
|
|
990
992
|
return { kind: "provider.refresh", ...(agent ? { agent } : {}) };
|
|
991
993
|
}
|
|
992
|
-
throw new Error("Usage: ouro provider refresh [--agent <name>]");
|
|
994
|
+
throw new Error("Usage: ouro provider refresh [--agent <name>] OR ouro provider check [--agent <name>] --lane outward|inner");
|
|
993
995
|
}
|
|
994
996
|
function parseSessionCommand(args) {
|
|
995
997
|
const { agent, rest: cleaned } = extractAgentFlag(args);
|
|
@@ -1011,28 +1013,42 @@ function parseAttentionCommand(args) {
|
|
|
1011
1013
|
}
|
|
1012
1014
|
return { kind: "attention.list", ...(agent ? { agent } : {}) };
|
|
1013
1015
|
}
|
|
1014
|
-
function
|
|
1015
|
-
const { agent, rest: cleaned } = extractAgentFlag(args);
|
|
1016
|
-
const sub = cleaned[0];
|
|
1017
|
-
if (sub !== "card" && sub !== "gauntlet") {
|
|
1018
|
-
throw new Error("Usage: ouro work card|gauntlet [--agent <name>] [--format text|json|--json]");
|
|
1019
|
-
}
|
|
1016
|
+
function parseWorkFormat(args, usageText) {
|
|
1020
1017
|
let format = "text";
|
|
1021
|
-
for (let i =
|
|
1022
|
-
if (
|
|
1018
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
1019
|
+
if (args[i] === "--json") {
|
|
1023
1020
|
format = "json";
|
|
1024
1021
|
continue;
|
|
1025
1022
|
}
|
|
1026
|
-
if (
|
|
1027
|
-
const value =
|
|
1023
|
+
if (args[i] === "--format" && args[i + 1]) {
|
|
1024
|
+
const value = args[++i];
|
|
1028
1025
|
if (value !== "text" && value !== "json") {
|
|
1029
1026
|
throw new Error("--format must be text or json");
|
|
1030
1027
|
}
|
|
1031
1028
|
format = value;
|
|
1032
1029
|
continue;
|
|
1033
1030
|
}
|
|
1034
|
-
throw new Error(
|
|
1031
|
+
throw new Error(usageText);
|
|
1032
|
+
}
|
|
1033
|
+
return format;
|
|
1034
|
+
}
|
|
1035
|
+
function parseWorkCommand(args) {
|
|
1036
|
+
const { agent, rest: cleaned } = extractAgentFlag(args);
|
|
1037
|
+
const sub = cleaned[0];
|
|
1038
|
+
if (sub === "sentinel") {
|
|
1039
|
+
const refresh = cleaned[1] === "refresh";
|
|
1040
|
+
const usageText = "Usage: ouro work sentinel [refresh] [--agent <name>] [--format text|json|--json]";
|
|
1041
|
+
const format = parseWorkFormat(cleaned.slice(refresh ? 2 : 1), usageText);
|
|
1042
|
+
return {
|
|
1043
|
+
kind: refresh ? "work.sentinel.refresh" : "work.sentinel",
|
|
1044
|
+
...(agent ? { agent } : {}),
|
|
1045
|
+
...(format !== "text" ? { format } : {}),
|
|
1046
|
+
};
|
|
1047
|
+
}
|
|
1048
|
+
if (sub !== "card" && sub !== "gauntlet") {
|
|
1049
|
+
throw new Error("Usage: ouro work card|gauntlet|sentinel [refresh] [--agent <name>] [--format text|json|--json]");
|
|
1035
1050
|
}
|
|
1051
|
+
const format = parseWorkFormat(cleaned.slice(1), `Usage: ouro work ${sub} [--agent <name>] [--format text|json|--json]`);
|
|
1036
1052
|
return { kind: sub === "card" ? "work.card" : "work.gauntlet", ...(agent ? { agent } : {}), ...(format !== "text" ? { format } : {}) };
|
|
1037
1053
|
}
|
|
1038
1054
|
function parseThoughtsCommand(args) {
|
|
@@ -62,6 +62,7 @@ const pulse_1 = require("./pulse");
|
|
|
62
62
|
const socket_client_1 = require("./socket-client");
|
|
63
63
|
const bundle_manifest_1 = require("../../mind/bundle-manifest");
|
|
64
64
|
const mcp_canary_1 = require("./mcp-canary");
|
|
65
|
+
const context_loss_sentinel_1 = require("../context-loss-sentinel");
|
|
65
66
|
function parseSocketPath(argv) {
|
|
66
67
|
const socketIndex = argv.indexOf("--socket");
|
|
67
68
|
if (socketIndex >= 0) {
|
|
@@ -92,6 +93,31 @@ if (mode === "dev") {
|
|
|
92
93
|
});
|
|
93
94
|
}
|
|
94
95
|
const managedAgents = (0, agent_discovery_1.listEnabledBundleAgents)();
|
|
96
|
+
function sentinelHealthStatus(receipt) {
|
|
97
|
+
if (receipt.verdict === "ready")
|
|
98
|
+
return "ok";
|
|
99
|
+
if (receipt.verdict === "watch")
|
|
100
|
+
return "warn";
|
|
101
|
+
return "critical";
|
|
102
|
+
}
|
|
103
|
+
async function refreshDaemonSentinel(agent, trigger, daemonHealthResults = []) {
|
|
104
|
+
const bundleRoot = path.join((0, identity_1.getAgentBundlesRoot)(), `${agent}.ouro`);
|
|
105
|
+
try {
|
|
106
|
+
const receipt = await (0, context_loss_sentinel_1.refreshContextLossSentinel)(agent, bundleRoot, { trigger, daemonHealthResults });
|
|
107
|
+
return {
|
|
108
|
+
name: `context-loss-sentinel:${agent}`,
|
|
109
|
+
status: sentinelHealthStatus(receipt),
|
|
110
|
+
message: `Sentinel ${receipt.verdict}: ${receipt.summary}`,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
return {
|
|
115
|
+
name: `context-loss-sentinel:${agent}`,
|
|
116
|
+
status: "critical",
|
|
117
|
+
message: `Sentinel refresh failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
}
|
|
95
121
|
const processManager = new process_manager_1.DaemonProcessManager({
|
|
96
122
|
agents: managedAgents.map((agent) => ({
|
|
97
123
|
name: agent,
|
|
@@ -133,6 +159,7 @@ const senseManager = new sense_manager_1.DaemonSenseManager({
|
|
|
133
159
|
const healthMonitor = new health_monitor_1.HealthMonitor({
|
|
134
160
|
processManager,
|
|
135
161
|
scheduler,
|
|
162
|
+
sentinelChecker: (resultsSoFar) => Promise.all(managedAgents.map((agent) => refreshDaemonSentinel(agent, "daemon_health", resultsSoFar))),
|
|
136
163
|
senseProbeProvider: () => [
|
|
137
164
|
...senseManager.listHealthProbes(),
|
|
138
165
|
...managedAgents.map((agent) => (0, mcp_canary_1.createMcpStatusCanaryProbe)({
|
|
@@ -349,11 +376,12 @@ function writeStopCommandHealthState() {
|
|
|
349
376
|
}
|
|
350
377
|
}
|
|
351
378
|
/* v8 ignore start -- habit wiring: lambdas delegate to processManager/fs; tested via HabitScheduler unit tests @preserve */
|
|
352
|
-
void daemon.start().then(() => {
|
|
379
|
+
void daemon.start().then(async () => {
|
|
353
380
|
const bundlesRoot = (0, identity_1.getAgentBundlesRoot)();
|
|
354
381
|
const ouroPath = (0, os_cron_deps_1.resolveOuroBinaryPath)();
|
|
355
382
|
const osCronDeps = (0, os_cron_deps_1.createRealOsCronDeps)();
|
|
356
383
|
for (const agent of managedAgents) {
|
|
384
|
+
await refreshDaemonSentinel(agent, "daemon_startup");
|
|
357
385
|
const bundleRoot = path.join(bundlesRoot, `${agent}.ouro`);
|
|
358
386
|
const habitsDir = path.join(bundleRoot, "habits");
|
|
359
387
|
const degradedComponent = `habits:${agent}`;
|
|
@@ -7,6 +7,7 @@ class HealthMonitor {
|
|
|
7
7
|
scheduler;
|
|
8
8
|
alertSink;
|
|
9
9
|
diskUsagePercent;
|
|
10
|
+
sentinelChecker;
|
|
10
11
|
onCriticalAgent;
|
|
11
12
|
onCriticalSense;
|
|
12
13
|
senseProbes;
|
|
@@ -18,6 +19,7 @@ class HealthMonitor {
|
|
|
18
19
|
this.scheduler = options.scheduler;
|
|
19
20
|
this.alertSink = options.alertSink ?? (() => undefined);
|
|
20
21
|
this.diskUsagePercent = options.diskUsagePercent ?? (() => 0);
|
|
22
|
+
this.sentinelChecker = options.sentinelChecker ?? (() => []);
|
|
21
23
|
this.onCriticalAgent = options.onCriticalAgent ?? (() => undefined);
|
|
22
24
|
this.onCriticalSense = options.onCriticalSense ?? (() => undefined);
|
|
23
25
|
this.senseProbes = options.senseProbes ?? [];
|
|
@@ -178,6 +180,7 @@ class HealthMonitor {
|
|
|
178
180
|
});
|
|
179
181
|
}
|
|
180
182
|
}
|
|
183
|
+
results.push(...await this.sentinelChecker(results.map((result) => ({ ...result }))));
|
|
181
184
|
this.lastResults = results.map((result) => ({ ...result }));
|
|
182
185
|
for (const result of results) {
|
|
183
186
|
(0, runtime_1.emitNervesEvent)({
|
|
@@ -125,6 +125,8 @@ function writeRecordScaffold(bundleRoot) {
|
|
|
125
125
|
fs.writeFileSync(recordPaths.entitiesPath, "{}\n", "utf-8");
|
|
126
126
|
fs.mkdirSync(path.join(bundleRoot, "arc", "flight-recorder", "events"), { recursive: true });
|
|
127
127
|
fs.mkdirSync(path.join(bundleRoot, "arc", "flight-recorder", "habit-receipts"), { recursive: true });
|
|
128
|
+
fs.mkdirSync(path.join(bundleRoot, "arc", "flight-recorder", "context-loss-sentinel", "history"), { recursive: true });
|
|
129
|
+
fs.mkdirSync(path.join(bundleRoot, "arc", "flight-recorder", "context-loss-sentinel", "receipts"), { recursive: true });
|
|
128
130
|
fs.mkdirSync(path.join(bundleRoot, "arc", "claims"), { recursive: true });
|
|
129
131
|
}
|
|
130
132
|
function writeHatchlingAgentConfig(bundleRoot, input) {
|
|
@@ -55,6 +55,7 @@ function createMailboxHttpReadHooks(options) {
|
|
|
55
55
|
readAgentChanges: options.readAgentChanges ?? ((agentName) => (0, mailbox_read_1.readChangesView)(agentRoot(agentName))),
|
|
56
56
|
readAgentSelfFix: options.readAgentSelfFix ?? ((agentName) => (0, mailbox_read_1.readSelfFixView)(agentRoot(agentName))),
|
|
57
57
|
readAgentContextLossGauntlet: options.readAgentContextLossGauntlet ?? ((agentName) => (0, mailbox_read_1.readContextLossGauntletView)(agentRoot(agentName), agentName)),
|
|
58
|
+
readAgentSentinel: options.readAgentSentinel ?? ((agentName) => (0, mailbox_read_1.readSentinelView)(agentRoot(agentName))),
|
|
58
59
|
readAgentNoteDecisions: options.readAgentNoteDecisions ?? ((agentName) => (0, mailbox_read_1.readNoteDecisionView)(agentRoot(agentName))),
|
|
59
60
|
readAgentHabits: options.readAgentHabits ?? ((agentName) => (0, mailbox_read_1.readHabitView)(agentRoot(agentName))),
|
|
60
61
|
readAgentMail: options.readAgentMail ?? ((agentName) => (0, mailbox_read_1.readMailView)(agentName)),
|
|
@@ -176,6 +176,10 @@ async function handleAgentRoute(request, response, context) {
|
|
|
176
176
|
(0, mailbox_http_response_1.writeJson)(response, 200, options.hooks.readAgentContextLossGauntlet(agent));
|
|
177
177
|
return;
|
|
178
178
|
}
|
|
179
|
+
if (surface === "sentinel") {
|
|
180
|
+
(0, mailbox_http_response_1.writeJson)(response, 200, options.hooks.readAgentSentinel(agent));
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
179
183
|
if (surface === "note-decisions") {
|
|
180
184
|
(0, mailbox_http_response_1.writeJson)(response, 200, options.hooks.readAgentNoteDecisions(agent));
|
|
181
185
|
return;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.readSelfFixView = exports.readMailboxContinuity = exports.readOrientationView = exports.readObligationDetailView = exports.readNoteDecisionView = exports.readContextLossGauntletView = exports.readChangesView = exports.readNeedsMeView = exports.readNotesView = exports.readLogView = exports.readHabitView = 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.readHabitView = 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;
|
|
4
4
|
var agent_machine_1 = require("./readers/agent-machine");
|
|
5
5
|
Object.defineProperty(exports, "readObligationSummary", { enumerable: true, get: function () { return agent_machine_1.readObligationSummary; } });
|
|
6
6
|
Object.defineProperty(exports, "readMailboxAgentState", { enumerable: true, get: function () { return agent_machine_1.readMailboxAgentState; } });
|
|
@@ -25,6 +25,7 @@ Object.defineProperty(exports, "readNeedsMeView", { enumerable: true, get: funct
|
|
|
25
25
|
var continuity_readers_1 = require("./readers/continuity-readers");
|
|
26
26
|
Object.defineProperty(exports, "readChangesView", { enumerable: true, get: function () { return continuity_readers_1.readChangesView; } });
|
|
27
27
|
Object.defineProperty(exports, "readContextLossGauntletView", { enumerable: true, get: function () { return continuity_readers_1.readContextLossGauntletView; } });
|
|
28
|
+
Object.defineProperty(exports, "readSentinelView", { enumerable: true, get: function () { return continuity_readers_1.readSentinelView; } });
|
|
28
29
|
Object.defineProperty(exports, "readNoteDecisionView", { enumerable: true, get: function () { return continuity_readers_1.readNoteDecisionView; } });
|
|
29
30
|
Object.defineProperty(exports, "readObligationDetailView", { enumerable: true, get: function () { return continuity_readers_1.readObligationDetailView; } });
|
|
30
31
|
Object.defineProperty(exports, "readOrientationView", { enumerable: true, get: function () { return continuity_readers_1.readOrientationView; } });
|
|
@@ -39,6 +39,7 @@ exports.readObligationDetailView = readObligationDetailView;
|
|
|
39
39
|
exports.readChangesView = readChangesView;
|
|
40
40
|
exports.readSelfFixView = readSelfFixView;
|
|
41
41
|
exports.readContextLossGauntletView = readContextLossGauntletView;
|
|
42
|
+
exports.readSentinelView = readSentinelView;
|
|
42
43
|
exports.readNoteDecisionView = readNoteDecisionView;
|
|
43
44
|
const fs = __importStar(require("fs"));
|
|
44
45
|
const path = __importStar(require("path"));
|
|
@@ -50,6 +51,7 @@ const presence_1 = require("../../../arc/presence");
|
|
|
50
51
|
const active_work_1 = require("../../active-work");
|
|
51
52
|
const session_activity_1 = require("../../session-activity");
|
|
52
53
|
const context_loss_gauntlet_1 = require("../../context-loss-gauntlet");
|
|
54
|
+
const context_loss_sentinel_1 = require("../../context-loss-sentinel");
|
|
53
55
|
const agent_machine_1 = require("./agent-machine");
|
|
54
56
|
function sortOpenObligations(obligations) {
|
|
55
57
|
const statusPriority = {
|
|
@@ -295,6 +297,9 @@ function readSelfFixView(_agentRoot) {
|
|
|
295
297
|
function readContextLossGauntletView(agentRoot, agentName) {
|
|
296
298
|
return (0, context_loss_gauntlet_1.runContextLossGauntlet)(agentName, agentRoot);
|
|
297
299
|
}
|
|
300
|
+
function readSentinelView(agentRoot) {
|
|
301
|
+
return (0, context_loss_sentinel_1.readContextLossSentinelView)(agentRoot, { limit: 20 });
|
|
302
|
+
}
|
|
298
303
|
function readNoteDecisionView(agentRoot, limit = 50) {
|
|
299
304
|
const logPath = path.join(agentRoot, "state", "mailbox", "note-decisions.jsonl");
|
|
300
305
|
const legacyLogPath = path.join(agentRoot, "state", "outlook", "note-decisions.jsonl");
|
|
@@ -153,7 +153,7 @@ function resolveCredential(poolResult, provider, agentName) {
|
|
|
153
153
|
warnings: [missingCredentialWarning(provider)],
|
|
154
154
|
};
|
|
155
155
|
}
|
|
156
|
-
function resolveReadiness(agentName, lane, provider, model, credential) {
|
|
156
|
+
function resolveReadiness(agentName, agentRoot, lane, provider, model, credential) {
|
|
157
157
|
if (credential.status === "missing") {
|
|
158
158
|
return { status: "unknown", reason: "credential-missing" };
|
|
159
159
|
}
|
|
@@ -162,6 +162,7 @@ function resolveReadiness(agentName, lane, provider, model, credential) {
|
|
|
162
162
|
}
|
|
163
163
|
if (credential.status === "present") {
|
|
164
164
|
const cached = (0, provider_readiness_cache_1.readProviderLaneReadiness)({
|
|
165
|
+
agentRoot,
|
|
165
166
|
agentName,
|
|
166
167
|
lane,
|
|
167
168
|
provider,
|
|
@@ -239,7 +240,7 @@ function resolveEffectiveProviderBinding(input) {
|
|
|
239
240
|
}
|
|
240
241
|
const poolResult = (0, provider_credentials_1.readProviderCredentialPool)(input.agentName);
|
|
241
242
|
const credentialResult = resolveCredential(poolResult, agentConfigResult.provider, input.agentName);
|
|
242
|
-
const readiness = resolveReadiness(input.agentName, laneResolution.lane, agentConfigResult.provider, agentConfigResult.model, credentialResult.credential);
|
|
243
|
+
const readiness = resolveReadiness(input.agentName, input.agentRoot, laneResolution.lane, agentConfigResult.provider, agentConfigResult.model, credentialResult.credential);
|
|
243
244
|
const warnings = [
|
|
244
245
|
...laneResolution.warnings,
|
|
245
246
|
...credentialResult.warnings,
|
|
@@ -1,15 +1,91 @@
|
|
|
1
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
|
+
})();
|
|
2
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
36
|
exports.recordProviderLaneReadiness = recordProviderLaneReadiness;
|
|
4
37
|
exports.readProviderLaneReadiness = readProviderLaneReadiness;
|
|
5
38
|
exports.clearProviderReadinessCache = clearProviderReadinessCache;
|
|
39
|
+
const fs = __importStar(require("fs"));
|
|
40
|
+
const path = __importStar(require("path"));
|
|
6
41
|
const runtime_1 = require("../nerves/runtime");
|
|
7
42
|
const readinessByLane = new Map();
|
|
8
43
|
function cacheKey(agentName, lane) {
|
|
9
44
|
return `${agentName}\0${lane}`;
|
|
10
45
|
}
|
|
11
|
-
function
|
|
46
|
+
function readinessStorePath(agentRoot) {
|
|
47
|
+
return path.join(agentRoot, "state", "providers", "readiness.json");
|
|
48
|
+
}
|
|
49
|
+
function defaultReadinessStore() {
|
|
50
|
+
return { schemaVersion: 1, updatedAt: new Date(0).toISOString(), lanes: {} };
|
|
51
|
+
}
|
|
52
|
+
function readReadinessStore(agentRoot) {
|
|
53
|
+
try {
|
|
54
|
+
const parsed = JSON.parse(fs.readFileSync(readinessStorePath(agentRoot), "utf-8"));
|
|
55
|
+
if (parsed.schemaVersion === 1 && parsed.lanes && typeof parsed.lanes === "object")
|
|
56
|
+
return parsed;
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return defaultReadinessStore();
|
|
60
|
+
}
|
|
61
|
+
return defaultReadinessStore();
|
|
62
|
+
}
|
|
63
|
+
function writeReadinessStore(agentRoot, entry) {
|
|
64
|
+
const storePath = readinessStorePath(agentRoot);
|
|
65
|
+
const store = readReadinessStore(agentRoot);
|
|
66
|
+
const updated = {
|
|
67
|
+
schemaVersion: 1,
|
|
68
|
+
updatedAt: entry.checkedAt,
|
|
69
|
+
lanes: { ...store.lanes, [entry.lane]: entry },
|
|
70
|
+
};
|
|
71
|
+
fs.mkdirSync(path.dirname(storePath), { recursive: true });
|
|
72
|
+
fs.writeFileSync(storePath, `${JSON.stringify(updated, null, 2)}\n`, "utf-8");
|
|
73
|
+
}
|
|
74
|
+
function matchesLookup(entry, input) {
|
|
75
|
+
return entry.agentName === input.agentName
|
|
76
|
+
&& entry.lane === input.lane
|
|
77
|
+
&& entry.provider === input.provider
|
|
78
|
+
&& entry.model === input.model
|
|
79
|
+
&& entry.credentialRevision === input.credentialRevision;
|
|
80
|
+
}
|
|
81
|
+
function isNewerReadiness(candidate, existing) {
|
|
82
|
+
return Date.parse(candidate.checkedAt) > Date.parse(existing.checkedAt);
|
|
83
|
+
}
|
|
84
|
+
function recordProviderLaneReadiness(input) {
|
|
85
|
+
const { agentRoot, ...entry } = input;
|
|
12
86
|
readinessByLane.set(cacheKey(entry.agentName, entry.lane), { ...entry });
|
|
87
|
+
if (agentRoot)
|
|
88
|
+
writeReadinessStore(agentRoot, entry);
|
|
13
89
|
(0, runtime_1.emitNervesEvent)({
|
|
14
90
|
component: "config/identity",
|
|
15
91
|
event: "config.provider_readiness_recorded",
|
|
@@ -25,15 +101,18 @@ function recordProviderLaneReadiness(entry) {
|
|
|
25
101
|
}
|
|
26
102
|
function readProviderLaneReadiness(input) {
|
|
27
103
|
const entry = readinessByLane.get(cacheKey(input.agentName, input.lane));
|
|
28
|
-
if (!
|
|
29
|
-
return null;
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
104
|
+
if (!input.agentRoot)
|
|
105
|
+
return entry && matchesLookup(entry, input) ? { ...entry } : null;
|
|
106
|
+
const durable = readReadinessStore(input.agentRoot).lanes[input.lane];
|
|
107
|
+
const memoryMatch = entry && matchesLookup(entry, input) ? entry : null;
|
|
108
|
+
const durableMatch = durable && matchesLookup(durable, input) ? durable : null;
|
|
109
|
+
const selected = memoryMatch && durableMatch
|
|
110
|
+
? (isNewerReadiness(durableMatch, memoryMatch) ? durableMatch : memoryMatch)
|
|
111
|
+
: (durableMatch ?? memoryMatch);
|
|
112
|
+
if (!selected)
|
|
35
113
|
return null;
|
|
36
|
-
|
|
114
|
+
readinessByLane.set(cacheKey(selected.agentName, selected.lane), { ...selected });
|
|
115
|
+
return { ...selected };
|
|
37
116
|
}
|
|
38
117
|
function clearProviderReadinessCache() {
|
|
39
118
|
readinessByLane.clear();
|