@ouro.bot/cli 0.1.0-alpha.642 → 0.1.0-alpha.644
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 +12 -0
- package/dist/heart/config.js +5 -1
- package/dist/heart/core.js +19 -2
- package/dist/heart/provider-failover.js +11 -1
- package/dist/heart/providers/openai-codex-token.js +289 -0
- package/dist/senses/pipeline.js +45 -0
- package/dist/senses/teams.js +4 -2
- package/package.json +10 -7
package/changelog.json
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_note": "This changelog is maintained as part of the PR/version-bump workflow. Agent-curated, not auto-generated. Agents read this file directly via read_file to understand what changed between versions.",
|
|
3
3
|
"versions": [
|
|
4
|
+
{
|
|
5
|
+
"version": "0.1.0-alpha.644",
|
|
6
|
+
"changes": [
|
|
7
|
+
"Refresh expired openai-codex OAuth credentials automatically and expose chat-driven recovery, with full coverage for stale-token repair, malformed local Codex auth files, transient refresh failures, and human-required reauth boundaries."
|
|
8
|
+
]
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"version": "0.1.0-alpha.643",
|
|
12
|
+
"changes": [
|
|
13
|
+
"Dependency audit hardening: refresh the Teams SDK family and vulnerable transitive packages, force Teams' stale msal-node edge onto the safe 5.2.x line, update Teams invoke handlers for the newer SDK route-handler return contract, and add a root npm audit gate to release preflight."
|
|
14
|
+
]
|
|
15
|
+
},
|
|
4
16
|
{
|
|
5
17
|
"version": "0.1.0-alpha.642",
|
|
6
18
|
"changes": [
|
package/dist/heart/config.js
CHANGED
|
@@ -231,7 +231,11 @@ function getAnthropicConfig() {
|
|
|
231
231
|
}
|
|
232
232
|
function getOpenAICodexConfig() {
|
|
233
233
|
const raw = readProviderConfig("openai-codex");
|
|
234
|
-
return {
|
|
234
|
+
return {
|
|
235
|
+
oauthAccessToken: typeof raw.oauthAccessToken === "string" ? raw.oauthAccessToken : "",
|
|
236
|
+
...(typeof raw.refreshToken === "string" ? { refreshToken: raw.refreshToken } : {}),
|
|
237
|
+
...(typeof raw.expiresAt === "number" ? { expiresAt: raw.expiresAt } : {}),
|
|
238
|
+
};
|
|
235
239
|
}
|
|
236
240
|
function getGithubCopilotConfig() {
|
|
237
241
|
const raw = readProviderConfig("github-copilot");
|
package/dist/heart/core.js
CHANGED
|
@@ -37,6 +37,7 @@ const tool_friction_1 = require("./tool-friction");
|
|
|
37
37
|
const provider_models_1 = require("./provider-models");
|
|
38
38
|
const provider_credentials_1 = require("./provider-credentials");
|
|
39
39
|
const provider_attempt_1 = require("./provider-attempt");
|
|
40
|
+
const openai_codex_token_1 = require("./providers/openai-codex-token");
|
|
40
41
|
const _providerRuntimes = {
|
|
41
42
|
human: null,
|
|
42
43
|
agent: null,
|
|
@@ -61,15 +62,25 @@ async function getProviderRuntimeFingerprint(facing) {
|
|
|
61
62
|
`Run \`ouro auth --agent ${agentName} --provider ${binding.provider}\`.`,
|
|
62
63
|
].join("\n"));
|
|
63
64
|
}
|
|
65
|
+
let record = credential.record;
|
|
66
|
+
if (binding.provider === "openai-codex") {
|
|
67
|
+
const refresh = await (0, openai_codex_token_1.refreshOpenAICodexProviderCredentials)(agentName, {
|
|
68
|
+
record,
|
|
69
|
+
reason: "runtime-init",
|
|
70
|
+
});
|
|
71
|
+
if (refresh.ok) {
|
|
72
|
+
record = refresh.record;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
64
75
|
return {
|
|
65
76
|
binding,
|
|
66
77
|
fingerprint: JSON.stringify({
|
|
67
78
|
lane: binding.lane,
|
|
68
79
|
provider: binding.provider,
|
|
69
80
|
model: binding.model,
|
|
70
|
-
credentialRevision:
|
|
81
|
+
credentialRevision: record.revision,
|
|
71
82
|
}),
|
|
72
|
-
credential:
|
|
83
|
+
credential: record,
|
|
73
84
|
};
|
|
74
85
|
}
|
|
75
86
|
function createProviderRegistry() {
|
|
@@ -844,6 +855,12 @@ async function runAgent(messages, callbacks, channel, signal, options) {
|
|
|
844
855
|
const seconds = delayMs / 1000;
|
|
845
856
|
const cause = RETRY_LABELS[record.classification];
|
|
846
857
|
try {
|
|
858
|
+
if (record.provider === "openai-codex" && record.classification === "auth-failure") {
|
|
859
|
+
await (0, openai_codex_token_1.refreshOpenAICodexProviderCredentials)((0, identity_2.getAgentName)(), {
|
|
860
|
+
force: true,
|
|
861
|
+
reason: "turn-auth-failure",
|
|
862
|
+
});
|
|
863
|
+
}
|
|
847
864
|
await (0, provider_credentials_1.refreshProviderCredentialPool)((0, identity_2.getAgentName)(), {
|
|
848
865
|
preserveCachedOnFailure: true,
|
|
849
866
|
providers: [record.provider],
|
|
@@ -126,6 +126,9 @@ function buildFailoverContext(errorMessage, classification, currentProvider, cur
|
|
|
126
126
|
if (classification === "auth-failure") {
|
|
127
127
|
lines.push("");
|
|
128
128
|
lines.push("To keep using the current provider:");
|
|
129
|
+
if (currentProvider === "openai-codex") {
|
|
130
|
+
lines.push(` - Reply "refresh openai-codex" to try the saved refresh token from this chat.`);
|
|
131
|
+
}
|
|
129
132
|
lines.push(` 1. Run \`ouro auth --agent ${agentName} --provider ${currentProvider}\``);
|
|
130
133
|
}
|
|
131
134
|
if (modelMismatch) {
|
|
@@ -182,11 +185,18 @@ function buildFailoverContext(errorMessage, classification, currentProvider, cur
|
|
|
182
185
|
}
|
|
183
186
|
function handleFailoverReply(reply, context) {
|
|
184
187
|
const lower = reply.toLowerCase().trim();
|
|
188
|
+
const currentProvider = context.currentProvider;
|
|
189
|
+
const currentLane = context.currentLane ?? "outward";
|
|
190
|
+
if (context.classification === "auth-failure"
|
|
191
|
+
&& (lower.includes(`refresh ${currentProvider}`)
|
|
192
|
+
|| lower.includes(`reauth ${currentProvider}`)
|
|
193
|
+
|| (currentProvider === "openai-codex" && (lower.includes("refresh codex") || lower.includes("reauth codex"))))) {
|
|
194
|
+
return { action: "refresh", provider: currentProvider, lane: currentLane };
|
|
195
|
+
}
|
|
185
196
|
const readyProviders = context.readyProviders ?? context.workingProviders.map((provider) => ({
|
|
186
197
|
provider,
|
|
187
198
|
model: (0, provider_models_1.resolveModelForProviderDisplay)(provider),
|
|
188
199
|
}));
|
|
189
|
-
const currentLane = context.currentLane ?? "outward";
|
|
190
200
|
for (const candidate of readyProviders) {
|
|
191
201
|
if (lower.includes(`switch to ${candidate.provider}`) || lower === candidate.provider) {
|
|
192
202
|
return {
|
|
@@ -0,0 +1,289 @@
|
|
|
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.readOpenAICodexJwtExpiresAt = readOpenAICodexJwtExpiresAt;
|
|
37
|
+
exports.refreshOpenAICodexProviderCredentials = refreshOpenAICodexProviderCredentials;
|
|
38
|
+
const fs = __importStar(require("node:fs"));
|
|
39
|
+
const os = __importStar(require("node:os"));
|
|
40
|
+
const path = __importStar(require("node:path"));
|
|
41
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
42
|
+
const provider_credentials_1 = require("../provider-credentials");
|
|
43
|
+
const OPENAI_CODEX_TOKEN_ENDPOINT = "https://auth.openai.com/oauth/token";
|
|
44
|
+
const OPENAI_CODEX_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
45
|
+
const OPENAI_CODEX_REFRESH_MARGIN_MS = 5 * 60_000;
|
|
46
|
+
function decodeJwtPayload(token) {
|
|
47
|
+
const parts = token.split(".");
|
|
48
|
+
if (parts.length < 2 || !parts[1])
|
|
49
|
+
return null;
|
|
50
|
+
try {
|
|
51
|
+
const base64 = parts[1]
|
|
52
|
+
.replace(/-/g, "+")
|
|
53
|
+
.replace(/_/g, "/")
|
|
54
|
+
.padEnd(Math.ceil(parts[1].length / 4) * 4, "=");
|
|
55
|
+
const parsed = JSON.parse(Buffer.from(base64, "base64").toString("utf8"));
|
|
56
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
57
|
+
return null;
|
|
58
|
+
return parsed;
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function readOpenAICodexJwtExpiresAt(token) {
|
|
65
|
+
const payload = decodeJwtPayload(token);
|
|
66
|
+
const exp = payload?.exp;
|
|
67
|
+
if (typeof exp !== "number" || !Number.isFinite(exp) || exp <= 0)
|
|
68
|
+
return undefined;
|
|
69
|
+
return Math.floor(exp * 1000);
|
|
70
|
+
}
|
|
71
|
+
function recordCredentialString(record, field) {
|
|
72
|
+
const value = record.credentials[field];
|
|
73
|
+
return typeof value === "string" ? value.trim() : "";
|
|
74
|
+
}
|
|
75
|
+
function recordCredentialNumber(record, field) {
|
|
76
|
+
const value = record.credentials[field];
|
|
77
|
+
if (typeof value === "number" && Number.isFinite(value) && value > 0)
|
|
78
|
+
return value;
|
|
79
|
+
if (typeof value === "string") {
|
|
80
|
+
const parsed = Number(value);
|
|
81
|
+
if (Number.isFinite(parsed) && parsed > 0)
|
|
82
|
+
return parsed;
|
|
83
|
+
}
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
function resolveExpiresAt(record) {
|
|
87
|
+
return recordCredentialNumber(record, "expiresAt")
|
|
88
|
+
?? readOpenAICodexJwtExpiresAt(recordCredentialString(record, "oauthAccessToken"));
|
|
89
|
+
}
|
|
90
|
+
function isRecordFresh(record, now) {
|
|
91
|
+
const expiresAt = resolveExpiresAt(record);
|
|
92
|
+
if (!expiresAt)
|
|
93
|
+
return true;
|
|
94
|
+
return expiresAt > now.getTime() + OPENAI_CODEX_REFRESH_MARGIN_MS;
|
|
95
|
+
}
|
|
96
|
+
function authCommand(agentName) {
|
|
97
|
+
return `ouro auth --agent ${agentName} --provider openai-codex`;
|
|
98
|
+
}
|
|
99
|
+
function readProviderRecordFailure(result, agentName) {
|
|
100
|
+
return {
|
|
101
|
+
ok: false,
|
|
102
|
+
actor: "human-required",
|
|
103
|
+
message: `openai-codex credentials could not be loaded for ${agentName}: ${result.error}. Run '${authCommand(agentName)}'.`,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
function parseRefreshFailure(body) {
|
|
107
|
+
try {
|
|
108
|
+
const parsed = JSON.parse(body);
|
|
109
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
110
|
+
return body.trim();
|
|
111
|
+
const record = parsed;
|
|
112
|
+
const error = record.error;
|
|
113
|
+
if (error && typeof error === "object" && !Array.isArray(error)) {
|
|
114
|
+
const code = error.code;
|
|
115
|
+
const message = error.message;
|
|
116
|
+
if (typeof message === "string" && message.trim())
|
|
117
|
+
return message.trim();
|
|
118
|
+
if (typeof code === "string" && code.trim())
|
|
119
|
+
return code.trim();
|
|
120
|
+
}
|
|
121
|
+
if (typeof error === "string" && error.trim())
|
|
122
|
+
return error.trim();
|
|
123
|
+
const code = record.code;
|
|
124
|
+
if (typeof code === "string" && code.trim())
|
|
125
|
+
return code.trim();
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
// Plain-text bodies are useful as-is.
|
|
129
|
+
}
|
|
130
|
+
return body.trim() || "refresh endpoint returned an empty error body";
|
|
131
|
+
}
|
|
132
|
+
async function updateLocalCodexAuthIfUnchanged(input) {
|
|
133
|
+
const authPath = path.join(input.homeDir, ".codex", "auth.json");
|
|
134
|
+
let raw;
|
|
135
|
+
try {
|
|
136
|
+
raw = fs.readFileSync(authPath, "utf8");
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
return "missing";
|
|
140
|
+
}
|
|
141
|
+
try {
|
|
142
|
+
const parsed = JSON.parse(raw);
|
|
143
|
+
if (!parsed.tokens || typeof parsed.tokens !== "object")
|
|
144
|
+
return "skipped";
|
|
145
|
+
const currentAccess = typeof parsed.tokens.access_token === "string" ? parsed.tokens.access_token : "";
|
|
146
|
+
const currentRefresh = typeof parsed.tokens.refresh_token === "string" ? parsed.tokens.refresh_token : "";
|
|
147
|
+
if (currentAccess !== input.oldAccessToken && currentRefresh !== input.oldRefreshToken) {
|
|
148
|
+
return "skipped";
|
|
149
|
+
}
|
|
150
|
+
parsed.tokens.access_token = input.newAccessToken;
|
|
151
|
+
parsed.tokens.refresh_token = input.newRefreshToken;
|
|
152
|
+
parsed.last_refresh = input.now.toISOString();
|
|
153
|
+
fs.writeFileSync(authPath, `${JSON.stringify(parsed, null, 2)}\n`, "utf8");
|
|
154
|
+
return "updated";
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
return "error";
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
async function requestOpenAICodexTokenRefresh(input) {
|
|
161
|
+
let response;
|
|
162
|
+
try {
|
|
163
|
+
response = await input.fetchImpl(OPENAI_CODEX_TOKEN_ENDPOINT, {
|
|
164
|
+
method: "POST",
|
|
165
|
+
headers: { "Content-Type": "application/json" },
|
|
166
|
+
body: JSON.stringify({
|
|
167
|
+
client_id: OPENAI_CODEX_CLIENT_ID,
|
|
168
|
+
grant_type: "refresh_token",
|
|
169
|
+
refresh_token: input.refreshToken,
|
|
170
|
+
}),
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
return { ok: false, detail: error instanceof Error ? error.message : String(error) };
|
|
175
|
+
}
|
|
176
|
+
if (!response.ok) {
|
|
177
|
+
const body = await response.text().catch(() => "");
|
|
178
|
+
return { ok: false, status: response.status, detail: parseRefreshFailure(body) };
|
|
179
|
+
}
|
|
180
|
+
let body;
|
|
181
|
+
try {
|
|
182
|
+
body = await response.json();
|
|
183
|
+
}
|
|
184
|
+
catch (error) {
|
|
185
|
+
return { ok: false, detail: `refresh endpoint returned invalid JSON: ${error instanceof Error ? error.message : String(error)}` };
|
|
186
|
+
}
|
|
187
|
+
const accessToken = typeof body.access_token === "string" ? body.access_token.trim() : "";
|
|
188
|
+
const refreshToken = typeof body.refresh_token === "string" ? body.refresh_token.trim() : input.refreshToken;
|
|
189
|
+
if (!accessToken)
|
|
190
|
+
return { ok: false, detail: "refresh endpoint returned no access_token" };
|
|
191
|
+
return { ok: true, accessToken, refreshToken };
|
|
192
|
+
}
|
|
193
|
+
async function refreshOpenAICodexProviderCredentials(agentName, options = {}) {
|
|
194
|
+
const now = options.now ?? new Date();
|
|
195
|
+
const readRecord = options.readRecord ?? provider_credentials_1.readProviderCredentialRecord;
|
|
196
|
+
const upsertCredential = options.upsertCredential ?? provider_credentials_1.upsertProviderCredential;
|
|
197
|
+
let record = options.record;
|
|
198
|
+
if (!record) {
|
|
199
|
+
const result = await readRecord(agentName, "openai-codex", { refreshIfMissing: true });
|
|
200
|
+
if (!result.ok)
|
|
201
|
+
return readProviderRecordFailure(result, agentName);
|
|
202
|
+
record = result.record;
|
|
203
|
+
}
|
|
204
|
+
if (!options.force && isRecordFresh(record, now)) {
|
|
205
|
+
(0, runtime_1.emitNervesEvent)({
|
|
206
|
+
component: "engine",
|
|
207
|
+
event: "engine.openai_codex_token_refresh_skipped",
|
|
208
|
+
message: "openai-codex token refresh skipped because the credential is still fresh",
|
|
209
|
+
meta: { agentName, reason: options.reason ?? "fresh" },
|
|
210
|
+
});
|
|
211
|
+
return { ok: true, refreshed: false, record };
|
|
212
|
+
}
|
|
213
|
+
const oldAccessToken = recordCredentialString(record, "oauthAccessToken");
|
|
214
|
+
const oldRefreshToken = recordCredentialString(record, "refreshToken");
|
|
215
|
+
if (!oldRefreshToken) {
|
|
216
|
+
return {
|
|
217
|
+
ok: false,
|
|
218
|
+
actor: "human-required",
|
|
219
|
+
message: `openai-codex has no saved refresh token for ${agentName}. Run '${authCommand(agentName)}'.`,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
(0, runtime_1.emitNervesEvent)({
|
|
223
|
+
component: "engine",
|
|
224
|
+
event: "engine.openai_codex_token_refresh_start",
|
|
225
|
+
message: "refreshing openai-codex OAuth token",
|
|
226
|
+
meta: { agentName, reason: options.reason ?? "unspecified" },
|
|
227
|
+
});
|
|
228
|
+
const refresh = await requestOpenAICodexTokenRefresh({
|
|
229
|
+
refreshToken: oldRefreshToken,
|
|
230
|
+
fetchImpl: options.fetchImpl ?? fetch,
|
|
231
|
+
});
|
|
232
|
+
if (!refresh.ok) {
|
|
233
|
+
const actor = refresh.status === 401 ? "human-required" : "agent-runnable";
|
|
234
|
+
(0, runtime_1.emitNervesEvent)({
|
|
235
|
+
level: actor === "human-required" ? "warn" : "error",
|
|
236
|
+
component: "engine",
|
|
237
|
+
event: "engine.openai_codex_token_refresh_error",
|
|
238
|
+
message: "openai-codex OAuth token refresh failed",
|
|
239
|
+
meta: {
|
|
240
|
+
agentName,
|
|
241
|
+
reason: options.reason ?? "unspecified",
|
|
242
|
+
actor,
|
|
243
|
+
...(refresh.status ? { status: refresh.status } : {}),
|
|
244
|
+
detail: refresh.detail,
|
|
245
|
+
},
|
|
246
|
+
});
|
|
247
|
+
return {
|
|
248
|
+
ok: false,
|
|
249
|
+
actor,
|
|
250
|
+
message: actor === "human-required"
|
|
251
|
+
? `openai-codex refresh token is no longer usable (${refresh.detail}). Run '${authCommand(agentName)}'.`
|
|
252
|
+
: `openai-codex token refresh failed (${refresh.detail}); retry refresh before asking for browser login.`,
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
const expiresAt = readOpenAICodexJwtExpiresAt(refresh.accessToken);
|
|
256
|
+
const credentials = {
|
|
257
|
+
oauthAccessToken: refresh.accessToken,
|
|
258
|
+
refreshToken: refresh.refreshToken,
|
|
259
|
+
...(expiresAt ? { expiresAt } : {}),
|
|
260
|
+
};
|
|
261
|
+
const updated = await upsertCredential({
|
|
262
|
+
agentName,
|
|
263
|
+
provider: "openai-codex",
|
|
264
|
+
credentials,
|
|
265
|
+
config: { ...record.config },
|
|
266
|
+
provenance: { source: record.provenance.source },
|
|
267
|
+
now,
|
|
268
|
+
});
|
|
269
|
+
const localAuth = await updateLocalCodexAuthIfUnchanged({
|
|
270
|
+
homeDir: options.homeDir ?? os.homedir(),
|
|
271
|
+
oldAccessToken,
|
|
272
|
+
oldRefreshToken,
|
|
273
|
+
newAccessToken: refresh.accessToken,
|
|
274
|
+
newRefreshToken: refresh.refreshToken,
|
|
275
|
+
now,
|
|
276
|
+
});
|
|
277
|
+
(0, runtime_1.emitNervesEvent)({
|
|
278
|
+
component: "engine",
|
|
279
|
+
event: "engine.openai_codex_token_refresh_end",
|
|
280
|
+
message: "refreshed openai-codex OAuth token",
|
|
281
|
+
meta: {
|
|
282
|
+
agentName,
|
|
283
|
+
reason: options.reason ?? "unspecified",
|
|
284
|
+
credentialRevision: updated.revision,
|
|
285
|
+
localCodexAuth: localAuth,
|
|
286
|
+
},
|
|
287
|
+
});
|
|
288
|
+
return { ok: true, refreshed: true, record: updated };
|
|
289
|
+
}
|
package/dist/senses/pipeline.js
CHANGED
|
@@ -53,6 +53,7 @@ const active_work_1 = require("../heart/active-work");
|
|
|
53
53
|
const delegation_1 = require("../heart/delegation");
|
|
54
54
|
const obligations_1 = require("../arc/obligations");
|
|
55
55
|
const provider_failover_1 = require("../heart/provider-failover");
|
|
56
|
+
const openai_codex_token_1 = require("../heart/providers/openai-codex-token");
|
|
56
57
|
const tempo_1 = require("../heart/tempo");
|
|
57
58
|
const temporal_view_1 = require("../heart/temporal-view");
|
|
58
59
|
const start_of_turn_packet_1 = require("../heart/start-of-turn-packet");
|
|
@@ -362,6 +363,50 @@ async function handleInboundTurn(input) {
|
|
|
362
363
|
}
|
|
363
364
|
// Switch failed OR succeeded — either way, fall through to normal processing.
|
|
364
365
|
}
|
|
366
|
+
else if (failoverAction.action === "refresh") {
|
|
367
|
+
const refresh = failoverAction.provider === "openai-codex"
|
|
368
|
+
? await (0, openai_codex_token_1.refreshOpenAICodexProviderCredentials)(failoverAgentName, {
|
|
369
|
+
force: true,
|
|
370
|
+
reason: "failover-reply",
|
|
371
|
+
})
|
|
372
|
+
: { ok: false, actor: "human-required", message: `provider ${failoverAction.provider} does not support chat-driven refresh` };
|
|
373
|
+
if (refresh.ok) {
|
|
374
|
+
(0, runtime_1.emitNervesEvent)({
|
|
375
|
+
component: "senses",
|
|
376
|
+
event: "senses.failover_refresh",
|
|
377
|
+
message: `refreshed ${failoverAction.provider} provider credentials via failover`,
|
|
378
|
+
meta: {
|
|
379
|
+
agentName: failoverAgentName,
|
|
380
|
+
lane: failoverAction.lane,
|
|
381
|
+
provider: failoverAction.provider,
|
|
382
|
+
refreshed: refresh.refreshed,
|
|
383
|
+
},
|
|
384
|
+
});
|
|
385
|
+
input.messages = [{
|
|
386
|
+
role: "user",
|
|
387
|
+
content: `[provider refresh: ${pendingContext.errorSummary}. refreshed ${failoverAction.provider} credentials for the ${failoverAction.lane} lane. your conversation history is intact — respond to the user's last message.]`,
|
|
388
|
+
}];
|
|
389
|
+
}
|
|
390
|
+
else {
|
|
391
|
+
(0, runtime_1.emitNervesEvent)({
|
|
392
|
+
level: refresh.actor === "human-required" ? "warn" : "error",
|
|
393
|
+
component: "senses",
|
|
394
|
+
event: "senses.failover_refresh_error",
|
|
395
|
+
message: `failed to refresh ${failoverAction.provider} provider credentials via failover`,
|
|
396
|
+
meta: {
|
|
397
|
+
agentName: failoverAgentName,
|
|
398
|
+
lane: failoverAction.lane,
|
|
399
|
+
provider: failoverAction.provider,
|
|
400
|
+
actor: refresh.actor,
|
|
401
|
+
error: refresh.message,
|
|
402
|
+
},
|
|
403
|
+
});
|
|
404
|
+
input.messages = [{
|
|
405
|
+
role: "user",
|
|
406
|
+
content: `[provider refresh failed: tried to refresh ${failoverAction.provider} for the ${failoverAction.lane} lane. actor: ${refresh.actor}. reason: ${refresh.message}. current lane unchanged: ${pendingContext.currentProvider} / ${pendingContext.currentModel}. If ready alternatives are listed in the prior failover message, switch to one; otherwise tell the user the concrete human-required auth step.]`,
|
|
407
|
+
}];
|
|
408
|
+
}
|
|
409
|
+
}
|
|
365
410
|
}
|
|
366
411
|
// Step 0b: Slash command interception (before friend resolution / agent turn)
|
|
367
412
|
{
|
package/dist/senses/teams.js
CHANGED
|
@@ -946,7 +946,7 @@ function registerBotHandlers(app, label) {
|
|
|
946
946
|
const turnKey = teamsTurnKey(convId);
|
|
947
947
|
// Validate payload — graceful no-op for malformed invocations
|
|
948
948
|
if (activity.value?.actionName !== "feedback" || !reaction) {
|
|
949
|
-
return;
|
|
949
|
+
return undefined;
|
|
950
950
|
}
|
|
951
951
|
const syntheticText = buildFeedbackSyntheticText(reaction, comment);
|
|
952
952
|
// Turn coordination: if a turn is active, enqueue as steering follow-up
|
|
@@ -957,7 +957,7 @@ function registerBotHandlers(app, label) {
|
|
|
957
957
|
receivedAt: Date.now(),
|
|
958
958
|
effect: (0, continuity_1.classifySteeringFollowUpEffect)(syntheticText),
|
|
959
959
|
});
|
|
960
|
-
return;
|
|
960
|
+
return undefined;
|
|
961
961
|
}
|
|
962
962
|
try {
|
|
963
963
|
const teamsContext = {
|
|
@@ -978,6 +978,7 @@ function registerBotHandlers(app, label) {
|
|
|
978
978
|
finally {
|
|
979
979
|
_turnCoordinator.endTurn(turnKey);
|
|
980
980
|
}
|
|
981
|
+
return undefined;
|
|
981
982
|
});
|
|
982
983
|
/* v8 ignore stop */
|
|
983
984
|
// Handle bot install — send welcome Adaptive Card with prompt starters.
|
|
@@ -999,6 +1000,7 @@ function registerBotHandlers(app, label) {
|
|
|
999
1000
|
const msg = err instanceof Error ? err.message : String(err);
|
|
1000
1001
|
(0, runtime_1.emitNervesEvent)({ level: "error", event: "channel.welcome_handler_error", component: "channels", message: msg.slice(0, 200), meta: {} });
|
|
1001
1002
|
}
|
|
1003
|
+
return undefined;
|
|
1002
1004
|
});
|
|
1003
1005
|
/* v8 ignore stop */
|
|
1004
1006
|
app.on("message", async (ctx) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ouro.bot/cli",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.644",
|
|
4
4
|
"main": "dist/heart/daemon/ouro-entry.js",
|
|
5
5
|
"bin": {
|
|
6
6
|
"cli": "dist/heart/daemon/ouro-bot-entry.js",
|
|
@@ -52,12 +52,12 @@
|
|
|
52
52
|
"@anthropic-ai/sdk": "^0.78.0",
|
|
53
53
|
"@azure/identity": "^4.13.0",
|
|
54
54
|
"@azure/storage-blob": "^12.31.0",
|
|
55
|
-
"@microsoft/teams.api": "2.0.
|
|
56
|
-
"@microsoft/teams.apps": "
|
|
57
|
-
"@microsoft/teams.cards": "2.0.
|
|
58
|
-
"@microsoft/teams.common": "2.0.
|
|
59
|
-
"@microsoft/teams.dev": "
|
|
60
|
-
"@microsoft/teams.graph": "2.0.
|
|
55
|
+
"@microsoft/teams.api": "2.0.11",
|
|
56
|
+
"@microsoft/teams.apps": "2.0.11",
|
|
57
|
+
"@microsoft/teams.cards": "2.0.11",
|
|
58
|
+
"@microsoft/teams.common": "2.0.11",
|
|
59
|
+
"@microsoft/teams.dev": "2.0.11",
|
|
60
|
+
"@microsoft/teams.graph": "2.0.11",
|
|
61
61
|
"@types/react": "^17.0.91",
|
|
62
62
|
"@types/ws": "^8.18.1",
|
|
63
63
|
"fast-glob": "^3.3.3",
|
|
@@ -87,6 +87,9 @@
|
|
|
87
87
|
"vitest": "^4.0.18"
|
|
88
88
|
},
|
|
89
89
|
"overrides": {
|
|
90
|
+
"@microsoft/teams.apps": {
|
|
91
|
+
"@azure/msal-node": "^5.2.2"
|
|
92
|
+
},
|
|
90
93
|
"@testing-library/react": {
|
|
91
94
|
"react": "$react",
|
|
92
95
|
"@types/react": "$@types/react"
|