@xopcai/xopc 0.0.117 → 0.0.118
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/README.md +24 -23
- package/README.zh-CN.md +45 -42
- package/dist/browser-ext/manifest.json +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-BBLhJ2C6.js → agent-avatar-dicebear-dzUrQoeZ.js} +1 -1
- package/dist/gateway/static/root/assets/agents-BCxZbkOw.js +2 -0
- package/dist/gateway/static/root/assets/{app-management-settings-panel-CBpLONvG.js → app-management-settings-panel-bxYJVJpx.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-CGcc54yj.js → appearance-settings-BCIykXlI.js} +1 -1
- package/dist/gateway/static/root/assets/{apps-page-DdDx0V4z.js → apps-page--wMEL00f.js} +1 -1
- package/dist/gateway/static/root/assets/{archive-plugin-DZsVqMhU.js → archive-plugin-Cki4SfU2.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-CIol6JWX.js → attachment-load-D-KnS2Do.js} +1 -1
- package/dist/gateway/static/root/assets/{binary-plugins-B3UauMfp.js → binary-plugins-D2sgvJja.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-2HJoFchH.js → block-editor-DIq-4Gvb.js} +27 -27
- package/dist/gateway/static/root/assets/browser-settings-page-DRhKdn81.js +8 -0
- package/dist/gateway/static/root/assets/{capability-presets-api-XQoMTI0A.js → capability-presets-api-CorhiHlg.js} +1 -1
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-qFx9Jtce.js +1 -0
- package/dist/gateway/static/root/assets/channels-settings-BAwAkqIA.js +1 -0
- package/dist/gateway/static/root/assets/connectors-page-BQLcfitM.js +2 -0
- package/dist/gateway/static/root/assets/{cron-api-R41aQCX9.js → cron-api-qGwmL-24.js} +1 -1
- package/dist/gateway/static/root/assets/cron-page-BBWMAj8-.js +3 -0
- package/dist/gateway/static/root/assets/{dreaming-settings-Bceaw1PG.js → dreaming-settings-Dw89Obml.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-DX5WRE_t.js → extension-debug-page-DYu1KaFQ.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-Buah7Bw-.js → extension-page-B3W6QwlZ.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-DuroLVnf.js → extension-settings-page-D7VQ-cxE.js} +1 -1
- package/dist/gateway/static/root/assets/{fetch-B5zdl27C.js → fetch-B7xDPu-R.js} +1 -1
- package/dist/gateway/static/root/assets/{format-interval-label-Dn_QKIqb.js → format-interval-label-BzxafKxR.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-api-gT4ZhE7h.js → gateway-config-api-BTBLSeUu.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-settings-iISWAZRm.js → gateway-settings-Cmwy58rS.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-startup-retry-B3kL8KGY.js → gateway-startup-retry-CYj9FWCp.js} +1 -1
- package/dist/gateway/static/root/assets/goal-detail-page-l7mu5GWX.js +1 -0
- package/dist/gateway/static/root/assets/{goals-config-api-AR-fVh0P.js → goals-config-api-CQQgzPr3.js} +1 -1
- package/dist/gateway/static/root/assets/{goals-page-Cpb21ezg.js → goals-page-BHitvJAv.js} +2 -2
- package/dist/gateway/static/root/assets/{goals-settings-CPAuFwoD.js → goals-settings-CYVY4NGp.js} +1 -1
- package/dist/gateway/static/root/assets/{heartbeat-config-api-D7bKFEeO.js → heartbeat-config-api-DriO_CN9.js} +1 -1
- package/dist/gateway/static/root/assets/heartbeat-settings-CUQTKxg7.js +1 -0
- package/dist/gateway/static/root/assets/index-C17Tsd_b.css +1 -0
- package/dist/gateway/static/root/assets/{index-CwhK8qCh.js → index-Cc41r16l.js} +120 -51
- package/dist/gateway/static/root/assets/{interval-presets-0DtsnxE0.js → interval-presets-DFPCKW_W.js} +1 -1
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-BPREtN8B.js → keyboard-shortcuts-settings-CcVcqstx.js} +1 -1
- package/dist/gateway/static/root/assets/{logs-page-DefCgF4o.js → logs-page-C2Uqvae9.js} +2 -2
- package/dist/gateway/static/root/assets/{markdown-split-ChbR9LGO.js → markdown-split-BwOFrJ2E.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-B82oP80n.js → media-plugins-Cnz6Q5G6.js} +1 -1
- package/dist/gateway/static/root/assets/messages-B1iSKjiw.js +3 -0
- package/dist/gateway/static/root/assets/models-hub-panel-BfLfcta8.js +2 -0
- package/dist/gateway/static/root/assets/note-detail-page-BjwOQBfT.js +2 -0
- package/dist/gateway/static/root/assets/{note-image-lightbox-CDknITM3.js → note-image-lightbox-B-Svpmza.js} +1 -1
- package/dist/gateway/static/root/assets/notes-page-DktdBiwB.js +1 -0
- package/dist/gateway/static/root/assets/{preview-open-alternatives-DV3xQsWB.js → preview-open-alternatives-Bss4TiwP.js} +1 -1
- package/dist/gateway/static/root/assets/remote-access-hub-rfVx96fq.js +1 -0
- package/dist/gateway/static/root/assets/{save-bar-controls-BODCXnvY.js → save-bar-controls-BRNjdgKq.js} +1 -1
- package/dist/gateway/static/root/assets/{schedule-field-CgJSCD3B.js → schedule-field-CUCKODde.js} +1 -1
- package/dist/gateway/static/root/assets/{schema-form-qtfEI6QM.js → schema-form-Dub9sy8r.js} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-DbtMFuuH.js +1 -0
- package/dist/gateway/static/root/assets/settings-advanced-gate-5re5gAnP.js +1 -0
- package/dist/gateway/static/root/assets/{settings-collapsible-section-BHZKjVcS.js → settings-collapsible-section-BeFLavGi.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-form-section-5i35D-J1.js → settings-form-section-dmyaPOTG.js} +1 -1
- package/dist/gateway/static/root/assets/settings-page-D-WMyLlj.js +1 -0
- package/dist/gateway/static/root/assets/setup-status-panel-Cl-DEJAU.js +1 -0
- package/dist/gateway/static/root/assets/{share-preview-page-B0H63tDv.js → share-preview-page-C8glrdqe.js} +1 -1
- package/dist/gateway/static/root/assets/shares-settings-CyPVvbLt.js +1 -0
- package/dist/gateway/static/root/assets/skill-reload-api-CleTH1oz.js +1 -0
- package/dist/gateway/static/root/assets/skills-page-Ck8hdXqR.js +2 -0
- package/dist/gateway/static/root/assets/{system-settings-panel-DXs2VmBt.js → system-settings-panel-DI-LN_8G.js} +1 -1
- package/dist/gateway/static/root/assets/{typed-models-lib-4gPuydz_.js → typed-models-lib-kX9duch4.js} +1 -1
- package/dist/gateway/static/root/assets/{use-channel-catalog-BCjP_VHb.js → use-channel-catalog-DqlWTsDb.js} +1 -1
- package/dist/gateway/static/root/assets/user-profile-settings-panel-B-UFPMXy.js +1 -0
- package/dist/gateway/static/root/assets/utils-Dm0hKalR.js +1 -0
- package/dist/gateway/static/root/assets/voice-api-key-field-C73IVZeC.js +1 -0
- package/dist/gateway/static/root/assets/{voice-player-store-BlF9V05n.js → voice-player-store-C_ArLJPM.js} +1 -1
- package/dist/gateway/static/root/assets/workflows-page-BtFGp2y_.js +18 -0
- package/dist/gateway/static/root/index.html +5 -5
- package/dist/package.js +1 -1
- package/dist/src/agent/agent-manager.js +2 -1
- package/dist/src/agent/agent-manager.js.map +1 -1
- package/dist/src/agent/agent-scope.js +0 -2
- package/dist/src/agent/agent-scope.js.map +1 -1
- package/dist/src/agent/background-review/prompts.d.ts +2 -2
- package/dist/src/agent/background-review/prompts.js +2 -2
- package/dist/src/agent/background-review/prompts.js.map +1 -1
- package/dist/src/agent/background-review/settings.d.ts +1 -1
- package/dist/src/agent/background-review/settings.js +7 -8
- package/dist/src/agent/background-review/settings.js.map +1 -1
- package/dist/src/agent/bootstrap/bootstrap-cache.d.ts +1 -0
- package/dist/src/agent/bootstrap/bootstrap-cache.js +4 -3
- package/dist/src/agent/bootstrap/bootstrap-cache.js.map +1 -1
- package/dist/src/agent/bootstrap/bootstrap-files.d.ts +4 -0
- package/dist/src/agent/bootstrap/bootstrap-files.js +4 -3
- package/dist/src/agent/bootstrap/bootstrap-files.js.map +1 -1
- package/dist/src/agent/bootstrap/filter-bootstrap-files.js +4 -2
- package/dist/src/agent/bootstrap/filter-bootstrap-files.js.map +1 -1
- package/dist/src/agent/bootstrap/load-bootstrap-files.d.ts +2 -1
- package/dist/src/agent/bootstrap/load-bootstrap-files.js +17 -16
- package/dist/src/agent/bootstrap/load-bootstrap-files.js.map +1 -1
- package/dist/src/agent/bootstrap/types.d.ts +1 -1
- package/dist/src/agent/context/workspace-templates/AGENTS.md +1 -1
- package/dist/src/agent/context/workspace.d.ts +4 -2
- package/dist/src/agent/context/workspace.js +5 -4
- package/dist/src/agent/context/workspace.js.map +1 -1
- package/dist/src/agent/embedded/run-for-session.js +4 -1
- package/dist/src/agent/embedded/run-for-session.js.map +1 -1
- package/dist/src/agent/fallback/candidates.js +10 -11
- package/dist/src/agent/fallback/candidates.js.map +1 -1
- package/dist/src/agent/memory/builtin-memory-store.d.ts +4 -2
- package/dist/src/agent/memory/builtin-memory-store.js +11 -6
- package/dist/src/agent/memory/builtin-memory-store.js.map +1 -1
- package/dist/src/agent/memory/builtin-provider.js +36 -7
- package/dist/src/agent/memory/builtin-provider.js.map +1 -1
- package/dist/src/agent/memory/candidate-extractor.d.ts +6 -0
- package/dist/src/agent/memory/candidate-extractor.js +60 -0
- package/dist/src/agent/memory/candidate-extractor.js.map +1 -0
- package/dist/src/agent/memory/manager.d.ts +2 -1
- package/dist/src/agent/memory/manager.js +27 -3
- package/dist/src/agent/memory/manager.js.map +1 -1
- package/dist/src/agent/memory/memory-config.js +3 -0
- package/dist/src/agent/memory/memory-config.js.map +1 -1
- package/dist/src/agent/memory/prefetch-coordinator.js.map +1 -1
- package/dist/src/agent/memory/types.d.ts +27 -4
- package/dist/src/agent/prompt/memory/index.d.ts +4 -2
- package/dist/src/agent/prompt/memory/index.js +18 -12
- package/dist/src/agent/prompt/memory/index.js.map +1 -1
- package/dist/src/agent/prompt/sections/memory-skills.js +1 -1
- package/dist/src/agent/prompt/sections/memory-skills.js.map +1 -1
- package/dist/src/agent/prompt/service-prompt-builder.js +4 -19
- package/dist/src/agent/prompt/service-prompt-builder.js.map +1 -1
- package/dist/src/agent/reply/post-compaction-context.js +1 -3
- package/dist/src/agent/reply/post-compaction-context.js.map +1 -1
- package/dist/src/agent/reply/startup-context.js +1 -5
- package/dist/src/agent/reply/startup-context.js.map +1 -1
- package/dist/src/agent/service.d.ts +3 -3
- package/dist/src/agent/service.js +7 -8
- package/dist/src/agent/service.js.map +1 -1
- package/dist/src/agent/tools/curated-memory-tool.js +57 -2
- package/dist/src/agent/tools/curated-memory-tool.js.map +1 -1
- package/dist/src/agent/tools/web-extract.js +6 -8
- package/dist/src/agent/tools/web-extract.js.map +1 -1
- package/dist/src/agent-manifest/index.js +2 -2
- package/dist/src/agent-manifest/resolver.d.ts +3 -2
- package/dist/src/agent-manifest/resolver.js +5 -3
- package/dist/src/agent-manifest/resolver.js.map +1 -1
- package/dist/src/agent-manifest/schema.d.ts +232 -0
- package/dist/src/agent-manifest/schema.js +4 -2
- package/dist/src/agent-manifest/schema.js.map +1 -1
- package/dist/src/agent-manifest/validator.d.ts +3 -2
- package/dist/src/agent-manifest/validator.js +2 -1
- package/dist/src/agent-manifest/validator.js.map +1 -1
- package/dist/src/channels/pipeline.d.ts +1 -1
- package/dist/src/channels/pipeline.js.map +1 -1
- package/dist/src/chat-commands/agent-edit.js +0 -1
- package/dist/src/chat-commands/agent-edit.js.map +1 -1
- package/dist/src/chat-commands/builtins/model.js +2 -2
- package/dist/src/chat-commands/builtins/model.js.map +1 -1
- package/dist/src/cli/command-manifest.d.ts +1 -1
- package/dist/src/cli/command-manifest.js +1 -1
- package/dist/src/cli/command-manifest.js.map +1 -1
- package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
- package/dist/src/cli/commands/doctor/checks/provider-auth.js +1 -0
- package/dist/src/cli/commands/doctor/checks/provider-auth.js.map +1 -1
- package/dist/src/cli/commands/doctor/checks/workspace-status.js +0 -1
- package/dist/src/cli/commands/doctor/checks/workspace-status.js.map +1 -1
- package/dist/src/cli/commands/init.js +7 -6
- package/dist/src/cli/commands/init.js.map +1 -1
- package/dist/src/cli/commands/models.js +13 -31
- package/dist/src/cli/commands/models.js.map +1 -1
- package/dist/src/cli/commands/onboard/model.js +17 -10
- package/dist/src/cli/commands/onboard/model.js.map +1 -1
- package/dist/src/cli/commands/onboard.js +2 -2
- package/dist/src/cli/commands/onboard.js.map +1 -1
- package/dist/src/cli/commands/tui.js +2 -2
- package/dist/src/cli/commands/tui.js.map +1 -1
- package/dist/src/cli/index.d.ts +1 -0
- package/dist/src/cli/index.js +10 -5
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/cli/registry.js +1 -1
- package/dist/src/cli/registry.js.map +1 -1
- package/dist/src/cli/templates.d.ts +1 -1
- package/dist/src/cli/templates.js +1 -11
- package/dist/src/cli/templates.js.map +1 -1
- package/dist/src/commands/agents.config.js +2 -10
- package/dist/src/commands/agents.config.js.map +1 -1
- package/dist/src/config/agent-profile.js +3 -2
- package/dist/src/config/agent-profile.js.map +1 -1
- package/dist/src/config/agent-typed-models.js +3 -5
- package/dist/src/config/agent-typed-models.js.map +1 -1
- package/dist/src/config/index.js +2 -2
- package/dist/src/config/paths.d.ts +8 -1
- package/dist/src/config/paths.js +16 -3
- package/dist/src/config/paths.js.map +1 -1
- package/dist/src/config/schema.d.ts +32 -32
- package/dist/src/config/schema.js +46 -26
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/gateway/agents-admin.d.ts +7 -0
- package/dist/src/gateway/agents-admin.js +51 -20
- package/dist/src/gateway/agents-admin.js.map +1 -1
- package/dist/src/gateway/chat-stream/mapper.js +9 -2
- package/dist/src/gateway/chat-stream/mapper.js.map +1 -1
- package/dist/src/gateway/global-defaults-admin.d.ts +36 -0
- package/dist/src/gateway/global-defaults-admin.js +146 -0
- package/dist/src/gateway/global-defaults-admin.js.map +1 -0
- package/dist/src/gateway/hono/routes/agents.js +33 -1
- package/dist/src/gateway/hono/routes/agents.js.map +1 -1
- package/dist/src/gateway/hono/routes/commands-skills.js +4 -5
- package/dist/src/gateway/hono/routes/commands-skills.js.map +1 -1
- package/dist/src/gateway/hono/routes/global-defaults.d.ts +3 -0
- package/dist/src/gateway/hono/routes/global-defaults.js +61 -0
- package/dist/src/gateway/hono/routes/global-defaults.js.map +1 -0
- package/dist/src/gateway/hono/routes/lazy-bundles.js +11 -3
- package/dist/src/gateway/hono/routes/lazy-bundles.js.map +1 -1
- package/dist/src/gateway/hono/routes/memory.js +75 -4
- package/dist/src/gateway/hono/routes/memory.js.map +1 -1
- package/dist/src/gateway/hono/routes/models.js +1 -1
- package/dist/src/gateway/service/marketplace-service.d.ts +4 -4
- package/dist/src/gateway/service/marketplace-service.js +11 -7
- package/dist/src/gateway/service/marketplace-service.js.map +1 -1
- package/dist/src/notes/service.js +3 -1
- package/dist/src/notes/service.js.map +1 -1
- package/dist/src/providers/index.d.ts +4 -5
- package/dist/src/providers/index.js +16 -16
- package/dist/src/providers/index.js.map +1 -1
- package/dist/src/session/session-workspace.js +2 -7
- package/dist/src/session/session-workspace.js.map +1 -1
- package/dist/src/session/store.js +0 -5
- package/dist/src/session/store.js.map +1 -1
- package/dist/src/storage/sqlite/index.d.ts +1 -1
- package/dist/src/storage/sqlite/index.js +2 -2
- package/dist/src/storage/sqlite/memory-index-repository.d.ts +1 -0
- package/dist/src/storage/sqlite/memory-index-repository.js +15 -14
- package/dist/src/storage/sqlite/memory-index-repository.js.map +1 -1
- package/dist/src/storage/sqlite/memory-records-repository.d.ts +44 -1
- package/dist/src/storage/sqlite/memory-records-repository.js +164 -20
- package/dist/src/storage/sqlite/memory-records-repository.js.map +1 -1
- package/dist/src/storage/sqlite/migrations/014_memory_record_governance.sql +10 -0
- package/dist/src/storage/sqlite/migrations/015_memory_trace_feedback.sql +4 -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/migrations/runner.js.map +1 -1
- package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
- package/dist/src/storage/sqlite/schema.js +1 -1
- package/dist/src/tui/backends/embedded-backend.d.ts +3 -0
- package/dist/src/tui/backends/embedded-backend.js +37 -20
- package/dist/src/tui/backends/embedded-backend.js.map +1 -1
- package/dist/src/tui/backends/gateway-sse-backend.d.ts +1 -0
- package/dist/src/tui/backends/gateway-sse-backend.js +16 -3
- package/dist/src/tui/backends/gateway-sse-backend.js.map +1 -1
- package/dist/src/tui/components/tui-header.d.ts +0 -1
- package/dist/src/tui/components/tui-header.js +3 -8
- package/dist/src/tui/components/tui-header.js.map +1 -1
- package/dist/src/tui/tui-startup-resources.js +2 -1
- package/dist/src/tui/tui-startup-resources.js.map +1 -1
- package/dist/src/tui/tui-startup-text.js +12 -12
- package/dist/src/tui/tui-startup-text.js.map +1 -1
- package/dist/src/tui/tui.js +4 -1
- package/dist/src/tui/tui.js.map +1 -1
- package/package.json +1 -1
- package/dist/gateway/static/root/assets/agents-C6xgagZd.js +0 -71
- package/dist/gateway/static/root/assets/browser-settings-page-CUOO-PQk.js +0 -8
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-CfqF-kGr.js +0 -1
- package/dist/gateway/static/root/assets/channels-settings-BYyRzsex.js +0 -1
- package/dist/gateway/static/root/assets/connectors-page-DIvEz4NX.js +0 -2
- package/dist/gateway/static/root/assets/cron-page-D97nFqQD.js +0 -3
- package/dist/gateway/static/root/assets/goal-detail-page-DCUROLMQ.js +0 -1
- package/dist/gateway/static/root/assets/heartbeat-settings-CC69vNx0.js +0 -1
- package/dist/gateway/static/root/assets/index-CyfhARL-.css +0 -1
- package/dist/gateway/static/root/assets/messages-Cf1O9JB0.js +0 -3
- package/dist/gateway/static/root/assets/models-hub-panel-DmKiIihT.js +0 -2
- package/dist/gateway/static/root/assets/note-detail-page-CxxfLRJZ.js +0 -2
- package/dist/gateway/static/root/assets/notes-page-X2slSx7V.js +0 -1
- package/dist/gateway/static/root/assets/remote-access-hub-BvmUWqgu.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-B0yESuuL.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-q2M8RKOo.js +0 -1
- package/dist/gateway/static/root/assets/settings-page-CGozabLD.js +0 -1
- package/dist/gateway/static/root/assets/setup-status-panel-FoOSe6nU.js +0 -1
- package/dist/gateway/static/root/assets/shares-settings-Cy5zWyYQ.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-DaZNFro9.js +0 -1
- package/dist/gateway/static/root/assets/skills-page-CnwtUzvw.js +0 -2
- package/dist/gateway/static/root/assets/voice-api-key-field-DvKiOglO.js +0 -1
- package/dist/gateway/static/root/assets/workflows-page-CAVZcONQ.js +0 -18
- package/dist/src/agent/context/workspace-templates/USER.md +0 -24
|
@@ -17,6 +17,19 @@ function normalizeGatewayModelChoice(model) {
|
|
|
17
17
|
id
|
|
18
18
|
};
|
|
19
19
|
}
|
|
20
|
+
function isRecord(value) {
|
|
21
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
22
|
+
}
|
|
23
|
+
function normalizeGatewaySseEvent(evt) {
|
|
24
|
+
if (evt.event !== "agent.stream" || !isRecord(evt.data)) return evt;
|
|
25
|
+
const inner = evt.data.event;
|
|
26
|
+
if (!isRecord(inner) || typeof inner.type !== "string" || !inner.type) return evt;
|
|
27
|
+
return {
|
|
28
|
+
event: inner.type,
|
|
29
|
+
data: inner,
|
|
30
|
+
source: evt.source
|
|
31
|
+
};
|
|
32
|
+
}
|
|
20
33
|
/** Fetch wrapper that adds auth headers. */
|
|
21
34
|
async function gatewayFetch(baseUrl, path, token, init) {
|
|
22
35
|
const headers = {
|
|
@@ -738,11 +751,11 @@ var GatewaySseBackend = class {
|
|
|
738
751
|
return;
|
|
739
752
|
}
|
|
740
753
|
const data = parseSSEData(sseEvent.data);
|
|
741
|
-
if (data !== null) this.onEvent?.({
|
|
754
|
+
if (data !== null) this.onEvent?.(normalizeGatewaySseEvent({
|
|
742
755
|
event: sseEvent.event,
|
|
743
756
|
data,
|
|
744
757
|
source: "broadcast"
|
|
745
|
-
});
|
|
758
|
+
}));
|
|
746
759
|
}, this.eventAbort.signal);
|
|
747
760
|
if (!this.eventAbort?.signal.aborted) {
|
|
748
761
|
this.onDisconnected?.("stream closed");
|
|
@@ -769,6 +782,6 @@ var GatewaySseBackend = class {
|
|
|
769
782
|
}
|
|
770
783
|
};
|
|
771
784
|
//#endregion
|
|
772
|
-
export { GatewaySseBackend };
|
|
785
|
+
export { GatewaySseBackend, normalizeGatewaySseEvent };
|
|
773
786
|
|
|
774
787
|
//# sourceMappingURL=gateway-sse-backend.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gateway-sse-backend.js","names":[],"sources":["../../../../src/tui/backends/gateway-sse-backend.ts"],"sourcesContent":["import { prependEnvelopeTimestamp } from '../../channels/envelope-timestamp.js';\nimport { parseModelRef } from '../../agent/models/selection.js';\nimport type { ExportFormat } from '../../session/types.js';\nimport type { TranscriptStoredRow } from '../../session/session-context-for-llm.js';\nimport { transcriptRowsToClientHistory } from '../../session/client-history.js';\nimport { createLogger } from '../../utils/logger.js';\nimport { consumeSSEStream, parseSSEData } from '../sse-consumer.js';\nimport type {\n ChatSendOptions,\n HistoryMessage,\n TuiBackend,\n TuiCompactionResult,\n TuiEvent,\n TuiModelChoice,\n TuiShareRequest,\n TuiShareResult,\n TuiSessionStats,\n TuiSessionItem,\n TuiStartupResources,\n TuiTranscriptTreeEntry,\n TuiAgentInfo,\n TuiWorkspaceFileSearchEntry,\n TuiWorkflowRunStartRequest,\n TuiWorkflowRunStartResult,\n} from '../tui-backend.js';\nimport type { SessionInfo } from '../tui-types.js';\nimport { computeTuiSessionStats } from '../tui-session-stats.js';\nimport { buildTuiTranscriptTree, transcriptTreeEntryIdToRowNumber } from '../tui-transcript-tree.js';\n\nconst log = createLogger('TUI:GatewaySSE');\n\ninterface GatewaySSEOptions {\n url: string;\n token?: string;\n}\n\nfunction normalizeGatewayModelChoice(model: TuiModelChoice): TuiModelChoice {\n const providerPrefix = `${model.provider}/`;\n const id = model.id.startsWith(providerPrefix)\n ? model.id.slice(providerPrefix.length)\n : model.id;\n return { ...model, id };\n}\n\n/** Fetch wrapper that adds auth headers. */\nasync function gatewayFetch(\n baseUrl: string,\n path: string,\n token: string | undefined,\n init?: RequestInit,\n): Promise<Response> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...(token ? { Authorization: `Bearer ${token}` } : {}),\n ...(init?.headers as Record<string, string> | undefined),\n };\n return fetch(`${baseUrl}${path}`, { ...init, headers });\n}\n\n/**\n * TUI backend that communicates with a running xopc gateway via HTTP + SSE.\n *\n * - Agent streaming: `POST /api/agent` with `Accept: text/event-stream`\n * - Broadcast events: `GET /api/events` via long-lived SSE\n * - REST calls for sessions, models, etc.\n */\nexport class GatewaySseBackend implements TuiBackend {\n private readonly baseUrl: string;\n private readonly token: string | undefined;\n private eventAbort: AbortController | null = null;\n private chatAbort: AbortController | null = null;\n\n onEvent?: (evt: TuiEvent) => void;\n onConnected?: () => void;\n onDisconnected?: (reason: string) => void;\n onGap?: (info: { expected: number; received: number }) => void;\n\n constructor(opts: GatewaySSEOptions) {\n this.baseUrl = opts.url.replace(/\\/+$/, '');\n this.token = opts.token;\n }\n\n get connectionLabel(): string {\n return this.baseUrl;\n }\n\n start(): void {\n this.startEventStream();\n }\n\n stop(): void {\n this.eventAbort?.abort();\n this.eventAbort = null;\n this.chatAbort?.abort();\n this.chatAbort = null;\n }\n\n getActiveSignal(): AbortSignal | undefined {\n const signal = this.chatAbort?.signal;\n return signal && !signal.aborted ? signal : undefined;\n }\n\n // ── Agent chat (POST /api/agent → SSE response body) ──\n\n async sendChat(opts: ChatSendOptions): Promise<{ runId: string }> {\n this.chatAbort?.abort();\n this.chatAbort = new AbortController();\n const signal = this.chatAbort.signal;\n const runId = crypto.randomUUID();\n\n\n // Fire-and-forget: run the HTTP request + SSE consumption in background\n // so the TUI event loop stays responsive for keyboard input.\n void (async () => {\n try {\n const res = await gatewayFetch(this.baseUrl, '/api/agent', this.token, {\n method: 'POST',\n headers: { Accept: 'text/event-stream' },\n body: JSON.stringify({\n // Prepend envelope timestamp for regular messages so the model knows\n // the current date/time. Skip for slash commands — parseSlashCommand\n // requires lines starting with '/'.\n message: opts.message.trimStart().startsWith('/')\n ? opts.message\n : prependEnvelopeTimestamp(opts.message),\n channel: 'webchat',\n sessionKey: opts.sessionKey,\n attachments: opts.attachments,\n thinking: opts.thinking,\n }),\n signal,\n });\n\n if (!res.ok) {\n const body = (await res.json().catch(() => ({}))) as { error?: { message?: string } };\n this.onEvent?.({\n event: 'error',\n data: {\n type: 'error',\n runId,\n sessionKey: opts.sessionKey,\n timestamp: Date.now(),\n payload: { code: 'GATEWAY_ERROR', message: body.error?.message ?? `Gateway error: ${res.status}` },\n },\n source: 'agent-response',\n });\n return;\n }\n\n const contentType = res.headers.get('Content-Type') ?? '';\n\n if (contentType.includes('text/event-stream') && res.body) {\n await consumeSSEStream(\n res.body,\n (sseEvent) => {\n if (signal.aborted) return;\n const data = parseSSEData<Record<string, unknown>>(sseEvent.data);\n if (!data) return;\n this.onEvent?.({ event: sseEvent.event, data, source: 'agent-response' });\n },\n signal,\n );\n } else {\n const json = (await res.json()) as { ok?: boolean; payload?: { content?: string } };\n if (json.ok && json.payload?.content) {\n const messageId = `msg_${runId}_1`;\n const base = { runId, sessionKey: opts.sessionKey, timestamp: Date.now() };\n this.onEvent?.({ event: 'run_start', data: { ...base, type: 'run_start', payload: { channel: 'webchat' } }, source: 'agent-response' });\n this.onEvent?.({ event: 'assistant_message_start', data: { ...base, type: 'assistant_message_start', payload: { messageId } }, source: 'agent-response' });\n this.onEvent?.({ event: 'assistant_delta', data: { ...base, type: 'assistant_delta', payload: { messageId, delta: json.payload.content } }, source: 'agent-response' });\n this.onEvent?.({ event: 'assistant_message_end', data: { ...base, type: 'assistant_message_end', payload: { messageId } }, source: 'agent-response' });\n this.onEvent?.({ event: 'run_end', data: { ...base, type: 'run_end', payload: { status: 'success' } }, source: 'agent-response' });\n }\n }\n } catch (error) {\n if (signal.aborted) return;\n const errorMessage = error instanceof Error ? error.message : String(error);\n this.onEvent?.({\n event: 'error',\n data: { type: 'error', runId, sessionKey: opts.sessionKey, timestamp: Date.now(), payload: { code: 'NETWORK_ERROR', message: errorMessage } },\n source: 'agent-response',\n });\n }\n })();\n\n return { runId };\n }\n\n async searchWorkspaceFiles(\n sessionKey: string,\n query: string,\n options?: { limit?: number },\n ): Promise<TuiWorkspaceFileSearchEntry[]> {\n try {\n const params = new URLSearchParams();\n params.set('q', query);\n params.set('limit', String(options?.limit ?? 15));\n params.set('sessionKey', sessionKey);\n const res = await gatewayFetch(\n this.baseUrl,\n `/api/workspace/editor/files/search?${params.toString()}`,\n this.token,\n );\n if (!res.ok) return [];\n const json = (await res.json()) as {\n ok?: boolean;\n payload?: { entries?: TuiWorkspaceFileSearchEntry[] };\n };\n return json.payload?.entries ?? [];\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n log.warn({ err, sessionKey, errorMessage }, `Gateway workspace file search failed: ${errorMessage}`);\n return [];\n }\n }\n\n async startWorkflowRun(opts: TuiWorkflowRunStartRequest): Promise<TuiWorkflowRunStartResult> {\n const goal = opts.goal?.trim();\n const res = await gatewayFetch(this.baseUrl, '/api/workflows/runs', this.token, {\n method: 'POST',\n body: JSON.stringify({\n definitionId: opts.definitionId,\n agentId: opts.agentId,\n parentSessionKey: opts.sessionKey,\n source: { kind: 'chat', sessionKey: opts.sessionKey },\n ...(goal ? { goal } : {}),\n ...(opts.input !== undefined ? { input: opts.input } : {}),\n }),\n });\n const json = (await res.json().catch(() => ({}))) as {\n runId?: string;\n sessionKey?: string;\n error?: string;\n code?: string;\n };\n if (!res.ok || !json.runId || !json.sessionKey) {\n throw new Error(json.error ?? `Workflow start failed (${res.status})`);\n }\n return {\n runId: json.runId,\n sessionKey: json.sessionKey,\n definitionId: opts.definitionId,\n };\n }\n\n async resumeChat(opts: { sessionKey: string; runId: string }): Promise<{ ok: boolean; reason?: string }> {\n this.chatAbort?.abort();\n this.chatAbort = new AbortController();\n const signal = this.chatAbort.signal;\n this.onEvent?.({\n event: 'run_start',\n data: { type: 'run_start', runId: opts.runId, sessionKey: opts.sessionKey, timestamp: Date.now(), payload: { channel: 'webchat' } },\n source: 'agent-resume',\n });\n\n let res: Response;\n try {\n res = await gatewayFetch(this.baseUrl, '/api/agent/resume', this.token, {\n method: 'POST',\n headers: { Accept: 'text/event-stream' },\n body: JSON.stringify({ runId: opts.runId, sessionKey: opts.sessionKey }),\n signal,\n });\n } catch (error) {\n if (signal.aborted) return { ok: false, reason: 'resume aborted' };\n const errorMessage = error instanceof Error ? error.message : String(error);\n return { ok: false, reason: errorMessage };\n }\n\n if (!res.ok) {\n const body = (await res.json().catch(() => ({}))) as { error?: { message?: string } };\n return { ok: false, reason: body.error?.message ?? `Resume failed: ${res.status}` };\n }\n\n const contentType = res.headers.get('Content-Type') ?? '';\n if (!contentType.includes('text/event-stream') || !res.body) {\n return { ok: false, reason: 'Resume endpoint did not return an SSE stream' };\n }\n\n void (async () => {\n try {\n await consumeSSEStream(\n res.body,\n (sseEvent) => {\n if (signal.aborted) return;\n const data = parseSSEData<Record<string, unknown>>(sseEvent.data);\n if (!data) return;\n this.onEvent?.({ event: sseEvent.event, data, source: 'agent-resume' });\n },\n signal,\n );\n } catch (error) {\n if (signal.aborted) return;\n const errorMessage = error instanceof Error ? error.message : String(error);\n this.onEvent?.({\n event: 'error',\n data: { type: 'error', runId: opts.runId, sessionKey: opts.sessionKey, timestamp: Date.now(), payload: { code: 'NETWORK_ERROR', message: errorMessage } },\n source: 'agent-resume',\n });\n }\n })();\n\n return { ok: true };\n }\n\n async abortChat(opts: { sessionKey: string; runId: string }): Promise<{ ok: boolean }> {\n this.chatAbort?.abort();\n this.chatAbort = null;\n try {\n const res = await gatewayFetch(this.baseUrl, '/api/agent/abort', this.token, {\n method: 'POST',\n body: JSON.stringify({ runId: opts.runId }),\n });\n const json = (await res.json()) as { ok?: boolean };\n return { ok: json.ok ?? false };\n } catch {\n return { ok: false };\n }\n }\n\n async steerChat(opts: { sessionKey: string; message: string }): Promise<{ ok: boolean }> {\n try {\n const res = await gatewayFetch(this.baseUrl, '/api/agent/steer', this.token, {\n method: 'POST',\n body: JSON.stringify({ sessionKey: opts.sessionKey, message: opts.message }),\n });\n if (!res.ok) return { ok: false };\n const json = (await res.json()) as { ok?: boolean };\n return { ok: json.ok === true };\n } catch {\n return { ok: false };\n }\n }\n\n // ── REST helpers ──\n\n async getStartupResources(sessionKey: string): Promise<TuiStartupResources> {\n const empty: TuiStartupResources = { context: [], skills: [], workflows: [], connectors: [] };\n try {\n const res = await gatewayFetch(\n this.baseUrl,\n `/api/tui/startup-resources?sessionKey=${encodeURIComponent(sessionKey)}`,\n this.token,\n );\n if (!res.ok) return empty;\n const json = (await res.json()) as { payload?: Partial<TuiStartupResources> };\n return {\n context: Array.isArray(json.payload?.context) ? json.payload.context : [],\n skills: Array.isArray(json.payload?.skills) ? json.payload.skills : [],\n workflows: Array.isArray(json.payload?.workflows) ? json.payload.workflows : [],\n connectors: Array.isArray(json.payload?.connectors) ? json.payload.connectors : [],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, sessionKey, errorMessage }, `Failed to load startup resources: ${errorMessage}`);\n return empty;\n }\n }\n\n async loadHistory(opts: {\n sessionKey: string;\n limit?: number;\n }): Promise<{ messages: HistoryMessage[] }> {\n try {\n const res = await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(opts.sessionKey)}?include=transcriptRows`,\n this.token,\n );\n if (!res.ok) return { messages: [] };\n const json = (await res.json()) as {\n session?: { transcriptRows?: unknown[] };\n };\n const rows = Array.isArray(json.session?.transcriptRows)\n ? (json.session.transcriptRows as TranscriptStoredRow[])\n : [];\n return { messages: transcriptRowsToClientHistory(rows, { limit: opts.limit }) };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, errorMessage }, `Failed to load history: ${errorMessage}`);\n return { messages: [] };\n }\n }\n\n async loadTranscriptTree(sessionKey: string): Promise<TuiTranscriptTreeEntry[]> {\n try {\n const res = await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(sessionKey)}?include=transcriptRows`,\n this.token,\n );\n if (!res.ok) return [];\n const json = (await res.json()) as {\n session?: { transcriptRows?: unknown[] };\n };\n const rows = Array.isArray(json.session?.transcriptRows)\n ? (json.session.transcriptRows as TranscriptStoredRow[])\n : [];\n return buildTuiTranscriptTree(rows);\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, sessionKey, errorMessage }, `Failed to load transcript tree: ${errorMessage}`);\n return [];\n }\n }\n\n async getSessionStats(sessionKey: string): Promise<TuiSessionStats> {\n try {\n const res = await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(sessionKey)}?include=transcriptRows`,\n this.token,\n );\n if (!res.ok) return computeTuiSessionStats([]);\n const json = (await res.json()) as {\n session?: { transcriptRows?: unknown[] };\n };\n const rows = Array.isArray(json.session?.transcriptRows)\n ? (json.session.transcriptRows as TranscriptStoredRow[])\n : [];\n return computeTuiSessionStats(rows);\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, sessionKey, errorMessage }, `Failed to load session stats: ${errorMessage}`);\n return computeTuiSessionStats([]);\n }\n }\n\n async listSessions(): Promise<TuiSessionItem[]> {\n try {\n const res = await gatewayFetch(this.baseUrl, '/api/sessions', this.token);\n if (!res.ok) return [];\n const json = (await res.json()) as {\n items?: Array<{\n key: string;\n name?: string;\n updatedAt?: string;\n estimatedTokens?: number;\n messageCount?: number;\n customData?: Record<string, unknown>;\n cwd?: string;\n }>;\n };\n return (json.items ?? []).map((s) => ({\n key: s.key,\n displayName: s.name,\n updatedAt: s.updatedAt ? Date.parse(s.updatedAt) : undefined,\n totalTokens: s.estimatedTokens ?? null,\n messageCount: typeof s.messageCount === 'number' ? s.messageCount : undefined,\n model:\n typeof s.customData?.model === 'string'\n ? s.customData.model\n : typeof s.customData?.modelRef === 'string'\n ? s.customData.modelRef\n : null,\n forkedFromSessionKey:\n typeof s.customData?.forkedFromSessionKey === 'string'\n ? s.customData.forkedFromSessionKey\n : undefined,\n cwd: typeof s.cwd === 'string' ? s.cwd : undefined,\n }));\n } catch {\n return [];\n }\n }\n\n async listAgents(): Promise<TuiAgentInfo[]> {\n const agents = new Map<string, TuiAgentInfo>();\n try {\n const res = await gatewayFetch(this.baseUrl, '/api/agents', this.token);\n if (res.ok) {\n const json = (await res.json()) as {\n payload?: { agents?: Array<{ id?: unknown; name?: unknown }> };\n };\n for (const row of json.payload?.agents ?? []) {\n if (typeof row.id !== 'string' || !row.id.trim()) continue;\n const id = row.id.trim().toLowerCase();\n agents.set(id, {\n id,\n enabled: true,\n ...(typeof row.name === 'string' && row.name.trim()\n ? { displayName: row.name.trim() }\n : {}),\n });\n }\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, errorMessage }, `Failed to load agents: ${errorMessage}`);\n }\n return [...agents.values()].sort((a, b) => a.id.localeCompare(b.id));\n }\n\n async getSessionInfo(sessionKey: string): Promise<SessionInfo> {\n const out: SessionInfo = {};\n try {\n const sessionPath = `/api/sessions/${encodeURIComponent(sessionKey)}`;\n const [sessionRes, agentCfgRes] = await Promise.all([\n gatewayFetch(this.baseUrl, sessionPath, this.token),\n gatewayFetch(this.baseUrl, `${sessionPath}/agent-config`, this.token),\n ]);\n\n type SessionRow = {\n name?: string;\n estimatedTokens?: number;\n customData?: Record<string, unknown>;\n };\n let session: SessionRow | undefined;\n\n if (sessionRes.ok) {\n const json = (await sessionRes.json()) as { session?: SessionRow };\n session = json.session;\n if (session) {\n if (session.name) out.displayName = session.name;\n if (session.estimatedTokens != null) out.totalTokens = session.estimatedTokens;\n }\n }\n\n if (agentCfgRes.ok) {\n const json = (await agentCfgRes.json()) as {\n ok?: boolean;\n payload?: {\n model?: string;\n thinkingLevel?: string;\n reasoningLevel?: string;\n verboseLevel?: string;\n effectiveWorkspacePath?: string;\n workingDirectoryLocked?: boolean;\n };\n };\n const p = json.payload;\n if (p?.model && typeof p.model === 'string') {\n const parsed = parseModelRef(p.model);\n if (parsed) {\n out.model = parsed.model;\n out.modelProvider = parsed.provider;\n } else {\n out.model = p.model;\n }\n }\n if (p?.thinkingLevel && typeof p.thinkingLevel === 'string') {\n out.thinkingLevel = p.thinkingLevel;\n }\n if (p?.reasoningLevel && typeof p.reasoningLevel === 'string') {\n out.reasoningLevel = p.reasoningLevel;\n }\n if (p?.verboseLevel && typeof p.verboseLevel === 'string') {\n out.verboseLevel = p.verboseLevel;\n }\n if (p?.effectiveWorkspacePath && typeof p.effectiveWorkspacePath === 'string') {\n out.effectiveWorkspacePath = p.effectiveWorkspacePath;\n }\n if (typeof p?.workingDirectoryLocked === 'boolean') {\n out.workingDirectoryLocked = p.workingDirectoryLocked;\n }\n }\n\n if (!out.model && session?.customData) {\n const cd = session.customData;\n const ref =\n typeof cd.model === 'string'\n ? cd.model\n : typeof cd.modelRef === 'string'\n ? cd.modelRef\n : undefined;\n if (ref) {\n const parsed = parseModelRef(ref);\n if (parsed) {\n out.model = parsed.model;\n out.modelProvider = parsed.provider;\n } else {\n out.model = ref;\n }\n }\n if (!out.modelProvider && typeof cd.modelProvider === 'string') {\n out.modelProvider = cd.modelProvider;\n }\n }\n\n if (out.totalTokens != null) {\n const models = await this.listModels();\n const match = models.find(\n (m) =>\n m.id === out.model &&\n (!out.modelProvider || m.provider === out.modelProvider),\n );\n const contextWindow = match?.contextWindow ?? 128_000;\n out.contextWindow = contextWindow;\n out.contextUsagePercent =\n contextWindow > 0\n ? Math.min(100, Math.round((out.totalTokens / contextWindow) * 100))\n : null;\n }\n\n return out;\n } catch {\n return {};\n }\n }\n\n async listModels(): Promise<TuiModelChoice[]> {\n try {\n const res = await gatewayFetch(this.baseUrl, '/api/models', this.token);\n if (!res.ok) return [];\n const json = (await res.json()) as {\n ok?: boolean;\n payload?: { models?: TuiModelChoice[] };\n };\n return (json.payload?.models ?? []).map(normalizeGatewayModelChoice);\n } catch {\n return [];\n }\n }\n\n async resetSession(sessionKey: string): Promise<void> {\n await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(sessionKey)}/reset`,\n this.token,\n { method: 'POST' },\n ).catch(() => {});\n }\n\n async compactSession(\n sessionKey: string,\n options?: { force?: boolean; instructions?: string },\n ): Promise<TuiCompactionResult> {\n try {\n const res = await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(sessionKey)}/compaction/run`,\n this.token,\n {\n method: 'POST',\n body: JSON.stringify({\n force: options?.force ?? true,\n instructions: options?.instructions,\n }),\n },\n );\n if (!res.ok) {\n return { compacted: false, summary: `Compaction failed (${res.status})` };\n }\n const json = (await res.json()) as {\n ok?: boolean;\n payload?: {\n result?: {\n compacted?: boolean;\n summary?: string;\n tokensBefore?: number;\n tokensAfter?: number;\n };\n };\n };\n const result = json.payload?.result;\n if (!result?.compacted) {\n return { compacted: false, summary: 'Nothing to compact' };\n }\n return {\n compacted: true,\n summary: `Compacted (${result.tokensBefore ?? '?'} → ${result.tokensAfter ?? '?'} tokens)`,\n tokensBefore: result.tokensBefore,\n tokensAfter: result.tokensAfter,\n transcriptSummary: result.summary,\n };\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n return { compacted: false, summary: errorMessage };\n }\n }\n\n async exportSession(sessionKey: string, format: ExportFormat): Promise<string> {\n const params = new URLSearchParams({ format });\n const res = await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(sessionKey)}/export?${params.toString()}`,\n this.token,\n );\n if (!res.ok) {\n throw new Error(`Export failed (${res.status})`);\n }\n const json = (await res.json()) as { content?: string };\n return json.content ?? '';\n }\n\n async importSession(\n targetSessionKey: string,\n jsonContent: string,\n ): Promise<{ sessionKey: string; rowCount: number }> {\n const res = await gatewayFetch(\n this.baseUrl,\n '/api/sessions/import',\n this.token,\n { method: 'POST', body: JSON.stringify({ targetKey: targetSessionKey, content: jsonContent }) },\n );\n const json = (await res.json().catch(() => ({}))) as {\n ok?: boolean;\n error?: string;\n sessionKey?: string;\n rowCount?: number;\n };\n if (!res.ok || json.ok === false || !json.sessionKey) {\n throw new Error(json.error ?? `Import failed (${res.status})`);\n }\n return { sessionKey: json.sessionKey, rowCount: json.rowCount ?? 0 };\n }\n\n async createShare(\n sessionKey: string,\n request: TuiShareRequest,\n options?: { agentId?: string },\n ): Promise<TuiShareResult> {\n const res = await gatewayFetch(this.baseUrl, '/api/shares/auto', this.token, {\n method: 'POST',\n body: JSON.stringify({\n path: request.path,\n audience: request.audience,\n mode: request.mode,\n title: request.title,\n description: request.description,\n sessionKey,\n agentId: options?.agentId,\n }),\n });\n const json = (await res.json().catch(() => ({}))) as {\n ok?: boolean;\n error?: { message?: string };\n payload?: {\n share?: {\n kind?: string;\n title?: string;\n description?: string;\n shareUrl?: string;\n reachability?: string;\n reachabilityHint?: string | null;\n expiresAt?: string;\n maxViews?: number | null;\n };\n thumbnail?: { url?: string };\n routing?: { reason?: string; hint?: string };\n };\n };\n if (!res.ok || json.ok === false || !json.payload?.share?.shareUrl) {\n throw new Error(json.error?.message ?? `Share failed (${res.status})`);\n }\n const share = json.payload.share;\n return {\n kind: share.kind ?? 'share',\n shareUrl: share.shareUrl,\n title: share.title,\n description: share.description,\n thumbnailUrl: json.payload.thumbnail?.url,\n reachability: share.reachability,\n reachabilityHint: share.reachabilityHint,\n expiresAt: share.expiresAt,\n maxViews: share.maxViews,\n routingReason: json.payload.routing?.reason,\n routingHint: json.payload.routing?.hint,\n };\n }\n\n async btwQuery(sessionKey: string, question: string): Promise<{ text: string; error?: string }> {\n const res = await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(sessionKey)}/btw`,\n this.token,\n { method: 'POST', body: JSON.stringify({ question }) },\n );\n if (!res.ok) {\n return { text: '', error: `BTW failed (${res.status})` };\n }\n return (await res.json()) as { text: string; error?: string };\n }\n\n async forkSession(\n sourceSessionKey: string,\n targetSessionKey: string,\n ): Promise<{ sessionKey: string; rowCount: number }> {\n const res = await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(sourceSessionKey)}/fork`,\n this.token,\n { method: 'POST', body: JSON.stringify({ targetKey: targetSessionKey }) },\n );\n const json = (await res.json().catch(() => ({}))) as {\n ok?: boolean;\n error?: string;\n sessionKey?: string;\n rowCount?: number;\n };\n if (!res.ok || json.ok === false || !json.sessionKey) {\n throw new Error(json.error ?? `Fork failed (${res.status})`);\n }\n return { sessionKey: json.sessionKey, rowCount: json.rowCount ?? 0 };\n }\n\n async forkSessionAt(\n sourceSessionKey: string,\n targetSessionKey: string,\n entryId: string,\n ): Promise<{ sessionKey: string; rowCount: number }> {\n const throughRow = transcriptTreeEntryIdToRowNumber(entryId);\n if (throughRow == null) {\n throw new Error(`Invalid transcript entry: ${entryId}`);\n }\n const res = await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(sourceSessionKey)}/fork-row`,\n this.token,\n { method: 'POST', body: JSON.stringify({ targetKey: targetSessionKey, throughRow }) },\n );\n const json = (await res.json().catch(() => ({}))) as {\n ok?: boolean;\n error?: string;\n sessionKey?: string;\n rowCount?: number;\n };\n if (!res.ok || json.ok === false || !json.sessionKey) {\n throw new Error(json.error ?? `Fork failed (${res.status})`);\n }\n return { sessionKey: json.sessionKey, rowCount: json.rowCount ?? 0 };\n }\n\n async setTranscriptLabel(\n sessionKey: string,\n entryId: string,\n label: string | undefined,\n ): Promise<{ ok: boolean }> {\n const res = await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(sessionKey)}/transcript/label`,\n this.token,\n { method: 'POST', body: JSON.stringify({ targetId: entryId, label }) },\n );\n const json = (await res.json().catch(() => ({}))) as { ok?: boolean; error?: string };\n if (!res.ok || json.ok === false) {\n throw new Error(json.error ?? `Label update failed (${res.status})`);\n }\n return { ok: true };\n }\n\n async appendCustomEntry(\n sessionKey: string,\n customType: string,\n data?: unknown,\n ): Promise<{ ok: boolean }> {\n const res = await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(sessionKey)}/transcript/custom`,\n this.token,\n { method: 'POST', body: JSON.stringify({ customType, data }) },\n );\n const json = (await res.json().catch(() => ({}))) as { ok?: boolean; error?: string };\n if (!res.ok || json.ok === false) {\n throw new Error(json.error ?? `Custom entry append failed (${res.status})`);\n }\n return { ok: true };\n }\n\n async appendCustomMessage(\n sessionKey: string,\n message: {\n customType: string;\n content?: string | unknown[];\n display?: boolean;\n details?: unknown;\n },\n ): Promise<{ ok: boolean }> {\n const res = await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(sessionKey)}/transcript/custom-message`,\n this.token,\n { method: 'POST', body: JSON.stringify(message) },\n );\n const json = (await res.json().catch(() => ({}))) as { ok?: boolean; error?: string };\n if (!res.ok || json.ok === false) {\n throw new Error(json.error ?? `Custom message append failed (${res.status})`);\n }\n return { ok: true };\n }\n\n async appendBashExecution(\n sessionKey: string,\n entry: {\n command: string;\n output?: string;\n exitCode?: number | null;\n signal?: string | null;\n excludeFromContext?: boolean;\n truncated?: boolean;\n fullOutputPath?: string;\n },\n ): Promise<{ ok: boolean }> {\n const res = await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(sessionKey)}/transcript/bash`,\n this.token,\n { method: 'POST', body: JSON.stringify(entry) },\n );\n const json = (await res.json().catch(() => ({}))) as { ok?: boolean; error?: string };\n if (!res.ok || json.ok === false) {\n throw new Error(json.error ?? `Bash execution append failed (${res.status})`);\n }\n return { ok: true };\n }\n\n async patchSession(sessionKey: string, patch: Record<string, unknown>): Promise<void> {\n const res = await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(sessionKey)}/agent-config`,\n this.token,\n { method: 'PATCH', body: JSON.stringify(patch) },\n );\n const json = (await res.json().catch(() => ({}))) as { ok?: boolean; error?: string };\n if (!res.ok || json.ok === false) {\n throw new Error(json.error ?? `Session config patch failed (${res.status})`);\n }\n }\n\n async renameSession(sessionKey: string, name: string): Promise<{ ok: boolean }> {\n try {\n const res = await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(sessionKey)}/rename`,\n this.token,\n { method: 'POST', body: JSON.stringify({ name }) },\n );\n return { ok: res.ok };\n } catch {\n return { ok: false };\n }\n }\n\n async deleteSession(sessionKey: string): Promise<{ ok: boolean }> {\n try {\n const res = await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(sessionKey)}`,\n this.token,\n { method: 'DELETE' },\n );\n if (!res.ok) return { ok: false };\n const json = (await res.json()) as { deleted?: boolean };\n return { ok: json.deleted !== false };\n } catch {\n return { ok: false };\n }\n }\n\n // ── Broadcast SSE (GET /api/events) ──\n\n private startEventStream(): void {\n this.eventAbort?.abort();\n this.eventAbort = new AbortController();\n\n const url = new URL(`${this.baseUrl}/api/events`);\n if (this.token) url.searchParams.set('token', this.token);\n\n const connect = async () => {\n try {\n const res = await fetch(url.toString(), {\n signal: this.eventAbort!.signal,\n headers: { Accept: 'text/event-stream' },\n });\n\n if (!res.ok || !res.body) {\n this.onDisconnected?.(`event stream error: ${res.status}`);\n this.scheduleReconnect();\n return;\n }\n\n this.onConnected?.();\n\n await consumeSSEStream(\n res.body,\n (sseEvent) => {\n if (sseEvent.event === 'connected') return;\n if (sseEvent.event === 'gap') {\n const gapData = parseSSEData(sseEvent.data) as {\n expected?: unknown;\n received?: unknown;\n } | null;\n if (\n gapData &&\n typeof gapData.expected === 'number' &&\n typeof gapData.received === 'number'\n ) {\n this.onGap?.({ expected: gapData.expected, received: gapData.received });\n }\n return;\n }\n const data = parseSSEData(sseEvent.data);\n if (data !== null) {\n this.onEvent?.({ event: sseEvent.event, data, source: 'broadcast' });\n }\n },\n this.eventAbort!.signal,\n );\n\n // Stream ended normally\n if (!this.eventAbort?.signal.aborted) {\n this.onDisconnected?.('stream closed');\n this.scheduleReconnect();\n }\n } catch (error) {\n if (this.eventAbort?.signal.aborted) return;\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, errorMessage }, `Event stream failed: ${errorMessage}`);\n this.onDisconnected?.(errorMessage);\n this.scheduleReconnect();\n }\n };\n\n void connect();\n }\n\n private scheduleReconnect(): void {\n if (this.eventAbort?.signal.aborted) return;\n setTimeout(() => {\n if (!this.eventAbort?.signal.aborted) {\n this.startEventStream();\n }\n }, 3000);\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"gateway-sse-backend.js","names":[],"sources":["../../../../src/tui/backends/gateway-sse-backend.ts"],"sourcesContent":["import { prependEnvelopeTimestamp } from '../../channels/envelope-timestamp.js';\nimport { parseModelRef } from '../../agent/models/selection.js';\nimport type { ExportFormat } from '../../session/types.js';\nimport type { TranscriptStoredRow } from '../../session/session-context-for-llm.js';\nimport { transcriptRowsToClientHistory } from '../../session/client-history.js';\nimport { createLogger } from '../../utils/logger.js';\nimport { consumeSSEStream, parseSSEData } from '../sse-consumer.js';\nimport type {\n ChatSendOptions,\n HistoryMessage,\n TuiBackend,\n TuiCompactionResult,\n TuiEvent,\n TuiModelChoice,\n TuiShareRequest,\n TuiShareResult,\n TuiSessionStats,\n TuiSessionItem,\n TuiStartupResources,\n TuiTranscriptTreeEntry,\n TuiAgentInfo,\n TuiWorkspaceFileSearchEntry,\n TuiWorkflowRunStartRequest,\n TuiWorkflowRunStartResult,\n} from '../tui-backend.js';\nimport type { SessionInfo } from '../tui-types.js';\nimport { computeTuiSessionStats } from '../tui-session-stats.js';\nimport { buildTuiTranscriptTree, transcriptTreeEntryIdToRowNumber } from '../tui-transcript-tree.js';\n\nconst log = createLogger('TUI:GatewaySSE');\n\ninterface GatewaySSEOptions {\n url: string;\n token?: string;\n}\n\nfunction normalizeGatewayModelChoice(model: TuiModelChoice): TuiModelChoice {\n const providerPrefix = `${model.provider}/`;\n const id = model.id.startsWith(providerPrefix)\n ? model.id.slice(providerPrefix.length)\n : model.id;\n return { ...model, id };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return !!value && typeof value === 'object' && !Array.isArray(value);\n}\n\nexport function normalizeGatewaySseEvent(evt: TuiEvent): TuiEvent {\n if (evt.event !== 'agent.stream' || !isRecord(evt.data)) return evt;\n const inner = evt.data.event;\n if (!isRecord(inner) || typeof inner.type !== 'string' || !inner.type) return evt;\n return { event: inner.type, data: inner, source: evt.source };\n}\n\n/** Fetch wrapper that adds auth headers. */\nasync function gatewayFetch(\n baseUrl: string,\n path: string,\n token: string | undefined,\n init?: RequestInit,\n): Promise<Response> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...(token ? { Authorization: `Bearer ${token}` } : {}),\n ...(init?.headers as Record<string, string> | undefined),\n };\n return fetch(`${baseUrl}${path}`, { ...init, headers });\n}\n\n/**\n * TUI backend that communicates with a running xopc gateway via HTTP + SSE.\n *\n * - Agent streaming: `POST /api/agent` with `Accept: text/event-stream`\n * - Broadcast events: `GET /api/events` via long-lived SSE\n * - REST calls for sessions, models, etc.\n */\nexport class GatewaySseBackend implements TuiBackend {\n private readonly baseUrl: string;\n private readonly token: string | undefined;\n private eventAbort: AbortController | null = null;\n private chatAbort: AbortController | null = null;\n\n onEvent?: (evt: TuiEvent) => void;\n onConnected?: () => void;\n onDisconnected?: (reason: string) => void;\n onGap?: (info: { expected: number; received: number }) => void;\n\n constructor(opts: GatewaySSEOptions) {\n this.baseUrl = opts.url.replace(/\\/+$/, '');\n this.token = opts.token;\n }\n\n get connectionLabel(): string {\n return this.baseUrl;\n }\n\n start(): void {\n this.startEventStream();\n }\n\n stop(): void {\n this.eventAbort?.abort();\n this.eventAbort = null;\n this.chatAbort?.abort();\n this.chatAbort = null;\n }\n\n getActiveSignal(): AbortSignal | undefined {\n const signal = this.chatAbort?.signal;\n return signal && !signal.aborted ? signal : undefined;\n }\n\n // ── Agent chat (POST /api/agent → SSE response body) ──\n\n async sendChat(opts: ChatSendOptions): Promise<{ runId: string }> {\n this.chatAbort?.abort();\n this.chatAbort = new AbortController();\n const signal = this.chatAbort.signal;\n const runId = crypto.randomUUID();\n\n\n // Fire-and-forget: run the HTTP request + SSE consumption in background\n // so the TUI event loop stays responsive for keyboard input.\n void (async () => {\n try {\n const res = await gatewayFetch(this.baseUrl, '/api/agent', this.token, {\n method: 'POST',\n headers: { Accept: 'text/event-stream' },\n body: JSON.stringify({\n // Prepend envelope timestamp for regular messages so the model knows\n // the current date/time. Skip for slash commands — parseSlashCommand\n // requires lines starting with '/'.\n message: opts.message.trimStart().startsWith('/')\n ? opts.message\n : prependEnvelopeTimestamp(opts.message),\n channel: 'webchat',\n sessionKey: opts.sessionKey,\n attachments: opts.attachments,\n thinking: opts.thinking,\n }),\n signal,\n });\n\n if (!res.ok) {\n const body = (await res.json().catch(() => ({}))) as { error?: { message?: string } };\n this.onEvent?.({\n event: 'error',\n data: {\n type: 'error',\n runId,\n sessionKey: opts.sessionKey,\n timestamp: Date.now(),\n payload: { code: 'GATEWAY_ERROR', message: body.error?.message ?? `Gateway error: ${res.status}` },\n },\n source: 'agent-response',\n });\n return;\n }\n\n const contentType = res.headers.get('Content-Type') ?? '';\n\n if (contentType.includes('text/event-stream') && res.body) {\n await consumeSSEStream(\n res.body,\n (sseEvent) => {\n if (signal.aborted) return;\n const data = parseSSEData<Record<string, unknown>>(sseEvent.data);\n if (!data) return;\n this.onEvent?.({ event: sseEvent.event, data, source: 'agent-response' });\n },\n signal,\n );\n } else {\n const json = (await res.json()) as { ok?: boolean; payload?: { content?: string } };\n if (json.ok && json.payload?.content) {\n const messageId = `msg_${runId}_1`;\n const base = { runId, sessionKey: opts.sessionKey, timestamp: Date.now() };\n this.onEvent?.({ event: 'run_start', data: { ...base, type: 'run_start', payload: { channel: 'webchat' } }, source: 'agent-response' });\n this.onEvent?.({ event: 'assistant_message_start', data: { ...base, type: 'assistant_message_start', payload: { messageId } }, source: 'agent-response' });\n this.onEvent?.({ event: 'assistant_delta', data: { ...base, type: 'assistant_delta', payload: { messageId, delta: json.payload.content } }, source: 'agent-response' });\n this.onEvent?.({ event: 'assistant_message_end', data: { ...base, type: 'assistant_message_end', payload: { messageId } }, source: 'agent-response' });\n this.onEvent?.({ event: 'run_end', data: { ...base, type: 'run_end', payload: { status: 'success' } }, source: 'agent-response' });\n }\n }\n } catch (error) {\n if (signal.aborted) return;\n const errorMessage = error instanceof Error ? error.message : String(error);\n this.onEvent?.({\n event: 'error',\n data: { type: 'error', runId, sessionKey: opts.sessionKey, timestamp: Date.now(), payload: { code: 'NETWORK_ERROR', message: errorMessage } },\n source: 'agent-response',\n });\n }\n })();\n\n return { runId };\n }\n\n async searchWorkspaceFiles(\n sessionKey: string,\n query: string,\n options?: { limit?: number },\n ): Promise<TuiWorkspaceFileSearchEntry[]> {\n try {\n const params = new URLSearchParams();\n params.set('q', query);\n params.set('limit', String(options?.limit ?? 15));\n params.set('sessionKey', sessionKey);\n const res = await gatewayFetch(\n this.baseUrl,\n `/api/workspace/editor/files/search?${params.toString()}`,\n this.token,\n );\n if (!res.ok) return [];\n const json = (await res.json()) as {\n ok?: boolean;\n payload?: { entries?: TuiWorkspaceFileSearchEntry[] };\n };\n return json.payload?.entries ?? [];\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n log.warn({ err, sessionKey, errorMessage }, `Gateway workspace file search failed: ${errorMessage}`);\n return [];\n }\n }\n\n async startWorkflowRun(opts: TuiWorkflowRunStartRequest): Promise<TuiWorkflowRunStartResult> {\n const goal = opts.goal?.trim();\n const res = await gatewayFetch(this.baseUrl, '/api/workflows/runs', this.token, {\n method: 'POST',\n body: JSON.stringify({\n definitionId: opts.definitionId,\n agentId: opts.agentId,\n parentSessionKey: opts.sessionKey,\n source: { kind: 'chat', sessionKey: opts.sessionKey },\n ...(goal ? { goal } : {}),\n ...(opts.input !== undefined ? { input: opts.input } : {}),\n }),\n });\n const json = (await res.json().catch(() => ({}))) as {\n runId?: string;\n sessionKey?: string;\n error?: string;\n code?: string;\n };\n if (!res.ok || !json.runId || !json.sessionKey) {\n throw new Error(json.error ?? `Workflow start failed (${res.status})`);\n }\n return {\n runId: json.runId,\n sessionKey: json.sessionKey,\n definitionId: opts.definitionId,\n };\n }\n\n async resumeChat(opts: { sessionKey: string; runId: string }): Promise<{ ok: boolean; reason?: string }> {\n this.chatAbort?.abort();\n this.chatAbort = new AbortController();\n const signal = this.chatAbort.signal;\n this.onEvent?.({\n event: 'run_start',\n data: { type: 'run_start', runId: opts.runId, sessionKey: opts.sessionKey, timestamp: Date.now(), payload: { channel: 'webchat' } },\n source: 'agent-resume',\n });\n\n let res: Response;\n try {\n res = await gatewayFetch(this.baseUrl, '/api/agent/resume', this.token, {\n method: 'POST',\n headers: { Accept: 'text/event-stream' },\n body: JSON.stringify({ runId: opts.runId, sessionKey: opts.sessionKey }),\n signal,\n });\n } catch (error) {\n if (signal.aborted) return { ok: false, reason: 'resume aborted' };\n const errorMessage = error instanceof Error ? error.message : String(error);\n return { ok: false, reason: errorMessage };\n }\n\n if (!res.ok) {\n const body = (await res.json().catch(() => ({}))) as { error?: { message?: string } };\n return { ok: false, reason: body.error?.message ?? `Resume failed: ${res.status}` };\n }\n\n const contentType = res.headers.get('Content-Type') ?? '';\n if (!contentType.includes('text/event-stream') || !res.body) {\n return { ok: false, reason: 'Resume endpoint did not return an SSE stream' };\n }\n\n void (async () => {\n try {\n await consumeSSEStream(\n res.body,\n (sseEvent) => {\n if (signal.aborted) return;\n const data = parseSSEData<Record<string, unknown>>(sseEvent.data);\n if (!data) return;\n this.onEvent?.({ event: sseEvent.event, data, source: 'agent-resume' });\n },\n signal,\n );\n } catch (error) {\n if (signal.aborted) return;\n const errorMessage = error instanceof Error ? error.message : String(error);\n this.onEvent?.({\n event: 'error',\n data: { type: 'error', runId: opts.runId, sessionKey: opts.sessionKey, timestamp: Date.now(), payload: { code: 'NETWORK_ERROR', message: errorMessage } },\n source: 'agent-resume',\n });\n }\n })();\n\n return { ok: true };\n }\n\n async abortChat(opts: { sessionKey: string; runId: string }): Promise<{ ok: boolean }> {\n this.chatAbort?.abort();\n this.chatAbort = null;\n try {\n const res = await gatewayFetch(this.baseUrl, '/api/agent/abort', this.token, {\n method: 'POST',\n body: JSON.stringify({ runId: opts.runId }),\n });\n const json = (await res.json()) as { ok?: boolean };\n return { ok: json.ok ?? false };\n } catch {\n return { ok: false };\n }\n }\n\n async steerChat(opts: { sessionKey: string; message: string }): Promise<{ ok: boolean }> {\n try {\n const res = await gatewayFetch(this.baseUrl, '/api/agent/steer', this.token, {\n method: 'POST',\n body: JSON.stringify({ sessionKey: opts.sessionKey, message: opts.message }),\n });\n if (!res.ok) return { ok: false };\n const json = (await res.json()) as { ok?: boolean };\n return { ok: json.ok === true };\n } catch {\n return { ok: false };\n }\n }\n\n // ── REST helpers ──\n\n async getStartupResources(sessionKey: string): Promise<TuiStartupResources> {\n const empty: TuiStartupResources = { context: [], skills: [], workflows: [], connectors: [] };\n try {\n const res = await gatewayFetch(\n this.baseUrl,\n `/api/tui/startup-resources?sessionKey=${encodeURIComponent(sessionKey)}`,\n this.token,\n );\n if (!res.ok) return empty;\n const json = (await res.json()) as { payload?: Partial<TuiStartupResources> };\n return {\n context: Array.isArray(json.payload?.context) ? json.payload.context : [],\n skills: Array.isArray(json.payload?.skills) ? json.payload.skills : [],\n workflows: Array.isArray(json.payload?.workflows) ? json.payload.workflows : [],\n connectors: Array.isArray(json.payload?.connectors) ? json.payload.connectors : [],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, sessionKey, errorMessage }, `Failed to load startup resources: ${errorMessage}`);\n return empty;\n }\n }\n\n async loadHistory(opts: {\n sessionKey: string;\n limit?: number;\n }): Promise<{ messages: HistoryMessage[] }> {\n try {\n const res = await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(opts.sessionKey)}?include=transcriptRows`,\n this.token,\n );\n if (!res.ok) return { messages: [] };\n const json = (await res.json()) as {\n session?: { transcriptRows?: unknown[] };\n };\n const rows = Array.isArray(json.session?.transcriptRows)\n ? (json.session.transcriptRows as TranscriptStoredRow[])\n : [];\n return { messages: transcriptRowsToClientHistory(rows, { limit: opts.limit }) };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, errorMessage }, `Failed to load history: ${errorMessage}`);\n return { messages: [] };\n }\n }\n\n async loadTranscriptTree(sessionKey: string): Promise<TuiTranscriptTreeEntry[]> {\n try {\n const res = await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(sessionKey)}?include=transcriptRows`,\n this.token,\n );\n if (!res.ok) return [];\n const json = (await res.json()) as {\n session?: { transcriptRows?: unknown[] };\n };\n const rows = Array.isArray(json.session?.transcriptRows)\n ? (json.session.transcriptRows as TranscriptStoredRow[])\n : [];\n return buildTuiTranscriptTree(rows);\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, sessionKey, errorMessage }, `Failed to load transcript tree: ${errorMessage}`);\n return [];\n }\n }\n\n async getSessionStats(sessionKey: string): Promise<TuiSessionStats> {\n try {\n const res = await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(sessionKey)}?include=transcriptRows`,\n this.token,\n );\n if (!res.ok) return computeTuiSessionStats([]);\n const json = (await res.json()) as {\n session?: { transcriptRows?: unknown[] };\n };\n const rows = Array.isArray(json.session?.transcriptRows)\n ? (json.session.transcriptRows as TranscriptStoredRow[])\n : [];\n return computeTuiSessionStats(rows);\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, sessionKey, errorMessage }, `Failed to load session stats: ${errorMessage}`);\n return computeTuiSessionStats([]);\n }\n }\n\n async listSessions(): Promise<TuiSessionItem[]> {\n try {\n const res = await gatewayFetch(this.baseUrl, '/api/sessions', this.token);\n if (!res.ok) return [];\n const json = (await res.json()) as {\n items?: Array<{\n key: string;\n name?: string;\n updatedAt?: string;\n estimatedTokens?: number;\n messageCount?: number;\n customData?: Record<string, unknown>;\n cwd?: string;\n }>;\n };\n return (json.items ?? []).map((s) => ({\n key: s.key,\n displayName: s.name,\n updatedAt: s.updatedAt ? Date.parse(s.updatedAt) : undefined,\n totalTokens: s.estimatedTokens ?? null,\n messageCount: typeof s.messageCount === 'number' ? s.messageCount : undefined,\n model:\n typeof s.customData?.model === 'string'\n ? s.customData.model\n : typeof s.customData?.modelRef === 'string'\n ? s.customData.modelRef\n : null,\n forkedFromSessionKey:\n typeof s.customData?.forkedFromSessionKey === 'string'\n ? s.customData.forkedFromSessionKey\n : undefined,\n cwd: typeof s.cwd === 'string' ? s.cwd : undefined,\n }));\n } catch {\n return [];\n }\n }\n\n async listAgents(): Promise<TuiAgentInfo[]> {\n const agents = new Map<string, TuiAgentInfo>();\n try {\n const res = await gatewayFetch(this.baseUrl, '/api/agents', this.token);\n if (res.ok) {\n const json = (await res.json()) as {\n payload?: { agents?: Array<{ id?: unknown; name?: unknown }> };\n };\n for (const row of json.payload?.agents ?? []) {\n if (typeof row.id !== 'string' || !row.id.trim()) continue;\n const id = row.id.trim().toLowerCase();\n agents.set(id, {\n id,\n enabled: true,\n ...(typeof row.name === 'string' && row.name.trim()\n ? { displayName: row.name.trim() }\n : {}),\n });\n }\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, errorMessage }, `Failed to load agents: ${errorMessage}`);\n }\n return [...agents.values()].sort((a, b) => a.id.localeCompare(b.id));\n }\n\n async getSessionInfo(sessionKey: string): Promise<SessionInfo> {\n const out: SessionInfo = {};\n try {\n const sessionPath = `/api/sessions/${encodeURIComponent(sessionKey)}`;\n const [sessionRes, agentCfgRes] = await Promise.all([\n gatewayFetch(this.baseUrl, sessionPath, this.token),\n gatewayFetch(this.baseUrl, `${sessionPath}/agent-config`, this.token),\n ]);\n\n type SessionRow = {\n name?: string;\n estimatedTokens?: number;\n customData?: Record<string, unknown>;\n };\n let session: SessionRow | undefined;\n\n if (sessionRes.ok) {\n const json = (await sessionRes.json()) as { session?: SessionRow };\n session = json.session;\n if (session) {\n if (session.name) out.displayName = session.name;\n if (session.estimatedTokens != null) out.totalTokens = session.estimatedTokens;\n }\n }\n\n if (agentCfgRes.ok) {\n const json = (await agentCfgRes.json()) as {\n ok?: boolean;\n payload?: {\n model?: string;\n thinkingLevel?: string;\n reasoningLevel?: string;\n verboseLevel?: string;\n effectiveWorkspacePath?: string;\n workingDirectoryLocked?: boolean;\n };\n };\n const p = json.payload;\n if (p?.model && typeof p.model === 'string') {\n const parsed = parseModelRef(p.model);\n if (parsed) {\n out.model = parsed.model;\n out.modelProvider = parsed.provider;\n } else {\n out.model = p.model;\n }\n }\n if (p?.thinkingLevel && typeof p.thinkingLevel === 'string') {\n out.thinkingLevel = p.thinkingLevel;\n }\n if (p?.reasoningLevel && typeof p.reasoningLevel === 'string') {\n out.reasoningLevel = p.reasoningLevel;\n }\n if (p?.verboseLevel && typeof p.verboseLevel === 'string') {\n out.verboseLevel = p.verboseLevel;\n }\n if (p?.effectiveWorkspacePath && typeof p.effectiveWorkspacePath === 'string') {\n out.effectiveWorkspacePath = p.effectiveWorkspacePath;\n }\n if (typeof p?.workingDirectoryLocked === 'boolean') {\n out.workingDirectoryLocked = p.workingDirectoryLocked;\n }\n }\n\n if (!out.model && session?.customData) {\n const cd = session.customData;\n const ref =\n typeof cd.model === 'string'\n ? cd.model\n : typeof cd.modelRef === 'string'\n ? cd.modelRef\n : undefined;\n if (ref) {\n const parsed = parseModelRef(ref);\n if (parsed) {\n out.model = parsed.model;\n out.modelProvider = parsed.provider;\n } else {\n out.model = ref;\n }\n }\n if (!out.modelProvider && typeof cd.modelProvider === 'string') {\n out.modelProvider = cd.modelProvider;\n }\n }\n\n if (out.totalTokens != null) {\n const models = await this.listModels();\n const match = models.find(\n (m) =>\n m.id === out.model &&\n (!out.modelProvider || m.provider === out.modelProvider),\n );\n const contextWindow = match?.contextWindow ?? 128_000;\n out.contextWindow = contextWindow;\n out.contextUsagePercent =\n contextWindow > 0\n ? Math.min(100, Math.round((out.totalTokens / contextWindow) * 100))\n : null;\n }\n\n return out;\n } catch {\n return {};\n }\n }\n\n async listModels(): Promise<TuiModelChoice[]> {\n try {\n const res = await gatewayFetch(this.baseUrl, '/api/models', this.token);\n if (!res.ok) return [];\n const json = (await res.json()) as {\n ok?: boolean;\n payload?: { models?: TuiModelChoice[] };\n };\n return (json.payload?.models ?? []).map(normalizeGatewayModelChoice);\n } catch {\n return [];\n }\n }\n\n async resetSession(sessionKey: string): Promise<void> {\n await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(sessionKey)}/reset`,\n this.token,\n { method: 'POST' },\n ).catch(() => {});\n }\n\n async compactSession(\n sessionKey: string,\n options?: { force?: boolean; instructions?: string },\n ): Promise<TuiCompactionResult> {\n try {\n const res = await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(sessionKey)}/compaction/run`,\n this.token,\n {\n method: 'POST',\n body: JSON.stringify({\n force: options?.force ?? true,\n instructions: options?.instructions,\n }),\n },\n );\n if (!res.ok) {\n return { compacted: false, summary: `Compaction failed (${res.status})` };\n }\n const json = (await res.json()) as {\n ok?: boolean;\n payload?: {\n result?: {\n compacted?: boolean;\n summary?: string;\n tokensBefore?: number;\n tokensAfter?: number;\n };\n };\n };\n const result = json.payload?.result;\n if (!result?.compacted) {\n return { compacted: false, summary: 'Nothing to compact' };\n }\n return {\n compacted: true,\n summary: `Compacted (${result.tokensBefore ?? '?'} → ${result.tokensAfter ?? '?'} tokens)`,\n tokensBefore: result.tokensBefore,\n tokensAfter: result.tokensAfter,\n transcriptSummary: result.summary,\n };\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n return { compacted: false, summary: errorMessage };\n }\n }\n\n async exportSession(sessionKey: string, format: ExportFormat): Promise<string> {\n const params = new URLSearchParams({ format });\n const res = await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(sessionKey)}/export?${params.toString()}`,\n this.token,\n );\n if (!res.ok) {\n throw new Error(`Export failed (${res.status})`);\n }\n const json = (await res.json()) as { content?: string };\n return json.content ?? '';\n }\n\n async importSession(\n targetSessionKey: string,\n jsonContent: string,\n ): Promise<{ sessionKey: string; rowCount: number }> {\n const res = await gatewayFetch(\n this.baseUrl,\n '/api/sessions/import',\n this.token,\n { method: 'POST', body: JSON.stringify({ targetKey: targetSessionKey, content: jsonContent }) },\n );\n const json = (await res.json().catch(() => ({}))) as {\n ok?: boolean;\n error?: string;\n sessionKey?: string;\n rowCount?: number;\n };\n if (!res.ok || json.ok === false || !json.sessionKey) {\n throw new Error(json.error ?? `Import failed (${res.status})`);\n }\n return { sessionKey: json.sessionKey, rowCount: json.rowCount ?? 0 };\n }\n\n async createShare(\n sessionKey: string,\n request: TuiShareRequest,\n options?: { agentId?: string },\n ): Promise<TuiShareResult> {\n const res = await gatewayFetch(this.baseUrl, '/api/shares/auto', this.token, {\n method: 'POST',\n body: JSON.stringify({\n path: request.path,\n audience: request.audience,\n mode: request.mode,\n title: request.title,\n description: request.description,\n sessionKey,\n agentId: options?.agentId,\n }),\n });\n const json = (await res.json().catch(() => ({}))) as {\n ok?: boolean;\n error?: { message?: string };\n payload?: {\n share?: {\n kind?: string;\n title?: string;\n description?: string;\n shareUrl?: string;\n reachability?: string;\n reachabilityHint?: string | null;\n expiresAt?: string;\n maxViews?: number | null;\n };\n thumbnail?: { url?: string };\n routing?: { reason?: string; hint?: string };\n };\n };\n if (!res.ok || json.ok === false || !json.payload?.share?.shareUrl) {\n throw new Error(json.error?.message ?? `Share failed (${res.status})`);\n }\n const share = json.payload.share;\n return {\n kind: share.kind ?? 'share',\n shareUrl: share.shareUrl,\n title: share.title,\n description: share.description,\n thumbnailUrl: json.payload.thumbnail?.url,\n reachability: share.reachability,\n reachabilityHint: share.reachabilityHint,\n expiresAt: share.expiresAt,\n maxViews: share.maxViews,\n routingReason: json.payload.routing?.reason,\n routingHint: json.payload.routing?.hint,\n };\n }\n\n async btwQuery(sessionKey: string, question: string): Promise<{ text: string; error?: string }> {\n const res = await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(sessionKey)}/btw`,\n this.token,\n { method: 'POST', body: JSON.stringify({ question }) },\n );\n if (!res.ok) {\n return { text: '', error: `BTW failed (${res.status})` };\n }\n return (await res.json()) as { text: string; error?: string };\n }\n\n async forkSession(\n sourceSessionKey: string,\n targetSessionKey: string,\n ): Promise<{ sessionKey: string; rowCount: number }> {\n const res = await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(sourceSessionKey)}/fork`,\n this.token,\n { method: 'POST', body: JSON.stringify({ targetKey: targetSessionKey }) },\n );\n const json = (await res.json().catch(() => ({}))) as {\n ok?: boolean;\n error?: string;\n sessionKey?: string;\n rowCount?: number;\n };\n if (!res.ok || json.ok === false || !json.sessionKey) {\n throw new Error(json.error ?? `Fork failed (${res.status})`);\n }\n return { sessionKey: json.sessionKey, rowCount: json.rowCount ?? 0 };\n }\n\n async forkSessionAt(\n sourceSessionKey: string,\n targetSessionKey: string,\n entryId: string,\n ): Promise<{ sessionKey: string; rowCount: number }> {\n const throughRow = transcriptTreeEntryIdToRowNumber(entryId);\n if (throughRow == null) {\n throw new Error(`Invalid transcript entry: ${entryId}`);\n }\n const res = await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(sourceSessionKey)}/fork-row`,\n this.token,\n { method: 'POST', body: JSON.stringify({ targetKey: targetSessionKey, throughRow }) },\n );\n const json = (await res.json().catch(() => ({}))) as {\n ok?: boolean;\n error?: string;\n sessionKey?: string;\n rowCount?: number;\n };\n if (!res.ok || json.ok === false || !json.sessionKey) {\n throw new Error(json.error ?? `Fork failed (${res.status})`);\n }\n return { sessionKey: json.sessionKey, rowCount: json.rowCount ?? 0 };\n }\n\n async setTranscriptLabel(\n sessionKey: string,\n entryId: string,\n label: string | undefined,\n ): Promise<{ ok: boolean }> {\n const res = await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(sessionKey)}/transcript/label`,\n this.token,\n { method: 'POST', body: JSON.stringify({ targetId: entryId, label }) },\n );\n const json = (await res.json().catch(() => ({}))) as { ok?: boolean; error?: string };\n if (!res.ok || json.ok === false) {\n throw new Error(json.error ?? `Label update failed (${res.status})`);\n }\n return { ok: true };\n }\n\n async appendCustomEntry(\n sessionKey: string,\n customType: string,\n data?: unknown,\n ): Promise<{ ok: boolean }> {\n const res = await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(sessionKey)}/transcript/custom`,\n this.token,\n { method: 'POST', body: JSON.stringify({ customType, data }) },\n );\n const json = (await res.json().catch(() => ({}))) as { ok?: boolean; error?: string };\n if (!res.ok || json.ok === false) {\n throw new Error(json.error ?? `Custom entry append failed (${res.status})`);\n }\n return { ok: true };\n }\n\n async appendCustomMessage(\n sessionKey: string,\n message: {\n customType: string;\n content?: string | unknown[];\n display?: boolean;\n details?: unknown;\n },\n ): Promise<{ ok: boolean }> {\n const res = await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(sessionKey)}/transcript/custom-message`,\n this.token,\n { method: 'POST', body: JSON.stringify(message) },\n );\n const json = (await res.json().catch(() => ({}))) as { ok?: boolean; error?: string };\n if (!res.ok || json.ok === false) {\n throw new Error(json.error ?? `Custom message append failed (${res.status})`);\n }\n return { ok: true };\n }\n\n async appendBashExecution(\n sessionKey: string,\n entry: {\n command: string;\n output?: string;\n exitCode?: number | null;\n signal?: string | null;\n excludeFromContext?: boolean;\n truncated?: boolean;\n fullOutputPath?: string;\n },\n ): Promise<{ ok: boolean }> {\n const res = await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(sessionKey)}/transcript/bash`,\n this.token,\n { method: 'POST', body: JSON.stringify(entry) },\n );\n const json = (await res.json().catch(() => ({}))) as { ok?: boolean; error?: string };\n if (!res.ok || json.ok === false) {\n throw new Error(json.error ?? `Bash execution append failed (${res.status})`);\n }\n return { ok: true };\n }\n\n async patchSession(sessionKey: string, patch: Record<string, unknown>): Promise<void> {\n const res = await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(sessionKey)}/agent-config`,\n this.token,\n { method: 'PATCH', body: JSON.stringify(patch) },\n );\n const json = (await res.json().catch(() => ({}))) as { ok?: boolean; error?: string };\n if (!res.ok || json.ok === false) {\n throw new Error(json.error ?? `Session config patch failed (${res.status})`);\n }\n }\n\n async renameSession(sessionKey: string, name: string): Promise<{ ok: boolean }> {\n try {\n const res = await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(sessionKey)}/rename`,\n this.token,\n { method: 'POST', body: JSON.stringify({ name }) },\n );\n return { ok: res.ok };\n } catch {\n return { ok: false };\n }\n }\n\n async deleteSession(sessionKey: string): Promise<{ ok: boolean }> {\n try {\n const res = await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(sessionKey)}`,\n this.token,\n { method: 'DELETE' },\n );\n if (!res.ok) return { ok: false };\n const json = (await res.json()) as { deleted?: boolean };\n return { ok: json.deleted !== false };\n } catch {\n return { ok: false };\n }\n }\n\n // ── Broadcast SSE (GET /api/events) ──\n\n private startEventStream(): void {\n this.eventAbort?.abort();\n this.eventAbort = new AbortController();\n\n const url = new URL(`${this.baseUrl}/api/events`);\n if (this.token) url.searchParams.set('token', this.token);\n\n const connect = async () => {\n try {\n const res = await fetch(url.toString(), {\n signal: this.eventAbort!.signal,\n headers: { Accept: 'text/event-stream' },\n });\n\n if (!res.ok || !res.body) {\n this.onDisconnected?.(`event stream error: ${res.status}`);\n this.scheduleReconnect();\n return;\n }\n\n this.onConnected?.();\n\n await consumeSSEStream(\n res.body,\n (sseEvent) => {\n if (sseEvent.event === 'connected') return;\n if (sseEvent.event === 'gap') {\n const gapData = parseSSEData(sseEvent.data) as {\n expected?: unknown;\n received?: unknown;\n } | null;\n if (\n gapData &&\n typeof gapData.expected === 'number' &&\n typeof gapData.received === 'number'\n ) {\n this.onGap?.({ expected: gapData.expected, received: gapData.received });\n }\n return;\n }\n const data = parseSSEData(sseEvent.data);\n if (data !== null) {\n this.onEvent?.(normalizeGatewaySseEvent({ event: sseEvent.event, data, source: 'broadcast' }));\n }\n },\n this.eventAbort!.signal,\n );\n\n // Stream ended normally\n if (!this.eventAbort?.signal.aborted) {\n this.onDisconnected?.('stream closed');\n this.scheduleReconnect();\n }\n } catch (error) {\n if (this.eventAbort?.signal.aborted) return;\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, errorMessage }, `Event stream failed: ${errorMessage}`);\n this.onDisconnected?.(errorMessage);\n this.scheduleReconnect();\n }\n };\n\n void connect();\n }\n\n private scheduleReconnect(): void {\n if (this.eventAbort?.signal.aborted) return;\n setTimeout(() => {\n if (!this.eventAbort?.signal.aborted) {\n this.startEventStream();\n }\n }, 3000);\n }\n}\n"],"mappings":"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"}
|
|
@@ -17,7 +17,6 @@ export declare class TuiHeader implements Component {
|
|
|
17
17
|
setCustomComponent(component: Component | undefined): void;
|
|
18
18
|
invalidate(): void;
|
|
19
19
|
private getCompactHints;
|
|
20
|
-
private getExpandedHints;
|
|
21
20
|
render(width: number): string[];
|
|
22
21
|
private customComponent;
|
|
23
22
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { theme } from "../theme.js";
|
|
2
2
|
import { formatKeyIds } from "../format-tui-hotkeys.js";
|
|
3
|
-
import { truncateToWidth
|
|
3
|
+
import { truncateToWidth } from "@earendil-works/pi-tui";
|
|
4
4
|
//#region src/tui/components/tui-header.ts
|
|
5
5
|
const COMPACT_HINTS = "escape interrupt · ctrl+c/ctrl+d clear/exit · / commands · ! bash · ctrl+o tools";
|
|
6
|
-
const
|
|
6
|
+
const STARTUP_HELP_HINT = "Press /start to show full startup help and loaded resources.";
|
|
7
7
|
/** Collapsible startup header (pi-style hints subset). */
|
|
8
8
|
var TuiHeader = class {
|
|
9
9
|
extensionLines = [];
|
|
@@ -26,18 +26,13 @@ var TuiHeader = class {
|
|
|
26
26
|
if (!this.keybindings) return COMPACT_HINTS;
|
|
27
27
|
return `${formatKeyIds(this.keybindings, "app.interrupt", { capitalize: false })} interrupt · ${formatKeyIds(this.keybindings, "app.clear", { capitalize: false })}/${formatKeyIds(this.keybindings, "app.exit", { capitalize: false })} clear/exit · / commands · ! bash · ${formatKeyIds(this.keybindings, "app.tools.expand", { capitalize: false })} tools`;
|
|
28
28
|
}
|
|
29
|
-
getExpandedHints() {
|
|
30
|
-
if (!this.keybindings) return EXPANDED_HINTS;
|
|
31
|
-
return EXPANDED_HINTS;
|
|
32
|
-
}
|
|
33
29
|
render(width) {
|
|
34
30
|
if (this.customComponent) return this.customComponent.render(width).map((line) => truncateToWidth(line, width, theme.dim("…")));
|
|
35
31
|
const { version, connectionLabel, sessionKey, showHints } = this.getModel();
|
|
36
32
|
const lines = [truncateToWidth(theme.header(`xopc tui v${version} — ${connectionLabel} — ${sessionKey}`), width, theme.dim("…"))];
|
|
37
33
|
if (showHints) {
|
|
38
34
|
lines.push(truncateToWidth(theme.dim(this.getCompactHints()), width, theme.dim("…")));
|
|
39
|
-
|
|
40
|
-
else lines.push(truncateToWidth(theme.dim(hint), width, theme.dim("…")));
|
|
35
|
+
lines.push(truncateToWidth(theme.dim(STARTUP_HELP_HINT), width, theme.dim("…")));
|
|
41
36
|
}
|
|
42
37
|
for (const extLine of this.extensionLines) lines.push(truncateToWidth(theme.dim(extLine), width, theme.dim("…")));
|
|
43
38
|
for (const component of this.extensionComponents) for (const line of component.render(width)) lines.push(truncateToWidth(line, width, theme.dim("…")));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tui-header.js","names":[],"sources":["../../../../src/tui/components/tui-header.ts"],"sourcesContent":["import type { Component, KeybindingsManager } from '@earendil-works/pi-tui';\nimport { truncateToWidth
|
|
1
|
+
{"version":3,"file":"tui-header.js","names":[],"sources":["../../../../src/tui/components/tui-header.ts"],"sourcesContent":["import type { Component, KeybindingsManager } from '@earendil-works/pi-tui';\nimport { truncateToWidth } from '@earendil-works/pi-tui';\n\nimport { theme } from '../theme.js';\nimport { formatKeyIds } from '../format-tui-hotkeys.js';\n\nexport type TuiHeaderModel = {\n version: string;\n connectionLabel: string;\n sessionKey: string;\n showHints: boolean;\n};\n\nconst COMPACT_HINTS =\n 'escape interrupt · ctrl+c/ctrl+d clear/exit · / commands · ! bash · ctrl+o tools';\nconst STARTUP_HELP_HINT = 'Press /start to show full startup help and loaded resources.';\n\n/** Collapsible startup header (pi-style hints subset). */\nexport class TuiHeader implements Component {\n private extensionLines: string[] = [];\n private extensionComponents: Component[] = [];\n\n constructor(\n private readonly getModel: () => TuiHeaderModel,\n private readonly keybindings?: KeybindingsManager,\n ) {}\n\n setExtensionLines(lines: string[]): void {\n this.extensionLines = lines;\n }\n\n setExtensionComponents(components: Component[]): void {\n this.extensionComponents = components;\n }\n\n setCustomComponent(component: Component | undefined): void {\n this.customComponent = component;\n }\n\n invalidate(): void {}\n\n private getCompactHints(): string {\n if (!this.keybindings) return COMPACT_HINTS;\n const interrupt = formatKeyIds(this.keybindings, 'app.interrupt', { capitalize: false });\n const clear = formatKeyIds(this.keybindings, 'app.clear', { capitalize: false });\n const exit = formatKeyIds(this.keybindings, 'app.exit', { capitalize: false });\n const tools = formatKeyIds(this.keybindings, 'app.tools.expand', { capitalize: false });\n return `${interrupt} interrupt · ${clear}/${exit} clear/exit · / commands · ! bash · ${tools} tools`;\n }\n\n render(width: number): string[] {\n if (this.customComponent) {\n const rendered = this.customComponent.render(width);\n return rendered.map((line) => truncateToWidth(line, width, theme.dim('…')));\n }\n\n const { version, connectionLabel, sessionKey, showHints } = this.getModel();\n const title = theme.header(`xopc tui v${version} — ${connectionLabel} — ${sessionKey}`);\n const lines = [truncateToWidth(title, width, theme.dim('…'))];\n\n if (showHints) {\n lines.push(truncateToWidth(theme.dim(this.getCompactHints()), width, theme.dim('…')));\n lines.push(truncateToWidth(theme.dim(STARTUP_HELP_HINT), width, theme.dim('…')));\n }\n\n for (const extLine of this.extensionLines) {\n lines.push(truncateToWidth(theme.dim(extLine), width, theme.dim('…')));\n }\n for (const component of this.extensionComponents) {\n for (const line of component.render(width)) {\n lines.push(truncateToWidth(line, width, theme.dim('…')));\n }\n }\n\n return lines;\n }\n\n private customComponent: Component | undefined;\n}\n"],"mappings":";;;;AAaA,MAAM,gBACJ;AACF,MAAM,oBAAoB;;AAG1B,IAAa,YAAb,MAA4C;CAC1C,iBAAmC,EAAE;CACrC,sBAA2C,EAAE;CAE7C,YACE,UACA,aACA;AAFiB,OAAA,WAAA;AACA,OAAA,cAAA;;CAGnB,kBAAkB,OAAuB;AACvC,OAAK,iBAAiB;;CAGxB,uBAAuB,YAA+B;AACpD,OAAK,sBAAsB;;CAG7B,mBAAmB,WAAwC;AACzD,OAAK,kBAAkB;;CAGzB,aAAmB;CAEnB,kBAAkC;AAChC,MAAI,CAAC,KAAK,YAAa,QAAO;AAK9B,SAAO,GAJW,aAAa,KAAK,aAAa,iBAAiB,EAAE,YAAY,OAAO,CAIpE,CAAC,eAHN,aAAa,KAAK,aAAa,aAAa,EAAE,YAAY,OAAO,CAGvC,CAAC,GAF5B,aAAa,KAAK,aAAa,YAAY,EAAE,YAAY,OAAO,CAE7B,CAAC,sCADnC,aAAa,KAAK,aAAa,oBAAoB,EAAE,YAAY,OAAO,CACM,CAAC;;CAG/F,OAAO,OAAyB;AAC9B,MAAI,KAAK,gBAEP,QADiB,KAAK,gBAAgB,OAAO,MAC9B,CAAC,KAAK,SAAS,gBAAgB,MAAM,OAAO,MAAM,IAAI,IAAI,CAAC,CAAC;EAG7E,MAAM,EAAE,SAAS,iBAAiB,YAAY,cAAc,KAAK,UAAU;EAE3E,MAAM,QAAQ,CAAC,gBADD,MAAM,OAAO,aAAa,QAAQ,KAAK,gBAAgB,KAAK,aACtC,EAAE,OAAO,MAAM,IAAI,IAAI,CAAC,CAAC;AAE7D,MAAI,WAAW;AACb,SAAM,KAAK,gBAAgB,MAAM,IAAI,KAAK,iBAAiB,CAAC,EAAE,OAAO,MAAM,IAAI,IAAI,CAAC,CAAC;AACrF,SAAM,KAAK,gBAAgB,MAAM,IAAI,kBAAkB,EAAE,OAAO,MAAM,IAAI,IAAI,CAAC,CAAC;;AAGlF,OAAK,MAAM,WAAW,KAAK,eACzB,OAAM,KAAK,gBAAgB,MAAM,IAAI,QAAQ,EAAE,OAAO,MAAM,IAAI,IAAI,CAAC,CAAC;AAExE,OAAK,MAAM,aAAa,KAAK,oBAC3B,MAAK,MAAM,QAAQ,UAAU,OAAO,MAAM,CACxC,OAAM,KAAK,gBAAgB,MAAM,OAAO,MAAM,IAAI,IAAI,CAAC,CAAC;AAI5D,SAAO;;CAGT"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { resolveStateDir } from "../config/paths-state.js";
|
|
2
2
|
import { resolveEffectiveAgentProfileForSession } from "../config/agent-profile.js";
|
|
3
|
-
import { init_paths, resolveAgentProfileDir, resolveBundledSkillsDir } from "../config/paths.js";
|
|
3
|
+
import { init_paths, resolveAgentProfileDir, resolveBundledSkillsDir, resolveUserProfilePath } from "../config/paths.js";
|
|
4
4
|
import { resolveBootstrapFilesSync } from "../agent/bootstrap/bootstrap-files.js";
|
|
5
5
|
import { createWorkflowCatalog } from "../agent/workflow/catalog.js";
|
|
6
6
|
import { createSkillConfigManager, isSkillEnabled } from "../agent/skills/config.js";
|
|
@@ -20,6 +20,7 @@ function collectTuiStartupResources(config, sessionKey) {
|
|
|
20
20
|
const workspaceDir = profile.resolvedWorkspacePath || getWorkspacePath(config);
|
|
21
21
|
const context = uniqueSorted(resolveBootstrapFilesSync({
|
|
22
22
|
profileDir,
|
|
23
|
+
userProfilePath: resolveUserProfilePath(),
|
|
23
24
|
sessionKey
|
|
24
25
|
}).filter((file) => !file.missing).map((file) => file.name || basename(file.path)));
|
|
25
26
|
const skillsConfig = createSkillConfigManager(resolveStateDir()).load();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tui-startup-resources.js","names":[],"sources":["../../../src/tui/tui-startup-resources.ts"],"sourcesContent":["import { basename } from 'node:path';\n\nimport { resolveBootstrapFilesSync } from '../agent/bootstrap/bootstrap-files.js';\nimport { loadSkills } from '../agent/skills/index.js';\nimport { isSkillEnabled, createSkillConfigManager } from '../agent/skills/config.js';\nimport { createWorkflowCatalog } from '../agent/workflow/catalog.js';\nimport { resolveEffectiveAgentProfileForSession } from '../config/agent-profile.js';\nimport { getWorkspacePath } from '../config/index.js';\nimport {
|
|
1
|
+
{"version":3,"file":"tui-startup-resources.js","names":[],"sources":["../../../src/tui/tui-startup-resources.ts"],"sourcesContent":["import { basename } from 'node:path';\n\nimport { resolveBootstrapFilesSync } from '../agent/bootstrap/bootstrap-files.js';\nimport { loadSkills } from '../agent/skills/index.js';\nimport { isSkillEnabled, createSkillConfigManager } from '../agent/skills/config.js';\nimport { createWorkflowCatalog } from '../agent/workflow/catalog.js';\nimport { resolveEffectiveAgentProfileForSession } from '../config/agent-profile.js';\nimport { getWorkspacePath } from '../config/index.js';\nimport {\n resolveAgentProfileDir,\n resolveBundledSkillsDir,\n resolveStateDir,\n resolveUserProfilePath,\n} from '../config/paths.js';\nimport { listConnectorInstances } from '../connectors/instances.js';\nimport type { Config } from '../config/schema.js';\nimport type { TuiStartupResources } from './tui-backend.js';\n\nfunction uniqueSorted(values: Iterable<string>): string[] {\n return [...new Set([...values].map((value) => value.trim()).filter(Boolean))]\n .sort((a, b) => a.localeCompare(b));\n}\n\nexport function collectTuiStartupResources(config: Config, sessionKey?: string): TuiStartupResources {\n const profile = resolveEffectiveAgentProfileForSession(config, sessionKey);\n const profileDir = resolveAgentProfileDir(config, profile.agentId);\n const workspaceDir = profile.resolvedWorkspacePath || getWorkspacePath(config);\n\n const context = uniqueSorted(\n resolveBootstrapFilesSync({ profileDir, userProfilePath: resolveUserProfilePath(), sessionKey })\n .filter((file) => !file.missing)\n .map((file) => file.name || basename(file.path)),\n );\n\n const skillsConfig = createSkillConfigManager(resolveStateDir()).load();\n const skills = uniqueSorted(\n loadSkills({\n workspaceDir,\n builtinDir: resolveBundledSkillsDir() ?? undefined,\n }).skills\n .filter((skill) => !skill.disableModelInvocation && isSkillEnabled(skill, skillsConfig))\n .map((skill) => skill.name),\n );\n\n const workflows = uniqueSorted(createWorkflowCatalog().list().map((entry) => entry.name));\n const connectors = uniqueSorted(\n listConnectorInstances(config).map((instance) => instance.displayName || instance.connectorId || instance.instanceId),\n );\n\n return { context, skills, workflows, connectors };\n}\n"],"mappings":";;;;;;;;;;;;YAa4B;AAK5B,SAAS,aAAa,QAAoC;AACxD,QAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,OAAO,CAAC,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC,OAAO,QAAQ,CAAC,CAAC,CAC1E,MAAM,GAAG,MAAM,EAAE,cAAc,EAAE,CAAC;;AAGvC,SAAgB,2BAA2B,QAAgB,YAA0C;CACnG,MAAM,UAAU,uCAAuC,QAAQ,WAAW;CAC1E,MAAM,aAAa,uBAAuB,QAAQ,QAAQ,QAAQ;CAClE,MAAM,eAAe,QAAQ,yBAAyB,iBAAiB,OAAO;CAE9E,MAAM,UAAU,aACd,0BAA0B;EAAE;EAAY,iBAAiB,wBAAwB;EAAE;EAAY,CAAC,CAC7F,QAAQ,SAAS,CAAC,KAAK,QAAQ,CAC/B,KAAK,SAAS,KAAK,QAAQ,SAAS,KAAK,KAAK,CAAC,CACnD;CAED,MAAM,eAAe,yBAAyB,iBAAiB,CAAC,CAAC,MAAM;AAevE,QAAO;EAAE;EAAS,QAdH,aACb,WAAW;GACT;GACA,YAAY,yBAAyB,IAAI,KAAA;GAC1C,CAAC,CAAC,OACA,QAAQ,UAAU,CAAC,MAAM,0BAA0B,eAAe,OAAO,aAAa,CAAC,CACvF,KAAK,UAAU,MAAM,KAAK,CAQP;EAAE,WALR,aAAa,uBAAuB,CAAC,MAAM,CAAC,KAAK,UAAU,MAAM,KAAK,CAKrD;EAAE,YAJlB,aACjB,uBAAuB,OAAO,CAAC,KAAK,aAAa,SAAS,eAAe,SAAS,eAAe,SAAS,WAAW,CAGxE;EAAE"}
|
|
@@ -37,18 +37,18 @@ function formatTuiStartupText(input) {
|
|
|
37
37
|
lines.push("");
|
|
38
38
|
}
|
|
39
39
|
lines.push(compactKeyLine(input.keybindings));
|
|
40
|
-
lines.push("Press /start to show full startup help
|
|
41
|
-
lines.push("");
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
40
|
+
lines.push("Press /start to show full startup help.");
|
|
41
|
+
if (expanded) lines.push("", "xopc can use skills, workflows, connectors, and project context. Ask it how to automate or connect services.", "", "Ask xopc:", " \"what workflows are available?\"", " \"use a connector to check issues or docs\"", " \"what skills can help with this task?\"");
|
|
42
|
+
if (expanded) {
|
|
43
|
+
lines.push("");
|
|
44
|
+
lines.push(...formatResourceSection("Context", input.resources?.context, { expanded }));
|
|
45
|
+
lines.push("");
|
|
46
|
+
lines.push(...formatResourceSection("Skills", input.resources?.skills, { expanded }));
|
|
47
|
+
lines.push("");
|
|
48
|
+
lines.push(...formatResourceSection("Workflows", input.resources?.workflows, { expanded }));
|
|
49
|
+
lines.push("");
|
|
50
|
+
lines.push(...formatResourceSection("Connectors", input.resources?.connectors, { expanded }));
|
|
51
|
+
}
|
|
52
52
|
if (expanded) lines.push("", "Useful commands:", " /help — Show all commands and shortcuts", " /workflow list — List saved workflows", " /models — List available models", " /settings — Open TUI settings", " /export — Export current session");
|
|
53
53
|
return lines.join("\n");
|
|
54
54
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tui-startup-text.js","names":[],"sources":["../../../src/tui/tui-startup-text.ts"],"sourcesContent":["import type { KeybindingsManager } from '@earendil-works/pi-tui';\n\nimport { formatKeyIds } from './format-tui-hotkeys.js';\nimport { formatModelLabel } from './tui-command-formatters.js';\nimport type { TuiStartupResources } from './tui-backend.js';\nimport type { TuiState } from './tui-types.js';\n\nconst DEFAULT_LIMIT = 6;\n\nfunction keyLabel(\n keybindings: KeybindingsManager | undefined,\n id: Parameters<KeybindingsManager['getKeys']>[0],\n fallback: string,\n): string {\n return keybindings ? formatKeyIds(keybindings, id, { capitalize: false }) : fallback;\n}\n\nfunction compactKeyLine(keybindings?: KeybindingsManager): string {\n const interrupt = keyLabel(keybindings, 'app.interrupt', 'escape');\n const clear = keyLabel(keybindings, 'app.clear', 'ctrl+c');\n const exit = keyLabel(keybindings, 'app.exit', 'ctrl+d');\n const tools = keyLabel(keybindings, 'app.tools.expand', 'ctrl+o');\n return `${interrupt} interrupt · ${clear}/${exit} clear/exit · / commands · ! bash · ${tools} tools`;\n}\n\nfunction formatResourceSection(\n title: string,\n values: readonly string[] | undefined,\n options: { expanded: boolean },\n): string[] {\n const list = [...new Set((values ?? []).map((v) => v.trim()).filter(Boolean))]\n .sort((a, b) => a.localeCompare(b));\n const lines = [`[${title}]`];\n if (list.length === 0) {\n lines.push(' none');\n return lines;\n }\n if (!options.expanded) {\n const visible = list.slice(0, DEFAULT_LIMIT);\n const suffix = list.length > visible.length ? `, ... ${list.length - visible.length} more` : '';\n lines.push(` ${visible.join(', ')}${suffix}`);\n return lines;\n }\n const visible = list.slice(0, 30);\n for (const value of visible) {\n lines.push(` ${value}`);\n }\n if (list.length > visible.length) {\n lines.push(` ... ${list.length - visible.length} more`);\n }\n return lines;\n}\n\nexport function formatTuiStartupText(input: {\n state: TuiState;\n isLocal: boolean;\n keybindings?: KeybindingsManager;\n resources?: TuiStartupResources;\n expanded?: boolean;\n}): string {\n const expanded = input.expanded === true;\n const lines: string[] = [];\n\n if (expanded) {\n lines.push('xopc TUI', '');\n lines.push(`Session: ${input.state.currentSessionKey}`);\n lines.push(`Mode: ${input.isLocal ? 'local embedded' : 'gateway'}`);\n lines.push(`Model: ${formatModelLabel(input.state)}`);\n lines.push('');\n }\n\n lines.push(compactKeyLine(input.keybindings));\n lines.push('Press /start to show full startup help
|
|
1
|
+
{"version":3,"file":"tui-startup-text.js","names":[],"sources":["../../../src/tui/tui-startup-text.ts"],"sourcesContent":["import type { KeybindingsManager } from '@earendil-works/pi-tui';\n\nimport { formatKeyIds } from './format-tui-hotkeys.js';\nimport { formatModelLabel } from './tui-command-formatters.js';\nimport type { TuiStartupResources } from './tui-backend.js';\nimport type { TuiState } from './tui-types.js';\n\nconst DEFAULT_LIMIT = 6;\n\nfunction keyLabel(\n keybindings: KeybindingsManager | undefined,\n id: Parameters<KeybindingsManager['getKeys']>[0],\n fallback: string,\n): string {\n return keybindings ? formatKeyIds(keybindings, id, { capitalize: false }) : fallback;\n}\n\nfunction compactKeyLine(keybindings?: KeybindingsManager): string {\n const interrupt = keyLabel(keybindings, 'app.interrupt', 'escape');\n const clear = keyLabel(keybindings, 'app.clear', 'ctrl+c');\n const exit = keyLabel(keybindings, 'app.exit', 'ctrl+d');\n const tools = keyLabel(keybindings, 'app.tools.expand', 'ctrl+o');\n return `${interrupt} interrupt · ${clear}/${exit} clear/exit · / commands · ! bash · ${tools} tools`;\n}\n\nfunction formatResourceSection(\n title: string,\n values: readonly string[] | undefined,\n options: { expanded: boolean },\n): string[] {\n const list = [...new Set((values ?? []).map((v) => v.trim()).filter(Boolean))]\n .sort((a, b) => a.localeCompare(b));\n const lines = [`[${title}]`];\n if (list.length === 0) {\n lines.push(' none');\n return lines;\n }\n if (!options.expanded) {\n const visible = list.slice(0, DEFAULT_LIMIT);\n const suffix = list.length > visible.length ? `, ... ${list.length - visible.length} more` : '';\n lines.push(` ${visible.join(', ')}${suffix}`);\n return lines;\n }\n const visible = list.slice(0, 30);\n for (const value of visible) {\n lines.push(` ${value}`);\n }\n if (list.length > visible.length) {\n lines.push(` ... ${list.length - visible.length} more`);\n }\n return lines;\n}\n\nexport function formatTuiStartupText(input: {\n state: TuiState;\n isLocal: boolean;\n keybindings?: KeybindingsManager;\n resources?: TuiStartupResources;\n expanded?: boolean;\n}): string {\n const expanded = input.expanded === true;\n const lines: string[] = [];\n\n if (expanded) {\n lines.push('xopc TUI', '');\n lines.push(`Session: ${input.state.currentSessionKey}`);\n lines.push(`Mode: ${input.isLocal ? 'local embedded' : 'gateway'}`);\n lines.push(`Model: ${formatModelLabel(input.state)}`);\n lines.push('');\n }\n\n lines.push(compactKeyLine(input.keybindings));\n lines.push('Press /start to show full startup help.');\n\n if (expanded) {\n lines.push(\n '',\n 'xopc can use skills, workflows, connectors, and project context. Ask it how to automate or connect services.',\n '',\n 'Ask xopc:',\n ' \"what workflows are available?\"',\n ' \"use a connector to check issues or docs\"',\n ' \"what skills can help with this task?\"',\n );\n }\n\n if (expanded) {\n lines.push('');\n lines.push(...formatResourceSection('Context', input.resources?.context, { expanded }));\n lines.push('');\n lines.push(...formatResourceSection('Skills', input.resources?.skills, { expanded }));\n lines.push('');\n lines.push(...formatResourceSection('Workflows', input.resources?.workflows, { expanded }));\n lines.push('');\n lines.push(...formatResourceSection('Connectors', input.resources?.connectors, { expanded }));\n }\n\n if (expanded) {\n lines.push(\n '',\n 'Useful commands:',\n ' /help — Show all commands and shortcuts',\n ' /workflow list — List saved workflows',\n ' /models — List available models',\n ' /settings — Open TUI settings',\n ' /export — Export current session',\n );\n }\n\n return lines.join('\\n');\n}\n"],"mappings":";;;AAOA,MAAM,gBAAgB;AAEtB,SAAS,SACP,aACA,IACA,UACQ;AACR,QAAO,cAAc,aAAa,aAAa,IAAI,EAAE,YAAY,OAAO,CAAC,GAAG;;AAG9E,SAAS,eAAe,aAA0C;AAKhE,QAAO,GAJW,SAAS,aAAa,iBAAiB,SAItC,CAAC,eAHN,SAAS,aAAa,aAAa,SAGT,CAAC,GAF5B,SAAS,aAAa,YAAY,SAEC,CAAC,sCADnC,SAAS,aAAa,oBAAoB,SACoC,CAAC;;AAG/F,SAAS,sBACP,OACA,QACA,SACU;CACV,MAAM,OAAO,CAAC,GAAG,IAAI,KAAK,UAAU,EAAE,EAAE,KAAK,MAAM,EAAE,MAAM,CAAC,CAAC,OAAO,QAAQ,CAAC,CAAC,CAC3E,MAAM,GAAG,MAAM,EAAE,cAAc,EAAE,CAAC;CACrC,MAAM,QAAQ,CAAC,IAAI,MAAM,GAAG;AAC5B,KAAI,KAAK,WAAW,GAAG;AACrB,QAAM,KAAK,SAAS;AACpB,SAAO;;AAET,KAAI,CAAC,QAAQ,UAAU;EACrB,MAAM,UAAU,KAAK,MAAM,GAAG,cAAc;EAC5C,MAAM,SAAS,KAAK,SAAS,QAAQ,SAAS,SAAS,KAAK,SAAS,QAAQ,OAAO,SAAS;AAC7F,QAAM,KAAK,KAAK,QAAQ,KAAK,KAAK,GAAG,SAAS;AAC9C,SAAO;;CAET,MAAM,UAAU,KAAK,MAAM,GAAG,GAAG;AACjC,MAAK,MAAM,SAAS,QAClB,OAAM,KAAK,KAAK,QAAQ;AAE1B,KAAI,KAAK,SAAS,QAAQ,OACxB,OAAM,KAAK,SAAS,KAAK,SAAS,QAAQ,OAAO,OAAO;AAE1D,QAAO;;AAGT,SAAgB,qBAAqB,OAM1B;CACT,MAAM,WAAW,MAAM,aAAa;CACpC,MAAM,QAAkB,EAAE;AAE1B,KAAI,UAAU;AACZ,QAAM,KAAK,YAAY,GAAG;AAC1B,QAAM,KAAK,YAAY,MAAM,MAAM,oBAAoB;AACvD,QAAM,KAAK,SAAS,MAAM,UAAU,mBAAmB,YAAY;AACnE,QAAM,KAAK,UAAU,iBAAiB,MAAM,MAAM,GAAG;AACrD,QAAM,KAAK,GAAG;;AAGhB,OAAM,KAAK,eAAe,MAAM,YAAY,CAAC;AAC7C,OAAM,KAAK,0CAA0C;AAErD,KAAI,SACF,OAAM,KACJ,IACA,gHACA,IACA,aACA,uCACA,iDACA,6CACD;AAGH,KAAI,UAAU;AACZ,QAAM,KAAK,GAAG;AACd,QAAM,KAAK,GAAG,sBAAsB,WAAW,MAAM,WAAW,SAAS,EAAE,UAAU,CAAC,CAAC;AACvF,QAAM,KAAK,GAAG;AACd,QAAM,KAAK,GAAG,sBAAsB,UAAU,MAAM,WAAW,QAAQ,EAAE,UAAU,CAAC,CAAC;AACrF,QAAM,KAAK,GAAG;AACd,QAAM,KAAK,GAAG,sBAAsB,aAAa,MAAM,WAAW,WAAW,EAAE,UAAU,CAAC,CAAC;AAC3F,QAAM,KAAK,GAAG;AACd,QAAM,KAAK,GAAG,sBAAsB,cAAc,MAAM,WAAW,YAAY,EAAE,UAAU,CAAC,CAAC;;AAG/F,KAAI,SACF,OAAM,KACJ,IACA,oBACA,6CACA,2CACA,qCACA,mCACA,qCACD;AAGH,QAAO,MAAM,KAAK,KAAK"}
|
package/dist/src/tui/tui.js
CHANGED
|
@@ -1976,9 +1976,12 @@ async function runTui(opts) {
|
|
|
1976
1976
|
}
|
|
1977
1977
|
await refreshSessionInfoWithBorder();
|
|
1978
1978
|
await refreshModelChoices();
|
|
1979
|
-
await refreshStartupResources();
|
|
1980
1979
|
await loadSessionHistory({ merge: true });
|
|
1981
1980
|
showStartupCardOnce();
|
|
1981
|
+
refreshStartupResources().then(() => {
|
|
1982
|
+
updateFooter();
|
|
1983
|
+
tui.requestRender();
|
|
1984
|
+
});
|
|
1982
1985
|
if (state.activeRunId) recoverActiveRunFromHistory("broadcast reconnect");
|
|
1983
1986
|
updateHeader();
|
|
1984
1987
|
updateFooter();
|