@xopcai/xopc 0.0.362 → 0.0.363
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/browser-ext/manifest.json +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/{activity-store-_2aJ4ozN.js → activity-store-GvR2gZ0T.js} +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-CSIn4ghK.js → agent-avatar-dicebear-DkcXyPH0.js} +1 -1
- package/dist/gateway/static/root/assets/{agent-defaults-settings-panel-DBNFaHYa.js → agent-defaults-settings-panel-B-l5AaYN.js} +1 -1
- package/dist/gateway/static/root/assets/{agent-plugin-dialog-DJkGbMVg.js → agent-plugin-dialog-BY6tPYOK.js} +1 -1
- package/dist/gateway/static/root/assets/{agents-CuQayYYr.js → agents-CdSEoeje.js} +3 -3
- package/dist/gateway/static/root/assets/{agents-admin-api-Dl0V7UQq.js → agents-admin-api-BkXb7VEZ.js} +1 -1
- package/dist/gateway/static/root/assets/{app-management-settings-panel-CSczq9QV.js → app-management-settings-panel-BtD53cfe.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-W0vc1J1Q.js → appearance-settings-BAweWgX_.js} +1 -1
- package/dist/gateway/static/root/assets/{apps-page-esnvb3hh.js → apps-page-CS6-hrIB.js} +1 -1
- package/dist/gateway/static/root/assets/{archive-plugin-CBR91gHK.js → archive-plugin-CKe3qAaL.js} +1 -1
- package/dist/gateway/static/root/assets/{automations-page-B9RdzKUS.js → automations-page-CtE8r9Y2.js} +1 -1
- package/dist/gateway/static/root/assets/{automations-workspace-U9TEZd_t.js → automations-workspace-pnMgR4Da.js} +1 -1
- package/dist/gateway/static/root/assets/{binary-plugins-C9KIjPph.js → binary-plugins-J51oS7-D.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-C0bammwh.js → block-editor-D5hf6WcP.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-automation-inputs-D2E0co_S.js → browser-automation-inputs-5B3qAdmb.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-automations-page-8kqdhAOz.js → browser-automations-page-PZDQlXH4.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-settings-page-Dl4idi13.js → browser-settings-page-2YDV-ZWt.js} +1 -1
- package/dist/gateway/static/root/assets/{capabilities-Bfktn_7E.js → capabilities-BKu9BqPe.js} +1 -1
- package/dist/gateway/static/root/assets/{capabilities-BmMMlNmY.js → capabilities-CT_ZW9fT.js} +1 -1
- package/dist/gateway/static/root/assets/{capabilities-page-3ImhKPBx.js → capabilities-page-CB-iX3Fy.js} +2 -2
- package/dist/gateway/static/root/assets/{capabilities-settings-panel-4VDEB_ob.js → capabilities-settings-panel-BCTuWNaW.js} +1 -1
- package/dist/gateway/static/root/assets/{capability-header-actions-D1gxCGi8.js → capability-header-actions--aC5XB9E.js} +1 -1
- package/dist/gateway/static/root/assets/{channels-Drbr2JR-.js → channels-JnAQP1bt.js} +1 -1
- package/dist/gateway/static/root/assets/{chat-terminal-dock-CSkksCaa.js → chat-terminal-dock-BFZEpvzn.js} +1 -1
- package/dist/gateway/static/root/assets/{computer-settings-page-BIXpOJAk.js → computer-settings-page-Dc9ooEM-.js} +1 -1
- package/dist/gateway/static/root/assets/{connector-service-page-CTnlVEE1.js → connector-service-page-1_QB5D-t.js} +1 -1
- package/dist/gateway/static/root/assets/{connectors-page-BluT2l4d.js → connectors-page-Um_Jy5zh.js} +1 -1
- package/dist/gateway/static/root/assets/{date-picker-Bw-CydDe.js → date-picker-C9ZqTPui.js} +1 -1
- package/dist/gateway/static/root/assets/{dependency-picker-CgHzhtAc.js → dependency-picker-DBz0Pmyr.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-YahEnyfd.js → desktop-pet-BLtp2MmX.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-settings-47Z4bqJr.js → desktop-pet-settings-BNGbqwec.js} +1 -1
- package/dist/gateway/static/root/assets/{device-pairing-wizard-C3aPEEaM.js → device-pairing-wizard-7rdo-A7d.js} +1 -1
- package/dist/gateway/static/root/assets/{directory-picker-path-field-DH_ok92y.js → directory-picker-path-field-BnilGgwM.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-t1TiLQrR.js → extension-debug-page-BGyJpRv4.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-BeV-7G1h.js → extension-page-DsrVktRN.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-provider-CFCxzqna.js → extension-provider-F9JzOWZ3.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-DCODJHOu.js → extension-settings-page-jeb0ytRK.js} +1 -1
- package/dist/gateway/static/root/assets/{fallback-models-CJzvASNV.js → fallback-models-bZcxkEDz.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-swr-B3LfyXpc.js → gateway-config-swr-CoRoOG1l.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-realtime-CWxXXAe_.js → gateway-realtime-D0ygm6bc.js} +1 -1
- package/dist/gateway/static/root/assets/gateway-realtime-store-DBvCRclh.js +1 -0
- package/dist/gateway/static/root/assets/{gateway-settings-Q5NFzAJ_.js → gateway-settings-A9SzLsGp.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-startup-retry-BzQP02wp.js → gateway-startup-retry-FDS6S6qa.js} +1 -1
- package/dist/gateway/static/root/assets/{home-page-DndTkAw8.js → home-page-CnHzaH9K.js} +1 -1
- package/dist/gateway/static/root/assets/{image-generation-api-BO-Xs__S.js → image-generation-api-BPd76jW5.js} +1 -1
- package/dist/gateway/static/root/assets/{imports-page-Bb6Gm30L.js → imports-page-CzpsV6Vz.js} +1 -1
- package/dist/gateway/static/root/assets/{index-qOHWhsEK.js → index-BYNmHn-z.js} +7 -7
- package/dist/gateway/static/root/assets/index-CbJNcWAn.css +1 -0
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-D2u6sk5l.js → keyboard-shortcuts-settings-D90Dvlwo.js} +1 -1
- package/dist/gateway/static/root/assets/{local-app-workbench-page-CHeMfXrB.js → local-app-workbench-page-_9IKMkV8.js} +1 -1
- package/dist/gateway/static/root/assets/{local-apps-page-BanU1SLr.js → local-apps-page-D6gejARs.js} +1 -1
- package/dist/gateway/static/root/assets/{local-session-drafts-DLZQBcZY.js → local-session-drafts-gbemPamv.js} +1 -1
- package/dist/gateway/static/root/assets/{locale-store-BbwWB0ra.js → locale-store-DId6-v2R.js} +1 -1
- package/dist/gateway/static/root/assets/{logs-page-CQdHKCL0.js → logs-page--t8d-oLb.js} +1 -1
- package/dist/gateway/static/root/assets/{management-settings-D0V5kCx0.js → management-settings-DVDmXzTg.js} +1 -1
- package/dist/gateway/static/root/assets/{markdown-view-DIBLXsPm.js → markdown-view-DQZ51j9e.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-XPzvDZPz.js → media-plugins-XLewwq2q.js} +1 -1
- package/dist/gateway/static/root/assets/{messages-B4AhAW4B.js → messages-BHvpZNe9.js} +2 -2
- package/dist/gateway/static/root/assets/models-hub-cache-B9SKjidc.js +1 -0
- package/dist/gateway/static/root/assets/{note-detail-panel-CodeKI_W.js → note-detail-panel-DuZAEfU1.js} +3 -3
- package/dist/gateway/static/root/assets/{notes-api-CLn4r03Z.js → notes-api-Byt65v3r.js} +1 -1
- package/dist/gateway/static/root/assets/{notes-page-liRQoHbF.js → notes-page-CgWNESGp.js} +1 -1
- package/dist/gateway/static/root/assets/{oauth-api-D9zkd42J.js → oauth-api-BYsCbyGZ.js} +1 -1
- package/dist/gateway/static/root/assets/{page-context-capture-button-DO6DB9pi.js → page-context-capture-button-Z72eiQZn.js} +1 -1
- package/dist/gateway/static/root/assets/{personal-page-DdfdwOv5.js → personal-page-BtMatL9a.js} +1 -1
- package/dist/gateway/static/root/assets/{product-open-page-C9a3sm6Z.js → product-open-page-w5Hdf0hQ.js} +1 -1
- package/dist/gateway/static/root/assets/{project-detail-page-BVe5R-Nj.js → project-detail-page-CP6I3ZCV.js} +1 -1
- package/dist/gateway/static/root/assets/{projects-page-D_rCwi7w.js → projects-page-DYs6XrrK.js} +1 -1
- package/dist/gateway/static/root/assets/{registry-api-BFxQTbXQ.js → registry-api-7F2WNEZC.js} +1 -1
- package/dist/gateway/static/root/assets/{remote-access-hub-B3imt2du.js → remote-access-hub-DjYoYAEw.js} +1 -1
- package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-C9WC8hJM.js → runtime-tools-settings-panel-Qar_DXLv.js} +1 -1
- package/dist/gateway/static/root/assets/{scenes-page-DL95V1UP.js → scenes-page-BSIcDCa-.js} +1 -1
- package/dist/gateway/static/root/assets/{schema-form-CDgGmgrq.js → schema-form-C_cJLGgF.js} +1 -1
- package/dist/gateway/static/root/assets/{send-session-input-tkAQXGBG.js → send-session-input-D1oc6BC6.js} +1 -1
- package/dist/gateway/static/root/assets/{session-api-9X7qep91.js → session-api-N3N37MrR.js} +1 -1
- package/dist/gateway/static/root/assets/{session-manager-Dy22bbJT.js → session-manager-CMGUwtbN.js} +1 -1
- package/dist/gateway/static/root/assets/{sessions-page-CeeKIVsM.js → sessions-page-BBV5Z24a.js} +1 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-DRc9IDJf.js +1 -0
- package/dist/gateway/static/root/assets/{settings-page-xmUGYSOG.js → settings-page-F2vGXBw7.js} +1 -1
- package/dist/gateway/static/root/assets/{setup-status-panel-CmjT9Q4P.js → setup-status-panel-DJxbjUOg.js} +1 -1
- package/dist/gateway/static/root/assets/{share-link-dialog-DeOe9qfQ.js → share-link-dialog-TBBGJPfT.js} +1 -1
- package/dist/gateway/static/root/assets/{share-preview-page-Ck27gozJ.js → share-preview-page-DyRLPsPv.js} +1 -1
- package/dist/gateway/static/root/assets/{shares-api-D-GuMkPu.js → shares-api-DU0fHP2G.js} +1 -1
- package/dist/gateway/static/root/assets/{shares-settings-DpvM2OyO.js → shares-settings-CJa6FBpP.js} +1 -1
- package/dist/gateway/static/root/assets/{skill-api-BpD02td8.js → skill-api-C340XD9d.js} +1 -1
- package/dist/gateway/static/root/assets/{skill-reload-api-CezAwYxM.js → skill-reload-api-ClyFqHk7.js} +1 -1
- package/dist/gateway/static/root/assets/{skills-page-BCgbVI-P.js → skills-page-C4lF94m5.js} +1 -1
- package/dist/gateway/static/root/assets/{system-settings-panel-UODcpzsP.js → system-settings-panel-CmJUu3lb.js} +1 -1
- package/dist/gateway/static/root/assets/{task-detail-page-BbqQnNes.js → task-detail-page-De-Rr8W6.js} +1 -1
- package/dist/gateway/static/root/assets/{theme-store-6IK2Ik2J.js → theme-store-OhcbFEUR.js} +1 -1
- package/dist/gateway/static/root/assets/tracing-settings-page-DoBJ0TYY.js +1 -0
- package/dist/gateway/static/root/assets/{tunnel-api-Eey2aZIz.js → tunnel-api-Bl4nrCuE.js} +1 -1
- package/dist/gateway/static/root/assets/{usage-page-BrWj5mk3.js → usage-page-QZEAaa8n.js} +1 -1
- package/dist/gateway/static/root/assets/{use-autosave-B-3BJbnn.js → use-autosave-veoM0dHn.js} +1 -1
- package/dist/gateway/static/root/assets/{use-composer-attachments-ByAHuR8v.js → use-composer-attachments-DB0bBGzZ.js} +1 -1
- package/dist/gateway/static/root/assets/{use-file-preview-expanded-CXVwWJeP.js → use-file-preview-expanded-Bope6MW6.js} +2 -2
- package/dist/gateway/static/root/assets/{user-model-page-Cg-oM8nY.js → user-model-page-Crh2oNpZ.js} +1 -1
- package/dist/gateway/static/root/assets/{voice-api-key-field-BtffIKZD.js → voice-api-key-field-CwD6a3KQ.js} +1 -1
- package/dist/gateway/static/root/assets/{voice-config-api-BMm9dSKX.js → voice-config-api-Daexehmc.js} +1 -1
- package/dist/gateway/static/root/assets/{work-discovery-overlay-ByHhF_4r.js → work-discovery-overlay-9jnjEAaz.js} +1 -1
- package/dist/gateway/static/root/assets/{workflow-run-setup-panel-DfNn5VTy.js → workflow-run-setup-panel-BAcPxdcT.js} +1 -1
- package/dist/gateway/static/root/assets/{workflows-page-B67D-7Wt.js → workflows-page-D8Zm-ooa.js} +1 -1
- package/dist/gateway/static/root/assets/{workspace-api-TAD7LYsc.js → workspace-api-Cs4sjV2A.js} +2 -2
- package/dist/gateway/static/root/index.html +28 -28
- package/dist/package.js +1 -1
- package/dist/src/agent/agent-manager.js +10 -6
- package/dist/src/agent/embedded/model-runtime.js +1 -1
- package/dist/src/agent/embedded/run-for-session.js +28 -3
- package/dist/src/agent/embedded/run-turn.js +18 -3
- package/dist/src/agent/image/tool-model-config.js +1 -1
- package/dist/src/agent/mcp/oauth/mcp-oauth-store.js +1 -1
- package/dist/src/agent/service.js +5 -3
- package/dist/src/agent/skills/hub-lock.js +1 -1
- package/dist/src/agent-catalog/migrations/legacy-config-cutover.js +1 -1
- package/dist/src/agent-catalog/repository.js +1 -1
- package/dist/src/auth/credentials.js +2 -2
- package/dist/src/auth/oauth-provider-lock.js +1 -1
- package/dist/src/auth/profiles/store.js +1 -1
- package/dist/src/auth/sync-provider-auth.js +1 -1
- package/dist/src/browser/providers/browser-ext-install.js +1 -1
- package/dist/src/chat-commands/agent-edit.js +1 -1
- package/dist/src/chat-commands/builtins/config.js +1 -1
- package/dist/src/cli/commands/agent.js +1 -1
- package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
- package/dist/src/cli/commands/doctor/checks/image-providers.js +1 -1
- package/dist/src/cli/commands/doctor/checks/provider-auth.js +3 -3
- package/dist/src/cli/commands/gateway/lifecycle.js +1 -1
- package/dist/src/cli/commands/models.js +1 -1
- package/dist/src/cli/commands/onboard/model.js +3 -3
- package/dist/src/config/agent-profile.js +1 -1
- package/dist/src/config/index.js +4 -4
- package/dist/src/config/loader.js +2 -2
- package/dist/src/config/models-json.js +1 -1
- package/dist/src/config/reload.d.ts +1 -0
- package/dist/src/config/reload.js +4 -0
- package/dist/src/config/rules.js +5 -0
- package/dist/src/config/schema.d.ts +23 -0
- package/dist/src/config/schema.js +6 -3
- package/dist/src/extensions/activation-context.js +1 -1
- package/dist/src/extensions/lockfile.js +1 -1
- package/dist/src/gateway/file-path-classifier.js +1 -1
- package/dist/src/gateway/hono/lib/config-payload.js +2 -2
- package/dist/src/gateway/hono/routes/agents.js +1 -1
- package/dist/src/gateway/hono/routes/config-patch/misc.js +2 -2
- package/dist/src/gateway/hono/routes/connectors.js +1 -1
- package/dist/src/gateway/hono/routes/lazy-bundles.js +8 -0
- package/dist/src/gateway/hono/routes/models.js +2 -2
- package/dist/src/gateway/hono/routes/observability.d.ts +3 -0
- package/dist/src/gateway/hono/routes/observability.js +106 -0
- package/dist/src/gateway/image-generation-setup.js +1 -1
- package/dist/src/gateway/lock.js +1 -1
- package/dist/src/gateway/security/gateway-scopes.js +1 -0
- package/dist/src/gateway/service/config-coordinator.d.ts +0 -25
- package/dist/src/gateway/service/config-coordinator.js +31 -0
- package/dist/src/gateway/service.js +5 -3
- package/dist/src/infra/update-restart.js +1 -1
- package/dist/src/observability/config.d.ts +40 -0
- package/dist/src/observability/config.js +31 -0
- package/dist/src/observability/langfuse.d.ts +31 -0
- package/dist/src/observability/langfuse.js +180 -0
- package/dist/src/observability/runtime.d.ts +57 -0
- package/dist/src/observability/runtime.js +404 -0
- package/dist/src/observability/sanitize.d.ts +3 -0
- package/dist/src/observability/sanitize.js +74 -0
- package/dist/src/observability/store.d.ts +20 -0
- package/dist/src/observability/store.js +128 -0
- package/dist/src/observability/storeWorker.d.ts +2 -0
- package/dist/src/observability/storeWorker.js +155 -0
- package/dist/src/observability/types.d.ts +21 -0
- package/dist/src/observability/types.js +1 -0
- package/dist/src/platform/index.js +1 -1
- package/dist/src/providers/auth-runtime/resolve-auth.js +1 -1
- package/dist/src/providers/index.js +3 -3
- package/dist/src/providers/model-call.js +19 -5
- package/dist/src/providers/model-catalog-persistence.js +1 -1
- package/dist/src/providers/model-registry.js +1 -1
- package/dist/src/providers/provider-disconnect-core.js +1 -1
- package/dist/src/providers/xopc-cloud-catalog-coordinator.js +1 -1
- package/dist/src/storage/sqlite/ai-usage-repository.d.ts +6 -0
- package/dist/src/storage/sqlite/ai-usage-repository.js +4 -2
- package/dist/src/storage/sqlite/migrations/233_observability_usage_links.sql +3 -0
- package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
- package/dist/src/storage/sqlite/migrations/runner.js +3 -3
- package/dist/src/storage/sqlite/schema.js +1 -1
- package/dist/src/tui/backends/embedded-backend.js +2 -2
- package/dist/src/tui/tui-commands.js +1 -1
- package/dist/src/tui/tui-startup-resources.js +1 -1
- package/dist/src/tui/tui.js +1 -1
- package/dist/src/usage/recorder.d.ts +4 -2
- package/dist/src/usage/recorder.js +54 -26
- package/dist/src/usage/types.d.ts +3 -0
- package/dist/src/utils/logger/context.d.ts +1 -1
- package/dist/src/utils/logger/context.js +4 -1
- package/dist/src/utils/logger/types.d.ts +3 -0
- package/package.json +6 -3
- package/dist/gateway/static/root/assets/gateway-realtime-store-DS4WzQyF.js +0 -1
- package/dist/gateway/static/root/assets/index-Byk2QKIS.css +0 -1
- package/dist/gateway/static/root/assets/models-hub-cache-DN5MnhL8.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-DuvJrDU2.js +0 -1
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
//#region src/observability/sanitize.ts
|
|
2
|
+
const SECRET_KEY = /authorization|api.?key|password|secret|access.?token|refresh.?token|bot.?token|cookie/i;
|
|
3
|
+
const OMIT_KEY = /^(data|base64|headers|image_url|audio|video)$/i;
|
|
4
|
+
function redactString(value) {
|
|
5
|
+
return value.replace(/Bearer\s+[^\s"']+/gi, "Bearer [REDACTED]").replace(/\b(?:sk-|pk-lf-|sk-lf-)[A-Za-z0-9_-]+/g, "[REDACTED]").replace(/((?:api[_-]?key|token|secret|password)\s*[=:]\s*)[^\s,;"']+/gi, "$1[REDACTED]").replace(/data:[^\s"']+;base64,[^\s"']+/gi, "[MEDIA OMITTED]");
|
|
6
|
+
}
|
|
7
|
+
/** Bounds traversal before serializing; never materializes an entire tool/file payload. */
|
|
8
|
+
function boundedJson(value, maxBytes = 8192) {
|
|
9
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
10
|
+
let budget = Math.max(64, maxBytes - 256);
|
|
11
|
+
let truncated = false;
|
|
12
|
+
const walk = (v, depth) => {
|
|
13
|
+
if (budget < 32 || depth > 5) {
|
|
14
|
+
truncated = true;
|
|
15
|
+
return "[TRUNCATED]";
|
|
16
|
+
}
|
|
17
|
+
if (v === null || typeof v === "number" || typeof v === "boolean") {
|
|
18
|
+
budget -= 16;
|
|
19
|
+
return v;
|
|
20
|
+
}
|
|
21
|
+
if (typeof v === "string") {
|
|
22
|
+
const slice = v.slice(0, Math.min(budget, 4096));
|
|
23
|
+
const clean = redactString(slice);
|
|
24
|
+
const buf = Buffer.from(clean);
|
|
25
|
+
const size = Math.min(budget, 4096);
|
|
26
|
+
budget -= Math.min(buf.length, size);
|
|
27
|
+
if (slice.length < v.length || buf.length > size) truncated = true;
|
|
28
|
+
return buf.subarray(0, size).toString("utf8");
|
|
29
|
+
}
|
|
30
|
+
if (typeof v !== "object") return void 0;
|
|
31
|
+
if (seen.has(v)) return "[CIRCULAR]";
|
|
32
|
+
seen.add(v);
|
|
33
|
+
if (Array.isArray(v)) {
|
|
34
|
+
const values = [];
|
|
35
|
+
for (let i = 0; i < Math.min(v.length, 40) && budget > 32; i++) values.push(walk(v[i], depth + 1));
|
|
36
|
+
if (values.length < v.length) truncated = true;
|
|
37
|
+
return values;
|
|
38
|
+
}
|
|
39
|
+
const result = {};
|
|
40
|
+
let count = 0;
|
|
41
|
+
for (const key in v) {
|
|
42
|
+
if (!Object.hasOwn(v, key)) continue;
|
|
43
|
+
if (++count > 40 || budget < 32) {
|
|
44
|
+
truncated = true;
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
const safeKey = key.slice(0, 80);
|
|
48
|
+
budget -= Buffer.byteLength(safeKey) + 8;
|
|
49
|
+
try {
|
|
50
|
+
result[safeKey] = SECRET_KEY.test(key) ? "[REDACTED]" : OMIT_KEY.test(key) ? "[OMITTED]" : walk(v[key], depth + 1);
|
|
51
|
+
} catch {
|
|
52
|
+
result[safeKey] = "[UNAVAILABLE]";
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return result;
|
|
56
|
+
};
|
|
57
|
+
let data;
|
|
58
|
+
try {
|
|
59
|
+
data = walk(value, 0);
|
|
60
|
+
} catch {
|
|
61
|
+
data = "[UNAVAILABLE]";
|
|
62
|
+
}
|
|
63
|
+
const text = JSON.stringify({
|
|
64
|
+
preview: data,
|
|
65
|
+
truncated
|
|
66
|
+
});
|
|
67
|
+
if (Buffer.byteLength(text) <= maxBytes) return text;
|
|
68
|
+
return JSON.stringify({
|
|
69
|
+
preview: Buffer.from(redactString(text)).subarray(0, Math.floor(maxBytes / 8)).toString("utf8"),
|
|
70
|
+
truncated: true
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
//#endregion
|
|
74
|
+
export { boundedJson, redactString };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { TracingConfig } from './config.js';
|
|
2
|
+
import type { TraceRecord } from './types.js';
|
|
3
|
+
export declare class LocalTraceStore {
|
|
4
|
+
private worker?;
|
|
5
|
+
private sequence;
|
|
6
|
+
private requests;
|
|
7
|
+
private pendingBytes;
|
|
8
|
+
private epoch;
|
|
9
|
+
private epochReady;
|
|
10
|
+
dropped: number;
|
|
11
|
+
lastError?: string;
|
|
12
|
+
constructor(config: TracingConfig, file?: string);
|
|
13
|
+
private fail;
|
|
14
|
+
request(op: string, data?: unknown): Promise<any>;
|
|
15
|
+
begin(record: TraceRecord): Promise<number>;
|
|
16
|
+
write(record: TraceRecord, epoch?: number): Promise<void>;
|
|
17
|
+
clear(): Promise<void>;
|
|
18
|
+
flush(): Promise<void>;
|
|
19
|
+
close(): Promise<void>;
|
|
20
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { init_paths_state, resolveStateDir } from "../config/paths-state.js";
|
|
2
|
+
import { redactString } from "./sanitize.js";
|
|
3
|
+
import { traceStoreWorkerSource } from "./storeWorker.js";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { randomUUID } from "node:crypto";
|
|
6
|
+
import { Worker } from "node:worker_threads";
|
|
7
|
+
//#region src/observability/store.ts
|
|
8
|
+
init_paths_state();
|
|
9
|
+
var LocalTraceStore = class {
|
|
10
|
+
worker;
|
|
11
|
+
sequence = 0;
|
|
12
|
+
requests = /* @__PURE__ */ new Map();
|
|
13
|
+
pendingBytes = 0;
|
|
14
|
+
epoch = 0;
|
|
15
|
+
epochReady;
|
|
16
|
+
dropped = 0;
|
|
17
|
+
lastError;
|
|
18
|
+
constructor(config, file = join(resolveStateDir(), "traces", "traces.db")) {
|
|
19
|
+
try {
|
|
20
|
+
this.worker = new Worker(traceStoreWorkerSource, {
|
|
21
|
+
eval: true,
|
|
22
|
+
execArgv: process.execArgv.filter((arg, index, args) => !arg.startsWith("--input-type") && args[index - 1] !== "--input-type"),
|
|
23
|
+
workerData: {
|
|
24
|
+
path: file,
|
|
25
|
+
config,
|
|
26
|
+
owner: randomUUID()
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
this.worker.on("message", ({ id, value, error }) => {
|
|
30
|
+
const request = this.requests.get(id);
|
|
31
|
+
if (!request) return;
|
|
32
|
+
clearTimeout(request.timer);
|
|
33
|
+
this.requests.delete(id);
|
|
34
|
+
this.pendingBytes -= request.bytes;
|
|
35
|
+
if (error) {
|
|
36
|
+
this.lastError = error;
|
|
37
|
+
request.reject(new Error(error));
|
|
38
|
+
} else request.resolve(value);
|
|
39
|
+
if (!this.requests.size) this.worker?.unref();
|
|
40
|
+
});
|
|
41
|
+
this.worker.on("error", (error) => this.fail(`Trace worker unavailable: ${redactString(error instanceof Error ? error.message : "Worker startup failed").slice(0, 200)}`));
|
|
42
|
+
this.worker.on("exit", () => {
|
|
43
|
+
if (this.worker) this.fail("Trace worker stopped");
|
|
44
|
+
});
|
|
45
|
+
this.worker.unref();
|
|
46
|
+
this.epochReady = this.request("status").then((s) => {
|
|
47
|
+
this.epoch = s.epoch;
|
|
48
|
+
}).catch(() => {});
|
|
49
|
+
} catch {
|
|
50
|
+
this.lastError = "Trace worker unavailable";
|
|
51
|
+
this.epochReady = Promise.resolve();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
fail(message) {
|
|
55
|
+
this.lastError = message;
|
|
56
|
+
for (const r of this.requests.values()) {
|
|
57
|
+
clearTimeout(r.timer);
|
|
58
|
+
r.reject(new Error(message));
|
|
59
|
+
}
|
|
60
|
+
this.requests.clear();
|
|
61
|
+
this.pendingBytes = 0;
|
|
62
|
+
this.worker = void 0;
|
|
63
|
+
}
|
|
64
|
+
async request(op, data) {
|
|
65
|
+
if (!this.worker) throw new Error(this.lastError ?? "Trace storage unavailable");
|
|
66
|
+
const bytes = Buffer.byteLength(JSON.stringify(data ?? null));
|
|
67
|
+
if (this.requests.size >= 2e3 || this.pendingBytes + bytes > 8 * 1048576) {
|
|
68
|
+
this.dropped++;
|
|
69
|
+
throw new Error("Trace queue full");
|
|
70
|
+
}
|
|
71
|
+
const id = ++this.sequence;
|
|
72
|
+
this.pendingBytes += bytes;
|
|
73
|
+
this.worker.ref();
|
|
74
|
+
return new Promise((resolve, reject) => {
|
|
75
|
+
const timer = setTimeout(() => {
|
|
76
|
+
this.requests.delete(id);
|
|
77
|
+
this.pendingBytes -= bytes;
|
|
78
|
+
this.dropped++;
|
|
79
|
+
reject(/* @__PURE__ */ new Error("Trace storage timeout"));
|
|
80
|
+
this.worker?.terminate();
|
|
81
|
+
}, 5e3);
|
|
82
|
+
this.requests.set(id, {
|
|
83
|
+
resolve,
|
|
84
|
+
reject,
|
|
85
|
+
timer,
|
|
86
|
+
bytes
|
|
87
|
+
});
|
|
88
|
+
this.worker.postMessage({
|
|
89
|
+
id,
|
|
90
|
+
op,
|
|
91
|
+
data
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
async begin(record) {
|
|
96
|
+
await this.epochReady;
|
|
97
|
+
return this.request("put", {
|
|
98
|
+
records: [record],
|
|
99
|
+
epoch: null
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
async write(record, epoch) {
|
|
103
|
+
await this.epochReady;
|
|
104
|
+
try {
|
|
105
|
+
await this.request("put", {
|
|
106
|
+
records: [record],
|
|
107
|
+
epoch: epoch ?? this.epoch
|
|
108
|
+
});
|
|
109
|
+
this.lastError = void 0;
|
|
110
|
+
} catch (error) {
|
|
111
|
+
this.dropped++;
|
|
112
|
+
this.lastError = error instanceof Error ? error.message : "Trace write failed";
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async clear() {
|
|
116
|
+
this.epoch = await this.request("clear");
|
|
117
|
+
}
|
|
118
|
+
async flush() {
|
|
119
|
+
await this.request("status");
|
|
120
|
+
}
|
|
121
|
+
async close() {
|
|
122
|
+
const worker = this.worker;
|
|
123
|
+
this.fail("Trace store closed");
|
|
124
|
+
await worker?.terminate();
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
//#endregion
|
|
128
|
+
export { LocalTraceStore };
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
//#region src/observability/storeWorker.ts
|
|
2
|
+
/** Self-contained worker source works in tsx, published unbundled Node, and Electron builds. */
|
|
3
|
+
const traceStoreWorkerSource = String.raw`
|
|
4
|
+
const { parentPort, workerData } = require('node:worker_threads');
|
|
5
|
+
const { DatabaseSync } = require('node:sqlite');
|
|
6
|
+
const fs = require('node:fs');
|
|
7
|
+
const path = require('node:path');
|
|
8
|
+
const file = workerData.path;
|
|
9
|
+
fs.mkdirSync(path.dirname(file), { recursive: true, mode: 0o700 });
|
|
10
|
+
const db = new DatabaseSync(file);
|
|
11
|
+
let config = workerData.config;
|
|
12
|
+
let dropped = 0;
|
|
13
|
+
let paused = false;
|
|
14
|
+
let lastPruned = 0;
|
|
15
|
+
function retryStartup(fn) {
|
|
16
|
+
const deadline = Date.now() + 1000;
|
|
17
|
+
const wait = new Int32Array(new SharedArrayBuffer(4));
|
|
18
|
+
while (true) {
|
|
19
|
+
try { return fn(); }
|
|
20
|
+
catch (error) {
|
|
21
|
+
if (!/database (?:table )?is locked/.test(String(error.message)) || Date.now() >= deadline) throw error;
|
|
22
|
+
Atomics.wait(wait, 0, 0, 25);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function limits() {
|
|
27
|
+
db.exec('PRAGMA max_page_count=' + Math.floor((config.local.maxStoreMiB - 4) * 1048576 / 8192));
|
|
28
|
+
}
|
|
29
|
+
db.exec('PRAGMA busy_timeout=100;');
|
|
30
|
+
retryStartup(() => {
|
|
31
|
+
if (Number(db.prepare('PRAGMA user_version').get().user_version) > 1) throw new Error('Trace database schema is newer than this runtime');
|
|
32
|
+
db.exec('PRAGMA page_size=4096; PRAGMA auto_vacuum=INCREMENTAL; PRAGMA journal_mode=WAL; PRAGMA synchronous=NORMAL; PRAGMA wal_autocheckpoint=128; PRAGMA journal_size_limit=1048576;');
|
|
33
|
+
db.exec('CREATE TABLE IF NOT EXISTS traces (trace_id TEXT PRIMARY KEY, started_at INTEGER NOT NULL, ended_at INTEGER, status TEXT NOT NULL, conversation_id TEXT, run_id TEXT, agent_id TEXT, owner TEXT, data TEXT NOT NULL, partial_reason TEXT); CREATE INDEX IF NOT EXISTS trace_time ON traces(started_at,trace_id); CREATE INDEX IF NOT EXISTS trace_conversation ON traces(conversation_id,started_at); CREATE TABLE IF NOT EXISTS spans (trace_id TEXT NOT NULL, span_id TEXT NOT NULL, data TEXT NOT NULL, bytes INTEGER NOT NULL, PRIMARY KEY(trace_id,span_id)); CREATE TABLE IF NOT EXISTS state (id INTEGER PRIMARY KEY, epoch INTEGER NOT NULL, window INTEGER NOT NULL, written INTEGER NOT NULL); INSERT OR IGNORE INTO state VALUES(1,0,0,0); CREATE TABLE IF NOT EXISTS leases (owner TEXT PRIMARY KEY, heartbeat INTEGER NOT NULL); PRAGMA user_version=1;');
|
|
34
|
+
limits();
|
|
35
|
+
});
|
|
36
|
+
function size() {
|
|
37
|
+
return ['', '-wal', '-shm'].reduce((sum, suffix) => { try { return sum + fs.statSync(file + suffix).size; } catch { return sum; } }, 0);
|
|
38
|
+
}
|
|
39
|
+
function permissions() { for (const suffix of ['', '-wal', '-shm']) { try { fs.chmodSync(file + suffix, 0o600); } catch {} } }
|
|
40
|
+
function transaction(fn) { db.exec('BEGIN IMMEDIATE'); try { const r = fn(); db.exec('COMMIT'); return r; } catch(e) { db.exec('ROLLBACK'); throw e; } }
|
|
41
|
+
function heartbeat() {
|
|
42
|
+
db.prepare('INSERT INTO leases VALUES (?,?) ON CONFLICT(owner) DO UPDATE SET heartbeat=excluded.heartbeat').run(workerData.owner, Date.now());
|
|
43
|
+
const stale = db.prepare('SELECT trace_id,data FROM traces WHERE ended_at IS NULL AND (started_at < ? OR owner IN (SELECT owner FROM leases WHERE heartbeat < ?)) LIMIT 256').all(Date.now()-86400000, Date.now()-120000);
|
|
44
|
+
for (const row of stale) { const data=JSON.parse(row.data); data.endedAt=Date.now(); data.status='interrupted'; db.prepare('UPDATE traces SET ended_at=?,status=?,data=? WHERE trace_id=?').run(data.endedAt,data.status,JSON.stringify(data),row.trace_id); }
|
|
45
|
+
db.prepare('DELETE FROM leases WHERE heartbeat < ? AND NOT EXISTS (SELECT 1 FROM traces WHERE traces.owner=leases.owner AND ended_at IS NULL)').run(Date.now()-120000);
|
|
46
|
+
}
|
|
47
|
+
function remove(ids) {
|
|
48
|
+
for (const row of ids) { db.prepare('DELETE FROM spans WHERE trace_id=?').run(row.trace_id); db.prepare('DELETE FROM traces WHERE trace_id=?').run(row.trace_id); }
|
|
49
|
+
}
|
|
50
|
+
function allocatedBytes() { return (Number(db.prepare('PRAGMA page_count').get().page_count) - Number(db.prepare('PRAGMA freelist_count').get().freelist_count)) * 4096; }
|
|
51
|
+
function counts() {
|
|
52
|
+
const t=db.prepare('SELECT COUNT(*) AS traces FROM traces').get();
|
|
53
|
+
const s=db.prepare('SELECT COUNT(*) AS spans,COALESCE(SUM(bytes),0) AS bytes FROM spans').get();
|
|
54
|
+
return { ...t,...s };
|
|
55
|
+
}
|
|
56
|
+
function prune() {
|
|
57
|
+
transaction(() => {
|
|
58
|
+
remove(db.prepare('SELECT trace_id FROM traces WHERE ended_at IS NOT NULL AND started_at < ? LIMIT 1000').all(Date.now()-config.local.retentionDays*86400000));
|
|
59
|
+
for(let i=0;i<10;i++) {
|
|
60
|
+
const c=counts();
|
|
61
|
+
if(c.traces < config.local.maxTraces && c.spans < config.local.maxSpans && c.bytes < (config.local.maxStoreMiB-4)*1048576*0.3 && allocatedBytes() < (config.local.maxStoreMiB-4)*1048576*0.3) break;
|
|
62
|
+
const old=db.prepare('SELECT trace_id FROM traces WHERE ended_at IS NOT NULL ORDER BY started_at LIMIT ?').all(Math.max(1,Math.min(100,Math.ceil(config.local.maxTraces/10))));
|
|
63
|
+
if(!old.length) break;
|
|
64
|
+
remove(old);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
db.exec('PRAGMA wal_checkpoint(TRUNCATE); PRAGMA incremental_vacuum(16384); PRAGMA wal_checkpoint(TRUNCATE);');
|
|
68
|
+
lastPruned=Date.now();
|
|
69
|
+
permissions();
|
|
70
|
+
}
|
|
71
|
+
function put(records, epoch) {
|
|
72
|
+
const max=config.local.maxStoreMiB*1048576;
|
|
73
|
+
const initial=counts();
|
|
74
|
+
if(size()>max*0.75 || initial.traces>=config.local.maxTraces || initial.spans>=config.local.maxSpans || initial.bytes>(max-4194304)*0.4 || allocatedBytes()>(max-4194304)*0.4 || Date.now()-lastPruned>60000) prune();
|
|
75
|
+
if(size()>max-2097152) { paused=true; dropped+=records.length; return; }
|
|
76
|
+
paused=false;
|
|
77
|
+
let acceptedEpoch;
|
|
78
|
+
transaction(() => {
|
|
79
|
+
const state=db.prepare('SELECT * FROM state WHERE id=1').get();
|
|
80
|
+
acceptedEpoch=state.epoch;
|
|
81
|
+
if(epoch !== null && epoch !== state.epoch) { dropped+=records.length; return; }
|
|
82
|
+
const minute=Math.floor(Date.now()/60000);
|
|
83
|
+
let written=minute===state.window ? state.written : 0;
|
|
84
|
+
let c=counts();
|
|
85
|
+
for(const source of records) {
|
|
86
|
+
const r={ ...source, attributes:{ ...source.attributes } };
|
|
87
|
+
const root=!r.parentSpanId || r.attributes['xopc.localRoot']===true;
|
|
88
|
+
if(written>=config.local.maxWriteMiBPerMinute*1048576+65536) { dropped++; continue; }
|
|
89
|
+
let trace=db.prepare('SELECT * FROM traces WHERE trace_id=?').get(r.traceId);
|
|
90
|
+
if(!trace) {
|
|
91
|
+
if(c.traces>=config.local.maxTraces) { dropped++; continue; }
|
|
92
|
+
const a=r.attributes;
|
|
93
|
+
const overview={ ...r, attributes: Object.fromEntries(Object.entries(a).filter(([k]) => !k.endsWith('.input') && !k.endsWith('.output'))) };
|
|
94
|
+
db.prepare('INSERT INTO traces VALUES(?,?,?,?,?,?,?,?,?,NULL)').run(r.traceId,r.startedAt,root?r.endedAt??null:null,root?r.status:'running',a['xopc.conversationId']??null,a['xopc.runId']??null,a['xopc.agentId']??null,workerData.owner,JSON.stringify(overview));
|
|
95
|
+
c.traces++;
|
|
96
|
+
trace=db.prepare('SELECT * FROM traces WHERE trace_id=?').get(r.traceId);
|
|
97
|
+
}
|
|
98
|
+
let text=JSON.stringify(r);
|
|
99
|
+
let bytes=Buffer.byteLength(text);
|
|
100
|
+
const previous=db.prepare('SELECT bytes FROM spans WHERE trace_id=? AND span_id=?').get(r.traceId,r.spanId);
|
|
101
|
+
const total=db.prepare('SELECT COUNT(*) AS n,COALESCE(SUM(bytes),0) AS bytes FROM spans WHERE trace_id=?').get(r.traceId);
|
|
102
|
+
const limited=c.bytes+bytes>(max-4194304)*0.4 || total.bytes+bytes-(previous?.bytes??0)>config.local.maxTraceKiB*1024 || written+bytes*(root?2:1)>config.local.maxWriteMiBPerMinute*1048576;
|
|
103
|
+
if(limited) {
|
|
104
|
+
delete r.attributes['langfuse.observation.input']; delete r.attributes['langfuse.observation.output'];
|
|
105
|
+
r.attributes['xopc.partialReason']='quota'; text=JSON.stringify(r); bytes=Buffer.byteLength(text);
|
|
106
|
+
db.prepare('UPDATE traces SET partial_reason=? WHERE trace_id=?').run('quota',r.traceId);
|
|
107
|
+
}
|
|
108
|
+
if(!previous && (c.spans>=config.local.maxSpans || total.n>=1000 || total.bytes+bytes>config.local.maxTraceKiB*1024) || written+bytes*(root?2:1)>config.local.maxWriteMiBPerMinute*1048576+65536) {
|
|
109
|
+
dropped++; db.prepare('UPDATE traces SET partial_reason=? WHERE trace_id=?').run('quota',r.traceId);
|
|
110
|
+
continue;
|
|
111
|
+
} else {
|
|
112
|
+
db.prepare('INSERT INTO spans VALUES(?,?,?,?) ON CONFLICT(trace_id,span_id) DO UPDATE SET data=excluded.data,bytes=excluded.bytes').run(r.traceId,r.spanId,text,bytes);
|
|
113
|
+
if(!previous) c.spans++;
|
|
114
|
+
written+=bytes*(root?2:1); c.bytes+=bytes-(previous?.bytes??0);
|
|
115
|
+
}
|
|
116
|
+
if(r.attributes['xopc.partialReason']) db.prepare('UPDATE traces SET partial_reason=? WHERE trace_id=?').run(String(r.attributes['xopc.partialReason']),r.traceId);
|
|
117
|
+
if(root) db.prepare('UPDATE traces SET started_at=?,ended_at=?,status=?,data=?,conversation_id=?,run_id=?,agent_id=? WHERE trace_id=?').run(r.startedAt,r.endedAt??null,r.status,text,r.attributes['xopc.conversationId']??null,r.attributes['xopc.runId']??null,r.attributes['xopc.agentId']??null,r.traceId);
|
|
118
|
+
}
|
|
119
|
+
db.prepare('UPDATE state SET window=?,written=? WHERE id=1').run(minute,written);
|
|
120
|
+
});
|
|
121
|
+
permissions();
|
|
122
|
+
return acceptedEpoch;
|
|
123
|
+
}
|
|
124
|
+
function list(q) {
|
|
125
|
+
const where=[]; const values=[];
|
|
126
|
+
for(const [key,column] of [['status','status'],['conversationId','conversation_id'],['runId','run_id'],['agentId','agent_id']]) if(q[key]) { where.push(column+'=?'); values.push(q[key]); }
|
|
127
|
+
if(q.from!==undefined) { where.push('started_at>=?'); values.push(q.from); }
|
|
128
|
+
if(q.to!==undefined) { where.push('started_at<?'); values.push(q.to); }
|
|
129
|
+
if(q.cursor) { const [time,id]=q.cursor.split(':'); where.push('(started_at < ? OR (started_at=? AND trace_id<?))'); values.push(Number(time),Number(time),id); }
|
|
130
|
+
const limit=Math.min(100,Math.max(1,q.limit||30));
|
|
131
|
+
const rows=db.prepare('SELECT * FROM traces'+(where.length?' WHERE '+where.join(' AND '):'')+' ORDER BY started_at DESC,trace_id DESC LIMIT ?').all(...values,limit+1);
|
|
132
|
+
const more=rows.length>limit; const page=rows.slice(0,limit);
|
|
133
|
+
return { traces:page.map(row => { const r=JSON.parse(row.data); delete r.attributes['langfuse.observation.input']; delete r.attributes['langfuse.observation.output']; const stats={generations:0,tokens:0,knownCostUsd:0,unknownCostCalls:0}; for(const value of db.prepare('SELECT data FROM spans WHERE trace_id=?').all(row.trace_id)) { const span=JSON.parse(value.data); if(span.type!=='generation') continue; stats.generations++; stats.tokens+=Number(span.attributes['gen_ai.usage.input_tokens']||0)+Number(span.attributes['gen_ai.usage.output_tokens']||0); const cost=span.attributes['langfuse.observation.cost_details']; const total=cost ? JSON.parse(cost).total : undefined; if(typeof total==='number') stats.knownCostUsd+=total; else stats.unknownCostCalls++; } return {...r,partialReason:row.partial_reason,stats}; }), nextCursor:more? page.at(-1).started_at+':'+page.at(-1).trace_id:null };
|
|
134
|
+
}
|
|
135
|
+
retryStartup(heartbeat); permissions();
|
|
136
|
+
const timer=setInterval(() => { try { heartbeat(); prune(); } catch {} },60000); timer.unref();
|
|
137
|
+
parentPort.on('message', msg => {
|
|
138
|
+
try {
|
|
139
|
+
let value;
|
|
140
|
+
if(msg.op==='put') value=put(msg.data.records,msg.data.epoch);
|
|
141
|
+
if(msg.op==='configure') { config=msg.data; limits(); prune(); }
|
|
142
|
+
if(msg.op==='status') value={...counts(),physicalBytes:size(),dropped,paused,lastPruned,epoch:db.prepare('SELECT epoch FROM state WHERE id=1').get().epoch};
|
|
143
|
+
if(msg.op==='list') value=list(msg.data);
|
|
144
|
+
if(msg.op==='detail') {
|
|
145
|
+
const row=db.prepare('SELECT * FROM traces WHERE trace_id=?').get(msg.data);
|
|
146
|
+
value=row? {trace:{...JSON.parse(row.data),partialReason:row.partial_reason},spans:db.prepare('SELECT data FROM spans WHERE trace_id=? ORDER BY json_extract(data,\'$.startedAt\'),span_id LIMIT 1000').all(msg.data).map(x=>JSON.parse(x.data))}:null;
|
|
147
|
+
}
|
|
148
|
+
if(msg.op==='prune') { prune(); value=true; }
|
|
149
|
+
if(msg.op==='clear') { value=transaction(() => { db.exec('DELETE FROM spans; DELETE FROM traces; UPDATE state SET epoch=epoch+1 WHERE id=1'); return db.prepare('SELECT epoch FROM state WHERE id=1').get().epoch; }); prune(); }
|
|
150
|
+
parentPort.postMessage({id:msg.id,value});
|
|
151
|
+
} catch(error) { parentPort.postMessage({id:msg.id,error:'Trace storage operation failed: '+String(error.message).slice(0,200)}); }
|
|
152
|
+
});
|
|
153
|
+
`;
|
|
154
|
+
//#endregion
|
|
155
|
+
export { traceStoreWorkerSource };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type TraceRecord = {
|
|
2
|
+
traceId: string;
|
|
3
|
+
spanId: string;
|
|
4
|
+
parentSpanId?: string;
|
|
5
|
+
name: string;
|
|
6
|
+
type: 'agent' | 'generation' | 'tool' | 'span';
|
|
7
|
+
startedAt: number;
|
|
8
|
+
endedAt?: number;
|
|
9
|
+
status: string;
|
|
10
|
+
attributes: Record<string, string | number | boolean>;
|
|
11
|
+
};
|
|
12
|
+
export type TraceQuery = {
|
|
13
|
+
limit?: number;
|
|
14
|
+
cursor?: string;
|
|
15
|
+
status?: string;
|
|
16
|
+
conversationId?: string;
|
|
17
|
+
runId?: string;
|
|
18
|
+
agentId?: string;
|
|
19
|
+
from?: number;
|
|
20
|
+
to?: number;
|
|
21
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { AgentManifestSchema, PlatformCapabilitySchema, PlatformConfigSchema, PlatformDiscoverySchema, PlatformEventSchema, init_contracts } from "./contracts.js";
|
|
2
1
|
import { init_resolution, resolveConnectedPlatformDiscovery, resolveConnectedPlatformEndpoint, setActivePlatformDiscovery } from "./resolution.js";
|
|
2
|
+
import { AgentManifestSchema, PlatformCapabilitySchema, PlatformConfigSchema, PlatformDiscoverySchema, PlatformEventSchema, init_contracts } from "./contracts.js";
|
|
3
3
|
import { PLATFORM_RUNTIME_PROFILE, PLATFORM_RUNTIME_PROVIDER, PlatformRuntimeClient, RuntimeCommandSchema, discoverPlatform } from "./client.js";
|
|
4
4
|
import { processRuntimeCommand } from "./worker.js";
|
|
5
5
|
//#region src/platform/index.ts
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { init_sync_provider_auth, resolveProviderApiKeyForAgentSync } from "../../auth/sync-provider-auth.js";
|
|
2
1
|
import { getApiKeyFromEnv, init_env_keys } from "../env-keys.js";
|
|
2
|
+
import { init_sync_provider_auth, resolveProviderApiKeyForAgentSync } from "../../auth/sync-provider-auth.js";
|
|
3
3
|
import { getDefaultAuthProfileStore } from "./auth-profile-store.js";
|
|
4
4
|
//#region src/providers/auth-runtime/resolve-auth.ts
|
|
5
5
|
init_env_keys();
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { __esmMin, __exportAll } from "../../_virtual/_rolldown/runtime.js";
|
|
2
|
-
import {
|
|
2
|
+
import { PROVIDER_ENV_MAP, getApiKeyFromEnv, init_env_keys } from "./env-keys.js";
|
|
3
3
|
import { getModelOAuthProviderIds, init_registry } from "../auth/oauth/registry.js";
|
|
4
|
+
import { CredentialResolver, hasCredentials, init_credentials } from "../auth/credentials.js";
|
|
5
|
+
import { getAgentDefaultModelRef, init_schema } from "../config/schema.js";
|
|
4
6
|
import { hasProviderAuthOnDiskSync, init_sync_provider_auth } from "../auth/sync-provider-auth.js";
|
|
5
|
-
import { PROVIDER_ENV_MAP, getApiKeyFromEnv, init_env_keys } from "./env-keys.js";
|
|
6
7
|
import { init_model_ref, splitProviderModelRef } from "./model-ref.js";
|
|
7
8
|
import { ModelRegistry, getModelRegistry, init_model_registry, prewarmModelRegistry, resetModelRegistry } from "./model-registry.js";
|
|
8
|
-
import { CredentialResolver, hasCredentials, init_credentials } from "../auth/credentials.js";
|
|
9
9
|
import { EXTENSION_PROVIDER_BASE_URL, init_constants } from "./constants.js";
|
|
10
10
|
import { applyOfficialModelMetadataCorrections, getSupplementalModels, init_model_supplements } from "./model-supplements.js";
|
|
11
11
|
import { getProviderRegistry, init_plugin_registry } from "./plugin-registry.js";
|
|
@@ -43,14 +43,21 @@ async function resolveModelCallOptions(model, options = {}, credentialOptions) {
|
|
|
43
43
|
*/
|
|
44
44
|
async function completeWithResolvedCredentials(model, context, options, credentialOptions, usageContext) {
|
|
45
45
|
const resolvedOptions = await resolveModelCallOptions(model, options, credentialOptions);
|
|
46
|
-
const usageCall = startAiUsageCall(model, usageContext
|
|
46
|
+
const usageCall = startAiUsageCall(model, usageContext, {
|
|
47
|
+
context,
|
|
48
|
+
options: {
|
|
49
|
+
reasoning: options.reasoning,
|
|
50
|
+
temperature: options.temperature,
|
|
51
|
+
maxTokens: options.maxTokens
|
|
52
|
+
}
|
|
53
|
+
});
|
|
47
54
|
try {
|
|
48
55
|
if (model.baseUrl === "extension://provider-plugin") {
|
|
49
|
-
const result = await (await createExtensionAwareStreamFn()(model, normalizeContext(context), resolvedOptions)).result();
|
|
56
|
+
const result = await (await usageCall.run(() => createExtensionAwareStreamFn()(model, normalizeContext(context), resolvedOptions))).result();
|
|
50
57
|
finishAiUsageFromMessage(usageCall, result);
|
|
51
58
|
return result;
|
|
52
59
|
}
|
|
53
|
-
const result = await completeSimple(model, context, resolvedOptions);
|
|
60
|
+
const result = await usageCall.run(() => completeSimple(model, context, resolvedOptions));
|
|
54
61
|
finishAiUsageFromMessage(usageCall, result);
|
|
55
62
|
return result;
|
|
56
63
|
} catch (error) {
|
|
@@ -68,9 +75,16 @@ async function completeWithResolvedCredentials(model, context, options, credenti
|
|
|
68
75
|
*/
|
|
69
76
|
async function createResolvedModelStream(model, context, options, credentialOptions, usageContext) {
|
|
70
77
|
const resolvedOptions = await resolveModelCallOptions(model, options, credentialOptions);
|
|
71
|
-
const usageCall = startAiUsageCall(model, usageContext
|
|
78
|
+
const usageCall = startAiUsageCall(model, usageContext, {
|
|
79
|
+
context,
|
|
80
|
+
options: {
|
|
81
|
+
reasoning: options.reasoning,
|
|
82
|
+
temperature: options.temperature,
|
|
83
|
+
maxTokens: options.maxTokens
|
|
84
|
+
}
|
|
85
|
+
});
|
|
72
86
|
try {
|
|
73
|
-
return observeAiUsageStream(await createExtensionAwareStreamFn()(model, normalizeContext(context), resolvedOptions), usageCall);
|
|
87
|
+
return observeAiUsageStream(await usageCall.run(() => createExtensionAwareStreamFn()(model, normalizeContext(context), resolvedOptions)), usageCall);
|
|
74
88
|
} catch (error) {
|
|
75
89
|
usageCall?.finish({
|
|
76
90
|
status: "failed",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { __esmMin } from "../../_virtual/_rolldown/runtime.js";
|
|
2
2
|
import { init_paths_state, resolveXopcCloudCatalogCachePath } from "../config/paths-state.js";
|
|
3
|
-
import { ComputerProfileSchema, init_src } from "../../packages/computer-control-contract/src/index.js";
|
|
4
3
|
import { init_write_file_atomic, writeTextAtomic } from "../infra/write-file-atomic.js";
|
|
4
|
+
import { ComputerProfileSchema, init_src } from "../../packages/computer-control-contract/src/index.js";
|
|
5
5
|
import { voiceManifestSchema } from "../../packages/realtime-protocol/src/voice-catalog.js";
|
|
6
6
|
import { init_voice } from "../../packages/realtime-protocol/src/voice.js";
|
|
7
7
|
import { readFileSync, rmSync } from "node:fs";
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { __esmMin } from "../../_virtual/_rolldown/runtime.js";
|
|
2
2
|
import { createLogger } from "../utils/logger/index.js";
|
|
3
3
|
import { init_logger } from "../utils/logger.js";
|
|
4
|
+
import { getApiKeyFromEnv, init_env_keys } from "./env-keys.js";
|
|
4
5
|
import { resolveModelsJsonPath } from "../config/paths.js";
|
|
5
6
|
import { init_resolve_config_value, resolveConfigValue, resolveHeaders } from "../config/resolve-config-value.js";
|
|
6
7
|
import { getDefaultModelValues, init_models_json, validateModelsConfig } from "../config/models-json.js";
|
|
7
|
-
import { getApiKeyFromEnv, init_env_keys } from "./env-keys.js";
|
|
8
8
|
import { getModelCatalogStore, init_model_catalog_store } from "./model-catalog-store.js";
|
|
9
9
|
import { init_model_ref, splitProviderModelRef } from "./model-ref.js";
|
|
10
10
|
import { existsSync, readFileSync } from "fs";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { createLogger } from "../utils/logger/index.js";
|
|
2
2
|
import { init_logger } from "../utils/logger.js";
|
|
3
|
-
import { getModelRegistry, init_model_registry } from "./model-registry.js";
|
|
4
3
|
import { init_oauth_provider_lock, withOAuthProviderLock } from "../auth/oauth-provider-lock.js";
|
|
5
4
|
import { CredentialResolver, init_credentials } from "../auth/credentials.js";
|
|
5
|
+
import { getModelRegistry, init_model_registry } from "./model-registry.js";
|
|
6
6
|
import { getProviderAuthService, init_provider_auth_service } from "./provider-auth-service.js";
|
|
7
7
|
import { evictAllEmbeddedSessionRunners } from "../agent/embedded/session-runner.js";
|
|
8
8
|
//#region src/providers/provider-disconnect-core.ts
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { createLogger } from "../utils/logger/index.js";
|
|
2
2
|
import { init_logger } from "../utils/logger.js";
|
|
3
|
+
import { init_oauth_provider_lock, withOAuthProviderLock } from "../auth/oauth-provider-lock.js";
|
|
3
4
|
import { ModelCatalogPersistence, init_model_catalog_persistence } from "./model-catalog-persistence.js";
|
|
4
5
|
import { getModelCatalogStore, init_model_catalog_store } from "./model-catalog-store.js";
|
|
5
6
|
import { getModelRegistry, init_model_registry } from "./model-registry.js";
|
|
6
|
-
import { init_oauth_provider_lock, withOAuthProviderLock } from "../auth/oauth-provider-lock.js";
|
|
7
7
|
import { reloadImageGenerationProviders } from "../agent/image/generation/provider-registry.js";
|
|
8
8
|
import { disconnectProviderCore } from "./provider-disconnect-core.js";
|
|
9
9
|
import { XopcCloudModelSource } from "./xopc-cloud-model-source.js";
|
|
@@ -55,6 +55,8 @@ export declare function listAiUsageEvents(query: AiUsageQuery & {
|
|
|
55
55
|
items: {
|
|
56
56
|
id: string;
|
|
57
57
|
traceId: string;
|
|
58
|
+
otelTraceId: string;
|
|
59
|
+
otelSpanId: string;
|
|
58
60
|
parentEventId: string;
|
|
59
61
|
conversationId: string;
|
|
60
62
|
runId: string;
|
|
@@ -85,6 +87,8 @@ export declare function listAiUsageEvents(query: AiUsageQuery & {
|
|
|
85
87
|
export declare function getAiUsageEventView(id: string): {
|
|
86
88
|
id: string;
|
|
87
89
|
traceId: string;
|
|
90
|
+
otelTraceId: string;
|
|
91
|
+
otelSpanId: string;
|
|
88
92
|
parentEventId: string;
|
|
89
93
|
conversationId: string;
|
|
90
94
|
runId: string;
|
|
@@ -113,6 +117,8 @@ export declare function getAiUsageEventView(id: string): {
|
|
|
113
117
|
export declare function listAiUsageTrace(traceId: string): {
|
|
114
118
|
id: string;
|
|
115
119
|
traceId: string;
|
|
120
|
+
otelTraceId: string;
|
|
121
|
+
otelSpanId: string;
|
|
116
122
|
parentEventId: string;
|
|
117
123
|
conversationId: string;
|
|
118
124
|
runId: string;
|
|
@@ -15,8 +15,8 @@ function insertAiUsageEvent(event) {
|
|
|
15
15
|
started_at, finished_at, duration_ms, input_tokens, output_tokens,
|
|
16
16
|
cache_read_tokens, cache_write_tokens, reasoning_tokens, total_tokens,
|
|
17
17
|
estimated_cost_microusd, cost_source, pricing_snapshot_json, error_summary,
|
|
18
|
-
created_at, updated_at
|
|
19
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(event.id, event.traceId, event.parentEventId ?? null, event.conversationId ?? null, event.runId ?? null, event.agentId ?? null, event.category, event.operation, event.trigger, event.reasonKey, event.provider, event.model, event.status, event.startedAt, event.finishedAt ?? null, event.durationMs ?? null, event.inputTokens ?? null, event.outputTokens ?? null, event.cacheReadTokens ?? null, event.cacheWriteTokens ?? null, event.reasoningTokens ?? null, event.totalTokens ?? null, event.estimatedCostMicrousd ?? null, event.costSource, event.pricingSnapshot ? JSON.stringify(event.pricingSnapshot) : null, boundedError(event.errorSummary) ?? null, event.startedAt, event.startedAt);
|
|
18
|
+
created_at, updated_at, otel_trace_id, otel_span_id
|
|
19
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(event.id, event.traceId, event.parentEventId ?? null, event.conversationId ?? null, event.runId ?? null, event.agentId ?? null, event.category, event.operation, event.trigger, event.reasonKey, event.provider, event.model, event.status, event.startedAt, event.finishedAt ?? null, event.durationMs ?? null, event.inputTokens ?? null, event.outputTokens ?? null, event.cacheReadTokens ?? null, event.cacheWriteTokens ?? null, event.reasoningTokens ?? null, event.totalTokens ?? null, event.estimatedCostMicrousd ?? null, event.costSource, event.pricingSnapshot ? JSON.stringify(event.pricingSnapshot) : null, boundedError(event.errorSummary) ?? null, event.startedAt, event.startedAt, event.otelTraceId ?? null, event.otelSpanId ?? null);
|
|
20
20
|
});
|
|
21
21
|
}
|
|
22
22
|
function finishAiUsageEvent(id, finish) {
|
|
@@ -143,6 +143,8 @@ function eventFromRow(row) {
|
|
|
143
143
|
return {
|
|
144
144
|
id: String(row.id),
|
|
145
145
|
traceId: String(row.trace_id),
|
|
146
|
+
otelTraceId: row.otel_trace_id ? String(row.otel_trace_id) : void 0,
|
|
147
|
+
otelSpanId: row.otel_span_id ? String(row.otel_span_id) : void 0,
|
|
146
148
|
parentEventId: row.parent_event_id ? String(row.parent_event_id) : void 0,
|
|
147
149
|
conversationId: row.conversation_id ? String(row.conversation_id) : void 0,
|
|
148
150
|
runId: row.run_id ? String(row.run_id) : void 0,
|
|
@@ -3,7 +3,7 @@ import type { ApplyMigrationsOptions } from './types.js';
|
|
|
3
3
|
/** Baseline schema version applied from schema.sql on first open. */
|
|
4
4
|
export declare const XOPC_DB_BASELINE_SCHEMA_VERSION = 165;
|
|
5
5
|
/** Latest schema version this release supports (increment when adding migrations). */
|
|
6
|
-
export declare const XOPC_DB_SCHEMA_VERSION =
|
|
6
|
+
export declare const XOPC_DB_SCHEMA_VERSION = 233;
|
|
7
7
|
export declare function resolveMigrationsDir(override?: string): string;
|
|
8
8
|
/**
|
|
9
9
|
* Apply sequential SQL migrations from (currentVersion + 1) through targetVersion.
|
|
@@ -57,7 +57,7 @@ function applySingleMigration(db, migration, discardOldScenes = false) {
|
|
|
57
57
|
*/
|
|
58
58
|
function applyPendingMigrations(db, options = {}) {
|
|
59
59
|
db.function("xopc_operation_id", () => currentOperationId() ?? null);
|
|
60
|
-
const targetVersion = options.targetVersion ??
|
|
60
|
+
const targetVersion = options.targetVersion ?? 233;
|
|
61
61
|
let currentVersion = readSchemaVersion(db);
|
|
62
62
|
if (currentVersion > targetVersion) throw new DatabaseSchemaTooNewError(currentVersion, targetVersion);
|
|
63
63
|
if (!options.migrationsDir && currentVersion < 165) throw new DatabaseSchemaTooOldError(currentVersion, 165);
|
|
@@ -95,7 +95,7 @@ function applyPendingMigrations(db, options = {}) {
|
|
|
95
95
|
}
|
|
96
96
|
/** Inspect schema version without mutating the database. */
|
|
97
97
|
function inspectSchemaMigrationStatus(db, options = {}) {
|
|
98
|
-
const appVersion = options.targetVersion ??
|
|
98
|
+
const appVersion = options.targetVersion ?? 233;
|
|
99
99
|
const dbVersion = readSchemaVersion(db);
|
|
100
100
|
if (dbVersion > appVersion) return {
|
|
101
101
|
dbVersion,
|
|
@@ -137,7 +137,7 @@ var init_runner = __esmMin((() => {
|
|
|
137
137
|
init_errors();
|
|
138
138
|
log = createLogger("Sqlite:Migrations");
|
|
139
139
|
XOPC_DB_BASELINE_SCHEMA_VERSION = 165;
|
|
140
|
-
XOPC_DB_SCHEMA_VERSION =
|
|
140
|
+
XOPC_DB_SCHEMA_VERSION = 233;
|
|
141
141
|
}));
|
|
142
142
|
//#endregion
|
|
143
143
|
init_runner();
|