@undefineds.co/xpod 0.4.25 → 0.4.27
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/api/ApiServer.d.ts +1 -0
- package/dist/api/ApiServer.js +38 -0
- package/dist/api/ai-config/AiConfigStore.js +18 -6
- package/dist/api/ai-gateway/connect/index.d.ts +1 -0
- package/dist/api/ai-gateway/connect/index.js +39 -18
- package/dist/api/ai-gateway/models/ProviderModelsAdapter.js +5 -2
- package/dist/api/container/common.js +8 -2
- package/dist/api/container/index.js +2 -2
- package/dist/api/container/local.js +6 -3
- package/dist/api/handlers/AiGatewayHandler.d.ts +1 -0
- package/dist/api/handlers/AiGatewayHandler.js +53 -3
- package/dist/api/handlers/AiGatewayManagementHandler.js +9 -0
- package/dist/api/handlers/TaskHandler.js +15 -10
- package/dist/api/network/NetworkEnvironmentConfigurationStore.js +1 -1
- package/dist/api/runs/ManagedRunWorker.d.ts +0 -1
- package/dist/api/runs/ManagedRunWorker.js +2 -14
- package/dist/api/runs/PiAgentRuntimeDriver.d.ts +0 -8
- package/dist/api/runs/PiAgentRuntimeDriver.js +65 -21
- package/dist/api/runs/RunStateCenter.js +1 -0
- package/dist/api/runs/store.d.ts +1 -0
- package/dist/api/runs/store.js +13 -0
- package/dist/api/tasks/TaskMaterializer.js +13 -6
- package/dist/api/tasks/TaskResumeDiagnostics.d.ts +28 -0
- package/dist/api/tasks/TaskResumeDiagnostics.js +148 -0
- package/dist/api/tasks/TaskService.js +40 -23
- package/dist/storage/conditions/StorageETagHandler.d.ts +1 -1
- package/dist/storage/conditions/StorageETagHandler.js +13 -4
- package/dist/util/task-model-diagnostics.d.ts +67 -0
- package/dist/util/task-model-diagnostics.js +118 -0
- package/node_modules/@undefineds.co/ai-connections/dist/AiApiKeyPool.d.ts +1 -0
- package/node_modules/@undefineds.co/ai-connections/dist/AiApiKeyPool.js +2 -1
- package/node_modules/@undefineds.co/ai-connections/dist/AiConnectDialog.d.ts +1 -0
- package/node_modules/@undefineds.co/ai-connections/dist/AiCredentialPoolSection.d.ts +1 -0
- package/node_modules/@undefineds.co/ai-connections/dist/AiCredentialPoolSection.js +10 -10
- package/node_modules/@undefineds.co/ai-connections/dist/AiGatewayKeyRow.d.ts +3 -1
- package/node_modules/@undefineds.co/ai-connections/dist/AiGatewayKeyRow.js +2 -2
- package/node_modules/@undefineds.co/ai-connections/dist/AiGatewayKeysSection.js +77 -23
- package/node_modules/@undefineds.co/ai-connections/dist/AiProviderCard.d.ts +1 -0
- package/node_modules/@undefineds.co/ai-connections/dist/AiSortableCredentialList.js +59 -59
- package/node_modules/@undefineds.co/ai-connections/dist/collection-runtime.js +2 -0
- package/node_modules/@undefineds.co/ai-connections/dist/collections.js +14 -6
- package/node_modules/@undefineds.co/ai-connections/dist/contract/ai-connections-client.d.ts +2 -1
- package/node_modules/@undefineds.co/ai-connections/dist/contract/ai-connections-client.js +1 -0
- package/node_modules/@undefineds.co/ai-connections/dist/contract/client/gateway-model-roles.d.ts +11 -0
- package/node_modules/@undefineds.co/ai-connections/dist/contract/client/gateway-model-roles.js +11 -0
- package/node_modules/@undefineds.co/ai-connections/dist/contract/client/normalize.d.ts +2 -1
- package/node_modules/@undefineds.co/ai-connections/dist/contract/client/normalize.js +12 -5
- package/node_modules/@undefineds.co/ai-connections/dist/contract/client/request.js +10 -2
- package/node_modules/@undefineds.co/ai-connections/dist/contract/client/types.d.ts +10 -0
- package/node_modules/@undefineds.co/ai-connections/package.json +1 -1
- package/node_modules/@undefineds.co/extension-sdk/dist/web.d.ts +1 -0
- package/node_modules/@undefineds.co/extension-sdk/package.json +1 -1
- package/node_modules/@undefineds.co/pod-collections/dist/index.cjs +60 -13
- package/node_modules/@undefineds.co/pod-collections/dist/mapping.d.ts +16 -1
- package/node_modules/@undefineds.co/pod-collections/dist/mapping.js +51 -0
- package/node_modules/@undefineds.co/pod-collections/dist/mutations.js +14 -18
- package/node_modules/@undefineds.co/pod-collections/package.json +1 -1
- package/node_modules/@undefineds.co/shared-ui/dist/list.d.ts +8 -1
- package/node_modules/@undefineds.co/shared-ui/dist/list.js +4 -1
- package/node_modules/@undefineds.co/shared-ui/dist/pod-sign-in/IdpChrome.d.ts +1 -1
- package/node_modules/@undefineds.co/shared-ui/dist/pod-sign-in/IdpChrome.js +2 -2
- package/node_modules/@undefineds.co/shared-ui/dist/pod-sign-in/IdpViews.js +2 -2
- package/node_modules/@undefineds.co/shared-ui/dist/pod-sign-in/PodSignInFrame.d.ts +3 -3
- package/node_modules/@undefineds.co/shared-ui/dist/pod-sign-in/PodSignInFrame.js +5 -5
- package/node_modules/@undefineds.co/shared-ui/dist/pod-sign-in/parts.d.ts +5 -5
- package/node_modules/@undefineds.co/shared-ui/dist/pod-sign-in/parts.js +5 -5
- package/node_modules/@undefineds.co/shared-ui/dist/section-header.js +1 -1
- package/node_modules/@undefineds.co/shared-ui/dist/theme.css +5 -32
- package/node_modules/@undefineds.co/shared-ui/package.json +1 -1
- package/node_modules/@undefineds.co/solid-sdk/package.json +1 -1
- package/package.json +2 -2
- package/static/app/assets/main.css +1 -1
- package/static/app/assets/main.js +1 -1
- package/static/dashboard/assets/AccountAuthBoundary-Dv52SZc6.js +1 -0
- package/static/dashboard/assets/DesktopOidcCallback-DaT_Rumf.js +1 -0
- package/static/dashboard/assets/DevicePages-HjclpIVm.js +2 -0
- package/static/dashboard/assets/ModelsPage-Be2TPd-x.js +11 -0
- package/static/dashboard/assets/{PaneListHeader-Dn5O0mwy.js → PaneListHeader-DtvnRmFq.js} +1 -1
- package/static/dashboard/assets/{PodDeletionAuthorizationPanel-D3NOG6ZM.js → PodDeletionAuthorizationPanel-BJk_8rJq.js} +3 -3
- package/static/dashboard/assets/PodPage-C4gzEspV.js +1 -0
- package/static/dashboard/assets/{SystemSettingsPage-BNQP_Yuc.js → SystemSettingsPage-HaNrP1H1.js} +1 -1
- package/static/dashboard/assets/TasksPage-BdO5K2L7.js +1 -0
- package/static/dashboard/assets/{WebDesktopEntry-vnb4CZWp.js → WebDesktopEntry-Cj9_ZfJU.js} +1 -1
- package/static/dashboard/assets/XpodAiConnectionsPodStore-B7gmXBmC.js +3 -0
- package/static/dashboard/assets/XpodShellApp-D_KY4nCW.js +4 -0
- package/static/dashboard/assets/{account-session-DCUmCop1.js → account-session-DlcKNG7k.js} +1 -1
- package/static/dashboard/assets/{button-By5Bgvmb.js → button-wslF55uZ.js} +1 -1
- package/static/dashboard/assets/collection-runtime-uxxgEu13.js +1 -0
- package/static/dashboard/assets/{createLucideIcon-bunMzPms.js → createLucideIcon-BJQl82Qk.js} +1 -1
- package/static/dashboard/assets/dashboard-CbJ-JkrI.css +1 -0
- package/static/dashboard/assets/{dashboard-mlsg3yaw.js → dashboard-Or0gnkou.js} +2 -2
- package/static/dashboard/assets/index-BqFeOML9.js +1 -0
- package/static/dashboard/assets/{index-DvMG5pHk.js → index-DgKn1twH.js} +2 -2
- package/static/dashboard/assets/{label-Bj03L9xs.js → label-CS6Sx6Zb.js} +1 -1
- package/static/dashboard/assets/list-UyhlCFKk.js +1 -0
- package/static/dashboard/assets/{nav-item-style-BNqWQY6p.js → nav-item-style-CW1Nnorq.js} +1 -1
- package/static/dashboard/assets/{pod-collections-host-KnnGFZHL.js → pod-collections-host-DjK2xf-b.js} +6 -6
- package/static/dashboard/assets/{safe-continuation-D9AhcWiB.js → safe-continuation-ClWAhcma.js} +1 -1
- package/static/dashboard/assets/{section-header-CLCzbOp_.js → section-header-CEIEcco-.js} +1 -1
- package/static/dashboard/assets/{setting-row-DTCec8A2.js → setting-row-CaYv3dfF.js} +1 -1
- package/static/dashboard/assets/xpod-remembered-login-BUF9fin1.js +310 -0
- package/static/dashboard/dashboard.html +2 -2
- package/static/settings/assets/{AccountAuthBoundary-C0og_gIA.js → AccountAuthBoundary-Chqd38oU.js} +1 -1
- package/static/settings/assets/{DesktopOidcCallback-C5q4oFZ0.js → DesktopOidcCallback-VUrOSM_q.js} +1 -1
- package/static/settings/assets/DevicePages-C6qwvWwu.js +2 -0
- package/static/settings/assets/ModelsPage-Ry_dObRQ.js +11 -0
- package/static/settings/assets/{PaneListHeader-DqVlMe2D.js → PaneListHeader-B5RkRqr4.js} +1 -1
- package/static/settings/assets/{PodDeletionAuthorizationPanel-DgVvarVY.js → PodDeletionAuthorizationPanel-DPM-5Izt.js} +3 -3
- package/static/settings/assets/PodPage-CxND6wwc.js +1 -0
- package/static/settings/assets/{SystemSettingsPage-Crzm_DrI.js → SystemSettingsPage-C8EdMkW7.js} +1 -1
- package/static/settings/assets/TasksPage-DuqptEm8.js +1 -0
- package/static/settings/assets/{WebDesktopEntry-BGC5eRF2.js → WebDesktopEntry-BRcyktkv.js} +1 -1
- package/static/settings/assets/XpodAiConnectionsPodStore-CyA_BJVC.js +3 -0
- package/static/settings/assets/XpodShellApp-CsivHJl0.js +4 -0
- package/static/settings/assets/XpodThemeProvider-CbJ-JkrI.css +1 -0
- package/static/settings/assets/{XpodThemeProvider-CB6LepTA.js → XpodThemeProvider-DK3pIdRG.js} +2 -2
- package/static/settings/assets/{account-session-BcLjCj0x.js → account-session-DJOZ03xr.js} +1 -1
- package/static/settings/assets/auth-callback-GIBYEz_7.js +1 -0
- package/static/settings/assets/{button-BObT8pTE.js → button-D3yW-2o1.js} +1 -1
- package/static/settings/assets/collection-runtime-DfGo25mh.js +1 -0
- package/static/settings/assets/{createLucideIcon-BVsUxXpZ.js → createLucideIcon-CacwLsmB.js} +1 -1
- package/static/settings/assets/{index-ktjMkj7-.js → index-r5KRYsKC.js} +1 -1
- package/static/settings/assets/{label-BrqO8_m_.js → label-MJjUmvzG.js} +1 -1
- package/static/settings/assets/list-Ce-5k1MX.js +1 -0
- package/static/settings/assets/{nav-item-style-Di2CxkDU.js → nav-item-style-ZUbL8KOZ.js} +1 -1
- package/static/settings/assets/{pod-collections-host-KnnGFZHL.js → pod-collections-host-DjK2xf-b.js} +6 -6
- package/static/settings/assets/{safe-continuation-Dx6ceJpY.js → safe-continuation-vlBZFSlU.js} +1 -1
- package/static/settings/assets/{section-header-Dtl7yjVr.js → section-header-DGJdEvQw.js} +1 -1
- package/static/settings/assets/{setting-row-BL6Hhe8N.js → setting-row-C7tuFiho.js} +1 -1
- package/static/settings/assets/{settings-DaRBxoZk.js → settings-CyQPih1O.js} +1 -1
- package/static/settings/assets/xpod-remembered-login-D-TzzT5K.js +567 -0
- package/static/settings/auth-callback.html +3 -3
- package/static/settings/settings.html +3 -3
- package/static/dashboard/assets/AccountAuthBoundary-BvoDydRz.js +0 -1
- package/static/dashboard/assets/DesktopOidcCallback-BwIj874x.js +0 -1
- package/static/dashboard/assets/DevicePages-jOjMcvsr.js +0 -2
- package/static/dashboard/assets/ModelsPage-CMTdfqBR.js +0 -11
- package/static/dashboard/assets/PodPage-BueBdJ9_.js +0 -1
- package/static/dashboard/assets/TasksPage-CxtLJiSF.js +0 -1
- package/static/dashboard/assets/XpodAiConnectionsPodStore-wx3Ttupm.js +0 -18
- package/static/dashboard/assets/XpodShellApp-5FWY8-d5.js +0 -4
- package/static/dashboard/assets/collection-runtime-BnomuB0E.js +0 -1
- package/static/dashboard/assets/dashboard-C64R553B.css +0 -1
- package/static/dashboard/assets/xpod-remembered-login-Dr69A0Rv.js +0 -295
- package/static/settings/assets/DevicePages-fyHjjucT.js +0 -2
- package/static/settings/assets/ModelsPage-EyOByLyE.js +0 -11
- package/static/settings/assets/PodPage-DY4Oi9wg.js +0 -1
- package/static/settings/assets/TasksPage-CfXJbnlt.js +0 -1
- package/static/settings/assets/XpodAiConnectionsPodStore-CXcmgKmQ.js +0 -3
- package/static/settings/assets/XpodShellApp-D1PS3Iut.js +0 -4
- package/static/settings/assets/XpodThemeProvider-C64R553B.css +0 -1
- package/static/settings/assets/auth-callback-DSPQ28ar.js +0 -1
- package/static/settings/assets/collection-runtime-DzNbdZGe.js +0 -1
- package/static/settings/assets/index-browser-3WfrNbcG.js +0 -273
- 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':
|
|
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
|
-
//
|
|
239
|
-
//
|
|
240
|
-
|
|
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
|
-
|
|
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);
|
package/dist/api/runs/store.d.ts
CHANGED
|
@@ -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;
|
package/dist/api/runs/store.js
CHANGED
|
@@ -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
|
|
103
|
-
|
|
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
|
-
|
|
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
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
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
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
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
|
-
/**
|
|
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
|
-
|
|
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
|
|
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;
|