@bastani/atomic 0.9.3-alpha.1 → 0.9.3-alpha.3
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 +15 -0
- package/dist/builtin/cursor/CHANGELOG.md +21 -0
- package/dist/builtin/cursor/README.md +2 -1
- package/dist/builtin/cursor/package.json +2 -2
- package/dist/builtin/cursor/src/cursor-models-raw.json +2 -9
- package/dist/builtin/cursor/src/model-mapper.ts +14 -3
- package/dist/builtin/cursor/src/proto/protobuf-codec-base64.ts +22 -0
- package/dist/builtin/cursor/src/proto/protobuf-codec-request.ts +53 -13
- package/dist/builtin/cursor/src/proto/protobuf-codec-wire.ts +24 -7
- package/dist/builtin/cursor/src/proto/protobuf-codec.ts +3 -2
- package/dist/builtin/cursor/src/stream.ts +5 -11
- package/dist/builtin/cursor/src/transport-types.ts +3 -0
- package/dist/builtin/cursor/src/transport.ts +1 -0
- package/dist/builtin/intercom/CHANGELOG.md +6 -0
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/mcp/CHANGELOG.md +6 -0
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/CHANGELOG.md +15 -0
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/subagents/src/extension/fanout-child.ts +1 -0
- package/dist/builtin/subagents/src/extension/index.ts +6 -3
- package/dist/builtin/subagents/src/extension/schemas.ts +0 -5
- package/dist/builtin/subagents/src/runs/background/async-job-tracker.ts +1 -4
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-single.ts +15 -1
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor.ts +35 -1
- package/dist/builtin/subagents/src/runs/shared/subagent-prompt-runtime.ts +4 -2
- package/dist/builtin/subagents/src/shared/types-async.ts +1 -0
- package/dist/builtin/subagents/src/slash/prompt-template-bridge.ts +27 -5
- package/dist/builtin/subagents/src/tui/render-layout.ts +27 -4
- package/dist/builtin/subagents/src/tui/render-result-animation.ts +22 -31
- package/dist/builtin/subagents/src/tui/render-result-compact.ts +6 -6
- package/dist/builtin/subagents/src/tui/render-result.ts +20 -19
- package/dist/builtin/subagents/src/tui/render-status-progress.ts +3 -3
- package/dist/builtin/subagents/src/tui/render-widget.ts +46 -7
- package/dist/builtin/subagents/src/tui/render.ts +2 -2
- package/dist/builtin/web-access/CHANGELOG.md +6 -0
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +49 -0
- package/dist/builtin/workflows/README.md +1 -1
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/src/authoring.d.ts +1 -1
- package/dist/builtin/workflows/src/durable/backend.ts +343 -0
- package/dist/builtin/workflows/src/durable/child-primitive.ts +79 -0
- package/dist/builtin/workflows/src/durable/dbos-backend.ts +421 -0
- package/dist/builtin/workflows/src/durable/dbos-envelope.ts +171 -0
- package/dist/builtin/workflows/src/durable/factory.ts +96 -0
- package/dist/builtin/workflows/src/durable/file-backend.ts +433 -0
- package/dist/builtin/workflows/src/durable/index.ts +73 -0
- package/dist/builtin/workflows/src/durable/resume-catalog.ts +217 -0
- package/dist/builtin/workflows/src/durable/resume-runtime.ts +299 -0
- package/dist/builtin/workflows/src/durable/scoped-backend.ts +171 -0
- package/dist/builtin/workflows/src/durable/stage-primitive.ts +284 -0
- package/dist/builtin/workflows/src/durable/tool-primitive.ts +180 -0
- package/dist/builtin/workflows/src/durable/types.ts +168 -0
- package/dist/builtin/workflows/src/durable/ui-primitive.ts +96 -0
- package/dist/builtin/workflows/src/engine/options.ts +3 -0
- package/dist/builtin/workflows/src/engine/primitives/parallel.ts +2 -2
- package/dist/builtin/workflows/src/engine/primitives/task.ts +4 -4
- package/dist/builtin/workflows/src/engine/primitives/ui.ts +22 -8
- package/dist/builtin/workflows/src/engine/primitives/workflow.ts +8 -0
- package/dist/builtin/workflows/src/engine/run-durable-finalize.ts +69 -0
- package/dist/builtin/workflows/src/engine/run-durable-stage-session.ts +31 -0
- package/dist/builtin/workflows/src/engine/run.ts +148 -6
- package/dist/builtin/workflows/src/engine/runtime.ts +8 -2
- package/dist/builtin/workflows/src/extension/extension-factory.ts +6 -12
- package/dist/builtin/workflows/src/extension/extension-lifecycle.ts +5 -1
- package/dist/builtin/workflows/src/extension/extension-runtime-state.ts +3 -0
- package/dist/builtin/workflows/src/extension/runtime.ts +48 -9
- package/dist/builtin/workflows/src/extension/workflow-run-control-command.ts +143 -4
- package/dist/builtin/workflows/src/runs/background/quit.ts +61 -0
- package/dist/builtin/workflows/src/runs/background/status.ts +1 -0
- package/dist/builtin/workflows/src/runs/foreground/executor-direct-helpers.ts +5 -5
- package/dist/builtin/workflows/src/runs/foreground/executor-stage-call.ts +74 -33
- package/dist/builtin/workflows/src/runs/foreground/executor-stage-context.ts +20 -1
- package/dist/builtin/workflows/src/runs/foreground/executor-stage-factory.ts +8 -7
- package/dist/builtin/workflows/src/runs/foreground/executor-stage-replay.ts +1 -0
- package/dist/builtin/workflows/src/runs/foreground/executor-stage-types.ts +1 -1
- package/dist/builtin/workflows/src/runs/foreground/executor-types.ts +19 -2
- package/dist/builtin/workflows/src/runs/foreground/stage-runner-context.ts +4 -0
- package/dist/builtin/workflows/src/runs/foreground/stage-runner-controller.ts +10 -10
- package/dist/builtin/workflows/src/runs/foreground/stage-runner-options.ts +5 -1
- package/dist/builtin/workflows/src/runs/foreground/stage-runner-send-user-message.ts +25 -0
- package/dist/builtin/workflows/src/runs/foreground/stage-runner-types.ts +3 -0
- package/dist/builtin/workflows/src/shared/authoring-contract-stage.d.ts +16 -0
- package/dist/builtin/workflows/src/shared/authoring-contract-stage.ts +20 -0
- package/dist/builtin/workflows/src/shared/authoring-contract-ui.d.ts +23 -1
- package/dist/builtin/workflows/src/shared/authoring-contract-ui.ts +30 -1
- package/dist/builtin/workflows/src/shared/store-public-types.ts +6 -2
- package/dist/builtin/workflows/src/shared/store-run-methods.ts +12 -6
- package/dist/builtin/workflows/src/shared/types.ts +55 -0
- package/dist/builtin/workflows/src/tui/graph-view-constants.ts +1 -1
- package/dist/builtin/workflows/src/tui/graph-view-graph-render.ts +41 -0
- package/dist/builtin/workflows/src/tui/graph-view-input.ts +82 -24
- package/dist/builtin/workflows/src/tui/graph-view-render.ts +7 -0
- package/dist/builtin/workflows/src/tui/graph-view-state.ts +22 -2
- package/dist/builtin/workflows/src/tui/graph-view-types.ts +4 -5
- package/dist/builtin/workflows/src/tui/overlay-adapter.ts +9 -11
- package/dist/builtin/workflows/src/tui/stage-chat-view-footer-status.ts +9 -3
- package/dist/builtin/workflows/src/tui/stage-chat-view-input.ts +11 -2
- package/dist/builtin/workflows/src/tui/stage-chat-view-live-events.ts +35 -0
- package/dist/builtin/workflows/src/tui/stage-chat-view-state.ts +51 -17
- package/dist/builtin/workflows/src/tui/stage-chat-view-status.ts +36 -0
- package/dist/builtin/workflows/src/tui/stage-chat-view-types.ts +5 -1
- package/dist/builtin/workflows/src/tui/stage-chat-view.ts +3 -1
- package/dist/builtin/workflows/src/tui/status-list.ts +14 -2
- package/dist/builtin/workflows/src/tui/widget.ts +23 -8
- package/dist/builtin/workflows/src/tui/workflow-attach-pane-types.ts +5 -4
- package/dist/builtin/workflows/src/tui/workflow-attach-pane.ts +8 -8
- package/dist/builtin/workflows/src/tui/workflow-resume-selector.ts +151 -0
- package/dist/core/extensions/loader-virtual-modules.d.ts.map +1 -1
- package/dist/core/extensions/loader-virtual-modules.js +47 -30
- package/dist/core/extensions/loader-virtual-modules.js.map +1 -1
- package/dist/core/messages.d.ts +1 -0
- package/dist/core/messages.d.ts.map +1 -1
- package/dist/core/messages.js +46 -1
- package/dist/core/messages.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +12 -0
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager-core.d.ts +15 -7
- package/dist/core/session-manager-core.d.ts.map +1 -1
- package/dist/core/session-manager-core.js +20 -9
- package/dist/core/session-manager-core.js.map +1 -1
- package/dist/core/session-manager-entries.d.ts +2 -2
- package/dist/core/session-manager-entries.d.ts.map +1 -1
- package/dist/core/session-manager-entries.js +9 -3
- package/dist/core/session-manager-entries.js.map +1 -1
- package/dist/core/session-manager-history.d.ts.map +1 -1
- package/dist/core/session-manager-history.js +2 -1
- package/dist/core/session-manager-history.js.map +1 -1
- package/dist/core/session-manager-list.d.ts +3 -3
- package/dist/core/session-manager-list.d.ts.map +1 -1
- package/dist/core/session-manager-list.js +27 -8
- package/dist/core/session-manager-list.js.map +1 -1
- package/dist/core/session-manager-storage.d.ts +3 -1
- package/dist/core/session-manager-storage.d.ts.map +1 -1
- package/dist/core/session-manager-storage.js +55 -12
- package/dist/core/session-manager-storage.js.map +1 -1
- package/dist/core/session-manager-tool-dependencies.d.ts +10 -0
- package/dist/core/session-manager-tool-dependencies.d.ts.map +1 -0
- package/dist/core/session-manager-tool-dependencies.js +133 -0
- package/dist/core/session-manager-tool-dependencies.js.map +1 -0
- package/dist/core/session-manager-types.d.ts +22 -0
- package/dist/core/session-manager-types.d.ts.map +1 -1
- package/dist/core/session-manager-types.js.map +1 -1
- package/dist/core/session-manager.d.ts +2 -2
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +1 -1
- package/dist/core/session-manager.js.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-runtime.d.ts +1 -0
- package/dist/modes/interactive/components/chat-session-host-runtime.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-runtime.js +12 -0
- package/dist/modes/interactive/components/chat-session-host-runtime.js.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-terminal-cleanup.d.ts +4 -0
- package/dist/modes/interactive/components/chat-session-host-terminal-cleanup.d.ts.map +1 -0
- package/dist/modes/interactive/components/chat-session-host-terminal-cleanup.js +131 -0
- package/dist/modes/interactive/components/chat-session-host-terminal-cleanup.js.map +1 -0
- package/dist/modes/interactive/components/chat-session-host.d.ts +2 -0
- package/dist/modes/interactive/components/chat-session-host.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-session-host.js +7 -1
- package/dist/modes/interactive/components/chat-session-host.js.map +1 -1
- package/dist/modes/interactive/components/chat-transcript.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-transcript.js +15 -4
- package/dist/modes/interactive/components/chat-transcript.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts +3 -0
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +26 -0
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/docs/compaction.md +2 -0
- package/docs/models.md +1 -1
- package/docs/providers.md +2 -1
- package/docs/session-format.md +6 -0
- package/docs/sessions.md +6 -0
- package/docs/workflows.md +105 -3
- package/package.json +4 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-manager-history.js","sourceRoot":"","sources":["../../src/core/session-manager-history.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentMessage } from \"@earendil-works/pi-agent-core\";\nimport { createBranchSummaryMessage, createCustomMessage } from \"./messages.ts\";\nimport { contentArrayHasAssistantThinkingBlock } from \"./thinking-blocks.ts\";\nimport type {\n\tContextCompactionEntry,\n\tContextDeletionFilters,\n\tFileEntry,\n\tSessionContext,\n\tSessionEntry,\n\tSessionMessageEntry,\n\tSessionTreeNode,\n} from \"./session-manager-types.ts\";\n\nexport function getLatestCompactionBoundaryEntry(entries: SessionEntry[]): ContextCompactionEntry | null {\n\tfor (let i = entries.length - 1; i >= 0; i--) {\n\t\tconst entry = entries[i];\n\t\tif (entry.type === \"context_compaction\") {\n\t\t\treturn entry;\n\t\t}\n\t}\n\treturn null;\n}\n\n/**\n * Build raw deletion filters from persisted context_compaction entries.\n *\n * These raw filters do not apply replay-safety repair for latest assistant\n * thinking/redacted_thinking blocks or their paired tool results. Production\n * context rebuild paths should prefer `buildEffectiveContextDeletionFilters`\n * or `buildContextDeletionFilteredPath(path)` unless they intentionally need\n * the un-repaired historical deletion plan for diagnostics.\n */\nexport function buildContextDeletionFilters(path: SessionEntry[]): ContextDeletionFilters {\n\tconst deletedEntryIds = new Set<string>();\n\tconst deletedContentBlocks = new Map<string, Set<number>>();\n\n\tfor (const entry of path) {\n\t\tif (entry.type !== \"context_compaction\") continue;\n\t\tfor (const target of entry.deletedTargets) {\n\t\t\tif (target.kind === \"entry\") {\n\t\t\t\tdeletedEntryIds.add(target.entryId);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst existing = deletedContentBlocks.get(target.entryId) ?? new Set<number>();\n\t\t\texisting.add(target.blockIndex);\n\t\t\tdeletedContentBlocks.set(target.entryId, existing);\n\t\t}\n\t}\n\n\treturn { deletedEntryIds, deletedContentBlocks };\n}\n\nfunction getToolCallContentBlockId(block: unknown): string | undefined {\n\tif (!block || typeof block !== \"object\") return undefined;\n\tconst candidate = block as { type?: unknown; id?: unknown };\n\treturn candidate.type === \"toolCall\" && typeof candidate.id === \"string\" ? candidate.id : undefined;\n}\n\nfunction getToolResultCallId(message: AgentMessage): string | undefined {\n\tif (message.role !== \"toolResult\") return undefined;\n\tconst toolCallId = (message as { toolCallId?: unknown }).toolCallId;\n\treturn typeof toolCallId === \"string\" ? toolCallId : undefined;\n}\n\nfunction collectToolCallContentBlockIds(content: readonly unknown[]): Set<string> {\n\tconst toolCallIds = new Set<string>();\n\tfor (const block of content) {\n\t\tconst toolCallId = getToolCallContentBlockId(block);\n\t\tif (toolCallId) toolCallIds.add(toolCallId);\n\t}\n\treturn toolCallIds;\n}\n\nfunction addDeletionTarget(filters: ContextDeletionFilters, target: ContextCompactionEntry[\"deletedTargets\"][number]): void {\n\tif (target.kind === \"entry\") {\n\t\tfilters.deletedEntryIds.add(target.entryId);\n\t\treturn;\n\t}\n\tconst existing = filters.deletedContentBlocks.get(target.entryId) ?? new Set<number>();\n\texisting.add(target.blockIndex);\n\tfilters.deletedContentBlocks.set(target.entryId, existing);\n}\n\nfunction buildToolResultEntryIdsByCallId(path: SessionEntry[]): Map<string, Set<string>> {\n\tconst toolResultEntryIdsByCallId = new Map<string, Set<string>>();\n\tfor (const entry of path) {\n\t\tif (entry.type !== \"message\") continue;\n\t\tconst toolCallId = getToolResultCallId(entry.message);\n\t\tif (!toolCallId) continue;\n\t\tconst existing = toolResultEntryIdsByCallId.get(toolCallId) ?? new Set<string>();\n\t\texisting.add(entry.id);\n\t\ttoolResultEntryIdsByCallId.set(toolCallId, existing);\n\t}\n\treturn toolResultEntryIdsByCallId;\n}\n\nfunction findRetainedThinkingAssistants(\n\tpath: SessionEntry[],\n\tdeletedEntryIds: ReadonlySet<string>,\n): SessionMessageEntry[] {\n\treturn path.filter((entry): entry is SessionMessageEntry => {\n\t\tif (entry.type !== \"message\") return false;\n\t\tif (deletedEntryIds.has(entry.id)) return false;\n\t\tif (entry.message.role !== \"assistant\") return false;\n\t\treturn contentArrayHasAssistantThinkingBlock(entry.message.content);\n\t});\n}\n\nexport function buildEffectiveContextDeletionFilters(path: SessionEntry[]): ContextDeletionFilters {\n\tconst filters = buildContextDeletionFilters(path);\n\tif (!path.some((entry) => entry.type === \"context_compaction\")) return filters;\n\n\tconst rawDeletedEntryIds = new Set<string>();\n\tfor (const compaction of path) {\n\t\tif (compaction.type !== \"context_compaction\") continue;\n\t\tfor (const target of compaction.deletedTargets) {\n\t\t\tif (target.kind === \"entry\") rawDeletedEntryIds.add(target.entryId);\n\t\t}\n\t}\n\tconst retainedThinkingAssistants = findRetainedThinkingAssistants(path, rawDeletedEntryIds);\n\tconst retainedThinkingAssistantIds = new Set(retainedThinkingAssistants.map((entry) => entry.id));\n\tconst retainedThinkingAssistantById = new Map(retainedThinkingAssistants.map((entry) => [entry.id, entry]));\n\tconst toolResultEntryIdsByCallId = buildToolResultEntryIdsByCallId(path);\n\tconst effectiveFilters: ContextDeletionFilters = {\n\t\tdeletedEntryIds: new Set<string>(),\n\t\tdeletedContentBlocks: new Map<string, Set<number>>(),\n\t};\n\tconst allRestoredToolResultEntryIds = new Set<string>();\n\n\tfor (const compaction of path) {\n\t\tif (compaction.type !== \"context_compaction\") continue;\n\t\tfor (const target of compaction.deletedTargets) {\n\t\t\tif (target.kind !== \"content_block\") continue;\n\t\t\tconst retainedThinkingAssistant = retainedThinkingAssistantById.get(target.entryId);\n\t\t\tif (!retainedThinkingAssistant) continue;\n\t\t\tconst content = (retainedThinkingAssistant.message as { content: readonly unknown[] }).content;\n\t\t\tfor (const toolCallId of collectToolCallContentBlockIds(content)) {\n\t\t\t\tfor (const entryId of toolResultEntryIdsByCallId.get(toolCallId) ?? []) {\n\t\t\t\t\tallRestoredToolResultEntryIds.add(entryId);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (const compaction of path) {\n\t\tif (compaction.type !== \"context_compaction\") continue;\n\t\tlet restoresRetainedThinkingAssistant = false;\n\t\tfor (const target of compaction.deletedTargets) {\n\t\t\tif (target.kind === \"content_block\" && retainedThinkingAssistantIds.has(target.entryId)) {\n\t\t\t\trestoresRetainedThinkingAssistant = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tfor (const target of compaction.deletedTargets) {\n\t\t\tif (target.kind === \"content_block\" && retainedThinkingAssistantIds.has(target.entryId)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// When a stale persisted plan tried to partially filter a retained\n\t\t\t// thinking-bearing assistant, treat the same compaction entry as one\n\t\t\t// unsafe unit and restore its paired tool results. Later compaction\n\t\t\t// entries may still trim those restored multi-block results normally,\n\t\t\t// but whole-entry deletion of those paired results remains unsafe in any\n\t\t\t// later compaction because the assistant tool call is retained.\n\t\t\tif (restoresRetainedThinkingAssistant && allRestoredToolResultEntryIds.has(target.entryId)) continue;\n\t\t\tif (target.kind === \"entry\" && allRestoredToolResultEntryIds.has(target.entryId)) continue;\n\t\t\taddDeletionTarget(effectiveFilters, target);\n\t\t}\n\t}\n\n\treturn effectiveFilters;\n}\n\nfunction filterContentArray<T>(content: T[], deletedBlocks: ReadonlySet<number>): T[] {\n\treturn content.filter((_, index) => !deletedBlocks.has(index));\n}\n\nfunction filterMessageContentBlocks(\n\tmessage: AgentMessage,\n\tdeletedBlocks: ReadonlySet<number> | undefined,\n): AgentMessage | undefined {\n\tif (!deletedBlocks || deletedBlocks.size === 0) return message;\n\n\tswitch (message.role) {\n\t\tcase \"user\": {\n\t\t\tif (!Array.isArray(message.content)) return message;\n\t\t\tconst content = filterContentArray(message.content, deletedBlocks);\n\t\t\tif (content.length === 0) return undefined;\n\t\t\treturn { ...message, content };\n\t\t}\n\t\tcase \"assistant\": {\n\t\t\tconst content = filterContentArray(message.content, deletedBlocks);\n\t\t\tif (content.length === 0) return undefined;\n\t\t\treturn { ...message, content };\n\t\t}\n\t\tcase \"toolResult\": {\n\t\t\tif (!Array.isArray(message.content)) return message;\n\t\t\tconst content = filterContentArray(message.content, deletedBlocks);\n\t\t\tif (content.length === 0) return undefined;\n\t\t\treturn { ...message, content };\n\t\t}\n\t\tcase \"custom\": {\n\t\t\tif (!Array.isArray(message.content)) return message;\n\t\t\tconst content = filterContentArray(message.content, deletedBlocks);\n\t\t\tif (content.length === 0) return undefined;\n\t\t\treturn { ...message, content };\n\t\t}\n\t\tcase \"bashExecution\":\n\t\tcase \"branchSummary\":\n\t\t\treturn message;\n\t}\n}\n\n/**\n * Return the active branch path after applying logical context-deletion entries.\n * Whole-entry deletions remove the entry from the path. Content-block deletions\n * clone only affected message/custom-message entries so retained blocks stay verbatim.\n * The optional filters parameter is for callers that already computed effective\n * filters with `buildEffectiveContextDeletionFilters(path)` and want to avoid\n * repeating the repair pass.\n */\nexport function buildContextDeletionFilteredPath(\n\tpath: SessionEntry[],\n\teffectiveFilters: ContextDeletionFilters = buildEffectiveContextDeletionFilters(path),\n): SessionEntry[] {\n\tconst filteredPath: SessionEntry[] = [];\n\n\tfor (const entry of path) {\n\t\tif (effectiveFilters.deletedEntryIds.has(entry.id)) continue;\n\n\t\tconst deletedBlocks = effectiveFilters.deletedContentBlocks.get(entry.id);\n\t\tif (!deletedBlocks || deletedBlocks.size === 0) {\n\t\t\tfilteredPath.push(entry);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (entry.type === \"message\") {\n\t\t\tconst message = filterMessageContentBlocks(entry.message, deletedBlocks);\n\t\t\tif (message) filteredPath.push({ ...entry, message });\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (entry.type === \"custom_message\" && Array.isArray(entry.content)) {\n\t\t\tconst content = filterContentArray(entry.content, deletedBlocks);\n\t\t\tif (content.length > 0) filteredPath.push({ ...entry, content });\n\t\t\tcontinue;\n\t\t}\n\n\t\tfilteredPath.push(entry);\n\t}\n\n\treturn filteredPath;\n}\n\n/**\n * Build the session context from entries using tree traversal.\n * If leafId is provided, walks from that entry to root.\n * Applies context-deletion filtering and includes branch summaries along the path.\n */\nexport function buildSessionContext(\n\tentries: SessionEntry[],\n\tleafId?: string | null,\n\tbyId?: Map<string, SessionEntry>,\n): SessionContext {\n\t// Build uuid index if not available\n\tif (!byId) {\n\t\tbyId = new Map<string, SessionEntry>();\n\t\tfor (const entry of entries) {\n\t\t\tbyId.set(entry.id, entry);\n\t\t}\n\t}\n\n\t// Find leaf\n\tlet leaf: SessionEntry | undefined;\n\tif (leafId === null) {\n\t\t// Explicitly null - return no messages (navigated to before first entry)\n\t\treturn { messages: [], thinkingLevel: \"off\", contextWindow: undefined, model: null };\n\t}\n\tif (leafId) {\n\t\tleaf = byId.get(leafId);\n\t}\n\tif (!leaf) {\n\t\t// Fallback to last entry (when leafId is undefined)\n\t\tleaf = entries[entries.length - 1];\n\t}\n\n\tif (!leaf) {\n\t\treturn { messages: [], thinkingLevel: \"off\", contextWindow: undefined, model: null };\n\t}\n\n\t// Walk from leaf to root, collecting path\n\tconst path = getBranchPath(leaf.id, byId);\n\n\t// Extract settings\n\tlet thinkingLevel = \"off\";\n\tlet contextWindow: number | undefined;\n\tlet model: { provider: string; modelId: string } | null = null;\n\n\tfor (const entry of path) {\n\t\tif (entry.type === \"thinking_level_change\") {\n\t\t\tthinkingLevel = entry.thinkingLevel;\n\t\t} else if (entry.type === \"context_window_change\") {\n\t\t\tcontextWindow = entry.contextWindow;\n\t\t} else if (entry.type === \"model_change\") {\n\t\t\tmodel = { provider: entry.provider, modelId: entry.modelId };\n\t\t} else if (entry.type === \"message\" && entry.message.role === \"assistant\") {\n\t\t\tmodel = { provider: entry.message.provider, modelId: entry.message.model };\n\t\t}\n\t}\n\n\tconst filteredPath = buildContextDeletionFilteredPath(path);\n\n\t// Build active context messages from the filtered path. Legacy \"compaction\"\n\t// entries are archival metadata and intentionally inert here.\n\tconst messages: AgentMessage[] = [];\n\n\tconst appendMessage = (entry: SessionEntry) => {\n\t\tlet message: AgentMessage | undefined;\n\t\tif (entry.type === \"message\") {\n\t\t\tmessage = entry.message;\n\t\t} else if (entry.type === \"custom_message\") {\n\t\t\tmessage = createCustomMessage(\n\t\t\t\tentry.customType,\n\t\t\t\tentry.content,\n\t\t\t\tentry.display,\n\t\t\t\tentry.details,\n\t\t\t\tentry.timestamp,\n\t\t\t\tentry.excludeFromContext,\n\t\t\t);\n\t\t} else if (entry.type === \"branch_summary\" && entry.summary) {\n\t\t\tmessage = createBranchSummaryMessage(entry.summary, entry.fromId, entry.timestamp);\n\t\t}\n\n\t\tif (message) messages.push(message);\n\t};\n\n\tfor (const entry of filteredPath) {\n\t\tappendMessage(entry);\n\t}\n\n\treturn { messages, thinkingLevel, contextWindow, model };\n}\n\nexport interface SessionIndex {\n\tbyId: Map<string, SessionEntry>;\n\tlabelsById: Map<string, string>;\n\tlabelTimestampsById: Map<string, string>;\n\tleafId: string | null;\n}\n\nexport function buildSessionIndex(fileEntries: FileEntry[]): SessionIndex {\n\tconst byId = new Map<string, SessionEntry>();\n\tconst labelsById = new Map<string, string>();\n\tconst labelTimestampsById = new Map<string, string>();\n\tlet leafId: string | null = null;\n\n\tfor (const entry of fileEntries) {\n\t\tif (entry.type === \"session\") continue;\n\t\tbyId.set(entry.id, entry);\n\t\tleafId = entry.id;\n\t\tif (entry.type === \"label\") {\n\t\t\tif (entry.label) {\n\t\t\t\tlabelsById.set(entry.targetId, entry.label);\n\t\t\t\tlabelTimestampsById.set(entry.targetId, entry.timestamp);\n\t\t\t} else {\n\t\t\t\tlabelsById.delete(entry.targetId);\n\t\t\t\tlabelTimestampsById.delete(entry.targetId);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { byId, labelsById, labelTimestampsById, leafId };\n}\n\nexport function getBranchPath(fromId: string | null | undefined, byId: Map<string, SessionEntry>): SessionEntry[] {\n\tconst path: SessionEntry[] = [];\n\tlet current = fromId ? byId.get(fromId) : undefined;\n\twhile (current) {\n\t\tpath.push(current);\n\t\tcurrent = current.parentId ? byId.get(current.parentId) : undefined;\n\t}\n\tpath.reverse();\n\treturn path;\n}\n\nexport function buildSessionTree(\n\tentries: SessionEntry[],\n\tlabelsById: ReadonlyMap<string, string>,\n\tlabelTimestampsById: ReadonlyMap<string, string>,\n): SessionTreeNode[] {\n\tconst nodeMap = new Map<string, SessionTreeNode>();\n\tconst roots: SessionTreeNode[] = [];\n\n\t// Create nodes with resolved labels\n\tfor (const entry of entries) {\n\t\tconst label = labelsById.get(entry.id);\n\t\tconst labelTimestamp = labelTimestampsById.get(entry.id);\n\t\tnodeMap.set(entry.id, { entry, children: [], label, labelTimestamp });\n\t}\n\n\t// Build tree\n\tfor (const entry of entries) {\n\t\tconst node = nodeMap.get(entry.id)!;\n\t\tif (entry.parentId === null || entry.parentId === entry.id) {\n\t\t\troots.push(node);\n\t\t} else {\n\t\t\tconst parent = nodeMap.get(entry.parentId);\n\t\t\tif (parent) {\n\t\t\t\tparent.children.push(node);\n\t\t\t} else {\n\t\t\t\t// Orphan - treat as root\n\t\t\t\troots.push(node);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Sort children by timestamp (oldest first, newest at bottom)\n\t// Use iterative approach to avoid stack overflow on deep trees\n\tconst stack: SessionTreeNode[] = [...roots];\n\twhile (stack.length > 0) {\n\t\tconst node = stack.pop()!;\n\t\tnode.children.sort((a, b) => new Date(a.entry.timestamp).getTime() - new Date(b.entry.timestamp).getTime());\n\t\tstack.push(...node.children);\n\t}\n\n\treturn roots;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"session-manager-history.js","sourceRoot":"","sources":["../../src/core/session-manager-history.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentMessage } from \"@earendil-works/pi-agent-core\";\nimport { createBranchSummaryMessage, createCustomMessage } from \"./messages.ts\";\nimport { contentArrayHasAssistantThinkingBlock } from \"./thinking-blocks.ts\";\nimport { reconcilePersistedToolDependencyFilters } from \"./session-manager-tool-dependencies.ts\";\nimport type {\n\tContextCompactionEntry,\n\tContextDeletionFilters,\n\tFileEntry,\n\tSessionContext,\n\tSessionEntry,\n\tSessionMessageEntry,\n\tSessionTreeNode,\n} from \"./session-manager-types.ts\";\n\nexport function getLatestCompactionBoundaryEntry(entries: SessionEntry[]): ContextCompactionEntry | null {\n\tfor (let i = entries.length - 1; i >= 0; i--) {\n\t\tconst entry = entries[i];\n\t\tif (entry.type === \"context_compaction\") {\n\t\t\treturn entry;\n\t\t}\n\t}\n\treturn null;\n}\n\n/**\n * Build raw deletion filters from persisted context_compaction entries.\n *\n * These raw filters do not apply replay-safety repair for latest assistant\n * thinking/redacted_thinking blocks or their paired tool results. Production\n * context rebuild paths should prefer `buildEffectiveContextDeletionFilters`\n * or `buildContextDeletionFilteredPath(path)` unless they intentionally need\n * the un-repaired historical deletion plan for diagnostics.\n */\nexport function buildContextDeletionFilters(path: SessionEntry[]): ContextDeletionFilters {\n\tconst deletedEntryIds = new Set<string>();\n\tconst deletedContentBlocks = new Map<string, Set<number>>();\n\n\tfor (const entry of path) {\n\t\tif (entry.type !== \"context_compaction\") continue;\n\t\tfor (const target of entry.deletedTargets) {\n\t\t\tif (target.kind === \"entry\") {\n\t\t\t\tdeletedEntryIds.add(target.entryId);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst existing = deletedContentBlocks.get(target.entryId) ?? new Set<number>();\n\t\t\texisting.add(target.blockIndex);\n\t\t\tdeletedContentBlocks.set(target.entryId, existing);\n\t\t}\n\t}\n\n\treturn { deletedEntryIds, deletedContentBlocks };\n}\n\nfunction getToolCallContentBlockId(block: unknown): string | undefined {\n\tif (!block || typeof block !== \"object\") return undefined;\n\tconst candidate = block as { type?: unknown; id?: unknown };\n\treturn candidate.type === \"toolCall\" && typeof candidate.id === \"string\" ? candidate.id : undefined;\n}\n\nfunction getToolResultCallId(message: AgentMessage): string | undefined {\n\tif (message.role !== \"toolResult\") return undefined;\n\tconst toolCallId = (message as { toolCallId?: unknown }).toolCallId;\n\treturn typeof toolCallId === \"string\" ? toolCallId : undefined;\n}\n\nfunction collectToolCallContentBlockIds(content: readonly unknown[]): Set<string> {\n\tconst toolCallIds = new Set<string>();\n\tfor (const block of content) {\n\t\tconst toolCallId = getToolCallContentBlockId(block);\n\t\tif (toolCallId) toolCallIds.add(toolCallId);\n\t}\n\treturn toolCallIds;\n}\n\nfunction addDeletionTarget(filters: ContextDeletionFilters, target: ContextCompactionEntry[\"deletedTargets\"][number]): void {\n\tif (target.kind === \"entry\") {\n\t\tfilters.deletedEntryIds.add(target.entryId);\n\t\treturn;\n\t}\n\tconst existing = filters.deletedContentBlocks.get(target.entryId) ?? new Set<number>();\n\texisting.add(target.blockIndex);\n\tfilters.deletedContentBlocks.set(target.entryId, existing);\n}\n\nfunction buildToolResultEntryIdsByCallId(path: SessionEntry[]): Map<string, Set<string>> {\n\tconst toolResultEntryIdsByCallId = new Map<string, Set<string>>();\n\tfor (const entry of path) {\n\t\tif (entry.type !== \"message\") continue;\n\t\tconst toolCallId = getToolResultCallId(entry.message);\n\t\tif (!toolCallId) continue;\n\t\tconst existing = toolResultEntryIdsByCallId.get(toolCallId) ?? new Set<string>();\n\t\texisting.add(entry.id);\n\t\ttoolResultEntryIdsByCallId.set(toolCallId, existing);\n\t}\n\treturn toolResultEntryIdsByCallId;\n}\n\nfunction findRetainedThinkingAssistants(\n\tpath: SessionEntry[],\n\tdeletedEntryIds: ReadonlySet<string>,\n): SessionMessageEntry[] {\n\treturn path.filter((entry): entry is SessionMessageEntry => {\n\t\tif (entry.type !== \"message\") return false;\n\t\tif (deletedEntryIds.has(entry.id)) return false;\n\t\tif (entry.message.role !== \"assistant\") return false;\n\t\treturn contentArrayHasAssistantThinkingBlock(entry.message.content);\n\t});\n}\n\nexport function buildEffectiveContextDeletionFilters(path: SessionEntry[]): ContextDeletionFilters {\n\tconst filters = buildContextDeletionFilters(path);\n\tif (!path.some((entry) => entry.type === \"context_compaction\")) return filters;\n\n\tconst rawDeletedEntryIds = new Set<string>();\n\tfor (const compaction of path) {\n\t\tif (compaction.type !== \"context_compaction\") continue;\n\t\tfor (const target of compaction.deletedTargets) {\n\t\t\tif (target.kind === \"entry\") rawDeletedEntryIds.add(target.entryId);\n\t\t}\n\t}\n\tconst retainedThinkingAssistants = findRetainedThinkingAssistants(path, rawDeletedEntryIds);\n\tconst retainedThinkingAssistantIds = new Set(retainedThinkingAssistants.map((entry) => entry.id));\n\tconst retainedThinkingAssistantById = new Map(retainedThinkingAssistants.map((entry) => [entry.id, entry]));\n\tconst toolResultEntryIdsByCallId = buildToolResultEntryIdsByCallId(path);\n\tconst effectiveFilters: ContextDeletionFilters = {\n\t\tdeletedEntryIds: new Set<string>(),\n\t\tdeletedContentBlocks: new Map<string, Set<number>>(),\n\t};\n\tconst allRestoredToolResultEntryIds = new Set<string>();\n\n\tfor (const compaction of path) {\n\t\tif (compaction.type !== \"context_compaction\") continue;\n\t\tfor (const target of compaction.deletedTargets) {\n\t\t\tif (target.kind !== \"content_block\") continue;\n\t\t\tconst retainedThinkingAssistant = retainedThinkingAssistantById.get(target.entryId);\n\t\t\tif (!retainedThinkingAssistant) continue;\n\t\t\tconst content = (retainedThinkingAssistant.message as { content: readonly unknown[] }).content;\n\t\t\tfor (const toolCallId of collectToolCallContentBlockIds(content)) {\n\t\t\t\tfor (const entryId of toolResultEntryIdsByCallId.get(toolCallId) ?? []) {\n\t\t\t\t\tallRestoredToolResultEntryIds.add(entryId);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (const compaction of path) {\n\t\tif (compaction.type !== \"context_compaction\") continue;\n\t\tlet restoresRetainedThinkingAssistant = false;\n\t\tfor (const target of compaction.deletedTargets) {\n\t\t\tif (target.kind === \"content_block\" && retainedThinkingAssistantIds.has(target.entryId)) {\n\t\t\t\trestoresRetainedThinkingAssistant = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tfor (const target of compaction.deletedTargets) {\n\t\t\tif (target.kind === \"content_block\" && retainedThinkingAssistantIds.has(target.entryId)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// When a stale persisted plan tried to partially filter a retained\n\t\t\t// thinking-bearing assistant, treat the same compaction entry as one\n\t\t\t// unsafe unit and restore its paired tool results. Later compaction\n\t\t\t// entries may still trim those restored multi-block results normally,\n\t\t\t// but whole-entry deletion of those paired results remains unsafe in any\n\t\t\t// later compaction because the assistant tool call is retained.\n\t\t\tif (restoresRetainedThinkingAssistant && allRestoredToolResultEntryIds.has(target.entryId)) continue;\n\t\t\tif (target.kind === \"entry\" && allRestoredToolResultEntryIds.has(target.entryId)) continue;\n\t\t\taddDeletionTarget(effectiveFilters, target);\n\t\t}\n\t}\n\n\treturn reconcilePersistedToolDependencyFilters(path, effectiveFilters);\n}\n\nfunction filterContentArray<T>(content: T[], deletedBlocks: ReadonlySet<number>): T[] {\n\treturn content.filter((_, index) => !deletedBlocks.has(index));\n}\n\nfunction filterMessageContentBlocks(\n\tmessage: AgentMessage,\n\tdeletedBlocks: ReadonlySet<number> | undefined,\n): AgentMessage | undefined {\n\tif (!deletedBlocks || deletedBlocks.size === 0) return message;\n\n\tswitch (message.role) {\n\t\tcase \"user\": {\n\t\t\tif (!Array.isArray(message.content)) return message;\n\t\t\tconst content = filterContentArray(message.content, deletedBlocks);\n\t\t\tif (content.length === 0) return undefined;\n\t\t\treturn { ...message, content };\n\t\t}\n\t\tcase \"assistant\": {\n\t\t\tconst content = filterContentArray(message.content, deletedBlocks);\n\t\t\tif (content.length === 0) return undefined;\n\t\t\treturn { ...message, content };\n\t\t}\n\t\tcase \"toolResult\": {\n\t\t\tif (!Array.isArray(message.content)) return message;\n\t\t\tconst content = filterContentArray(message.content, deletedBlocks);\n\t\t\tif (content.length === 0) return undefined;\n\t\t\treturn { ...message, content };\n\t\t}\n\t\tcase \"custom\": {\n\t\t\tif (!Array.isArray(message.content)) return message;\n\t\t\tconst content = filterContentArray(message.content, deletedBlocks);\n\t\t\tif (content.length === 0) return undefined;\n\t\t\treturn { ...message, content };\n\t\t}\n\t\tcase \"bashExecution\":\n\t\tcase \"branchSummary\":\n\t\t\treturn message;\n\t}\n}\n\n/**\n * Return the active branch path after applying logical context-deletion entries.\n * Whole-entry deletions remove the entry from the path. Content-block deletions\n * clone only affected message/custom-message entries so retained blocks stay verbatim.\n * The optional filters parameter is for callers that already computed effective\n * filters with `buildEffectiveContextDeletionFilters(path)` and want to avoid\n * repeating the repair pass.\n */\nexport function buildContextDeletionFilteredPath(\n\tpath: SessionEntry[],\n\teffectiveFilters: ContextDeletionFilters = buildEffectiveContextDeletionFilters(path),\n): SessionEntry[] {\n\tconst filteredPath: SessionEntry[] = [];\n\n\tfor (const entry of path) {\n\t\tif (effectiveFilters.deletedEntryIds.has(entry.id)) continue;\n\n\t\tconst deletedBlocks = effectiveFilters.deletedContentBlocks.get(entry.id);\n\t\tif (!deletedBlocks || deletedBlocks.size === 0) {\n\t\t\tfilteredPath.push(entry);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (entry.type === \"message\") {\n\t\t\tconst message = filterMessageContentBlocks(entry.message, deletedBlocks);\n\t\t\tif (message) filteredPath.push({ ...entry, message });\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (entry.type === \"custom_message\" && Array.isArray(entry.content)) {\n\t\t\tconst content = filterContentArray(entry.content, deletedBlocks);\n\t\t\tif (content.length > 0) filteredPath.push({ ...entry, content });\n\t\t\tcontinue;\n\t\t}\n\n\t\tfilteredPath.push(entry);\n\t}\n\n\treturn filteredPath;\n}\n\n/**\n * Build the session context from entries using tree traversal.\n * If leafId is provided, walks from that entry to root.\n * Applies context-deletion filtering and includes branch summaries along the path.\n */\nexport function buildSessionContext(\n\tentries: SessionEntry[],\n\tleafId?: string | null,\n\tbyId?: Map<string, SessionEntry>,\n): SessionContext {\n\t// Build uuid index if not available\n\tif (!byId) {\n\t\tbyId = new Map<string, SessionEntry>();\n\t\tfor (const entry of entries) {\n\t\t\tbyId.set(entry.id, entry);\n\t\t}\n\t}\n\n\t// Find leaf\n\tlet leaf: SessionEntry | undefined;\n\tif (leafId === null) {\n\t\t// Explicitly null - return no messages (navigated to before first entry)\n\t\treturn { messages: [], thinkingLevel: \"off\", contextWindow: undefined, model: null };\n\t}\n\tif (leafId) {\n\t\tleaf = byId.get(leafId);\n\t}\n\tif (!leaf) {\n\t\t// Fallback to last entry (when leafId is undefined)\n\t\tleaf = entries[entries.length - 1];\n\t}\n\n\tif (!leaf) {\n\t\treturn { messages: [], thinkingLevel: \"off\", contextWindow: undefined, model: null };\n\t}\n\n\t// Walk from leaf to root, collecting path\n\tconst path = getBranchPath(leaf.id, byId);\n\n\t// Extract settings\n\tlet thinkingLevel = \"off\";\n\tlet contextWindow: number | undefined;\n\tlet model: { provider: string; modelId: string } | null = null;\n\n\tfor (const entry of path) {\n\t\tif (entry.type === \"thinking_level_change\") {\n\t\t\tthinkingLevel = entry.thinkingLevel;\n\t\t} else if (entry.type === \"context_window_change\") {\n\t\t\tcontextWindow = entry.contextWindow;\n\t\t} else if (entry.type === \"model_change\") {\n\t\t\tmodel = { provider: entry.provider, modelId: entry.modelId };\n\t\t} else if (entry.type === \"message\" && entry.message.role === \"assistant\") {\n\t\t\tmodel = { provider: entry.message.provider, modelId: entry.message.model };\n\t\t}\n\t}\n\n\tconst filteredPath = buildContextDeletionFilteredPath(path);\n\n\t// Build active context messages from the filtered path. Legacy \"compaction\"\n\t// entries are archival metadata and intentionally inert here.\n\tconst messages: AgentMessage[] = [];\n\n\tconst appendMessage = (entry: SessionEntry) => {\n\t\tlet message: AgentMessage | undefined;\n\t\tif (entry.type === \"message\") {\n\t\t\tmessage = entry.message;\n\t\t} else if (entry.type === \"custom_message\") {\n\t\t\tmessage = createCustomMessage(\n\t\t\t\tentry.customType,\n\t\t\t\tentry.content,\n\t\t\t\tentry.display,\n\t\t\t\tentry.details,\n\t\t\t\tentry.timestamp,\n\t\t\t\tentry.excludeFromContext,\n\t\t\t);\n\t\t} else if (entry.type === \"branch_summary\" && entry.summary) {\n\t\t\tmessage = createBranchSummaryMessage(entry.summary, entry.fromId, entry.timestamp);\n\t\t}\n\n\t\tif (message) messages.push(message);\n\t};\n\n\tfor (const entry of filteredPath) {\n\t\tappendMessage(entry);\n\t}\n\n\treturn { messages, thinkingLevel, contextWindow, model };\n}\n\nexport interface SessionIndex {\n\tbyId: Map<string, SessionEntry>;\n\tlabelsById: Map<string, string>;\n\tlabelTimestampsById: Map<string, string>;\n\tleafId: string | null;\n}\n\nexport function buildSessionIndex(fileEntries: FileEntry[]): SessionIndex {\n\tconst byId = new Map<string, SessionEntry>();\n\tconst labelsById = new Map<string, string>();\n\tconst labelTimestampsById = new Map<string, string>();\n\tlet leafId: string | null = null;\n\n\tfor (const entry of fileEntries) {\n\t\tif (entry.type === \"session\") continue;\n\t\tbyId.set(entry.id, entry);\n\t\tleafId = entry.id;\n\t\tif (entry.type === \"label\") {\n\t\t\tif (entry.label) {\n\t\t\t\tlabelsById.set(entry.targetId, entry.label);\n\t\t\t\tlabelTimestampsById.set(entry.targetId, entry.timestamp);\n\t\t\t} else {\n\t\t\t\tlabelsById.delete(entry.targetId);\n\t\t\t\tlabelTimestampsById.delete(entry.targetId);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { byId, labelsById, labelTimestampsById, leafId };\n}\n\nexport function getBranchPath(fromId: string | null | undefined, byId: Map<string, SessionEntry>): SessionEntry[] {\n\tconst path: SessionEntry[] = [];\n\tlet current = fromId ? byId.get(fromId) : undefined;\n\twhile (current) {\n\t\tpath.push(current);\n\t\tcurrent = current.parentId ? byId.get(current.parentId) : undefined;\n\t}\n\tpath.reverse();\n\treturn path;\n}\n\nexport function buildSessionTree(\n\tentries: SessionEntry[],\n\tlabelsById: ReadonlyMap<string, string>,\n\tlabelTimestampsById: ReadonlyMap<string, string>,\n): SessionTreeNode[] {\n\tconst nodeMap = new Map<string, SessionTreeNode>();\n\tconst roots: SessionTreeNode[] = [];\n\n\t// Create nodes with resolved labels\n\tfor (const entry of entries) {\n\t\tconst label = labelsById.get(entry.id);\n\t\tconst labelTimestamp = labelTimestampsById.get(entry.id);\n\t\tnodeMap.set(entry.id, { entry, children: [], label, labelTimestamp });\n\t}\n\n\t// Build tree\n\tfor (const entry of entries) {\n\t\tconst node = nodeMap.get(entry.id)!;\n\t\tif (entry.parentId === null || entry.parentId === entry.id) {\n\t\t\troots.push(node);\n\t\t} else {\n\t\t\tconst parent = nodeMap.get(entry.parentId);\n\t\t\tif (parent) {\n\t\t\t\tparent.children.push(node);\n\t\t\t} else {\n\t\t\t\t// Orphan - treat as root\n\t\t\t\troots.push(node);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Sort children by timestamp (oldest first, newest at bottom)\n\t// Use iterative approach to avoid stack overflow on deep trees\n\tconst stack: SessionTreeNode[] = [...roots];\n\twhile (stack.length > 0) {\n\t\tconst node = stack.pop()!;\n\t\tnode.children.sort((a, b) => new Date(a.entry.timestamp).getTime() - new Date(b.entry.timestamp).getTime());\n\t\tstack.push(...node.children);\n\t}\n\n\treturn roots;\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { SessionInfo, SessionListProgress } from "./session-manager-types.ts";
|
|
2
|
-
export declare function listSessionsFromDir(dir: string, onProgress?: SessionListProgress, progressOffset?: number, progressTotal?: number): Promise<SessionInfo[]>;
|
|
3
|
-
export declare function listProjectSessions(cwd: string, sessionDir?: string, onProgress?: SessionListProgress): Promise<SessionInfo[]>;
|
|
4
|
-
export declare function listAllSessions(sessionDirOrOnProgress?: string | SessionListProgress, onProgress?: SessionListProgress): Promise<SessionInfo[]>;
|
|
2
|
+
export declare function listSessionsFromDir(dir: string, onProgress?: SessionListProgress, progressOffset?: number, progressTotal?: number, includeInternal?: boolean): Promise<SessionInfo[]>;
|
|
3
|
+
export declare function listProjectSessions(cwd: string, sessionDir?: string, onProgress?: SessionListProgress, includeInternal?: boolean): Promise<SessionInfo[]>;
|
|
4
|
+
export declare function listAllSessions(sessionDirOrOnProgress?: string | SessionListProgress, onProgress?: SessionListProgress, includeInternal?: boolean): Promise<SessionInfo[]>;
|
|
5
5
|
//# sourceMappingURL=session-manager-list.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-manager-list.d.ts","sourceRoot":"","sources":["../../src/core/session-manager-list.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAIX,WAAW,EAEX,mBAAmB,EAEnB,MAAM,4BAA4B,CAAC;AAmHpC,wBAAsB,mBAAmB,CACxC,GAAG,EAAE,MAAM,EACX,UAAU,CAAC,EAAE,mBAAmB,EAChC,cAAc,SAAI,EAClB,aAAa,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,WAAW,EAAE,CAAC,CA8BxB;AAED,wBAAsB,mBAAmB,CACxC,GAAG,EAAE,MAAM,EACX,UAAU,CAAC,EAAE,MAAM,EACnB,UAAU,CAAC,EAAE,mBAAmB,GAC9B,OAAO,CAAC,WAAW,EAAE,CAAC,CASxB;AAED,wBAAsB,eAAe,CACpC,sBAAsB,CAAC,EAAE,MAAM,GAAG,mBAAmB,EACrD,UAAU,CAAC,EAAE,mBAAmB,GAC9B,OAAO,CAAC,WAAW,EAAE,CAAC,CAyDxB","sourcesContent":["import type { AgentMessage } from \"@earendil-works/pi-agent-core\";\nimport type { Message, TextContent } from \"@earendil-works/pi-ai\";\nimport { existsSync } from \"fs\";\nimport { readdir, readFile, stat } from \"fs/promises\";\nimport { join } from \"path\";\nimport { getSessionsDir } from \"../config.ts\";\nimport { normalizePath, resolvePath } from \"../utils/paths.ts\";\nimport { parseSessionEntries } from \"./session-manager-migrations.ts\";\nimport { getDefaultSessionDir, getDefaultSessionDirPath } from \"./session-manager-paths.ts\";\nimport {\n\tsessionCwdMatches,\n} from \"./session-manager-storage.ts\";\nimport type {\n\tFileEntry,\n\tSessionEntryBase,\n\tSessionHeader,\n\tSessionInfo,\n\tSessionInfoEntry,\n\tSessionListProgress,\n\tSessionMessageEntry,\n} from \"./session-manager-types.ts\";\n\nfunction isMessageWithContent(message: AgentMessage): message is Message {\n\treturn typeof (message as Message).role === \"string\" && \"content\" in message;\n}\n\nfunction extractTextContent(message: Message): string {\n\tconst content = message.content;\n\tif (typeof content === \"string\") {\n\t\treturn content;\n\t}\n\treturn content\n\t\t.filter((block): block is TextContent => block.type === \"text\")\n\t\t.map((block) => block.text)\n\t\t.join(\" \");\n}\n\nfunction getLastActivityTime(entries: FileEntry[]): number | undefined {\n\tlet lastActivityTime: number | undefined;\n\n\tfor (const entry of entries) {\n\t\tif (entry.type !== \"message\") continue;\n\n\t\tconst message = (entry as SessionMessageEntry).message;\n\t\tif (!isMessageWithContent(message)) continue;\n\t\tif (message.role !== \"user\" && message.role !== \"assistant\") continue;\n\n\t\tconst msgTimestamp = (message as { timestamp?: number }).timestamp;\n\t\tif (typeof msgTimestamp === \"number\") {\n\t\t\tlastActivityTime = Math.max(lastActivityTime ?? 0, msgTimestamp);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst entryTimestamp = (entry as SessionEntryBase).timestamp;\n\t\tif (typeof entryTimestamp === \"string\") {\n\t\t\tconst t = new Date(entryTimestamp).getTime();\n\t\t\tif (!Number.isNaN(t)) {\n\t\t\t\tlastActivityTime = Math.max(lastActivityTime ?? 0, t);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn lastActivityTime;\n}\n\nfunction getSessionModifiedDate(entries: FileEntry[], header: SessionHeader, statsMtime: Date): Date {\n\tconst lastActivityTime = getLastActivityTime(entries);\n\tif (typeof lastActivityTime === \"number\" && lastActivityTime > 0) {\n\t\treturn new Date(lastActivityTime);\n\t}\n\n\tconst headerTime = typeof header.timestamp === \"string\" ? new Date(header.timestamp).getTime() : NaN;\n\treturn !Number.isNaN(headerTime) ? new Date(headerTime) : statsMtime;\n}\n\nasync function buildSessionInfo(filePath: string): Promise<SessionInfo | null> {\n\ttry {\n\t\tconst content = await readFile(filePath, \"utf8\");\n\t\tconst entries = parseSessionEntries(content);\n\n\t\tif (entries.length === 0) return null;\n\t\tconst header = entries[0];\n\t\tif (header.type !== \"session\") return null;\n\n\t\tconst stats = await stat(filePath);\n\t\tlet messageCount = 0;\n\t\tlet firstMessage = \"\";\n\t\tconst allMessages: string[] = [];\n\t\tlet name: string | undefined;\n\n\t\tfor (const entry of entries) {\n\t\t\t// Extract session name (use latest, including explicit clears)\n\t\t\tif (entry.type === \"session_info\") {\n\t\t\t\tconst infoEntry = entry as SessionInfoEntry;\n\t\t\t\tname = infoEntry.name?.trim() || undefined;\n\t\t\t}\n\n\t\t\tif (entry.type !== \"message\") continue;\n\t\t\tmessageCount++;\n\n\t\t\tconst message = (entry as SessionMessageEntry).message;\n\t\t\tif (!isMessageWithContent(message)) continue;\n\t\t\tif (message.role !== \"user\" && message.role !== \"assistant\") continue;\n\n\t\t\tconst textContent = extractTextContent(message);\n\t\t\tif (!textContent) continue;\n\n\t\t\tallMessages.push(textContent);\n\t\t\tif (!firstMessage && message.role === \"user\") {\n\t\t\t\tfirstMessage = textContent;\n\t\t\t}\n\t\t}\n\n\t\tconst cwd = typeof (header as SessionHeader).cwd === \"string\" ? (header as SessionHeader).cwd : \"\";\n\t\tconst parentSessionPath = (header as SessionHeader).parentSession;\n\n\t\tconst modified = getSessionModifiedDate(entries, header as SessionHeader, stats.mtime);\n\n\t\treturn {\n\t\t\tpath: filePath,\n\t\t\tid: (header as SessionHeader).id,\n\t\t\tcwd,\n\t\t\tname,\n\t\t\tparentSessionPath,\n\t\t\tcreated: new Date((header as SessionHeader).timestamp),\n\t\t\tmodified,\n\t\t\tmessageCount,\n\t\t\tfirstMessage: firstMessage || \"(no messages)\",\n\t\t\tallMessagesText: allMessages.join(\" \"),\n\t\t};\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nexport async function listSessionsFromDir(\n\tdir: string,\n\tonProgress?: SessionListProgress,\n\tprogressOffset = 0,\n\tprogressTotal?: number,\n): Promise<SessionInfo[]> {\n\tconst sessions: SessionInfo[] = [];\n\tif (!existsSync(dir)) {\n\t\treturn sessions;\n\t}\n\n\ttry {\n\t\tconst dirEntries = await readdir(dir);\n\t\tconst files = dirEntries.filter((f) => f.endsWith(\".jsonl\")).map((f) => join(dir, f));\n\t\tconst total = progressTotal ?? files.length;\n\n\t\tlet loaded = 0;\n\t\tconst results = await Promise.all(\n\t\t\tfiles.map(async (file) => {\n\t\t\t\tconst info = await buildSessionInfo(file);\n\t\t\t\tloaded++;\n\t\t\t\tonProgress?.(progressOffset + loaded, total);\n\t\t\t\treturn info;\n\t\t\t}),\n\t\t);\n\t\tfor (const info of results) {\n\t\t\tif (info) {\n\t\t\t\tsessions.push(info);\n\t\t\t}\n\t\t}\n\t} catch {\n\t\t// Return empty list on error\n\t}\n\n\treturn sessions;\n}\n\nexport async function listProjectSessions(\n\tcwd: string,\n\tsessionDir?: string,\n\tonProgress?: SessionListProgress,\n): Promise<SessionInfo[]> {\n\tconst dir = sessionDir ? normalizePath(sessionDir) : getDefaultSessionDir(cwd);\n\tconst filterCwd = sessionDir !== undefined && dir !== getDefaultSessionDirPath(cwd);\n\tconst resolvedCwd = resolvePath(cwd);\n\tconst sessions = (await listSessionsFromDir(dir, onProgress)).filter(\n\t\t(session) => !filterCwd || sessionCwdMatches(session.cwd, resolvedCwd),\n\t);\n\tsessions.sort((a, b) => b.modified.getTime() - a.modified.getTime());\n\treturn sessions;\n}\n\nexport async function listAllSessions(\n\tsessionDirOrOnProgress?: string | SessionListProgress,\n\tonProgress?: SessionListProgress,\n): Promise<SessionInfo[]> {\n\tconst customSessionDir =\n\t\ttypeof sessionDirOrOnProgress === \"string\" ? normalizePath(sessionDirOrOnProgress) : undefined;\n\tconst progress = typeof sessionDirOrOnProgress === \"function\" ? sessionDirOrOnProgress : onProgress;\n\tif (customSessionDir) {\n\t\tconst sessions = await listSessionsFromDir(customSessionDir, progress);\n\t\tsessions.sort((a, b) => b.modified.getTime() - a.modified.getTime());\n\t\treturn sessions;\n\t}\n\n\tconst sessionsDir = getSessionsDir();\n\n\ttry {\n\t\tif (!existsSync(sessionsDir)) {\n\t\t\treturn [];\n\t\t}\n\t\tconst entries = await readdir(sessionsDir, { withFileTypes: true });\n\t\tconst dirs = entries.filter((e) => e.isDirectory()).map((e) => join(sessionsDir, e.name));\n\n\t\t// Count total files first for accurate progress\n\t\tlet totalFiles = 0;\n\t\tconst dirFiles: string[][] = [];\n\t\tfor (const dir of dirs) {\n\t\t\ttry {\n\t\t\t\tconst files = (await readdir(dir)).filter((f) => f.endsWith(\".jsonl\"));\n\t\t\t\tdirFiles.push(files.map((f) => join(dir, f)));\n\t\t\t\ttotalFiles += files.length;\n\t\t\t} catch {\n\t\t\t\tdirFiles.push([]);\n\t\t\t}\n\t\t}\n\n\t\t// Process all files with progress tracking\n\t\tlet loaded = 0;\n\t\tconst sessions: SessionInfo[] = [];\n\t\tconst allFiles = dirFiles.flat();\n\n\t\tconst results = await Promise.all(\n\t\t\tallFiles.map(async (file) => {\n\t\t\t\tconst info = await buildSessionInfo(file);\n\t\t\t\tloaded++;\n\t\t\t\tprogress?.(loaded, totalFiles);\n\t\t\t\treturn info;\n\t\t\t}),\n\t\t);\n\n\t\tfor (const info of results) {\n\t\t\tif (info) {\n\t\t\t\tsessions.push(info);\n\t\t\t}\n\t\t}\n\n\t\tsessions.sort((a, b) => b.modified.getTime() - a.modified.getTime());\n\t\treturn sessions;\n\t} catch {\n\t\treturn [];\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"session-manager-list.d.ts","sourceRoot":"","sources":["../../src/core/session-manager-list.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAIX,WAAW,EAEX,mBAAmB,EAGnB,MAAM,4BAA4B,CAAC;AAyHpC,wBAAsB,mBAAmB,CACxC,GAAG,EAAE,MAAM,EACX,UAAU,CAAC,EAAE,mBAAmB,EAChC,cAAc,SAAI,EAClB,aAAa,CAAC,EAAE,MAAM,EACtB,eAAe,UAAQ,GACrB,OAAO,CAAC,WAAW,EAAE,CAAC,CAqCxB;AAED,wBAAsB,mBAAmB,CACxC,GAAG,EAAE,MAAM,EACX,UAAU,CAAC,EAAE,MAAM,EACnB,UAAU,CAAC,EAAE,mBAAmB,EAChC,eAAe,UAAQ,GACrB,OAAO,CAAC,WAAW,EAAE,CAAC,CASxB;AAED,wBAAsB,eAAe,CACpC,sBAAsB,CAAC,EAAE,MAAM,GAAG,mBAAmB,EACrD,UAAU,CAAC,EAAE,mBAAmB,EAChC,eAAe,UAAQ,GACrB,OAAO,CAAC,WAAW,EAAE,CAAC,CAgExB","sourcesContent":["import type { AgentMessage } from \"@earendil-works/pi-agent-core\";\nimport type { Message, TextContent } from \"@earendil-works/pi-ai\";\nimport { existsSync } from \"fs\";\nimport { readdir, readFile, stat } from \"fs/promises\";\nimport { join } from \"path\";\nimport { getSessionsDir } from \"../config.ts\";\nimport { normalizePath, resolvePath } from \"../utils/paths.ts\";\nimport { parseSessionEntries } from \"./session-manager-migrations.ts\";\nimport { getDefaultSessionDir, getDefaultSessionDirPath } from \"./session-manager-paths.ts\";\nimport {\n\tisInternalHeader,\n\treadSessionHeader,\n\tsessionCwdMatches,\n} from \"./session-manager-storage.ts\";\nimport type {\n\tFileEntry,\n\tSessionEntryBase,\n\tSessionHeader,\n\tSessionInfo,\n\tSessionInfoEntry,\n\tSessionListProgress,\n\tSessionMessageEntry,\n\tSessionWorkflowMetadata,\n} from \"./session-manager-types.ts\";\n\nfunction isMessageWithContent(message: AgentMessage): message is Message {\n\treturn typeof (message as Message).role === \"string\" && \"content\" in message;\n}\n\nfunction extractTextContent(message: Message): string {\n\tconst content = message.content;\n\tif (typeof content === \"string\") {\n\t\treturn content;\n\t}\n\treturn content\n\t\t.filter((block): block is TextContent => block.type === \"text\")\n\t\t.map((block) => block.text)\n\t\t.join(\" \");\n}\n\nfunction getLastActivityTime(entries: FileEntry[]): number | undefined {\n\tlet lastActivityTime: number | undefined;\n\n\tfor (const entry of entries) {\n\t\tif (entry.type !== \"message\") continue;\n\n\t\tconst message = (entry as SessionMessageEntry).message;\n\t\tif (!isMessageWithContent(message)) continue;\n\t\tif (message.role !== \"user\" && message.role !== \"assistant\") continue;\n\n\t\tconst msgTimestamp = (message as { timestamp?: number }).timestamp;\n\t\tif (typeof msgTimestamp === \"number\") {\n\t\t\tlastActivityTime = Math.max(lastActivityTime ?? 0, msgTimestamp);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst entryTimestamp = (entry as SessionEntryBase).timestamp;\n\t\tif (typeof entryTimestamp === \"string\") {\n\t\t\tconst t = new Date(entryTimestamp).getTime();\n\t\t\tif (!Number.isNaN(t)) {\n\t\t\t\tlastActivityTime = Math.max(lastActivityTime ?? 0, t);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn lastActivityTime;\n}\n\nfunction getSessionModifiedDate(entries: FileEntry[], header: SessionHeader, statsMtime: Date): Date {\n\tconst lastActivityTime = getLastActivityTime(entries);\n\tif (typeof lastActivityTime === \"number\" && lastActivityTime > 0) {\n\t\treturn new Date(lastActivityTime);\n\t}\n\n\tconst headerTime = typeof header.timestamp === \"string\" ? new Date(header.timestamp).getTime() : NaN;\n\treturn !Number.isNaN(headerTime) ? new Date(headerTime) : statsMtime;\n}\n\nasync function buildSessionInfo(filePath: string): Promise<SessionInfo | null> {\n\ttry {\n\t\tconst content = await readFile(filePath, \"utf8\");\n\t\tconst entries = parseSessionEntries(content);\n\n\t\tif (entries.length === 0) return null;\n\t\tconst header = entries[0];\n\t\tif (header.type !== \"session\") return null;\n\n\t\tconst stats = await stat(filePath);\n\t\tlet messageCount = 0;\n\t\tlet firstMessage = \"\";\n\t\tconst allMessages: string[] = [];\n\t\tlet name: string | undefined;\n\n\t\tfor (const entry of entries) {\n\t\t\t// Extract session name (use latest, including explicit clears)\n\t\t\tif (entry.type === \"session_info\") {\n\t\t\t\tconst infoEntry = entry as SessionInfoEntry;\n\t\t\t\tname = infoEntry.name?.trim() || undefined;\n\t\t\t}\n\n\t\t\tif (entry.type !== \"message\") continue;\n\t\t\tmessageCount++;\n\n\t\t\tconst message = (entry as SessionMessageEntry).message;\n\t\t\tif (!isMessageWithContent(message)) continue;\n\t\t\tif (message.role !== \"user\" && message.role !== \"assistant\") continue;\n\n\t\t\tconst textContent = extractTextContent(message);\n\t\t\tif (!textContent) continue;\n\n\t\t\tallMessages.push(textContent);\n\t\t\tif (!firstMessage && message.role === \"user\") {\n\t\t\t\tfirstMessage = textContent;\n\t\t\t}\n\t\t}\n\n\t\tconst cwd = typeof (header as SessionHeader).cwd === \"string\" ? (header as SessionHeader).cwd : \"\";\n\t\tconst parentSessionPath = (header as SessionHeader).parentSession;\n\t\tconst internal = (header as SessionHeader).internal === true ? true : undefined;\n\t\tconst workflowHeader = (header as SessionHeader).workflow as SessionWorkflowMetadata | undefined;\n\t\tconst workflow =\n\t\t\tworkflowHeader && typeof workflowHeader.runId === \"string\" ? workflowHeader : undefined;\n\n\t\tconst modified = getSessionModifiedDate(entries, header as SessionHeader, stats.mtime);\n\n\t\treturn {\n\t\t\tpath: filePath,\n\t\t\tid: (header as SessionHeader).id,\n\t\t\tcwd,\n\t\t\tname,\n\t\t\tparentSessionPath,\n\t\t\t...(internal ? { internal } : {}),\n\t\t\t...(workflow ? { workflow } : {}),\n\t\t\tcreated: new Date((header as SessionHeader).timestamp),\n\t\t\tmodified,\n\t\t\tmessageCount,\n\t\t\tfirstMessage: firstMessage || \"(no messages)\",\n\t\t\tallMessagesText: allMessages.join(\" \"),\n\t\t};\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nexport async function listSessionsFromDir(\n\tdir: string,\n\tonProgress?: SessionListProgress,\n\tprogressOffset = 0,\n\tprogressTotal?: number,\n\tincludeInternal = false,\n): Promise<SessionInfo[]> {\n\tconst sessions: SessionInfo[] = [];\n\tif (!existsSync(dir)) {\n\t\treturn sessions;\n\t}\n\n\ttry {\n\t\tconst dirEntries = await readdir(dir);\n\t\tconst files = dirEntries.filter((f) => f.endsWith(\".jsonl\")).map((f) => join(dir, f));\n\t\tconst total = progressTotal ?? files.length;\n\n\t\tlet loaded = 0;\n\t\tconst results = await Promise.all(\n\t\t\tfiles.map(async (file) => {\n\t\t\t\t// Prefilter via the header so hidden/internal sessions are skipped\n\t\t\t\t// before the expensive full-transcript parse in buildSessionInfo.\n\t\t\t\tif (!includeInternal && isInternalHeader(readSessionHeader(file))) {\n\t\t\t\t\tloaded++;\n\t\t\t\t\tonProgress?.(progressOffset + loaded, total);\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\tconst info = await buildSessionInfo(file);\n\t\t\t\tloaded++;\n\t\t\t\tonProgress?.(progressOffset + loaded, total);\n\t\t\t\treturn info;\n\t\t\t}),\n\t\t);\n\t\tfor (const info of results) {\n\t\t\tif (info && (includeInternal || !info.internal)) {\n\t\t\t\tsessions.push(info);\n\t\t\t}\n\t\t}\n\t} catch {\n\t\t// Return empty list on error\n\t}\n\n\treturn sessions;\n}\n\nexport async function listProjectSessions(\n\tcwd: string,\n\tsessionDir?: string,\n\tonProgress?: SessionListProgress,\n\tincludeInternal = false,\n): Promise<SessionInfo[]> {\n\tconst dir = sessionDir ? normalizePath(sessionDir) : getDefaultSessionDir(cwd);\n\tconst filterCwd = sessionDir !== undefined && dir !== getDefaultSessionDirPath(cwd);\n\tconst resolvedCwd = resolvePath(cwd);\n\tconst sessions = (await listSessionsFromDir(dir, onProgress, 0, undefined, includeInternal)).filter(\n\t\t(session) => !filterCwd || sessionCwdMatches(session.cwd, resolvedCwd),\n\t);\n\tsessions.sort((a, b) => b.modified.getTime() - a.modified.getTime());\n\treturn sessions;\n}\n\nexport async function listAllSessions(\n\tsessionDirOrOnProgress?: string | SessionListProgress,\n\tonProgress?: SessionListProgress,\n\tincludeInternal = false,\n): Promise<SessionInfo[]> {\n\tconst customSessionDir =\n\t\ttypeof sessionDirOrOnProgress === \"string\" ? normalizePath(sessionDirOrOnProgress) : undefined;\n\tconst progress = typeof sessionDirOrOnProgress === \"function\" ? sessionDirOrOnProgress : onProgress;\n\tif (customSessionDir) {\n\t\tconst sessions = await listSessionsFromDir(customSessionDir, progress, 0, undefined, includeInternal);\n\t\tsessions.sort((a, b) => b.modified.getTime() - a.modified.getTime());\n\t\treturn sessions;\n\t}\n\n\tconst sessionsDir = getSessionsDir();\n\n\ttry {\n\t\tif (!existsSync(sessionsDir)) {\n\t\t\treturn [];\n\t\t}\n\t\tconst entries = await readdir(sessionsDir, { withFileTypes: true });\n\t\tconst dirs = entries.filter((e) => e.isDirectory()).map((e) => join(sessionsDir, e.name));\n\n\t\t// Count total files first for accurate progress\n\t\tlet totalFiles = 0;\n\t\tconst dirFiles: string[][] = [];\n\t\tfor (const dir of dirs) {\n\t\t\ttry {\n\t\t\t\tconst files = (await readdir(dir)).filter((f) => f.endsWith(\".jsonl\"));\n\t\t\t\tdirFiles.push(files.map((f) => join(dir, f)));\n\t\t\t\ttotalFiles += files.length;\n\t\t\t} catch {\n\t\t\t\tdirFiles.push([]);\n\t\t\t}\n\t\t}\n\n\t\t// Process all files with progress tracking\n\t\tlet loaded = 0;\n\t\tconst sessions: SessionInfo[] = [];\n\t\tconst allFiles = dirFiles.flat();\n\n\t\tconst results = await Promise.all(\n\t\t\tallFiles.map(async (file) => {\n\t\t\t\t// Prefilter via the header so hidden/internal sessions are skipped\n\t\t\t\t// before the expensive full-transcript parse in buildSessionInfo.\n\t\t\t\tif (!includeInternal && isInternalHeader(readSessionHeader(file))) {\n\t\t\t\t\tloaded++;\n\t\t\t\t\tprogress?.(loaded, totalFiles);\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\tconst info = await buildSessionInfo(file);\n\t\t\t\tloaded++;\n\t\t\t\tprogress?.(loaded, totalFiles);\n\t\t\t\treturn info;\n\t\t\t}),\n\t\t);\n\n\t\tfor (const info of results) {\n\t\t\tif (info && (includeInternal || !info.internal)) {\n\t\t\t\tsessions.push(info);\n\t\t\t}\n\t\t}\n\n\t\tsessions.sort((a, b) => b.modified.getTime() - a.modified.getTime());\n\t\treturn sessions;\n\t} catch {\n\t\treturn [];\n\t}\n}\n"]}
|
|
@@ -5,7 +5,7 @@ import { getSessionsDir } from "../config.js";
|
|
|
5
5
|
import { normalizePath, resolvePath } from "../utils/paths.js";
|
|
6
6
|
import { parseSessionEntries } from "./session-manager-migrations.js";
|
|
7
7
|
import { getDefaultSessionDir, getDefaultSessionDirPath } from "./session-manager-paths.js";
|
|
8
|
-
import { sessionCwdMatches, } from "./session-manager-storage.js";
|
|
8
|
+
import { isInternalHeader, readSessionHeader, sessionCwdMatches, } from "./session-manager-storage.js";
|
|
9
9
|
function isMessageWithContent(message) {
|
|
10
10
|
return typeof message.role === "string" && "content" in message;
|
|
11
11
|
}
|
|
@@ -90,6 +90,9 @@ async function buildSessionInfo(filePath) {
|
|
|
90
90
|
}
|
|
91
91
|
const cwd = typeof header.cwd === "string" ? header.cwd : "";
|
|
92
92
|
const parentSessionPath = header.parentSession;
|
|
93
|
+
const internal = header.internal === true ? true : undefined;
|
|
94
|
+
const workflowHeader = header.workflow;
|
|
95
|
+
const workflow = workflowHeader && typeof workflowHeader.runId === "string" ? workflowHeader : undefined;
|
|
93
96
|
const modified = getSessionModifiedDate(entries, header, stats.mtime);
|
|
94
97
|
return {
|
|
95
98
|
path: filePath,
|
|
@@ -97,6 +100,8 @@ async function buildSessionInfo(filePath) {
|
|
|
97
100
|
cwd,
|
|
98
101
|
name,
|
|
99
102
|
parentSessionPath,
|
|
103
|
+
...(internal ? { internal } : {}),
|
|
104
|
+
...(workflow ? { workflow } : {}),
|
|
100
105
|
created: new Date(header.timestamp),
|
|
101
106
|
modified,
|
|
102
107
|
messageCount,
|
|
@@ -108,7 +113,7 @@ async function buildSessionInfo(filePath) {
|
|
|
108
113
|
return null;
|
|
109
114
|
}
|
|
110
115
|
}
|
|
111
|
-
export async function listSessionsFromDir(dir, onProgress, progressOffset = 0, progressTotal) {
|
|
116
|
+
export async function listSessionsFromDir(dir, onProgress, progressOffset = 0, progressTotal, includeInternal = false) {
|
|
112
117
|
const sessions = [];
|
|
113
118
|
if (!existsSync(dir)) {
|
|
114
119
|
return sessions;
|
|
@@ -119,13 +124,20 @@ export async function listSessionsFromDir(dir, onProgress, progressOffset = 0, p
|
|
|
119
124
|
const total = progressTotal ?? files.length;
|
|
120
125
|
let loaded = 0;
|
|
121
126
|
const results = await Promise.all(files.map(async (file) => {
|
|
127
|
+
// Prefilter via the header so hidden/internal sessions are skipped
|
|
128
|
+
// before the expensive full-transcript parse in buildSessionInfo.
|
|
129
|
+
if (!includeInternal && isInternalHeader(readSessionHeader(file))) {
|
|
130
|
+
loaded++;
|
|
131
|
+
onProgress?.(progressOffset + loaded, total);
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
122
134
|
const info = await buildSessionInfo(file);
|
|
123
135
|
loaded++;
|
|
124
136
|
onProgress?.(progressOffset + loaded, total);
|
|
125
137
|
return info;
|
|
126
138
|
}));
|
|
127
139
|
for (const info of results) {
|
|
128
|
-
if (info) {
|
|
140
|
+
if (info && (includeInternal || !info.internal)) {
|
|
129
141
|
sessions.push(info);
|
|
130
142
|
}
|
|
131
143
|
}
|
|
@@ -135,19 +147,19 @@ export async function listSessionsFromDir(dir, onProgress, progressOffset = 0, p
|
|
|
135
147
|
}
|
|
136
148
|
return sessions;
|
|
137
149
|
}
|
|
138
|
-
export async function listProjectSessions(cwd, sessionDir, onProgress) {
|
|
150
|
+
export async function listProjectSessions(cwd, sessionDir, onProgress, includeInternal = false) {
|
|
139
151
|
const dir = sessionDir ? normalizePath(sessionDir) : getDefaultSessionDir(cwd);
|
|
140
152
|
const filterCwd = sessionDir !== undefined && dir !== getDefaultSessionDirPath(cwd);
|
|
141
153
|
const resolvedCwd = resolvePath(cwd);
|
|
142
|
-
const sessions = (await listSessionsFromDir(dir, onProgress)).filter((session) => !filterCwd || sessionCwdMatches(session.cwd, resolvedCwd));
|
|
154
|
+
const sessions = (await listSessionsFromDir(dir, onProgress, 0, undefined, includeInternal)).filter((session) => !filterCwd || sessionCwdMatches(session.cwd, resolvedCwd));
|
|
143
155
|
sessions.sort((a, b) => b.modified.getTime() - a.modified.getTime());
|
|
144
156
|
return sessions;
|
|
145
157
|
}
|
|
146
|
-
export async function listAllSessions(sessionDirOrOnProgress, onProgress) {
|
|
158
|
+
export async function listAllSessions(sessionDirOrOnProgress, onProgress, includeInternal = false) {
|
|
147
159
|
const customSessionDir = typeof sessionDirOrOnProgress === "string" ? normalizePath(sessionDirOrOnProgress) : undefined;
|
|
148
160
|
const progress = typeof sessionDirOrOnProgress === "function" ? sessionDirOrOnProgress : onProgress;
|
|
149
161
|
if (customSessionDir) {
|
|
150
|
-
const sessions = await listSessionsFromDir(customSessionDir, progress);
|
|
162
|
+
const sessions = await listSessionsFromDir(customSessionDir, progress, 0, undefined, includeInternal);
|
|
151
163
|
sessions.sort((a, b) => b.modified.getTime() - a.modified.getTime());
|
|
152
164
|
return sessions;
|
|
153
165
|
}
|
|
@@ -176,13 +188,20 @@ export async function listAllSessions(sessionDirOrOnProgress, onProgress) {
|
|
|
176
188
|
const sessions = [];
|
|
177
189
|
const allFiles = dirFiles.flat();
|
|
178
190
|
const results = await Promise.all(allFiles.map(async (file) => {
|
|
191
|
+
// Prefilter via the header so hidden/internal sessions are skipped
|
|
192
|
+
// before the expensive full-transcript parse in buildSessionInfo.
|
|
193
|
+
if (!includeInternal && isInternalHeader(readSessionHeader(file))) {
|
|
194
|
+
loaded++;
|
|
195
|
+
progress?.(loaded, totalFiles);
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
179
198
|
const info = await buildSessionInfo(file);
|
|
180
199
|
loaded++;
|
|
181
200
|
progress?.(loaded, totalFiles);
|
|
182
201
|
return info;
|
|
183
202
|
}));
|
|
184
203
|
for (const info of results) {
|
|
185
|
-
if (info) {
|
|
204
|
+
if (info && (includeInternal || !info.internal)) {
|
|
186
205
|
sessions.push(info);
|
|
187
206
|
}
|
|
188
207
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-manager-list.js","sourceRoot":"","sources":["../../src/core/session-manager-list.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentMessage } from \"@earendil-works/pi-agent-core\";\nimport type { Message, TextContent } from \"@earendil-works/pi-ai\";\nimport { existsSync } from \"fs\";\nimport { readdir, readFile, stat } from \"fs/promises\";\nimport { join } from \"path\";\nimport { getSessionsDir } from \"../config.ts\";\nimport { normalizePath, resolvePath } from \"../utils/paths.ts\";\nimport { parseSessionEntries } from \"./session-manager-migrations.ts\";\nimport { getDefaultSessionDir, getDefaultSessionDirPath } from \"./session-manager-paths.ts\";\nimport {\n\tsessionCwdMatches,\n} from \"./session-manager-storage.ts\";\nimport type {\n\tFileEntry,\n\tSessionEntryBase,\n\tSessionHeader,\n\tSessionInfo,\n\tSessionInfoEntry,\n\tSessionListProgress,\n\tSessionMessageEntry,\n} from \"./session-manager-types.ts\";\n\nfunction isMessageWithContent(message: AgentMessage): message is Message {\n\treturn typeof (message as Message).role === \"string\" && \"content\" in message;\n}\n\nfunction extractTextContent(message: Message): string {\n\tconst content = message.content;\n\tif (typeof content === \"string\") {\n\t\treturn content;\n\t}\n\treturn content\n\t\t.filter((block): block is TextContent => block.type === \"text\")\n\t\t.map((block) => block.text)\n\t\t.join(\" \");\n}\n\nfunction getLastActivityTime(entries: FileEntry[]): number | undefined {\n\tlet lastActivityTime: number | undefined;\n\n\tfor (const entry of entries) {\n\t\tif (entry.type !== \"message\") continue;\n\n\t\tconst message = (entry as SessionMessageEntry).message;\n\t\tif (!isMessageWithContent(message)) continue;\n\t\tif (message.role !== \"user\" && message.role !== \"assistant\") continue;\n\n\t\tconst msgTimestamp = (message as { timestamp?: number }).timestamp;\n\t\tif (typeof msgTimestamp === \"number\") {\n\t\t\tlastActivityTime = Math.max(lastActivityTime ?? 0, msgTimestamp);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst entryTimestamp = (entry as SessionEntryBase).timestamp;\n\t\tif (typeof entryTimestamp === \"string\") {\n\t\t\tconst t = new Date(entryTimestamp).getTime();\n\t\t\tif (!Number.isNaN(t)) {\n\t\t\t\tlastActivityTime = Math.max(lastActivityTime ?? 0, t);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn lastActivityTime;\n}\n\nfunction getSessionModifiedDate(entries: FileEntry[], header: SessionHeader, statsMtime: Date): Date {\n\tconst lastActivityTime = getLastActivityTime(entries);\n\tif (typeof lastActivityTime === \"number\" && lastActivityTime > 0) {\n\t\treturn new Date(lastActivityTime);\n\t}\n\n\tconst headerTime = typeof header.timestamp === \"string\" ? new Date(header.timestamp).getTime() : NaN;\n\treturn !Number.isNaN(headerTime) ? new Date(headerTime) : statsMtime;\n}\n\nasync function buildSessionInfo(filePath: string): Promise<SessionInfo | null> {\n\ttry {\n\t\tconst content = await readFile(filePath, \"utf8\");\n\t\tconst entries = parseSessionEntries(content);\n\n\t\tif (entries.length === 0) return null;\n\t\tconst header = entries[0];\n\t\tif (header.type !== \"session\") return null;\n\n\t\tconst stats = await stat(filePath);\n\t\tlet messageCount = 0;\n\t\tlet firstMessage = \"\";\n\t\tconst allMessages: string[] = [];\n\t\tlet name: string | undefined;\n\n\t\tfor (const entry of entries) {\n\t\t\t// Extract session name (use latest, including explicit clears)\n\t\t\tif (entry.type === \"session_info\") {\n\t\t\t\tconst infoEntry = entry as SessionInfoEntry;\n\t\t\t\tname = infoEntry.name?.trim() || undefined;\n\t\t\t}\n\n\t\t\tif (entry.type !== \"message\") continue;\n\t\t\tmessageCount++;\n\n\t\t\tconst message = (entry as SessionMessageEntry).message;\n\t\t\tif (!isMessageWithContent(message)) continue;\n\t\t\tif (message.role !== \"user\" && message.role !== \"assistant\") continue;\n\n\t\t\tconst textContent = extractTextContent(message);\n\t\t\tif (!textContent) continue;\n\n\t\t\tallMessages.push(textContent);\n\t\t\tif (!firstMessage && message.role === \"user\") {\n\t\t\t\tfirstMessage = textContent;\n\t\t\t}\n\t\t}\n\n\t\tconst cwd = typeof (header as SessionHeader).cwd === \"string\" ? (header as SessionHeader).cwd : \"\";\n\t\tconst parentSessionPath = (header as SessionHeader).parentSession;\n\n\t\tconst modified = getSessionModifiedDate(entries, header as SessionHeader, stats.mtime);\n\n\t\treturn {\n\t\t\tpath: filePath,\n\t\t\tid: (header as SessionHeader).id,\n\t\t\tcwd,\n\t\t\tname,\n\t\t\tparentSessionPath,\n\t\t\tcreated: new Date((header as SessionHeader).timestamp),\n\t\t\tmodified,\n\t\t\tmessageCount,\n\t\t\tfirstMessage: firstMessage || \"(no messages)\",\n\t\t\tallMessagesText: allMessages.join(\" \"),\n\t\t};\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nexport async function listSessionsFromDir(\n\tdir: string,\n\tonProgress?: SessionListProgress,\n\tprogressOffset = 0,\n\tprogressTotal?: number,\n): Promise<SessionInfo[]> {\n\tconst sessions: SessionInfo[] = [];\n\tif (!existsSync(dir)) {\n\t\treturn sessions;\n\t}\n\n\ttry {\n\t\tconst dirEntries = await readdir(dir);\n\t\tconst files = dirEntries.filter((f) => f.endsWith(\".jsonl\")).map((f) => join(dir, f));\n\t\tconst total = progressTotal ?? files.length;\n\n\t\tlet loaded = 0;\n\t\tconst results = await Promise.all(\n\t\t\tfiles.map(async (file) => {\n\t\t\t\tconst info = await buildSessionInfo(file);\n\t\t\t\tloaded++;\n\t\t\t\tonProgress?.(progressOffset + loaded, total);\n\t\t\t\treturn info;\n\t\t\t}),\n\t\t);\n\t\tfor (const info of results) {\n\t\t\tif (info) {\n\t\t\t\tsessions.push(info);\n\t\t\t}\n\t\t}\n\t} catch {\n\t\t// Return empty list on error\n\t}\n\n\treturn sessions;\n}\n\nexport async function listProjectSessions(\n\tcwd: string,\n\tsessionDir?: string,\n\tonProgress?: SessionListProgress,\n): Promise<SessionInfo[]> {\n\tconst dir = sessionDir ? normalizePath(sessionDir) : getDefaultSessionDir(cwd);\n\tconst filterCwd = sessionDir !== undefined && dir !== getDefaultSessionDirPath(cwd);\n\tconst resolvedCwd = resolvePath(cwd);\n\tconst sessions = (await listSessionsFromDir(dir, onProgress)).filter(\n\t\t(session) => !filterCwd || sessionCwdMatches(session.cwd, resolvedCwd),\n\t);\n\tsessions.sort((a, b) => b.modified.getTime() - a.modified.getTime());\n\treturn sessions;\n}\n\nexport async function listAllSessions(\n\tsessionDirOrOnProgress?: string | SessionListProgress,\n\tonProgress?: SessionListProgress,\n): Promise<SessionInfo[]> {\n\tconst customSessionDir =\n\t\ttypeof sessionDirOrOnProgress === \"string\" ? normalizePath(sessionDirOrOnProgress) : undefined;\n\tconst progress = typeof sessionDirOrOnProgress === \"function\" ? sessionDirOrOnProgress : onProgress;\n\tif (customSessionDir) {\n\t\tconst sessions = await listSessionsFromDir(customSessionDir, progress);\n\t\tsessions.sort((a, b) => b.modified.getTime() - a.modified.getTime());\n\t\treturn sessions;\n\t}\n\n\tconst sessionsDir = getSessionsDir();\n\n\ttry {\n\t\tif (!existsSync(sessionsDir)) {\n\t\t\treturn [];\n\t\t}\n\t\tconst entries = await readdir(sessionsDir, { withFileTypes: true });\n\t\tconst dirs = entries.filter((e) => e.isDirectory()).map((e) => join(sessionsDir, e.name));\n\n\t\t// Count total files first for accurate progress\n\t\tlet totalFiles = 0;\n\t\tconst dirFiles: string[][] = [];\n\t\tfor (const dir of dirs) {\n\t\t\ttry {\n\t\t\t\tconst files = (await readdir(dir)).filter((f) => f.endsWith(\".jsonl\"));\n\t\t\t\tdirFiles.push(files.map((f) => join(dir, f)));\n\t\t\t\ttotalFiles += files.length;\n\t\t\t} catch {\n\t\t\t\tdirFiles.push([]);\n\t\t\t}\n\t\t}\n\n\t\t// Process all files with progress tracking\n\t\tlet loaded = 0;\n\t\tconst sessions: SessionInfo[] = [];\n\t\tconst allFiles = dirFiles.flat();\n\n\t\tconst results = await Promise.all(\n\t\t\tallFiles.map(async (file) => {\n\t\t\t\tconst info = await buildSessionInfo(file);\n\t\t\t\tloaded++;\n\t\t\t\tprogress?.(loaded, totalFiles);\n\t\t\t\treturn info;\n\t\t\t}),\n\t\t);\n\n\t\tfor (const info of results) {\n\t\t\tif (info) {\n\t\t\t\tsessions.push(info);\n\t\t\t}\n\t\t}\n\n\t\tsessions.sort((a, b) => b.modified.getTime() - a.modified.getTime());\n\t\treturn sessions;\n\t} catch {\n\t\treturn [];\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"session-manager-list.js","sourceRoot":"","sources":["../../src/core/session-manager-list.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentMessage } from \"@earendil-works/pi-agent-core\";\nimport type { Message, TextContent } from \"@earendil-works/pi-ai\";\nimport { existsSync } from \"fs\";\nimport { readdir, readFile, stat } from \"fs/promises\";\nimport { join } from \"path\";\nimport { getSessionsDir } from \"../config.ts\";\nimport { normalizePath, resolvePath } from \"../utils/paths.ts\";\nimport { parseSessionEntries } from \"./session-manager-migrations.ts\";\nimport { getDefaultSessionDir, getDefaultSessionDirPath } from \"./session-manager-paths.ts\";\nimport {\n\tisInternalHeader,\n\treadSessionHeader,\n\tsessionCwdMatches,\n} from \"./session-manager-storage.ts\";\nimport type {\n\tFileEntry,\n\tSessionEntryBase,\n\tSessionHeader,\n\tSessionInfo,\n\tSessionInfoEntry,\n\tSessionListProgress,\n\tSessionMessageEntry,\n\tSessionWorkflowMetadata,\n} from \"./session-manager-types.ts\";\n\nfunction isMessageWithContent(message: AgentMessage): message is Message {\n\treturn typeof (message as Message).role === \"string\" && \"content\" in message;\n}\n\nfunction extractTextContent(message: Message): string {\n\tconst content = message.content;\n\tif (typeof content === \"string\") {\n\t\treturn content;\n\t}\n\treturn content\n\t\t.filter((block): block is TextContent => block.type === \"text\")\n\t\t.map((block) => block.text)\n\t\t.join(\" \");\n}\n\nfunction getLastActivityTime(entries: FileEntry[]): number | undefined {\n\tlet lastActivityTime: number | undefined;\n\n\tfor (const entry of entries) {\n\t\tif (entry.type !== \"message\") continue;\n\n\t\tconst message = (entry as SessionMessageEntry).message;\n\t\tif (!isMessageWithContent(message)) continue;\n\t\tif (message.role !== \"user\" && message.role !== \"assistant\") continue;\n\n\t\tconst msgTimestamp = (message as { timestamp?: number }).timestamp;\n\t\tif (typeof msgTimestamp === \"number\") {\n\t\t\tlastActivityTime = Math.max(lastActivityTime ?? 0, msgTimestamp);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst entryTimestamp = (entry as SessionEntryBase).timestamp;\n\t\tif (typeof entryTimestamp === \"string\") {\n\t\t\tconst t = new Date(entryTimestamp).getTime();\n\t\t\tif (!Number.isNaN(t)) {\n\t\t\t\tlastActivityTime = Math.max(lastActivityTime ?? 0, t);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn lastActivityTime;\n}\n\nfunction getSessionModifiedDate(entries: FileEntry[], header: SessionHeader, statsMtime: Date): Date {\n\tconst lastActivityTime = getLastActivityTime(entries);\n\tif (typeof lastActivityTime === \"number\" && lastActivityTime > 0) {\n\t\treturn new Date(lastActivityTime);\n\t}\n\n\tconst headerTime = typeof header.timestamp === \"string\" ? new Date(header.timestamp).getTime() : NaN;\n\treturn !Number.isNaN(headerTime) ? new Date(headerTime) : statsMtime;\n}\n\nasync function buildSessionInfo(filePath: string): Promise<SessionInfo | null> {\n\ttry {\n\t\tconst content = await readFile(filePath, \"utf8\");\n\t\tconst entries = parseSessionEntries(content);\n\n\t\tif (entries.length === 0) return null;\n\t\tconst header = entries[0];\n\t\tif (header.type !== \"session\") return null;\n\n\t\tconst stats = await stat(filePath);\n\t\tlet messageCount = 0;\n\t\tlet firstMessage = \"\";\n\t\tconst allMessages: string[] = [];\n\t\tlet name: string | undefined;\n\n\t\tfor (const entry of entries) {\n\t\t\t// Extract session name (use latest, including explicit clears)\n\t\t\tif (entry.type === \"session_info\") {\n\t\t\t\tconst infoEntry = entry as SessionInfoEntry;\n\t\t\t\tname = infoEntry.name?.trim() || undefined;\n\t\t\t}\n\n\t\t\tif (entry.type !== \"message\") continue;\n\t\t\tmessageCount++;\n\n\t\t\tconst message = (entry as SessionMessageEntry).message;\n\t\t\tif (!isMessageWithContent(message)) continue;\n\t\t\tif (message.role !== \"user\" && message.role !== \"assistant\") continue;\n\n\t\t\tconst textContent = extractTextContent(message);\n\t\t\tif (!textContent) continue;\n\n\t\t\tallMessages.push(textContent);\n\t\t\tif (!firstMessage && message.role === \"user\") {\n\t\t\t\tfirstMessage = textContent;\n\t\t\t}\n\t\t}\n\n\t\tconst cwd = typeof (header as SessionHeader).cwd === \"string\" ? (header as SessionHeader).cwd : \"\";\n\t\tconst parentSessionPath = (header as SessionHeader).parentSession;\n\t\tconst internal = (header as SessionHeader).internal === true ? true : undefined;\n\t\tconst workflowHeader = (header as SessionHeader).workflow as SessionWorkflowMetadata | undefined;\n\t\tconst workflow =\n\t\t\tworkflowHeader && typeof workflowHeader.runId === \"string\" ? workflowHeader : undefined;\n\n\t\tconst modified = getSessionModifiedDate(entries, header as SessionHeader, stats.mtime);\n\n\t\treturn {\n\t\t\tpath: filePath,\n\t\t\tid: (header as SessionHeader).id,\n\t\t\tcwd,\n\t\t\tname,\n\t\t\tparentSessionPath,\n\t\t\t...(internal ? { internal } : {}),\n\t\t\t...(workflow ? { workflow } : {}),\n\t\t\tcreated: new Date((header as SessionHeader).timestamp),\n\t\t\tmodified,\n\t\t\tmessageCount,\n\t\t\tfirstMessage: firstMessage || \"(no messages)\",\n\t\t\tallMessagesText: allMessages.join(\" \"),\n\t\t};\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nexport async function listSessionsFromDir(\n\tdir: string,\n\tonProgress?: SessionListProgress,\n\tprogressOffset = 0,\n\tprogressTotal?: number,\n\tincludeInternal = false,\n): Promise<SessionInfo[]> {\n\tconst sessions: SessionInfo[] = [];\n\tif (!existsSync(dir)) {\n\t\treturn sessions;\n\t}\n\n\ttry {\n\t\tconst dirEntries = await readdir(dir);\n\t\tconst files = dirEntries.filter((f) => f.endsWith(\".jsonl\")).map((f) => join(dir, f));\n\t\tconst total = progressTotal ?? files.length;\n\n\t\tlet loaded = 0;\n\t\tconst results = await Promise.all(\n\t\t\tfiles.map(async (file) => {\n\t\t\t\t// Prefilter via the header so hidden/internal sessions are skipped\n\t\t\t\t// before the expensive full-transcript parse in buildSessionInfo.\n\t\t\t\tif (!includeInternal && isInternalHeader(readSessionHeader(file))) {\n\t\t\t\t\tloaded++;\n\t\t\t\t\tonProgress?.(progressOffset + loaded, total);\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\tconst info = await buildSessionInfo(file);\n\t\t\t\tloaded++;\n\t\t\t\tonProgress?.(progressOffset + loaded, total);\n\t\t\t\treturn info;\n\t\t\t}),\n\t\t);\n\t\tfor (const info of results) {\n\t\t\tif (info && (includeInternal || !info.internal)) {\n\t\t\t\tsessions.push(info);\n\t\t\t}\n\t\t}\n\t} catch {\n\t\t// Return empty list on error\n\t}\n\n\treturn sessions;\n}\n\nexport async function listProjectSessions(\n\tcwd: string,\n\tsessionDir?: string,\n\tonProgress?: SessionListProgress,\n\tincludeInternal = false,\n): Promise<SessionInfo[]> {\n\tconst dir = sessionDir ? normalizePath(sessionDir) : getDefaultSessionDir(cwd);\n\tconst filterCwd = sessionDir !== undefined && dir !== getDefaultSessionDirPath(cwd);\n\tconst resolvedCwd = resolvePath(cwd);\n\tconst sessions = (await listSessionsFromDir(dir, onProgress, 0, undefined, includeInternal)).filter(\n\t\t(session) => !filterCwd || sessionCwdMatches(session.cwd, resolvedCwd),\n\t);\n\tsessions.sort((a, b) => b.modified.getTime() - a.modified.getTime());\n\treturn sessions;\n}\n\nexport async function listAllSessions(\n\tsessionDirOrOnProgress?: string | SessionListProgress,\n\tonProgress?: SessionListProgress,\n\tincludeInternal = false,\n): Promise<SessionInfo[]> {\n\tconst customSessionDir =\n\t\ttypeof sessionDirOrOnProgress === \"string\" ? normalizePath(sessionDirOrOnProgress) : undefined;\n\tconst progress = typeof sessionDirOrOnProgress === \"function\" ? sessionDirOrOnProgress : onProgress;\n\tif (customSessionDir) {\n\t\tconst sessions = await listSessionsFromDir(customSessionDir, progress, 0, undefined, includeInternal);\n\t\tsessions.sort((a, b) => b.modified.getTime() - a.modified.getTime());\n\t\treturn sessions;\n\t}\n\n\tconst sessionsDir = getSessionsDir();\n\n\ttry {\n\t\tif (!existsSync(sessionsDir)) {\n\t\t\treturn [];\n\t\t}\n\t\tconst entries = await readdir(sessionsDir, { withFileTypes: true });\n\t\tconst dirs = entries.filter((e) => e.isDirectory()).map((e) => join(sessionsDir, e.name));\n\n\t\t// Count total files first for accurate progress\n\t\tlet totalFiles = 0;\n\t\tconst dirFiles: string[][] = [];\n\t\tfor (const dir of dirs) {\n\t\t\ttry {\n\t\t\t\tconst files = (await readdir(dir)).filter((f) => f.endsWith(\".jsonl\"));\n\t\t\t\tdirFiles.push(files.map((f) => join(dir, f)));\n\t\t\t\ttotalFiles += files.length;\n\t\t\t} catch {\n\t\t\t\tdirFiles.push([]);\n\t\t\t}\n\t\t}\n\n\t\t// Process all files with progress tracking\n\t\tlet loaded = 0;\n\t\tconst sessions: SessionInfo[] = [];\n\t\tconst allFiles = dirFiles.flat();\n\n\t\tconst results = await Promise.all(\n\t\t\tallFiles.map(async (file) => {\n\t\t\t\t// Prefilter via the header so hidden/internal sessions are skipped\n\t\t\t\t// before the expensive full-transcript parse in buildSessionInfo.\n\t\t\t\tif (!includeInternal && isInternalHeader(readSessionHeader(file))) {\n\t\t\t\t\tloaded++;\n\t\t\t\t\tprogress?.(loaded, totalFiles);\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\tconst info = await buildSessionInfo(file);\n\t\t\t\tloaded++;\n\t\t\t\tprogress?.(loaded, totalFiles);\n\t\t\t\treturn info;\n\t\t\t}),\n\t\t);\n\n\t\tfor (const info of results) {\n\t\t\tif (info && (includeInternal || !info.internal)) {\n\t\t\t\tsessions.push(info);\n\t\t\t}\n\t\t}\n\n\t\tsessions.sort((a, b) => b.modified.getTime() - a.modified.getTime());\n\t\treturn sessions;\n\t} catch {\n\t\treturn [];\n\t}\n}\n"]}
|
|
@@ -2,10 +2,12 @@ import type { FileEntry, SessionHeader } from "./session-manager-types.ts";
|
|
|
2
2
|
/** Exported for testing */
|
|
3
3
|
export declare function loadEntriesFromFile(filePath: string): FileEntry[];
|
|
4
4
|
export declare function readSessionHeader(filePath: string): SessionHeader | null;
|
|
5
|
+
/** Returns true when a session header marks the session as internal (e.g. a workflow stage session). */
|
|
6
|
+
export declare function isInternalHeader(header: SessionHeader | null | undefined): boolean;
|
|
5
7
|
export declare function getSessionHeaderCwd(header: SessionHeader): string | undefined;
|
|
6
8
|
export declare function sessionCwdMatches(cwd: string | undefined, resolvedCwd: string): boolean;
|
|
7
9
|
/** Exported for testing */
|
|
8
|
-
export declare function findMostRecentSession(sessionDir: string, cwd?: string): string | null;
|
|
10
|
+
export declare function findMostRecentSession(sessionDir: string, cwd?: string, includeInternal?: boolean): string | null;
|
|
9
11
|
export declare function serializeSessionEntries(entries: FileEntry[]): string;
|
|
10
12
|
export declare function writeSessionEntries(filePath: string, entries: FileEntry[]): void;
|
|
11
13
|
export declare function appendSessionEntry(filePath: string, entry: FileEntry): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-manager-storage.d.ts","sourceRoot":"","sources":["../../src/core/session-manager-storage.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,SAAS,EAAgB,aAAa,EAAE,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"session-manager-storage.d.ts","sourceRoot":"","sources":["../../src/core/session-manager-storage.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,SAAS,EAAgB,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAoBzF,2BAA2B;AAC3B,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAAE,CA0CjE;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CA6CxE;AAED,wGAAwG;AACxG,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAElF;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,GAAG,SAAS,CAG7E;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAEvF;AAED,2BAA2B;AAC3B,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,eAAe,UAAQ,GAAG,MAAM,GAAG,IAAI,CAqB9G;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,MAAM,CAEpE;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,IAAI,CAEhF;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,IAAI,CAE3E;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,IAAI,CAIjF;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,OAAO,CAEjE;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAIjD","sourcesContent":["import {\n\tappendFileSync,\n\tcloseSync,\n\texistsSync,\n\tmkdirSync,\n\topenSync,\n\treaddirSync,\n\treadSync,\n\tstatSync,\n\twriteFileSync,\n} from \"fs\";\nimport { join } from \"path\";\nimport { StringDecoder } from \"string_decoder\";\nimport { normalizePath, resolvePath } from \"../utils/paths.ts\";\nimport type { FileEntry, SessionEntry, SessionHeader } from \"./session-manager-types.ts\";\n\nconst SESSION_READ_BUFFER_SIZE = 1024 * 1024;\n/**\n * Dedicated small read chunk for header-only reads. Session headers are small\n * (typically a few KB), so reading in 64KB chunks avoids allocating/transferring\n * the full 1MiB transcript buffer just to inspect the first line during listing\n * and resume-history prefiltering.\n */\nconst HEADER_READ_BUFFER_SIZE = 64 * 1024;\n\nfunction parseSessionEntryLine(line: string): FileEntry | null {\n\tif (!line.trim()) return null;\n\ttry {\n\t\treturn JSON.parse(line) as FileEntry;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/** Exported for testing */\nexport function loadEntriesFromFile(filePath: string): FileEntry[] {\n\tconst resolvedFilePath = normalizePath(filePath);\n\tif (!existsSync(resolvedFilePath)) return [];\n\n\tconst entries: FileEntry[] = [];\n\tconst fd = openSync(resolvedFilePath, \"r\");\n\ttry {\n\t\tconst decoder = new StringDecoder(\"utf8\");\n\t\tconst buffer = Buffer.allocUnsafe(SESSION_READ_BUFFER_SIZE);\n\t\tlet pending = \"\";\n\n\t\twhile (true) {\n\t\t\tconst bytesRead = readSync(fd, buffer, 0, buffer.length, null);\n\t\t\tif (bytesRead === 0) break;\n\n\t\t\tpending += decoder.write(buffer.subarray(0, bytesRead));\n\t\t\tlet lineStart = 0;\n\t\t\tlet newlineIndex = pending.indexOf(\"\\n\", lineStart);\n\t\t\twhile (newlineIndex !== -1) {\n\t\t\t\tconst entry = parseSessionEntryLine(pending.slice(lineStart, newlineIndex));\n\t\t\t\tif (entry) entries.push(entry);\n\t\t\t\tlineStart = newlineIndex + 1;\n\t\t\t\tnewlineIndex = pending.indexOf(\"\\n\", lineStart);\n\t\t\t}\n\t\t\tpending = pending.slice(lineStart);\n\t\t}\n\n\t\tpending += decoder.end();\n\t\tconst finalEntry = parseSessionEntryLine(pending);\n\t\tif (finalEntry) entries.push(finalEntry);\n\t} finally {\n\t\tcloseSync(fd);\n\t}\n\n\t// Validate session header\n\tif (entries.length === 0) return entries;\n\tconst header = entries[0];\n\tif (header.type !== \"session\" || !(\"id\" in header) || typeof header.id !== \"string\") {\n\t\treturn [];\n\t}\n\n\treturn entries;\n}\n\nexport function readSessionHeader(filePath: string): SessionHeader | null {\n\ttry {\n\t\tconst fd = openSync(filePath, \"r\");\n\t\ttry {\n\t\t\t// Read the full first line rather than a fixed 512-byte window so very\n\t\t\t// long headers (e.g. internal workflow headers carrying stage metadata)\n\t\t\t// are not truncated and dropped from listing/resume filtering.\n\t\t\tconst decoder = new StringDecoder(\"utf8\");\n\t\t\t// Use a small dedicated header buffer instead of the 1MiB transcript\n\t\t\t// buffer so prefiltering internal sessions during listing stays cheap.\n\t\t\t// The loop still reads in chunks until the first newline (or EOF) so\n\t\t\t// headers larger than one chunk are handled correctly.\n\t\t\tconst buffer = Buffer.allocUnsafe(HEADER_READ_BUFFER_SIZE);\n\t\t\tlet pending = \"\";\n\t\t\tlet foundNewline = false;\n\t\t\twhile (true) {\n\t\t\t\tconst bytesRead = readSync(fd, buffer, 0, buffer.length, null);\n\t\t\t\tif (bytesRead === 0) break;\n\t\t\t\tpending += decoder.write(buffer.subarray(0, bytesRead));\n\t\t\t\tconst newlineIndex = pending.indexOf(\"\\n\");\n\t\t\t\tif (newlineIndex !== -1) {\n\t\t\t\t\tpending = pending.slice(0, newlineIndex);\n\t\t\t\t\tfoundNewline = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Only flush the decoder when we hit EOF without a newline. Once a\n\t\t\t// newline was found, any remaining decoder bytes belong to data after\n\t\t\t// the header line; flushing them would corrupt the parsed header.\n\t\t\tif (!foundNewline) {\n\t\t\t\tpending += decoder.end();\n\t\t\t}\n\t\t\tconst firstLine = pending.split(\"\\n\")[0];\n\t\t\tif (!firstLine) return null;\n\t\t\tconst header = JSON.parse(firstLine) as Record<string, unknown>;\n\t\t\tif (header.type !== \"session\" || typeof header.id !== \"string\") {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn header as unknown as SessionHeader;\n\t\t} finally {\n\t\t\tcloseSync(fd);\n\t\t}\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/** Returns true when a session header marks the session as internal (e.g. a workflow stage session). */\nexport function isInternalHeader(header: SessionHeader | null | undefined): boolean {\n\treturn header?.internal === true;\n}\n\nexport function getSessionHeaderCwd(header: SessionHeader): string | undefined {\n\tconst cwd = (header as { cwd?: unknown }).cwd;\n\treturn typeof cwd === \"string\" ? cwd : undefined;\n}\n\nexport function sessionCwdMatches(cwd: string | undefined, resolvedCwd: string): boolean {\n\treturn cwd !== undefined && cwd !== \"\" && resolvePath(cwd) === resolvedCwd;\n}\n\n/** Exported for testing */\nexport function findMostRecentSession(sessionDir: string, cwd?: string, includeInternal = false): string | null {\n\tconst resolvedSessionDir = normalizePath(sessionDir);\n\tconst resolvedCwd = cwd ? resolvePath(cwd) : undefined;\n\ttry {\n\t\tconst files = readdirSync(resolvedSessionDir)\n\t\t\t.filter((f) => f.endsWith(\".jsonl\"))\n\t\t\t.map((f) => join(resolvedSessionDir, f))\n\t\t\t.map((path) => ({ path, header: readSessionHeader(path) }))\n\t\t\t.filter(\n\t\t\t\t(file): file is { path: string; header: SessionHeader } =>\n\t\t\t\t\tfile.header !== null &&\n\t\t\t\t\t(!resolvedCwd || sessionCwdMatches(getSessionHeaderCwd(file.header), resolvedCwd)) &&\n\t\t\t\t\t(includeInternal || !isInternalHeader(file.header)),\n\t\t\t)\n\t\t\t.map(({ path }) => ({ path, mtime: statSync(path).mtime }))\n\t\t\t.sort((a, b) => b.mtime.getTime() - a.mtime.getTime());\n\n\t\treturn files[0]?.path || null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nexport function serializeSessionEntries(entries: FileEntry[]): string {\n\treturn `${entries.map((e) => JSON.stringify(e)).join(\"\\n\")}\\n`;\n}\n\nexport function writeSessionEntries(filePath: string, entries: FileEntry[]): void {\n\twriteFileSync(filePath, serializeSessionEntries(entries));\n}\n\nexport function appendSessionEntry(filePath: string, entry: FileEntry): void {\n\tappendFileSync(filePath, `${JSON.stringify(entry)}\\n`);\n}\n\nexport function appendSessionEntries(filePath: string, entries: FileEntry[]): void {\n\tfor (const entry of entries) {\n\t\tappendSessionEntry(filePath, entry);\n\t}\n}\n\nexport function hasAssistantMessage(entries: FileEntry[]): boolean {\n\treturn entries.some((entry): entry is SessionEntry => entry.type === \"message\" && entry.message.role === \"assistant\");\n}\n\nexport function ensureDirectory(dir: string): void {\n\tif (!existsSync(dir)) {\n\t\tmkdirSync(dir, { recursive: true });\n\t}\n}\n"]}
|
|
@@ -3,6 +3,13 @@ import { join } from "path";
|
|
|
3
3
|
import { StringDecoder } from "string_decoder";
|
|
4
4
|
import { normalizePath, resolvePath } from "../utils/paths.js";
|
|
5
5
|
const SESSION_READ_BUFFER_SIZE = 1024 * 1024;
|
|
6
|
+
/**
|
|
7
|
+
* Dedicated small read chunk for header-only reads. Session headers are small
|
|
8
|
+
* (typically a few KB), so reading in 64KB chunks avoids allocating/transferring
|
|
9
|
+
* the full 1MiB transcript buffer just to inspect the first line during listing
|
|
10
|
+
* and resume-history prefiltering.
|
|
11
|
+
*/
|
|
12
|
+
const HEADER_READ_BUFFER_SIZE = 64 * 1024;
|
|
6
13
|
function parseSessionEntryLine(line) {
|
|
7
14
|
if (!line.trim())
|
|
8
15
|
return null;
|
|
@@ -60,22 +67,57 @@ export function loadEntriesFromFile(filePath) {
|
|
|
60
67
|
export function readSessionHeader(filePath) {
|
|
61
68
|
try {
|
|
62
69
|
const fd = openSync(filePath, "r");
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
try {
|
|
71
|
+
// Read the full first line rather than a fixed 512-byte window so very
|
|
72
|
+
// long headers (e.g. internal workflow headers carrying stage metadata)
|
|
73
|
+
// are not truncated and dropped from listing/resume filtering.
|
|
74
|
+
const decoder = new StringDecoder("utf8");
|
|
75
|
+
// Use a small dedicated header buffer instead of the 1MiB transcript
|
|
76
|
+
// buffer so prefiltering internal sessions during listing stays cheap.
|
|
77
|
+
// The loop still reads in chunks until the first newline (or EOF) so
|
|
78
|
+
// headers larger than one chunk are handled correctly.
|
|
79
|
+
const buffer = Buffer.allocUnsafe(HEADER_READ_BUFFER_SIZE);
|
|
80
|
+
let pending = "";
|
|
81
|
+
let foundNewline = false;
|
|
82
|
+
while (true) {
|
|
83
|
+
const bytesRead = readSync(fd, buffer, 0, buffer.length, null);
|
|
84
|
+
if (bytesRead === 0)
|
|
85
|
+
break;
|
|
86
|
+
pending += decoder.write(buffer.subarray(0, bytesRead));
|
|
87
|
+
const newlineIndex = pending.indexOf("\n");
|
|
88
|
+
if (newlineIndex !== -1) {
|
|
89
|
+
pending = pending.slice(0, newlineIndex);
|
|
90
|
+
foundNewline = true;
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// Only flush the decoder when we hit EOF without a newline. Once a
|
|
95
|
+
// newline was found, any remaining decoder bytes belong to data after
|
|
96
|
+
// the header line; flushing them would corrupt the parsed header.
|
|
97
|
+
if (!foundNewline) {
|
|
98
|
+
pending += decoder.end();
|
|
99
|
+
}
|
|
100
|
+
const firstLine = pending.split("\n")[0];
|
|
101
|
+
if (!firstLine)
|
|
102
|
+
return null;
|
|
103
|
+
const header = JSON.parse(firstLine);
|
|
104
|
+
if (header.type !== "session" || typeof header.id !== "string") {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
return header;
|
|
108
|
+
}
|
|
109
|
+
finally {
|
|
110
|
+
closeSync(fd);
|
|
72
111
|
}
|
|
73
|
-
return header;
|
|
74
112
|
}
|
|
75
113
|
catch {
|
|
76
114
|
return null;
|
|
77
115
|
}
|
|
78
116
|
}
|
|
117
|
+
/** Returns true when a session header marks the session as internal (e.g. a workflow stage session). */
|
|
118
|
+
export function isInternalHeader(header) {
|
|
119
|
+
return header?.internal === true;
|
|
120
|
+
}
|
|
79
121
|
export function getSessionHeaderCwd(header) {
|
|
80
122
|
const cwd = header.cwd;
|
|
81
123
|
return typeof cwd === "string" ? cwd : undefined;
|
|
@@ -84,7 +126,7 @@ export function sessionCwdMatches(cwd, resolvedCwd) {
|
|
|
84
126
|
return cwd !== undefined && cwd !== "" && resolvePath(cwd) === resolvedCwd;
|
|
85
127
|
}
|
|
86
128
|
/** Exported for testing */
|
|
87
|
-
export function findMostRecentSession(sessionDir, cwd) {
|
|
129
|
+
export function findMostRecentSession(sessionDir, cwd, includeInternal = false) {
|
|
88
130
|
const resolvedSessionDir = normalizePath(sessionDir);
|
|
89
131
|
const resolvedCwd = cwd ? resolvePath(cwd) : undefined;
|
|
90
132
|
try {
|
|
@@ -93,7 +135,8 @@ export function findMostRecentSession(sessionDir, cwd) {
|
|
|
93
135
|
.map((f) => join(resolvedSessionDir, f))
|
|
94
136
|
.map((path) => ({ path, header: readSessionHeader(path) }))
|
|
95
137
|
.filter((file) => file.header !== null &&
|
|
96
|
-
(!resolvedCwd || sessionCwdMatches(getSessionHeaderCwd(file.header), resolvedCwd))
|
|
138
|
+
(!resolvedCwd || sessionCwdMatches(getSessionHeaderCwd(file.header), resolvedCwd)) &&
|
|
139
|
+
(includeInternal || !isInternalHeader(file.header)))
|
|
97
140
|
.map(({ path }) => ({ path, mtime: statSync(path).mtime }))
|
|
98
141
|
.sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
|
|
99
142
|
return files[0]?.path || null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-manager-storage.js","sourceRoot":"","sources":["../../src/core/session-manager-storage.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\tappendFileSync,\n\tcloseSync,\n\texistsSync,\n\tmkdirSync,\n\topenSync,\n\treaddirSync,\n\treadSync,\n\tstatSync,\n\twriteFileSync,\n} from \"fs\";\nimport { join } from \"path\";\nimport { StringDecoder } from \"string_decoder\";\nimport { normalizePath, resolvePath } from \"../utils/paths.ts\";\nimport type { FileEntry, SessionEntry, SessionHeader } from \"./session-manager-types.ts\";\n\nconst SESSION_READ_BUFFER_SIZE = 1024 * 1024;\n\nfunction parseSessionEntryLine(line: string): FileEntry | null {\n\tif (!line.trim()) return null;\n\ttry {\n\t\treturn JSON.parse(line) as FileEntry;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/** Exported for testing */\nexport function loadEntriesFromFile(filePath: string): FileEntry[] {\n\tconst resolvedFilePath = normalizePath(filePath);\n\tif (!existsSync(resolvedFilePath)) return [];\n\n\tconst entries: FileEntry[] = [];\n\tconst fd = openSync(resolvedFilePath, \"r\");\n\ttry {\n\t\tconst decoder = new StringDecoder(\"utf8\");\n\t\tconst buffer = Buffer.allocUnsafe(SESSION_READ_BUFFER_SIZE);\n\t\tlet pending = \"\";\n\n\t\twhile (true) {\n\t\t\tconst bytesRead = readSync(fd, buffer, 0, buffer.length, null);\n\t\t\tif (bytesRead === 0) break;\n\n\t\t\tpending += decoder.write(buffer.subarray(0, bytesRead));\n\t\t\tlet lineStart = 0;\n\t\t\tlet newlineIndex = pending.indexOf(\"\\n\", lineStart);\n\t\t\twhile (newlineIndex !== -1) {\n\t\t\t\tconst entry = parseSessionEntryLine(pending.slice(lineStart, newlineIndex));\n\t\t\t\tif (entry) entries.push(entry);\n\t\t\t\tlineStart = newlineIndex + 1;\n\t\t\t\tnewlineIndex = pending.indexOf(\"\\n\", lineStart);\n\t\t\t}\n\t\t\tpending = pending.slice(lineStart);\n\t\t}\n\n\t\tpending += decoder.end();\n\t\tconst finalEntry = parseSessionEntryLine(pending);\n\t\tif (finalEntry) entries.push(finalEntry);\n\t} finally {\n\t\tcloseSync(fd);\n\t}\n\n\t// Validate session header\n\tif (entries.length === 0) return entries;\n\tconst header = entries[0];\n\tif (header.type !== \"session\" || !(\"id\" in header) || typeof header.id !== \"string\") {\n\t\treturn [];\n\t}\n\n\treturn entries;\n}\n\nexport function readSessionHeader(filePath: string): SessionHeader | null {\n\ttry {\n\t\tconst fd = openSync(filePath, \"r\");\n\t\tconst buffer = Buffer.alloc(512);\n\t\tconst bytesRead = readSync(fd, buffer, 0, 512, 0);\n\t\tcloseSync(fd);\n\t\tconst firstLine = buffer.toString(\"utf8\", 0, bytesRead).split(\"\\n\")[0];\n\t\tif (!firstLine) return null;\n\t\tconst header = JSON.parse(firstLine) as Record<string, unknown>;\n\t\tif (header.type !== \"session\" || typeof header.id !== \"string\") {\n\t\t\treturn null;\n\t\t}\n\t\treturn header as unknown as SessionHeader;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nexport function getSessionHeaderCwd(header: SessionHeader): string | undefined {\n\tconst cwd = (header as { cwd?: unknown }).cwd;\n\treturn typeof cwd === \"string\" ? cwd : undefined;\n}\n\nexport function sessionCwdMatches(cwd: string | undefined, resolvedCwd: string): boolean {\n\treturn cwd !== undefined && cwd !== \"\" && resolvePath(cwd) === resolvedCwd;\n}\n\n/** Exported for testing */\nexport function findMostRecentSession(sessionDir: string, cwd?: string): string | null {\n\tconst resolvedSessionDir = normalizePath(sessionDir);\n\tconst resolvedCwd = cwd ? resolvePath(cwd) : undefined;\n\ttry {\n\t\tconst files = readdirSync(resolvedSessionDir)\n\t\t\t.filter((f) => f.endsWith(\".jsonl\"))\n\t\t\t.map((f) => join(resolvedSessionDir, f))\n\t\t\t.map((path) => ({ path, header: readSessionHeader(path) }))\n\t\t\t.filter(\n\t\t\t\t(file): file is { path: string; header: SessionHeader } =>\n\t\t\t\t\tfile.header !== null &&\n\t\t\t\t\t(!resolvedCwd || sessionCwdMatches(getSessionHeaderCwd(file.header), resolvedCwd)),\n\t\t\t)\n\t\t\t.map(({ path }) => ({ path, mtime: statSync(path).mtime }))\n\t\t\t.sort((a, b) => b.mtime.getTime() - a.mtime.getTime());\n\n\t\treturn files[0]?.path || null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nexport function serializeSessionEntries(entries: FileEntry[]): string {\n\treturn `${entries.map((e) => JSON.stringify(e)).join(\"\\n\")}\\n`;\n}\n\nexport function writeSessionEntries(filePath: string, entries: FileEntry[]): void {\n\twriteFileSync(filePath, serializeSessionEntries(entries));\n}\n\nexport function appendSessionEntry(filePath: string, entry: FileEntry): void {\n\tappendFileSync(filePath, `${JSON.stringify(entry)}\\n`);\n}\n\nexport function appendSessionEntries(filePath: string, entries: FileEntry[]): void {\n\tfor (const entry of entries) {\n\t\tappendSessionEntry(filePath, entry);\n\t}\n}\n\nexport function hasAssistantMessage(entries: FileEntry[]): boolean {\n\treturn entries.some((entry): entry is SessionEntry => entry.type === \"message\" && entry.message.role === \"assistant\");\n}\n\nexport function ensureDirectory(dir: string): void {\n\tif (!existsSync(dir)) {\n\t\tmkdirSync(dir, { recursive: true });\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"session-manager-storage.js","sourceRoot":"","sources":["../../src/core/session-manager-storage.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,cAAc,EACd,SAAS,EACT,UAAU,EACV,SAAS,EACT,QAAQ,EACR,WAAW,EACX,QAAQ,EACR,QAAQ,EACR,aAAa,GACb,MAAM,IAAI,CAAC;AACZ,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAG/D,MAAM,wBAAwB,GAAG,IAAI,GAAG,IAAI,CAAC;AAC7C;;;;;GAKG;AACH,MAAM,uBAAuB,GAAG,EAAE,GAAG,IAAI,CAAC;AAE1C,SAAS,qBAAqB,CAAC,IAAY;IAC1C,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;QAAE,OAAO,IAAI,CAAC;IAC9B,IAAI,CAAC;QACJ,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAc,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,IAAI,CAAC;IACb,CAAC;AACF,CAAC;AAED,2BAA2B;AAC3B,MAAM,UAAU,mBAAmB,CAAC,QAAgB;IACnD,MAAM,gBAAgB,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;IACjD,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC;QAAE,OAAO,EAAE,CAAC;IAE7C,MAAM,OAAO,GAAgB,EAAE,CAAC;IAChC,MAAM,EAAE,GAAG,QAAQ,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAC;IAC3C,IAAI,CAAC;QACJ,MAAM,OAAO,GAAG,IAAI,aAAa,CAAC,MAAM,CAAC,CAAC;QAC1C,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC,wBAAwB,CAAC,CAAC;QAC5D,IAAI,OAAO,GAAG,EAAE,CAAC;QAEjB,OAAO,IAAI,EAAE,CAAC;YACb,MAAM,SAAS,GAAG,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;YAC/D,IAAI,SAAS,KAAK,CAAC;gBAAE,MAAM;YAE3B,OAAO,IAAI,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC;YACxD,IAAI,SAAS,GAAG,CAAC,CAAC;YAClB,IAAI,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;YACpD,OAAO,YAAY,KAAK,CAAC,CAAC,EAAE,CAAC;gBAC5B,MAAM,KAAK,GAAG,qBAAqB,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC,CAAC;gBAC5E,IAAI,KAAK;oBAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBAC/B,SAAS,GAAG,YAAY,GAAG,CAAC,CAAC;gBAC7B,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;YACjD,CAAC;YACD,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QACpC,CAAC;QAED,OAAO,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;QACzB,MAAM,UAAU,GAAG,qBAAqB,CAAC,OAAO,CAAC,CAAC;QAClD,IAAI,UAAU;YAAE,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAC1C,CAAC;YAAS,CAAC;QACV,SAAS,CAAC,EAAE,CAAC,CAAC;IACf,CAAC;IAED,0BAA0B;IAC1B,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,OAAO,CAAC;IACzC,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAC1B,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,OAAO,MAAM,CAAC,EAAE,KAAK,QAAQ,EAAE,CAAC;QACrF,OAAO,EAAE,CAAC;IACX,CAAC;IAED,OAAO,OAAO,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,QAAgB;IACjD,IAAI,CAAC;QACJ,MAAM,EAAE,GAAG,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;QACnC,IAAI,CAAC;YACJ,uEAAuE;YACvE,wEAAwE;YACxE,+DAA+D;YAC/D,MAAM,OAAO,GAAG,IAAI,aAAa,CAAC,MAAM,CAAC,CAAC;YAC1C,qEAAqE;YACrE,uEAAuE;YACvE,qEAAqE;YACrE,uDAAuD;YACvD,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC,uBAAuB,CAAC,CAAC;YAC3D,IAAI,OAAO,GAAG,EAAE,CAAC;YACjB,IAAI,YAAY,GAAG,KAAK,CAAC;YACzB,OAAO,IAAI,EAAE,CAAC;gBACb,MAAM,SAAS,GAAG,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;gBAC/D,IAAI,SAAS,KAAK,CAAC;oBAAE,MAAM;gBAC3B,OAAO,IAAI,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC;gBACxD,MAAM,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;gBAC3C,IAAI,YAAY,KAAK,CAAC,CAAC,EAAE,CAAC;oBACzB,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC;oBACzC,YAAY,GAAG,IAAI,CAAC;oBACpB,MAAM;gBACP,CAAC;YACF,CAAC;YACD,mEAAmE;YACnE,sEAAsE;YACtE,kEAAkE;YAClE,IAAI,CAAC,YAAY,EAAE,CAAC;gBACnB,OAAO,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;YAC1B,CAAC;YACD,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;YACzC,IAAI,CAAC,SAAS;gBAAE,OAAO,IAAI,CAAC;YAC5B,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAA4B,CAAC;YAChE,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,IAAI,OAAO,MAAM,CAAC,EAAE,KAAK,QAAQ,EAAE,CAAC;gBAChE,OAAO,IAAI,CAAC;YACb,CAAC;YACD,OAAO,MAAkC,CAAC;QAC3C,CAAC;gBAAS,CAAC;YACV,SAAS,CAAC,EAAE,CAAC,CAAC;QACf,CAAC;IACF,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,IAAI,CAAC;IACb,CAAC;AACF,CAAC;AAED,wGAAwG;AACxG,MAAM,UAAU,gBAAgB,CAAC,MAAwC;IACxE,OAAO,MAAM,EAAE,QAAQ,KAAK,IAAI,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,MAAqB;IACxD,MAAM,GAAG,GAAI,MAA4B,CAAC,GAAG,CAAC;IAC9C,OAAO,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,GAAuB,EAAE,WAAmB;IAC7E,OAAO,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,EAAE,IAAI,WAAW,CAAC,GAAG,CAAC,KAAK,WAAW,CAAC;AAC5E,CAAC;AAED,2BAA2B;AAC3B,MAAM,UAAU,qBAAqB,CAAC,UAAkB,EAAE,GAAY,EAAE,eAAe,GAAG,KAAK;IAC9F,MAAM,kBAAkB,GAAG,aAAa,CAAC,UAAU,CAAC,CAAC;IACrD,MAAM,WAAW,GAAG,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACvD,IAAI,CAAC;QACJ,MAAM,KAAK,GAAG,WAAW,CAAC,kBAAkB,CAAC;aAC3C,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;aACnC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC,CAAC,CAAC;aACvC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,iBAAiB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;aAC1D,MAAM,CACN,CAAC,IAAI,EAAmD,EAAE,CACzD,IAAI,CAAC,MAAM,KAAK,IAAI;YACpB,CAAC,CAAC,WAAW,IAAI,iBAAiB,CAAC,mBAAmB,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC,CAAC;YAClF,CAAC,eAAe,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CACpD;aACA,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;aAC1D,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QAExD,OAAO,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,IAAI,IAAI,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,IAAI,CAAC;IACb,CAAC;AACF,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,OAAoB;IAC3D,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;AAChE,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,QAAgB,EAAE,OAAoB;IACzE,aAAa,CAAC,QAAQ,EAAE,uBAAuB,CAAC,OAAO,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,QAAgB,EAAE,KAAgB;IACpE,cAAc,CAAC,QAAQ,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AACxD,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,QAAgB,EAAE,OAAoB;IAC1E,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC7B,kBAAkB,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IACrC,CAAC;AACF,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,OAAoB;IACvD,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,EAAyB,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,SAAS,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC;AACvH,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,GAAW;IAC1C,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACtB,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACrC,CAAC;AACF,CAAC","sourcesContent":["import {\n\tappendFileSync,\n\tcloseSync,\n\texistsSync,\n\tmkdirSync,\n\topenSync,\n\treaddirSync,\n\treadSync,\n\tstatSync,\n\twriteFileSync,\n} from \"fs\";\nimport { join } from \"path\";\nimport { StringDecoder } from \"string_decoder\";\nimport { normalizePath, resolvePath } from \"../utils/paths.ts\";\nimport type { FileEntry, SessionEntry, SessionHeader } from \"./session-manager-types.ts\";\n\nconst SESSION_READ_BUFFER_SIZE = 1024 * 1024;\n/**\n * Dedicated small read chunk for header-only reads. Session headers are small\n * (typically a few KB), so reading in 64KB chunks avoids allocating/transferring\n * the full 1MiB transcript buffer just to inspect the first line during listing\n * and resume-history prefiltering.\n */\nconst HEADER_READ_BUFFER_SIZE = 64 * 1024;\n\nfunction parseSessionEntryLine(line: string): FileEntry | null {\n\tif (!line.trim()) return null;\n\ttry {\n\t\treturn JSON.parse(line) as FileEntry;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/** Exported for testing */\nexport function loadEntriesFromFile(filePath: string): FileEntry[] {\n\tconst resolvedFilePath = normalizePath(filePath);\n\tif (!existsSync(resolvedFilePath)) return [];\n\n\tconst entries: FileEntry[] = [];\n\tconst fd = openSync(resolvedFilePath, \"r\");\n\ttry {\n\t\tconst decoder = new StringDecoder(\"utf8\");\n\t\tconst buffer = Buffer.allocUnsafe(SESSION_READ_BUFFER_SIZE);\n\t\tlet pending = \"\";\n\n\t\twhile (true) {\n\t\t\tconst bytesRead = readSync(fd, buffer, 0, buffer.length, null);\n\t\t\tif (bytesRead === 0) break;\n\n\t\t\tpending += decoder.write(buffer.subarray(0, bytesRead));\n\t\t\tlet lineStart = 0;\n\t\t\tlet newlineIndex = pending.indexOf(\"\\n\", lineStart);\n\t\t\twhile (newlineIndex !== -1) {\n\t\t\t\tconst entry = parseSessionEntryLine(pending.slice(lineStart, newlineIndex));\n\t\t\t\tif (entry) entries.push(entry);\n\t\t\t\tlineStart = newlineIndex + 1;\n\t\t\t\tnewlineIndex = pending.indexOf(\"\\n\", lineStart);\n\t\t\t}\n\t\t\tpending = pending.slice(lineStart);\n\t\t}\n\n\t\tpending += decoder.end();\n\t\tconst finalEntry = parseSessionEntryLine(pending);\n\t\tif (finalEntry) entries.push(finalEntry);\n\t} finally {\n\t\tcloseSync(fd);\n\t}\n\n\t// Validate session header\n\tif (entries.length === 0) return entries;\n\tconst header = entries[0];\n\tif (header.type !== \"session\" || !(\"id\" in header) || typeof header.id !== \"string\") {\n\t\treturn [];\n\t}\n\n\treturn entries;\n}\n\nexport function readSessionHeader(filePath: string): SessionHeader | null {\n\ttry {\n\t\tconst fd = openSync(filePath, \"r\");\n\t\ttry {\n\t\t\t// Read the full first line rather than a fixed 512-byte window so very\n\t\t\t// long headers (e.g. internal workflow headers carrying stage metadata)\n\t\t\t// are not truncated and dropped from listing/resume filtering.\n\t\t\tconst decoder = new StringDecoder(\"utf8\");\n\t\t\t// Use a small dedicated header buffer instead of the 1MiB transcript\n\t\t\t// buffer so prefiltering internal sessions during listing stays cheap.\n\t\t\t// The loop still reads in chunks until the first newline (or EOF) so\n\t\t\t// headers larger than one chunk are handled correctly.\n\t\t\tconst buffer = Buffer.allocUnsafe(HEADER_READ_BUFFER_SIZE);\n\t\t\tlet pending = \"\";\n\t\t\tlet foundNewline = false;\n\t\t\twhile (true) {\n\t\t\t\tconst bytesRead = readSync(fd, buffer, 0, buffer.length, null);\n\t\t\t\tif (bytesRead === 0) break;\n\t\t\t\tpending += decoder.write(buffer.subarray(0, bytesRead));\n\t\t\t\tconst newlineIndex = pending.indexOf(\"\\n\");\n\t\t\t\tif (newlineIndex !== -1) {\n\t\t\t\t\tpending = pending.slice(0, newlineIndex);\n\t\t\t\t\tfoundNewline = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Only flush the decoder when we hit EOF without a newline. Once a\n\t\t\t// newline was found, any remaining decoder bytes belong to data after\n\t\t\t// the header line; flushing them would corrupt the parsed header.\n\t\t\tif (!foundNewline) {\n\t\t\t\tpending += decoder.end();\n\t\t\t}\n\t\t\tconst firstLine = pending.split(\"\\n\")[0];\n\t\t\tif (!firstLine) return null;\n\t\t\tconst header = JSON.parse(firstLine) as Record<string, unknown>;\n\t\t\tif (header.type !== \"session\" || typeof header.id !== \"string\") {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn header as unknown as SessionHeader;\n\t\t} finally {\n\t\t\tcloseSync(fd);\n\t\t}\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/** Returns true when a session header marks the session as internal (e.g. a workflow stage session). */\nexport function isInternalHeader(header: SessionHeader | null | undefined): boolean {\n\treturn header?.internal === true;\n}\n\nexport function getSessionHeaderCwd(header: SessionHeader): string | undefined {\n\tconst cwd = (header as { cwd?: unknown }).cwd;\n\treturn typeof cwd === \"string\" ? cwd : undefined;\n}\n\nexport function sessionCwdMatches(cwd: string | undefined, resolvedCwd: string): boolean {\n\treturn cwd !== undefined && cwd !== \"\" && resolvePath(cwd) === resolvedCwd;\n}\n\n/** Exported for testing */\nexport function findMostRecentSession(sessionDir: string, cwd?: string, includeInternal = false): string | null {\n\tconst resolvedSessionDir = normalizePath(sessionDir);\n\tconst resolvedCwd = cwd ? resolvePath(cwd) : undefined;\n\ttry {\n\t\tconst files = readdirSync(resolvedSessionDir)\n\t\t\t.filter((f) => f.endsWith(\".jsonl\"))\n\t\t\t.map((f) => join(resolvedSessionDir, f))\n\t\t\t.map((path) => ({ path, header: readSessionHeader(path) }))\n\t\t\t.filter(\n\t\t\t\t(file): file is { path: string; header: SessionHeader } =>\n\t\t\t\t\tfile.header !== null &&\n\t\t\t\t\t(!resolvedCwd || sessionCwdMatches(getSessionHeaderCwd(file.header), resolvedCwd)) &&\n\t\t\t\t\t(includeInternal || !isInternalHeader(file.header)),\n\t\t\t)\n\t\t\t.map(({ path }) => ({ path, mtime: statSync(path).mtime }))\n\t\t\t.sort((a, b) => b.mtime.getTime() - a.mtime.getTime());\n\n\t\treturn files[0]?.path || null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nexport function serializeSessionEntries(entries: FileEntry[]): string {\n\treturn `${entries.map((e) => JSON.stringify(e)).join(\"\\n\")}\\n`;\n}\n\nexport function writeSessionEntries(filePath: string, entries: FileEntry[]): void {\n\twriteFileSync(filePath, serializeSessionEntries(entries));\n}\n\nexport function appendSessionEntry(filePath: string, entry: FileEntry): void {\n\tappendFileSync(filePath, `${JSON.stringify(entry)}\\n`);\n}\n\nexport function appendSessionEntries(filePath: string, entries: FileEntry[]): void {\n\tfor (const entry of entries) {\n\t\tappendSessionEntry(filePath, entry);\n\t}\n}\n\nexport function hasAssistantMessage(entries: FileEntry[]): boolean {\n\treturn entries.some((entry): entry is SessionEntry => entry.type === \"message\" && entry.message.role === \"assistant\");\n}\n\nexport function ensureDirectory(dir: string): void {\n\tif (!existsSync(dir)) {\n\t\tmkdirSync(dir, { recursive: true });\n\t}\n}\n"]}
|