@ouro.bot/cli 0.1.0-alpha.18 → 0.1.0-alpha.181
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/AdoptionSpecialist.ouro/agent.json +3 -2
- package/AdoptionSpecialist.ouro/psyche/SOUL.md +3 -3
- package/AdoptionSpecialist.ouro/psyche/identities/monty.md +2 -2
- package/README.md +187 -191
- package/changelog.json +1219 -0
- package/dist/heart/active-work.js +633 -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/commitments.js +105 -0
- package/dist/heart/config.js +40 -30
- package/dist/heart/core.js +620 -124
- package/dist/heart/cross-chat-delivery.js +146 -0
- package/dist/heart/daemon/agent-discovery.js +81 -0
- package/dist/heart/daemon/agent-service.js +360 -0
- package/dist/heart/daemon/auth-flow.js +477 -0
- package/dist/heart/daemon/cadence.js +70 -0
- package/dist/heart/daemon/daemon-cli.js +2476 -206
- package/dist/heart/daemon/daemon-entry.js +157 -4
- package/dist/heart/daemon/daemon-health.js +136 -0
- package/dist/heart/daemon/daemon-runtime-sync.js +212 -0
- package/dist/heart/daemon/daemon-tombstone.js +123 -0
- package/dist/heart/daemon/daemon.js +449 -17
- package/dist/heart/daemon/habit-migration.js +181 -0
- package/dist/heart/daemon/habit-parser.js +119 -0
- package/dist/heart/daemon/habit-scheduler.js +369 -0
- package/dist/heart/daemon/hatch-animation.js +10 -3
- package/dist/heart/daemon/hatch-flow.js +30 -111
- package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
- package/dist/heart/daemon/hooks/bundle-meta.js +92 -0
- package/dist/heart/daemon/inner-status.js +89 -0
- package/dist/heart/daemon/launchd.js +165 -0
- package/dist/heart/daemon/log-tailer.js +4 -3
- package/dist/heart/daemon/mcp-server.js +591 -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-global-installer.js +128 -0
- package/dist/heart/daemon/ouro-path-installer.js +78 -34
- package/dist/heart/daemon/ouro-version-manager.js +171 -0
- package/dist/heart/daemon/outlook-http.js +399 -0
- package/dist/heart/daemon/outlook-read.js +1291 -0
- package/dist/heart/daemon/outlook-render.js +1032 -0
- package/dist/heart/daemon/outlook-types.js +7 -0
- package/dist/heart/daemon/outlook-view.js +194 -0
- package/dist/heart/daemon/process-manager.js +75 -0
- package/dist/heart/daemon/run-hooks.js +39 -0
- package/dist/heart/daemon/runtime-logging.js +58 -15
- package/dist/heart/daemon/runtime-metadata.js +219 -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 +50 -2
- 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 +202 -0
- package/dist/heart/daemon/specialist-orchestrator.js +2 -2
- package/dist/heart/daemon/specialist-prompt.js +13 -9
- package/dist/heart/daemon/specialist-tools.js +78 -9
- package/dist/heart/daemon/staged-restart.js +114 -0
- package/dist/heart/daemon/task-scheduler.js +3 -25
- package/dist/heart/daemon/thoughts.js +507 -0
- package/dist/heart/daemon/update-checker.js +111 -0
- package/dist/heart/daemon/update-hooks.js +138 -0
- package/dist/heart/daemon/wrapper-publish-guard.js +86 -0
- package/dist/heart/delegation.js +62 -0
- package/dist/heart/identity.js +136 -55
- package/dist/heart/kicks.js +2 -20
- package/dist/heart/migrate-config.js +127 -0
- package/dist/heart/model-capabilities.js +48 -0
- package/dist/heart/obligations.js +197 -0
- package/dist/heart/progress-story.js +42 -0
- package/dist/heart/provider-failover.js +88 -0
- package/dist/heart/provider-ping.js +172 -0
- package/dist/heart/providers/anthropic-token.js +163 -0
- package/dist/heart/providers/anthropic.js +196 -36
- package/dist/heart/providers/azure.js +116 -10
- package/dist/heart/providers/github-copilot.js +157 -0
- package/dist/heart/providers/minimax.js +33 -3
- package/dist/heart/providers/openai-codex.js +51 -16
- package/dist/heart/session-activity.js +172 -0
- package/dist/heart/session-recall.js +216 -0
- package/dist/heart/streaming.js +62 -32
- package/dist/heart/target-resolution.js +127 -0
- package/dist/heart/tool-activity-callbacks.js +36 -0
- package/dist/heart/tool-description.js +135 -0
- package/dist/heart/tool-loop.js +200 -0
- package/dist/heart/turn-coordinator.js +28 -0
- package/dist/mind/associative-recall.js +122 -32
- package/dist/mind/bundle-manifest.js +14 -0
- package/dist/mind/context.js +125 -14
- package/dist/mind/{memory.js → diary.js} +70 -56
- package/dist/mind/file-state.js +179 -0
- package/dist/mind/first-impressions.js +16 -2
- package/dist/mind/friends/channel.js +56 -0
- package/dist/mind/friends/group-context.js +144 -0
- package/dist/mind/friends/store-file.js +19 -0
- package/dist/mind/friends/trust-explanation.js +74 -0
- package/dist/mind/friends/types.js +8 -0
- package/dist/mind/journal-index.js +161 -0
- package/dist/mind/obligation-steering.js +221 -0
- package/dist/mind/obligations.js +134 -0
- package/dist/mind/pending.js +76 -9
- package/dist/mind/phrases.js +1 -0
- package/dist/mind/prompt.js +721 -102
- package/dist/mind/scrutiny.js +173 -0
- package/dist/mind/token-estimate.js +8 -12
- package/dist/nerves/cli-logging.js +15 -2
- package/dist/nerves/coverage/audit.js +1 -1
- package/dist/nerves/coverage/run-artifacts.js +1 -1
- package/dist/nerves/event-buffer.js +111 -0
- package/dist/nerves/index.js +63 -3
- package/dist/nerves/observation.js +20 -0
- package/dist/nerves/runtime.js +5 -1
- package/dist/outlook-ui/assets/index-DN6vVYPm.css +1 -0
- package/dist/outlook-ui/assets/index-M1l1tDTX.js +61 -0
- package/dist/outlook-ui/index.html +15 -0
- package/dist/repertoire/ado-semantic.js +11 -5
- package/dist/repertoire/coding/codex-jsonl.js +64 -0
- package/dist/repertoire/coding/context-pack.js +254 -0
- package/dist/repertoire/coding/feedback.js +197 -30
- package/dist/repertoire/coding/manager.js +149 -2
- package/dist/repertoire/coding/spawner.js +55 -9
- package/dist/repertoire/coding/tools.js +170 -7
- package/dist/repertoire/guardrails.js +291 -0
- package/dist/repertoire/mcp-client.js +254 -0
- package/dist/repertoire/mcp-manager.js +198 -0
- package/dist/repertoire/shell-sessions.js +133 -0
- package/dist/repertoire/skills.js +14 -23
- 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 +11 -28
- package/dist/repertoire/tools-base.js +1111 -327
- package/dist/repertoire/tools-bluebubbles.js +94 -0
- package/dist/repertoire/tools-github.js +1 -6
- package/dist/repertoire/tools-teams.js +12 -59
- package/dist/repertoire/tools.js +318 -124
- 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 +97 -0
- package/dist/senses/bluebubbles-client.js +102 -3
- package/dist/senses/bluebubbles-entry.js +2 -0
- package/dist/senses/bluebubbles-inbound-log.js +109 -0
- package/dist/senses/bluebubbles-media.js +118 -23
- package/dist/senses/bluebubbles-model.js +12 -4
- package/dist/senses/bluebubbles-mutation-log.js +45 -5
- package/dist/senses/bluebubbles-runtime-state.js +109 -0
- package/dist/senses/bluebubbles-session-cleanup.js +72 -0
- package/dist/senses/bluebubbles.js +1008 -102
- package/dist/senses/cli/adapter.js +274 -0
- package/dist/senses/cli/ink-app.js +156 -0
- package/dist/senses/cli/inline-diff.js +64 -0
- package/dist/senses/cli/message-list.js +51 -0
- package/dist/senses/cli/ouro-tui.js +323 -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 +525 -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 +198 -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 +593 -240
- package/dist/senses/commands.js +38 -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 +592 -84
- package/dist/senses/pipeline.js +545 -0
- package/dist/senses/shared-turn.js +196 -0
- package/dist/senses/surface-tool.js +95 -0
- package/dist/senses/teams.js +667 -120
- package/dist/senses/trust-gate.js +112 -2
- package/package.json +16 -5
- package/skills/configure-dev-tools.md +81 -0
- package/subagents/README.md +4 -70
- package/dist/heart/daemon/subagent-installer.js +0 -134
- package/dist/senses/debug-activity.js +0 -107
- package/subagents/work-doer.md +0 -235
- package/subagents/work-merger.md +0 -618
- package/subagents/work-planner.md +0 -382
|
@@ -0,0 +1,163 @@
|
|
|
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.needsRefresh = needsRefresh;
|
|
37
|
+
exports.refreshAnthropicToken = refreshAnthropicToken;
|
|
38
|
+
exports.persistTokenState = persistTokenState;
|
|
39
|
+
exports.ensureFreshToken = ensureFreshToken;
|
|
40
|
+
/* v8 ignore start -- OAuth token lifecycle: requires live API calls, tested via integration @preserve */
|
|
41
|
+
const fs = __importStar(require("fs"));
|
|
42
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
43
|
+
const identity_1 = require("../identity");
|
|
44
|
+
const OAUTH_TOKEN_ENDPOINT = "https://console.anthropic.com/v1/oauth/token";
|
|
45
|
+
const OAUTH_CLIENT_ID = "9d1c250a-e61b-44d9-88ed-5944d1962f5e";
|
|
46
|
+
const REFRESH_MARGIN_MS = 5 * 60 * 1000; // refresh 5 minutes before expiry
|
|
47
|
+
/**
|
|
48
|
+
* Check if the Anthropic OAuth token needs refreshing.
|
|
49
|
+
* Returns true if no expiresAt is set (legacy token) or if within 5 min of expiry.
|
|
50
|
+
*/
|
|
51
|
+
function needsRefresh(expiresAt) {
|
|
52
|
+
if (!expiresAt)
|
|
53
|
+
return true; // legacy token with no expiry — always try refresh
|
|
54
|
+
return Date.now() > expiresAt - REFRESH_MARGIN_MS;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Refresh an Anthropic OAuth access token using the refresh token.
|
|
58
|
+
* Returns the new token state or null if refresh fails.
|
|
59
|
+
*/
|
|
60
|
+
async function refreshAnthropicToken(refreshToken, fetchImpl = fetch) {
|
|
61
|
+
try {
|
|
62
|
+
const response = await fetchImpl(OAUTH_TOKEN_ENDPOINT, {
|
|
63
|
+
method: "POST",
|
|
64
|
+
headers: { "Content-Type": "application/json" },
|
|
65
|
+
body: JSON.stringify({
|
|
66
|
+
grant_type: "refresh_token",
|
|
67
|
+
refresh_token: refreshToken,
|
|
68
|
+
client_id: OAUTH_CLIENT_ID,
|
|
69
|
+
}),
|
|
70
|
+
});
|
|
71
|
+
if (!response.ok) {
|
|
72
|
+
(0, runtime_1.emitNervesEvent)({
|
|
73
|
+
level: "warn",
|
|
74
|
+
component: "engine",
|
|
75
|
+
event: "engine.anthropic_token_refresh_failed",
|
|
76
|
+
message: `token refresh failed: ${response.status}`,
|
|
77
|
+
meta: { status: response.status },
|
|
78
|
+
});
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
const json = await response.json();
|
|
82
|
+
if (!json.access_token) {
|
|
83
|
+
(0, runtime_1.emitNervesEvent)({
|
|
84
|
+
level: "warn",
|
|
85
|
+
component: "engine",
|
|
86
|
+
event: "engine.anthropic_token_refresh_failed",
|
|
87
|
+
message: "token refresh returned no access_token",
|
|
88
|
+
meta: {},
|
|
89
|
+
});
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
const state = {
|
|
93
|
+
accessToken: json.access_token,
|
|
94
|
+
refreshToken: json.refresh_token ?? refreshToken, // keep old if not returned
|
|
95
|
+
expiresAt: Date.now() + (json.expires_in ?? 28800) * 1000, // default 8h
|
|
96
|
+
};
|
|
97
|
+
(0, runtime_1.emitNervesEvent)({
|
|
98
|
+
component: "engine",
|
|
99
|
+
event: "engine.anthropic_token_refreshed",
|
|
100
|
+
message: "anthropic OAuth token refreshed",
|
|
101
|
+
meta: { expiresAt: new Date(state.expiresAt).toISOString() },
|
|
102
|
+
});
|
|
103
|
+
return state;
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
(0, runtime_1.emitNervesEvent)({
|
|
107
|
+
level: "warn",
|
|
108
|
+
component: "engine",
|
|
109
|
+
event: "engine.anthropic_token_refresh_error",
|
|
110
|
+
message: "token refresh threw",
|
|
111
|
+
meta: { error: error instanceof Error ? error.message : String(error) },
|
|
112
|
+
});
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Persist refreshed token state back to secrets.json.
|
|
118
|
+
*/
|
|
119
|
+
function persistTokenState(agentName, state) {
|
|
120
|
+
try {
|
|
121
|
+
const secretsPath = (0, identity_1.getAgentSecretsPath)(agentName);
|
|
122
|
+
const raw = fs.readFileSync(secretsPath, "utf-8");
|
|
123
|
+
const secrets = JSON.parse(raw);
|
|
124
|
+
secrets.providers = secrets.providers ?? {};
|
|
125
|
+
secrets.providers.anthropic = secrets.providers.anthropic ?? {};
|
|
126
|
+
secrets.providers.anthropic.setupToken = state.accessToken;
|
|
127
|
+
secrets.providers.anthropic.refreshToken = state.refreshToken;
|
|
128
|
+
secrets.providers.anthropic.expiresAt = state.expiresAt;
|
|
129
|
+
fs.writeFileSync(secretsPath, JSON.stringify(secrets, null, 2) + "\n", "utf-8");
|
|
130
|
+
/* v8 ignore start -- defensive: persistence failure must not crash the provider @preserve */
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
(0, runtime_1.emitNervesEvent)({
|
|
134
|
+
level: "warn",
|
|
135
|
+
component: "engine",
|
|
136
|
+
event: "engine.anthropic_token_persist_error",
|
|
137
|
+
message: "failed to persist refreshed token",
|
|
138
|
+
meta: { error: error instanceof Error ? error.message : String(error) },
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
/* v8 ignore stop */
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Ensure the Anthropic token is fresh. If expired, refresh and persist.
|
|
145
|
+
* Returns the current valid access token, or null if refresh failed and
|
|
146
|
+
* the existing token is expired.
|
|
147
|
+
*/
|
|
148
|
+
async function ensureFreshToken(currentToken, refreshToken, expiresAt, agentName, fetchImpl) {
|
|
149
|
+
if (!needsRefresh(expiresAt)) {
|
|
150
|
+
return currentToken; // still fresh
|
|
151
|
+
}
|
|
152
|
+
if (!refreshToken) {
|
|
153
|
+
// No refresh token — use the current token as-is (may be expired)
|
|
154
|
+
return currentToken;
|
|
155
|
+
}
|
|
156
|
+
const newState = await refreshAnthropicToken(refreshToken, fetchImpl);
|
|
157
|
+
if (!newState) {
|
|
158
|
+
return currentToken; // refresh failed — try the old token
|
|
159
|
+
}
|
|
160
|
+
persistTokenState(agentName, newState);
|
|
161
|
+
return newState.accessToken;
|
|
162
|
+
}
|
|
163
|
+
/* v8 ignore stop */
|
|
@@ -1,13 +1,50 @@
|
|
|
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
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
37
|
};
|
|
5
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.toAnthropicMessages = toAnthropicMessages;
|
|
40
|
+
exports.classifyAnthropicError = classifyAnthropicError;
|
|
6
41
|
exports.createAnthropicProviderRuntime = createAnthropicProviderRuntime;
|
|
7
42
|
const sdk_1 = __importDefault(require("@anthropic-ai/sdk"));
|
|
8
43
|
const config_1 = require("../config");
|
|
9
44
|
const identity_1 = require("../identity");
|
|
10
45
|
const runtime_1 = require("../../nerves/runtime");
|
|
46
|
+
const streaming_1 = require("../streaming");
|
|
47
|
+
const model_capabilities_1 = require("../model-capabilities");
|
|
11
48
|
const ANTHROPIC_SETUP_TOKEN_PREFIX = "sk-ant-oat01-";
|
|
12
49
|
const ANTHROPIC_SETUP_TOKEN_MIN_LENGTH = 80;
|
|
13
50
|
const ANTHROPIC_OAUTH_BETA_HEADER = "claude-code-20250219,oauth-2025-04-20,fine-grained-tool-streaming-2025-05-14,interleaved-thinking-2025-05-14";
|
|
@@ -21,10 +58,10 @@ function getAnthropicSetupTokenInstructions() {
|
|
|
21
58
|
const agentName = getAnthropicAgentNameForGuidance();
|
|
22
59
|
return [
|
|
23
60
|
"Fix:",
|
|
24
|
-
` 1. Run \`
|
|
25
|
-
" (or run `claude setup-token` and paste the token manually)",
|
|
61
|
+
` 1. Run \`ouro auth --agent ${agentName}\``,
|
|
26
62
|
` 2. Open ${getAnthropicSecretsPathForGuidance()}`,
|
|
27
63
|
" 3. Confirm providers.anthropic.setupToken is set",
|
|
64
|
+
" 4. After reauth, retry the failed ouro command or reconnect this session.",
|
|
28
65
|
].join("\n");
|
|
29
66
|
}
|
|
30
67
|
function getAnthropicReauthGuidance(reason) {
|
|
@@ -92,6 +129,18 @@ function toAnthropicMessages(messages) {
|
|
|
92
129
|
if (msg.role === "assistant") {
|
|
93
130
|
const assistant = msg;
|
|
94
131
|
const blocks = [];
|
|
132
|
+
// Restore thinking blocks before text/tool_use blocks
|
|
133
|
+
const thinkingBlocks = assistant._thinking_blocks;
|
|
134
|
+
if (thinkingBlocks) {
|
|
135
|
+
for (const tb of thinkingBlocks) {
|
|
136
|
+
if (tb.type === "thinking") {
|
|
137
|
+
blocks.push({ type: "thinking", thinking: tb.thinking, signature: tb.signature });
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
blocks.push({ type: "redacted_thinking", data: tb.data });
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
95
144
|
const text = toAnthropicTextContent(assistant.content);
|
|
96
145
|
if (text) {
|
|
97
146
|
blocks.push({ type: "text", text });
|
|
@@ -172,6 +221,29 @@ function mergeAnthropicToolArguments(current, partial) {
|
|
|
172
221
|
}
|
|
173
222
|
return current + partial;
|
|
174
223
|
}
|
|
224
|
+
/* v8 ignore start -- shared network error utility, tested via classification tests @preserve */
|
|
225
|
+
function isNetworkError(error) {
|
|
226
|
+
const code = error.code || "";
|
|
227
|
+
if (["ECONNRESET", "ECONNREFUSED", "ENOTFOUND", "ETIMEDOUT", "EPIPE",
|
|
228
|
+
"EAI_AGAIN", "EHOSTUNREACH", "ENETUNREACH", "ECONNABORTED"].includes(code))
|
|
229
|
+
return true;
|
|
230
|
+
const msg = error.message || "";
|
|
231
|
+
return msg.includes("fetch failed") || msg.includes("socket hang up") || msg.includes("getaddrinfo");
|
|
232
|
+
}
|
|
233
|
+
/* v8 ignore stop */
|
|
234
|
+
function classifyAnthropicError(error) {
|
|
235
|
+
const status = error.status;
|
|
236
|
+
if (status === 401 || status === 403 || isAnthropicAuthFailure(error))
|
|
237
|
+
return "auth-failure";
|
|
238
|
+
if (status === 429)
|
|
239
|
+
return "rate-limit";
|
|
240
|
+
if (status === 529 || (status && status >= 500))
|
|
241
|
+
return "server-error";
|
|
242
|
+
if (isNetworkError(error))
|
|
243
|
+
return "network-error";
|
|
244
|
+
return "unknown";
|
|
245
|
+
}
|
|
246
|
+
/* v8 ignore start -- auth detection: only called from classifyAnthropicError which always passes Error @preserve */
|
|
175
247
|
function isAnthropicAuthFailure(error) {
|
|
176
248
|
if (!(error instanceof Error))
|
|
177
249
|
return false;
|
|
@@ -184,40 +256,52 @@ function isAnthropicAuthFailure(error) {
|
|
|
184
256
|
lower.includes("unauthorized") ||
|
|
185
257
|
lower.includes("invalid api key"));
|
|
186
258
|
}
|
|
187
|
-
|
|
188
|
-
const base = error instanceof Error ? error.message : String(error);
|
|
189
|
-
if (isAnthropicAuthFailure(error)) {
|
|
190
|
-
return new Error(getAnthropicReauthGuidance(`Anthropic authentication failed (${base}).`));
|
|
191
|
-
}
|
|
192
|
-
return error instanceof Error ? error : new Error(String(error));
|
|
193
|
-
}
|
|
259
|
+
/* v8 ignore stop */
|
|
194
260
|
async function streamAnthropicMessages(client, model, request) {
|
|
195
261
|
const { system, messages } = toAnthropicMessages(request.messages);
|
|
196
262
|
const anthropicTools = toAnthropicTools(request.activeTools);
|
|
263
|
+
const modelCaps = (0, model_capabilities_1.getModelCapabilities)(model);
|
|
264
|
+
const maxTokens = modelCaps.maxOutputTokens ?? 16384;
|
|
197
265
|
const params = {
|
|
198
266
|
model,
|
|
199
|
-
max_tokens:
|
|
267
|
+
max_tokens: maxTokens,
|
|
200
268
|
messages,
|
|
201
269
|
stream: true,
|
|
270
|
+
thinking: { type: "adaptive" },
|
|
271
|
+
output_config: { effort: request.reasoningEffort ?? "medium" },
|
|
202
272
|
};
|
|
203
|
-
|
|
204
|
-
|
|
273
|
+
// The Anthropic API requires a Claude Code identification block in the system
|
|
274
|
+
// prompt when using OAuth setup tokens (sk-ant-oat01). Without it, Opus/Sonnet
|
|
275
|
+
// 4.6 requests are rejected with 400. This is the API's validation that the
|
|
276
|
+
// token is being used by a Claude Code client.
|
|
277
|
+
const claudeCodePreamble = { type: "text", text: "You are Claude Code, Anthropic's official CLI for Claude." };
|
|
278
|
+
if (system) {
|
|
279
|
+
params.system = [claudeCodePreamble, { type: "text", text: system }];
|
|
280
|
+
}
|
|
281
|
+
else {
|
|
282
|
+
params.system = [claudeCodePreamble];
|
|
283
|
+
}
|
|
205
284
|
if (anthropicTools.length > 0)
|
|
206
285
|
params.tools = anthropicTools;
|
|
207
286
|
if (request.toolChoiceRequired && anthropicTools.length > 0) {
|
|
208
|
-
|
|
287
|
+
// Thinking (adaptive or enabled) only supports tool_choice "auto" or "none".
|
|
288
|
+
// "any" forces tool use which is incompatible with extended thinking.
|
|
289
|
+
params.tool_choice = params.thinking ? { type: "auto" } : /* v8 ignore next -- no-thinking path: thinking always set for 4.6 models @preserve */ { type: "any" };
|
|
209
290
|
}
|
|
210
291
|
let response;
|
|
211
292
|
try {
|
|
212
293
|
response = await client.messages.create(params, request.signal ? { signal: request.signal } : {});
|
|
213
294
|
}
|
|
214
295
|
catch (error) {
|
|
215
|
-
throw
|
|
296
|
+
throw error instanceof Error ? error : new Error(String(error));
|
|
216
297
|
}
|
|
217
298
|
let content = "";
|
|
218
299
|
let streamStarted = false;
|
|
219
300
|
let usage;
|
|
220
301
|
const toolCalls = new Map();
|
|
302
|
+
const thinkingBlocks = new Map();
|
|
303
|
+
const redactedBlocks = new Map();
|
|
304
|
+
const answerStreamer = new streaming_1.SettleStreamer(request.callbacks, request.eagerSettleStreaming);
|
|
221
305
|
try {
|
|
222
306
|
for await (const event of response) {
|
|
223
307
|
if (request.signal?.aborted)
|
|
@@ -225,17 +309,29 @@ async function streamAnthropicMessages(client, model, request) {
|
|
|
225
309
|
const eventType = String(event.type ?? "");
|
|
226
310
|
if (eventType === "content_block_start") {
|
|
227
311
|
const block = event.content_block;
|
|
228
|
-
|
|
229
|
-
|
|
312
|
+
const index = Number(event.index);
|
|
313
|
+
if (block?.type === "thinking") {
|
|
314
|
+
thinkingBlocks.set(index, { type: "thinking", thinking: "", signature: "" });
|
|
315
|
+
}
|
|
316
|
+
else if (block?.type === "redacted_thinking") {
|
|
317
|
+
redactedBlocks.set(index, { type: "redacted_thinking", data: String(block.data ?? "") });
|
|
318
|
+
}
|
|
319
|
+
else if (block?.type === "tool_use") {
|
|
230
320
|
const rawInput = block.input;
|
|
231
321
|
const input = rawInput && typeof rawInput === "object"
|
|
232
322
|
? JSON.stringify(rawInput)
|
|
233
323
|
: "";
|
|
324
|
+
const name = String(block.name ?? "");
|
|
234
325
|
toolCalls.set(index, {
|
|
235
326
|
id: String(block.id ?? ""),
|
|
236
|
-
name
|
|
327
|
+
name,
|
|
237
328
|
arguments: input,
|
|
238
329
|
});
|
|
330
|
+
// Activate eager streaming for sole settle tool call
|
|
331
|
+
/* v8 ignore next -- settle streaming activation, tested via SettleStreamer unit tests @preserve */
|
|
332
|
+
if (name === "settle" && toolCalls.size === 1) {
|
|
333
|
+
answerStreamer.activate();
|
|
334
|
+
}
|
|
239
335
|
}
|
|
240
336
|
continue;
|
|
241
337
|
}
|
|
@@ -257,14 +353,31 @@ async function streamAnthropicMessages(client, model, request) {
|
|
|
257
353
|
request.callbacks.onModelStreamStart();
|
|
258
354
|
streamStarted = true;
|
|
259
355
|
}
|
|
260
|
-
|
|
356
|
+
const thinkingText = String(delta?.thinking ?? "");
|
|
357
|
+
request.callbacks.onReasoningChunk(thinkingText);
|
|
358
|
+
const thinkingIndex = Number(event.index);
|
|
359
|
+
const thinkingBlock = thinkingBlocks.get(thinkingIndex);
|
|
360
|
+
if (thinkingBlock)
|
|
361
|
+
thinkingBlock.thinking += thinkingText;
|
|
362
|
+
continue;
|
|
363
|
+
}
|
|
364
|
+
if (deltaType === "signature_delta") {
|
|
365
|
+
const sigIndex = Number(event.index);
|
|
366
|
+
const sigBlock = thinkingBlocks.get(sigIndex);
|
|
367
|
+
if (sigBlock)
|
|
368
|
+
sigBlock.signature += String(delta?.signature ?? "");
|
|
261
369
|
continue;
|
|
262
370
|
}
|
|
263
371
|
if (deltaType === "input_json_delta") {
|
|
264
372
|
const index = Number(event.index);
|
|
265
373
|
const existing = toolCalls.get(index);
|
|
266
374
|
if (existing) {
|
|
267
|
-
|
|
375
|
+
const partialJson = String(delta?.partial_json ?? "");
|
|
376
|
+
existing.arguments = mergeAnthropicToolArguments(existing.arguments, partialJson);
|
|
377
|
+
/* v8 ignore next -- settle delta streaming, tested via SettleStreamer unit tests @preserve */
|
|
378
|
+
if (existing.name === "settle" && toolCalls.size === 1) {
|
|
379
|
+
answerStreamer.processDelta(partialJson);
|
|
380
|
+
}
|
|
268
381
|
}
|
|
269
382
|
continue;
|
|
270
383
|
}
|
|
@@ -286,16 +399,25 @@ async function streamAnthropicMessages(client, model, request) {
|
|
|
286
399
|
}
|
|
287
400
|
}
|
|
288
401
|
catch (error) {
|
|
289
|
-
throw
|
|
402
|
+
throw error instanceof Error ? error : /* v8 ignore next -- defensive: stream errors are always Error @preserve */ new Error(String(error));
|
|
290
403
|
}
|
|
404
|
+
// Collect all thinking blocks (regular + redacted) sorted by index to preserve ordering
|
|
405
|
+
const allThinkingIndices = [...thinkingBlocks.keys(), ...redactedBlocks.keys()].sort((a, b) => a - b);
|
|
406
|
+
const outputItems = allThinkingIndices.map((idx) => {
|
|
407
|
+
const tb = thinkingBlocks.get(idx);
|
|
408
|
+
if (tb)
|
|
409
|
+
return tb;
|
|
410
|
+
return redactedBlocks.get(idx);
|
|
411
|
+
});
|
|
291
412
|
return {
|
|
292
413
|
content,
|
|
293
414
|
toolCalls: [...toolCalls.values()],
|
|
294
|
-
outputItems
|
|
415
|
+
outputItems,
|
|
295
416
|
usage,
|
|
417
|
+
settleStreamed: answerStreamer.streamed,
|
|
296
418
|
};
|
|
297
419
|
}
|
|
298
|
-
function createAnthropicProviderRuntime() {
|
|
420
|
+
function createAnthropicProviderRuntime(model) {
|
|
299
421
|
(0, runtime_1.emitNervesEvent)({
|
|
300
422
|
component: "engine",
|
|
301
423
|
event: "engine.provider_init",
|
|
@@ -303,30 +425,68 @@ function createAnthropicProviderRuntime() {
|
|
|
303
425
|
meta: { provider: "anthropic" },
|
|
304
426
|
});
|
|
305
427
|
const anthropicConfig = (0, config_1.getAnthropicConfig)();
|
|
306
|
-
if (!
|
|
307
|
-
throw new Error(getAnthropicReauthGuidance("provider 'anthropic' is selected in agent.json but providers.anthropic.
|
|
428
|
+
if (!anthropicConfig.setupToken) {
|
|
429
|
+
throw new Error(getAnthropicReauthGuidance("provider 'anthropic' is selected in agent.json but providers.anthropic.setupToken is missing in secrets.json."));
|
|
308
430
|
}
|
|
431
|
+
const modelCaps = (0, model_capabilities_1.getModelCapabilities)(model);
|
|
432
|
+
const capabilities = new Set();
|
|
433
|
+
if (modelCaps.reasoningEffort)
|
|
434
|
+
capabilities.add("reasoning-effort");
|
|
309
435
|
const credential = resolveAnthropicSetupTokenCredential();
|
|
310
|
-
const
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
436
|
+
const refreshToken = anthropicConfig.refreshToken;
|
|
437
|
+
const expiresAt = anthropicConfig.expiresAt;
|
|
438
|
+
function createClient(token) {
|
|
439
|
+
return new sdk_1.default({
|
|
440
|
+
authToken: token,
|
|
441
|
+
timeout: 30000,
|
|
442
|
+
maxRetries: 0,
|
|
443
|
+
defaultHeaders: {
|
|
444
|
+
"anthropic-beta": ANTHROPIC_OAUTH_BETA_HEADER,
|
|
445
|
+
"anthropic-dangerous-direct-browser-access": "true",
|
|
446
|
+
"user-agent": "claude-cli/2.1.2 (external, cli)",
|
|
447
|
+
"x-app": "cli",
|
|
448
|
+
},
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
let currentToken = credential.token;
|
|
452
|
+
let client = createClient(currentToken);
|
|
453
|
+
/* v8 ignore start -- token refresh: dynamic import + ensureFreshToken, tested via integration @preserve */
|
|
454
|
+
async function ensureClient() {
|
|
455
|
+
try {
|
|
456
|
+
const { ensureFreshToken } = await Promise.resolve().then(() => __importStar(require("./anthropic-token")));
|
|
457
|
+
const { getAgentName } = await Promise.resolve().then(() => __importStar(require("../identity")));
|
|
458
|
+
const freshToken = await ensureFreshToken(currentToken, refreshToken, expiresAt, getAgentName());
|
|
459
|
+
if (freshToken !== currentToken) {
|
|
460
|
+
currentToken = freshToken;
|
|
461
|
+
client = createClient(freshToken);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
catch {
|
|
465
|
+
// refresh failed — use existing client
|
|
466
|
+
}
|
|
467
|
+
return client;
|
|
468
|
+
}
|
|
469
|
+
/* v8 ignore stop */
|
|
318
470
|
return {
|
|
319
471
|
id: "anthropic",
|
|
320
|
-
model
|
|
321
|
-
client
|
|
472
|
+
model,
|
|
473
|
+
/* v8 ignore next -- getter: returns mutable client ref @preserve */
|
|
474
|
+
get client() { return client; },
|
|
475
|
+
capabilities,
|
|
476
|
+
supportedReasoningEfforts: modelCaps.reasoningEffort,
|
|
322
477
|
resetTurnState(_messages) {
|
|
323
478
|
// Anthropic request payload is derived from canonical messages each turn.
|
|
324
479
|
},
|
|
325
480
|
appendToolOutput(_callId, _output) {
|
|
326
481
|
// Anthropic uses canonical messages for tool_result tracking.
|
|
327
482
|
},
|
|
328
|
-
streamTurn(request) {
|
|
329
|
-
|
|
483
|
+
async streamTurn(request) {
|
|
484
|
+
const freshClient = await ensureClient();
|
|
485
|
+
return streamAnthropicMessages(freshClient, model, request);
|
|
486
|
+
},
|
|
487
|
+
/* v8 ignore next 3 -- delegation: classification logic tested via classifyAnthropicError @preserve */
|
|
488
|
+
classifyError(error) {
|
|
489
|
+
return classifyAnthropicError(error);
|
|
330
490
|
},
|
|
331
491
|
};
|
|
332
492
|
}
|