@geminixiang/mikan 1.0.0-beta.44 → 1.0.0-beta.46
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/CHANGELOG.md +32 -0
- package/README.md +12 -19
- package/dist/adapter.d.ts +1 -1
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js.map +1 -1
- package/dist/adapters/discord/bot.d.ts +3 -1
- package/dist/adapters/discord/bot.d.ts.map +1 -1
- package/dist/adapters/discord/bot.js +143 -141
- package/dist/adapters/discord/bot.js.map +1 -1
- package/dist/adapters/discord/context.d.ts +2 -8
- package/dist/adapters/discord/context.d.ts.map +1 -1
- package/dist/adapters/discord/context.js +1 -1
- package/dist/adapters/discord/context.js.map +1 -1
- package/dist/adapters/discord/format.d.ts.map +1 -1
- package/dist/adapters/discord/format.js +2 -4
- package/dist/adapters/discord/format.js.map +1 -1
- package/dist/adapters/github/bot.d.ts.map +1 -1
- package/dist/adapters/github/bot.js +1 -4
- package/dist/adapters/github/bot.js.map +1 -1
- package/dist/adapters/github/context.d.ts +2 -9
- package/dist/adapters/github/context.d.ts.map +1 -1
- package/dist/adapters/github/context.js.map +1 -1
- package/dist/adapters/github/github-ops.d.ts +4 -17
- package/dist/adapters/github/github-ops.d.ts.map +1 -1
- package/dist/adapters/github/github-ops.js +17 -86
- package/dist/adapters/github/github-ops.js.map +1 -1
- package/dist/adapters/github/tool-pack.d.ts.map +1 -1
- package/dist/adapters/github/tool-pack.js +0 -1
- package/dist/adapters/github/tool-pack.js.map +1 -1
- package/dist/adapters/github/tools/checks.d.ts +0 -1
- package/dist/adapters/github/tools/checks.d.ts.map +1 -1
- package/dist/adapters/github/tools/checks.js +3 -22
- package/dist/adapters/github/tools/checks.js.map +1 -1
- package/dist/adapters/github/types.d.ts +0 -33
- package/dist/adapters/github/types.d.ts.map +1 -1
- package/dist/adapters/github/types.js.map +1 -1
- package/dist/adapters/progressive-renderer.d.ts.map +1 -1
- package/dist/adapters/progressive-renderer.js +311 -318
- package/dist/adapters/progressive-renderer.js.map +1 -1
- package/dist/adapters/slack/blocks.d.ts.map +1 -1
- package/dist/adapters/slack/blocks.js +1 -4
- package/dist/adapters/slack/blocks.js.map +1 -1
- package/dist/adapters/slack/bot.d.ts +12 -0
- package/dist/adapters/slack/bot.d.ts.map +1 -1
- package/dist/adapters/slack/bot.js +158 -178
- package/dist/adapters/slack/bot.js.map +1 -1
- package/dist/adapters/slack/context.d.ts +2 -8
- package/dist/adapters/slack/context.d.ts.map +1 -1
- package/dist/adapters/slack/context.js +1 -1
- package/dist/adapters/slack/context.js.map +1 -1
- package/dist/adapters/slack/response-lifecycle.d.ts.map +1 -1
- package/dist/adapters/slack/response-lifecycle.js +135 -123
- package/dist/adapters/slack/response-lifecycle.js.map +1 -1
- package/dist/adapters/slack/stream-limits.d.ts +1 -1
- package/dist/adapters/slack/stream-limits.js +1 -1
- package/dist/adapters/slack/stream-limits.js.map +1 -1
- package/dist/adapters/slack/types.d.ts +4 -0
- package/dist/adapters/slack/types.d.ts.map +1 -1
- package/dist/adapters/slack/types.js.map +1 -1
- package/dist/adapters/telegram/context.d.ts +2 -8
- package/dist/adapters/telegram/context.d.ts.map +1 -1
- package/dist/adapters/telegram/context.js +1 -2
- package/dist/adapters/telegram/context.js.map +1 -1
- package/dist/adapters/types.d.ts +11 -5
- package/dist/adapters/types.d.ts.map +1 -1
- package/dist/adapters/types.js.map +1 -1
- package/dist/agent/catalog.d.ts +6 -18
- package/dist/agent/catalog.d.ts.map +1 -1
- package/dist/agent/catalog.js +19 -155
- package/dist/agent/catalog.js.map +1 -1
- package/dist/agent/execution.d.ts +0 -7
- package/dist/agent/execution.d.ts.map +1 -1
- package/dist/agent/execution.js +23 -21
- package/dist/agent/execution.js.map +1 -1
- package/dist/agent/presenter.d.ts +7 -9
- package/dist/agent/presenter.d.ts.map +1 -1
- package/dist/agent/presenter.js +355 -357
- package/dist/agent/presenter.js.map +1 -1
- package/dist/agent/prompt.d.ts +5 -12
- package/dist/agent/prompt.d.ts.map +1 -1
- package/dist/agent/prompt.js +56 -81
- package/dist/agent/prompt.js.map +1 -1
- package/dist/agent/runner.d.ts +0 -7
- package/dist/agent/runner.d.ts.map +1 -1
- package/dist/agent/runner.js +255 -308
- package/dist/agent/runner.js.map +1 -1
- package/dist/agent/types.d.ts +21 -17
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/types.js +0 -1
- package/dist/agent/types.js.map +1 -1
- package/dist/cli/boot.d.ts.map +1 -1
- package/dist/cli/boot.js +7 -10
- package/dist/cli/boot.js.map +1 -1
- package/dist/cli/onboard.d.ts +10 -0
- package/dist/cli/onboard.d.ts.map +1 -0
- package/dist/cli/onboard.js +237 -0
- package/dist/cli/onboard.js.map +1 -0
- package/dist/cli/types.d.ts +8 -3
- package/dist/cli/types.d.ts.map +1 -1
- package/dist/cli/types.js.map +1 -1
- package/dist/commands/auto-reply.d.ts.map +1 -1
- package/dist/commands/auto-reply.js +5 -12
- package/dist/commands/auto-reply.js.map +1 -1
- package/dist/commands/manifest.d.ts.map +1 -1
- package/dist/commands/manifest.js +9 -16
- package/dist/commands/manifest.js.map +1 -1
- package/dist/commands/new.d.ts.map +1 -1
- package/dist/commands/new.js +15 -10
- package/dist/commands/new.js.map +1 -1
- package/dist/commands/registry.d.ts.map +1 -1
- package/dist/commands/registry.js +0 -2
- package/dist/commands/registry.js.map +1 -1
- package/dist/commands/sandbox.d.ts.map +1 -1
- package/dist/commands/sandbox.js +89 -70
- package/dist/commands/sandbox.js.map +1 -1
- package/dist/commands/session-view.js +9 -2
- package/dist/commands/session-view.js.map +1 -1
- package/dist/commands/types.d.ts +4 -3
- package/dist/commands/types.d.ts.map +1 -1
- package/dist/commands/types.js.map +1 -1
- package/dist/config.d.ts +13 -17
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +39 -37
- package/dist/config.js.map +1 -1
- package/dist/dream/index.d.ts +20 -0
- package/dist/dream/index.d.ts.map +1 -0
- package/dist/dream/index.js +253 -0
- package/dist/dream/index.js.map +1 -0
- package/dist/dream/types.d.ts +30 -0
- package/dist/dream/types.d.ts.map +1 -0
- package/dist/dream/types.js.map +1 -0
- package/dist/env-manifest.d.ts.map +1 -1
- package/dist/env-manifest.js +2 -34
- package/dist/env-manifest.js.map +1 -1
- package/dist/execution-resolver.d.ts +9 -2
- package/dist/execution-resolver.d.ts.map +1 -1
- package/dist/execution-resolver.js +26 -27
- package/dist/execution-resolver.js.map +1 -1
- package/dist/harness/event-format.d.ts +1 -2
- package/dist/harness/event-format.d.ts.map +1 -1
- package/dist/harness/event-format.js +9 -15
- package/dist/harness/event-format.js.map +1 -1
- package/dist/harness/index.d.ts +1 -7
- package/dist/harness/index.d.ts.map +1 -1
- package/dist/harness/index.js +0 -4
- package/dist/harness/index.js.map +1 -1
- package/dist/harness/models.d.ts.map +1 -1
- package/dist/harness/models.js +15 -35
- package/dist/harness/models.js.map +1 -1
- package/dist/harness/runner.d.ts +7 -16
- package/dist/harness/runner.d.ts.map +1 -1
- package/dist/harness/runner.js +13 -131
- package/dist/harness/runner.js.map +1 -1
- package/dist/harness/session-store.d.ts +1 -1
- package/dist/harness/session-store.d.ts.map +1 -1
- package/dist/harness/session-store.js +1 -1
- package/dist/harness/session-store.js.map +1 -1
- package/dist/harness/subagent-runner.d.ts +2 -2
- package/dist/harness/subagent-runner.d.ts.map +1 -1
- package/dist/harness/subagent-runner.js +157 -162
- package/dist/harness/subagent-runner.js.map +1 -1
- package/dist/harness/types.d.ts +1 -13
- package/dist/harness/types.d.ts.map +1 -1
- package/dist/harness/types.js.map +1 -1
- package/dist/index.d.ts +3 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -5
- package/dist/index.js.map +1 -1
- package/dist/main.js +31 -212
- package/dist/main.js.map +1 -1
- package/dist/mcp/catalog.d.ts +5 -0
- package/dist/mcp/catalog.d.ts.map +1 -0
- package/dist/mcp/catalog.js +135 -0
- package/dist/mcp/catalog.js.map +1 -0
- package/dist/mcp/loader.d.ts.map +1 -1
- package/dist/mcp/loader.js +3 -6
- package/dist/mcp/loader.js.map +1 -1
- package/dist/mcp/types.d.ts +22 -0
- package/dist/mcp/types.d.ts.map +1 -1
- package/dist/mcp/types.js.map +1 -1
- package/dist/office/index.d.ts +136 -8
- package/dist/office/index.d.ts.map +1 -1
- package/dist/office/index.js +1050 -9
- package/dist/office/index.js.map +1 -1
- package/dist/office/types.d.ts +19 -1
- package/dist/office/types.d.ts.map +1 -1
- package/dist/office/types.js.map +1 -1
- package/dist/packages/index.d.ts +168 -5
- package/dist/packages/index.d.ts.map +1 -1
- package/dist/packages/index.js +749 -5
- package/dist/packages/index.js.map +1 -1
- package/dist/packages/types.d.ts +1 -5
- package/dist/packages/types.d.ts.map +1 -1
- package/dist/packages/types.js.map +1 -1
- package/dist/provisioner.d.ts +2 -9
- package/dist/provisioner.d.ts.map +1 -1
- package/dist/provisioner.js +3 -12
- package/dist/provisioner.js.map +1 -1
- package/dist/runtime/conversation-runtime.d.ts.map +1 -1
- package/dist/runtime/conversation-runtime.js +117 -380
- package/dist/runtime/conversation-runtime.js.map +1 -1
- package/dist/runtime/session-lifecycle.d.ts +32 -18
- package/dist/runtime/session-lifecycle.d.ts.map +1 -1
- package/dist/runtime/session-lifecycle.js +159 -46
- package/dist/runtime/session-lifecycle.js.map +1 -1
- package/dist/runtime/types.d.ts +4 -26
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/runtime/types.js.map +1 -1
- package/dist/sandbox/cloudflare.d.ts.map +1 -1
- package/dist/sandbox/cloudflare.js +2 -3
- package/dist/sandbox/cloudflare.js.map +1 -1
- package/dist/sandbox/container.d.ts +0 -1
- package/dist/sandbox/container.d.ts.map +1 -1
- package/dist/sandbox/container.js +2 -3
- package/dist/sandbox/container.js.map +1 -1
- package/dist/sandbox/host.d.ts.map +1 -1
- package/dist/sandbox/host.js +4 -2
- package/dist/sandbox/host.js.map +1 -1
- package/dist/sandbox/identity.d.ts +10 -19
- package/dist/sandbox/identity.d.ts.map +1 -1
- package/dist/sandbox/identity.js +8 -8
- package/dist/sandbox/identity.js.map +1 -1
- package/dist/sandbox/index.d.ts +7 -10
- package/dist/sandbox/index.d.ts.map +1 -1
- package/dist/sandbox/index.js +45 -16
- package/dist/sandbox/index.js.map +1 -1
- package/dist/sandbox/types.d.ts +13 -45
- package/dist/sandbox/types.d.ts.map +1 -1
- package/dist/sandbox/types.js.map +1 -1
- package/dist/sandbox/utils.d.ts +5 -0
- package/dist/sandbox/utils.d.ts.map +1 -1
- package/dist/sandbox/utils.js +10 -0
- package/dist/sandbox/utils.js.map +1 -1
- package/dist/sessions/chat-history-sync.d.ts +1 -3
- package/dist/sessions/chat-history-sync.d.ts.map +1 -1
- package/dist/sessions/chat-history-sync.js +58 -11
- package/dist/sessions/chat-history-sync.js.map +1 -1
- package/dist/sessions/migrate-v3.d.ts +2 -5
- package/dist/sessions/migrate-v3.d.ts.map +1 -1
- package/dist/sessions/migrate-v3.js +34 -4
- package/dist/sessions/migrate-v3.js.map +1 -1
- package/dist/sessions/store.d.ts +5 -6
- package/dist/sessions/store.d.ts.map +1 -1
- package/dist/sessions/store.js +62 -2
- package/dist/sessions/store.js.map +1 -1
- package/dist/sessions/types.d.ts +9 -0
- package/dist/sessions/types.d.ts.map +1 -1
- package/dist/sessions/types.js.map +1 -1
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/bash.js +2 -10
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/read.js +2 -2
- package/dist/tools/read.js.map +1 -1
- package/dist/tools/sandbox.d.ts.map +1 -1
- package/dist/tools/sandbox.js +2 -3
- package/dist/tools/sandbox.js.map +1 -1
- package/dist/tools/subagent.d.ts +1 -1
- package/dist/tools/subagent.js.map +1 -1
- package/dist/types.d.ts +38 -127
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/vault/index.d.ts +9 -2
- package/dist/vault/index.d.ts.map +1 -1
- package/dist/vault/index.js +29 -5
- package/dist/vault/index.js.map +1 -1
- package/dist/web/admin/portal.d.ts +12 -1
- package/dist/web/admin/portal.d.ts.map +1 -1
- package/dist/web/admin/portal.js +437 -422
- package/dist/web/admin/portal.js.map +1 -1
- package/dist/web/admin/types.d.ts +2 -2
- package/dist/web/admin/types.d.ts.map +1 -1
- package/dist/web/admin/types.js.map +1 -1
- package/dist/web/login/portal.d.ts +7 -1
- package/dist/web/login/portal.d.ts.map +1 -1
- package/dist/web/login/portal.js +143 -78
- package/dist/web/login/portal.js.map +1 -1
- package/dist/web/portal-shell.d.ts +1 -0
- package/dist/web/portal-shell.d.ts.map +1 -1
- package/dist/web/portal-shell.js +12 -1
- package/dist/web/portal-shell.js.map +1 -1
- package/dist/web/server.d.ts +3 -3
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/server.js.map +1 -1
- package/dist/web/session-view/portal.d.ts +8 -1
- package/dist/web/session-view/portal.d.ts.map +1 -1
- package/dist/web/session-view/portal.js +609 -132
- package/dist/web/session-view/portal.js.map +1 -1
- package/dist/web/session-view/types.d.ts +8 -0
- package/dist/web/session-view/types.d.ts.map +1 -1
- package/dist/web/session-view/types.js.map +1 -1
- package/dist/workspace-projection/index.d.ts +14 -2
- package/dist/workspace-projection/index.d.ts.map +1 -1
- package/dist/workspace-projection/index.js +102 -14
- package/dist/workspace-projection/index.js.map +1 -1
- package/dist/workspace-projection/types.d.ts +14 -1
- package/dist/workspace-projection/types.d.ts.map +1 -1
- package/dist/workspace-projection/types.js.map +1 -1
- package/package.json +3 -5
- package/dist/adapters/github/cloudbuild.d.ts +0 -19
- package/dist/adapters/github/cloudbuild.d.ts.map +0 -1
- package/dist/adapters/github/cloudbuild.js +0 -71
- package/dist/adapters/github/cloudbuild.js.map +0 -1
- package/dist/adapters/github/gcp-auth.d.ts +0 -28
- package/dist/adapters/github/gcp-auth.d.ts.map +0 -1
- package/dist/adapters/github/gcp-auth.js +0 -196
- package/dist/adapters/github/gcp-auth.js.map +0 -1
- package/dist/agent/index.d.ts +0 -9
- package/dist/agent/index.d.ts.map +0 -1
- package/dist/agent/index.js +0 -8
- package/dist/agent/index.js.map +0 -1
- package/dist/cli/ext-dev.d.ts +0 -2
- package/dist/cli/ext-dev.d.ts.map +0 -1
- package/dist/cli/ext-dev.js +0 -188
- package/dist/cli/ext-dev.js.map +0 -1
- package/dist/cli/ext.d.ts +0 -2
- package/dist/cli/ext.d.ts.map +0 -1
- package/dist/cli/ext.js +0 -324
- package/dist/cli/ext.js.map +0 -1
- package/dist/commands/extensions.d.ts +0 -26
- package/dist/commands/extensions.d.ts.map +0 -1
- package/dist/commands/extensions.js +0 -103
- package/dist/commands/extensions.js.map +0 -1
- package/dist/extension-schedules.d.ts +0 -31
- package/dist/extension-schedules.d.ts.map +0 -1
- package/dist/extension-schedules.js +0 -229
- package/dist/extension-schedules.js.map +0 -1
- package/dist/harness/auth.d.ts +0 -17
- package/dist/harness/auth.d.ts.map +0 -1
- package/dist/harness/auth.js +0 -92
- package/dist/harness/auth.js.map +0 -1
- package/dist/harness/extensions/loader.d.ts +0 -40
- package/dist/harness/extensions/loader.d.ts.map +0 -1
- package/dist/harness/extensions/loader.js +0 -723
- package/dist/harness/extensions/loader.js.map +0 -1
- package/dist/harness/extensions/registry.d.ts +0 -155
- package/dist/harness/extensions/registry.d.ts.map +0 -1
- package/dist/harness/extensions/registry.js +0 -420
- package/dist/harness/extensions/registry.js.map +0 -1
- package/dist/harness/extensions/types.d.ts +0 -665
- package/dist/harness/extensions/types.d.ts.map +0 -1
- package/dist/harness/extensions/types.js.map +0 -1
- package/dist/office/address.d.ts +0 -31
- package/dist/office/address.d.ts.map +0 -1
- package/dist/office/address.js +0 -113
- package/dist/office/address.js.map +0 -1
- package/dist/office/layout.d.ts +0 -27
- package/dist/office/layout.d.ts.map +0 -1
- package/dist/office/layout.js +0 -97
- package/dist/office/layout.js.map +0 -1
- package/dist/office/migration.d.ts +0 -44
- package/dist/office/migration.d.ts.map +0 -1
- package/dist/office/migration.js +0 -255
- package/dist/office/migration.js.map +0 -1
- package/dist/office/registry.d.ts +0 -75
- package/dist/office/registry.d.ts.map +0 -1
- package/dist/office/registry.js +0 -646
- package/dist/office/registry.js.map +0 -1
- package/dist/packages/admin.d.ts +0 -17
- package/dist/packages/admin.d.ts.map +0 -1
- package/dist/packages/admin.js +0 -98
- package/dist/packages/admin.js.map +0 -1
- package/dist/packages/inspect.d.ts +0 -11
- package/dist/packages/inspect.d.ts.map +0 -1
- package/dist/packages/inspect.js +0 -98
- package/dist/packages/inspect.js.map +0 -1
- package/dist/packages/materialize.d.ts +0 -40
- package/dist/packages/materialize.d.ts.map +0 -1
- package/dist/packages/materialize.js +0 -297
- package/dist/packages/materialize.js.map +0 -1
- package/dist/packages/resolve.d.ts +0 -25
- package/dist/packages/resolve.d.ts.map +0 -1
- package/dist/packages/resolve.js +0 -208
- package/dist/packages/resolve.js.map +0 -1
- package/dist/packages/source.d.ts +0 -39
- package/dist/packages/source.d.ts.map +0 -1
- package/dist/packages/source.js +0 -189
- package/dist/packages/source.js.map +0 -1
- package/dist/sandbox/errors.d.ts +0 -6
- package/dist/sandbox/errors.d.ts.map +0 -1
- package/dist/sandbox/errors.js +0 -11
- package/dist/sandbox/errors.js.map +0 -1
- package/dist/sandbox/firecracker.d.ts +0 -18
- package/dist/sandbox/firecracker.d.ts.map +0 -1
- package/dist/sandbox/firecracker.js +0 -223
- package/dist/sandbox/firecracker.js.map +0 -1
- package/dist/sandbox/gondolin.d.ts +0 -57
- package/dist/sandbox/gondolin.d.ts.map +0 -1
- package/dist/sandbox/gondolin.js +0 -764
- package/dist/sandbox/gondolin.js.map +0 -1
- package/dist/sandbox/image.d.ts +0 -3
- package/dist/sandbox/image.d.ts.map +0 -1
- package/dist/sandbox/image.js +0 -29
- package/dist/sandbox/image.js.map +0 -1
- package/dist/sessions/conversation-log.d.ts +0 -9
- package/dist/sessions/conversation-log.d.ts.map +0 -1
- package/dist/sessions/conversation-log.js +0 -50
- package/dist/sessions/conversation-log.js.map +0 -1
- package/dist/sessions/rotation.d.ts +0 -2
- package/dist/sessions/rotation.d.ts.map +0 -1
- package/dist/sessions/rotation.js +0 -41
- package/dist/sessions/rotation.js.map +0 -1
- package/dist/vault/disabled.d.ts +0 -9
- package/dist/vault/disabled.d.ts.map +0 -1
- package/dist/vault/disabled.js +0 -25
- package/dist/vault/disabled.js.map +0 -1
- package/dist/web/admin/store.d.ts +0 -13
- package/dist/web/admin/store.d.ts.map +0 -1
- package/dist/web/admin/store.js +0 -14
- package/dist/web/admin/store.js.map +0 -1
- package/dist/web/login/store.d.ts +0 -8
- package/dist/web/login/store.d.ts.map +0 -1
- package/dist/web/login/store.js +0 -15
- package/dist/web/login/store.js.map +0 -1
- package/dist/web/session-view/service.d.ts +0 -6
- package/dist/web/session-view/service.d.ts.map +0 -1
- package/dist/web/session-view/service.js +0 -517
- package/dist/web/session-view/service.js.map +0 -1
- package/dist/web/session-view/store.d.ts +0 -8
- package/dist/web/session-view/store.d.ts.map +0 -1
- package/dist/web/session-view/store.js +0 -15
- package/dist/web/session-view/store.js.map +0 -1
- /package/dist/{harness/extensions → dream}/types.js +0 -0
|
@@ -1,13 +1,31 @@
|
|
|
1
|
+
import { dirname, join, resolve } from "node:path";
|
|
2
|
+
import { existsSync, readdirSync } from "node:fs";
|
|
3
|
+
import { SessionStore, } from "../../harness/index.js";
|
|
4
|
+
import { getThreadSessionFile, resolveChannelSessionFile, tryResolveThreadSession, } from "../../sessions/store.js";
|
|
5
|
+
import { isPlatformHistorySession } from "../../sessions/store.js";
|
|
1
6
|
import { basename } from "node:path";
|
|
7
|
+
import { InMemoryTokenStore } from "../token-store.js";
|
|
2
8
|
import MarkdownIt from "markdown-it";
|
|
3
9
|
import { createConversationEvent, createConversationMessage, } from "../../adapter.js";
|
|
4
10
|
import { createOfficeAddress } from "../../office/index.js";
|
|
5
11
|
import * as log from "../../log.js";
|
|
6
|
-
import { escapeHtml,
|
|
12
|
+
import { escapeHtml, jsonResponse as json, readJsonBody, renderPortalShell, } from "../portal-shell.js";
|
|
7
13
|
import { reportUserFacingError } from "../../observability/sentry.js";
|
|
8
14
|
import { inferConversationKind } from "../../sessions/session-key.js";
|
|
9
15
|
import { isThreadSessionKey, threadSuffixOf } from "../../sessions/session-key.js";
|
|
10
|
-
|
|
16
|
+
const SESSION_VIEW_TOKEN_TTL_MS = 24 * 60 * 60 * 1000;
|
|
17
|
+
export class InMemorySessionViewTokenStore extends InMemoryTokenStore {
|
|
18
|
+
create(options) {
|
|
19
|
+
return this.createRecord(SESSION_VIEW_TOKEN_TTL_MS, {
|
|
20
|
+
platform: options.platform,
|
|
21
|
+
platformUserId: options.platformUserId,
|
|
22
|
+
...(options.platformUserName ? { platformUserName: options.platformUserName } : {}),
|
|
23
|
+
conversationId: options.conversationId,
|
|
24
|
+
sessionKey: options.sessionKey,
|
|
25
|
+
sessionFile: options.sessionFile,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
|
11
29
|
const markdown = new MarkdownIt({
|
|
12
30
|
html: false,
|
|
13
31
|
linkify: true,
|
|
@@ -61,75 +79,93 @@ export async function handleSessionViewRequest(req, res, url, sessionViewTokenSt
|
|
|
61
79
|
if (req.method !== "GET" || url.pathname !== "/session") {
|
|
62
80
|
return false;
|
|
63
81
|
}
|
|
64
|
-
const
|
|
65
|
-
if (!
|
|
66
|
-
res.writeHead(
|
|
67
|
-
res.end(renderStatusPage("Session unavailable",
|
|
82
|
+
const target = await resolveSessionRequestTarget(sessionViewTokenStore, url.searchParams.get("token"), url.searchParams.get("session"));
|
|
83
|
+
if (!target.ok) {
|
|
84
|
+
res.writeHead(target.status, { "Content-Type": "text/html; charset=utf-8" });
|
|
85
|
+
res.end(renderStatusPage("Session unavailable", target.message));
|
|
68
86
|
return true;
|
|
69
87
|
}
|
|
70
|
-
const entry =
|
|
71
|
-
if (!entry) {
|
|
72
|
-
res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
|
|
73
|
-
res.end(renderStatusPage("Session unavailable", "This session link is invalid or has expired."));
|
|
74
|
-
return true;
|
|
75
|
-
}
|
|
76
|
-
const requestedSession = url.searchParams.get("session");
|
|
77
|
-
let targetSessionFile;
|
|
88
|
+
const { activeSessionKey, entry, targetSessionFile } = target;
|
|
78
89
|
try {
|
|
79
|
-
|
|
90
|
+
const model = await loadSessionViewModel(targetSessionFile);
|
|
91
|
+
const displayedSessionKey = activeSessionKey;
|
|
92
|
+
const isRunning = interactive?.handler.isRunning(createOfficeAddress(entry.platform, entry.conversationId), displayedSessionKey) ?? false;
|
|
93
|
+
res.writeHead(200, {
|
|
94
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
95
|
+
"Cache-Control": "no-store",
|
|
96
|
+
});
|
|
97
|
+
res.end(renderSessionPage({
|
|
98
|
+
model,
|
|
99
|
+
token: entry.token,
|
|
100
|
+
expiresAt: entry.expiresAt,
|
|
101
|
+
isRunning,
|
|
102
|
+
displayedSessionKey,
|
|
103
|
+
conversationId: entry.conversationId,
|
|
104
|
+
}));
|
|
80
105
|
}
|
|
81
106
|
catch (error) {
|
|
82
|
-
log.logWarning(`[${entry.conversationId}]
|
|
107
|
+
log.logWarning(`[${entry.conversationId}] Failed to render session ${entry.sessionFile}`, error instanceof Error ? error.message : String(error));
|
|
83
108
|
reportUserFacingError(error, {
|
|
84
109
|
domain: "session_view",
|
|
85
110
|
surface: "session_view",
|
|
86
|
-
operation: "
|
|
111
|
+
operation: "render_session",
|
|
87
112
|
severity: "error",
|
|
88
113
|
platform: entry.platform,
|
|
89
114
|
context: {
|
|
90
115
|
conversationId: entry.conversationId,
|
|
91
116
|
sessionKey: entry.sessionKey,
|
|
92
|
-
sessionFile: basename(
|
|
93
|
-
requestedSession,
|
|
117
|
+
sessionFile: basename(targetSessionFile),
|
|
94
118
|
},
|
|
95
119
|
});
|
|
96
120
|
res.writeHead(500, { "Content-Type": "text/html; charset=utf-8" });
|
|
97
|
-
res.end(renderStatusPage("Session unavailable", "The
|
|
98
|
-
return true;
|
|
121
|
+
res.end(renderStatusPage("Session unavailable", "The session could not be loaded right now."));
|
|
99
122
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Token/session selection is shared by HTML, SSE, and message transports.
|
|
127
|
+
* Invalid or expired client capabilities are 400; missing server wiring is
|
|
128
|
+
* 503; a selected corrupt session is a server-side storage failure and is 500.
|
|
129
|
+
*/
|
|
130
|
+
async function resolveSessionRequestTarget(sessionViewTokenStore, rawToken, requestedSession) {
|
|
131
|
+
if (!sessionViewTokenStore) {
|
|
132
|
+
return { ok: false, status: 503, message: "Session view is not configured." };
|
|
133
|
+
}
|
|
134
|
+
const token = rawToken?.trim() ?? "";
|
|
135
|
+
const entry = token ? sessionViewTokenStore.peek(token) : undefined;
|
|
136
|
+
if (!entry) {
|
|
137
|
+
return { ok: false, status: 400, message: "This session link is invalid or has expired." };
|
|
104
138
|
}
|
|
139
|
+
let targetSessionFile;
|
|
105
140
|
try {
|
|
106
|
-
|
|
107
|
-
const displayedSessionKey = resolveDisplayedSessionKey(entry, targetSessionFile);
|
|
108
|
-
const isRunning = interactive?.handler.isRunning(createOfficeAddress(entry.platform, entry.conversationId), displayedSessionKey) ?? false;
|
|
109
|
-
res.writeHead(200, {
|
|
110
|
-
"Content-Type": "text/html; charset=utf-8",
|
|
111
|
-
"Cache-Control": "no-store",
|
|
112
|
-
});
|
|
113
|
-
res.end(renderSessionPage(model, entry.token, entry.expiresAt, isRunning, displayedSessionKey, entry.conversationId));
|
|
141
|
+
targetSessionFile = await resolveRequestedSessionFile(entry.sessionFile, requestedSession);
|
|
114
142
|
}
|
|
115
143
|
catch (error) {
|
|
116
|
-
log.logWarning(`[${entry.conversationId}]
|
|
144
|
+
log.logWarning(`[${entry.conversationId}] Corrupted session file referenced for ${entry.sessionFile}`, error instanceof Error ? error.message : String(error));
|
|
117
145
|
reportUserFacingError(error, {
|
|
118
146
|
domain: "session_view",
|
|
119
147
|
surface: "session_view",
|
|
120
|
-
operation: "
|
|
148
|
+
operation: "resolve_requested_session",
|
|
121
149
|
severity: "error",
|
|
122
150
|
platform: entry.platform,
|
|
123
151
|
context: {
|
|
124
152
|
conversationId: entry.conversationId,
|
|
125
153
|
sessionKey: entry.sessionKey,
|
|
126
|
-
sessionFile: basename(
|
|
154
|
+
sessionFile: basename(entry.sessionFile),
|
|
155
|
+
requestedSession,
|
|
127
156
|
},
|
|
128
157
|
});
|
|
129
|
-
|
|
130
|
-
res.end(renderStatusPage("Session unavailable", "The session could not be loaded right now."));
|
|
158
|
+
return { ok: false, status: 500, message: "The selected session file is corrupted." };
|
|
131
159
|
}
|
|
132
|
-
|
|
160
|
+
if (!targetSessionFile) {
|
|
161
|
+
return { ok: false, status: 400, message: "The selected session is invalid." };
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
ok: true,
|
|
165
|
+
entry,
|
|
166
|
+
targetSessionFile,
|
|
167
|
+
activeSessionKey: resolveDisplayedSessionKey(entry, targetSessionFile),
|
|
168
|
+
};
|
|
133
169
|
}
|
|
134
170
|
function resolveDisplayedSessionKey(entry, sessionFile) {
|
|
135
171
|
if (entry.platform === "slack") {
|
|
@@ -149,7 +185,8 @@ function renderTimelineItems(items, token) {
|
|
|
149
185
|
? items.map((item) => renderItem(item, token)).join("\n")
|
|
150
186
|
: `<div class="system-event"><span class="event-dot"></span><span class="event-text">No messages yet — send one to the bot, then refresh.</span></div>`;
|
|
151
187
|
}
|
|
152
|
-
function renderSessionPage(
|
|
188
|
+
function renderSessionPage(options) {
|
|
189
|
+
const { model, token, expiresAt, isRunning, displayedSessionKey, conversationId } = options;
|
|
153
190
|
const items = renderTimelineItems(model.items, token);
|
|
154
191
|
const relatedSections = model.parent
|
|
155
192
|
? `<section class="related-card stack">
|
|
@@ -364,47 +401,18 @@ function renderLiveSystemEvent(text, tone = "default") {
|
|
|
364
401
|
return `<div class="system-event${cls}" data-live-item><span class="event-dot"></span><span class="event-text">${esc(text)}</span></div>`;
|
|
365
402
|
}
|
|
366
403
|
async function handleSessionStreamRequest(req, res, url, sessionViewTokenStore, interactive) {
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
res.
|
|
370
|
-
res.end("Session stream unavailable");
|
|
371
|
-
return;
|
|
372
|
-
}
|
|
373
|
-
const entry = sessionViewTokenStore.peek(token);
|
|
374
|
-
if (!entry) {
|
|
375
|
-
res.writeHead(400, { "Content-Type": "text/plain; charset=utf-8" });
|
|
376
|
-
res.end("Invalid session token");
|
|
377
|
-
return;
|
|
378
|
-
}
|
|
379
|
-
const requestedSession = url.searchParams.get("session");
|
|
380
|
-
let targetSessionFile;
|
|
381
|
-
try {
|
|
382
|
-
targetSessionFile = await resolveRequestedSessionFile(entry.sessionFile, requestedSession);
|
|
383
|
-
}
|
|
384
|
-
catch (error) {
|
|
385
|
-
reportUserFacingError(error, {
|
|
386
|
-
domain: "session_view",
|
|
387
|
-
surface: "session_view",
|
|
388
|
-
operation: "session_stream",
|
|
389
|
-
severity: "error",
|
|
390
|
-
platform: entry.platform,
|
|
391
|
-
context: {
|
|
392
|
-
conversationId: entry.conversationId,
|
|
393
|
-
sessionKey: entry.sessionKey,
|
|
394
|
-
sessionFile: basename(entry.sessionFile),
|
|
395
|
-
requestedSession,
|
|
396
|
-
},
|
|
397
|
-
});
|
|
398
|
-
res.writeHead(500, { "Content-Type": "text/plain; charset=utf-8" });
|
|
399
|
-
res.end("Session stream unavailable");
|
|
404
|
+
if (!interactive) {
|
|
405
|
+
res.writeHead(503, { "Content-Type": "text/plain; charset=utf-8" });
|
|
406
|
+
res.end("Session stream is not configured.");
|
|
400
407
|
return;
|
|
401
408
|
}
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
res.
|
|
409
|
+
const target = await resolveSessionRequestTarget(sessionViewTokenStore, url.searchParams.get("token"), url.searchParams.get("session"));
|
|
410
|
+
if (!target.ok) {
|
|
411
|
+
res.writeHead(target.status, { "Content-Type": "text/plain; charset=utf-8" });
|
|
412
|
+
res.end(target.message);
|
|
405
413
|
return;
|
|
406
414
|
}
|
|
407
|
-
const activeSessionKey =
|
|
415
|
+
const { activeSessionKey, entry } = target;
|
|
408
416
|
const streamKey = sessionStreamKey({ ...entry, sessionKey: activeSessionKey });
|
|
409
417
|
res.writeHead(200, {
|
|
410
418
|
"Content-Type": "text/event-stream; charset=utf-8",
|
|
@@ -427,64 +435,43 @@ async function handleSessionStreamRequest(req, res, url, sessionViewTokenStore,
|
|
|
427
435
|
});
|
|
428
436
|
}
|
|
429
437
|
async function handleSessionMessageRequest(req, res, sessionViewTokenStore, interactive) {
|
|
430
|
-
if (!
|
|
438
|
+
if (!interactive) {
|
|
431
439
|
json(res, 503, { ok: false, error: "Session chat is not configured." });
|
|
432
440
|
return;
|
|
433
441
|
}
|
|
434
|
-
const
|
|
435
|
-
if (
|
|
436
|
-
return;
|
|
437
|
-
let body;
|
|
438
|
-
try {
|
|
439
|
-
body = JSON.parse(rawBody);
|
|
440
|
-
}
|
|
441
|
-
catch {
|
|
442
|
-
json(res, 400, { ok: false, error: "Invalid request body." });
|
|
442
|
+
const body = await readJsonBody(req, res, 1024 * 1024);
|
|
443
|
+
if (!body)
|
|
443
444
|
return;
|
|
444
|
-
|
|
445
|
-
const
|
|
446
|
-
const
|
|
447
|
-
const
|
|
448
|
-
const requestedSessionKey = body.sessionKey?.trim() || "";
|
|
445
|
+
const token = typeof body.token === "string" ? body.token.trim() : "";
|
|
446
|
+
const text = typeof body.text === "string" ? body.text.trim() : "";
|
|
447
|
+
const requestedSession = typeof body.session === "string" ? body.session.trim() || null : null;
|
|
448
|
+
const requestedSessionKey = typeof body.sessionKey === "string" ? body.sessionKey.trim() : "";
|
|
449
449
|
if (!token || !text) {
|
|
450
450
|
json(res, 400, { ok: false, error: "Missing token or text." });
|
|
451
451
|
return;
|
|
452
452
|
}
|
|
453
|
-
const
|
|
454
|
-
if (!
|
|
455
|
-
json(res,
|
|
456
|
-
return;
|
|
457
|
-
}
|
|
458
|
-
let targetSessionFile;
|
|
459
|
-
try {
|
|
460
|
-
targetSessionFile = await resolveRequestedSessionFile(entry.sessionFile, requestedSession);
|
|
461
|
-
}
|
|
462
|
-
catch (error) {
|
|
463
|
-
reportUserFacingError(error, {
|
|
464
|
-
domain: "session_view",
|
|
465
|
-
surface: "session_view",
|
|
466
|
-
operation: "session_message",
|
|
467
|
-
severity: "error",
|
|
468
|
-
platform: entry.platform,
|
|
469
|
-
context: {
|
|
470
|
-
conversationId: entry.conversationId,
|
|
471
|
-
sessionKey: entry.sessionKey,
|
|
472
|
-
sessionFile: basename(entry.sessionFile),
|
|
473
|
-
requestedSession,
|
|
474
|
-
},
|
|
475
|
-
});
|
|
476
|
-
json(res, 500, { ok: false, error: "Session file could not be loaded." });
|
|
477
|
-
return;
|
|
478
|
-
}
|
|
479
|
-
if (!targetSessionFile) {
|
|
480
|
-
json(res, 400, { ok: false, error: "Invalid session file." });
|
|
453
|
+
const target = await resolveSessionRequestTarget(sessionViewTokenStore, token, requestedSession);
|
|
454
|
+
if (!target.ok) {
|
|
455
|
+
json(res, target.status, { ok: false, error: target.message });
|
|
481
456
|
return;
|
|
482
457
|
}
|
|
483
|
-
const activeSessionKey
|
|
458
|
+
const { activeSessionKey, entry, targetSessionFile } = target;
|
|
484
459
|
if (requestedSessionKey && requestedSessionKey !== activeSessionKey) {
|
|
485
460
|
json(res, 400, { ok: false, error: "Session target mismatch." });
|
|
486
461
|
return;
|
|
487
462
|
}
|
|
463
|
+
await dispatchSessionViewMessage({
|
|
464
|
+
res,
|
|
465
|
+
interactive,
|
|
466
|
+
token,
|
|
467
|
+
text,
|
|
468
|
+
activeSessionKey,
|
|
469
|
+
entry,
|
|
470
|
+
targetSessionFile,
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
async function dispatchSessionViewMessage(input) {
|
|
474
|
+
const { res, interactive, token, text, activeSessionKey, entry, targetSessionFile } = input;
|
|
488
475
|
const bot = interactive.botsByPlatform[entry.platform];
|
|
489
476
|
if (!bot) {
|
|
490
477
|
json(res, 503, { ok: false, error: `No bot configured for ${entry.platform}.` });
|
|
@@ -541,10 +528,6 @@ async function handleSessionMessageRequest(req, res, sessionViewTokenStore, inte
|
|
|
541
528
|
void interactive.handler
|
|
542
529
|
.handleEvent(event, bot, context)
|
|
543
530
|
.then(async () => {
|
|
544
|
-
if (!targetSessionFile) {
|
|
545
|
-
sessionViewStreamHub.publish(streamKey, { type: "status", running: false });
|
|
546
|
-
return;
|
|
547
|
-
}
|
|
548
531
|
const model = await loadSessionViewModel(targetSessionFile);
|
|
549
532
|
sessionViewStreamHub.publish(streamKey, {
|
|
550
533
|
type: "refresh",
|
|
@@ -609,13 +592,6 @@ function createSessionViewResponseContext(publish) {
|
|
|
609
592
|
},
|
|
610
593
|
};
|
|
611
594
|
}
|
|
612
|
-
function json(res, status, body) {
|
|
613
|
-
res.writeHead(status, {
|
|
614
|
-
"Content-Type": "application/json; charset=utf-8",
|
|
615
|
-
"Cache-Control": "no-store",
|
|
616
|
-
});
|
|
617
|
-
res.end(JSON.stringify(body));
|
|
618
|
-
}
|
|
619
595
|
function renderStatusPage(title, message) {
|
|
620
596
|
return renderHtmlDocument(title, `<section class="card stack">
|
|
621
597
|
<p class="eyebrow">mikan</p>
|
|
@@ -1657,4 +1633,505 @@ const sessionViewStyles = `
|
|
|
1657
1633
|
.asst-card { border-radius: 4px 14px 14px 14px; }
|
|
1658
1634
|
}
|
|
1659
1635
|
`;
|
|
1636
|
+
// ── Session view model (formerly service.ts) ──────────────────────────────────
|
|
1637
|
+
function entryIsoTime(entry) {
|
|
1638
|
+
if (!entry || typeof entry.timestamp !== "number" || !Number.isFinite(entry.timestamp)) {
|
|
1639
|
+
return undefined;
|
|
1640
|
+
}
|
|
1641
|
+
return new Date(entry.timestamp).toISOString();
|
|
1642
|
+
}
|
|
1643
|
+
export function resolveExistingSessionFile(conversationDir, sessionKey) {
|
|
1644
|
+
if (isThreadSessionKey(sessionKey)) {
|
|
1645
|
+
return tryResolveThreadSession(getThreadSessionFile(conversationDir, sessionKey));
|
|
1646
|
+
}
|
|
1647
|
+
return resolveChannelSessionFile(conversationDir);
|
|
1648
|
+
}
|
|
1649
|
+
export async function loadSessionViewModel(sessionFile) {
|
|
1650
|
+
const resolvedFile = resolve(sessionFile);
|
|
1651
|
+
const sm = await SessionStore.inspect(resolvedFile);
|
|
1652
|
+
const header = sm.getHeader();
|
|
1653
|
+
const entries = await sm.getEntries();
|
|
1654
|
+
const updatedAt = entryIsoTime(entries.at(-1)) ?? header.timestamp;
|
|
1655
|
+
const title = (await sm.getSessionName()) || `Session ${header.id.slice(0, 8)}`;
|
|
1656
|
+
const parent = await resolveParentRelation(resolvedFile, header);
|
|
1657
|
+
const threadCandidates = [];
|
|
1658
|
+
for (const candidate of listRelatedSessionFiles(resolvedFile)) {
|
|
1659
|
+
if (candidate === resolvedFile)
|
|
1660
|
+
continue;
|
|
1661
|
+
const relation = await buildSessionRelation(candidate, "thread", resolvedFile, header.id, entries);
|
|
1662
|
+
if (relation)
|
|
1663
|
+
threadCandidates.push(relation);
|
|
1664
|
+
}
|
|
1665
|
+
const threads = threadCandidates.toSorted((a, b) => a.updatedAt < b.updatedAt ? -1 : a.updatedAt > b.updatedAt ? 1 : 0);
|
|
1666
|
+
const threadsByEntryId = new Map();
|
|
1667
|
+
for (const thread of threads) {
|
|
1668
|
+
if (!thread.anchorEntryId)
|
|
1669
|
+
continue;
|
|
1670
|
+
const bucket = threadsByEntryId.get(thread.anchorEntryId) ?? [];
|
|
1671
|
+
bucket.push(thread);
|
|
1672
|
+
threadsByEntryId.set(thread.anchorEntryId, bucket);
|
|
1673
|
+
}
|
|
1674
|
+
const items = entries.flatMap((entry) => {
|
|
1675
|
+
const item = mapEntryToItem(entry);
|
|
1676
|
+
if (!item)
|
|
1677
|
+
return [];
|
|
1678
|
+
if (item.entryId) {
|
|
1679
|
+
const anchoredThreads = threadsByEntryId.get(item.entryId);
|
|
1680
|
+
if (anchoredThreads) {
|
|
1681
|
+
item.threads = anchoredThreads;
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
return [item];
|
|
1685
|
+
});
|
|
1686
|
+
return {
|
|
1687
|
+
sessionId: header.id,
|
|
1688
|
+
fileName: basename(resolvedFile),
|
|
1689
|
+
title,
|
|
1690
|
+
createdAt: header.timestamp,
|
|
1691
|
+
updatedAt,
|
|
1692
|
+
entryCount: entries.length,
|
|
1693
|
+
items,
|
|
1694
|
+
parent: parent ?? undefined,
|
|
1695
|
+
threads,
|
|
1696
|
+
};
|
|
1697
|
+
}
|
|
1698
|
+
async function resolveRequestedSessionFile(baseSessionFile, requestedFileName) {
|
|
1699
|
+
const resolvedBase = resolve(baseSessionFile);
|
|
1700
|
+
if (!requestedFileName)
|
|
1701
|
+
return resolvedBase;
|
|
1702
|
+
const trimmed = requestedFileName.trim();
|
|
1703
|
+
if (!trimmed)
|
|
1704
|
+
return resolvedBase;
|
|
1705
|
+
const fileName = basename(trimmed);
|
|
1706
|
+
if (fileName !== trimmed || !fileName.endsWith(".jsonl"))
|
|
1707
|
+
return null;
|
|
1708
|
+
const candidate = join(dirname(resolvedBase), fileName);
|
|
1709
|
+
if (!existsSync(candidate))
|
|
1710
|
+
return null;
|
|
1711
|
+
try {
|
|
1712
|
+
await SessionStore.open(candidate);
|
|
1713
|
+
}
|
|
1714
|
+
catch (err) {
|
|
1715
|
+
throw new Error(`Session file is corrupted: ${candidate}: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
|
|
1716
|
+
}
|
|
1717
|
+
return candidate;
|
|
1718
|
+
}
|
|
1719
|
+
function listRelatedSessionFiles(sessionFile) {
|
|
1720
|
+
const dir = dirname(sessionFile);
|
|
1721
|
+
if (!existsSync(dir))
|
|
1722
|
+
return [];
|
|
1723
|
+
return readdirSync(dir)
|
|
1724
|
+
.filter((name) => name.endsWith(".jsonl"))
|
|
1725
|
+
.map((fileName) => join(dir, fileName));
|
|
1726
|
+
}
|
|
1727
|
+
async function buildSessionRelation(sessionFile, kind, expectedParent, expectedParentId, expectedParentEntries) {
|
|
1728
|
+
let sm;
|
|
1729
|
+
try {
|
|
1730
|
+
sm = await SessionStore.inspect(sessionFile);
|
|
1731
|
+
}
|
|
1732
|
+
catch (err) {
|
|
1733
|
+
log.logWarning(`Skipping corrupted session file while building ${kind} relation: ${sessionFile}`, err instanceof Error ? err.message : String(err));
|
|
1734
|
+
return null;
|
|
1735
|
+
}
|
|
1736
|
+
const header = sm.getHeader();
|
|
1737
|
+
if (kind === "thread" &&
|
|
1738
|
+
!isChildThreadSession(sessionFile, header.parentSession, expectedParent, expectedParentId, header.parentSessionId)) {
|
|
1739
|
+
return null;
|
|
1740
|
+
}
|
|
1741
|
+
const entries = await sm.getEntries();
|
|
1742
|
+
const updatedAt = entryIsoTime(entries.at(-1)) ?? header.timestamp;
|
|
1743
|
+
const threadId = kind === "thread" ? getFixedThreadSessionId(sessionFile) : null;
|
|
1744
|
+
const anchorEntryId = kind === "thread" && expectedParent
|
|
1745
|
+
? findThreadAnchorEntryId(expectedParentEntries ??
|
|
1746
|
+
(await (await SessionStore.inspect(expectedParent)).getEntries()), entries, threadId)
|
|
1747
|
+
: undefined;
|
|
1748
|
+
return {
|
|
1749
|
+
kind,
|
|
1750
|
+
fileName: basename(sessionFile),
|
|
1751
|
+
sessionId: header.id,
|
|
1752
|
+
title: (await sm.getSessionName()) || `Session ${header.id.slice(0, 8)}`,
|
|
1753
|
+
updatedAt,
|
|
1754
|
+
entryCount: entries.length,
|
|
1755
|
+
summary: extractSessionSummary(entries),
|
|
1756
|
+
anchorEntryId,
|
|
1757
|
+
};
|
|
1758
|
+
}
|
|
1759
|
+
async function resolveParentRelation(sessionFile, header) {
|
|
1760
|
+
if (header.parentSession) {
|
|
1761
|
+
const parentPath = resolve(header.parentSession);
|
|
1762
|
+
if (existsSync(parentPath)) {
|
|
1763
|
+
return (await buildSessionRelation(parentPath, "parent")) ?? undefined;
|
|
1764
|
+
}
|
|
1765
|
+
// Path is stale — try UUID fallback before heuristic.
|
|
1766
|
+
if (header.parentSessionId) {
|
|
1767
|
+
const found = findSessionFileById(dirname(sessionFile), header.parentSessionId);
|
|
1768
|
+
if (found)
|
|
1769
|
+
return (await buildSessionRelation(found, "parent")) ?? undefined;
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1772
|
+
return buildInferredThreadParentRelation(sessionFile);
|
|
1773
|
+
}
|
|
1774
|
+
function findSessionFileById(sessionDir, targetId) {
|
|
1775
|
+
if (!existsSync(sessionDir))
|
|
1776
|
+
return null;
|
|
1777
|
+
for (const name of readdirSync(sessionDir)) {
|
|
1778
|
+
if (!name.endsWith(".jsonl"))
|
|
1779
|
+
continue;
|
|
1780
|
+
try {
|
|
1781
|
+
const filePath = join(sessionDir, name);
|
|
1782
|
+
if (SessionStore.readHeader(filePath)?.id === targetId)
|
|
1783
|
+
return filePath;
|
|
1784
|
+
}
|
|
1785
|
+
catch {
|
|
1786
|
+
continue;
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1789
|
+
return null;
|
|
1790
|
+
}
|
|
1791
|
+
async function buildInferredThreadParentRelation(sessionFile) {
|
|
1792
|
+
if (!getFixedThreadSessionId(sessionFile))
|
|
1793
|
+
return undefined;
|
|
1794
|
+
const parentSession = resolveChannelSessionFile(dirname(dirname(sessionFile)));
|
|
1795
|
+
if (!parentSession || parentSession === sessionFile)
|
|
1796
|
+
return undefined;
|
|
1797
|
+
return (await buildSessionRelation(parentSession, "parent")) ?? undefined;
|
|
1798
|
+
}
|
|
1799
|
+
function isChildThreadSession(sessionFile, parentSession, expectedParent, expectedParentId, threadParentSessionId) {
|
|
1800
|
+
if (!expectedParent)
|
|
1801
|
+
return false;
|
|
1802
|
+
if (parentSession) {
|
|
1803
|
+
const resolvedParent = resolve(parentSession);
|
|
1804
|
+
if (resolvedParent === expectedParent ||
|
|
1805
|
+
(isPlatformHistorySession(expectedParent) && isPlatformHistorySession(resolvedParent))) {
|
|
1806
|
+
return true;
|
|
1807
|
+
}
|
|
1808
|
+
// Path is stale — fall back to UUID comparison.
|
|
1809
|
+
if (expectedParentId && threadParentSessionId) {
|
|
1810
|
+
return threadParentSessionId === expectedParentId;
|
|
1811
|
+
}
|
|
1812
|
+
return false;
|
|
1813
|
+
}
|
|
1814
|
+
if (!getFixedThreadSessionId(sessionFile))
|
|
1815
|
+
return false;
|
|
1816
|
+
return resolveChannelSessionFile(dirname(dirname(sessionFile))) === expectedParent;
|
|
1817
|
+
}
|
|
1818
|
+
function getFixedThreadSessionId(sessionFile) {
|
|
1819
|
+
const fileName = basename(sessionFile);
|
|
1820
|
+
if (!fileName.endsWith(".jsonl"))
|
|
1821
|
+
return null;
|
|
1822
|
+
if (/^\d{4}-\d{2}-\d{2}T.+_[0-9a-f]{8}\.jsonl$/i.test(fileName))
|
|
1823
|
+
return null;
|
|
1824
|
+
return fileName.slice(0, -".jsonl".length);
|
|
1825
|
+
}
|
|
1826
|
+
function findThreadAnchorEntryId(parentEntries, childEntries, threadId) {
|
|
1827
|
+
const threadTimestamp = threadId ? Number(threadId) * 1000 : undefined;
|
|
1828
|
+
if (threadTimestamp !== undefined && Number.isFinite(threadTimestamp)) {
|
|
1829
|
+
const timestampAnchor = findEntryIdByMessageTimestamp(parentEntries, threadTimestamp);
|
|
1830
|
+
if (timestampAnchor)
|
|
1831
|
+
return timestampAnchor;
|
|
1832
|
+
}
|
|
1833
|
+
let sharedCount = 0;
|
|
1834
|
+
while (sharedCount < parentEntries.length &&
|
|
1835
|
+
sharedCount < childEntries.length &&
|
|
1836
|
+
parentEntries[sharedCount]?.id === childEntries[sharedCount]?.id) {
|
|
1837
|
+
sharedCount += 1;
|
|
1838
|
+
}
|
|
1839
|
+
if (sharedCount > 0) {
|
|
1840
|
+
return parentEntries[sharedCount - 1]?.id;
|
|
1841
|
+
}
|
|
1842
|
+
const contentAnchor = findSharedContentAnchorEntryId(parentEntries, childEntries);
|
|
1843
|
+
if (contentAnchor)
|
|
1844
|
+
return contentAnchor;
|
|
1845
|
+
const childRoot = findComparableUserMessage(childEntries);
|
|
1846
|
+
if (!childRoot)
|
|
1847
|
+
return undefined;
|
|
1848
|
+
return findParentAnchorByRootMessage(parentEntries, childRoot);
|
|
1849
|
+
}
|
|
1850
|
+
function findEntryIdByMessageTimestamp(entries, timestamp) {
|
|
1851
|
+
for (const entry of entries) {
|
|
1852
|
+
if (entry.type !== "message")
|
|
1853
|
+
continue;
|
|
1854
|
+
const messageTimestamp = entry.message.timestamp;
|
|
1855
|
+
if (messageTimestamp === timestamp)
|
|
1856
|
+
return entry.id;
|
|
1857
|
+
}
|
|
1858
|
+
return undefined;
|
|
1859
|
+
}
|
|
1860
|
+
function findSharedContentAnchorEntryId(parentEntries, childEntries) {
|
|
1861
|
+
const parentMessages = parentEntries.flatMap((entry) => {
|
|
1862
|
+
const comparable = getComparableSessionMessage(entry);
|
|
1863
|
+
return comparable ? [comparable] : [];
|
|
1864
|
+
});
|
|
1865
|
+
const childMessages = childEntries.flatMap((entry) => {
|
|
1866
|
+
const comparable = getComparableSessionMessage(entry);
|
|
1867
|
+
return comparable ? [comparable] : [];
|
|
1868
|
+
});
|
|
1869
|
+
if (parentMessages.length === 0 || childMessages.length === 0)
|
|
1870
|
+
return undefined;
|
|
1871
|
+
let bestParentEnd = -1;
|
|
1872
|
+
let bestLength = 0;
|
|
1873
|
+
for (let parentStart = 0; parentStart < parentMessages.length; parentStart++) {
|
|
1874
|
+
let length = 0;
|
|
1875
|
+
while (parentStart + length < parentMessages.length && length < childMessages.length) {
|
|
1876
|
+
const parentMessage = parentMessages[parentStart + length];
|
|
1877
|
+
const childMessage = childMessages[length];
|
|
1878
|
+
if (parentMessage === undefined ||
|
|
1879
|
+
childMessage === undefined ||
|
|
1880
|
+
parentMessage.role !== childMessage.role ||
|
|
1881
|
+
parentMessage.normalizedText !== childMessage.normalizedText) {
|
|
1882
|
+
break;
|
|
1883
|
+
}
|
|
1884
|
+
length += 1;
|
|
1885
|
+
}
|
|
1886
|
+
if (length > bestLength) {
|
|
1887
|
+
bestLength = length;
|
|
1888
|
+
bestParentEnd = parentStart + length - 1;
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
1891
|
+
return bestLength > 0 ? parentMessages[bestParentEnd]?.entryId : undefined;
|
|
1892
|
+
}
|
|
1893
|
+
function getComparableSessionMessage(entry) {
|
|
1894
|
+
if (entry.type !== "message")
|
|
1895
|
+
return null;
|
|
1896
|
+
const role = entry.message.role;
|
|
1897
|
+
if (role !== "user" && role !== "assistant")
|
|
1898
|
+
return null;
|
|
1899
|
+
const body = contentToText(entry.message.content);
|
|
1900
|
+
const normalizedText = normalizeComparableSessionText(body, role);
|
|
1901
|
+
if (!normalizedText)
|
|
1902
|
+
return null;
|
|
1903
|
+
return { entryId: entry.id, role, normalizedText };
|
|
1904
|
+
}
|
|
1905
|
+
function normalizeComparableSessionText(text, role) {
|
|
1906
|
+
const normalized = role === "user" ? normalizeComparableUserText(text) : text.trim();
|
|
1907
|
+
return normalized.replace(/\s+/g, " ").trim();
|
|
1908
|
+
}
|
|
1909
|
+
function findParentAnchorByRootMessage(parentEntries, childRoot) {
|
|
1910
|
+
let textMatchId;
|
|
1911
|
+
for (const entry of parentEntries) {
|
|
1912
|
+
const comparable = getComparableUserMessage(entry);
|
|
1913
|
+
if (!comparable)
|
|
1914
|
+
continue;
|
|
1915
|
+
if (comparable.normalizedText !== childRoot.normalizedText)
|
|
1916
|
+
continue;
|
|
1917
|
+
if (childRoot.messageTimestamp !== undefined &&
|
|
1918
|
+
comparable.messageTimestamp !== undefined &&
|
|
1919
|
+
comparable.messageTimestamp === childRoot.messageTimestamp) {
|
|
1920
|
+
return entry.id;
|
|
1921
|
+
}
|
|
1922
|
+
textMatchId ??= entry.id;
|
|
1923
|
+
}
|
|
1924
|
+
return textMatchId;
|
|
1925
|
+
}
|
|
1926
|
+
function findComparableUserMessage(entries) {
|
|
1927
|
+
for (const entry of entries) {
|
|
1928
|
+
const comparable = getComparableUserMessage(entry);
|
|
1929
|
+
if (comparable)
|
|
1930
|
+
return comparable;
|
|
1931
|
+
}
|
|
1932
|
+
return null;
|
|
1933
|
+
}
|
|
1934
|
+
function getComparableUserMessage(entry) {
|
|
1935
|
+
if (entry.type !== "message" || entry.message.role !== "user")
|
|
1936
|
+
return null;
|
|
1937
|
+
const body = contentToText(entry.message.content);
|
|
1938
|
+
const normalizedText = normalizeComparableUserText(body);
|
|
1939
|
+
if (!normalizedText)
|
|
1940
|
+
return null;
|
|
1941
|
+
const messageTimestamp = typeof entry.message.timestamp === "number" ? entry.message.timestamp : undefined;
|
|
1942
|
+
return { normalizedText, messageTimestamp };
|
|
1943
|
+
}
|
|
1944
|
+
function normalizeComparableUserText(text) {
|
|
1945
|
+
const withoutTimestamp = text.replace(/^\[[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}[+-][0-9]{2}:[0-9]{2}\]\s+(?=\[[^\]]+\](?:\s+\[in-thread:[^\]]+\])?:\s)/, "");
|
|
1946
|
+
return withoutTimestamp
|
|
1947
|
+
.replace(/\n*<slack_attachments>\n[\s\S]*?\n<\/slack_attachments>\s*$/g, "")
|
|
1948
|
+
.trim();
|
|
1949
|
+
}
|
|
1950
|
+
function extractSessionSummary(entries) {
|
|
1951
|
+
for (const entry of entries) {
|
|
1952
|
+
if (entry.type !== "message")
|
|
1953
|
+
continue;
|
|
1954
|
+
const item = mapEntryToItem(entry);
|
|
1955
|
+
if (!item?.body)
|
|
1956
|
+
continue;
|
|
1957
|
+
return collapseSummary(item.body);
|
|
1958
|
+
}
|
|
1959
|
+
return undefined;
|
|
1960
|
+
}
|
|
1961
|
+
function collapseSummary(text) {
|
|
1962
|
+
const singleLine = text.replace(/\s+/g, " ").trim();
|
|
1963
|
+
return singleLine.length > 96 ? `${singleLine.slice(0, 93)}…` : singleLine;
|
|
1964
|
+
}
|
|
1965
|
+
function mapEntryToItem(entry) {
|
|
1966
|
+
switch (entry.type) {
|
|
1967
|
+
case "message":
|
|
1968
|
+
return mapMessageEntry(entry);
|
|
1969
|
+
case "model_change":
|
|
1970
|
+
return {
|
|
1971
|
+
kind: "system",
|
|
1972
|
+
title: "Model changed",
|
|
1973
|
+
body: `${entry.provider} / ${entry.modelId}`,
|
|
1974
|
+
meta: entryIsoTime(entry) ?? "",
|
|
1975
|
+
tone: "muted",
|
|
1976
|
+
};
|
|
1977
|
+
case "thinking_level_change":
|
|
1978
|
+
return {
|
|
1979
|
+
kind: "system",
|
|
1980
|
+
title: "Thinking level changed",
|
|
1981
|
+
body: entry.thinkingLevel,
|
|
1982
|
+
meta: entryIsoTime(entry) ?? "",
|
|
1983
|
+
tone: "muted",
|
|
1984
|
+
};
|
|
1985
|
+
case "compaction":
|
|
1986
|
+
return mapCompactionEntry(entry);
|
|
1987
|
+
case "branch_summary":
|
|
1988
|
+
return mapSessionSummaryEntry(entry);
|
|
1989
|
+
case "custom":
|
|
1990
|
+
return {
|
|
1991
|
+
kind: "system",
|
|
1992
|
+
title: `Custom data · ${entry.customType}`,
|
|
1993
|
+
body: entry.data === undefined ? "(no data)" : JSON.stringify(entry.data, null, 2),
|
|
1994
|
+
meta: entryIsoTime(entry) ?? "",
|
|
1995
|
+
tone: "muted",
|
|
1996
|
+
};
|
|
1997
|
+
default:
|
|
1998
|
+
return null;
|
|
1999
|
+
}
|
|
2000
|
+
}
|
|
2001
|
+
function mapMessageEntry(entry) {
|
|
2002
|
+
const message = entry.message;
|
|
2003
|
+
switch (message.role) {
|
|
2004
|
+
case "user":
|
|
2005
|
+
return {
|
|
2006
|
+
kind: "user",
|
|
2007
|
+
title: "User",
|
|
2008
|
+
body: contentToText(message.content),
|
|
2009
|
+
meta: entryIsoTime(entry) ?? "",
|
|
2010
|
+
entryId: entry.id,
|
|
2011
|
+
};
|
|
2012
|
+
case "assistant": {
|
|
2013
|
+
const assistantBody = contentToText(message.content) || (message.errorMessage ? `_${message.errorMessage}_` : "");
|
|
2014
|
+
const metaParts = [message.provider, message.model, message.stopReason].filter(Boolean);
|
|
2015
|
+
return {
|
|
2016
|
+
kind: "assistant",
|
|
2017
|
+
title: "Assistant",
|
|
2018
|
+
body: assistantBody,
|
|
2019
|
+
meta: metaParts.length > 0
|
|
2020
|
+
? `${entryIsoTime(entry) ?? ""} · ${metaParts.join(" · ")}`
|
|
2021
|
+
: (entryIsoTime(entry) ?? ""),
|
|
2022
|
+
entryId: entry.id,
|
|
2023
|
+
};
|
|
2024
|
+
}
|
|
2025
|
+
case "toolResult":
|
|
2026
|
+
return {
|
|
2027
|
+
kind: "tool",
|
|
2028
|
+
title: `Tool result · ${String(message.toolName ?? "unknown")}`,
|
|
2029
|
+
body: contentToText(message.content),
|
|
2030
|
+
meta: entryIsoTime(entry) ?? "",
|
|
2031
|
+
tone: message.isError ? "err" : "ok",
|
|
2032
|
+
entryId: entry.id,
|
|
2033
|
+
};
|
|
2034
|
+
case "bashExecution": {
|
|
2035
|
+
const command = String(message.command ?? "").trim();
|
|
2036
|
+
const output = String(message.output ?? "").trim();
|
|
2037
|
+
const details = [
|
|
2038
|
+
typeof message.exitCode === "number" ? `[exitCode] ${message.exitCode}` : "",
|
|
2039
|
+
message.cancelled ? `[cancelled] true` : "",
|
|
2040
|
+
message.truncated ? `[truncated] true` : "",
|
|
2041
|
+
].filter(Boolean);
|
|
2042
|
+
const body = [command ? `$ ${command}` : "", output, ...details].filter(Boolean).join("\n\n");
|
|
2043
|
+
return {
|
|
2044
|
+
kind: "tool",
|
|
2045
|
+
title: "Bash execution",
|
|
2046
|
+
body: body || "(no output)",
|
|
2047
|
+
meta: entryIsoTime(entry) ?? "",
|
|
2048
|
+
entryId: entry.id,
|
|
2049
|
+
};
|
|
2050
|
+
}
|
|
2051
|
+
case "custom":
|
|
2052
|
+
return {
|
|
2053
|
+
kind: "system",
|
|
2054
|
+
title: `Custom message · ${String(message.customType ?? "custom")}`,
|
|
2055
|
+
body: contentToText(message.content),
|
|
2056
|
+
meta: entryIsoTime(entry) ?? "",
|
|
2057
|
+
tone: "muted",
|
|
2058
|
+
entryId: entry.id,
|
|
2059
|
+
};
|
|
2060
|
+
case "branchSummary":
|
|
2061
|
+
return {
|
|
2062
|
+
kind: "system",
|
|
2063
|
+
title: "Session summary",
|
|
2064
|
+
body: String(message.summary ?? ""),
|
|
2065
|
+
meta: entryIsoTime(entry) ?? "",
|
|
2066
|
+
tone: "muted",
|
|
2067
|
+
entryId: entry.id,
|
|
2068
|
+
};
|
|
2069
|
+
case "compactionSummary":
|
|
2070
|
+
return {
|
|
2071
|
+
kind: "system",
|
|
2072
|
+
title: "Compaction summary",
|
|
2073
|
+
body: String(message.summary ?? ""),
|
|
2074
|
+
meta: entryIsoTime(entry) ?? "",
|
|
2075
|
+
tone: "muted",
|
|
2076
|
+
entryId: entry.id,
|
|
2077
|
+
};
|
|
2078
|
+
default:
|
|
2079
|
+
return {
|
|
2080
|
+
kind: "system",
|
|
2081
|
+
title: `Message · ${String(message.role ?? "unknown")}`,
|
|
2082
|
+
body: contentToText(message.content),
|
|
2083
|
+
meta: entryIsoTime(entry) ?? "",
|
|
2084
|
+
tone: "muted",
|
|
2085
|
+
entryId: entry.id,
|
|
2086
|
+
};
|
|
2087
|
+
}
|
|
2088
|
+
}
|
|
2089
|
+
function mapCompactionEntry(entry) {
|
|
2090
|
+
return {
|
|
2091
|
+
kind: "system",
|
|
2092
|
+
title: "Context compacted",
|
|
2093
|
+
body: entry.summary,
|
|
2094
|
+
meta: `${entryIsoTime(entry) ?? ""} · ${entry.tokensBefore} tokens before compaction`,
|
|
2095
|
+
tone: "muted",
|
|
2096
|
+
};
|
|
2097
|
+
}
|
|
2098
|
+
function mapSessionSummaryEntry(entry) {
|
|
2099
|
+
return {
|
|
2100
|
+
kind: "system",
|
|
2101
|
+
title: "Session summary",
|
|
2102
|
+
body: entry.summary,
|
|
2103
|
+
meta: entryIsoTime(entry) ?? "",
|
|
2104
|
+
tone: "muted",
|
|
2105
|
+
};
|
|
2106
|
+
}
|
|
2107
|
+
function contentToText(content) {
|
|
2108
|
+
if (typeof content === "string")
|
|
2109
|
+
return content;
|
|
2110
|
+
if (!Array.isArray(content))
|
|
2111
|
+
return "";
|
|
2112
|
+
const lines = [];
|
|
2113
|
+
for (const block of content) {
|
|
2114
|
+
if (!block || typeof block !== "object")
|
|
2115
|
+
continue;
|
|
2116
|
+
const value = block;
|
|
2117
|
+
if (value.type === "text" && typeof value.text === "string") {
|
|
2118
|
+
lines.push(value.text);
|
|
2119
|
+
continue;
|
|
2120
|
+
}
|
|
2121
|
+
if (value.type === "thinking" && typeof value.thinking === "string") {
|
|
2122
|
+
lines.push(`[thinking]\n${value.thinking}`);
|
|
2123
|
+
continue;
|
|
2124
|
+
}
|
|
2125
|
+
if (value.type === "toolCall") {
|
|
2126
|
+
const name = typeof value.name === "string" ? value.name : "tool";
|
|
2127
|
+
const args = value.arguments === undefined ? "" : JSON.stringify(value.arguments, null, 2);
|
|
2128
|
+
lines.push([`[toolCall] ${name}`, args].filter(Boolean).join("\n"));
|
|
2129
|
+
continue;
|
|
2130
|
+
}
|
|
2131
|
+
if (value.type === "image") {
|
|
2132
|
+
lines.push(`[image ${String(value.mimeType ?? "unknown")}]`);
|
|
2133
|
+
}
|
|
2134
|
+
}
|
|
2135
|
+
return lines.join("\n\n");
|
|
2136
|
+
}
|
|
1660
2137
|
//# sourceMappingURL=portal.js.map
|