@undefineds.co/xpod 0.4.25 → 0.4.28

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.
Files changed (155) hide show
  1. package/dist/api/ApiServer.d.ts +1 -0
  2. package/dist/api/ApiServer.js +38 -0
  3. package/dist/api/ai-config/AiConfigStore.js +18 -6
  4. package/dist/api/ai-gateway/connect/index.d.ts +1 -0
  5. package/dist/api/ai-gateway/connect/index.js +39 -18
  6. package/dist/api/ai-gateway/models/ProviderModelsAdapter.js +5 -2
  7. package/dist/api/container/common.js +8 -2
  8. package/dist/api/container/index.js +2 -2
  9. package/dist/api/container/local.js +6 -3
  10. package/dist/api/handlers/AiGatewayHandler.d.ts +1 -0
  11. package/dist/api/handlers/AiGatewayHandler.js +53 -3
  12. package/dist/api/handlers/AiGatewayManagementHandler.js +9 -0
  13. package/dist/api/handlers/TaskHandler.js +15 -10
  14. package/dist/api/network/NetworkEnvironmentConfigurationStore.js +1 -1
  15. package/dist/api/runs/ManagedRunWorker.d.ts +0 -1
  16. package/dist/api/runs/ManagedRunWorker.js +2 -14
  17. package/dist/api/runs/PiAgentRuntimeDriver.d.ts +0 -8
  18. package/dist/api/runs/PiAgentRuntimeDriver.js +65 -21
  19. package/dist/api/runs/RunStateCenter.js +1 -0
  20. package/dist/api/runs/store.d.ts +1 -0
  21. package/dist/api/runs/store.js +13 -0
  22. package/dist/api/tasks/TaskMaterializer.js +13 -6
  23. package/dist/api/tasks/TaskResumeDiagnostics.d.ts +28 -0
  24. package/dist/api/tasks/TaskResumeDiagnostics.js +148 -0
  25. package/dist/api/tasks/TaskService.js +40 -23
  26. package/dist/storage/conditions/StorageETagHandler.d.ts +1 -1
  27. package/dist/storage/conditions/StorageETagHandler.js +13 -4
  28. package/dist/util/task-model-diagnostics.d.ts +67 -0
  29. package/dist/util/task-model-diagnostics.js +118 -0
  30. package/node_modules/@undefineds.co/ai-connections/dist/AiApiKeyPool.d.ts +1 -0
  31. package/node_modules/@undefineds.co/ai-connections/dist/AiApiKeyPool.js +2 -1
  32. package/node_modules/@undefineds.co/ai-connections/dist/AiConnectDialog.d.ts +1 -0
  33. package/node_modules/@undefineds.co/ai-connections/dist/AiCredentialPoolSection.d.ts +1 -0
  34. package/node_modules/@undefineds.co/ai-connections/dist/AiCredentialPoolSection.js +10 -10
  35. package/node_modules/@undefineds.co/ai-connections/dist/AiGatewayKeyRow.d.ts +3 -1
  36. package/node_modules/@undefineds.co/ai-connections/dist/AiGatewayKeyRow.js +2 -2
  37. package/node_modules/@undefineds.co/ai-connections/dist/AiGatewayKeysSection.js +77 -23
  38. package/node_modules/@undefineds.co/ai-connections/dist/AiProviderCard.d.ts +1 -0
  39. package/node_modules/@undefineds.co/ai-connections/dist/AiSortableCredentialList.js +59 -59
  40. package/node_modules/@undefineds.co/ai-connections/dist/collection-runtime.js +2 -0
  41. package/node_modules/@undefineds.co/ai-connections/dist/collections.js +14 -6
  42. package/node_modules/@undefineds.co/ai-connections/dist/contract/ai-connections-client.d.ts +2 -1
  43. package/node_modules/@undefineds.co/ai-connections/dist/contract/ai-connections-client.js +1 -0
  44. package/node_modules/@undefineds.co/ai-connections/dist/contract/client/gateway-model-roles.d.ts +11 -0
  45. package/node_modules/@undefineds.co/ai-connections/dist/contract/client/gateway-model-roles.js +11 -0
  46. package/node_modules/@undefineds.co/ai-connections/dist/contract/client/normalize.d.ts +2 -1
  47. package/node_modules/@undefineds.co/ai-connections/dist/contract/client/normalize.js +12 -5
  48. package/node_modules/@undefineds.co/ai-connections/dist/contract/client/request.js +10 -2
  49. package/node_modules/@undefineds.co/ai-connections/dist/contract/client/types.d.ts +10 -0
  50. package/node_modules/@undefineds.co/ai-connections/package.json +1 -1
  51. package/node_modules/@undefineds.co/extension-sdk/dist/web.d.ts +1 -0
  52. package/node_modules/@undefineds.co/extension-sdk/package.json +1 -1
  53. package/node_modules/@undefineds.co/pod-collections/dist/index.cjs +60 -13
  54. package/node_modules/@undefineds.co/pod-collections/dist/mapping.d.ts +16 -1
  55. package/node_modules/@undefineds.co/pod-collections/dist/mapping.js +51 -0
  56. package/node_modules/@undefineds.co/pod-collections/dist/mutations.js +14 -18
  57. package/node_modules/@undefineds.co/pod-collections/package.json +1 -1
  58. package/node_modules/@undefineds.co/shared-ui/dist/list.d.ts +8 -1
  59. package/node_modules/@undefineds.co/shared-ui/dist/list.js +4 -1
  60. package/node_modules/@undefineds.co/shared-ui/dist/pod-sign-in/IdpChrome.d.ts +1 -1
  61. package/node_modules/@undefineds.co/shared-ui/dist/pod-sign-in/IdpChrome.js +2 -2
  62. package/node_modules/@undefineds.co/shared-ui/dist/pod-sign-in/IdpViews.js +2 -2
  63. package/node_modules/@undefineds.co/shared-ui/dist/pod-sign-in/PodSignInFrame.d.ts +3 -3
  64. package/node_modules/@undefineds.co/shared-ui/dist/pod-sign-in/PodSignInFrame.js +5 -5
  65. package/node_modules/@undefineds.co/shared-ui/dist/pod-sign-in/parts.d.ts +5 -5
  66. package/node_modules/@undefineds.co/shared-ui/dist/pod-sign-in/parts.js +5 -5
  67. package/node_modules/@undefineds.co/shared-ui/dist/section-header.js +1 -1
  68. package/node_modules/@undefineds.co/shared-ui/dist/theme.css +5 -32
  69. package/node_modules/@undefineds.co/shared-ui/package.json +1 -1
  70. package/node_modules/@undefineds.co/solid-sdk/package.json +1 -1
  71. package/package.json +2 -2
  72. package/static/app/assets/main.css +1 -1
  73. package/static/app/assets/main.js +1 -1
  74. package/static/dashboard/assets/AccountAuthBoundary-Dv52SZc6.js +1 -0
  75. package/static/dashboard/assets/DesktopOidcCallback-DaT_Rumf.js +1 -0
  76. package/static/dashboard/assets/DevicePages-HjclpIVm.js +2 -0
  77. package/static/dashboard/assets/ModelsPage-Be2TPd-x.js +11 -0
  78. package/static/dashboard/assets/{PaneListHeader-Dn5O0mwy.js → PaneListHeader-DtvnRmFq.js} +1 -1
  79. package/static/dashboard/assets/{PodDeletionAuthorizationPanel-D3NOG6ZM.js → PodDeletionAuthorizationPanel-BJk_8rJq.js} +3 -3
  80. package/static/dashboard/assets/PodPage-C4gzEspV.js +1 -0
  81. package/static/dashboard/assets/{SystemSettingsPage-BNQP_Yuc.js → SystemSettingsPage-HaNrP1H1.js} +1 -1
  82. package/static/dashboard/assets/TasksPage-BdO5K2L7.js +1 -0
  83. package/static/dashboard/assets/{WebDesktopEntry-vnb4CZWp.js → WebDesktopEntry-Cj9_ZfJU.js} +1 -1
  84. package/static/dashboard/assets/XpodAiConnectionsPodStore-B7gmXBmC.js +3 -0
  85. package/static/dashboard/assets/XpodShellApp-D_KY4nCW.js +4 -0
  86. package/static/dashboard/assets/{account-session-DCUmCop1.js → account-session-DlcKNG7k.js} +1 -1
  87. package/static/dashboard/assets/{button-By5Bgvmb.js → button-wslF55uZ.js} +1 -1
  88. package/static/dashboard/assets/collection-runtime-uxxgEu13.js +1 -0
  89. package/static/dashboard/assets/{createLucideIcon-bunMzPms.js → createLucideIcon-BJQl82Qk.js} +1 -1
  90. package/static/dashboard/assets/dashboard-CbJ-JkrI.css +1 -0
  91. package/static/dashboard/assets/{dashboard-mlsg3yaw.js → dashboard-Or0gnkou.js} +2 -2
  92. package/static/dashboard/assets/index-BqFeOML9.js +1 -0
  93. package/static/dashboard/assets/{index-DvMG5pHk.js → index-DgKn1twH.js} +2 -2
  94. package/static/dashboard/assets/{label-Bj03L9xs.js → label-CS6Sx6Zb.js} +1 -1
  95. package/static/dashboard/assets/list-UyhlCFKk.js +1 -0
  96. package/static/dashboard/assets/{nav-item-style-BNqWQY6p.js → nav-item-style-CW1Nnorq.js} +1 -1
  97. package/static/dashboard/assets/{pod-collections-host-KnnGFZHL.js → pod-collections-host-DjK2xf-b.js} +6 -6
  98. package/static/dashboard/assets/{safe-continuation-D9AhcWiB.js → safe-continuation-ClWAhcma.js} +1 -1
  99. package/static/dashboard/assets/{section-header-CLCzbOp_.js → section-header-CEIEcco-.js} +1 -1
  100. package/static/dashboard/assets/{setting-row-DTCec8A2.js → setting-row-CaYv3dfF.js} +1 -1
  101. package/static/dashboard/assets/xpod-remembered-login-BUF9fin1.js +310 -0
  102. package/static/dashboard/dashboard.html +2 -2
  103. package/static/settings/assets/{AccountAuthBoundary-C0og_gIA.js → AccountAuthBoundary-Chqd38oU.js} +1 -1
  104. package/static/settings/assets/{DesktopOidcCallback-C5q4oFZ0.js → DesktopOidcCallback-VUrOSM_q.js} +1 -1
  105. package/static/settings/assets/DevicePages-C6qwvWwu.js +2 -0
  106. package/static/settings/assets/ModelsPage-Ry_dObRQ.js +11 -0
  107. package/static/settings/assets/{PaneListHeader-DqVlMe2D.js → PaneListHeader-B5RkRqr4.js} +1 -1
  108. package/static/settings/assets/{PodDeletionAuthorizationPanel-DgVvarVY.js → PodDeletionAuthorizationPanel-DPM-5Izt.js} +3 -3
  109. package/static/settings/assets/PodPage-CxND6wwc.js +1 -0
  110. package/static/settings/assets/{SystemSettingsPage-Crzm_DrI.js → SystemSettingsPage-C8EdMkW7.js} +1 -1
  111. package/static/settings/assets/TasksPage-DuqptEm8.js +1 -0
  112. package/static/settings/assets/{WebDesktopEntry-BGC5eRF2.js → WebDesktopEntry-BRcyktkv.js} +1 -1
  113. package/static/settings/assets/XpodAiConnectionsPodStore-CyA_BJVC.js +3 -0
  114. package/static/settings/assets/XpodShellApp-CsivHJl0.js +4 -0
  115. package/static/settings/assets/XpodThemeProvider-CbJ-JkrI.css +1 -0
  116. package/static/settings/assets/{XpodThemeProvider-CB6LepTA.js → XpodThemeProvider-DK3pIdRG.js} +2 -2
  117. package/static/settings/assets/{account-session-BcLjCj0x.js → account-session-DJOZ03xr.js} +1 -1
  118. package/static/settings/assets/auth-callback-GIBYEz_7.js +1 -0
  119. package/static/settings/assets/{button-BObT8pTE.js → button-D3yW-2o1.js} +1 -1
  120. package/static/settings/assets/collection-runtime-DfGo25mh.js +1 -0
  121. package/static/settings/assets/{createLucideIcon-BVsUxXpZ.js → createLucideIcon-CacwLsmB.js} +1 -1
  122. package/static/settings/assets/{index-ktjMkj7-.js → index-r5KRYsKC.js} +1 -1
  123. package/static/settings/assets/{label-BrqO8_m_.js → label-MJjUmvzG.js} +1 -1
  124. package/static/settings/assets/list-Ce-5k1MX.js +1 -0
  125. package/static/settings/assets/{nav-item-style-Di2CxkDU.js → nav-item-style-ZUbL8KOZ.js} +1 -1
  126. package/static/settings/assets/{pod-collections-host-KnnGFZHL.js → pod-collections-host-DjK2xf-b.js} +6 -6
  127. package/static/settings/assets/{safe-continuation-Dx6ceJpY.js → safe-continuation-vlBZFSlU.js} +1 -1
  128. package/static/settings/assets/{section-header-Dtl7yjVr.js → section-header-DGJdEvQw.js} +1 -1
  129. package/static/settings/assets/{setting-row-BL6Hhe8N.js → setting-row-C7tuFiho.js} +1 -1
  130. package/static/settings/assets/{settings-DaRBxoZk.js → settings-CyQPih1O.js} +1 -1
  131. package/static/settings/assets/xpod-remembered-login-D-TzzT5K.js +567 -0
  132. package/static/settings/auth-callback.html +3 -3
  133. package/static/settings/settings.html +3 -3
  134. package/static/dashboard/assets/AccountAuthBoundary-BvoDydRz.js +0 -1
  135. package/static/dashboard/assets/DesktopOidcCallback-BwIj874x.js +0 -1
  136. package/static/dashboard/assets/DevicePages-jOjMcvsr.js +0 -2
  137. package/static/dashboard/assets/ModelsPage-CMTdfqBR.js +0 -11
  138. package/static/dashboard/assets/PodPage-BueBdJ9_.js +0 -1
  139. package/static/dashboard/assets/TasksPage-CxtLJiSF.js +0 -1
  140. package/static/dashboard/assets/XpodAiConnectionsPodStore-wx3Ttupm.js +0 -18
  141. package/static/dashboard/assets/XpodShellApp-5FWY8-d5.js +0 -4
  142. package/static/dashboard/assets/collection-runtime-BnomuB0E.js +0 -1
  143. package/static/dashboard/assets/dashboard-C64R553B.css +0 -1
  144. package/static/dashboard/assets/xpod-remembered-login-Dr69A0Rv.js +0 -295
  145. package/static/settings/assets/DevicePages-fyHjjucT.js +0 -2
  146. package/static/settings/assets/ModelsPage-EyOByLyE.js +0 -11
  147. package/static/settings/assets/PodPage-DY4Oi9wg.js +0 -1
  148. package/static/settings/assets/TasksPage-CfXJbnlt.js +0 -1
  149. package/static/settings/assets/XpodAiConnectionsPodStore-CXcmgKmQ.js +0 -3
  150. package/static/settings/assets/XpodShellApp-D1PS3Iut.js +0 -4
  151. package/static/settings/assets/XpodThemeProvider-C64R553B.css +0 -1
  152. package/static/settings/assets/auth-callback-DSPQ28ar.js +0 -1
  153. package/static/settings/assets/collection-runtime-DzNbdZGe.js +0 -1
  154. package/static/settings/assets/index-browser-3WfrNbcG.js +0 -273
  155. package/static/settings/assets/xpod-remembered-login-Q66DH12t.js +0 -295
@@ -26,6 +26,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
26
26
  exports.PI_AGENT_WORKER_EVENT_PREFIX = exports.PiAgentRuntimeDriver = void 0;
27
27
  exports.classifyAssistantFailure = classifyAssistantFailure;
28
28
  const fs = __importStar(require("node:fs"));
29
+ const task_model_diagnostics_1 = require("../../util/task-model-diagnostics");
29
30
  const path = __importStar(require("node:path"));
30
31
  const os = __importStar(require("node:os"));
31
32
  const crypto = __importStar(require("node:crypto"));
@@ -135,6 +136,30 @@ class PiAgentRuntimeDriver {
135
136
  let failed = false;
136
137
  let releaseApprovalTool;
137
138
  let pausedForApproval = false;
139
+ const sessionHeader = `xpod-${crypto.createHash('sha256').update(input.threadId).digest('hex')}`;
140
+ let stage = 'not_invoked';
141
+ let api = 'other';
142
+ let credentialPresent = false;
143
+ let retryCount = 0;
144
+ let failureLogged = false;
145
+ const logFailure = (stopReason, errorMessage) => {
146
+ if (failureLogged || pausedForApproval || input.signal?.aborted)
147
+ return;
148
+ failureLogged = true;
149
+ // The SDK discards the structured provider response; only its formatted
150
+ // error message survives, so record a bounded hint and never the raw text.
151
+ const sdkErrorHint = (0, task_model_diagnostics_1.classifyTaskModelSdkErrorHint)(errorMessage);
152
+ const receipt = (0, task_model_diagnostics_1.selectTaskModelDiagnosticReceipt)({ event: 'xpod.task-model-diagnostic', schemaVersion: 1,
153
+ scope: 'session', stage, api, stopReason, retryCount,
154
+ correlationHash: (0, task_model_diagnostics_1.hashTaskModelDiagnosticSession)(sessionHeader), httpStatus: null, credentialPresent,
155
+ ...(sdkErrorHint ? { sdkErrorHint } : {}) });
156
+ if (receipt) {
157
+ try {
158
+ this.logger.error(JSON.stringify(receipt));
159
+ }
160
+ catch { /* Diagnostics cannot alter run failure semantics. */ }
161
+ }
162
+ };
138
163
  let releaseLifecycleWait;
139
164
  const onAbort = () => {
140
165
  failed = true;
@@ -146,6 +171,9 @@ class PiAgentRuntimeDriver {
146
171
  try {
147
172
  workspace = await this.prepareWorkspace(input);
148
173
  const runtime = await this.getWarmRuntime(input, workspace);
174
+ api = runtime.piConfig.api === 'openai-completions' || runtime.piConfig.api === 'openai-responses'
175
+ ? runtime.piConfig.api : 'other';
176
+ credentialPresent = Boolean(runtime.piConfig.apiKey);
149
177
  const sessionManager = this.createSessionManager(runtime.pi, input.runId, runtime.workdir);
150
178
  const approvalTool = {
151
179
  name: 'request_approval', label: 'Request approval',
@@ -195,13 +223,36 @@ class PiAgentRuntimeDriver {
195
223
  resourceLoader: runtime.resourceLoader,
196
224
  model: { ...runtime.piConfig.model, headers: {
197
225
  'user-agent': `Xpod/${xpodVersion}`,
198
- 'x-opencode-session': `xpod-${crypto.createHash('sha256').update(input.threadId).digest('hex')}`,
226
+ 'x-opencode-session': sessionHeader,
199
227
  } },
200
228
  thinkingLevel: runtime.thinkingLevel,
201
229
  tools: runtime.tools,
202
230
  customTools: [approvalTool],
203
231
  });
204
232
  session = result.session;
233
+ const originalStreamFn = session.agent.streamFn.bind(session.agent);
234
+ session.agent.streamFn = (model, context, options) => {
235
+ stage = 'model_invoked';
236
+ credentialPresent = Boolean(options?.apiKey);
237
+ const response = originalStreamFn(model, context, { ...options,
238
+ onPayload: payload => {
239
+ stage = 'payload_prepared';
240
+ return options?.onPayload?.(payload);
241
+ },
242
+ });
243
+ const observe = (stream) => {
244
+ const iterate = stream[Symbol.asyncIterator].bind(stream);
245
+ stream[Symbol.asyncIterator] = async function* () {
246
+ for await (const event of { [Symbol.asyncIterator]: iterate }) {
247
+ if (event.type === 'start')
248
+ stage = 'stream_open';
249
+ yield event;
250
+ }
251
+ };
252
+ return stream;
253
+ };
254
+ return response instanceof Promise ? response.then(observe) : observe(response);
255
+ };
205
256
  if (input.signal?.aborted)
206
257
  return;
207
258
  session.agent.replaceMessages(this.toPiMessages(input, runtime.piConfig));
@@ -211,6 +262,8 @@ class PiAgentRuntimeDriver {
211
262
  };
212
263
  const unsubscribe = session.subscribe((event) => {
213
264
  this.projectPiEvent(event, queue, streamState);
265
+ if (event.type === 'auto_retry_start')
266
+ retryCount += 1;
214
267
  if (event.type === 'agent_start' || event.type === 'auto_retry_end')
215
268
  releaseLifecycleWait?.();
216
269
  });
@@ -235,9 +288,10 @@ class PiAgentRuntimeDriver {
235
288
  const lastAssistant = (session?.messages ?? []).slice().reverse().find(message => message.role === 'assistant');
236
289
  if (!pausedForApproval && !input.signal?.aborted && lastAssistant?.role === 'assistant' &&
237
290
  (lastAssistant.stopReason === 'error' || lastAssistant.stopReason === 'aborted')) {
238
- // Provider errorMessage can contain credentials or response bodies. Expose only a stable
239
- // classification plus allowlisted wire facts (protocol/provider/model), never the body.
240
- this.logAssistantFailure(lastAssistant);
291
+ // The feature structured receipt is the single diagnostic sink for one failure; the
292
+ // pushed message carries only allowlisted wire facts (class/protocol/provider/model),
293
+ // never the provider body or credential material.
294
+ logFailure(lastAssistant.stopReason, lastAssistant.errorMessage);
241
295
  queue.push({ type: 'error', message: `Pi assistant ended with ${lastAssistant.stopReason}${describeAssistantFailure(lastAssistant)}` });
242
296
  }
243
297
  else if (!streamState.assistantTextStreamed && streamState.lastAssistantText.length > 0) {
@@ -245,8 +299,10 @@ class PiAgentRuntimeDriver {
245
299
  }
246
300
  queue.close();
247
301
  }).catch((error) => {
248
- if (!pausedForApproval)
302
+ if (!pausedForApproval) {
303
+ logFailure('unknown', error);
249
304
  queue.push({ type: 'error', message: this.formatError(error) });
305
+ }
250
306
  queue.close();
251
307
  }).finally(() => {
252
308
  unsubscribe();
@@ -263,7 +319,10 @@ class PiAgentRuntimeDriver {
263
319
  }
264
320
  }
265
321
  catch (error) {
266
- yield this.startupErrorToEvent(error);
322
+ const event = this.startupErrorToEvent(error);
323
+ if (event.type === 'error')
324
+ logFailure('unknown');
325
+ yield event;
267
326
  }
268
327
  finally {
269
328
  input.signal?.removeEventListener('abort', onAbort);
@@ -878,21 +937,6 @@ class PiAgentRuntimeDriver {
878
937
  formatError(error) {
879
938
  return error instanceof Error ? error.message : String(error);
880
939
  }
881
- /**
882
- * Record a failed assistant turn with allowlisted wire facts only.
883
- *
884
- * The provider `errorMessage` can contain credentials, prompts, or response bodies, so it never
885
- * leaves this method. What is kept is enough to place the failure: the protocol/provider/model
886
- * and a stable coarse classification derived from the shape of the message (never its text).
887
- */
888
- logAssistantFailure(message) {
889
- this.logger.warn(`Pi assistant turn failed ${JSON.stringify({
890
- api: safeLabel(message.api),
891
- provider: safeLabel(message.provider),
892
- model: safeLabel(message.model),
893
- class: classifyAssistantFailure(message.errorMessage),
894
- })}`);
895
- }
896
940
  startupErrorToEvent(error) {
897
941
  if (error instanceof WaitingRunnerError) {
898
942
  return {
@@ -295,6 +295,7 @@ class RunStateCenter {
295
295
  kind: 'client_tool_output',
296
296
  itemId: updatedItem.id,
297
297
  },
298
+ authBindingId: (0, store_1.resolveRunAuthBindingId)(run),
298
299
  context: context,
299
300
  })) {
300
301
  const cancellation = await this.checkCancellation(run, threadRef, assistantState, context);
@@ -25,6 +25,7 @@ export interface RunRecordData {
25
25
  completedAt?: number;
26
26
  updatedAt: number;
27
27
  }
28
+ export declare function resolveRunAuthBindingId(run: RunRecordData): string | undefined;
28
29
  export interface RunStepRecordData {
29
30
  /** Base-relative Solid resource id, e.g. `chat/default/2026/05/18/runs.ttl#step_x`. */
30
31
  id: string;
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveRunAuthBindingId = resolveRunAuthBindingId;
3
4
  exports.isClaimableRunStatus = isClaimableRunStatus;
4
5
  exports.hasActiveRunLease = hasActiveRunLease;
5
6
  exports.canClaimRun = canClaimRun;
@@ -15,6 +16,18 @@ exports.buildRunStepResourceId = buildRunStepResourceId;
15
16
  exports.resolveBoundPodBaseUrl = resolveBoundPodBaseUrl;
16
17
  exports.resolveRunUrn = resolveRunUrn;
17
18
  exports.resolveDataResource = resolveDataResource;
19
+ function resolveRunAuthBindingId(run) {
20
+ const authBindingId = run.metadata?.authBindingId;
21
+ if (typeof authBindingId === 'string' && authBindingId.length > 0) {
22
+ return authBindingId;
23
+ }
24
+ const authBinding = run.metadata?.authBinding;
25
+ if (authBinding && typeof authBinding === 'object') {
26
+ const id = authBinding.id;
27
+ return typeof id === 'string' && id.length > 0 ? id : undefined;
28
+ }
29
+ return undefined;
30
+ }
18
31
  function isClaimableRunStatus(status) {
19
32
  return status === 'queued' || status === 'running';
20
33
  }
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.TaskMaterializer = void 0;
4
+ const TaskResumeDiagnostics_1 = require("./TaskResumeDiagnostics");
4
5
  const RunCancellation_1 = require("../runs/RunCancellation");
5
6
  const global_logger_factory_1 = require("global-logger-factory");
6
7
  const RunApproval_1 = require("../runs/RunApproval");
@@ -98,22 +99,28 @@ class TaskMaterializer {
98
99
  };
99
100
  }
100
101
  async resumeClientToolOutput(run, itemId, output, approval, context) {
101
- const executionContext = await this.withInvocationAiConnections(context);
102
- const prepared = await this.continuation.prepareClientToolOutput({
103
- threadRef: (0, types_1.toThreadRef)({ thread_id: run.thread }), itemId, output, context: executionContext,
102
+ const executionContext = await (0, TaskResumeDiagnostics_1.withTaskResumeStage)('invocation_issue', () => this.withInvocationAiConnections(context));
103
+ const prepared = await (0, TaskResumeDiagnostics_1.withTaskResumeStage)('continuation_prepare', async () => {
104
+ const prepared = await this.continuation.prepareClientToolOutput({
105
+ threadRef: (0, types_1.toThreadRef)({ thread_id: run.thread }), itemId, output, context: executionContext,
106
+ });
107
+ if (prepared)
108
+ prepared.claim.item.metadata = { ...prepared.claim.item.metadata, approval };
109
+ return prepared;
104
110
  });
105
111
  if (!prepared)
106
112
  return false;
107
- prepared.claim.item.metadata = { ...prepared.claim.item.metadata, approval };
108
113
  let completed = false;
109
114
  try {
110
- for await (const _event of this.continuation.completePreparedClientToolOutput(prepared, executionContext)) { /* Durable state is the result. */ }
115
+ await (0, TaskResumeDiagnostics_1.withTaskResumeStage)('continuation_complete', async () => {
116
+ for await (const _event of this.continuation.completePreparedClientToolOutput(prepared, executionContext)) { /* Durable state is the result. */ }
117
+ });
111
118
  completed = true;
112
119
  return true;
113
120
  }
114
121
  finally {
115
122
  if (!completed)
116
- await this.continuation.releaseClientToolOutput(prepared, executionContext);
123
+ await (0, TaskResumeDiagnostics_1.withTaskResumeStage)('continuation_release', () => this.continuation.releaseClientToolOutput(prepared, executionContext));
117
124
  }
118
125
  }
119
126
  async executeRun(input) {
@@ -0,0 +1,28 @@
1
+ /** Fixed resume boundaries, never derived from error prose or caller metadata. */
2
+ export declare const TASK_RESUME_STAGES: readonly ["route_request", "route_validation", "route_run_read", "run_read", "approval_validation", "checkpoint_validation", "task_lookup", "task_auth_restore", "invocation_issue", "continuation_prepare", "continuation_complete", "continuation_release", "run_result_read"];
3
+ export type TaskResumeStage = typeof TASK_RESUME_STAGES[number];
4
+ export declare function getTaskResumeStage(error: unknown): TaskResumeStage | undefined;
5
+ /** First inner boundary wins; frozen errors and original throw semantics are preserved. */
6
+ export declare function withTaskResumeStage<T>(stage: TaskResumeStage, operation: () => Promise<T>): Promise<T>;
7
+ export declare const TASK_RESUME_ERROR_TYPES: readonly ["error", "type_error", "range_error", "syntax_error", "non_error"];
8
+ export type TaskResumeErrorType = typeof TASK_RESUME_ERROR_TYPES[number];
9
+ export declare function getTaskResumeErrorType(error: unknown): TaskResumeErrorType;
10
+ export declare const TASK_RESUME_FAILURE_NAMES: readonly ["Error", "TypeError", "RangeError", "SyntaxError", "ReferenceError", "EvalError", "URIError", "AggregateError"];
11
+ export declare const TASK_RESUME_FAILURE_CODES: readonly ["ENOSPC", "ECONNREFUSED", "ECONNRESET", "ETIMEDOUT", "ENOTFOUND", "EACCES", "ERR_INVALID_URL"];
12
+ export declare const TASK_RESUME_SITE_MODULES: readonly ["api/runs/RunStateCenter", "api/runs/InngestRunExecutionBackend", "api/runs/ConditionalResourceDocument", "api/runs/RunApproval", "api/runs/RunCancellation", "api/runs/ManagedRunWorker", "api/runs/store", "api/chatkit/pod-store", "api/tasks/TaskMaterializer", "api/tasks/TaskService", "api/handlers/TaskHandler"];
13
+ export interface TaskResumeFailure {
14
+ name: typeof TASK_RESUME_FAILURE_NAMES[number];
15
+ code?: typeof TASK_RESUME_FAILURE_CODES[number];
16
+ causeCode?: typeof TASK_RESUME_FAILURE_CODES[number];
17
+ site?: {
18
+ module: typeof TASK_RESUME_SITE_MODULES[number];
19
+ line: number;
20
+ column: number;
21
+ coordinate: 'compiled_js' | 'source_ts';
22
+ kind: 'first_project_frame';
23
+ };
24
+ }
25
+ /** The same strict schema is used for HTTP evidence; unknown fields are never copied. */
26
+ export declare function selectTaskResumeFailure(value: unknown): TaskResumeFailure | undefined;
27
+ /** Attribution only: a whitelisted project frame may be a caller of an external origin. */
28
+ export declare function getTaskResumeFailure(error: unknown): TaskResumeFailure | undefined;
@@ -0,0 +1,148 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TASK_RESUME_SITE_MODULES = exports.TASK_RESUME_FAILURE_CODES = exports.TASK_RESUME_FAILURE_NAMES = exports.TASK_RESUME_ERROR_TYPES = exports.TASK_RESUME_STAGES = void 0;
4
+ exports.getTaskResumeStage = getTaskResumeStage;
5
+ exports.withTaskResumeStage = withTaskResumeStage;
6
+ exports.getTaskResumeErrorType = getTaskResumeErrorType;
7
+ exports.selectTaskResumeFailure = selectTaskResumeFailure;
8
+ exports.getTaskResumeFailure = getTaskResumeFailure;
9
+ const node_util_1 = require("node:util");
10
+ /** Fixed resume boundaries, never derived from error prose or caller metadata. */
11
+ exports.TASK_RESUME_STAGES = [
12
+ 'route_request', 'route_validation', 'route_run_read', 'run_read', 'approval_validation',
13
+ 'checkpoint_validation', 'task_lookup', 'task_auth_restore', 'invocation_issue',
14
+ 'continuation_prepare', 'continuation_complete', 'continuation_release', 'run_result_read',
15
+ ];
16
+ const stages = new WeakMap();
17
+ function getTaskResumeStage(error) {
18
+ return error instanceof Error ? stages.get(error) : undefined;
19
+ }
20
+ /** First inner boundary wins; frozen errors and original throw semantics are preserved. */
21
+ async function withTaskResumeStage(stage, operation) {
22
+ try {
23
+ return await operation();
24
+ }
25
+ catch (error) {
26
+ if (error instanceof Error && exports.TASK_RESUME_STAGES.includes(stage) && !stages.has(error))
27
+ stages.set(error, stage);
28
+ throw error;
29
+ }
30
+ }
31
+ exports.TASK_RESUME_ERROR_TYPES = ['error', 'type_error', 'range_error', 'syntax_error', 'non_error'];
32
+ function getTaskResumeErrorType(error) {
33
+ if (error instanceof TypeError)
34
+ return 'type_error';
35
+ if (error instanceof RangeError)
36
+ return 'range_error';
37
+ if (error instanceof SyntaxError)
38
+ return 'syntax_error';
39
+ return error instanceof Error ? 'error' : 'non_error';
40
+ }
41
+ exports.TASK_RESUME_FAILURE_NAMES = ['Error', 'TypeError', 'RangeError', 'SyntaxError',
42
+ 'ReferenceError', 'EvalError', 'URIError', 'AggregateError'];
43
+ exports.TASK_RESUME_FAILURE_CODES = ['ENOSPC', 'ECONNREFUSED', 'ECONNRESET', 'ETIMEDOUT',
44
+ 'ENOTFOUND', 'EACCES', 'ERR_INVALID_URL'];
45
+ exports.TASK_RESUME_SITE_MODULES = [
46
+ 'api/runs/RunStateCenter', 'api/runs/InngestRunExecutionBackend', 'api/runs/ConditionalResourceDocument',
47
+ 'api/runs/RunApproval', 'api/runs/RunCancellation', 'api/runs/ManagedRunWorker', 'api/runs/store',
48
+ 'api/chatkit/pod-store', 'api/tasks/TaskMaterializer', 'api/tasks/TaskService', 'api/handlers/TaskHandler',
49
+ ];
50
+ function ownValue(value, key) {
51
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
52
+ return descriptor && 'value' in descriptor ? descriptor.value : undefined;
53
+ }
54
+ function allowed(value, values) {
55
+ return typeof value === 'string' && values.some(entry => entry === value);
56
+ }
57
+ function coordinate(value) {
58
+ return typeof value === 'number' && Number.isSafeInteger(value) && value > 0 && value <= 1_000_000;
59
+ }
60
+ function record(value) {
61
+ return value !== null && typeof value === 'object' && !Array.isArray(value)
62
+ && [Object.prototype, null].includes(Object.getPrototypeOf(value));
63
+ }
64
+ /** The same strict schema is used for HTTP evidence; unknown fields are never copied. */
65
+ function selectTaskResumeFailure(value) {
66
+ try {
67
+ if (!record(value) || Object.keys(value).some(key => !['name', 'code', 'causeCode', 'site'].includes(key)))
68
+ return undefined;
69
+ const name = ownValue(value, 'name');
70
+ if (!allowed(name, exports.TASK_RESUME_FAILURE_NAMES))
71
+ return undefined;
72
+ const result = { name };
73
+ for (const key of ['code', 'causeCode']) {
74
+ const code = ownValue(value, key);
75
+ if (Object.prototype.hasOwnProperty.call(value, key)) {
76
+ if (!allowed(code, exports.TASK_RESUME_FAILURE_CODES))
77
+ return undefined;
78
+ result[key] = code;
79
+ }
80
+ }
81
+ if (Object.prototype.hasOwnProperty.call(value, 'site')) {
82
+ const site = ownValue(value, 'site');
83
+ if (!record(site) || Object.keys(site).some(key => !['module', 'line', 'column', 'coordinate', 'kind'].includes(key)))
84
+ return undefined;
85
+ const module = ownValue(site, 'module');
86
+ const line = ownValue(site, 'line');
87
+ const column = ownValue(site, 'column');
88
+ const location = ownValue(site, 'coordinate');
89
+ if (!allowed(module, exports.TASK_RESUME_SITE_MODULES) || !coordinate(line) || !coordinate(column)
90
+ || !allowed(location, ['compiled_js', 'source_ts']) || ownValue(site, 'kind') !== 'first_project_frame')
91
+ return undefined;
92
+ result.site = { module, line, column, coordinate: location, kind: 'first_project_frame' };
93
+ }
94
+ return result;
95
+ }
96
+ catch {
97
+ return undefined;
98
+ }
99
+ }
100
+ /** Attribution only: a whitelisted project frame may be a caller of an external origin. */
101
+ function getTaskResumeFailure(error) {
102
+ try {
103
+ if (!node_util_1.types.isNativeError(error))
104
+ return undefined;
105
+ const native = error;
106
+ const name = native instanceof TypeError ? 'TypeError'
107
+ : native instanceof RangeError ? 'RangeError' : native instanceof SyntaxError ? 'SyntaxError'
108
+ : native instanceof ReferenceError ? 'ReferenceError' : native instanceof EvalError ? 'EvalError'
109
+ : native instanceof URIError ? 'URIError' : native instanceof AggregateError ? 'AggregateError' : 'Error';
110
+ const result = { name };
111
+ const code = ownValue(native, 'code');
112
+ if (allowed(code, exports.TASK_RESUME_FAILURE_CODES))
113
+ result.code = code;
114
+ const cause = ownValue(native, 'cause');
115
+ const causeCode = node_util_1.types.isNativeError(cause) ? ownValue(cause, 'code') : undefined;
116
+ if (allowed(causeCode, exports.TASK_RESUME_FAILURE_CODES))
117
+ result.causeCode = causeCode;
118
+ for (const field of ['message', 'name']) {
119
+ const descriptor = Object.getOwnPropertyDescriptor(native, field);
120
+ if (descriptor && (!('value' in descriptor) || typeof descriptor.value !== 'string'
121
+ || descriptor.value.length > 8192 || /[\r\n]/.test(descriptor.value)))
122
+ return result;
123
+ }
124
+ const stack = ownValue(native, 'stack');
125
+ if (typeof stack !== 'string' || stack.length > 8192)
126
+ return result;
127
+ // Ignore the message line; parse only bounded V8/Bun frame lines, never eval or user paths.
128
+ for (const frame of stack.split('\n').slice(1, 17)) {
129
+ const match = /^\s+at (?:async )?(?:[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)* \()?((?:file:\/\/)?\/app\/(dist|src)\/(api\/[A-Za-z0-9/_-]+)\.(js|ts)):(\d{1,7}):(\d{1,7})\)?$/.exec(frame);
130
+ if (frame.includes('(') !== frame.endsWith(')'))
131
+ continue;
132
+ if (!match || (match[2] === 'dist' ? match[4] !== 'js' : match[4] !== 'ts'))
133
+ continue;
134
+ const module = match[3];
135
+ const line = Number(match[5]);
136
+ const column = Number(match[6]);
137
+ if (!allowed(module, exports.TASK_RESUME_SITE_MODULES) || !coordinate(line) || !coordinate(column))
138
+ continue;
139
+ result.site = { module, line, column, coordinate: match[2] === 'dist' ? 'compiled_js' : 'source_ts', kind: 'first_project_frame' };
140
+ break;
141
+ }
142
+ return result;
143
+ }
144
+ catch {
145
+ return undefined;
146
+ }
147
+ }
148
+ //# sourceMappingURL=TaskResumeDiagnostics.js.map
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.TaskService = void 0;
4
+ const TaskResumeDiagnostics_1 = require("./TaskResumeDiagnostics");
4
5
  const RunApproval_1 = require("../runs/RunApproval");
5
6
  const RunCancellation_1 = require("../runs/RunCancellation");
6
7
  const cron_1 = require("./cron");
@@ -146,23 +147,29 @@ class TaskService {
146
147
  return this.materializer.materialize({ task, context, trigger: { kind: 'manual' }, background: true });
147
148
  }
148
149
  async resumeApprovedRun(input, context, resolveExecutionContext) {
149
- const run = await this.store.loadRun(input.runId, context);
150
- const approval = await this.store.readTaskApproval?.(input.approval, context);
151
- if (!approval || !['approved', 'rejected'].includes(approval.status)
152
- || approval.decisionBy !== input.owner || (approval.assignedTo && approval.assignedTo !== input.owner) || !approval.resolvedAt
153
- || approval.thread !== run.thread)
154
- throw new Error('Approval does not authorize this run');
150
+ const run = await (0, TaskResumeDiagnostics_1.withTaskResumeStage)('run_read', () => this.store.loadRun(input.runId, context));
151
+ const approval = await (0, TaskResumeDiagnostics_1.withTaskResumeStage)('approval_validation', async () => {
152
+ const approval = await this.store.readTaskApproval?.(input.approval, context);
153
+ if (!approval || !['approved', 'rejected'].includes(approval.status)
154
+ || approval.decisionBy !== input.owner || (approval.assignedTo && approval.assignedTo !== input.owner) || !approval.resolvedAt
155
+ || approval.thread !== run.thread)
156
+ throw new Error('Approval does not authorize this run');
157
+ return approval;
158
+ });
155
159
  const threadRef = (0, types_1.toThreadRef)({ thread_id: run.thread });
156
160
  const output = JSON.stringify({ kind: 'approval_decision', approval: input.approval, decision: approval.status, actionExecuted: false });
157
161
  const waiting = run.metadata?.waitingTool;
158
162
  // Bind the checkpoint to this Run through the durable waiting-tool receipt. The receipt
159
163
  // lives on the Run row, independent of the client_tool_call item's free-form metadata;
160
164
  // `metadata.runId` remains only as a fallback for runs that predate the receipt.
161
- const items = await this.store.loadThreadItems(threadRef, undefined, 1000, 'asc', context);
162
- const item = items.data.find(item => item.type === 'client_tool_call' && item.call_id === approval.toolCallId
163
- && (waiting?.itemId !== undefined ? item.id === waiting.itemId : item.metadata?.runId === run.id));
164
- if (!item || item.type !== 'client_tool_call' || item.name !== approval.toolName)
165
- throw new Error('Approval does not match the pending tool checkpoint');
165
+ const item = await (0, TaskResumeDiagnostics_1.withTaskResumeStage)('checkpoint_validation', async () => {
166
+ const items = await this.store.loadThreadItems(threadRef, undefined, 1000, 'asc', context);
167
+ const item = items.data.find(item => item.type === 'client_tool_call' && item.call_id === approval.toolCallId
168
+ && (waiting?.itemId !== undefined ? item.id === waiting.itemId : item.metadata?.runId === run.id));
169
+ if (!item || item.type !== 'client_tool_call' || item.name !== approval.toolName)
170
+ throw new Error('Approval does not match the pending tool checkpoint');
171
+ return item;
172
+ });
166
173
  const atCheckpoint = waiting?.itemId === item.id && waiting.requestId === approval.toolCallId;
167
174
  if (item.status === 'completed' && item.output === output) {
168
175
  if (approval.status === 'rejected' && atCheckpoint && run.status === 'waiting_input') {
@@ -173,10 +180,14 @@ class TaskService {
173
180
  await (0, RunApproval_1.updateRunApprovalSession)(this.store, run, 'completed', context);
174
181
  return { run, resumed: false, duplicate: true };
175
182
  }
176
- if (approval.expiresAt && new Date(approval.expiresAt).getTime() <= Date.now())
177
- throw new Error('Approval has expired');
178
- if (run.status !== 'waiting_input' || waiting?.itemId !== item.id || waiting.requestId !== approval.toolCallId)
179
- throw new Error('Run is not waiting at this approval checkpoint');
183
+ await (0, TaskResumeDiagnostics_1.withTaskResumeStage)('approval_validation', async () => {
184
+ if (approval.expiresAt && new Date(approval.expiresAt).getTime() <= Date.now())
185
+ throw new Error('Approval has expired');
186
+ });
187
+ await (0, TaskResumeDiagnostics_1.withTaskResumeStage)('checkpoint_validation', async () => {
188
+ if (run.status !== 'waiting_input' || waiting?.itemId !== item.id || waiting.requestId !== approval.toolCallId)
189
+ throw new Error('Run is not waiting at this approval checkpoint');
190
+ });
180
191
  if (approval.status === 'rejected') {
181
192
  await this.store.saveItem(threadRef, { ...item, status: 'completed', output, metadata: { ...item.metadata, approval: input.approval } }, context);
182
193
  const cancelled = await (0, RunCancellation_1.cancelRun)({ store: this.store, runId: run.id, context, resourceIri: () => run.id });
@@ -186,17 +197,23 @@ class TaskService {
186
197
  // must still restore their separately granted execution credential.
187
198
  let execution = context;
188
199
  if (run.task) {
189
- const tasks = await this.store.listTasks({}, context);
190
- const task = tasks.find(task => task.id === run.task);
191
- if (!task)
192
- throw new Error('Task not found for this run');
193
- const granted = await resolveExecutionContext?.(task, context);
194
- if (!granted)
195
- throw new Error('Agent execution credential is unavailable');
200
+ const task = await (0, TaskResumeDiagnostics_1.withTaskResumeStage)('task_lookup', async () => {
201
+ const tasks = await this.store.listTasks({}, context);
202
+ const task = tasks.find(task => task.id === run.task);
203
+ if (!task)
204
+ throw new Error('Task not found for this run');
205
+ return task;
206
+ });
207
+ const granted = await (0, TaskResumeDiagnostics_1.withTaskResumeStage)('task_auth_restore', async () => {
208
+ const granted = await resolveExecutionContext?.(task, context);
209
+ if (!granted)
210
+ throw new Error('Agent execution credential is unavailable');
211
+ return granted;
212
+ });
196
213
  execution = granted;
197
214
  }
198
215
  const resumed = await this.materializer.resumeClientToolOutput(run, item.id, output, input.approval, execution);
199
- return { run: await this.store.loadRun(run.id, context), resumed, ...(!resumed ? { duplicate: true } : {}) };
216
+ return { run: await (0, TaskResumeDiagnostics_1.withTaskResumeStage)('run_result_read', () => this.store.loadRun(run.id, context)), resumed, ...(!resumed ? { duplicate: true } : {}) };
200
217
  }
201
218
  async materializeDueTasks(context, options = {}) {
202
219
  const now = options.now ?? (0, types_1.nowTimestamp)();
@@ -1,5 +1,5 @@
1
1
  import { type ETagHandler, type RepresentationMetadata } from '@solid/community-server';
2
- /** Render an operational storage revision for the negotiated representation. */
2
+ /** Finalize an isolated response copy with its negotiated HTTP tag. */
3
3
  export declare function getStorageETag(metadata: RepresentationMetadata): string | undefined;
4
4
  /** Persisted revision authority for reads, conditional mutations and notifications. */
5
5
  export declare class StorageETagHandler implements ETagHandler {
@@ -2,20 +2,29 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.StorageETagHandler = void 0;
4
4
  exports.getStorageETag = getStorageETag;
5
+ const community_server_1 = require("@solid/community-server");
6
+ const n3_1 = require("n3");
5
7
  const StorageVersion_1 = require("../StorageVersion");
6
- /** Render an operational storage revision for the negotiated representation. */
7
- function getStorageETag(metadata) {
8
+ function renderStorageETag(metadata) {
8
9
  const revision = (0, StorageVersion_1.getStorageVersion)(metadata);
9
10
  if (revision && metadata.contentType) {
10
11
  return `"xpod-${revision}-${Buffer.from(metadata.contentType).toString('base64url')}"`;
11
12
  }
12
- // Uninitialized metadata cannot establish a safe write baseline.
13
13
  return undefined;
14
14
  }
15
+ /** Finalize an isolated response copy with its negotiated HTTP tag. */
16
+ function getStorageETag(metadata) {
17
+ const tag = renderStorageETag(metadata);
18
+ // CSS merges response metadata into a 304 error that already contains this tag. Keeping the raw
19
+ // revision here creates two ETag values; the persisted revision remains recoverable from the tag.
20
+ if (tag)
21
+ metadata.set(community_server_1.HH.terms.etag, n3_1.DataFactory.literal(tag));
22
+ return tag;
23
+ }
15
24
  /** Persisted revision authority for reads, conditional mutations and notifications. */
16
25
  class StorageETagHandler {
17
26
  getETag(metadata) {
18
- return getStorageETag(metadata);
27
+ return renderStorageETag(metadata);
19
28
  }
20
29
  matchesETag(metadata, eTag, strict) {
21
30
  const parsed = StorageVersion_1.STORAGE_ETAG_PATTERN.exec(eTag);
@@ -0,0 +1,67 @@
1
+ export declare const TASK_MODEL_DIAGNOSTIC_STAGES: readonly ["not_invoked", "model_invoked", "payload_prepared", "stream_open"];
2
+ export declare const TASK_MODEL_DIAGNOSTIC_APIS: readonly ["openai-completions", "openai-responses", "other"];
3
+ export declare const TASK_MODEL_DIAGNOSTIC_STOP_REASONS: readonly ["error", "aborted", "unknown"];
4
+ export declare const TASK_GATEWAY_DIAGNOSTIC_ROUTES: readonly ["chat_completions", "responses", "anthropic_messages"];
5
+ export declare const TASK_GATEWAY_DIAGNOSTIC_CODES: readonly ["invalid_request", "invalid_tool_arguments", "unsupported_protocol_event", "service_access_missing", "credential_unavailable", "model_not_available", "no_model_available", "embedding_model_not_allowed", "provider_error", "internal_error"];
6
+ export declare const TASK_MODEL_SDK_ERROR_KINDS: readonly ["http_status", "connection", "timeout", "aborted", "length_limit", "content_filter", "unknown"];
7
+ export type TaskModelSdkErrorKind = typeof TASK_MODEL_SDK_ERROR_KINDS[number];
8
+ /**
9
+ * Bounded classification of the SDK-formatted provider error message. The SDK
10
+ * discards the structured HTTP response, so this is explicitly a hint and never
11
+ * a measured HTTP status; the raw message (which may embed a response body) is
12
+ * never retained.
13
+ */
14
+ export interface TaskModelSdkErrorHint {
15
+ kind: TaskModelSdkErrorKind;
16
+ /** Present only for kind 'http_status'; parsed from the SDK's fixed "<status> <message>" prefix. */
17
+ status?: number;
18
+ }
19
+ export interface TaskModelDiagnosticReceipt {
20
+ event: 'xpod.task-model-diagnostic';
21
+ schemaVersion: 1;
22
+ /** The existing invocation header identifies a session, not a unique Run. */
23
+ scope: 'session';
24
+ correlationHash: string;
25
+ stage: typeof TASK_MODEL_DIAGNOSTIC_STAGES[number];
26
+ api: typeof TASK_MODEL_DIAGNOSTIC_APIS[number];
27
+ stopReason: typeof TASK_MODEL_DIAGNOSTIC_STOP_REASONS[number];
28
+ retryCount: number;
29
+ credentialPresent: boolean;
30
+ /** The SDK discards the structured HTTP status before emitting an error. */
31
+ httpStatus: null;
32
+ /** Bounded hint derived from the SDK error message; never the raw text. */
33
+ sdkErrorHint?: TaskModelSdkErrorHint;
34
+ }
35
+ export interface TaskGatewayDiagnosticReceipt {
36
+ event: 'xpod.task-gateway-diagnostic';
37
+ schemaVersion: 1;
38
+ scope: 'session';
39
+ correlationHash: string;
40
+ route: typeof TASK_GATEWAY_DIAGNOSTIC_ROUTES[number];
41
+ callerHTTPstatus: number;
42
+ code: typeof TASK_GATEWAY_DIAGNOSTIC_CODES[number];
43
+ underlyingErrorStatus?: number;
44
+ streamOpen: boolean;
45
+ durationMs: number;
46
+ }
47
+ export interface TaskGatewayHttpDiagnosticReceipt {
48
+ event: 'xpod.task-gateway-http-diagnostic';
49
+ schemaVersion: 1;
50
+ scope: 'session';
51
+ correlationHash: string;
52
+ route: typeof TASK_GATEWAY_DIAGNOSTIC_ROUTES[number];
53
+ callerHTTPstatus: number;
54
+ statusSource: 'response_finished' | 'response_closed';
55
+ durationMs: number;
56
+ }
57
+ export type TaskModelFailureDiagnostic = TaskModelDiagnosticReceipt | TaskGatewayDiagnosticReceipt | TaskGatewayHttpDiagnosticReceipt;
58
+ /** Hash the exact header bytes; malformed headers never enter a diagnostic receipt. */
59
+ export declare function hashTaskModelDiagnosticSession(sessionHeader: unknown): string | undefined;
60
+ /**
61
+ * Classify the installed SDK's provider error message into a fixed enum.
62
+ * Only the SDK's own leading status prefix and exact error constants are
63
+ * recognized; embedded response text is never inspected or retained.
64
+ */
65
+ export declare function classifyTaskModelSdkErrorHint(message: unknown): TaskModelSdkErrorHint | undefined;
66
+ /** The sole field allowlist used by producers and the candidate artifact projector. */
67
+ export declare function selectTaskModelDiagnosticReceipt(value: unknown): TaskModelFailureDiagnostic | undefined;