@pugi/cli 0.1.0-beta.7 → 0.1.0-beta.87
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 +96 -0
- package/THIRD_PARTY_NOTICES.md +40 -0
- package/assets/pugi-prozr2-mascot.ansi +9 -0
- package/bin/run.js +33 -1
- package/dist/commands/deploy.js +40 -40
- package/dist/commands/flatten.js +191 -0
- package/dist/commands/jobs-watch.js +201 -0
- package/dist/commands/jobs.js +42 -27
- package/dist/commands/smoke.js +133 -0
- package/dist/core/agent-progress/cleanup.js +134 -0
- package/dist/core/agent-progress/schema.js +144 -0
- package/dist/core/agent-progress/writer.js +101 -0
- package/dist/core/agents/adaptive-router.js +330 -0
- package/dist/core/agents/query-decomposer.js +297 -0
- package/dist/core/agents/registry.js +2 -2
- package/dist/core/approvals/shortcut-resolver.js +98 -0
- package/dist/core/artifact-chain/dispatcher.js +148 -0
- package/dist/core/artifact-chain/exporter.js +164 -0
- package/dist/core/artifact-chain/state.js +243 -0
- package/dist/core/artifact-chain/steps.js +169 -0
- package/dist/core/ask-user/question.js +92 -0
- package/dist/core/audit/audit-trail.js +275 -0
- package/dist/core/auth/ensure-authenticated.js +129 -0
- package/dist/core/auth/env-provider.js +238 -0
- package/dist/core/auto-open-browser.js +4 -4
- package/dist/core/auto-update/channels.js +122 -0
- package/dist/core/auto-update/checker.js +241 -0
- package/dist/core/auto-update/state.js +235 -0
- package/dist/core/bare-mode/index.js +107 -0
- package/dist/core/bash/redirect.js +281 -0
- package/dist/core/bash-classifier.js +436 -40
- package/dist/core/checkpoint/resumer.js +149 -0
- package/dist/core/checkpoint/rewinder.js +291 -0
- package/dist/core/checkpoints/shadow-git.js +670 -0
- package/dist/core/citations/parser.js +109 -0
- package/dist/core/classifier/yolo-classifier.js +88 -0
- package/dist/core/codegraph/decision-store.js +248 -0
- package/dist/core/codegraph/detect-repo.js +459 -0
- package/dist/core/codegraph/install.js +134 -0
- package/dist/core/codegraph/offer-hook.js +220 -0
- package/dist/core/compact/auto-trigger.js +96 -0
- package/dist/core/compact/buffer-rewriter.js +115 -0
- package/dist/core/compact/summarizer.js +208 -0
- package/dist/core/compact/token-counter.js +108 -0
- package/dist/core/consensus/anvil-fanout.js +25 -25
- package/dist/core/consensus/diff-capture.js +121 -12
- package/dist/core/consensus/rubric.js +21 -21
- package/dist/core/context/builder.js +6 -6
- package/dist/core/context/compaction-events.js +8 -8
- package/dist/core/context/compaction.js +31 -31
- package/dist/core/context/index.js +15 -8
- package/dist/core/context/invariants.js +51 -51
- package/dist/core/context/markdown-loader.js +28 -10
- package/dist/core/context/markdown-traverse.js +255 -0
- package/dist/core/context/pugiignore.js +41 -41
- package/dist/core/context/repo-skeleton.js +37 -37
- package/dist/core/context/tool-eviction.js +55 -0
- package/dist/core/context/watcher.js +32 -32
- package/dist/core/context/working-set.js +23 -23
- package/dist/core/coordinator/agent-tools.js +77 -0
- package/dist/core/coordinator/agent-toolset.js +65 -0
- package/dist/core/coordinator/fsm.js +73 -0
- package/dist/core/coordinator/mode-fsm.js +70 -0
- package/dist/core/cost/rate-card.js +129 -0
- package/dist/core/cost/tracker.js +221 -0
- package/dist/core/credentials.js +12 -12
- package/dist/core/cron/scheduler.js +138 -0
- package/dist/core/denial-tracking/index.js +8 -0
- package/dist/core/denial-tracking/state.js +264 -0
- package/dist/core/diagnostics/probe-runner.js +93 -0
- package/dist/core/diagnostics/probes/api.js +46 -0
- package/dist/core/diagnostics/probes/auth.js +93 -0
- package/dist/core/diagnostics/probes/bare-mode.js +42 -0
- package/dist/core/diagnostics/probes/cli-version.js +127 -0
- package/dist/core/diagnostics/probes/config.js +72 -0
- package/dist/core/diagnostics/probes/denial-tracking.js +57 -0
- package/dist/core/diagnostics/probes/disk.js +81 -0
- package/dist/core/diagnostics/probes/engine-live.js +46 -0
- package/dist/core/diagnostics/probes/git.js +65 -0
- package/dist/core/diagnostics/probes/hooks.js +118 -0
- package/dist/core/diagnostics/probes/mcp.js +75 -0
- package/dist/core/diagnostics/probes/node.js +59 -0
- package/dist/core/diagnostics/probes/pnpm.js +36 -0
- package/dist/core/diagnostics/probes/pugi-md.js +89 -0
- package/dist/core/diagnostics/probes/sandbox.js +40 -0
- package/dist/core/diagnostics/probes/session.js +74 -0
- package/dist/core/diagnostics/probes/status-snapshot.js +488 -0
- package/dist/core/diagnostics/probes/workspace.js +63 -0
- package/dist/core/diagnostics/types.js +70 -0
- package/dist/core/dispatch/cache-cleanup.js +197 -0
- package/dist/core/dispatch/cache-handoff.js +295 -0
- package/dist/core/edits/apply-patch-layer-e.js +189 -0
- package/dist/core/edits/dispatch.js +293 -7
- package/dist/core/edits/format-matrix.js +26 -0
- package/dist/core/edits/fuzzy-ladder.js +650 -0
- package/dist/core/edits/index.js +3 -1
- package/dist/core/edits/journal.js +199 -0
- package/dist/core/edits/layer-a-apply.js +15 -15
- package/dist/core/edits/layer-a-fuzzy-apply.js +198 -0
- package/dist/core/edits/layer-b-apply.js +9 -9
- package/dist/core/edits/layer-c-apply.js +6 -6
- package/dist/core/edits/layer-d-ast.js +557 -14
- package/dist/core/edits/marker-parser.js +12 -12
- package/dist/core/edits/security-gate.js +27 -27
- package/dist/core/edits/verify-hook.js +273 -0
- package/dist/core/edits/worktree.js +322 -0
- package/dist/core/engine/anvil-client.js +140 -26
- package/dist/core/engine/auto-compact.js +179 -0
- package/dist/core/engine/budgets.js +186 -0
- package/dist/core/engine/context-prefix.js +155 -0
- package/dist/core/engine/index.js +1 -1
- package/dist/core/engine/intensity.js +158 -0
- package/dist/core/engine/intent.js +260 -0
- package/dist/core/engine/native-pugi.js +1295 -227
- package/dist/core/engine/prompts.js +134 -16
- package/dist/core/engine/strip-internal-fields.js +124 -0
- package/dist/core/engine/tool-bridge.js +1295 -59
- package/dist/core/evaluation/golden-dataset.js +293 -0
- package/dist/core/feedback/queue.js +177 -0
- package/dist/core/feedback/submitter.js +145 -0
- package/dist/core/file-cache.js +113 -1
- package/dist/core/flatten/flatten-repo.js +439 -0
- package/dist/core/format/osc8-link.js +28 -0
- package/dist/core/hook-chains.js +392 -0
- package/dist/core/hooks/citation-verify-hook.js +138 -0
- package/dist/core/hooks/citation-verify.js +112 -0
- package/dist/core/hooks/events.js +44 -0
- package/dist/core/hooks/index.js +15 -0
- package/dist/core/hooks/registry.js +213 -0
- package/dist/core/hooks/runner.js +236 -0
- package/dist/core/hooks/v2/event-emitter.js +115 -0
- package/dist/core/hooks/v2/executor.js +282 -0
- package/dist/core/hooks/v2/index.js +25 -0
- package/dist/core/hooks/v2/lifecycle.js +104 -0
- package/dist/core/hooks/v2/loader.js +216 -0
- package/dist/core/hooks/v2/matcher.js +125 -0
- package/dist/core/hooks/v2/trust.js +143 -0
- package/dist/core/hooks/v2/types.js +86 -0
- package/dist/core/image/renderer.js +71 -0
- package/dist/core/init/detector.js +582 -0
- package/dist/core/init/template-renderer.js +242 -0
- package/dist/core/jobs/registry.js +18 -18
- package/dist/core/ledger/results-tsv.js +142 -0
- package/dist/core/log-discipline/stdout-redirect.js +51 -0
- package/dist/core/lsp/cache.js +105 -0
- package/dist/core/lsp/client.js +776 -0
- package/dist/core/lsp/language-detect.js +66 -0
- package/dist/core/lsp/post-edit-diagnostics.js +171 -0
- package/dist/core/lsp/symbol-tools.js +372 -0
- package/dist/core/mcp/client.js +97 -28
- package/dist/core/mcp/http-server.js +553 -0
- package/dist/core/mcp/orchestrator-tools.js +662 -0
- package/dist/core/mcp/permission.js +190 -0
- package/dist/core/mcp/registry.js +39 -17
- package/dist/core/mcp/server-tools.js +219 -0
- package/dist/core/mcp/server.js +397 -0
- package/dist/core/mcp/trust.js +10 -10
- package/dist/core/memory/dual-write.js +416 -0
- package/dist/core/memory/passive-extract.js +130 -0
- package/dist/core/memory/phase1-kinds.js +20 -0
- package/dist/core/memory/secret-scanner.js +304 -0
- package/dist/core/memory-sync/queue.js +170 -0
- package/dist/core/metrics/extract.js +113 -0
- package/dist/core/modes/roo-modes.js +68 -0
- package/dist/core/onboarding/ensure-initialized.js +133 -0
- package/dist/core/onboarding/marker.js +111 -0
- package/dist/core/onboarding/telemetry-state.js +108 -0
- package/dist/core/output-style/presets.js +176 -0
- package/dist/core/output-style/state.js +185 -0
- package/dist/core/path-security.js +287 -5
- package/dist/core/permission.js +82 -22
- package/dist/core/permissions/auto-classifier.js +124 -0
- package/dist/core/permissions/bash-parser.js +371 -0
- package/dist/core/permissions/circuit-breaker.js +83 -0
- package/dist/core/permissions/constrained-edit.js +91 -0
- package/dist/core/permissions/gate.js +278 -0
- package/dist/core/permissions/index.js +20 -0
- package/dist/core/permissions/mode.js +174 -0
- package/dist/core/permissions/network-egress.js +137 -0
- package/dist/core/permissions/state.js +241 -0
- package/dist/core/permissions/tool-class.js +93 -0
- package/dist/core/plan-mode/ui-state.js +51 -0
- package/dist/core/plans/plan-artifact.js +721 -0
- package/dist/core/policy-limits/etag-store.js +122 -0
- package/dist/core/prd-check/parser.js +215 -0
- package/dist/core/prd-check/reporter.js +127 -0
- package/dist/core/prd-check/session-review.js +557 -0
- package/dist/core/prd-check/verifiers.js +223 -0
- package/dist/core/prompt-cache/client-cache.js +99 -0
- package/dist/core/prompts/assembly.js +29 -0
- package/dist/core/prompts/registry.js +364 -0
- package/dist/core/pugi-md/cc-compat-rules.js +735 -0
- package/dist/core/pugi-md/context-injector.js +76 -0
- package/dist/core/pugi-md/walk-up.js +207 -0
- package/dist/core/python/uv-installer.js +270 -0
- package/dist/core/python/uv-resolver.js +83 -0
- package/dist/core/rate-limit/narrator.js +146 -0
- package/dist/core/recipes/cli-types.js +20 -0
- package/dist/core/recipes/loader.js +103 -0
- package/dist/core/recipes/runner.js +345 -0
- package/dist/core/recipes/schema.js +587 -0
- package/dist/core/release-notes/parser.js +241 -0
- package/dist/core/release-notes/state.js +116 -0
- package/dist/core/repl/ask.js +37 -37
- package/dist/core/repl/cancellation.js +26 -26
- package/dist/core/repl/cap-warning.js +4 -4
- package/dist/core/repl/clipboard-read.js +11 -11
- package/dist/core/repl/dispatch-fsm.js +12 -12
- package/dist/core/repl/history-search.js +15 -15
- package/dist/core/repl/history.js +28 -18
- package/dist/core/repl/kill-ring.js +5 -5
- package/dist/core/repl/model-pricing.js +135 -0
- package/dist/core/repl/privacy-banner.js +22 -22
- package/dist/core/repl/session.js +2157 -214
- package/dist/core/repl/slash-commands.js +533 -40
- package/dist/core/repl/store/index.js +1 -1
- package/dist/core/repl/store/jsonl-log.js +22 -22
- package/dist/core/repl/store/lockfile.js +10 -10
- package/dist/core/repl/store/session-store.js +136 -107
- package/dist/core/repl/store/types.js +15 -15
- package/dist/core/repl/store/uuid-v7.js +12 -12
- package/dist/core/repl/workspace-context.js +43 -21
- package/dist/core/repo-map/build.js +125 -0
- package/dist/core/repo-map/cache.js +185 -0
- package/dist/core/repo-map/extractor.js +254 -0
- package/dist/core/repo-map/formatter.js +145 -0
- package/dist/core/repo-map/page-rank.js +105 -0
- package/dist/core/repo-map/scanner.js +211 -0
- package/dist/core/retry-budget/budget.js +284 -0
- package/dist/core/retry-budget/index.js +5 -0
- package/dist/core/retry-budget/retry-cap.js +74 -0
- package/dist/core/routing/lead-worker.js +43 -0
- package/dist/core/routing/pre-flight-estimator.js +108 -0
- package/dist/core/runs/run-tree.js +103 -0
- package/dist/core/security/injection-scanner.js +367 -0
- package/dist/core/security/output-filter.js +418 -0
- package/dist/core/session/env-file.js +105 -0
- package/dist/core/session/section-budgets.js +140 -0
- package/dist/core/session.js +92 -0
- package/dist/core/settings.js +286 -5
- package/dist/core/share/formatter.js +271 -0
- package/dist/core/share/redactor.js +221 -0
- package/dist/core/share/uploader.js +267 -0
- package/dist/core/skills/defaults.js +457 -0
- package/dist/core/skills/loader.js +22 -22
- package/dist/core/skills/sources.js +27 -27
- package/dist/core/smoke/headless-driver.js +174 -0
- package/dist/core/smoke/orchestrator.js +194 -0
- package/dist/core/smoke/runner.js +238 -0
- package/dist/core/smoke/scenario-parser.js +316 -0
- package/dist/core/statusline.js +99 -0
- package/dist/core/subagents/dispatcher-real.js +600 -0
- package/dist/core/subagents/dispatcher.js +132 -43
- package/dist/core/subagents/index.js +19 -6
- package/dist/core/subagents/isolation-matrix.js +213 -0
- package/dist/core/subagents/spawn.js +19 -4
- package/dist/core/telemetry/emitter.js +229 -0
- package/dist/core/telemetry/queue.js +251 -0
- package/dist/core/theme/context.js +91 -0
- package/dist/core/theme/presets.js +228 -0
- package/dist/core/theme/state.js +181 -0
- package/dist/core/todos/invariant.js +10 -0
- package/dist/core/todos/state.js +177 -0
- package/dist/core/tool-schema/compressor.js +89 -0
- package/dist/core/transport/version-interceptor.js +166 -0
- package/dist/core/trust.js +2 -2
- package/dist/core/tui/thinking-block.js +64 -0
- package/dist/core/vim/keymap.js +288 -0
- package/dist/core/vim/state.js +92 -0
- package/dist/core/watch-markers/marker-watcher.js +133 -0
- package/dist/core/worktree-manager/cleanup.js +123 -0
- package/dist/core/worktree-manager/manager.js +303 -0
- package/dist/index.js +28 -0
- package/dist/runtime/bootstrap.js +190 -0
- package/dist/runtime/cli.js +4162 -488
- package/dist/runtime/commands/agents.js +30 -30
- package/dist/runtime/commands/budget.js +5 -5
- package/dist/runtime/commands/cancel.js +231 -0
- package/dist/runtime/commands/chain.js +489 -0
- package/dist/runtime/commands/codegraph-status.js +227 -0
- package/dist/runtime/commands/compact.js +297 -0
- package/dist/runtime/commands/config.js +32 -32
- package/dist/runtime/commands/cost.js +199 -0
- package/dist/runtime/commands/delegate.js +244 -13
- package/dist/runtime/commands/dispatch.js +126 -0
- package/dist/runtime/commands/doctor.js +579 -0
- package/dist/runtime/commands/feedback.js +184 -0
- package/dist/runtime/commands/hooks.js +184 -0
- package/dist/runtime/commands/init.js +254 -0
- package/dist/runtime/commands/lsp.js +368 -0
- package/dist/runtime/commands/mcp.js +879 -0
- package/dist/runtime/commands/memory.js +582 -0
- package/dist/runtime/commands/model.js +237 -0
- package/dist/runtime/commands/onboarding.js +275 -0
- package/dist/runtime/commands/patch.js +128 -0
- package/dist/runtime/commands/permissions.js +112 -0
- package/dist/runtime/commands/plan.js +143 -0
- package/dist/runtime/commands/prd-check.js +285 -0
- package/dist/runtime/commands/privacy.js +17 -17
- package/dist/runtime/commands/recipe.js +325 -0
- package/dist/runtime/commands/redo-blob-store.js +92 -0
- package/dist/runtime/commands/redo.js +361 -0
- package/dist/runtime/commands/release-notes.js +229 -0
- package/dist/runtime/commands/repo-map.js +95 -0
- package/dist/runtime/commands/report.js +299 -0
- package/dist/runtime/commands/resume.js +118 -0
- package/dist/runtime/commands/review-consensus.js +68 -53
- package/dist/runtime/commands/rewind.js +333 -0
- package/dist/runtime/commands/roster.js +14 -14
- package/dist/runtime/commands/sessions.js +163 -0
- package/dist/runtime/commands/share.js +316 -0
- package/dist/runtime/commands/skills.js +31 -31
- package/dist/runtime/commands/status.js +186 -0
- package/dist/runtime/commands/stickers.js +82 -0
- package/dist/runtime/commands/style.js +194 -0
- package/dist/runtime/commands/theme.js +196 -0
- package/dist/runtime/commands/undo.js +54 -22
- package/dist/runtime/commands/update.js +289 -0
- package/dist/runtime/commands/vim.js +140 -0
- package/dist/runtime/commands/worktree.js +177 -0
- package/dist/runtime/commands/worktrees.js +155 -0
- package/dist/runtime/headless-repl.js +195 -0
- package/dist/runtime/headless.js +543 -0
- package/dist/runtime/load-hooks-or-exit.js +71 -0
- package/dist/runtime/plan-decompose.js +531 -0
- package/dist/runtime/update-check.js +28 -28
- package/dist/runtime/version.js +65 -0
- package/dist/skills/bundled/batch.js +617 -0
- package/dist/skills/bundled/index.js +45 -0
- package/dist/skills/bundled/loop.js +358 -0
- package/dist/skills/bundled/remember.js +383 -0
- package/dist/skills/bundled/simplify.js +289 -0
- package/dist/skills/bundled/skillify.js +373 -0
- package/dist/skills/bundled/stuck.js +558 -0
- package/dist/skills/bundled/verify.js +439 -0
- package/dist/testing/vcr.js +486 -0
- package/dist/tools/agent-tool.js +229 -0
- package/dist/tools/apply-patch.js +556 -0
- package/dist/tools/ask-user-question.js +222 -0
- package/dist/tools/ask-user.js +115 -0
- package/dist/tools/bash.js +623 -45
- package/dist/tools/brief.js +224 -0
- package/dist/tools/enter-worktree.js +250 -0
- package/dist/tools/exit-worktree.js +147 -0
- package/dist/tools/file-tools.js +161 -44
- package/dist/tools/lsp-tools.js +189 -0
- package/dist/tools/mcp-tool.js +260 -0
- package/dist/tools/multi-edit.js +361 -0
- package/dist/tools/powershell.js +268 -0
- package/dist/tools/registry.js +85 -0
- package/dist/tools/skill-tool.js +96 -0
- package/dist/tools/sleep.js +99 -0
- package/dist/tools/synthetic-output.js +133 -0
- package/dist/tools/tasks.js +208 -0
- package/dist/tools/todo-write.js +184 -0
- package/dist/tools/verify-plan-execution.js +295 -0
- package/dist/tools/web-fetch-injection-scanner.js +207 -0
- package/dist/tools/web-fetch.js +195 -10
- package/dist/tools/web-search.js +458 -0
- package/dist/tui/agent-progress-card.js +111 -0
- package/dist/tui/agent-tree.js +11 -1
- package/dist/tui/ask-modal.js +14 -14
- package/dist/tui/ask-user-question-prompt.js +203 -0
- package/dist/tui/compact-banner.js +81 -0
- package/dist/tui/conversation-pane.js +85 -11
- package/dist/tui/cost-table.js +111 -0
- package/dist/tui/device-flow.js +2 -2
- package/dist/tui/doctor-table.js +46 -0
- package/dist/tui/feedback-prompt.js +156 -0
- package/dist/tui/input-box.js +247 -32
- package/dist/tui/login-picker.js +3 -3
- package/dist/tui/markdown-render.js +6 -6
- package/dist/tui/onboarding-wizard.js +240 -0
- package/dist/tui/permissions-picker.js +86 -0
- package/dist/tui/render.js +35 -0
- package/dist/tui/repl-render.js +332 -54
- package/dist/tui/repl-splash-art.js +16 -16
- package/dist/tui/repl-splash-mascot.js +48 -24
- package/dist/tui/repl-splash.js +22 -22
- package/dist/tui/repl.js +124 -44
- package/dist/tui/slash-palette.js +6 -6
- package/dist/tui/splash.js +2 -2
- package/dist/tui/status-bar.js +109 -31
- package/dist/tui/status-table.js +7 -0
- package/dist/tui/stickers-art.js +136 -0
- package/dist/tui/style-table.js +28 -0
- package/dist/tui/theme-table.js +29 -0
- package/dist/tui/thinking-spinner.js +123 -0
- package/dist/tui/tool-stream-pane.js +53 -4
- package/dist/tui/update-banner.js +27 -2
- package/dist/tui/vim-input.js +267 -0
- package/dist/tui/welcome-banner.js +107 -0
- package/dist/tui/welcome-data.js +293 -0
- package/dist/tui/workspace-context.js +2 -2
- package/docs/examples/codegraph.mcp.json +10 -0
- package/package.json +23 -6
- package/test/scenarios/codegen-create-file.scenario.txt +13 -0
- package/test/scenarios/compact-force.scenario.txt +11 -0
- package/test/scenarios/identity.scenario.txt +11 -0
- package/test/scenarios/persona-handoff.scenario.txt +11 -0
- package/test/scenarios/walkback.scenario.txt +12 -0
- package/dist/core/engine/compaction-hook.js +0 -154
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Append-only JSONL event log — Sprint
|
|
2
|
+
* Append-only JSONL event log — Sprint .
|
|
3
3
|
*
|
|
4
4
|
* Durable truth for the session. The SQLite index is rebuildable from
|
|
5
5
|
* the JSONL; if the index is lost or corrupt, `pugi sessions --rebuild`
|
|
@@ -8,10 +8,10 @@
|
|
|
8
8
|
*
|
|
9
9
|
* File layout per session directory:
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
11
|
+
* <sessionDir>/events.0.jsonl active rotation file
|
|
12
|
+
* <sessionDir>/events.1.jsonl previous rotation, oldest event first
|
|
13
|
+
* <sessionDir>/events.2.jsonl older still
|
|
14
|
+
* ...
|
|
15
15
|
*
|
|
16
16
|
* Rotation threshold is 50 MB per spec §4.2 must-ship #2. When the
|
|
17
17
|
* active file exceeds the threshold AFTER a write, we close it, rename
|
|
@@ -21,23 +21,23 @@
|
|
|
21
21
|
*
|
|
22
22
|
* Crash safety:
|
|
23
23
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
24
|
+
* - Each write is a single `appendFileSync` of `JSON.stringify(event)\n`.
|
|
25
|
+
* On Linux/macOS, append-mode writes of bytes < PIPE_BUF (4096) are
|
|
26
|
+
* atomic with respect to other appends. Our events are well under
|
|
27
|
+
* 4 KB so the OS guarantees per-line atomicity even if two
|
|
28
|
+
* processes hold append fds (the lockfile already prevents that,
|
|
29
|
+
* but defence-in-depth).
|
|
30
|
+
* - After each write we call `fsync(fd)` so the bytes are durable
|
|
31
|
+
* against power loss / kernel panic. Slow but correct — the
|
|
32
|
+
* conversation-flow path is throughput-bound by the LLM, not by
|
|
33
|
+
* the disk, so a few hundred extra microseconds per event is
|
|
34
|
+
* invisible to the operator.
|
|
35
|
+
* - On reopen we walk every line and drop any that fail JSON parse.
|
|
36
|
+
* A crash mid-write leaves a truncated tail; we treat it as
|
|
37
|
+
* "event was never written" and continue from there. The next
|
|
38
|
+
* write may overlap with the truncated bytes; that is acceptable
|
|
39
|
+
* because the truncated line is invalid JSON either way and the
|
|
40
|
+
* reader is already designed to skip it.
|
|
41
41
|
*/
|
|
42
42
|
import { closeSync, existsSync, fsyncSync, mkdirSync, openSync, readdirSync, readFileSync, renameSync, statSync, writeSync, } from 'node:fs';
|
|
43
43
|
import { resolve } from 'node:path';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* PID lockfile — Sprint
|
|
2
|
+
* PID lockfile — Sprint .
|
|
3
3
|
*
|
|
4
4
|
* Prevents two REPL processes in the same project directory from
|
|
5
5
|
* writing to the same `session.db` concurrently. SQLite's WAL handles
|
|
@@ -9,15 +9,15 @@
|
|
|
9
9
|
*
|
|
10
10
|
* Algorithm:
|
|
11
11
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
12
|
+
* 1. Try `O_CREAT | O_EXCL` open of `<dir>/session.lock`. Write the
|
|
13
|
+
* caller's PID + process start time as the body. If the open
|
|
14
|
+
* succeeds, we hold the lock; return.
|
|
15
|
+
* 2. If `EEXIST`, read the existing PID. Probe with `kill(pid, 0)`
|
|
16
|
+
* (signal 0 = "check liveness, do not deliver"). If the probe
|
|
17
|
+
* returns ESRCH, the holder is dead — unlink the stale lock and
|
|
18
|
+
* retry the exclusive create.
|
|
19
|
+
* 3. If the probe says the PID is alive, throw `SessionLockBusyError`
|
|
20
|
+
* so the caller surfaces a clean message to the operator.
|
|
21
21
|
*
|
|
22
22
|
* Why not `proper-lockfile`: pulling a 14kB dependency for a 60-line
|
|
23
23
|
* PID file is poor cost/benefit. The stale-detection loop is the only
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* SessionStore — Sprint
|
|
2
|
+
* SessionStore — Sprint .
|
|
3
3
|
*
|
|
4
4
|
* SQLite-indexed, JSONL-durable session store. The JSONL log is the
|
|
5
5
|
* source of truth; the SQLite tables are a queryable cache. Layout:
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
7
|
+
* ~/.pugi/projects/<project-slug>/
|
|
8
|
+
* session.db SQLite index (sessions + FTS5)
|
|
9
|
+
* session.lock PID lockfile
|
|
10
|
+
* sessions/<session-id>/
|
|
11
|
+
* events.0.jsonl active append log
|
|
12
|
+
* events.1.jsonl older rotations (50 MB threshold)
|
|
13
13
|
*
|
|
14
14
|
* The store lives entirely under `$HOME/.pugi/` per the local-first
|
|
15
15
|
* invariants memo (`feedback_no_landing_oes_secondary_2026_05_23.md`)
|
|
@@ -19,20 +19,20 @@
|
|
|
19
19
|
*
|
|
20
20
|
* Why node:sqlite over better-sqlite3:
|
|
21
21
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
22
|
+
* - Zero install-time native build. better-sqlite3 needs prebuilt
|
|
23
|
+
* binaries for every Node ABI × platform; missing a wheel forces
|
|
24
|
+
* a node-gyp compile that fails on bare-metal CI agents without
|
|
25
|
+
* a C++ toolchain. Pugi CLI ships via npm to operators who almost
|
|
26
|
+
* certainly do not have build-essential installed.
|
|
27
|
+
* - Available since Node 22.5.0 (stable subset; LTS as of 2026). Our
|
|
28
|
+
* `engines.node` is pinned to `>=22.5.0` in apps/pugi-cli +
|
|
29
|
+
* packages/pugi-sdk so npm refuses to install the CLI on Node 20.x
|
|
30
|
+
* instead of crash-on-import. CI runs Node 22 (matrix-free; see
|
|
31
|
+
* `.github/workflows/ci.yml`).
|
|
32
|
+
* - Marked Experimental by Node — silenced via process.emitWarning
|
|
33
|
+
* suppression at boot. The API surface we use (DatabaseSync,
|
|
34
|
+
* StatementSync, exec/prepare) is the stable subset that has
|
|
35
|
+
* shipped unchanged since Node 22.5.
|
|
36
36
|
*
|
|
37
37
|
* Schema is created at first open. `schema_meta` carries a single row
|
|
38
38
|
* `('version', '1')`; future migrations bump the version + run the
|
|
@@ -47,32 +47,32 @@ import { takeLock } from './lockfile.js';
|
|
|
47
47
|
import { uuidV7 } from './uuid-v7.js';
|
|
48
48
|
const SCHEMA_VERSION = '1';
|
|
49
49
|
const SCHEMA_SQL = `
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
50
|
+
CREATE TABLE IF NOT EXISTS sessions (
|
|
51
|
+
id TEXT PRIMARY KEY,
|
|
52
|
+
created_at INTEGER NOT NULL,
|
|
53
|
+
updated_at INTEGER NOT NULL,
|
|
54
|
+
workspace_root TEXT NOT NULL,
|
|
55
|
+
branch TEXT,
|
|
56
|
+
project_slug TEXT NOT NULL,
|
|
57
|
+
title TEXT,
|
|
58
|
+
turn_count INTEGER NOT NULL DEFAULT 0,
|
|
59
|
+
event_count INTEGER NOT NULL DEFAULT 0,
|
|
60
|
+
model TEXT,
|
|
61
|
+
tenant_id TEXT,
|
|
62
|
+
status TEXT NOT NULL DEFAULT 'active'
|
|
63
|
+
);
|
|
64
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_updated ON sessions(updated_at DESC);
|
|
65
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_project ON sessions(project_slug, updated_at DESC);
|
|
66
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS sessions_fts USING fts5(
|
|
67
|
+
id UNINDEXED,
|
|
68
|
+
title,
|
|
69
|
+
body,
|
|
70
|
+
tokenize='unicode61'
|
|
71
|
+
);
|
|
72
|
+
CREATE TABLE IF NOT EXISTS schema_meta (
|
|
73
|
+
key TEXT PRIMARY KEY,
|
|
74
|
+
value TEXT NOT NULL
|
|
75
|
+
);
|
|
76
76
|
`;
|
|
77
77
|
/** Default list limit. Operators rarely scroll past the most recent 50. */
|
|
78
78
|
const DEFAULT_LIST_LIMIT = 50;
|
|
@@ -180,13 +180,13 @@ export class SqliteSessionStore {
|
|
|
180
180
|
// bump turn_count and set title. Splitting into multiple statements
|
|
181
181
|
// would race the lockfile if a second process slipped through.
|
|
182
182
|
const sql = `
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
183
|
+
UPDATE sessions
|
|
184
|
+
SET event_count = event_count + 1,
|
|
185
|
+
updated_at = ?,
|
|
186
|
+
turn_count = turn_count + ${isUserTurn ? 1 : 0},
|
|
187
|
+
title = COALESCE(title, ?)
|
|
188
|
+
WHERE id = ?
|
|
189
|
+
`;
|
|
190
190
|
db.prepare(sql).run(ts, titlePatch ?? null, sessionId);
|
|
191
191
|
// Mirror the user-turn body into FTS so substring search hits
|
|
192
192
|
// every previous user turn, not just the most recent one.
|
|
@@ -237,13 +237,13 @@ export class SqliteSessionStore {
|
|
|
237
237
|
}
|
|
238
238
|
const whereClause = where.length > 0 ? `WHERE ${where.join(' AND ')}` : '';
|
|
239
239
|
const sql = `
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
240
|
+
SELECT id, created_at, updated_at, workspace_root, branch, project_slug,
|
|
241
|
+
title, turn_count, event_count, model, tenant_id, status
|
|
242
|
+
FROM sessions
|
|
243
|
+
${whereClause}
|
|
244
|
+
ORDER BY updated_at DESC
|
|
245
|
+
LIMIT ?
|
|
246
|
+
`;
|
|
247
247
|
const stmt = db.prepare(sql);
|
|
248
248
|
const rows = stmt.all(...params, limit);
|
|
249
249
|
return rows.map(rawToSession);
|
|
@@ -294,15 +294,15 @@ export class SqliteSessionStore {
|
|
|
294
294
|
// `unable to use function MATCH in the requested context`. We
|
|
295
295
|
// search title+body together by matching against `sessions_fts`.
|
|
296
296
|
const sql = `
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
297
|
+
SELECT s.id, s.created_at, s.updated_at, s.workspace_root, s.branch,
|
|
298
|
+
s.project_slug, s.title, s.turn_count, s.event_count, s.model,
|
|
299
|
+
s.tenant_id, s.status
|
|
300
|
+
FROM sessions_fts f
|
|
301
|
+
JOIN sessions s ON s.id = f.id
|
|
302
|
+
WHERE sessions_fts MATCH ?
|
|
303
|
+
ORDER BY s.updated_at DESC
|
|
304
|
+
LIMIT ?
|
|
305
|
+
`;
|
|
306
306
|
let rows;
|
|
307
307
|
try {
|
|
308
308
|
rows = db.prepare(sql).all(ftsQuery, limit);
|
|
@@ -361,10 +361,10 @@ export class SqliteSessionStore {
|
|
|
361
361
|
// which maps to SQLITE_OPEN_READONLY. The option form is the
|
|
362
362
|
// documented API; the file-URI form (file:...?mode=ro) also works.
|
|
363
363
|
const db = new DatabaseSync(dbPath, { readOnly: true });
|
|
364
|
-
return new SqliteSessionStoreReadOnlyView(db);
|
|
364
|
+
return new SqliteSessionStoreReadOnlyView(db, projectStoreDir);
|
|
365
365
|
}
|
|
366
366
|
/* ------------------------------------------------------------ */
|
|
367
|
-
/* Internals
|
|
367
|
+
/* Internals */
|
|
368
368
|
/* ------------------------------------------------------------ */
|
|
369
369
|
takeLockOrThrow() {
|
|
370
370
|
const lockPath = resolve(this.projectDir, 'session.lock');
|
|
@@ -420,11 +420,11 @@ export class SqliteSessionStore {
|
|
|
420
420
|
getSessionSync(sessionId) {
|
|
421
421
|
const db = this.requireDb();
|
|
422
422
|
const stmt = db.prepare(`
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
423
|
+
SELECT id, created_at, updated_at, workspace_root, branch, project_slug,
|
|
424
|
+
title, turn_count, event_count, model, tenant_id, status
|
|
425
|
+
FROM sessions
|
|
426
|
+
WHERE id = ?
|
|
427
|
+
`);
|
|
428
428
|
const row = stmt.get(sessionId);
|
|
429
429
|
return row ? rawToSession(row) : null;
|
|
430
430
|
}
|
|
@@ -432,11 +432,11 @@ export class SqliteSessionStore {
|
|
|
432
432
|
const db = this.requireDb();
|
|
433
433
|
const ts = this.now();
|
|
434
434
|
db.prepare(`
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
435
|
+
INSERT INTO sessions
|
|
436
|
+
(id, created_at, updated_at, workspace_root, branch, project_slug,
|
|
437
|
+
title, turn_count, event_count, model, tenant_id, status)
|
|
438
|
+
VALUES (?, ?, ?, ?, ?, ?, NULL, 0, 0, ?, ?, 'active')
|
|
439
|
+
`).run(input.id, ts, ts, input.workspaceRoot, input.branch, input.projectSlug, input.model, input.tenantId);
|
|
440
440
|
// Seed an empty FTS row so the title patch path can rely on a
|
|
441
441
|
// single UPDATE OR REPLACE without first checking for the row.
|
|
442
442
|
this.upsertFtsRow(input.id, '', '');
|
|
@@ -584,8 +584,37 @@ export class SqliteSessionStore {
|
|
|
584
584
|
*/
|
|
585
585
|
export class SqliteSessionStoreReadOnlyView {
|
|
586
586
|
db;
|
|
587
|
-
|
|
587
|
+
projectStoreDir;
|
|
588
|
+
constructor(db,
|
|
589
|
+
/**
|
|
590
|
+
* Project store directory — required for the JSONL event read path.
|
|
591
|
+
* L9 : `/rewind` + `/resume` need to walk events from
|
|
592
|
+
* inside the read-only view so the rewind picker + resume preview
|
|
593
|
+
* never take the writer lockfile.
|
|
594
|
+
*/
|
|
595
|
+
projectStoreDir) {
|
|
588
596
|
this.db = db;
|
|
597
|
+
this.projectStoreDir = projectStoreDir;
|
|
598
|
+
}
|
|
599
|
+
/**
|
|
600
|
+
* Read every event for a session via the durable JSONL log. The
|
|
601
|
+
* SQLite cache is NOT used here — JSONL is the source of truth and
|
|
602
|
+
* the cache only holds counters. The walk stitches across rotation
|
|
603
|
+
* files (`events.<n>.jsonl`) in the same order `JsonlEventLog.read`
|
|
604
|
+
* uses inside the writer path so consumers see one consistent stream
|
|
605
|
+
* whether they came in via the writer store OR the read-only view.
|
|
606
|
+
*/
|
|
607
|
+
async events(sessionId, opts) {
|
|
608
|
+
const sessionDir = resolve(this.projectStoreDir, 'sessions', sessionId);
|
|
609
|
+
if (!existsSync(sessionDir))
|
|
610
|
+
return [];
|
|
611
|
+
const log = new JsonlEventLog({ sessionDir });
|
|
612
|
+
try {
|
|
613
|
+
return log.read(opts);
|
|
614
|
+
}
|
|
615
|
+
finally {
|
|
616
|
+
log.close();
|
|
617
|
+
}
|
|
589
618
|
}
|
|
590
619
|
async list(opts) {
|
|
591
620
|
const limit = clampLimit(opts?.limit ?? DEFAULT_LIST_LIMIT, MAX_LIST_LIMIT);
|
|
@@ -613,13 +642,13 @@ export class SqliteSessionStoreReadOnlyView {
|
|
|
613
642
|
}
|
|
614
643
|
const whereClause = where.length > 0 ? `WHERE ${where.join(' AND ')}` : '';
|
|
615
644
|
const sql = `
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
645
|
+
SELECT id, created_at, updated_at, workspace_root, branch, project_slug,
|
|
646
|
+
title, turn_count, event_count, model, tenant_id, status
|
|
647
|
+
FROM sessions
|
|
648
|
+
${whereClause}
|
|
649
|
+
ORDER BY updated_at DESC
|
|
650
|
+
LIMIT ?
|
|
651
|
+
`;
|
|
623
652
|
const rows = this.db
|
|
624
653
|
.prepare(sql)
|
|
625
654
|
.all(...params, limit);
|
|
@@ -633,15 +662,15 @@ export class SqliteSessionStoreReadOnlyView {
|
|
|
633
662
|
const limit = clampLimit(opts?.limit ?? DEFAULT_SEARCH_LIMIT, MAX_SEARCH_LIMIT);
|
|
634
663
|
const ftsQuery = sanitiseFtsQuery(trimmed);
|
|
635
664
|
const sql = `
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
665
|
+
SELECT s.id, s.created_at, s.updated_at, s.workspace_root, s.branch,
|
|
666
|
+
s.project_slug, s.title, s.turn_count, s.event_count, s.model,
|
|
667
|
+
s.tenant_id, s.status
|
|
668
|
+
FROM sessions_fts f
|
|
669
|
+
JOIN sessions s ON s.id = f.id
|
|
670
|
+
WHERE sessions_fts MATCH ?
|
|
671
|
+
ORDER BY s.updated_at DESC
|
|
672
|
+
LIMIT ?
|
|
673
|
+
`;
|
|
645
674
|
let rows;
|
|
646
675
|
try {
|
|
647
676
|
rows = this.db.prepare(sql).all(ftsQuery, limit);
|
|
@@ -656,11 +685,11 @@ export class SqliteSessionStoreReadOnlyView {
|
|
|
656
685
|
}
|
|
657
686
|
async get(sessionId) {
|
|
658
687
|
const stmt = this.db.prepare(`
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
688
|
+
SELECT id, created_at, updated_at, workspace_root, branch, project_slug,
|
|
689
|
+
title, turn_count, event_count, model, tenant_id, status
|
|
690
|
+
FROM sessions
|
|
691
|
+
WHERE id = ?
|
|
692
|
+
`);
|
|
664
693
|
const row = stmt.get(sessionId);
|
|
665
694
|
return row ? rawToSession(row) : null;
|
|
666
695
|
}
|
|
@@ -722,9 +751,9 @@ function sanitiseSlugForFs(raw) {
|
|
|
722
751
|
* Pull a human-readable text body out of an event payload. We support
|
|
723
752
|
* three shapes the producer commonly emits:
|
|
724
753
|
*
|
|
725
|
-
*
|
|
726
|
-
*
|
|
727
|
-
*
|
|
754
|
+
* - `{ text: string }` — explicit text body.
|
|
755
|
+
* - `{ brief: string }` — REPL `dispatch` brief.
|
|
756
|
+
* - `string` — payload is the body itself.
|
|
728
757
|
*
|
|
729
758
|
* Anything else returns the empty string so a typo at the call site
|
|
730
759
|
* silently degrades to "no FTS body" rather than crashing the writer.
|
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Persistent REPL session store — Sprint
|
|
2
|
+
* Persistent REPL session store — Sprint .
|
|
3
3
|
*
|
|
4
4
|
* Public types consumed by the REPL session module + the `pugi sessions`
|
|
5
5
|
* / `pugi resume` dispatchers. Wire format is stable:
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
7
|
+
* - `SessionRow` mirrors the SQLite `sessions` table one-to-one and is
|
|
8
|
+
* also the JSON shape returned by `pugi sessions --json`. Field names
|
|
9
|
+
* are camelCase (PascalCase types, camelCase fields per CLAUDE.md
|
|
10
|
+
* conventions). The SQL columns themselves stay snake_case because
|
|
11
|
+
* they originate from raw SQL.
|
|
12
12
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
13
|
+
* - `SessionEvent` is the on-disk shape of one line in
|
|
14
|
+
* `events.<n>.jsonl`. `kind` is a closed union; `payload` is an
|
|
15
|
+
* opaque JSON value so the producer can attach whatever fields the
|
|
16
|
+
* event type requires without forcing the store to change.
|
|
17
17
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
18
|
+
* - `SessionListOptions` / `SessionLoadEventsOptions` /
|
|
19
|
+
* `SessionSearchOptions` are inputs the store accepts. Defaults are
|
|
20
|
+
* spec'd inline so the test plan can pin them without reading the
|
|
21
|
+
* implementation.
|
|
22
22
|
*
|
|
23
|
-
* The blob store + `pugi undo` + named checkpoints are
|
|
23
|
+
* The blob store + `pugi undo` + named checkpoints are follow-ups
|
|
24
24
|
* — out of scope for THIS PR per spec. The types here intentionally do
|
|
25
25
|
* NOT model blob refs so a future blob-store landing can extend without
|
|
26
26
|
* a wire break.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* UUID v7 generator — Sprint
|
|
2
|
+
* UUID v7 generator — Sprint .
|
|
3
3
|
*
|
|
4
4
|
* uuid v7 (RFC 9562 draft) is a time-sortable 128-bit identifier whose
|
|
5
5
|
* first 48 bits are the unix-epoch milliseconds, the next 4 bits are
|
|
@@ -9,17 +9,17 @@
|
|
|
9
9
|
*
|
|
10
10
|
* Why v7 and not v4 (random) or v6 (gregorian time):
|
|
11
11
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
12
|
+
* - The session id IS the primary key of the SQLite table. We want
|
|
13
|
+
* inserts to land at the end of the b-tree to keep page fanout
|
|
14
|
+
* small. v4 fragments the tree (uniformly random keys); v7 sorts
|
|
15
|
+
* in time order so inserts are append-only.
|
|
16
|
+
* - `pugi sessions` sorts by `updated_at DESC` for display, but the
|
|
17
|
+
* pagination cursor uses the session id directly — a v7 id IS the
|
|
18
|
+
* creation timestamp, so the cursor is a single column compare
|
|
19
|
+
* instead of (updated_at, id) tuple.
|
|
20
|
+
* - Operators see ids in `/resume` picker; v7 prefix is monotonically
|
|
21
|
+
* increasing so the most recent session lands at the bottom of an
|
|
22
|
+
* id sort, matching the human expectation of "newest last".
|
|
23
23
|
*
|
|
24
24
|
* Node 22 does not ship a v7 generator (`crypto.randomUUID()` is v4
|
|
25
25
|
* only). We implement it inline with `crypto.randomBytes(10)` for the
|