@ouro.bot/cli 0.1.0-alpha.35 → 0.1.0-alpha.350
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +188 -187
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/agent.json +3 -2
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/SOUL.md +1 -1
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-serpent.md +1 -1
- package/changelog.json +2088 -0
- package/dist/arc/attention-types.js +8 -0
- package/dist/arc/cares.js +140 -0
- package/dist/arc/episodes.js +117 -0
- package/dist/arc/intentions.js +133 -0
- package/dist/arc/json-store.js +117 -0
- package/dist/arc/obligations.js +237 -0
- package/dist/arc/packets.js +193 -0
- package/dist/arc/presence.js +185 -0
- package/dist/arc/task-lifecycle.js +65 -0
- package/dist/heart/active-work.js +832 -0
- package/dist/heart/agent-entry.js +37 -2
- package/dist/heart/attachments/image-normalize.js +194 -0
- package/dist/heart/attachments/materialize.js +97 -0
- package/dist/heart/attachments/originals.js +88 -0
- package/dist/heart/attachments/render.js +29 -0
- package/dist/heart/attachments/sources/adapter.js +2 -0
- package/dist/heart/attachments/sources/bluebubbles.js +156 -0
- package/dist/heart/attachments/sources/cli-local-file.js +78 -0
- package/dist/heart/attachments/sources/index.js +16 -0
- package/dist/heart/attachments/store.js +103 -0
- package/dist/heart/attachments/types.js +93 -0
- package/dist/heart/auth/auth-flow.js +463 -0
- package/dist/heart/bridges/manager.js +358 -0
- package/dist/heart/bridges/state-machine.js +135 -0
- package/dist/heart/bridges/store.js +123 -0
- package/dist/heart/bundle-state.js +168 -0
- package/dist/heart/commitments.js +111 -0
- package/dist/heart/config-registry.js +304 -0
- package/dist/heart/config.js +53 -21
- package/dist/heart/core.js +744 -252
- package/dist/heart/cross-chat-delivery.js +131 -0
- package/dist/heart/daemon/agent-config-check.js +561 -0
- package/dist/heart/daemon/agent-discovery.js +79 -3
- package/dist/heart/daemon/agent-service.js +360 -0
- package/dist/heart/daemon/agentic-repair.js +170 -0
- package/dist/heart/daemon/bluebubbles-health-diagnostics.js +122 -0
- package/dist/heart/daemon/cadence.js +70 -0
- package/dist/heart/daemon/cli-defaults.js +591 -0
- package/dist/heart/daemon/cli-exec.js +2633 -0
- package/dist/heart/daemon/cli-help.js +306 -0
- package/dist/heart/daemon/cli-parse.js +913 -0
- package/dist/heart/daemon/cli-render-doctor.js +57 -0
- package/dist/heart/daemon/cli-render.js +512 -0
- package/dist/heart/daemon/cli-types.js +8 -0
- package/dist/heart/daemon/daemon-cli.js +30 -1171
- package/dist/heart/daemon/daemon-entry.js +358 -3
- package/dist/heart/daemon/daemon-health.js +141 -0
- package/dist/heart/daemon/daemon-runtime-sync.js +157 -12
- package/dist/heart/daemon/daemon-tombstone.js +236 -0
- package/dist/heart/daemon/daemon.js +751 -58
- package/dist/heart/daemon/doctor-types.js +8 -0
- package/dist/heart/daemon/doctor.js +465 -0
- package/dist/heart/daemon/health-monitor.js +79 -1
- package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
- package/dist/heart/daemon/hooks/bundle-meta.js +115 -1
- package/dist/heart/daemon/http-health-probe.js +80 -0
- package/dist/heart/daemon/inner-status.js +89 -0
- package/dist/heart/daemon/interactive-repair.js +91 -0
- package/dist/heart/daemon/launchd.js +46 -9
- package/dist/heart/daemon/log-tailer.js +82 -12
- package/dist/heart/daemon/logs-prune.js +105 -0
- package/dist/heart/daemon/message-router.js +17 -8
- package/dist/heart/daemon/os-cron-deps.js +134 -0
- package/dist/heart/daemon/ouro-bot-entry.js +1 -1
- package/dist/heart/daemon/process-manager.js +201 -0
- package/dist/heart/daemon/provider-discovery.js +105 -0
- package/dist/heart/daemon/pulse.js +463 -0
- package/dist/heart/daemon/run-hooks.js +2 -0
- package/dist/heart/daemon/runtime-logging.js +67 -16
- package/dist/heart/daemon/runtime-metadata.js +101 -0
- package/dist/heart/daemon/runtime-mode.js +67 -0
- package/dist/heart/daemon/safe-mode.js +161 -0
- package/dist/heart/daemon/sense-manager.js +72 -3
- package/dist/heart/daemon/session-id-resolver.js +131 -0
- package/dist/heart/daemon/skill-management-installer.js +94 -0
- package/dist/heart/daemon/socket-client.js +307 -0
- package/dist/heart/daemon/stale-bundle-prune.js +96 -0
- package/dist/heart/daemon/startup-tui.js +237 -0
- package/dist/heart/daemon/task-scheduler.js +3 -25
- package/dist/heart/daemon/thoughts.js +510 -0
- package/dist/heart/daemon/up-progress.js +135 -0
- package/dist/heart/delegation.js +62 -0
- package/dist/heart/habits/habit-migration.js +181 -0
- package/dist/heart/habits/habit-parser.js +140 -0
- package/dist/heart/habits/habit-scheduler.js +371 -0
- package/dist/heart/{daemon → hatch}/hatch-flow.js +52 -120
- package/dist/heart/{daemon → hatch}/hatch-specialist.js +3 -3
- package/dist/heart/{daemon → hatch}/specialist-prompt.js +10 -7
- package/dist/heart/{daemon → hatch}/specialist-tools.js +56 -10
- package/dist/heart/identity.js +154 -59
- package/dist/heart/kept-notes.js +357 -0
- package/dist/heart/kicks.js +2 -20
- package/dist/heart/machine-identity.js +161 -0
- package/dist/heart/mcp/mcp-server.js +653 -0
- package/dist/heart/migrate-config.js +127 -0
- package/dist/heart/model-capabilities.js +59 -0
- package/dist/heart/outlook/outlook-http-hooks.js +64 -0
- package/dist/heart/outlook/outlook-http-response.js +7 -0
- package/dist/heart/outlook/outlook-http-routes.js +232 -0
- package/dist/heart/outlook/outlook-http-static.js +99 -0
- package/dist/heart/outlook/outlook-http-transport.js +116 -0
- package/dist/heart/outlook/outlook-http.js +99 -0
- package/dist/heart/outlook/outlook-read.js +28 -0
- package/dist/heart/outlook/outlook-types.js +27 -0
- package/dist/heart/outlook/outlook-view.js +194 -0
- package/dist/heart/outlook/readers/agent-machine.js +355 -0
- package/dist/heart/outlook/readers/continuity-readers.js +332 -0
- package/dist/heart/outlook/readers/runtime-readers.js +660 -0
- package/dist/heart/outlook/readers/sessions.js +231 -0
- package/dist/heart/outlook/readers/shared.js +111 -0
- package/dist/heart/progress-story.js +42 -0
- package/dist/heart/provider-attempt.js +133 -0
- package/dist/heart/provider-binding-resolver.js +240 -0
- package/dist/heart/provider-credential-pool.js +395 -0
- package/dist/heart/provider-failover.js +135 -0
- package/dist/heart/provider-models.js +81 -0
- package/dist/heart/provider-ping.js +258 -0
- package/dist/heart/provider-state.js +208 -0
- package/dist/heart/providers/anthropic-token.js +163 -0
- package/dist/heart/providers/anthropic.js +171 -50
- package/dist/heart/providers/azure.js +97 -11
- package/dist/heart/providers/error-classification.js +63 -0
- package/dist/heart/providers/github-copilot.js +135 -0
- package/dist/heart/providers/minimax-vlm.js +189 -0
- package/dist/heart/providers/minimax.js +23 -6
- package/dist/heart/providers/openai-codex.js +33 -23
- package/dist/heart/session-activity.js +190 -0
- package/dist/heart/session-events.js +727 -0
- package/dist/heart/session-transcript.js +162 -0
- package/dist/heart/start-of-turn-packet.js +341 -0
- package/dist/heart/streaming.js +36 -27
- package/dist/heart/sync.js +332 -0
- package/dist/heart/target-resolution.js +127 -0
- package/dist/heart/tempo.js +93 -0
- package/dist/heart/temporal-view.js +41 -0
- package/dist/heart/tool-activity-callbacks.js +36 -0
- package/dist/heart/tool-description.js +135 -0
- package/dist/heart/tool-friction.js +55 -0
- package/dist/heart/tool-loop.js +200 -0
- package/dist/heart/turn-context.js +358 -0
- package/dist/heart/turn-coordinator.js +28 -0
- package/dist/heart/{daemon → versioning}/ouro-bot-global-installer.js +1 -1
- package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +1 -1
- package/dist/heart/versioning/ouro-path-installer.js +296 -0
- package/dist/heart/versioning/ouro-version-manager.js +295 -0
- package/dist/heart/{daemon → versioning}/staged-restart.js +40 -8
- package/dist/heart/{daemon → versioning}/update-checker.js +12 -2
- package/dist/heart/{daemon → versioning}/update-hooks.js +63 -59
- package/dist/mind/bundle-manifest.js +7 -1
- package/dist/mind/context.js +140 -94
- package/dist/mind/diary-integrity.js +60 -0
- package/dist/mind/{memory.js → diary.js} +84 -96
- package/dist/mind/embedding-provider.js +60 -0
- package/dist/mind/file-state.js +179 -0
- package/dist/mind/first-impressions.js +14 -1
- package/dist/mind/friends/channel.js +56 -0
- package/dist/mind/friends/group-context.js +144 -0
- package/dist/mind/friends/resolver.js +38 -1
- package/dist/mind/friends/store-file.js +58 -3
- package/dist/mind/friends/trust-explanation.js +74 -0
- package/dist/mind/friends/types.js +9 -1
- package/dist/mind/journal-index.js +161 -0
- package/dist/mind/note-search.js +268 -0
- package/dist/mind/obligation-steering.js +221 -0
- package/dist/mind/pending.js +74 -7
- package/dist/mind/prompt.js +1000 -112
- package/dist/mind/provenance-trust.js +26 -0
- package/dist/mind/scrutiny.js +173 -0
- package/dist/mind/token-estimate.js +8 -12
- package/dist/nerves/cli-logging.js +7 -1
- package/dist/nerves/coverage/audit-rules.js +15 -6
- package/dist/nerves/coverage/audit.js +28 -2
- package/dist/nerves/coverage/cli.js +1 -1
- package/dist/nerves/coverage/file-completeness.js +83 -5
- package/dist/nerves/coverage/run-artifacts.js +1 -1
- package/dist/nerves/event-buffer.js +111 -0
- package/dist/nerves/index.js +224 -4
- package/dist/nerves/observation.js +20 -0
- package/dist/nerves/redact.js +79 -0
- package/dist/nerves/runtime.js +5 -1
- package/dist/outlook-ui/assets/index-LwChZTgL.css +1 -0
- package/dist/outlook-ui/assets/index-xTdv64BV.js +61 -0
- package/dist/outlook-ui/index.html +15 -0
- package/dist/repertoire/ado-client.js +15 -56
- package/dist/repertoire/ado-semantic.js +11 -10
- package/dist/repertoire/api-client.js +97 -0
- package/dist/repertoire/bitwarden-store.js +319 -0
- package/dist/repertoire/bundle-templates.js +72 -0
- package/dist/repertoire/bw-installer.js +79 -0
- package/dist/repertoire/coding/codex-jsonl.js +64 -0
- package/dist/repertoire/coding/context-pack.js +330 -0
- package/dist/repertoire/coding/feedback.js +197 -30
- package/dist/repertoire/coding/manager.js +158 -9
- package/dist/repertoire/coding/spawner.js +55 -9
- package/dist/repertoire/coding/tools.js +170 -7
- package/dist/repertoire/commerce-errors.js +109 -0
- package/dist/repertoire/commerce-self-test.js +156 -0
- package/dist/repertoire/credential-access.js +527 -0
- package/dist/repertoire/duffel-client.js +185 -0
- package/dist/repertoire/github-client.js +14 -55
- package/dist/repertoire/graph-client.js +11 -52
- package/dist/repertoire/guardrails.js +375 -0
- package/dist/repertoire/mcp-client.js +255 -0
- package/dist/repertoire/mcp-manager.js +305 -0
- package/dist/repertoire/mcp-tools.js +63 -0
- package/dist/repertoire/shell-sessions.js +133 -0
- package/dist/repertoire/skills.js +15 -24
- package/dist/repertoire/stripe-client.js +131 -0
- package/dist/repertoire/tasks/board.js +43 -5
- package/dist/repertoire/tasks/fix.js +182 -0
- package/dist/repertoire/tasks/index.js +28 -10
- package/dist/repertoire/tasks/lifecycle.js +2 -2
- package/dist/repertoire/tasks/parser.js +3 -2
- package/dist/repertoire/tasks/scanner.js +194 -37
- package/dist/repertoire/tasks/transitions.js +16 -79
- package/dist/repertoire/tool-results.js +29 -0
- package/dist/repertoire/tools-attachments.js +316 -0
- package/dist/repertoire/tools-base.js +45 -771
- package/dist/repertoire/tools-bluebubbles.js +1 -0
- package/dist/repertoire/tools-bridge.js +141 -0
- package/dist/repertoire/tools-bundle.js +984 -0
- package/dist/repertoire/tools-config.js +185 -0
- package/dist/repertoire/tools-continuity.js +248 -0
- package/dist/repertoire/tools-credential.js +182 -0
- package/dist/repertoire/tools-files.js +342 -0
- package/dist/repertoire/tools-flight.js +224 -0
- package/dist/repertoire/tools-flow.js +105 -0
- package/dist/repertoire/tools-github.js +1 -7
- package/dist/repertoire/tools-notes.js +376 -0
- package/dist/repertoire/tools-session.js +739 -0
- package/dist/repertoire/tools-shell.js +120 -0
- package/dist/repertoire/tools-stripe.js +180 -0
- package/dist/repertoire/tools-surface.js +243 -0
- package/dist/repertoire/tools-teams.js +12 -62
- package/dist/repertoire/tools-travel.js +125 -0
- package/dist/repertoire/tools-user-profile.js +144 -0
- package/dist/repertoire/tools-vault.js +110 -0
- package/dist/repertoire/tools.js +144 -138
- package/dist/repertoire/travel-api-client.js +360 -0
- package/dist/repertoire/user-profile.js +118 -0
- package/dist/repertoire/vault-setup.js +241 -0
- package/dist/scripts/claude-code-hook.js +41 -0
- package/dist/scripts/claude-code-stop-hook.js +47 -0
- package/dist/senses/attention-queue.js +116 -0
- package/dist/senses/bluebubbles/attachment-cache.js +53 -0
- package/dist/senses/bluebubbles/attachment-download.js +137 -0
- package/dist/senses/{bluebubbles-client.js → bluebubbles/client.js} +225 -9
- package/dist/senses/bluebubbles/entry.js +13 -0
- package/dist/senses/bluebubbles/inbound-log.js +113 -0
- package/dist/senses/bluebubbles/index.js +1590 -0
- package/dist/senses/{bluebubbles-media.js → bluebubbles/media.js} +121 -70
- package/dist/senses/{bluebubbles-model.js → bluebubbles/model.js} +43 -12
- package/dist/senses/{bluebubbles-mutation-log.js → bluebubbles/mutation-log.js} +46 -6
- package/dist/senses/bluebubbles/replay.js +129 -0
- package/dist/senses/bluebubbles/runtime-state.js +109 -0
- package/dist/senses/{bluebubbles-session-cleanup.js → bluebubbles/session-cleanup.js} +1 -1
- package/dist/senses/cli/bracketed-paste.js +82 -0
- package/dist/senses/cli/image-paste.js +287 -0
- package/dist/senses/cli/image-ref-navigation.js +75 -0
- package/dist/senses/cli/ink-app.js +156 -0
- package/dist/senses/cli/inline-diff.js +64 -0
- package/dist/senses/cli/input-keys.js +174 -0
- package/dist/senses/cli/kill-ring.js +86 -0
- package/dist/senses/cli/message-list.js +51 -0
- package/dist/senses/cli/ouro-tui.js +605 -0
- package/dist/senses/cli/spinner-imperative.js +135 -0
- package/dist/senses/cli/spinner.js +101 -0
- package/dist/senses/cli/status-line.js +60 -0
- package/dist/senses/cli/streaming-markdown.js +526 -0
- package/dist/senses/cli/tool-display.js +83 -0
- package/dist/senses/cli/tool-render.js +85 -0
- package/dist/senses/cli/tui-store.js +240 -0
- package/dist/senses/cli/virtual-list.js +35 -0
- package/dist/senses/cli-entry.js +1 -1
- package/dist/senses/cli-layout.js +187 -0
- package/dist/senses/cli.js +603 -246
- package/dist/senses/commands.js +65 -1
- package/dist/senses/continuity.js +94 -0
- package/dist/senses/habit-turn-message.js +108 -0
- package/dist/senses/inner-dialog-worker.js +112 -19
- package/dist/senses/inner-dialog.js +633 -86
- package/dist/senses/pipeline.js +567 -0
- package/dist/senses/shared-turn.js +199 -0
- package/dist/senses/surface-tool.js +68 -0
- package/dist/senses/teams.js +665 -160
- package/dist/senses/trust-gate.js +112 -2
- package/package.json +29 -7
- package/skills/agent-commerce.md +106 -0
- package/skills/browser-navigation.md +110 -0
- package/skills/commerce-setup-guide.md +116 -0
- package/skills/commerce-setup.md +84 -0
- package/skills/configure-dev-tools.md +81 -0
- package/skills/travel-planning.md +138 -0
- package/dist/heart/daemon/ouro-path-installer.js +0 -178
- package/dist/heart/daemon/subagent-installer.js +0 -134
- package/dist/mind/associative-recall.js +0 -197
- package/dist/senses/bluebubbles-entry.js +0 -11
- package/dist/senses/bluebubbles.js +0 -548
- package/dist/senses/debug-activity.js +0 -127
- package/subagents/README.md +0 -73
- package/subagents/work-doer.md +0 -235
- package/subagents/work-merger.md +0 -618
- package/subagents/work-planner.md +0 -382
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/python.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
- /package/dist/heart/{daemon → hatch}/hatch-animation.js +0 -0
- /package/dist/heart/{daemon → hatch}/specialist-orchestrator.js +0 -0
- /package/dist/heart/{daemon → versioning}/ouro-uti.js +0 -0
- /package/dist/heart/{daemon → versioning}/wrapper-publish-guard.js +0 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildFailoverContext = buildFailoverContext;
|
|
4
|
+
exports.handleFailoverReply = handleFailoverReply;
|
|
5
|
+
const provider_models_1 = require("./provider-models");
|
|
6
|
+
const runtime_1 = require("../nerves/runtime");
|
|
7
|
+
const CLASSIFICATION_LABELS = {
|
|
8
|
+
"auth-failure": "authentication failed",
|
|
9
|
+
"usage-limit": "hit its usage limit",
|
|
10
|
+
"rate-limit": "is being rate limited",
|
|
11
|
+
"server-error": "is experiencing an outage",
|
|
12
|
+
"network-error": "is unreachable (network error)",
|
|
13
|
+
"unknown": "encountered an error",
|
|
14
|
+
};
|
|
15
|
+
function formatProviderWithModel(provider, model) {
|
|
16
|
+
if (!model)
|
|
17
|
+
return provider;
|
|
18
|
+
if ((0, provider_models_1.getProviderModelMismatchMessage)(provider, model)) {
|
|
19
|
+
return `${provider} [configured model: ${model}]`;
|
|
20
|
+
}
|
|
21
|
+
return `${provider} (${model})`;
|
|
22
|
+
}
|
|
23
|
+
function formatErrorDetail(errorMessage, errorSummary) {
|
|
24
|
+
const detail = errorMessage.replace(/\s+/g, " ").trim();
|
|
25
|
+
if (!detail || detail === errorSummary)
|
|
26
|
+
return "";
|
|
27
|
+
return detail.length > 300 ? `${detail.slice(0, 297)}...` : detail;
|
|
28
|
+
}
|
|
29
|
+
function formatFailingProviderLine(provider, classification, agentName) {
|
|
30
|
+
const authCommand = `ouro auth --agent ${agentName} --provider ${provider}`;
|
|
31
|
+
switch (classification) {
|
|
32
|
+
case "auth-failure":
|
|
33
|
+
return ` - ${provider}: credentials need to be refreshed. Run \`${authCommand}\`.`;
|
|
34
|
+
case "network-error":
|
|
35
|
+
return ` - ${provider}: could not be reached. Check network/provider availability; if credentials may be stale, run \`${authCommand}\`.`;
|
|
36
|
+
case "server-error":
|
|
37
|
+
return ` - ${provider}: provider outage or server error. Retry later; if it keeps failing, run \`${authCommand}\`.`;
|
|
38
|
+
case "rate-limit":
|
|
39
|
+
return ` - ${provider}: rate limited. Wait and retry, or switch to a ready provider below.`;
|
|
40
|
+
case "usage-limit":
|
|
41
|
+
return ` - ${provider}: usage limit hit. Wait for quota reset, raise quota, or switch to a ready provider below.`;
|
|
42
|
+
case "unknown":
|
|
43
|
+
return ` - ${provider}: could not be reached. Run \`${authCommand}\` if credentials may be stale.`;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function buildFailoverContext(errorMessage, classification, currentProvider, currentModel, agentName, inventory, providerModels) {
|
|
47
|
+
const label = CLASSIFICATION_LABELS[classification];
|
|
48
|
+
const providerWithModel = formatProviderWithModel(currentProvider, currentModel);
|
|
49
|
+
const errorSummary = `${providerWithModel} ${label}`;
|
|
50
|
+
const errorDetail = formatErrorDetail(errorMessage, errorSummary);
|
|
51
|
+
const modelMismatch = (0, provider_models_1.getProviderModelMismatchMessage)(currentProvider, currentModel);
|
|
52
|
+
const workingProviders = [];
|
|
53
|
+
const unconfiguredProviders = [];
|
|
54
|
+
const failingProviders = [];
|
|
55
|
+
for (const [provider, result] of Object.entries(inventory)) {
|
|
56
|
+
if (result.ok) {
|
|
57
|
+
workingProviders.push(provider);
|
|
58
|
+
}
|
|
59
|
+
else if (result.classification === "auth-failure" && result.message === "no credentials configured") {
|
|
60
|
+
unconfiguredProviders.push(provider);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
// Configured but ping failed (expired token, provider also down, etc.)
|
|
64
|
+
failingProviders.push({ provider, classification: result.classification });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
const lines = [`${errorSummary}.`];
|
|
68
|
+
if (errorDetail) {
|
|
69
|
+
lines.push(`provider detail: ${errorDetail}`);
|
|
70
|
+
}
|
|
71
|
+
if (classification === "auth-failure") {
|
|
72
|
+
lines.push("");
|
|
73
|
+
lines.push("To keep using the current provider:");
|
|
74
|
+
lines.push(` 1. Run \`ouro auth --agent ${agentName} --provider ${currentProvider}\``);
|
|
75
|
+
}
|
|
76
|
+
if (modelMismatch) {
|
|
77
|
+
const defaultModel = (0, provider_models_1.getDefaultModelForProvider)(currentProvider);
|
|
78
|
+
lines.push("");
|
|
79
|
+
lines.push("Config warning:");
|
|
80
|
+
lines.push(` - ${modelMismatch}`);
|
|
81
|
+
lines.push(" - Repair the configured model with:");
|
|
82
|
+
lines.push(` \`ouro config model --agent ${agentName} --facing human ${defaultModel}\``);
|
|
83
|
+
lines.push(` \`ouro config model --agent ${agentName} --facing agent ${defaultModel}\``);
|
|
84
|
+
}
|
|
85
|
+
if (workingProviders.length > 0) {
|
|
86
|
+
lines.push("");
|
|
87
|
+
lines.push("Ready providers:");
|
|
88
|
+
for (const provider of workingProviders) {
|
|
89
|
+
const model = (0, provider_models_1.resolveModelForProviderDisplay)(provider, providerModels[provider]);
|
|
90
|
+
lines.push(` - ${provider} (${model}): reply "switch to ${provider}"`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (failingProviders.length > 0) {
|
|
94
|
+
lines.push("");
|
|
95
|
+
lines.push("Configured but unavailable:");
|
|
96
|
+
for (const { provider, classification } of failingProviders) {
|
|
97
|
+
lines.push(formatFailingProviderLine(provider, classification, agentName));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (unconfiguredProviders.length > 0) {
|
|
101
|
+
lines.push("");
|
|
102
|
+
lines.push("Not configured:");
|
|
103
|
+
for (const provider of unconfiguredProviders) {
|
|
104
|
+
lines.push(` - ${provider}: run \`ouro auth --agent ${agentName} --provider ${provider}\``);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (workingProviders.length === 0 && unconfiguredProviders.length === 0 && failingProviders.length === 0) {
|
|
108
|
+
lines.push("");
|
|
109
|
+
lines.push(`No other providers are available. Run \`ouro auth --agent ${agentName}\` in terminal to configure one.`);
|
|
110
|
+
}
|
|
111
|
+
(0, runtime_1.emitNervesEvent)({
|
|
112
|
+
component: "engine",
|
|
113
|
+
event: "engine.failover_context_built",
|
|
114
|
+
message: "built provider failover context",
|
|
115
|
+
meta: { currentProvider, classification, workingProviders, unconfiguredProviders },
|
|
116
|
+
});
|
|
117
|
+
return {
|
|
118
|
+
errorSummary,
|
|
119
|
+
classification,
|
|
120
|
+
currentProvider,
|
|
121
|
+
agentName,
|
|
122
|
+
workingProviders,
|
|
123
|
+
unconfiguredProviders,
|
|
124
|
+
userMessage: lines.join("\n"),
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
function handleFailoverReply(reply, context) {
|
|
128
|
+
const lower = reply.toLowerCase().trim();
|
|
129
|
+
for (const provider of context.workingProviders) {
|
|
130
|
+
if (lower.includes(`switch to ${provider}`) || lower === provider) {
|
|
131
|
+
return { action: "switch", provider };
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return { action: "dismiss" };
|
|
135
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_PROVIDER_MODELS = void 0;
|
|
4
|
+
exports.getProviderDisplayName = getProviderDisplayName;
|
|
5
|
+
exports.getDefaultModelForProvider = getDefaultModelForProvider;
|
|
6
|
+
exports.isModelClearlyIncompatibleWithProvider = isModelClearlyIncompatibleWithProvider;
|
|
7
|
+
exports.resolveModelForProviderSelection = resolveModelForProviderSelection;
|
|
8
|
+
exports.resolveModelForProviderDisplay = resolveModelForProviderDisplay;
|
|
9
|
+
exports.getProviderModelMismatchMessage = getProviderModelMismatchMessage;
|
|
10
|
+
const runtime_1 = require("../nerves/runtime");
|
|
11
|
+
exports.DEFAULT_PROVIDER_MODELS = {
|
|
12
|
+
anthropic: "claude-opus-4-6",
|
|
13
|
+
azure: "gpt-4o-mini",
|
|
14
|
+
minimax: "MiniMax-M2.7",
|
|
15
|
+
"openai-codex": "gpt-5.4",
|
|
16
|
+
"github-copilot": "claude-sonnet-4.6",
|
|
17
|
+
};
|
|
18
|
+
const PROVIDER_NAMES = {
|
|
19
|
+
anthropic: "Anthropic",
|
|
20
|
+
azure: "Azure OpenAI",
|
|
21
|
+
minimax: "MiniMax",
|
|
22
|
+
"openai-codex": "OpenAI Codex",
|
|
23
|
+
"github-copilot": "GitHub Copilot",
|
|
24
|
+
};
|
|
25
|
+
function normalized(model) {
|
|
26
|
+
return model.trim().toLowerCase();
|
|
27
|
+
}
|
|
28
|
+
function getProviderDisplayName(provider) {
|
|
29
|
+
return PROVIDER_NAMES[provider];
|
|
30
|
+
}
|
|
31
|
+
function getDefaultModelForProvider(provider) {
|
|
32
|
+
return exports.DEFAULT_PROVIDER_MODELS[provider];
|
|
33
|
+
}
|
|
34
|
+
function isModelClearlyIncompatibleWithProvider(provider, model) {
|
|
35
|
+
const value = normalized(model);
|
|
36
|
+
if (!value)
|
|
37
|
+
return true;
|
|
38
|
+
switch (provider) {
|
|
39
|
+
case "anthropic":
|
|
40
|
+
return !value.startsWith("claude-");
|
|
41
|
+
case "minimax":
|
|
42
|
+
return !value.startsWith("minimax");
|
|
43
|
+
case "openai-codex":
|
|
44
|
+
return value.startsWith("claude-") || value.startsWith("minimax");
|
|
45
|
+
case "azure":
|
|
46
|
+
return value.startsWith("claude-") || value.startsWith("minimax");
|
|
47
|
+
case "github-copilot":
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function resolveModelForProviderSelection(provider, currentModel) {
|
|
52
|
+
const trimmed = currentModel.trim();
|
|
53
|
+
if (trimmed && !isModelClearlyIncompatibleWithProvider(provider, trimmed)) {
|
|
54
|
+
return { model: trimmed, preserved: true };
|
|
55
|
+
}
|
|
56
|
+
const model = getDefaultModelForProvider(provider);
|
|
57
|
+
(0, runtime_1.emitNervesEvent)({
|
|
58
|
+
component: "config/identity",
|
|
59
|
+
event: "config_identity.provider_model_defaulted",
|
|
60
|
+
message: "defaulted provider model during provider selection",
|
|
61
|
+
meta: { provider, previousModel: currentModel, model },
|
|
62
|
+
});
|
|
63
|
+
return { model, preserved: false };
|
|
64
|
+
}
|
|
65
|
+
function resolveModelForProviderDisplay(provider, modelHint) {
|
|
66
|
+
const hint = modelHint?.trim() ?? "";
|
|
67
|
+
if (hint && !isModelClearlyIncompatibleWithProvider(provider, hint))
|
|
68
|
+
return hint;
|
|
69
|
+
return getDefaultModelForProvider(provider);
|
|
70
|
+
}
|
|
71
|
+
function getProviderModelMismatchMessage(provider, model) {
|
|
72
|
+
const trimmed = model.trim();
|
|
73
|
+
if (!isModelClearlyIncompatibleWithProvider(provider, trimmed))
|
|
74
|
+
return null;
|
|
75
|
+
const providerName = getProviderDisplayName(provider);
|
|
76
|
+
const defaultModel = getDefaultModelForProvider(provider);
|
|
77
|
+
if (!trimmed) {
|
|
78
|
+
return `${providerName} has no model set. Suggested model: ${defaultModel}.`;
|
|
79
|
+
}
|
|
80
|
+
return `${providerName} is currently paired with ${trimmed}, which does not look like a model for ${providerName}. Suggested model: ${defaultModel}.`;
|
|
81
|
+
}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sanitizeErrorMessage = sanitizeErrorMessage;
|
|
4
|
+
exports.pingGithubCopilotModel = pingGithubCopilotModel;
|
|
5
|
+
exports.pingProvider = pingProvider;
|
|
6
|
+
exports.runHealthInventory = runHealthInventory;
|
|
7
|
+
const identity_1 = require("./identity");
|
|
8
|
+
const anthropic_1 = require("./providers/anthropic");
|
|
9
|
+
const azure_1 = require("./providers/azure");
|
|
10
|
+
const minimax_1 = require("./providers/minimax");
|
|
11
|
+
const openai_codex_1 = require("./providers/openai-codex");
|
|
12
|
+
const github_copilot_1 = require("./providers/github-copilot");
|
|
13
|
+
const auth_flow_1 = require("./auth/auth-flow");
|
|
14
|
+
const provider_models_1 = require("./provider-models");
|
|
15
|
+
const runtime_1 = require("../nerves/runtime");
|
|
16
|
+
const provider_attempt_1 = require("./provider-attempt");
|
|
17
|
+
const PING_TIMEOUT_MS = 10_000;
|
|
18
|
+
const PING_PROMPT = "ping";
|
|
19
|
+
const CHAT_PING_MAX_TOKENS = 1;
|
|
20
|
+
const RESPONSE_PING_MAX_OUTPUT_TOKENS = 16;
|
|
21
|
+
const ANTHROPIC_SETUP_PING_MODEL = "claude-haiku-4-5-20251001";
|
|
22
|
+
const DEFAULT_AZURE_API_VERSION = "2025-04-01-preview";
|
|
23
|
+
const PING_CALLBACKS = {
|
|
24
|
+
onModelStart() { },
|
|
25
|
+
onModelStreamStart() { },
|
|
26
|
+
onTextChunk() { },
|
|
27
|
+
onReasoningChunk() { },
|
|
28
|
+
onToolStart() { },
|
|
29
|
+
onToolEnd() { },
|
|
30
|
+
onError() { },
|
|
31
|
+
};
|
|
32
|
+
function createPingMessages() {
|
|
33
|
+
return [{ role: "user", content: PING_PROMPT }];
|
|
34
|
+
}
|
|
35
|
+
function createChatPingRequest(model) {
|
|
36
|
+
return { model, max_tokens: CHAT_PING_MAX_TOKENS, messages: createPingMessages() };
|
|
37
|
+
}
|
|
38
|
+
function createResponsePingRequest(model) {
|
|
39
|
+
return { model, input: PING_PROMPT, max_output_tokens: RESPONSE_PING_MAX_OUTPUT_TOKENS };
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Strip raw JSON/HTML API response bodies from error messages.
|
|
43
|
+
* SDK errors often include the full response: "400 {"type":"error",...}" or "403 <html>...".
|
|
44
|
+
* Extract just the HTTP status and a short human-readable summary.
|
|
45
|
+
*/
|
|
46
|
+
function sanitizeErrorMessage(message) {
|
|
47
|
+
const statusMatch = message.match(/^(\d{3})\s/);
|
|
48
|
+
if (!statusMatch)
|
|
49
|
+
return message;
|
|
50
|
+
const status = statusMatch[1];
|
|
51
|
+
const body = message.slice(status.length).trim();
|
|
52
|
+
// HTML response (Cloudflare challenge, error pages, etc.)
|
|
53
|
+
if (body.startsWith("<") || body.includes("<!DOCTYPE") || body.includes("<html")) {
|
|
54
|
+
return `HTTP ${status}`;
|
|
55
|
+
}
|
|
56
|
+
// JSON response
|
|
57
|
+
if (body.startsWith("{")) {
|
|
58
|
+
try {
|
|
59
|
+
const json = JSON.parse(body);
|
|
60
|
+
const inner = json?.error?.message;
|
|
61
|
+
if (typeof inner === "string" && inner && inner !== "Error") {
|
|
62
|
+
return `${status} ${inner}`;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
catch { /* not valid JSON */ }
|
|
66
|
+
return `HTTP ${status}`;
|
|
67
|
+
}
|
|
68
|
+
// Already clean (e.g., "401 Provided authentication token is expired.")
|
|
69
|
+
return message;
|
|
70
|
+
}
|
|
71
|
+
async function readGithubCopilotModelPingError(response) {
|
|
72
|
+
let detail = `HTTP ${response.status}`;
|
|
73
|
+
try {
|
|
74
|
+
const json = await response.json();
|
|
75
|
+
/* v8 ignore start -- error format parsing: all branches tested via config-models.test.ts @preserve */
|
|
76
|
+
if (typeof json.error === "string")
|
|
77
|
+
detail = json.error;
|
|
78
|
+
else if (typeof json.error === "object" && json.error !== null) {
|
|
79
|
+
const errObj = json.error;
|
|
80
|
+
if (typeof errObj.message === "string")
|
|
81
|
+
detail = errObj.message;
|
|
82
|
+
}
|
|
83
|
+
else if (typeof json.message === "string")
|
|
84
|
+
detail = json.message;
|
|
85
|
+
/* v8 ignore stop */
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
// response body not JSON — keep HTTP status
|
|
89
|
+
}
|
|
90
|
+
return detail;
|
|
91
|
+
}
|
|
92
|
+
function createStatusError(message, status) {
|
|
93
|
+
return Object.assign(new Error(message), { status });
|
|
94
|
+
}
|
|
95
|
+
async function pingGithubCopilotModel(baseUrl, token, model, fetchImpl = fetch) {
|
|
96
|
+
const base = baseUrl.replace(/\/+$/, "");
|
|
97
|
+
const isClaude = model.startsWith("claude");
|
|
98
|
+
const url = isClaude ? `${base}/chat/completions` : `${base}/responses`;
|
|
99
|
+
const body = isClaude
|
|
100
|
+
? JSON.stringify(createChatPingRequest(model))
|
|
101
|
+
: JSON.stringify(createResponsePingRequest(model));
|
|
102
|
+
const attempt = await (0, provider_attempt_1.runProviderAttempt)({
|
|
103
|
+
operation: "model-ping",
|
|
104
|
+
provider: "github-copilot",
|
|
105
|
+
model,
|
|
106
|
+
classifyError: github_copilot_1.classifyGithubCopilotError,
|
|
107
|
+
policy: {
|
|
108
|
+
maxAttempts: 3,
|
|
109
|
+
baseDelayMs: 0,
|
|
110
|
+
backoffMultiplier: 2,
|
|
111
|
+
},
|
|
112
|
+
run: async () => {
|
|
113
|
+
const response = await fetchImpl(url, {
|
|
114
|
+
method: "POST",
|
|
115
|
+
headers: {
|
|
116
|
+
Authorization: `Bearer ${token}`,
|
|
117
|
+
"Content-Type": "application/json",
|
|
118
|
+
},
|
|
119
|
+
body,
|
|
120
|
+
});
|
|
121
|
+
if (!response.ok) {
|
|
122
|
+
throw createStatusError(await readGithubCopilotModelPingError(response), response.status);
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
return attempt.ok ? { ok: true } : { ok: false, error: attempt.error.message };
|
|
127
|
+
}
|
|
128
|
+
function hasEmptyCredentials(provider, config) {
|
|
129
|
+
const record = config;
|
|
130
|
+
if (provider === "azure") {
|
|
131
|
+
const hasManagedIdentity = typeof record.endpoint === "string" && record.endpoint.length > 0 &&
|
|
132
|
+
typeof record.deployment === "string" && record.deployment.length > 0 &&
|
|
133
|
+
typeof record.managedIdentityClientId === "string" && record.managedIdentityClientId.length > 0;
|
|
134
|
+
if (hasManagedIdentity)
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
return identity_1.PROVIDER_CREDENTIALS[provider].required.some((key) => !record[key]);
|
|
138
|
+
}
|
|
139
|
+
function createRuntimeForPing(provider, config, model) {
|
|
140
|
+
// Use the same provider defaults as auth switch and hatch so verification
|
|
141
|
+
// cannot drift to stale provider/model pairings, and pass the checked
|
|
142
|
+
// credentials directly so daemon-side pings do not depend on --agent globals.
|
|
143
|
+
const resolvedModel = model ?? (0, provider_models_1.getDefaultModelForProvider)(provider);
|
|
144
|
+
switch (provider) {
|
|
145
|
+
case "anthropic":
|
|
146
|
+
return (0, anthropic_1.createAnthropicProviderRuntime)(resolvedModel, config);
|
|
147
|
+
case "azure":
|
|
148
|
+
return (0, azure_1.createAzureProviderRuntime)(resolvedModel, {
|
|
149
|
+
...config,
|
|
150
|
+
apiVersion: config.apiVersion ?? DEFAULT_AZURE_API_VERSION,
|
|
151
|
+
});
|
|
152
|
+
case "minimax":
|
|
153
|
+
return (0, minimax_1.createMinimaxProviderRuntime)(resolvedModel, config);
|
|
154
|
+
case "openai-codex":
|
|
155
|
+
return (0, openai_codex_1.createOpenAICodexProviderRuntime)(resolvedModel, config);
|
|
156
|
+
case "github-copilot":
|
|
157
|
+
return (0, github_copilot_1.createGithubCopilotProviderRuntime)(resolvedModel, config);
|
|
158
|
+
/* v8 ignore next 2 -- exhaustive: all providers handled above @preserve */
|
|
159
|
+
default:
|
|
160
|
+
throw new Error(`unsupported provider for ping: ${provider}`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
async function pingProvider(provider, config, options = {}) {
|
|
164
|
+
if (hasEmptyCredentials(provider, config)) {
|
|
165
|
+
return { ok: false, classification: "auth-failure", message: "no credentials configured" };
|
|
166
|
+
}
|
|
167
|
+
let runtime;
|
|
168
|
+
try {
|
|
169
|
+
runtime = createRuntimeForPing(provider, config, options.model);
|
|
170
|
+
/* v8 ignore start -- factory creation failure: tested via individual provider init tests @preserve */
|
|
171
|
+
}
|
|
172
|
+
catch (error) {
|
|
173
|
+
return {
|
|
174
|
+
ok: false,
|
|
175
|
+
classification: "auth-failure",
|
|
176
|
+
message: error instanceof Error ? error.message : String(error),
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
/* v8 ignore stop */
|
|
180
|
+
const attempt = await (0, provider_attempt_1.runProviderAttempt)({
|
|
181
|
+
operation: "ping",
|
|
182
|
+
provider,
|
|
183
|
+
model: runtime.model,
|
|
184
|
+
classifyError: (error) => runtime.classifyError(error),
|
|
185
|
+
policy: {
|
|
186
|
+
maxAttempts: 3,
|
|
187
|
+
baseDelayMs: 0,
|
|
188
|
+
backoffMultiplier: 2,
|
|
189
|
+
...options.attemptPolicy,
|
|
190
|
+
},
|
|
191
|
+
sleep: options.sleep,
|
|
192
|
+
run: async () => {
|
|
193
|
+
const controller = new AbortController();
|
|
194
|
+
/* v8 ignore next -- timeout callback: only fires after 10s, tests resolve faster @preserve */
|
|
195
|
+
const timeout = setTimeout(() => controller.abort(), PING_TIMEOUT_MS);
|
|
196
|
+
try {
|
|
197
|
+
// Minimal API call — no thinking, no reasoning, no tools.
|
|
198
|
+
if (provider === "anthropic") {
|
|
199
|
+
// Use haiku for the ping — setup tokens may not have access to newer
|
|
200
|
+
// models, but if haiku works, the credentials are valid.
|
|
201
|
+
// Override the beta header to exclude thinking (which requires a
|
|
202
|
+
// thinking param in the request body).
|
|
203
|
+
const client = runtime.client;
|
|
204
|
+
await client.messages.create(createChatPingRequest(ANTHROPIC_SETUP_PING_MODEL), { signal: controller.signal, headers: { "anthropic-beta": "claude-code-20250219,oauth-2025-04-20" } });
|
|
205
|
+
}
|
|
206
|
+
else if (provider === "openai-codex") {
|
|
207
|
+
await runtime.streamTurn({
|
|
208
|
+
messages: createPingMessages(),
|
|
209
|
+
activeTools: [],
|
|
210
|
+
callbacks: PING_CALLBACKS,
|
|
211
|
+
signal: controller.signal,
|
|
212
|
+
toolChoiceRequired: false,
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
// OpenAI-compatible providers (azure, minimax, github-copilot)
|
|
217
|
+
const client = runtime.client;
|
|
218
|
+
await client.chat.completions.create(createChatPingRequest(runtime.model), { signal: controller.signal });
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
finally {
|
|
222
|
+
clearTimeout(timeout);
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
});
|
|
226
|
+
if (attempt.ok) {
|
|
227
|
+
return { ok: true, attempts: attempt.attempts };
|
|
228
|
+
}
|
|
229
|
+
(0, runtime_1.emitNervesEvent)({
|
|
230
|
+
component: "engine",
|
|
231
|
+
event: "engine.provider_ping_fail",
|
|
232
|
+
message: `provider ping failed: ${provider}`,
|
|
233
|
+
meta: { provider, classification: attempt.classification, error: attempt.error.message },
|
|
234
|
+
});
|
|
235
|
+
return {
|
|
236
|
+
ok: false,
|
|
237
|
+
classification: attempt.classification,
|
|
238
|
+
message: sanitizeErrorMessage(attempt.error.message),
|
|
239
|
+
attempts: attempt.attempts,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
const PINGABLE_PROVIDERS = ["anthropic", "openai-codex", "azure", "minimax", "github-copilot"];
|
|
243
|
+
async function runHealthInventory(agentName, currentProvider, deps = {}) {
|
|
244
|
+
/* v8 ignore next -- default: tests inject ping dep @preserve */
|
|
245
|
+
const ping = deps.ping ?? pingProvider;
|
|
246
|
+
const { secrets } = (0, auth_flow_1.loadAgentSecrets)(agentName);
|
|
247
|
+
const providers = PINGABLE_PROVIDERS.filter((p) => p !== currentProvider);
|
|
248
|
+
const results = await Promise.all(providers.map(async (provider) => {
|
|
249
|
+
const config = secrets.providers[provider];
|
|
250
|
+
const result = await ping(provider, config);
|
|
251
|
+
return [provider, result];
|
|
252
|
+
}));
|
|
253
|
+
const inventory = {};
|
|
254
|
+
for (const [provider, result] of results) {
|
|
255
|
+
inventory[provider] = result;
|
|
256
|
+
}
|
|
257
|
+
return inventory;
|
|
258
|
+
}
|
|
@@ -0,0 +1,208 @@
|
|
|
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.validateProviderState = validateProviderState;
|
|
37
|
+
exports.getProviderStatePath = getProviderStatePath;
|
|
38
|
+
exports.readProviderState = readProviderState;
|
|
39
|
+
exports.writeProviderState = writeProviderState;
|
|
40
|
+
exports.bootstrapProviderStateFromAgentConfig = bootstrapProviderStateFromAgentConfig;
|
|
41
|
+
const fs = __importStar(require("fs"));
|
|
42
|
+
const path = __importStar(require("path"));
|
|
43
|
+
const runtime_1 = require("../nerves/runtime");
|
|
44
|
+
const identity_1 = require("./identity");
|
|
45
|
+
const LANES = ["outward", "inner"];
|
|
46
|
+
const VALID_SOURCES = new Set(["bootstrap", "local"]);
|
|
47
|
+
const VALID_READINESS = new Set(["ready", "failed", "stale", "unknown"]);
|
|
48
|
+
function isProvider(value) {
|
|
49
|
+
return typeof value === "string" && Object.prototype.hasOwnProperty.call(identity_1.PROVIDER_CREDENTIALS, value);
|
|
50
|
+
}
|
|
51
|
+
function isNonEmptyString(value) {
|
|
52
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
53
|
+
}
|
|
54
|
+
function validateBinding(value, label) {
|
|
55
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
56
|
+
throw new Error(`${label} must be an object`);
|
|
57
|
+
}
|
|
58
|
+
const record = value;
|
|
59
|
+
if (!isProvider(record.provider))
|
|
60
|
+
throw new Error(`${label}.provider must be a valid provider`);
|
|
61
|
+
if (!isNonEmptyString(record.model))
|
|
62
|
+
throw new Error(`${label}.model must be a non-empty string`);
|
|
63
|
+
if (!VALID_SOURCES.has(record.source)) {
|
|
64
|
+
throw new Error(`${label}.source must be bootstrap or local`);
|
|
65
|
+
}
|
|
66
|
+
if (!isNonEmptyString(record.updatedAt))
|
|
67
|
+
throw new Error(`${label}.updatedAt must be a non-empty string`);
|
|
68
|
+
return {
|
|
69
|
+
provider: record.provider,
|
|
70
|
+
model: record.model,
|
|
71
|
+
source: record.source,
|
|
72
|
+
updatedAt: record.updatedAt,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function validateReadiness(value, label) {
|
|
76
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
77
|
+
throw new Error(`${label} must be an object`);
|
|
78
|
+
}
|
|
79
|
+
const record = value;
|
|
80
|
+
if (!VALID_READINESS.has(record.status)) {
|
|
81
|
+
throw new Error(`${label}.status must be ready, failed, stale, or unknown`);
|
|
82
|
+
}
|
|
83
|
+
if (!isProvider(record.provider))
|
|
84
|
+
throw new Error(`${label}.provider must be a valid provider`);
|
|
85
|
+
if (!isNonEmptyString(record.model))
|
|
86
|
+
throw new Error(`${label}.model must be a non-empty string`);
|
|
87
|
+
if (record.checkedAt !== undefined && typeof record.checkedAt !== "string") {
|
|
88
|
+
throw new Error(`${label}.checkedAt must be a string when present`);
|
|
89
|
+
}
|
|
90
|
+
if (record.credentialRevision !== undefined && typeof record.credentialRevision !== "string") {
|
|
91
|
+
throw new Error(`${label}.credentialRevision must be a string when present`);
|
|
92
|
+
}
|
|
93
|
+
if (record.error !== undefined && typeof record.error !== "string") {
|
|
94
|
+
throw new Error(`${label}.error must be a string when present`);
|
|
95
|
+
}
|
|
96
|
+
if (record.attempts !== undefined && typeof record.attempts !== "number") {
|
|
97
|
+
throw new Error(`${label}.attempts must be a number when present`);
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
status: record.status,
|
|
101
|
+
provider: record.provider,
|
|
102
|
+
model: record.model,
|
|
103
|
+
...(record.checkedAt !== undefined ? { checkedAt: record.checkedAt } : {}),
|
|
104
|
+
...(record.credentialRevision !== undefined ? { credentialRevision: record.credentialRevision } : {}),
|
|
105
|
+
...(record.error !== undefined ? { error: record.error } : {}),
|
|
106
|
+
...(record.attempts !== undefined ? { attempts: record.attempts } : {}),
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
function validateProviderState(value) {
|
|
110
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
111
|
+
throw new Error("provider state must be an object");
|
|
112
|
+
}
|
|
113
|
+
const record = value;
|
|
114
|
+
if (record.schemaVersion !== 1)
|
|
115
|
+
throw new Error("schemaVersion must be 1");
|
|
116
|
+
if (!isNonEmptyString(record.machineId))
|
|
117
|
+
throw new Error("machineId must be a non-empty string");
|
|
118
|
+
if (!isNonEmptyString(record.updatedAt))
|
|
119
|
+
throw new Error("updatedAt must be a non-empty string");
|
|
120
|
+
if (!record.lanes || typeof record.lanes !== "object" || Array.isArray(record.lanes)) {
|
|
121
|
+
throw new Error("lanes must be an object");
|
|
122
|
+
}
|
|
123
|
+
const rawLanes = record.lanes;
|
|
124
|
+
const lanes = {
|
|
125
|
+
outward: validateBinding(rawLanes.outward, "outward"),
|
|
126
|
+
inner: validateBinding(rawLanes.inner, "inner"),
|
|
127
|
+
};
|
|
128
|
+
if (!record.readiness || typeof record.readiness !== "object" || Array.isArray(record.readiness)) {
|
|
129
|
+
throw new Error("readiness must be an object");
|
|
130
|
+
}
|
|
131
|
+
const rawReadiness = record.readiness;
|
|
132
|
+
const readiness = {};
|
|
133
|
+
for (const lane of LANES) {
|
|
134
|
+
if (rawReadiness[lane] !== undefined) {
|
|
135
|
+
readiness[lane] = validateReadiness(rawReadiness[lane], `${lane}.readiness`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return {
|
|
139
|
+
schemaVersion: 1,
|
|
140
|
+
machineId: record.machineId,
|
|
141
|
+
updatedAt: record.updatedAt,
|
|
142
|
+
lanes,
|
|
143
|
+
readiness,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
function getProviderStatePath(agentRoot) {
|
|
147
|
+
return path.join(agentRoot, "state", "providers.json");
|
|
148
|
+
}
|
|
149
|
+
function readProviderState(agentRoot) {
|
|
150
|
+
const statePath = getProviderStatePath(agentRoot);
|
|
151
|
+
let raw;
|
|
152
|
+
try {
|
|
153
|
+
raw = fs.readFileSync(statePath, "utf-8");
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
const code = error.code;
|
|
157
|
+
if (code === "ENOENT") {
|
|
158
|
+
return { ok: false, reason: "missing", statePath, error: "provider state not found" };
|
|
159
|
+
}
|
|
160
|
+
return { ok: false, reason: "invalid", statePath, error: String(error) };
|
|
161
|
+
}
|
|
162
|
+
try {
|
|
163
|
+
const state = validateProviderState(JSON.parse(raw));
|
|
164
|
+
(0, runtime_1.emitNervesEvent)({
|
|
165
|
+
component: "config/identity",
|
|
166
|
+
event: "config.provider_state_read",
|
|
167
|
+
message: "read provider state",
|
|
168
|
+
meta: { statePath, machineId: state.machineId },
|
|
169
|
+
});
|
|
170
|
+
return { ok: true, statePath, state };
|
|
171
|
+
}
|
|
172
|
+
catch (error) {
|
|
173
|
+
return { ok: false, reason: "invalid", statePath, error: String(error) };
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
function writeProviderState(agentRoot, state) {
|
|
177
|
+
const statePath = getProviderStatePath(agentRoot);
|
|
178
|
+
const validated = validateProviderState(state);
|
|
179
|
+
fs.mkdirSync(path.dirname(statePath), { recursive: true });
|
|
180
|
+
fs.writeFileSync(statePath, `${JSON.stringify(validated, null, 2)}\n`, "utf-8");
|
|
181
|
+
(0, runtime_1.emitNervesEvent)({
|
|
182
|
+
component: "config/identity",
|
|
183
|
+
event: "config.provider_state_written",
|
|
184
|
+
message: "wrote provider state",
|
|
185
|
+
meta: { statePath, machineId: validated.machineId },
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
function binding(provider, model, updatedAt) {
|
|
189
|
+
return {
|
|
190
|
+
provider,
|
|
191
|
+
model,
|
|
192
|
+
source: "bootstrap",
|
|
193
|
+
updatedAt,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
function bootstrapProviderStateFromAgentConfig(input) {
|
|
197
|
+
const updatedAt = input.now.toISOString();
|
|
198
|
+
return {
|
|
199
|
+
schemaVersion: 1,
|
|
200
|
+
machineId: input.machineId,
|
|
201
|
+
updatedAt,
|
|
202
|
+
lanes: {
|
|
203
|
+
outward: binding(input.agentConfig.humanFacing.provider, input.agentConfig.humanFacing.model, updatedAt),
|
|
204
|
+
inner: binding(input.agentConfig.agentFacing.provider, input.agentConfig.agentFacing.model, updatedAt),
|
|
205
|
+
},
|
|
206
|
+
readiness: {},
|
|
207
|
+
};
|
|
208
|
+
}
|