@evident-ai/cli 3.6.0 → 3.6.1-dev.2fd7890

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/dist/index.js CHANGED
@@ -100,6 +100,26 @@ function clearCredentials() {
100
100
  credentials.set("byEndpoint", byEndpoint);
101
101
  hardenCredentialsPermissions();
102
102
  }
103
+ function getManagedInferenceCredential(runnerId) {
104
+ hardenCredentialsPermissions();
105
+ return credentials.get("managedInferenceByEndpoint")?.[credentialsKey()]?.[runnerId];
106
+ }
107
+ function setManagedInferenceCredential(runnerId, credential) {
108
+ const byEndpoint = credentials.get("managedInferenceByEndpoint") ?? {};
109
+ byEndpoint[credentialsKey()] ??= {};
110
+ byEndpoint[credentialsKey()][runnerId] = credential;
111
+ credentials.set("managedInferenceByEndpoint", byEndpoint);
112
+ hardenCredentialsPermissions();
113
+ }
114
+ function clearManagedInferenceCredential(runnerId) {
115
+ const byEndpoint = credentials.get("managedInferenceByEndpoint") ?? {};
116
+ const runners = byEndpoint[credentialsKey()];
117
+ if (!runners || !(runnerId in runners)) return;
118
+ delete runners[runnerId];
119
+ if (Object.keys(runners).length === 0) delete byEndpoint[credentialsKey()];
120
+ credentials.set("managedInferenceByEndpoint", byEndpoint);
121
+ hardenCredentialsPermissions();
122
+ }
103
123
  function clearAllCredentials() {
104
124
  credentials.clear();
105
125
  hardenCredentialsPermissions();
@@ -119,13 +139,13 @@ function getCliName() {
119
139
  // src/lib/api.ts
120
140
  async function request(path, method, body, headers = {}) {
121
141
  const url = `${getApiUrlConfig()}${path}`;
122
- const requestHeaders = {
142
+ const requestHeaders2 = {
123
143
  "Content-Type": "application/json",
124
144
  ...headers
125
145
  };
126
146
  const response = await fetch(url, {
127
147
  method,
128
- headers: requestHeaders,
148
+ headers: requestHeaders2,
129
149
  body: body ? JSON.stringify(body) : void 0
130
150
  });
131
151
  if (!response.ok) {
@@ -1156,9 +1176,9 @@ async function claudeUsage() {
1156
1176
  }
1157
1177
 
1158
1178
  // src/commands/run.ts
1159
- import { chmodSync as chmodSync3, existsSync as existsSync3, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "node:fs";
1160
- import { homedir as homedir7 } from "node:os";
1161
- import { isAbsolute as isAbsolute4, join as join12, parse, resolve as resolvePath2 } from "node:path";
1179
+ import { chmodSync as chmodSync3, existsSync as existsSync3, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "node:fs";
1180
+ import { homedir as homedir8 } from "node:os";
1181
+ import { isAbsolute as isAbsolute4, join as join13, parse, resolve as resolvePath2 } from "node:path";
1162
1182
  import chalk7 from "chalk";
1163
1183
 
1164
1184
  // ../../packages/types/src/agents/index.ts
@@ -1287,6 +1307,102 @@ var TUNNEL_USAGE_REARM_PING_PATH = "/__evident/usage-rearm";
1287
1307
  var MAX_FILE_PUSH_BYTES = 64 * 1024;
1288
1308
  var MAX_FILE_SYNC_DIRECTORIES = 16;
1289
1309
 
1310
+ // ../../packages/types/src/credential-proxy-native-config.ts
1311
+ var EVIDENT_PROXY_TOKEN_REFERENCE = "{env:EVIDENT_PROXY_TOKEN}";
1312
+ var CLAUDE_PROVIDER = {
1313
+ namespace: "evident-claude",
1314
+ name: "Evident Claude credential proxy",
1315
+ npm: "@ai-sdk/anthropic"
1316
+ };
1317
+ var NATIVE_PROVIDERS = {
1318
+ claude: CLAUDE_PROVIDER,
1319
+ anthropic: CLAUDE_PROVIDER,
1320
+ codex: {
1321
+ namespace: "evident-codex",
1322
+ name: "Evident Codex credential proxy",
1323
+ npm: "@ai-sdk/openai"
1324
+ },
1325
+ openai: {
1326
+ namespace: "evident-openai",
1327
+ name: "Evident OpenAI credential proxy",
1328
+ npm: "@ai-sdk/openai"
1329
+ },
1330
+ kimi: {
1331
+ namespace: "evident-kimi",
1332
+ name: "Evident Kimi credential proxy",
1333
+ npm: "@ai-sdk/openai-compatible",
1334
+ sdkName: "evident-kimi"
1335
+ },
1336
+ antigravity: {
1337
+ namespace: "evident-antigravity",
1338
+ name: "Evident Antigravity credential proxy",
1339
+ npm: "@ai-sdk/openai-compatible",
1340
+ sdkName: "evident-antigravity"
1341
+ },
1342
+ xai: {
1343
+ namespace: "evident-xai",
1344
+ name: "Evident xAI credential proxy",
1345
+ npm: "@ai-sdk/openai-compatible",
1346
+ sdkName: "evident-xai"
1347
+ },
1348
+ mistral: {
1349
+ namespace: "evident-mistral",
1350
+ name: "Evident Mistral credential proxy",
1351
+ npm: "@ai-sdk/openai-compatible",
1352
+ sdkName: "evident-mistral"
1353
+ }
1354
+ };
1355
+ var CREDENTIAL_PROXY_NATIVE_NAMESPACES = [
1356
+ ...new Set(Object.values(NATIVE_PROVIDERS).map(({ namespace }) => namespace))
1357
+ ];
1358
+ function credentialProxyInferenceUrl(baseUrl) {
1359
+ const trimmed = baseUrl.trim().replace(/\/+$/, "");
1360
+ return /\/v1$/i.test(trimmed) ? trimmed : `${trimmed}/v1`;
1361
+ }
1362
+ function buildCredentialProxyNativeConfig({
1363
+ inferenceBaseUrl,
1364
+ modelDescriptors
1365
+ }) {
1366
+ const baseURL = credentialProxyInferenceUrl(inferenceBaseUrl);
1367
+ const providers = {};
1368
+ const unsupportedProviders = /* @__PURE__ */ new Set();
1369
+ for (const descriptor of modelDescriptors) {
1370
+ const definition = NATIVE_PROVIDERS[descriptor.provider.trim().toLowerCase()];
1371
+ if (!definition) {
1372
+ unsupportedProviders.add(descriptor.provider.trim() || descriptor.provider);
1373
+ continue;
1374
+ }
1375
+ providers[definition.namespace] ??= definition.npm === "@ai-sdk/anthropic" ? {
1376
+ name: definition.name,
1377
+ npm: definition.npm,
1378
+ options: { baseURL, authToken: EVIDENT_PROXY_TOKEN_REFERENCE },
1379
+ models: {}
1380
+ } : definition.npm === "@ai-sdk/openai" ? {
1381
+ name: definition.name,
1382
+ npm: definition.npm,
1383
+ options: { baseURL, apiKey: EVIDENT_PROXY_TOKEN_REFERENCE },
1384
+ models: {}
1385
+ } : {
1386
+ name: definition.name,
1387
+ npm: definition.npm,
1388
+ options: {
1389
+ name: definition.sdkName,
1390
+ baseURL,
1391
+ apiKey: EVIDENT_PROXY_TOKEN_REFERENCE
1392
+ },
1393
+ models: {}
1394
+ };
1395
+ providers[definition.namespace].models[descriptor.id] = { name: descriptor.id };
1396
+ }
1397
+ return {
1398
+ unsupportedProviders: [...unsupportedProviders],
1399
+ config: {
1400
+ $schema: "https://opencode.ai/config.json",
1401
+ provider: providers
1402
+ }
1403
+ };
1404
+ }
1405
+
1290
1406
  // ../../packages/types/src/logging/index.ts
1291
1407
  var CORRELATION_ID_HEADER = "x-evident-correlation-id";
1292
1408
  var FORWARD_FAILURE_REASON_HEADER = "X-Evident-Failure-Reason";
@@ -1326,7 +1442,7 @@ import ora4 from "ora";
1326
1442
  import { select as select4 } from "@inquirer/prompts";
1327
1443
 
1328
1444
  // src/lib/telemetry.ts
1329
- var CLI_VERSION = (true ? "3.6.0" : void 0) ?? process.env.npm_package_version ?? "unknown";
1445
+ var CLI_VERSION = (true ? "3.6.1-dev.2fd7890" : void 0) ?? process.env.npm_package_version ?? "unknown";
1330
1446
  function getCliVersion() {
1331
1447
  return CLI_VERSION;
1332
1448
  }
@@ -1407,7 +1523,7 @@ async function flushEvents() {
1407
1523
  const controller = new AbortController();
1408
1524
  const timeout = setTimeout(() => controller.abort(), FLUSH_TIMEOUT_MS);
1409
1525
  try {
1410
- const request2 = {
1526
+ const request3 = {
1411
1527
  events,
1412
1528
  client_type: "cli",
1413
1529
  client_version: CLI_VERSION
@@ -1418,7 +1534,7 @@ async function flushEvents() {
1418
1534
  "Content-Type": "application/json",
1419
1535
  Authorization: authHeader
1420
1536
  },
1421
- body: JSON.stringify(request2),
1537
+ body: JSON.stringify(request3),
1422
1538
  signal: controller.signal
1423
1539
  });
1424
1540
  if (!response.ok) {
@@ -2075,7 +2191,8 @@ function createDependencyInstallDiagnosticReporter({
2075
2191
  var FORWARDED_LEVELS_BY_SOURCE = {
2076
2192
  "cli.run": /* @__PURE__ */ new Set(["warn", "error"]),
2077
2193
  opencode: /* @__PURE__ */ new Set(["warn", "error"]),
2078
- "cli.session-cleanup": /* @__PURE__ */ new Set(["info", "warn", "error"])
2194
+ "cli.session-cleanup": /* @__PURE__ */ new Set(["info", "warn", "error"]),
2195
+ "cli.runtime-provenance": /* @__PURE__ */ new Set(["info", "warn", "error"])
2079
2196
  };
2080
2197
  var SEVERITY_BY_LEVEL = {
2081
2198
  info: "info",
@@ -2871,6 +2988,58 @@ async function listAllSessions(port, client) {
2871
2988
  }
2872
2989
  return { sessions };
2873
2990
  }
2991
+ var INTERRUPT_NETWORK_CODES = /* @__PURE__ */ new Set([
2992
+ "ABORT_ERR",
2993
+ "EAI_AGAIN",
2994
+ "ECONNREFUSED",
2995
+ "ECONNRESET",
2996
+ "ENETUNREACH",
2997
+ "ENOTFOUND",
2998
+ "ETIMEDOUT",
2999
+ "UND_ERR_CONNECT_TIMEOUT",
3000
+ "UND_ERR_HEADERS_TIMEOUT",
3001
+ "UND_ERR_SOCKET"
3002
+ ]);
3003
+ function interruptFailureDiagnostic(error2) {
3004
+ const message = error2 instanceof Error ? error2.message : "";
3005
+ const httpStatus = /^OpenCode request failed: HTTP ([1-5]\d{2})(?::|$)/.exec(message)?.[1];
3006
+ if (httpStatus) return `HTTP ${httpStatus}`;
3007
+ const candidates = [error2, error2 instanceof Error ? error2.cause : void 0];
3008
+ for (const candidate of candidates) {
3009
+ if (typeof candidate !== "object" || candidate === null || !("code" in candidate)) continue;
3010
+ const code = candidate.code;
3011
+ if (typeof code === "string" && INTERRUPT_NETWORK_CODES.has(code)) {
3012
+ return redactDiagnosticText(`network ${code}`).slice(0, 120);
3013
+ }
3014
+ }
3015
+ const name = error2 instanceof Error && ["AbortError", "Error", "TimeoutError", "TypeError"].includes(error2.name) ? error2.name : "unknown error";
3016
+ return redactDiagnosticText(`request threw ${name}`).slice(0, 120);
3017
+ }
3018
+ async function interruptSession(port, id, client) {
3019
+ if (client?.version === "v2") {
3020
+ try {
3021
+ await interruptV2Session(client, id);
3022
+ return { kind: "confirmed_stopped" };
3023
+ } catch (error2) {
3024
+ return { kind: "not_confirmed", diagnostic: interruptFailureDiagnostic(error2) };
3025
+ }
3026
+ }
3027
+ try {
3028
+ const res = await requestWithClient(
3029
+ port,
3030
+ client,
3031
+ `/session/${id}/abort`,
3032
+ { method: "POST" },
3033
+ { allowStatuses: ALL_HTTP_STATUSES }
3034
+ );
3035
+ if (res.status >= 200 && res.status < 300) {
3036
+ return { kind: "confirmed_stopped", status: res.status };
3037
+ }
3038
+ return { kind: "not_confirmed", status: res.status };
3039
+ } catch (error2) {
3040
+ return { kind: "threw", diagnostic: interruptFailureDiagnostic(error2) };
3041
+ }
3042
+ }
2874
3043
  async function deleteSession(port, id, client) {
2875
3044
  if (client?.version === "v2") return deleteV2Session(client, id);
2876
3045
  try {
@@ -4221,6 +4390,11 @@ async function listV2Permissions(client, sessionId) {
4221
4390
  return null;
4222
4391
  }
4223
4392
  }
4393
+ async function interruptV2Session(client, sessionId) {
4394
+ await client.request(`/api/session/${encodeURIComponent(sessionId)}/interrupt`, {
4395
+ method: "POST"
4396
+ });
4397
+ }
4224
4398
  async function getV2ActiveSessions(client) {
4225
4399
  try {
4226
4400
  const body = await readJson(await client.request("/api/session/active"));
@@ -4623,11 +4797,11 @@ function failureMessage(status2) {
4623
4797
  function isRetryableStatus(status2) {
4624
4798
  return CONNECT_KEY_RETRY_STATUSES.includes(status2);
4625
4799
  }
4626
- async function importOneCredential(request2, credential, sleepForRetry, log3) {
4800
+ async function importOneCredential(request3, credential, sleepForRetry, log3) {
4627
4801
  const path = `/api/integration/${encodeURIComponent(credential.integrationId)}/connect/key`;
4628
4802
  for (let attempt = 1; attempt <= CONNECT_KEY_MAX_ATTEMPTS; attempt += 1) {
4629
4803
  try {
4630
- const response = await request2(
4804
+ const response = await request3(
4631
4805
  path,
4632
4806
  {
4633
4807
  method: "POST",
@@ -4662,7 +4836,7 @@ async function importOneCredential(request2, credential, sleepForRetry, log3) {
4662
4836
  throw new Error("OpenCode credential import retry loop did not return");
4663
4837
  }
4664
4838
  async function importOpenCodeCredentials({
4665
- request: request2,
4839
+ request: request3,
4666
4840
  classified,
4667
4841
  connectedIntegrationIds,
4668
4842
  sleepForRetry = sleep2,
@@ -4674,7 +4848,7 @@ async function importOpenCodeCredentials({
4674
4848
  result.alreadyConnected.push(credential.integrationId);
4675
4849
  continue;
4676
4850
  }
4677
- const outcome = await importOneCredential(request2, credential, sleepForRetry, log3);
4851
+ const outcome = await importOneCredential(request3, credential, sleepForRetry, log3);
4678
4852
  if (outcome.ok) result.imported.push(credential.integrationId);
4679
4853
  else result.failed.push(outcome.failure);
4680
4854
  }
@@ -4692,7 +4866,7 @@ function parseIntegrationList(value) {
4692
4866
  return { integrationIds, hasData: value.data.length > 0 };
4693
4867
  }
4694
4868
  async function getConnectedOpenCodeIntegrationIds({
4695
- request: request2,
4869
+ request: request3,
4696
4870
  directory,
4697
4871
  sleepForRetry = sleep2,
4698
4872
  log: log3
@@ -4700,7 +4874,7 @@ async function getConnectedOpenCodeIntegrationIds({
4700
4874
  const path = `/api/integration?location%5Bdirectory%5D=${encodeURIComponent(directory)}`;
4701
4875
  for (let attempt = 1; attempt <= INTEGRATION_LIST_MAX_READS; attempt += 1) {
4702
4876
  try {
4703
- const response = await request2(path);
4877
+ const response = await request3(path);
4704
4878
  const parsed = parseIntegrationList(await response.json());
4705
4879
  if (parsed === null) {
4706
4880
  log3(
@@ -4729,7 +4903,7 @@ async function getConnectedOpenCodeIntegrationIds({
4729
4903
  return null;
4730
4904
  }
4731
4905
  async function importOpenCodeV2Credentials({
4732
- request: request2,
4906
+ request: request3,
4733
4907
  homeDir,
4734
4908
  directory,
4735
4909
  sleepForRetry = sleep2,
@@ -4747,7 +4921,7 @@ async function importOpenCodeV2Credentials({
4747
4921
  const classified = classifyOpenCodeCredentials(credentials2);
4748
4922
  if (classified.importable.length === 0) return emptyResult(classified.unsupported);
4749
4923
  const connectedIntegrationIds = await getConnectedOpenCodeIntegrationIds({
4750
- request: request2,
4924
+ request: request3,
4751
4925
  directory,
4752
4926
  sleepForRetry,
4753
4927
  log: log3
@@ -4756,7 +4930,7 @@ async function importOpenCodeV2Credentials({
4756
4930
  return emptyResult(classified.unsupported, "connection-list-unavailable");
4757
4931
  }
4758
4932
  return importOpenCodeCredentials({
4759
- request: request2,
4933
+ request: request3,
4760
4934
  classified,
4761
4935
  connectedIntegrationIds,
4762
4936
  sleepForRetry,
@@ -7141,8 +7315,8 @@ import { chmod, mkdir, open as open3, realpath, rename, unlink } from "node:fs/p
7141
7315
  import { basename as basename2, dirname as dirname6, isAbsolute as isAbsolute2, join as join9, relative as relative2, resolve as resolve3, sep as sep2 } from "node:path";
7142
7316
  var FILE_MODE = 384;
7143
7317
  var DIRECTORY_MODE = 448;
7144
- async function writePushedFile(request2) {
7145
- const { requestedPath, content, allowedDirectories, homeDir } = request2;
7318
+ async function writePushedFile(request3) {
7319
+ const { requestedPath, content, allowedDirectories, homeDir } = request3;
7146
7320
  const bytes = content.byteLength;
7147
7321
  if (allowedDirectories.length === 0) {
7148
7322
  return refuse("file_sync_disabled", "File sync is not enabled on this runner.", {
@@ -8891,6 +9065,34 @@ var ChannelDriver = class _ChannelDriver {
8891
9065
  if (state === "failed" && isAbortedTerminalReply(messages, ocId ?? "")) {
8892
9066
  const ongoing = await this.isSessionOngoing(sessionId);
8893
9067
  if (ongoing === false) {
9068
+ const age = this.absoluteAgeForRow(message.processing_started_at);
9069
+ if (age.tripped && age.anchorMs !== null) {
9070
+ try {
9071
+ await this.markFailed(
9072
+ conv.id,
9073
+ message.id,
9074
+ sessionId,
9075
+ this.absoluteCeilingError(message.id, sessionId, age.anchorMs),
9076
+ void 0,
9077
+ {
9078
+ classification: null,
9079
+ errorShape: extractReplyErrorShape(findLastAssistantReplyFor(messages, ocId ?? ""))
9080
+ }
9081
+ );
9082
+ } catch (err) {
9083
+ if (err instanceof ChannelAuthError) throw err;
9084
+ this.log({
9085
+ level: "warn",
9086
+ message: `Re-drive: failed to report past-ceiling restart orphan ${message.id.slice(0, 8)} terminal; refusing to re-dispatch: ${err instanceof Error ? err.message : String(err)}`,
9087
+ conversation_id: conv.id,
9088
+ message_id: message.id
9089
+ });
9090
+ return "unresolved";
9091
+ }
9092
+ this.clearRedriveUnresolved(message.id);
9093
+ void this.postSignal(conv.id, message.id, "redrive_ceiling_refused");
9094
+ return "settled";
9095
+ }
8894
9096
  this.log({
8895
9097
  level: "info",
8896
9098
  message: `Re-drive: message ${message.id.slice(0, 8)} carries an abort-shaped terminal error and session ${sessionId.slice(0, 8)} is not-ongoing per the active-session status check \u2014 restart orphan, re-dispatching instead of marking it permanently failed`,
@@ -8961,6 +9163,29 @@ var ChannelDriver = class _ChannelDriver {
8961
9163
  message_id: message.id
8962
9164
  });
8963
9165
  }
9166
+ if (this.absoluteAgeForRow(message.processing_started_at).tripped) {
9167
+ try {
9168
+ await this.markFailed(
9169
+ conv.id,
9170
+ message.id,
9171
+ sessionId,
9172
+ this.absoluteCeilingError(message.id, sessionId, anchorMs)
9173
+ );
9174
+ } catch (err) {
9175
+ if (err instanceof ChannelAuthError) throw err;
9176
+ this.log({
9177
+ level: "warn",
9178
+ message: `Re-drive: failed to report past-ceiling message ${message.id.slice(0, 8)} terminal; refusing to re-attach: ${err instanceof Error ? err.message : String(err)}`,
9179
+ conversation_id: conv.id,
9180
+ message_id: message.id
9181
+ });
9182
+ const bound = await this.boundRedriveOutcome(conv, message, "reattach");
9183
+ return bound === "abandoned" ? "abandoned" : "unresolved";
9184
+ }
9185
+ this.clearRedriveUnresolved(message.id);
9186
+ void this.postSignal(conv.id, message.id, "redrive_ceiling_refused");
9187
+ return "abandoned";
9188
+ }
8964
9189
  const title = await this.resolveSessionTitle(sessionId, conv.id);
8965
9190
  try {
8966
9191
  await this.markProcessing(conv.id, message.id, sessionId, ocId, title);
@@ -9227,8 +9452,7 @@ var ChannelDriver = class _ChannelDriver {
9227
9452
  const since = this.redriveOutcomeFailingSince.get(message.id);
9228
9453
  if (since === void 0) this.redriveOutcomeFailingSince.set(message.id, now);
9229
9454
  const durationTripped = now - (since ?? now) >= this.pausedMaxWaitMs;
9230
- const parsed = message.processing_started_at ? Date.parse(message.processing_started_at) : NaN;
9231
- const absoluteAgeTripped = !Number.isNaN(parsed) && now - parsed >= ABSOLUTE_MAX_PROCESSING_MS;
9455
+ const absoluteAgeTripped = this.absoluteAgeForRow(message.processing_started_at).tripped;
9232
9456
  if (!durationTripped && !absoluteAgeTripped) {
9233
9457
  this.signalRedriveOutcomeUnreported(conv, message, outcome);
9234
9458
  return "retry";
@@ -9267,6 +9491,15 @@ var ChannelDriver = class _ChannelDriver {
9267
9491
  });
9268
9492
  return "abandoned";
9269
9493
  }
9494
+ /** Row-level guards use the durable server stamp; in-flight guards use processingAnchorMs. */
9495
+ absoluteAgeForRow(stamp) {
9496
+ const anchorMs = stamp ? Date.parse(stamp) : NaN;
9497
+ return Number.isNaN(anchorMs) ? { tripped: false, anchorMs: null } : { tripped: this.now() - anchorMs >= ABSOLUTE_MAX_PROCESSING_MS, anchorMs };
9498
+ }
9499
+ absoluteCeilingError(messageId, sessionId, anchorMs, retainedReason = this.readRetainedSessionError(sessionId)) {
9500
+ const baseError = `Message ${messageId.slice(0, 8)} exceeded the absolute processing ceiling (${Math.round((this.now() - anchorMs) / 6e4)}min, session ${sessionId}) while still actively running.`;
9501
+ return retainedReason ? `${baseError} ${SESSION_ERROR_RETAINED_DETAIL}${retainedReason}` : baseError;
9502
+ }
9270
9503
  /**
9271
9504
  * Record one poll outcome toward the re-drive fence's consecutive-identical-
9272
9505
  * failure streak (#1348) and return the resulting count. `signature === null`
@@ -9708,10 +9941,7 @@ var ChannelDriver = class _ChannelDriver {
9708
9941
  opencodeMessageId,
9709
9942
  message,
9710
9943
  dispatchedAt: this.now(),
9711
- // Anchor the absolute-age ceiling to the SERVER-SIDE `processed_at`, NOT
9712
- // `dispatchedAt` — so a re-adopted zombie's age reflects the real turn duration
9713
- // and the ceiling fires on the ORIGINAL turn.
9714
- processingAnchorMs: processedAtMs,
9944
+ processingAnchorMs: this.absoluteAgeForRow(message.processing_started_at).anchorMs ?? processedAtMs,
9715
9945
  deadline: watchDeadlineAnchorMs + this.pausedMaxWaitMs,
9716
9946
  // The server row is ALREADY `processing`; do not re-fire markProcessing.
9717
9947
  started: true,
@@ -10162,7 +10392,9 @@ var ChannelDriver = class _ChannelDriver {
10162
10392
  }
10163
10393
  const { openQuestions, openPermissions, questionsPolledOk, permissionsPolledOk } = await this.pollInteractions(sessionId, watcher, messages);
10164
10394
  if (watcher.generation !== generation) return;
10395
+ const ceilingAttemptsThisTick = /* @__PURE__ */ new Set();
10165
10396
  for (const inFlight of [...watcher.inFlight.values()]) {
10397
+ if (ceilingAttemptsThisTick.has(inFlight.evidentMessageId)) continue;
10166
10398
  await this.serviceInFlightMessage(
10167
10399
  sessionId,
10168
10400
  watcher,
@@ -10171,7 +10403,8 @@ var ChannelDriver = class _ChannelDriver {
10171
10403
  openQuestions,
10172
10404
  openPermissions,
10173
10405
  questionsPolledOk,
10174
- permissionsPolledOk
10406
+ permissionsPolledOk,
10407
+ ceilingAttemptsThisTick
10175
10408
  );
10176
10409
  }
10177
10410
  }
@@ -10212,13 +10445,117 @@ var ChannelDriver = class _ChannelDriver {
10212
10445
  inFlight.deadline = this.now() + this.pausedMaxWaitMs;
10213
10446
  }
10214
10447
  }
10448
+ async settleAbsoluteCeiling(sessionId, watcher, messages, ceilingAttemptsThisTick) {
10449
+ const entries = [...watcher.inFlight.values()];
10450
+ const pastCeiling = entries.filter(
10451
+ (entry) => this.now() - entry.processingAnchorMs >= ABSOLUTE_MAX_PROCESSING_MS
10452
+ );
10453
+ const healthySiblings = entries.length - pastCeiling.length;
10454
+ const landed = [];
10455
+ let allRecordsLanded = true;
10456
+ const retainedReason = this.readRetainedSessionError(sessionId);
10457
+ for (const entry of pastCeiling) {
10458
+ if (!watcher.inFlight.has(entry.evidentMessageId)) continue;
10459
+ ceilingAttemptsThisTick.add(entry.evidentMessageId);
10460
+ const error2 = this.absoluteCeilingError(
10461
+ entry.evidentMessageId,
10462
+ sessionId,
10463
+ entry.processingAnchorMs,
10464
+ retainedReason
10465
+ );
10466
+ this.log({
10467
+ level: "warn",
10468
+ message: error2,
10469
+ conversation_id: watcher.conv.id,
10470
+ message_id: entry.evidentMessageId
10471
+ });
10472
+ try {
10473
+ await this.markFailed(
10474
+ watcher.conv.id,
10475
+ entry.evidentMessageId,
10476
+ sessionId,
10477
+ error2,
10478
+ void 0,
10479
+ {
10480
+ classification: null,
10481
+ errorShape: extractReplyErrorShape(
10482
+ findLastAssistantReplyFor(messages, entry.opencodeMessageId)
10483
+ )
10484
+ }
10485
+ );
10486
+ } catch (err) {
10487
+ if (err instanceof ChannelAuthError) throw err;
10488
+ allRecordsLanded = false;
10489
+ const watched_for_ms = this.now() - entry.processingAnchorMs;
10490
+ if (err instanceof ChannelTerminalError) {
10491
+ void this.postSignal(watcher.conv.id, entry.evidentMessageId, "gave_up", {
10492
+ watched_for_ms,
10493
+ interrupt_outcome: "record_terminal"
10494
+ });
10495
+ this.removeInFlight(watcher, entry.evidentMessageId);
10496
+ continue;
10497
+ }
10498
+ if (this.now() >= entry.deadline) {
10499
+ void this.postSignal(watcher.conv.id, entry.evidentMessageId, "gave_up", {
10500
+ watched_for_ms,
10501
+ interrupt_outcome: "record_deadline"
10502
+ });
10503
+ this.removeInFlight(watcher, entry.evidentMessageId);
10504
+ continue;
10505
+ }
10506
+ void this.postSignal(watcher.conv.id, entry.evidentMessageId, "gave_up", {
10507
+ watched_for_ms,
10508
+ interrupt_outcome: "record_retry"
10509
+ });
10510
+ this.log({
10511
+ level: "warn",
10512
+ message: `Failed to record the absolute-ceiling failure for message ${entry.evidentMessageId.slice(0, 8)}; leaving its running turn untouched and retrying next tick: ${err instanceof Error ? err.message : String(err)}`,
10513
+ conversation_id: watcher.conv.id,
10514
+ message_id: entry.evidentMessageId
10515
+ });
10516
+ continue;
10517
+ }
10518
+ entry.done = true;
10519
+ landed.push(entry);
10520
+ }
10521
+ if (!allRecordsLanded) {
10522
+ for (const entry of landed) this.removeInFlight(watcher, entry.evidentMessageId);
10523
+ return;
10524
+ }
10525
+ if (healthySiblings > 0) {
10526
+ for (const entry of landed) {
10527
+ void this.postSignal(watcher.conv.id, entry.evidentMessageId, "gave_up", {
10528
+ watched_for_ms: this.now() - entry.processingAnchorMs,
10529
+ interrupt_outcome: "deferred_for_healthy_sibling",
10530
+ healthy_siblings: healthySiblings
10531
+ });
10532
+ this.removeInFlight(watcher, entry.evidentMessageId);
10533
+ }
10534
+ return;
10535
+ }
10536
+ const interrupt = await interruptSession(this.port, sessionId, this.openCodeClient);
10537
+ for (const entry of landed) {
10538
+ void this.postSignal(watcher.conv.id, entry.evidentMessageId, "gave_up", {
10539
+ watched_for_ms: this.now() - entry.processingAnchorMs,
10540
+ interrupt_outcome: interrupt.kind
10541
+ });
10542
+ this.removeInFlight(watcher, entry.evidentMessageId);
10543
+ }
10544
+ if (interrupt.kind !== "confirmed_stopped") {
10545
+ this.log({
10546
+ level: "warn",
10547
+ message: `Absolute-ceiling records landed for session ${sessionId}, but OpenCode interruption was ${interrupt.kind}${interrupt.diagnostic ? ` (${interrupt.diagnostic})` : "status" in interrupt && typeof interrupt.status === "number" ? ` (HTTP ${interrupt.status})` : ""}; the rows are terminal and released, while the session may still be running`,
10548
+ conversation_id: watcher.conv.id
10549
+ });
10550
+ }
10551
+ }
10215
10552
  /**
10216
10553
  * Drive ONE in-flight message's lifecycle from the tick's message snapshot.
10217
10554
  * Fires markProcessing on queued→running and markDone on done (each once),
10218
10555
  * applies the idle-path re-dispatch guard, and removes the message from the
10219
10556
  * in-flight set on completion or timeout.
10220
10557
  */
10221
- async serviceInFlightMessage(sessionId, watcher, inFlight, messages, openQuestions, openPermissions, questionsPolledOk, permissionsPolledOk) {
10558
+ async serviceInFlightMessage(sessionId, watcher, inFlight, messages, openQuestions, openPermissions, questionsPolledOk, permissionsPolledOk, ceilingAttemptsThisTick) {
10222
10559
  const conv = watcher.conv;
10223
10560
  const state = messageRunState(messages, inFlight.opencodeMessageId);
10224
10561
  const id = inFlight.evidentMessageId;
@@ -10562,17 +10899,8 @@ var ChannelDriver = class _ChannelDriver {
10562
10899
  return;
10563
10900
  }
10564
10901
  }
10565
- if (activelyRunning && this.now() - inFlight.processingAnchorMs >= ABSOLUTE_MAX_PROCESSING_MS) {
10566
- this.log({
10567
- level: "warn",
10568
- message: `Message ${inFlight.evidentMessageId.slice(0, 8)} exceeded the absolute processing ceiling (${Math.round((this.now() - inFlight.processingAnchorMs) / 6e4)}min, session ${sessionId}) while still actively running \u2014 releasing so the cron can reclaim it`,
10569
- conversation_id: conv.id,
10570
- message_id: inFlight.evidentMessageId
10571
- });
10572
- void this.postSignal(conv.id, inFlight.evidentMessageId, "gave_up", {
10573
- watched_for_ms: this.now() - inFlight.processingAnchorMs
10574
- });
10575
- this.removeInFlight(watcher, inFlight.evidentMessageId);
10902
+ if (watcher.inFlight.has(inFlight.evidentMessageId) && activelyRunning && this.now() - inFlight.processingAnchorMs >= ABSOLUTE_MAX_PROCESSING_MS) {
10903
+ await this.settleAbsoluteCeiling(sessionId, watcher, messages, ceilingAttemptsThisTick);
10576
10904
  return;
10577
10905
  }
10578
10906
  const heartbeatDue = activelyRunning && !inFlight.awaitingHumanLatched && !inFlight.aliveInFlight && this.now() - inFlight.lastAliveAt >= HEARTBEAT_MS;
@@ -11195,6 +11523,33 @@ var ChannelDriver = class _ChannelDriver {
11195
11523
  }
11196
11524
  const restartAborted = state === "failed" && sessionOngoing === false && isAbortedTerminalReply(messages, ocId ?? "");
11197
11525
  if (restartAborted) {
11526
+ const age = this.absoluteAgeForRow(row.processing_started_at);
11527
+ if (age.tripped && age.anchorMs !== null) {
11528
+ try {
11529
+ await this.markFailed(
11530
+ row.conversation_id,
11531
+ row.id,
11532
+ sessionId,
11533
+ this.absoluteCeilingError(row.id, sessionId, age.anchorMs),
11534
+ void 0,
11535
+ {
11536
+ classification: null,
11537
+ errorShape: extractReplyErrorShape(findLastAssistantReplyFor(messages, ocId ?? ""))
11538
+ }
11539
+ );
11540
+ } catch (err) {
11541
+ if (err instanceof ChannelAuthError) throw err;
11542
+ this.log({
11543
+ level: "warn",
11544
+ message: `Re-adopt: failed to report past-ceiling restart orphan ${row.id.slice(0, 8)} terminal; refusing to re-dispatch: ${err instanceof Error ? err.message : String(err)}`,
11545
+ conversation_id: row.conversation_id,
11546
+ message_id: row.id
11547
+ });
11548
+ return;
11549
+ }
11550
+ void this.postSignal(row.conversation_id, row.id, "redrive_ceiling_refused");
11551
+ return;
11552
+ }
11198
11553
  this.log({
11199
11554
  level: "info",
11200
11555
  message: `Re-adopt: message ${row.id.slice(0, 8)} carries an abort-shaped terminal error and session ${sessionId.slice(0, 8)} is not-ongoing per the active-session status check \u2014 restart orphan, re-dispatching instead of marking it permanently failed`,
@@ -11740,7 +12095,8 @@ var ChannelDriver = class _ChannelDriver {
11740
12095
  source_message_id: row.source_message_id,
11741
12096
  slack_user_id: row.slack_user_id,
11742
12097
  attachments: row.attachments ?? null,
11743
- opencode_message_id: row.opencode_message_id
12098
+ opencode_message_id: row.opencode_message_id,
12099
+ processing_started_at: row.processing_started_at
11744
12100
  };
11745
12101
  }
11746
12102
  /**
@@ -13538,6 +13894,38 @@ async function configureGitHubAccess({ env, log: log3 }) {
13538
13894
  import { execFileSync as execFileSync2 } from "node:child_process";
13539
13895
  import { copyFileSync, existsSync as existsSync2, statSync as statSync6 } from "node:fs";
13540
13896
  import { isAbsolute as isAbsolute3, join as join11, resolve as resolve4 } from "node:path";
13897
+ function parseConfigContent(content) {
13898
+ if (!content?.trim()) return {};
13899
+ const parsed = JSON.parse(content);
13900
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
13901
+ throw new Error("OPENCODE_CONFIG_CONTENT must contain a JSON object");
13902
+ }
13903
+ return parsed;
13904
+ }
13905
+ function mergeManagedOpenCodeConfigContent(content, managedProviders) {
13906
+ const config = parseConfigContent(content);
13907
+ const provider = typeof config.provider === "object" && config.provider !== null && !Array.isArray(config.provider) ? { ...config.provider } : {};
13908
+ for (const namespace of CREDENTIAL_PROXY_NATIVE_NAMESPACES) {
13909
+ delete provider[namespace];
13910
+ }
13911
+ return JSON.stringify({ ...config, provider: { ...provider, ...managedProviders } });
13912
+ }
13913
+ function removeManagedOpenCodeConfigContent(content) {
13914
+ if (!content?.trim()) return content;
13915
+ const config = parseConfigContent(content);
13916
+ if (typeof config.provider !== "object" || config.provider === null || Array.isArray(config.provider)) {
13917
+ return content;
13918
+ }
13919
+ const provider = { ...config.provider };
13920
+ let changed = false;
13921
+ for (const namespace of CREDENTIAL_PROXY_NATIVE_NAMESPACES) {
13922
+ if (namespace in provider) {
13923
+ delete provider[namespace];
13924
+ changed = true;
13925
+ }
13926
+ }
13927
+ return changed ? JSON.stringify({ ...config, provider }) : content;
13928
+ }
13541
13929
  function isFile(filePath) {
13542
13930
  return existsSync2(filePath) && statSync6(filePath).isFile();
13543
13931
  }
@@ -13571,6 +13959,159 @@ function applyRunnerOpenCodeConfig({
13571
13959
  log3(`Applied runner OpenCode config ${source} to ${join11(cwd, target)}`);
13572
13960
  }
13573
13961
 
13962
+ // src/lib/opencode/runtime-provenance.ts
13963
+ import { readFileSync as readFileSync7, readdirSync } from "node:fs";
13964
+ import { homedir as homedir7 } from "node:os";
13965
+ import { join as join12 } from "node:path";
13966
+ var PACKAGE_NAME = /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/i;
13967
+ var REGISTRY_SPEC = /^[a-z0-9.*+^~<>=|-]+$/i;
13968
+ function errorKind(error2) {
13969
+ if (!(error2 instanceof Error)) return "unknown error";
13970
+ const code = "code" in error2 && typeof error2.code === "string" ? ` (${error2.code})` : "";
13971
+ return `${error2.name}${code}`;
13972
+ }
13973
+ function parseRegistryPluginSpec(value) {
13974
+ const separator = value.lastIndexOf("@");
13975
+ if (separator <= 0) return null;
13976
+ const name = value.slice(0, separator);
13977
+ const spec = value.slice(separator + 1);
13978
+ if (!PACKAGE_NAME.test(name) || !REGISTRY_SPEC.test(spec)) return null;
13979
+ return { name, spec };
13980
+ }
13981
+ function parseJsonc(value) {
13982
+ let output = "";
13983
+ let inString = false;
13984
+ let escaped = false;
13985
+ let lineComment = false;
13986
+ let blockComment = false;
13987
+ for (let index = 0; index < value.length; index++) {
13988
+ const character = value[index];
13989
+ const next = value[index + 1];
13990
+ if (lineComment) {
13991
+ if (character === "\n") {
13992
+ lineComment = false;
13993
+ output += character;
13994
+ }
13995
+ continue;
13996
+ }
13997
+ if (blockComment) {
13998
+ if (character === "*" && next === "/") {
13999
+ blockComment = false;
14000
+ index++;
14001
+ } else if (character === "\n") {
14002
+ output += character;
14003
+ }
14004
+ continue;
14005
+ }
14006
+ if (inString) {
14007
+ output += character;
14008
+ if (escaped) escaped = false;
14009
+ else if (character === "\\") escaped = true;
14010
+ else if (character === '"') inString = false;
14011
+ continue;
14012
+ }
14013
+ if (character === '"') {
14014
+ inString = true;
14015
+ output += character;
14016
+ } else if (character === "/" && next === "/") {
14017
+ lineComment = true;
14018
+ index++;
14019
+ } else if (character === "/" && next === "*") {
14020
+ blockComment = true;
14021
+ index++;
14022
+ } else if (character === ",") {
14023
+ let lookahead = index + 1;
14024
+ while (/\s/.test(value[lookahead] ?? "")) lookahead++;
14025
+ if (value[lookahead] !== "]" && value[lookahead] !== "}") output += character;
14026
+ } else {
14027
+ output += character;
14028
+ }
14029
+ }
14030
+ return JSON.parse(output);
14031
+ }
14032
+ function openCodeCacheRoot(env = process.env, homeDir = homedir7()) {
14033
+ return join12(env.XDG_CACHE_HOME ?? join12(homeDir, ".cache"), "opencode");
14034
+ }
14035
+ function readConfiguredPluginSpecs({
14036
+ cwd = process.cwd(),
14037
+ log: log3
14038
+ }) {
14039
+ let config;
14040
+ try {
14041
+ try {
14042
+ config = parseJsonc(readFileSync7(join12(cwd, "opencode.jsonc"), "utf8"));
14043
+ } catch (error2) {
14044
+ if (!("code" in Object(error2)) || Object(error2).code !== "ENOENT") throw error2;
14045
+ config = parseJsonc(readFileSync7(join12(cwd, "opencode.json"), "utf8"));
14046
+ }
14047
+ } catch (error2) {
14048
+ log3(`Could not read OpenCode plugin configuration: ${errorKind(error2)}`);
14049
+ return [];
14050
+ }
14051
+ if (typeof config !== "object" || config === null || !("plugin" in config)) return [];
14052
+ const plugins = config.plugin;
14053
+ if (!Array.isArray(plugins)) {
14054
+ log3("Could not read OpenCode plugin configuration: plugin is not an array");
14055
+ return [];
14056
+ }
14057
+ const configured = plugins.filter(
14058
+ (plugin) => typeof plugin === "string" && parseRegistryPluginSpec(plugin) !== null
14059
+ );
14060
+ if (configured.length !== plugins.length) {
14061
+ log3(
14062
+ `Skipped ${plugins.length - configured.length} non-registry OpenCode plugin spec(s); their values were not recorded`
14063
+ );
14064
+ }
14065
+ return configured;
14066
+ }
14067
+ function resolveInstalledPluginVersions({
14068
+ cacheRoot,
14069
+ env = process.env,
14070
+ homeDir = homedir7(),
14071
+ log: log3
14072
+ }) {
14073
+ const packagesRoot = join12(cacheRoot ?? openCodeCacheRoot(env, homeDir), "packages");
14074
+ let directories;
14075
+ try {
14076
+ directories = readdirSync(packagesRoot, { withFileTypes: true });
14077
+ } catch (error2) {
14078
+ if ("code" in Object(error2) && Object(error2).code === "ENOENT") return [];
14079
+ log3(`Could not inspect the OpenCode plugin cache: ${errorKind(error2)}`);
14080
+ return [];
14081
+ }
14082
+ const plugins = [];
14083
+ let skipped = 0;
14084
+ for (const directory of directories) {
14085
+ if (!directory.isDirectory()) continue;
14086
+ const parsed = parseRegistryPluginSpec(directory.name);
14087
+ if (parsed === null) {
14088
+ skipped++;
14089
+ continue;
14090
+ }
14091
+ let resolvedVersion = null;
14092
+ try {
14093
+ const packageJson = JSON.parse(
14094
+ readFileSync7(
14095
+ join12(packagesRoot, directory.name, "node_modules", parsed.name, "package.json"),
14096
+ "utf8"
14097
+ )
14098
+ );
14099
+ if (typeof packageJson.version === "string" && REGISTRY_SPEC.test(packageJson.version)) {
14100
+ resolvedVersion = packageJson.version;
14101
+ } else {
14102
+ log3(`Could not resolve OpenCode plugin ${parsed.name}@${parsed.spec}: invalid version`);
14103
+ }
14104
+ } catch (error2) {
14105
+ log3(`Could not resolve OpenCode plugin ${parsed.name}@${parsed.spec}: ${errorKind(error2)}`);
14106
+ }
14107
+ plugins.push({ ...parsed, resolvedVersion });
14108
+ }
14109
+ if (skipped > 0) {
14110
+ log3(`Skipped ${skipped} non-registry OpenCode package cache entry; its value was not recorded`);
14111
+ }
14112
+ return plugins;
14113
+ }
14114
+
13574
14115
  // src/lib/credential-sync.ts
13575
14116
  import { renameSync, writeFileSync as writeFileSync5 } from "node:fs";
13576
14117
  var CREDENTIAL_SYNC_TICK_TIMEOUT_MS = 3e4;
@@ -13806,6 +14347,230 @@ function createCredentialSync({
13806
14347
  };
13807
14348
  }
13808
14349
 
14350
+ // src/lib/managed-inference-setup.ts
14351
+ var defaultStore = {
14352
+ get: getManagedInferenceCredential,
14353
+ set: setManagedInferenceCredential,
14354
+ clear: clearManagedInferenceCredential
14355
+ };
14356
+ function endpoint(options, operation) {
14357
+ return `${options.apiUrl}/runners/${options.agentId}/managed-credentials/${operation}`;
14358
+ }
14359
+ function requestHeaders(options) {
14360
+ return { Authorization: options.getAuthHeader(), "Content-Type": "application/json" };
14361
+ }
14362
+ function request2(options) {
14363
+ return withRequestTimeout(options.fetchImpl ?? fetch, REQUEST_TIMEOUT_MS);
14364
+ }
14365
+ function safeFailure(operation, status2) {
14366
+ return status2 === void 0 ? `${operation} could not reach the managed credential service` : `${operation} returned HTTP ${status2}`;
14367
+ }
14368
+ function logTransportFailure(options, operation, error2, authorization) {
14369
+ let detail = error2 instanceof Error ? error2.message : String(error2);
14370
+ const credential = authorization.split(/\s+/, 2)[1];
14371
+ for (const secret of [authorization, credential]) {
14372
+ if (secret) detail = detail.replaceAll(secret, "[redacted]");
14373
+ }
14374
+ options.log(
14375
+ `MANAGED-INFERENCE-TRANSPORT-FAILED: operation=${operation} detail=${redactDiagnosticText(detail)}`,
14376
+ "debug"
14377
+ );
14378
+ }
14379
+ async function responseJson(response) {
14380
+ try {
14381
+ const value = await response.json();
14382
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
14383
+ } catch (error2) {
14384
+ return null;
14385
+ }
14386
+ }
14387
+ function readSetup(value) {
14388
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return null;
14389
+ const setup = value;
14390
+ if (typeof setup.runner_id !== "string" || typeof setup.generation !== "string" || typeof setup.status !== "string" || setup.token_id !== null && typeof setup.token_id !== "string") {
14391
+ return null;
14392
+ }
14393
+ return setup;
14394
+ }
14395
+ function clearLocalSetup(options) {
14396
+ const env = options.env ?? process.env;
14397
+ (options.store ?? defaultStore).clear(options.agentId);
14398
+ delete env.EVIDENT_PROXY_TOKEN;
14399
+ try {
14400
+ const content = removeManagedOpenCodeConfigContent(env.OPENCODE_CONFIG_CONTENT);
14401
+ if (content === void 0) delete env.OPENCODE_CONFIG_CONTENT;
14402
+ else env.OPENCODE_CONFIG_CONTENT = content;
14403
+ } catch (error2) {
14404
+ options.log(
14405
+ "MANAGED-INFERENCE-CLEANUP-PARTIAL: stored credentials were removed, but malformed OPENCODE_CONFIG_CONTENT could not be inspected",
14406
+ "warn"
14407
+ );
14408
+ }
14409
+ }
14410
+ async function discoverManagedInferenceSetup(options) {
14411
+ const headers = requestHeaders(options);
14412
+ let response;
14413
+ try {
14414
+ response = await request2(options)(endpoint(options, "current"), {
14415
+ headers
14416
+ });
14417
+ } catch (error2) {
14418
+ logTransportFailure(options, "discovery", error2, headers.Authorization);
14419
+ return { kind: "failed", reason: safeFailure("Managed setup discovery") };
14420
+ }
14421
+ if (response.status === 404) {
14422
+ clearLocalSetup(options);
14423
+ return { kind: "none" };
14424
+ }
14425
+ if (!response.ok) {
14426
+ return { kind: "failed", reason: safeFailure("Managed setup discovery", response.status) };
14427
+ }
14428
+ const body = await responseJson(response);
14429
+ const setup = readSetup(body?.setup);
14430
+ return setup ? { kind: "setup", setup } : { kind: "failed", reason: "Managed setup discovery returned an invalid response" };
14431
+ }
14432
+ async function post(options, operation, body) {
14433
+ const headers = requestHeaders(options);
14434
+ try {
14435
+ return await request2(options)(endpoint(options, operation), {
14436
+ method: "POST",
14437
+ headers,
14438
+ body: JSON.stringify(body)
14439
+ });
14440
+ } catch (error2) {
14441
+ logTransportFailure(options, operation, error2, headers.Authorization);
14442
+ return null;
14443
+ }
14444
+ }
14445
+ function applyCredential(options, credential) {
14446
+ const { config, unsupportedProviders } = buildCredentialProxyNativeConfig({
14447
+ inferenceBaseUrl: credential.inferenceBaseUrl,
14448
+ modelDescriptors: credential.modelDescriptors
14449
+ });
14450
+ if (unsupportedProviders.length > 0 || Object.keys(config.provider).length === 0) {
14451
+ return "Managed setup contains unsupported provider descriptors";
14452
+ }
14453
+ const env = options.env ?? process.env;
14454
+ let merged;
14455
+ try {
14456
+ merged = mergeManagedOpenCodeConfigContent(env.OPENCODE_CONFIG_CONTENT, config.provider);
14457
+ } catch (error2) {
14458
+ return "OPENCODE_CONFIG_CONTENT is malformed; managed providers were not installed";
14459
+ }
14460
+ try {
14461
+ (options.store ?? defaultStore).set(options.agentId, credential);
14462
+ } catch (error2) {
14463
+ options.log(
14464
+ `MANAGED-INFERENCE-STORE-FAILED: runner=${options.agentId} generation=${credential.generation}`,
14465
+ "warn"
14466
+ );
14467
+ return "Managed credentials could not be written to the protected store";
14468
+ }
14469
+ env.EVIDENT_PROXY_TOKEN = credential.token;
14470
+ env.OPENCODE_CONFIG_CONTENT = merged;
14471
+ return null;
14472
+ }
14473
+ async function reportManagedInferenceApplyFailure(options, failure) {
14474
+ const response = await post(options, "report-failure", {
14475
+ generation: failure.generation,
14476
+ token_id: failure.tokenId
14477
+ });
14478
+ if (!response?.ok) {
14479
+ options.log(
14480
+ `MANAGED-INFERENCE-REPORT-FAILED: runner=${options.agentId} generation=${failure.generation}${response ? ` status=${response.status}` : ""}`,
14481
+ "warn"
14482
+ );
14483
+ }
14484
+ }
14485
+ async function reaffirmStored(options, credential) {
14486
+ const response = await post(options, "reaffirm", {
14487
+ generation: credential.generation,
14488
+ token_id: credential.tokenId
14489
+ });
14490
+ if (!response?.ok) {
14491
+ return {
14492
+ kind: "failed",
14493
+ reason: response?.status === 409 ? "Stored managed credential no longer matches this setup; restart required" : response ? safeFailure("Managed credential reaffirmation", response.status) : safeFailure("Managed credential reaffirmation")
14494
+ };
14495
+ }
14496
+ const body = await responseJson(response);
14497
+ const setup = readSetup(body?.setup);
14498
+ if (!setup || setup.generation !== credential.generation || setup.token_id !== credential.tokenId) {
14499
+ return { kind: "failed", reason: "Managed credential reaffirmation returned a mismatch" };
14500
+ }
14501
+ const failure = applyCredential(options, credential);
14502
+ return failure ? { kind: "failed", reason: failure } : { kind: "ready", generation: credential.generation, tokenId: credential.tokenId };
14503
+ }
14504
+ async function prepareManagedInferenceSetup(options, discovery, health) {
14505
+ if (health.authFailed) {
14506
+ return {
14507
+ kind: "failed",
14508
+ reason: "OpenCode authentication failed; managed credentials were not rotated"
14509
+ };
14510
+ }
14511
+ const stored = (options.store ?? defaultStore).get(options.agentId);
14512
+ if (health.healthy) {
14513
+ if (!stored) {
14514
+ return {
14515
+ kind: "failed",
14516
+ reason: "Managed credentials are missing for the serving OpenCode process; restart required"
14517
+ };
14518
+ }
14519
+ return reaffirmStored(options, stored);
14520
+ }
14521
+ if (discovery.setup.status === "acknowledged" && stored?.generation === discovery.setup.generation && stored.tokenId === discovery.setup.token_id) {
14522
+ return reaffirmStored(options, stored);
14523
+ }
14524
+ const response = await post(options, "bootstrap", { generation: discovery.setup.generation });
14525
+ if (!response?.ok) {
14526
+ return {
14527
+ kind: "failed",
14528
+ reason: response ? safeFailure("Managed credential bootstrap", response.status) : safeFailure("Managed credential bootstrap")
14529
+ };
14530
+ }
14531
+ const body = await responseJson(response);
14532
+ const setup = readSetup(body?.setup);
14533
+ const token = body?.inference_token;
14534
+ const inferenceBaseUrl = body?.inference_base_url;
14535
+ const modelDescriptors = body?.model_descriptors;
14536
+ if (!setup || setup.generation !== discovery.setup.generation || typeof setup.token_id !== "string" || typeof token !== "string" || typeof inferenceBaseUrl !== "string" || !Array.isArray(modelDescriptors) || !modelDescriptors.every(
14537
+ (descriptor) => typeof descriptor === "object" && descriptor !== null && typeof descriptor.id === "string" && typeof descriptor.provider === "string"
14538
+ )) {
14539
+ return { kind: "failed", reason: "Managed credential bootstrap returned an invalid response" };
14540
+ }
14541
+ const credential = {
14542
+ generation: setup.generation,
14543
+ tokenId: setup.token_id,
14544
+ token,
14545
+ inferenceBaseUrl,
14546
+ modelDescriptors
14547
+ };
14548
+ const failure = applyCredential(options, credential);
14549
+ if (failure) {
14550
+ await reportManagedInferenceApplyFailure(options, credential);
14551
+ return { kind: "failed", reason: failure };
14552
+ }
14553
+ return { kind: "ready", generation: credential.generation, tokenId: credential.tokenId };
14554
+ }
14555
+ async function acknowledgeManagedInferenceSetup(options, preparation) {
14556
+ const response = await post(options, "acknowledge", {
14557
+ generation: preparation.generation,
14558
+ token_id: preparation.tokenId,
14559
+ config_validated: true
14560
+ });
14561
+ if (!response?.ok) {
14562
+ throw new Error(
14563
+ response ? safeFailure("Managed credential acknowledgment", response.status) : safeFailure("Managed credential acknowledgment")
14564
+ );
14565
+ }
14566
+ const body = await responseJson(response);
14567
+ const setup = readSetup(body?.setup);
14568
+ if (!setup || setup.generation !== preparation.generation || setup.token_id !== preparation.tokenId) {
14569
+ throw new Error("Managed credential acknowledgment returned a mismatch");
14570
+ }
14571
+ return response.status === 200 && setup.status === "acknowledged" ? "acknowledged" : "pending";
14572
+ }
14573
+
13809
14574
  // src/commands/run.ts
13810
14575
  var MAX_ACTIVITY_LOG_ENTRIES = 10;
13811
14576
  var MAX_DISCONNECT_REASON_LENGTH = 200;
@@ -13846,7 +14611,7 @@ function resolveFileSyncDirectories(raw, homeDir) {
13846
14611
  if (trimmed === "") {
13847
14612
  throw new Error("--enable-file-sync-to requires a directory path (got an empty value)");
13848
14613
  }
13849
- const expanded = trimmed === "~" ? homeDir : trimmed.startsWith("~/") ? join12(homeDir, trimmed.slice(2)) : trimmed;
14614
+ const expanded = trimmed === "~" ? homeDir : trimmed.startsWith("~/") ? join13(homeDir, trimmed.slice(2)) : trimmed;
13850
14615
  if (!isAbsolute4(expanded)) {
13851
14616
  throw new Error(`--enable-file-sync-to requires an absolute directory path; got "${entry}"`);
13852
14617
  }
@@ -14001,7 +14766,7 @@ function logActivity(state, entry) {
14001
14766
  }
14002
14767
  function reportSessionDbRecovery(state) {
14003
14768
  try {
14004
- const report = drainSessionDbRecoveryReport({ homeDir: homedir7(), env: process.env });
14769
+ const report = drainSessionDbRecoveryReport({ homeDir: homedir8(), env: process.env });
14005
14770
  for (const record of report.records) {
14006
14771
  const activity = buildSessionDbRecoveryActivity(record);
14007
14772
  if (!activity) throw new Error("could not map session-DB recovery record");
@@ -14082,7 +14847,7 @@ async function importOpenCodeV2CredentialsForState(state) {
14082
14847
  }
14083
14848
  const result = await importOpenCodeV2Credentials({
14084
14849
  request: client.request,
14085
- homeDir: homedir7(),
14850
+ homeDir: homedir8(),
14086
14851
  directory,
14087
14852
  guard: state.opencodeV2CredentialImportGuard,
14088
14853
  log: (message, level = "info") => logActivity(state, { type: "info", level, message })
@@ -14502,7 +15267,7 @@ function shouldWarnForReclaimSkip(reason) {
14502
15267
  return major > 22 || major === 22 && (minor > 13 || minor === 13 && patch >= 0);
14503
15268
  }
14504
15269
  function sessionDbPath() {
14505
- return join12(homedir7(), ".local", "share", "opencode", "opencode.db");
15270
+ return join13(homedir8(), ".local", "share", "opencode", "opencode.db");
14506
15271
  }
14507
15272
  function logSessionCleanupActivity(state, phase, entry) {
14508
15273
  logActivity(state, {
@@ -14746,7 +15511,7 @@ function scheduleSessionCleanup(state, driver, options) {
14746
15511
  for (const warning2 of config.warnings) {
14747
15512
  logActivity(state, { type: "info", level: "warn", message: `Session cleanup: ${warning2}` });
14748
15513
  }
14749
- const dbBytes = statSessionDbBytes(homedir7());
15514
+ const dbBytes = statSessionDbBytes(homedir8());
14750
15515
  void (async () => {
14751
15516
  const reclaimAvailability = dbBytes !== null && config.enabled ? await probeReclaimAvailability({ dbPath: sessionDbPath(), requiredBytes: dbBytes }) : null;
14752
15517
  if (reclaimAvailability !== null) {
@@ -14965,7 +15730,7 @@ function scheduleResourceUsageReporting(state, options) {
14965
15730
  });
14966
15731
  return;
14967
15732
  }
14968
- const { collect, stop } = createResourceUsageCollector(homedir7());
15733
+ const { collect, stop } = createResourceUsageCollector(homedir8());
14969
15734
  state.stopResourceUsageSampling = stop;
14970
15735
  let consecutiveFailures = 0;
14971
15736
  const tick = async () => {
@@ -15206,7 +15971,7 @@ async function run(options) {
15206
15971
  let fileSyncDirectories;
15207
15972
  try {
15208
15973
  logLevel = resolveLogLevel(options);
15209
- fileSyncDirectories = resolveFileSyncDirectories(options.enableFileSyncTo, homedir7());
15974
+ fileSyncDirectories = resolveFileSyncDirectories(options.enableFileSyncTo, homedir8());
15210
15975
  if (options.restoreSessionDb && !options.sessionDbNoReplicateMarker) {
15211
15976
  throw new Error(
15212
15977
  "--restore-session-db requires --session-db-no-replicate-marker <path>; restore failures must block Litestream replication"
@@ -15492,7 +16257,7 @@ async function run(options) {
15492
16257
  state.credentialSync?.arm();
15493
16258
  const dependencyInstallDiagnosticReporter = createDependencyInstallDiagnosticReporter({
15494
16259
  context: {
15495
- homeDir: homedir7(),
16260
+ homeDir: homedir8(),
15496
16261
  cwd: process.cwd(),
15497
16262
  microvmId: process.env.MICROVM_ID?.trim(),
15498
16263
  euid: process.getuid?.(),
@@ -15515,7 +16280,7 @@ async function run(options) {
15515
16280
  dependencyInstallDiagnosticReporter
15516
16281
  );
15517
16282
  const openCodeLogTail = tailOpenCodeLogFile(
15518
- resolveOpenCodeLogPath(homedir7(), process.env),
16283
+ resolveOpenCodeLogPath(homedir8(), process.env),
15519
16284
  openCodeActivityForwarder
15520
16285
  );
15521
16286
  state.stopOpenCodeLogTail = () => {
@@ -15590,6 +16355,40 @@ async function run(options) {
15590
16355
  for (const warning2 of opencodeVersionWarnings) {
15591
16356
  logActivity(state, { type: "info", level: "warn", message: warning2 });
15592
16357
  }
16358
+ const managedSetupOptions = {
16359
+ agentId: state.agentId,
16360
+ apiUrl: getApiUrlConfig(),
16361
+ getAuthHeader: () => state.authHeader,
16362
+ log: (message, level = "info") => logActivity(state, {
16363
+ type: level === "error" ? "error" : "info",
16364
+ level,
16365
+ ...level === "error" ? { error: message } : { message }
16366
+ })
16367
+ };
16368
+ const managedDiscovery = await discoverManagedInferenceSetup(managedSetupOptions);
16369
+ let managedPreparation = { kind: "none" };
16370
+ if (managedDiscovery.kind === "failed") {
16371
+ managedPreparation = managedDiscovery;
16372
+ managedSetupOptions.log(`MANAGED-INFERENCE-NOT-READY: ${managedDiscovery.reason}`, "warn");
16373
+ } else if (managedDiscovery.kind === "setup") {
16374
+ const managedHealth = opencodeVersion === "v2" ? await checkOpenCode2Health(state.port) : await checkOpenCodeHealth(state.port);
16375
+ managedPreparation = await prepareManagedInferenceSetup(
16376
+ managedSetupOptions,
16377
+ managedDiscovery,
16378
+ managedHealth
16379
+ );
16380
+ if (managedPreparation.kind === "failed") {
16381
+ managedSetupOptions.log(
16382
+ `MANAGED-INFERENCE-NOT-READY: ${managedPreparation.reason}`,
16383
+ "warn"
16384
+ );
16385
+ } else if (managedPreparation.kind === "ready") {
16386
+ managedSetupOptions.log(
16387
+ `MANAGED-INFERENCE-PREPARED: runner=${state.agentId} generation=${managedPreparation.generation}`,
16388
+ "info"
16389
+ );
16390
+ }
16391
+ }
15593
16392
  const { value: maxActiveSessions, warnings: maxActiveSessionsWarnings } = resolveMaxActiveSessions(options, process.env);
15594
16393
  for (const warning2 of maxActiveSessionsWarnings) {
15595
16394
  logActivity(state, { type: "info", level: "warn", message: warning2 });
@@ -15616,6 +16415,32 @@ async function run(options) {
15616
16415
  state.opencodeProcess = options.opencodePidFile ? null : oc.process;
15617
16416
  state.opencodeVersion = oc.version;
15618
16417
  state.opencodeApiVersion = opencodeVersion;
16418
+ let configuredPlugins = [];
16419
+ let resolvedPlugins = [];
16420
+ try {
16421
+ const provenanceLog = (message) => logActivity(state, { type: "info", level: "warn", message });
16422
+ configuredPlugins = readConfiguredPluginSpecs({ log: provenanceLog });
16423
+ resolvedPlugins = resolveInstalledPluginVersions({ log: provenanceLog });
16424
+ } catch (error2) {
16425
+ const kind = error2 instanceof Error ? error2.name : "unknown error";
16426
+ logActivity(state, {
16427
+ type: "info",
16428
+ level: "warn",
16429
+ message: `Could not collect OpenCode runtime provenance: ${kind}`
16430
+ });
16431
+ }
16432
+ logActivity(state, {
16433
+ type: "info",
16434
+ level: "info",
16435
+ message: "OpenCode runtime provenance",
16436
+ metadata: {
16437
+ opencode_version: state.opencodeVersion,
16438
+ opencode_api_version: state.opencodeApiVersion,
16439
+ configured_plugins: JSON.stringify(configuredPlugins),
16440
+ resolved_plugins: JSON.stringify(resolvedPlugins)
16441
+ },
16442
+ source: "cli.runtime-provenance"
16443
+ });
15619
16444
  let opencodePassword = null;
15620
16445
  if (opencodeVersion === "v2" && "password" in oc) {
15621
16446
  const value = oc.password;
@@ -15644,7 +16469,7 @@ async function run(options) {
15644
16469
  const provenance = checkSessionDbProvenance({
15645
16470
  dbPath: sessionDbPath(),
15646
16471
  currentVersion: state.opencodeVersion,
15647
- homeDir: homedir7(),
16472
+ homeDir: homedir8(),
15648
16473
  env: process.env
15649
16474
  });
15650
16475
  if (provenance.anomaly) {
@@ -15658,6 +16483,30 @@ async function run(options) {
15658
16483
  }
15659
16484
  }
15660
16485
  state.opencodeConnected = oc.notReadyReason === null;
16486
+ if (managedPreparation.kind === "ready" && oc.notReadyReason === null) {
16487
+ try {
16488
+ const acknowledgement = await acknowledgeManagedInferenceSetup(
16489
+ managedSetupOptions,
16490
+ managedPreparation
16491
+ );
16492
+ if (acknowledgement === "acknowledged") {
16493
+ managedSetupOptions.log(
16494
+ `MANAGED-INFERENCE-ACKNOWLEDGED: runner=${state.agentId} generation=${managedPreparation.generation}`,
16495
+ "info"
16496
+ );
16497
+ } else {
16498
+ managedSetupOptions.log(
16499
+ `MANAGED-INFERENCE-NOT-READY: policy refresh is pending for runner=${state.agentId} generation=${managedPreparation.generation}`,
16500
+ "warn"
16501
+ );
16502
+ }
16503
+ } catch (error2) {
16504
+ managedSetupOptions.log(
16505
+ `MANAGED-INFERENCE-NOT-READY: ${error2 instanceof Error ? error2.message : String(error2)}`,
16506
+ "warn"
16507
+ );
16508
+ }
16509
+ }
15661
16510
  const version2 = state.opencodeVersion ? ` (v${state.opencodeVersion})` : "";
15662
16511
  ocSpinner?.succeed(`OpenCode running on port ${state.port}${version2}`);
15663
16512
  if (!state.interactive && oc.notReadyReason !== null) {
@@ -15716,7 +16565,7 @@ async function run(options) {
15716
16565
  let existingPid;
15717
16566
  if (existsSync3(options.litestreamPidFile)) {
15718
16567
  try {
15719
- const rawPid = readFileSync7(options.litestreamPidFile, "utf8").trim();
16568
+ const rawPid = readFileSync8(options.litestreamPidFile, "utf8").trim();
15720
16569
  const parsedPid = Number(rawPid);
15721
16570
  if (/^\d+$/.test(rawPid) && Number.isSafeInteger(parsedPid) && parsedPid > 0) {
15722
16571
  existingPid = parsedPid;
@@ -15815,7 +16664,7 @@ async function run(options) {
15815
16664
  // #559: the `--enable-file-sync-to` allow-list. Empty ⇒ pending files are
15816
16665
  // REJECTED with `file_sync_disabled` on the ack, not silently ignored.
15817
16666
  fileSyncDirectories,
15818
- homeDir: homedir7(),
16667
+ homeDir: homedir8(),
15819
16668
  maxActiveSessions,
15820
16669
  log: (entry) => (
15821
16670
  // Thread the driver's real level straight through so `debug`/`warn`
@@ -16059,9 +16908,9 @@ program.name("evident").description("Run OpenCode locally and connect it to Evid
16059
16908
  "--endpoint <url>",
16060
16909
  "Evident API base URL (default: production; e.g. http://localhost:3001)"
16061
16910
  ).option("--tunnel <url>", "Tunnel WebSocket URL (default: production; e.g. ws://localhost:8787)").hook("preAction", (thisCommand) => {
16062
- const { endpoint, tunnel } = thisCommand.opts();
16063
- if (endpoint) {
16064
- setEndpoint(endpoint);
16911
+ const { endpoint: endpoint2, tunnel } = thisCommand.opts();
16912
+ if (endpoint2) {
16913
+ setEndpoint(endpoint2);
16065
16914
  }
16066
16915
  if (tunnel) {
16067
16916
  setTunnelUrl(tunnel);