@adhdev/daemon-core 0.9.82-rc.45 → 0.9.82-rc.451
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/boot/daemon-lifecycle.d.ts +9 -0
- package/dist/boot/process-hardening.d.ts +50 -0
- package/dist/build-info.d.ts +37 -0
- package/dist/chat/source-machine.d.ts +166 -0
- package/dist/chat/source-resolver.d.ts +104 -0
- package/dist/chat/subscription-updates.d.ts +1 -0
- package/dist/cli-adapter-types.d.ts +68 -2
- package/dist/cli-adapters/cli-script-runner.d.ts +117 -0
- package/dist/cli-adapters/cli-state-engine.d.ts +200 -0
- package/dist/cli-adapters/provider-cli-adapter.d.ts +126 -64
- package/dist/cli-adapters/provider-cli-parse.d.ts +5 -0
- package/dist/cli-adapters/provider-cli-runtime.d.ts +34 -0
- package/dist/cli-adapters/provider-cli-shared.d.ts +74 -0
- package/dist/cli-adapters/pty-write-chunking.d.ts +34 -0
- package/dist/cli-adapters/raw-terminal-io.d.ts +37 -0
- package/dist/cli-adapters/resolve-executable.d.ts +69 -0
- package/dist/cli-adapters/terminal-backends/ghostty-vt-backend.d.ts +6 -3
- package/dist/cli-adapters/terminal-backends/types.d.ts +10 -2
- package/dist/cli-adapters/terminal-screen.d.ts +4 -11
- package/dist/commands/chat-commands-debug-bundle.d.ts +14 -0
- package/dist/commands/chat-commands-read.d.ts +7 -0
- package/dist/commands/chat-commands-scope.d.ts +39 -0
- package/dist/commands/chat-commands-shared.d.ts +33 -0
- package/dist/commands/chat-commands-write.d.ts +14 -0
- package/dist/commands/chat-commands.d.ts +10 -23
- package/dist/commands/cli-manager.d.ts +25 -1
- package/dist/commands/handler.d.ts +125 -0
- package/dist/commands/high-family/index.d.ts +3 -0
- package/dist/commands/high-family/mesh-coordinator-launch.d.ts +2 -0
- package/dist/commands/high-family/mesh-events.d.ts +2 -0
- package/dist/commands/high-family/mesh-status.d.ts +2 -0
- package/dist/commands/high-family/types.d.ts +64 -0
- package/dist/commands/low-family/coordinator-prompt.d.ts +9 -0
- package/dist/commands/low-family/daemon-lifecycle.d.ts +2 -0
- package/dist/commands/low-family/diagnostics.d.ts +2 -0
- package/dist/commands/low-family/index.d.ts +3 -0
- package/dist/commands/low-family/mesh-ledger.d.ts +10 -0
- package/dist/commands/low-family/mesh-node-logs.d.ts +2 -0
- package/dist/commands/low-family/notification.d.ts +2 -0
- package/dist/commands/low-family/refine-config.d.ts +2 -0
- package/dist/commands/low-family/session-host.d.ts +2 -0
- package/dist/commands/low-family/spec-providerdev.d.ts +11 -0
- package/dist/commands/low-family/status-meta.d.ts +2 -0
- package/dist/commands/low-family/types.d.ts +33 -0
- package/dist/commands/med-family/cli-agent.d.ts +2 -0
- package/dist/commands/med-family/fast-forward.d.ts +2 -0
- package/dist/commands/med-family/ide.d.ts +10 -0
- package/dist/commands/med-family/index.d.ts +3 -0
- package/dist/commands/med-family/mesh-crud.d.ts +29 -0
- package/dist/commands/med-family/mesh-host-pairing.d.ts +2 -0
- package/dist/commands/med-family/mesh-queue.d.ts +2 -0
- package/dist/commands/med-family/mesh-restart.d.ts +2 -0
- package/dist/commands/med-family/types.d.ts +152 -0
- package/dist/commands/mesh-coordinator.d.ts +85 -1
- package/dist/commands/router.d.ts +279 -2
- package/dist/commands/upgrade-helper.d.ts +41 -1
- package/dist/config/chat-history.d.ts +9 -0
- package/dist/config/config.d.ts +5 -0
- package/dist/config/mesh-config.d.ts +129 -1
- package/dist/config/mesh-json-config.d.ts +199 -0
- package/dist/config/repo-settings.d.ts +77 -0
- package/dist/daemon/dev-server.d.ts +0 -2
- package/dist/detection/ide-detector.d.ts +13 -0
- package/dist/detection/win32-ide-version.d.ts +37 -0
- package/dist/git/change-impact-config.d.ts +159 -0
- package/dist/git/git-commands.d.ts +12 -1
- package/dist/git/git-diff.d.ts +6 -0
- package/dist/git/git-executor.d.ts +11 -0
- package/dist/git/git-status.d.ts +57 -0
- package/dist/git/git-types.d.ts +2 -50
- package/dist/git/git-worktree.d.ts +71 -1
- package/dist/git/index.d.ts +3 -1
- package/dist/index.d.ts +66 -14
- package/dist/index.js +49753 -18414
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +49614 -18438
- package/dist/index.mjs.map +1 -1
- package/dist/installer.d.ts +1 -4
- package/dist/ipc/local-ipc-server.d.ts +91 -0
- package/dist/launch.d.ts +1 -1
- package/dist/logging/async-batch-writer.d.ts +10 -0
- package/dist/logging/log-redactor.d.ts +24 -0
- package/dist/logging/log-tail-reader.d.ts +81 -0
- package/dist/logging/logger.d.ts +1 -1
- package/dist/mesh/contracts.d.ts +164 -0
- package/dist/mesh/coordinator-prompt.d.ts +89 -0
- package/dist/mesh/coordinator-registry.d.ts +59 -0
- package/dist/mesh/mesh-active-work.d.ts +174 -0
- package/dist/mesh/mesh-clone-grace.d.ts +8 -0
- package/dist/mesh/mesh-coordinator-config.d.ts +21 -0
- package/dist/mesh/mesh-delivery-policy.d.ts +131 -0
- package/dist/mesh/mesh-event-classify.d.ts +5 -0
- package/dist/mesh/mesh-event-forwarding.d.ts +41 -0
- package/dist/mesh/mesh-event-trace.d.ts +21 -0
- package/dist/mesh/mesh-events-coordinator.d.ts +4 -0
- package/dist/mesh/mesh-events-pending.d.ts +69 -0
- package/dist/mesh/mesh-events-stale.d.ts +61 -0
- package/dist/mesh/mesh-events-utils.d.ts +75 -0
- package/dist/mesh/mesh-events.d.ts +6 -49
- package/dist/mesh/mesh-fast-forward.d.ts +81 -0
- package/dist/mesh/mesh-host-ownership.d.ts +29 -0
- package/dist/mesh/mesh-init.d.ts +128 -0
- package/dist/mesh/mesh-ledger-reconciliation.d.ts +23 -4
- package/dist/mesh/mesh-ledger.d.ts +115 -1
- package/dist/mesh/mesh-magi-status.d.ts +63 -0
- package/dist/mesh/mesh-missions.d.ts +189 -0
- package/dist/mesh/mesh-node-identity.d.ts +306 -0
- package/dist/mesh/mesh-queue-assignment.d.ts +113 -0
- package/dist/mesh/mesh-reconcile-loop.d.ts +79 -0
- package/dist/mesh/mesh-refine-batch.d.ts +68 -0
- package/dist/mesh/mesh-refine-gates.d.ts +428 -0
- package/dist/mesh/mesh-refine-status.d.ts +62 -0
- package/dist/mesh/mesh-review-inbox.d.ts +90 -0
- package/dist/mesh/mesh-routing.d.ts +70 -0
- package/dist/mesh/mesh-runtime-store.d.ts +450 -0
- package/dist/mesh/mesh-scheduling-runtime.d.ts +78 -0
- package/dist/mesh/mesh-task-inflight.d.ts +46 -0
- package/dist/mesh/mesh-task-stats.d.ts +49 -0
- package/dist/mesh/mesh-unresolved-forward-outbox.d.ts +30 -0
- package/dist/mesh/mesh-warmup-deadline.d.ts +68 -0
- package/dist/mesh/mesh-work-queue.d.ts +300 -5
- package/dist/mesh/preview-freshness.d.ts +18 -0
- package/dist/mesh/refine-config.d.ts +216 -0
- package/dist/mesh/worktree-bootstrap-config.d.ts +145 -0
- package/dist/providers/acp-provider-instance.d.ts +5 -0
- package/dist/providers/approval-utils.d.ts +20 -0
- package/dist/providers/chat-message-normalization.d.ts +32 -1
- package/dist/providers/cli-provider-instance.d.ts +254 -3
- package/dist/providers/contracts.d.ts +149 -6
- package/dist/providers/external-sources.d.ts +71 -0
- package/dist/providers/manual-attendance.d.ts +63 -0
- package/dist/providers/native-history/antigravity-cli-transcript.d.ts +126 -0
- package/dist/providers/native-history/claude-cli-transcript.d.ts +70 -0
- package/dist/providers/native-history/codex-cli-transcript.d.ts +73 -0
- package/dist/providers/native-history/constants.d.ts +12 -0
- package/dist/providers/native-history/dispatcher.d.ts +27 -0
- package/dist/providers/native-history/hermes-cli-transcript.d.ts +30 -0
- package/dist/providers/native-history/index.d.ts +13 -0
- package/dist/providers/provider-instance-manager.d.ts +29 -0
- package/dist/providers/provider-instance.d.ts +23 -1
- package/dist/providers/provider-loader.d.ts +26 -4
- package/dist/providers/provider-trust.d.ts +31 -0
- package/dist/providers/read-chat-contract.d.ts +29 -0
- package/dist/providers/sdk/v1/builders/acp/detect-status.d.ts +68 -0
- package/dist/providers/sdk/v1/builders/cli/detect-status.d.ts +86 -0
- package/dist/providers/sdk/v1/builders/cli/parse-approval-squash.d.ts +59 -0
- package/dist/providers/sdk/v1/builders/cli/parse-approval.d.ts +65 -0
- package/dist/providers/sdk/v1/builders/cli/parse-session.d.ts +91 -0
- package/dist/providers/sdk/v1/builders/cli/visible-region.d.ts +42 -0
- package/dist/providers/sdk/v1/fixture-tooling/format.d.ts +126 -0
- package/dist/providers/sdk/v1/fixture-tooling/index.d.ts +8 -0
- package/dist/providers/sdk/v1/fixture-tooling/replay.d.ts +38 -0
- package/dist/providers/sdk/v1/index.d.ts +30 -0
- package/dist/providers/sdk/v1/sandbox/README-design.d.ts +193 -0
- package/dist/providers/sdk/v1/sandbox/require-whitelist.d.ts +74 -0
- package/dist/providers/sdk/v1/sandbox/script-runner.d.ts +98 -0
- package/dist/providers/sdk/v1/types/cli/index.d.ts +277 -0
- package/dist/providers/sdk/v1/types/common/index.d.ts +203 -0
- package/dist/providers/sdk/v1/validators/index.d.ts +5 -0
- package/dist/providers/sdk/v1/validators/manifest.d.ts +40 -0
- package/dist/providers/sdk/v1/validators/taint.d.ts +52 -0
- package/dist/providers/spec/adapter.d.ts +98 -0
- package/dist/providers/spec/cli-adapter.d.ts +223 -0
- package/dist/providers/spec/evaluator.d.ts +45 -0
- package/dist/providers/spec/fsm-driver.d.ts +444 -0
- package/dist/providers/spec/fsm-evaluator.d.ts +93 -0
- package/dist/providers/spec/fsm-loader.d.ts +14 -0
- package/dist/providers/spec/fsm-types.d.ts +202 -0
- package/dist/providers/spec/native-history-executor.d.ts +41 -0
- package/dist/providers/spec/pre-launch-trust.d.ts +16 -0
- package/dist/providers/spec/route.d.ts +4 -0
- package/dist/providers/spec/types.d.ts +222 -0
- package/dist/providers/status-monitor.d.ts +7 -7
- package/dist/providers/transcript-v2.d.ts +176 -0
- package/dist/providers/types/interactive-prompt.d.ts +84 -0
- package/dist/providers/working-dir.d.ts +17 -0
- package/dist/repo-mesh-types.d.ts +519 -13
- package/dist/runtime-defaults.d.ts +2 -0
- package/dist/session-host/managed-host.d.ts +64 -0
- package/dist/session-host/runtime-surface.d.ts +10 -16
- package/dist/sessions/registry.d.ts +6 -0
- package/dist/shared-types-extra.d.ts +2 -4
- package/dist/shared-types.d.ts +86 -55
- package/dist/status/normalize.d.ts +1 -1
- package/dist/status/normalize.js +1 -0
- package/dist/status/normalize.js.map +1 -1
- package/dist/status/normalize.mjs +1 -0
- package/dist/status/normalize.mjs.map +1 -1
- package/dist/status/reporter.d.ts +2 -0
- package/dist/status/snapshot.d.ts +26 -0
- package/dist/system/hash.d.ts +8 -0
- package/dist/system/load-better-sqlite3.d.ts +21 -0
- package/dist/types.d.ts +5 -0
- package/package.json +7 -3
- package/src/agent-stream/poller.ts +2 -3
- package/src/agent-stream/provider-adapter.ts +1 -1
- package/src/boot/daemon-lifecycle.ts +63 -12
- package/src/boot/process-hardening.ts +89 -0
- package/src/build-info.ts +73 -0
- package/src/chat/source-machine.ts +534 -0
- package/src/chat/source-resolver.ts +0 -0
- package/src/chat/subscription-updates.ts +20 -1
- package/src/cli-adapter-types.d.ts +3 -1
- package/src/cli-adapter-types.ts +68 -2
- package/src/cli-adapters/cli-script-runner.ts +421 -0
- package/src/cli-adapters/cli-state-engine.ts +1246 -0
- package/src/cli-adapters/provider-cli-adapter.d.ts +1 -1
- package/src/cli-adapters/provider-cli-adapter.ts +914 -1144
- package/src/cli-adapters/provider-cli-parse.d.ts +2 -0
- package/src/cli-adapters/provider-cli-parse.ts +19 -0
- package/src/cli-adapters/provider-cli-runtime.ts +56 -7
- package/src/cli-adapters/provider-cli-shared.d.ts +4 -0
- package/src/cli-adapters/provider-cli-shared.ts +121 -11
- package/src/cli-adapters/pty-transport.ts +2 -1
- package/src/cli-adapters/pty-write-chunking.ts +106 -0
- package/src/cli-adapters/raw-terminal-io.ts +252 -0
- package/src/cli-adapters/resolve-executable.ts +204 -0
- package/src/cli-adapters/session-host-transport.ts +2 -1
- package/src/cli-adapters/terminal-backends/ghostty-vt-backend.ts +63 -29
- package/src/cli-adapters/terminal-backends/types.ts +10 -3
- package/src/cli-adapters/terminal-screen.ts +16 -81
- package/src/commands/WINDOWS-UPGRADE-LOCK-FAILURE.md +198 -0
- package/src/commands/chat-commands-debug-bundle.ts +398 -0
- package/src/commands/chat-commands-read.ts +2488 -0
- package/src/commands/chat-commands-scope.ts +54 -0
- package/src/commands/chat-commands-shared.ts +114 -0
- package/src/commands/chat-commands-write.ts +891 -0
- package/src/commands/chat-commands.ts +19 -1841
- package/src/commands/cli-manager.ts +596 -27
- package/src/commands/handler.ts +841 -2
- package/src/commands/high-family/index.ts +28 -0
- package/src/commands/high-family/mesh-coordinator-launch.ts +680 -0
- package/src/commands/high-family/mesh-events.ts +89 -0
- package/src/commands/high-family/mesh-status.ts +822 -0
- package/src/commands/high-family/types.ts +80 -0
- package/src/commands/low-family/coordinator-prompt.ts +72 -0
- package/src/commands/low-family/daemon-lifecycle.ts +107 -0
- package/src/commands/low-family/diagnostics.ts +57 -0
- package/src/commands/low-family/index.ts +37 -0
- package/src/commands/low-family/mesh-ledger.ts +62 -0
- package/src/commands/low-family/mesh-node-logs.ts +87 -0
- package/src/commands/low-family/notification.ts +116 -0
- package/src/commands/low-family/refine-config.ts +106 -0
- package/src/commands/low-family/session-host.ts +274 -0
- package/src/commands/low-family/spec-providerdev.ts +217 -0
- package/src/commands/low-family/status-meta.ts +112 -0
- package/src/commands/low-family/types.ts +39 -0
- package/src/commands/med-family/cli-agent.ts +238 -0
- package/src/commands/med-family/fast-forward.ts +230 -0
- package/src/commands/med-family/ide.ts +163 -0
- package/src/commands/med-family/index.ts +37 -0
- package/src/commands/med-family/mesh-crud.ts +1253 -0
- package/src/commands/med-family/mesh-host-pairing.ts +234 -0
- package/src/commands/med-family/mesh-queue.ts +167 -0
- package/src/commands/med-family/mesh-restart.ts +92 -0
- package/src/commands/med-family/types.ts +153 -0
- package/src/commands/mesh-coordinator.ts +334 -124
- package/src/commands/router.ts +2893 -3536
- package/src/commands/stream-commands.ts +8 -0
- package/src/commands/upgrade-helper.ts +310 -45
- package/src/config/chat-history.ts +493 -24
- package/src/config/config.ts +12 -0
- package/src/config/mesh-config.ts +619 -23
- package/src/config/mesh-json-config.ts +376 -0
- package/src/config/recent-activity.ts +8 -2
- package/src/config/repo-settings.ts +111 -0
- package/src/daemon/dev-auto-implement.ts +3 -2
- package/src/daemon/dev-cli-debug.ts +10 -1
- package/src/daemon/dev-server.ts +0 -541
- package/src/detection/cli-detector.ts +28 -9
- package/src/detection/ide-detector.ts +55 -16
- package/src/detection/win32-ide-version.ts +106 -0
- package/src/git/change-impact-config.ts +354 -0
- package/src/git/git-commands.ts +78 -15
- package/src/git/git-diff.ts +81 -11
- package/src/git/git-executor.ts +12 -0
- package/src/git/git-status.ts +767 -48
- package/src/git/git-types.ts +14 -62
- package/src/git/git-worktree.ts +261 -4
- package/src/git/index.ts +17 -0
- package/src/index.ts +198 -12
- package/src/installer.d.ts +1 -1
- package/src/installer.ts +8 -6
- package/src/ipc/local-ipc-server.ts +278 -0
- package/src/launch.d.ts +1 -1
- package/src/launch.ts +37 -28
- package/src/logging/async-batch-writer.ts +55 -0
- package/src/logging/command-log.ts +7 -5
- package/src/logging/log-redactor.ts +100 -0
- package/src/logging/log-tail-reader.ts +341 -0
- package/src/logging/logger.ts +14 -7
- package/src/mesh/contracts.ts +338 -0
- package/src/mesh/coordinator-prompt.ts +416 -34
- package/src/mesh/coordinator-registry.ts +121 -0
- package/src/mesh/mesh-active-work.ts +645 -0
- package/src/mesh/mesh-clone-grace.ts +68 -0
- package/src/mesh/mesh-coordinator-config.ts +97 -0
- package/src/mesh/mesh-delivery-policy.ts +315 -0
- package/src/mesh/mesh-event-classify.ts +51 -0
- package/src/mesh/mesh-event-forwarding.ts +1897 -0
- package/src/mesh/mesh-event-trace.ts +67 -0
- package/src/mesh/mesh-events-coordinator.ts +32 -0
- package/src/mesh/mesh-events-pending.ts +656 -0
- package/src/mesh/mesh-events-stale.ts +389 -0
- package/src/mesh/mesh-events-utils.ts +443 -0
- package/src/mesh/mesh-events.ts +33 -1035
- package/src/mesh/mesh-fast-forward.ts +843 -0
- package/src/mesh/mesh-host-ownership.ts +111 -0
- package/src/mesh/mesh-init.ts +350 -0
- package/src/mesh/mesh-ledger-reconciliation.ts +12 -7
- package/src/mesh/mesh-ledger.ts +841 -102
- package/src/mesh/mesh-magi-status.ts +223 -0
- package/src/mesh/mesh-missions.ts +449 -0
- package/src/mesh/mesh-node-identity.ts +1985 -0
- package/src/mesh/mesh-queue-assignment.ts +1924 -0
- package/src/mesh/mesh-reconcile-loop.ts +2018 -0
- package/src/mesh/mesh-refine-batch.ts +205 -0
- package/src/mesh/mesh-refine-gates.ts +1673 -0
- package/src/mesh/mesh-refine-status.ts +231 -0
- package/src/mesh/mesh-review-inbox.ts +307 -0
- package/src/mesh/mesh-routing.ts +291 -0
- package/src/mesh/mesh-runtime-store.ts +1853 -0
- package/src/mesh/mesh-scheduling-runtime.ts +198 -0
- package/src/mesh/mesh-task-inflight.ts +70 -0
- package/src/mesh/mesh-task-stats.ts +161 -0
- package/src/mesh/mesh-unresolved-forward-outbox.ts +185 -0
- package/src/mesh/mesh-warmup-deadline.ts +152 -0
- package/src/mesh/mesh-work-queue.ts +1254 -141
- package/src/mesh/preview-freshness.ts +118 -0
- package/src/mesh/refine-config.ts +423 -0
- package/src/mesh/worktree-bootstrap-config.ts +450 -0
- package/src/providers/acp-provider-instance.ts +43 -10
- package/src/providers/approval-utils.d.ts +5 -0
- package/src/providers/approval-utils.ts +57 -5
- package/src/providers/chat-message-normalization.ts +92 -4
- package/src/providers/cli-provider-instance.ts +2177 -107
- package/src/providers/contracts.d.ts +55 -0
- package/src/providers/contracts.ts +160 -6
- package/src/providers/extension-provider-instance.ts +12 -7
- package/src/providers/external-sources.ts +218 -0
- package/src/providers/ide-provider-instance.ts +35 -12
- package/src/providers/manual-attendance.ts +85 -0
- package/src/providers/native-history/antigravity-cli-transcript.ts +1043 -0
- package/src/providers/native-history/claude-cli-transcript.ts +396 -0
- package/src/providers/native-history/codex-cli-transcript.ts +479 -0
- package/src/providers/native-history/constants.ts +19 -0
- package/src/providers/native-history/dispatcher.ts +373 -0
- package/src/providers/native-history/hermes-cli-transcript.ts +260 -0
- package/src/providers/native-history/index.ts +30 -0
- package/src/providers/provider-instance-manager.ts +71 -0
- package/src/providers/provider-instance.ts +19 -1
- package/src/providers/provider-loader.ts +668 -50
- package/src/providers/provider-schema.ts +87 -14
- package/src/providers/provider-trust.ts +114 -0
- package/src/providers/read-chat-contract.ts +76 -16
- package/src/providers/sdk/README.md +49 -0
- package/src/providers/sdk/v1/builders/acp/detect-status.ts +144 -0
- package/src/providers/sdk/v1/builders/cli/detect-status.ts +313 -0
- package/src/providers/sdk/v1/builders/cli/parse-approval-squash.ts +158 -0
- package/src/providers/sdk/v1/builders/cli/parse-approval.ts +250 -0
- package/src/providers/sdk/v1/builders/cli/parse-session.ts +276 -0
- package/src/providers/sdk/v1/builders/cli/visible-region.ts +143 -0
- package/src/providers/sdk/v1/fixture-tooling/format.ts +130 -0
- package/src/providers/sdk/v1/fixture-tooling/index.ts +22 -0
- package/src/providers/sdk/v1/fixture-tooling/replay.ts +352 -0
- package/src/providers/sdk/v1/index.ts +152 -0
- package/src/providers/sdk/v1/sandbox/README-design.ts +195 -0
- package/src/providers/sdk/v1/sandbox/require-whitelist.ts +472 -0
- package/src/providers/sdk/v1/sandbox/script-runner.ts +150 -0
- package/src/providers/sdk/v1/schemas/cli/provider.schema.json +509 -0
- package/src/providers/sdk/v1/schemas/primitives/acp-session-protocol-v1.json +131 -0
- package/src/providers/sdk/v1/schemas/primitives/native-history-codex-rollout-v1.json +66 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-approval-squash-v1.json +91 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-assistant-block-v1.json +91 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-cue-ordering-v1.json +47 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-dispatch-order-v1.json +32 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-footer-chrome-v1.json +42 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-index-finder-v1.json +27 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-modal-v1.json +125 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-prompt-marker-v1.json +45 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-session-id-extraction-v1.json +46 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-settled-prompt-v1.json +71 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-spinner-v1.json +83 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-transcript-pty-v1.json +83 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-visible-region-v1.json +57 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-welcome-screen-v1.json +35 -0
- package/src/providers/sdk/v1/types/cli/index.ts +365 -0
- package/src/providers/sdk/v1/types/common/index.ts +229 -0
- package/src/providers/sdk/v1/validators/index.ts +19 -0
- package/src/providers/sdk/v1/validators/manifest.ts +110 -0
- package/src/providers/sdk/v1/validators/taint.ts +309 -0
- package/src/providers/spec/adapter.ts +246 -0
- package/src/providers/spec/cli-adapter.ts +1064 -0
- package/src/providers/spec/evaluator.ts +407 -0
- package/src/providers/spec/fsm-driver.ts +1481 -0
- package/src/providers/spec/fsm-evaluator.ts +290 -0
- package/src/providers/spec/fsm-loader.ts +128 -0
- package/src/providers/spec/fsm-types.ts +301 -0
- package/src/providers/spec/native-history-executor.ts +1174 -0
- package/src/providers/spec/pre-launch-trust.ts +104 -0
- package/src/providers/spec/route.ts +51 -0
- package/src/providers/spec/types.ts +262 -0
- package/src/providers/status-monitor.d.ts +7 -7
- package/src/providers/status-monitor.ts +37 -22
- package/src/providers/transcript-v2.ts +567 -0
- package/src/providers/types/interactive-prompt.ts +536 -0
- package/src/providers/version-archive.ts +64 -24
- package/src/providers/working-dir.ts +23 -0
- package/src/repo-mesh-types.ts +758 -14
- package/src/runtime-defaults.ts +39 -0
- package/src/session-host/managed-host.ts +218 -0
- package/src/session-host/runtime-surface.ts +20 -80
- package/src/sessions/registry.ts +6 -0
- package/src/shared-types-extra.ts +2 -4
- package/src/shared-types.d.ts +8 -0
- package/src/shared-types.ts +124 -55
- package/src/status/builders.ts +26 -6
- package/src/status/normalize.ts +2 -0
- package/src/status/reporter.ts +19 -1
- package/src/status/snapshot.ts +95 -26
- package/src/system/hash.ts +23 -0
- package/src/system/host-memory.ts +29 -12
- package/src/system/load-better-sqlite3.ts +68 -0
- package/src/types.ts +5 -0
- package/dist/cli-adapters/terminal-backends/xterm-backend.d.ts +0 -17
- package/dist/mesh/mesh-sync.d.ts +0 -53
- package/src/cli-adapters/terminal-backends/ghostty-vt-backend.d.ts +0 -16
- package/src/cli-adapters/terminal-backends/xterm-backend.ts +0 -97
- package/src/mesh/mesh-sync.ts +0 -111
|
@@ -0,0 +1,1043 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* antigravity-cli-transcript — Daemon-side built-in native history adapter for antigravity-cli.
|
|
3
|
+
*
|
|
4
|
+
* Antigravity CLI stores conversations in multiple complementary locations:
|
|
5
|
+
*
|
|
6
|
+
* 1. ~/.gemini/antigravity-cli/history.jsonl
|
|
7
|
+
* Lightweight index of user prompts, one row per turn:
|
|
8
|
+
* { conversationId: string, display: string, workspace: string, timestamp: number }
|
|
9
|
+
*
|
|
10
|
+
* 2. ~/.gemini/antigravity-cli/brain/<uuid>/.system_generated/logs/transcript*.jsonl
|
|
11
|
+
* Per-session full transcript, one row per agent step:
|
|
12
|
+
* { source: 'USER_EXPLICIT'|'MODEL', type: string, content: string, status: 'DONE'|..., created_at: number }
|
|
13
|
+
*
|
|
14
|
+
* 3. ~/.gemini/antigravity-cli/conversations/<uuid>.pb
|
|
15
|
+
* Legacy protobuf binary — schema not publicly documented. Adapter extracts
|
|
16
|
+
* printable UTF-8 text runs as best-effort content (no proto library needed).
|
|
17
|
+
*
|
|
18
|
+
* 4. ~/.gemini/antigravity-cli/conversations/<uuid>.db ← current format
|
|
19
|
+
* Per-session SQLite database. Recent antigravity migrated conversation
|
|
20
|
+
* storage from .pb (+ brain/*.jsonl) to a per-session SQLite db. The
|
|
21
|
+
* schema is a trajectory of `steps`, NOT a simple messages(role,content)
|
|
22
|
+
* table:
|
|
23
|
+
* steps(idx INTEGER PK, step_type INTEGER, status INTEGER,
|
|
24
|
+
* step_payload BLOB [protobuf], ...)
|
|
25
|
+
* Each `step_payload` is a protobuf message. Empirically (introspected
|
|
26
|
+
* from real stores):
|
|
27
|
+
* - step_type 14 → a USER turn. The prompt text is the largest
|
|
28
|
+
* contiguous UTF-8 run inside the payload (field 19 subtree).
|
|
29
|
+
* - step_type 15 → a MODEL/assistant turn. The assistant's final
|
|
30
|
+
* natural-language answer lives at payload field 20 → field 1
|
|
31
|
+
* (identical to field 8). Field 20 → field 3 is the internal
|
|
32
|
+
* reasoning summary and is intentionally NOT surfaced.
|
|
33
|
+
* - other step types are tool calls / ephemeral system context.
|
|
34
|
+
* We read the blobs with a tiny dependency-free protobuf field walker
|
|
35
|
+
* (no proto schema / codegen needed) and map the two message step types.
|
|
36
|
+
* Because the daemon does NOT read this db, native history previously
|
|
37
|
+
* returned 0 rows for these sessions and read_chat fell back to the
|
|
38
|
+
* pty parser (which only echoes the user's own input) — assistant
|
|
39
|
+
* answers appeared lost even though they were on disk.
|
|
40
|
+
*
|
|
41
|
+
* This adapter provides:
|
|
42
|
+
* - Full coverage from a per-session .db (current format) — preferred.
|
|
43
|
+
* - Full coverage when a brain transcript exists (legacy authoritative source).
|
|
44
|
+
* - Partial coverage (user prompts only) from history.jsonl as fallback.
|
|
45
|
+
* - Best-effort raw-string extraction from .pb files when no other source exists.
|
|
46
|
+
*
|
|
47
|
+
* watchPath (from provider.v1.json):
|
|
48
|
+
* ~/.gemini/antigravity-cli/history.jsonl
|
|
49
|
+
* ~/.gemini/antigravity-cli/brain/{uuid}/.system_generated/logs/transcript*.jsonl
|
|
50
|
+
* ~/.gemini/antigravity-cli/conversations/{uuid}.pb
|
|
51
|
+
* ~/.gemini/antigravity-cli/conversations/{uuid}.db
|
|
52
|
+
*
|
|
53
|
+
* OSS code (AGPL-3.0). Must not import from packages/ (proprietary).
|
|
54
|
+
*/
|
|
55
|
+
|
|
56
|
+
import * as fs from 'fs';
|
|
57
|
+
import * as path from 'path';
|
|
58
|
+
import * as os from 'os';
|
|
59
|
+
import { loadBetterSqlite3 } from '../../system/load-better-sqlite3.js';
|
|
60
|
+
import { LOG } from '../../logging/logger.js';
|
|
61
|
+
|
|
62
|
+
// ─── Types ─────────────────────────────────────────────────────────────────
|
|
63
|
+
|
|
64
|
+
export type NativeHistoryRole = 'user' | 'assistant' | 'system';
|
|
65
|
+
export type NativeHistoryKind = 'standard' | 'tool' | 'session_start';
|
|
66
|
+
|
|
67
|
+
export interface NativeHistoryMessage {
|
|
68
|
+
ts: string;
|
|
69
|
+
receivedAt: number;
|
|
70
|
+
role: NativeHistoryRole;
|
|
71
|
+
content: string;
|
|
72
|
+
kind: NativeHistoryKind;
|
|
73
|
+
senderName?: string;
|
|
74
|
+
agent: 'antigravity-cli';
|
|
75
|
+
historySessionId: string;
|
|
76
|
+
workspace?: string;
|
|
77
|
+
/** Stable per-message identity (v2 contract). */
|
|
78
|
+
providerUnitKey?: string;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface NativeHistorySession {
|
|
82
|
+
messages: NativeHistoryMessage[];
|
|
83
|
+
providerSessionId: string;
|
|
84
|
+
source: 'provider-native';
|
|
85
|
+
sourcePath: string;
|
|
86
|
+
sourceMtimeMs: number;
|
|
87
|
+
/**
|
|
88
|
+
* 'full' — brain transcript was available and parsed.
|
|
89
|
+
* 'partial' — only history.jsonl user-prompts are available.
|
|
90
|
+
* 'best-effort' — .pb file bytes were used for raw text extraction.
|
|
91
|
+
*/
|
|
92
|
+
nativeHistoryCoverage: 'full' | 'partial' | 'best-effort';
|
|
93
|
+
workspace?: string;
|
|
94
|
+
partialReason?: string;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface NativeHistorySessionMeta {
|
|
98
|
+
historySessionId: string;
|
|
99
|
+
sessionId: string;
|
|
100
|
+
sourcePath: string;
|
|
101
|
+
sourceMtimeMs: number;
|
|
102
|
+
messageCount: number;
|
|
103
|
+
firstMessageAt: number;
|
|
104
|
+
lastMessageAt: number;
|
|
105
|
+
sessionTitle?: string;
|
|
106
|
+
preview?: string;
|
|
107
|
+
workspace?: string;
|
|
108
|
+
agent: 'antigravity-cli';
|
|
109
|
+
source: 'provider-native';
|
|
110
|
+
nativeHistoryCoverage: 'full' | 'partial' | 'best-effort';
|
|
111
|
+
partialReason?: string;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ─── Internal helpers ───────────────────────────────────────────────────────
|
|
115
|
+
|
|
116
|
+
function extractTimestampValue(value: unknown): number {
|
|
117
|
+
if (typeof value === 'number' && Number.isFinite(value) && value > 0) return value;
|
|
118
|
+
if (typeof value === 'string') {
|
|
119
|
+
const numeric = Number(value);
|
|
120
|
+
if (Number.isFinite(numeric) && numeric > 0) return numeric;
|
|
121
|
+
const parsed = Date.parse(value);
|
|
122
|
+
if (Number.isFinite(parsed) && parsed > 0) return parsed;
|
|
123
|
+
}
|
|
124
|
+
return 0;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function statMtimeMs(filePath: string): number {
|
|
128
|
+
try { return fs.statSync(filePath).mtimeMs; } catch { return 0; }
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function isUuidLike(value: string): boolean {
|
|
132
|
+
return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function antigravityRoot(): string {
|
|
136
|
+
return path.join(os.homedir(), '.gemini', 'antigravity-cli');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function historyJsonlPath(): string {
|
|
140
|
+
return path.join(antigravityRoot(), 'history.jsonl');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function brainRoot(): string {
|
|
144
|
+
return path.join(antigravityRoot(), 'brain');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function conversationsRoot(): string {
|
|
148
|
+
return path.join(antigravityRoot(), 'conversations');
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function resolvePathInside(root: string, ...segments: string[]): string | null {
|
|
152
|
+
const rootPath = path.resolve(root);
|
|
153
|
+
const targetPath = path.resolve(rootPath, ...segments);
|
|
154
|
+
if (targetPath !== rootPath && !targetPath.startsWith(rootPath + path.sep)) return null;
|
|
155
|
+
return targetPath;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Find the brain transcript file for a given session UUID.
|
|
160
|
+
* Prefers transcript_full.jsonl over transcript.jsonl when both exist.
|
|
161
|
+
*/
|
|
162
|
+
function findBrainTranscriptPath(sessionId: string): string | null {
|
|
163
|
+
if (!isUuidLike(sessionId)) return null;
|
|
164
|
+
const logsRoot = resolvePathInside(brainRoot(), sessionId, '.system_generated', 'logs');
|
|
165
|
+
if (!logsRoot || !fs.existsSync(logsRoot)) return null;
|
|
166
|
+
|
|
167
|
+
const candidates = (['transcript_full.jsonl', 'transcript.jsonl'] as const)
|
|
168
|
+
.map((file) => resolvePathInside(logsRoot, file))
|
|
169
|
+
.filter((p): p is string => p !== null && fs.existsSync(p));
|
|
170
|
+
|
|
171
|
+
if (candidates.length === 0) {
|
|
172
|
+
// Look for any transcript*.jsonl
|
|
173
|
+
let entries: fs.Dirent[] = [];
|
|
174
|
+
try { entries = fs.readdirSync(logsRoot, { withFileTypes: true }); } catch { return null; }
|
|
175
|
+
const transcriptFiles = entries
|
|
176
|
+
.filter((e) => e.isFile() && /^transcript.*\.jsonl$/.test(e.name))
|
|
177
|
+
.map((e) => path.join(logsRoot, e.name));
|
|
178
|
+
if (transcriptFiles.length === 0) return null;
|
|
179
|
+
transcriptFiles.sort((a, b) => statMtimeMs(b) - statMtimeMs(a));
|
|
180
|
+
return transcriptFiles[0];
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
candidates.sort((a, b) => statMtimeMs(b) - statMtimeMs(a));
|
|
184
|
+
return candidates[0];
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* List all session UUIDs that have a brain transcript directory.
|
|
189
|
+
*/
|
|
190
|
+
function listBrainSessions(): Array<{ sessionId: string; transcriptPath: string; sourceMtimeMs: number }> {
|
|
191
|
+
const root = brainRoot();
|
|
192
|
+
let entries: fs.Dirent[] = [];
|
|
193
|
+
try { entries = fs.readdirSync(root, { withFileTypes: true }); } catch { return []; }
|
|
194
|
+
|
|
195
|
+
const results: Array<{ sessionId: string; transcriptPath: string; sourceMtimeMs: number }> = [];
|
|
196
|
+
for (const entry of entries) {
|
|
197
|
+
if (!entry.isDirectory() || !isUuidLike(entry.name)) continue;
|
|
198
|
+
const transcriptPath = findBrainTranscriptPath(entry.name);
|
|
199
|
+
if (!transcriptPath) continue;
|
|
200
|
+
results.push({
|
|
201
|
+
sessionId: entry.name,
|
|
202
|
+
transcriptPath,
|
|
203
|
+
sourceMtimeMs: statMtimeMs(transcriptPath),
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
return results;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Strip USER_REQUEST XML wrapper from antigravity user prompts.
|
|
211
|
+
* Antigravity wraps structured user input in <USER_REQUEST>...</USER_REQUEST>.
|
|
212
|
+
*/
|
|
213
|
+
function extractUserRequestContent(content: string): string {
|
|
214
|
+
const raw = content.trim();
|
|
215
|
+
const match = raw.match(/<USER_REQUEST>\s*([\s\S]*?)\s*<\/USER_REQUEST>/i);
|
|
216
|
+
if (match) return match[1].trim();
|
|
217
|
+
return raw
|
|
218
|
+
.replace(/<ADDITIONAL_METADATA>[\s\S]*?<\/ADDITIONAL_METADATA>/gi, '')
|
|
219
|
+
.replace(/<USER_SETTINGS_CHANGE>[\s\S]*?<\/USER_SETTINGS_CHANGE>/gi, '')
|
|
220
|
+
.replace(/<\/?USER_REQUEST>/gi, '')
|
|
221
|
+
.trim();
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Determine the NativeHistoryKind from an antigravity transcript row type.
|
|
226
|
+
*/
|
|
227
|
+
function antigravityRowKind(rowType: string): NativeHistoryKind {
|
|
228
|
+
if (rowType === 'PLANNER_RESPONSE') return 'standard';
|
|
229
|
+
if (rowType && rowType !== 'USER_INPUT') return 'tool';
|
|
230
|
+
return 'standard';
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// ─── Brain transcript parser ─────────────────────────────────────────────────
|
|
234
|
+
|
|
235
|
+
interface TranscriptRow {
|
|
236
|
+
source?: string;
|
|
237
|
+
type?: string;
|
|
238
|
+
content?: string;
|
|
239
|
+
status?: string;
|
|
240
|
+
created_at?: unknown;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Parse a brain transcript JSONL file into NativeHistoryMessages.
|
|
245
|
+
* Only rows with status === 'DONE' are processed.
|
|
246
|
+
* USER_EXPLICIT / USER_INPUT rows → user role.
|
|
247
|
+
* MODEL rows → assistant role.
|
|
248
|
+
*/
|
|
249
|
+
function parseBrainTranscript(
|
|
250
|
+
filePath: string,
|
|
251
|
+
sessionId: string,
|
|
252
|
+
workspace?: string,
|
|
253
|
+
): NativeHistoryMessage[] | null {
|
|
254
|
+
let raw: string;
|
|
255
|
+
try { raw = fs.readFileSync(filePath, 'utf-8'); } catch { return null; }
|
|
256
|
+
|
|
257
|
+
const lines = raw.split('\n').filter(Boolean);
|
|
258
|
+
if (lines.length === 0) return null;
|
|
259
|
+
|
|
260
|
+
const records: NativeHistoryMessage[] = [];
|
|
261
|
+
const normalizedWorkspace = typeof workspace === 'string' ? workspace.trim() : '';
|
|
262
|
+
let fallbackTs = statMtimeMs(filePath) || Date.now();
|
|
263
|
+
|
|
264
|
+
for (const line of lines) {
|
|
265
|
+
let row: TranscriptRow | null = null;
|
|
266
|
+
try { row = JSON.parse(line) as TranscriptRow; } catch { continue; }
|
|
267
|
+
if (!row || typeof row !== 'object') continue;
|
|
268
|
+
if (row.status !== 'DONE') continue;
|
|
269
|
+
|
|
270
|
+
const rowSource = String(row.source ?? '').trim();
|
|
271
|
+
const rowType = String(row.type ?? '').trim();
|
|
272
|
+
const rawContent = typeof row.content === 'string' ? row.content : '';
|
|
273
|
+
const receivedAt = extractTimestampValue(row.created_at) || fallbackTs + records.length;
|
|
274
|
+
|
|
275
|
+
if (rowSource === 'USER_EXPLICIT' && rowType === 'USER_INPUT') {
|
|
276
|
+
const content = extractUserRequestContent(rawContent);
|
|
277
|
+
if (!content) continue;
|
|
278
|
+
const msg: NativeHistoryMessage = {
|
|
279
|
+
ts: new Date(receivedAt).toISOString(),
|
|
280
|
+
receivedAt,
|
|
281
|
+
role: 'user',
|
|
282
|
+
content,
|
|
283
|
+
kind: 'standard',
|
|
284
|
+
agent: 'antigravity-cli',
|
|
285
|
+
historySessionId: sessionId,
|
|
286
|
+
};
|
|
287
|
+
if (normalizedWorkspace) msg.workspace = normalizedWorkspace;
|
|
288
|
+
records.push(msg);
|
|
289
|
+
} else if (rowSource === 'MODEL') {
|
|
290
|
+
const content = rawContent.trim();
|
|
291
|
+
if (!content) continue;
|
|
292
|
+
const kind = antigravityRowKind(rowType);
|
|
293
|
+
const msg: NativeHistoryMessage = {
|
|
294
|
+
ts: new Date(receivedAt).toISOString(),
|
|
295
|
+
receivedAt,
|
|
296
|
+
role: 'assistant',
|
|
297
|
+
content,
|
|
298
|
+
kind,
|
|
299
|
+
agent: 'antigravity-cli',
|
|
300
|
+
historySessionId: sessionId,
|
|
301
|
+
};
|
|
302
|
+
if (kind === 'tool') msg.senderName = 'Tool';
|
|
303
|
+
if (normalizedWorkspace) msg.workspace = normalizedWorkspace;
|
|
304
|
+
records.push(msg);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return records.length > 0 ? records : null;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// ─── History JSONL parser ────────────────────────────────────────────────────
|
|
312
|
+
|
|
313
|
+
interface HistoryRow {
|
|
314
|
+
conversationId: string;
|
|
315
|
+
display: string;
|
|
316
|
+
workspace: string;
|
|
317
|
+
receivedAt: number;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function readHistoryRows(): HistoryRow[] {
|
|
321
|
+
const sourcePath = historyJsonlPath();
|
|
322
|
+
let lines: string[] = [];
|
|
323
|
+
try { lines = fs.readFileSync(sourcePath, 'utf-8').split('\n').filter(Boolean); } catch { return []; }
|
|
324
|
+
|
|
325
|
+
const rows: HistoryRow[] = [];
|
|
326
|
+
for (const line of lines) {
|
|
327
|
+
let parsed: unknown = null;
|
|
328
|
+
try { parsed = JSON.parse(line); } catch { continue; }
|
|
329
|
+
if (!parsed || typeof parsed !== 'object') continue;
|
|
330
|
+
|
|
331
|
+
const row = parsed as Record<string, unknown>;
|
|
332
|
+
const conversationId = String(row.conversationId ?? '').trim();
|
|
333
|
+
const display = typeof row.display === 'string' ? row.display.trim() : '';
|
|
334
|
+
const workspace = typeof row.workspace === 'string' ? row.workspace.trim() : '';
|
|
335
|
+
const receivedAt = extractTimestampValue(row.timestamp);
|
|
336
|
+
|
|
337
|
+
if (!conversationId || !isUuidLike(conversationId) || !display || !receivedAt) continue;
|
|
338
|
+
rows.push({ conversationId, display, workspace, receivedAt });
|
|
339
|
+
}
|
|
340
|
+
return rows;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// ─── Protobuf best-effort text extraction ────────────────────────────────────
|
|
344
|
+
|
|
345
|
+
const MIN_PRINTABLE_RUN = 8;
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Extract printable UTF-8 text runs from raw binary data (similar to the `strings` command).
|
|
349
|
+
* Runs of printable ASCII/UTF-8 characters of length >= MIN_PRINTABLE_RUN are collected.
|
|
350
|
+
* This is used as a best-effort fallback when the .pb schema is not available.
|
|
351
|
+
*/
|
|
352
|
+
function extractStringsFromBuffer(buf: Buffer): string[] {
|
|
353
|
+
const strings: string[] = [];
|
|
354
|
+
let current: number[] = [];
|
|
355
|
+
|
|
356
|
+
for (let i = 0; i < buf.length; i++) {
|
|
357
|
+
const byte = buf[i];
|
|
358
|
+
// Accept printable ASCII (32-126) and common whitespace (9=tab, 10=LF, 13=CR)
|
|
359
|
+
if ((byte >= 32 && byte <= 126) || byte === 9 || byte === 10 || byte === 13) {
|
|
360
|
+
current.push(byte);
|
|
361
|
+
} else {
|
|
362
|
+
if (current.length >= MIN_PRINTABLE_RUN) {
|
|
363
|
+
const str = Buffer.from(current).toString('utf-8').trim();
|
|
364
|
+
if (str) strings.push(str);
|
|
365
|
+
}
|
|
366
|
+
current = [];
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
// Flush remaining
|
|
370
|
+
if (current.length >= MIN_PRINTABLE_RUN) {
|
|
371
|
+
const str = Buffer.from(current).toString('utf-8').trim();
|
|
372
|
+
if (str) strings.push(str);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
return strings;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Read a .pb conversation file and extract best-effort text content.
|
|
380
|
+
* Returns a single NativeHistoryMessage with the extracted content,
|
|
381
|
+
* or null if nothing readable was found.
|
|
382
|
+
*/
|
|
383
|
+
function parsePbFile(
|
|
384
|
+
filePath: string,
|
|
385
|
+
sessionId: string,
|
|
386
|
+
): NativeHistoryMessage[] | null {
|
|
387
|
+
let buf: Buffer;
|
|
388
|
+
try { buf = fs.readFileSync(filePath); } catch { return null; }
|
|
389
|
+
if (buf.length === 0) return null;
|
|
390
|
+
|
|
391
|
+
const strings = extractStringsFromBuffer(buf);
|
|
392
|
+
// Filter out very short or likely-binary strings
|
|
393
|
+
const meaningful = strings.filter((s) => s.length >= MIN_PRINTABLE_RUN && /\w/.test(s));
|
|
394
|
+
if (meaningful.length === 0) return null;
|
|
395
|
+
|
|
396
|
+
const content = meaningful.join('\n');
|
|
397
|
+
const sourceMtimeMs = statMtimeMs(filePath);
|
|
398
|
+
|
|
399
|
+
return [
|
|
400
|
+
{
|
|
401
|
+
ts: new Date(sourceMtimeMs).toISOString(),
|
|
402
|
+
receivedAt: sourceMtimeMs,
|
|
403
|
+
role: 'assistant',
|
|
404
|
+
content,
|
|
405
|
+
kind: 'standard',
|
|
406
|
+
agent: 'antigravity-cli',
|
|
407
|
+
historySessionId: sessionId,
|
|
408
|
+
},
|
|
409
|
+
];
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// ─── SQLite (.db) conversation reader ────────────────────────────────────────
|
|
413
|
+
//
|
|
414
|
+
// Recent antigravity stores each conversation in a per-session SQLite db at
|
|
415
|
+
// conversations/<uuid>.db. See the file header for the schema. We decode the
|
|
416
|
+
// protobuf `step_payload` blobs with a minimal, dependency-free field walker —
|
|
417
|
+
// we only need two leaf strings (user prompt / assistant answer), so a full
|
|
418
|
+
// proto schema is unnecessary.
|
|
419
|
+
|
|
420
|
+
/** Antigravity step_type values that map to a chat message. */
|
|
421
|
+
const AGY_STEP_TYPE_USER = 14;
|
|
422
|
+
const AGY_STEP_TYPE_MODEL = 15;
|
|
423
|
+
|
|
424
|
+
interface ProtoField {
|
|
425
|
+
field: number;
|
|
426
|
+
wireType: number;
|
|
427
|
+
/** For wireType 2 (length-delimited): the raw bytes. */
|
|
428
|
+
bytes?: Buffer;
|
|
429
|
+
/** For wireType 0 (varint): the value. */
|
|
430
|
+
varint?: number;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Read a base-128 varint starting at `offset`. Returns [value, nextOffset].
|
|
435
|
+
* Values are read as JS numbers (safe: the fields we consume are small).
|
|
436
|
+
*/
|
|
437
|
+
function readVarint(buf: Buffer, offset: number): [number, number] {
|
|
438
|
+
let result = 0;
|
|
439
|
+
let shift = 0;
|
|
440
|
+
let i = offset;
|
|
441
|
+
while (i < buf.length) {
|
|
442
|
+
const byte = buf[i];
|
|
443
|
+
i += 1;
|
|
444
|
+
result += (byte & 0x7f) * Math.pow(2, shift);
|
|
445
|
+
if ((byte & 0x80) === 0) return [result, i];
|
|
446
|
+
shift += 7;
|
|
447
|
+
if (shift > 63) break; // malformed / oversized
|
|
448
|
+
}
|
|
449
|
+
return [result, i];
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* Decode the top-level fields of a protobuf message. Best-effort: stops on the
|
|
454
|
+
* first malformed byte rather than throwing, so a partially-corrupt blob still
|
|
455
|
+
* yields the fields decoded so far.
|
|
456
|
+
*/
|
|
457
|
+
function decodeProtoFields(buf: Buffer): ProtoField[] {
|
|
458
|
+
const fields: ProtoField[] = [];
|
|
459
|
+
let i = 0;
|
|
460
|
+
while (i < buf.length) {
|
|
461
|
+
const [key, afterKey] = readVarint(buf, i);
|
|
462
|
+
if (afterKey === i) break;
|
|
463
|
+
i = afterKey;
|
|
464
|
+
const field = Math.floor(key / 8);
|
|
465
|
+
const wireType = key & 7;
|
|
466
|
+
if (field <= 0) break;
|
|
467
|
+
if (wireType === 0) {
|
|
468
|
+
const [value, next] = readVarint(buf, i);
|
|
469
|
+
if (next === i) break;
|
|
470
|
+
i = next;
|
|
471
|
+
fields.push({ field, wireType, varint: value });
|
|
472
|
+
} else if (wireType === 2) {
|
|
473
|
+
const [len, afterLen] = readVarint(buf, i);
|
|
474
|
+
i = afterLen;
|
|
475
|
+
if (len < 0 || i + len > buf.length) break;
|
|
476
|
+
fields.push({ field, wireType, bytes: buf.subarray(i, i + len) });
|
|
477
|
+
i += len;
|
|
478
|
+
} else if (wireType === 5) {
|
|
479
|
+
i += 4;
|
|
480
|
+
} else if (wireType === 1) {
|
|
481
|
+
i += 8;
|
|
482
|
+
} else {
|
|
483
|
+
break; // wireType 3/4 (groups) — unused by antigravity payloads
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
return fields;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/** Return the bytes of the first length-delimited field with number `field`. */
|
|
490
|
+
function firstLenField(buf: Buffer, field: number): Buffer | null {
|
|
491
|
+
for (const f of decodeProtoFields(buf)) {
|
|
492
|
+
if (f.field === field && f.wireType === 2 && f.bytes) return f.bytes;
|
|
493
|
+
}
|
|
494
|
+
return null;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/** Heuristic: is this buffer (mostly) printable UTF-8 text? */
|
|
498
|
+
function looksLikeText(buf: Buffer): boolean {
|
|
499
|
+
if (buf.length === 0) return false;
|
|
500
|
+
let printable = 0;
|
|
501
|
+
for (let i = 0; i < buf.length; i++) {
|
|
502
|
+
const b = buf[i];
|
|
503
|
+
// ASCII printable + common whitespace, or any high byte (UTF-8 lead/cont).
|
|
504
|
+
if ((b >= 32 && b <= 126) || b === 9 || b === 10 || b === 13 || b >= 0x80) printable += 1;
|
|
505
|
+
}
|
|
506
|
+
return printable / buf.length >= 0.9;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Antigravity prefixes some assistant answers with a literal `MARKER_V1`
|
|
511
|
+
* sentinel followed by a blank line. Strip it so the user-visible bubble starts
|
|
512
|
+
* at the real text.
|
|
513
|
+
*/
|
|
514
|
+
function stripAnswerMarker(text: string): string {
|
|
515
|
+
return text.replace(/^\s*MARKER_V1\s*/, '');
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* Extract the assistant's final natural-language answer from a step_type 15
|
|
520
|
+
* payload: field 20 → field 1 (identical to field 8). Field 20 → field 3 is the
|
|
521
|
+
* private reasoning summary and is deliberately skipped. Returns '' if absent
|
|
522
|
+
* (e.g. a pure reasoning / tool-only model step, which carries no user-visible
|
|
523
|
+
* answer text).
|
|
524
|
+
*/
|
|
525
|
+
function extractModelAnswer(payload: Buffer): string {
|
|
526
|
+
const inner = firstLenField(payload, 20);
|
|
527
|
+
if (!inner) return '';
|
|
528
|
+
const answer = firstLenField(inner, 1) ?? firstLenField(inner, 8);
|
|
529
|
+
if (!answer || !looksLikeText(answer)) return '';
|
|
530
|
+
return stripAnswerMarker(answer.toString('utf-8')).trim();
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
/**
|
|
534
|
+
* Extract the user prompt from a step_type 14 payload: field 19 → field 2 (the
|
|
535
|
+
* clean prompt text; field 19 → field 3 wraps the same string with a leading
|
|
536
|
+
* newline and is used only as a fallback). The USER_REQUEST XML wrapper, when
|
|
537
|
+
* present, is unwrapped to match the brain-transcript reader's output.
|
|
538
|
+
*/
|
|
539
|
+
function extractUserPrompt(payload: Buffer): string {
|
|
540
|
+
const inner = firstLenField(payload, 19);
|
|
541
|
+
if (!inner) return '';
|
|
542
|
+
const raw = firstLenField(inner, 2) ?? firstLenField(inner, 3);
|
|
543
|
+
if (!raw || !looksLikeText(raw)) return '';
|
|
544
|
+
const text = raw.toString('utf-8').trim();
|
|
545
|
+
if (!text) return '';
|
|
546
|
+
return extractUserRequestContent(text);
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
interface AgyDbStepRow {
|
|
550
|
+
idx: number;
|
|
551
|
+
step_type: number;
|
|
552
|
+
step_payload: Buffer | null;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* Parse a per-session conversations/<uuid>.db (SQLite) into NativeHistoryMessages.
|
|
557
|
+
* Returns null when the db is unreadable, empty, or yields no chat messages.
|
|
558
|
+
*/
|
|
559
|
+
/**
|
|
560
|
+
* True when an error thrown by better-sqlite3 open/read is a transient
|
|
561
|
+
* SQLITE_BUSY / "database is locked" condition rather than a permanent one.
|
|
562
|
+
*
|
|
563
|
+
* On win32 antigravity holds a mandatory WAL write/checkpoint lock while it
|
|
564
|
+
* persists a step; a readonly open racing that lock throws SQLITE_BUSY. That
|
|
565
|
+
* is transient — the answer IS already on disk — so it must be retried, NOT
|
|
566
|
+
* collapsed to "no session" (which erases the just-written assistant answer on
|
|
567
|
+
* a chat_history re-query). macOS advisory locking + WAL reader-doesn't-block-
|
|
568
|
+
* writer masks this, which is why it is win32-specific.
|
|
569
|
+
*/
|
|
570
|
+
function isSqliteBusyError(err: unknown): boolean {
|
|
571
|
+
if (!err) return false;
|
|
572
|
+
const code = (err as any).code;
|
|
573
|
+
if (typeof code === 'string' && code.includes('SQLITE_BUSY')) return true;
|
|
574
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
575
|
+
return /SQLITE_BUSY|database is locked|database table is locked/i.test(msg);
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
const AGY_DB_BUSY_TIMEOUT_MS = 3000;
|
|
579
|
+
const AGY_DB_MAX_ATTEMPTS = 4;
|
|
580
|
+
const AGY_DB_RETRY_BACKOFF_MS = [50, 100, 150];
|
|
581
|
+
|
|
582
|
+
function sleepBusy(ms: number): void {
|
|
583
|
+
// Synchronous busy-wait: parseConversationDb is a sync function called from a
|
|
584
|
+
// sync read path, and better-sqlite3 itself is synchronous. The waits are
|
|
585
|
+
// tiny (≤150ms) and only occur under genuine lock contention, so a short
|
|
586
|
+
// spin-sleep is acceptable and keeps the call site synchronous.
|
|
587
|
+
const end = Date.now() + ms;
|
|
588
|
+
while (Date.now() < end) { /* spin */ }
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
function parseConversationDb(
|
|
592
|
+
filePath: string,
|
|
593
|
+
sessionId: string,
|
|
594
|
+
workspace?: string,
|
|
595
|
+
): NativeHistoryMessage[] | null {
|
|
596
|
+
let Database: any;
|
|
597
|
+
try {
|
|
598
|
+
Database = loadBetterSqlite3();
|
|
599
|
+
} catch (err) {
|
|
600
|
+
// better-sqlite3 binding genuinely unavailable (ABI mismatch / not built
|
|
601
|
+
// into this bundle). This is the only true "cannot read at all" case — a
|
|
602
|
+
// real load failure, distinct from transient lock contention below. Warn
|
|
603
|
+
// once at WARN so it is greppable; the reader degrades gracefully (returns
|
|
604
|
+
// null → dispatcher falls back to brain/.pb).
|
|
605
|
+
LOG.warn(
|
|
606
|
+
'NativeHistory',
|
|
607
|
+
`antigravity .db reader could not load better-sqlite3 for ${path.basename(filePath)}: ${err instanceof Error ? err.message : String(err)} (native binding unavailable — assistant answers in this .db will not surface)`,
|
|
608
|
+
);
|
|
609
|
+
return null;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
let rows: AgyDbStepRow[] | null = null;
|
|
613
|
+
let lastBusyErr: unknown;
|
|
614
|
+
|
|
615
|
+
for (let attempt = 1; attempt <= AGY_DB_MAX_ATTEMPTS; attempt++) {
|
|
616
|
+
let db: any;
|
|
617
|
+
try {
|
|
618
|
+
db = new Database(filePath, { readonly: true, fileMustExist: true });
|
|
619
|
+
// Ask SQLite itself to wait (rather than failing fast) if the WAL
|
|
620
|
+
// lock is momentarily held by antigravity. Set as early as possible
|
|
621
|
+
// after open so the prepare/all below inherits the wait.
|
|
622
|
+
try { db.pragma(`busy_timeout = ${AGY_DB_BUSY_TIMEOUT_MS}`); } catch { /* ignore */ }
|
|
623
|
+
rows = db
|
|
624
|
+
.prepare(
|
|
625
|
+
`SELECT idx, step_type, step_payload
|
|
626
|
+
FROM steps
|
|
627
|
+
WHERE step_type IN (${AGY_STEP_TYPE_USER}, ${AGY_STEP_TYPE_MODEL})
|
|
628
|
+
ORDER BY idx ASC`,
|
|
629
|
+
)
|
|
630
|
+
.all() as AgyDbStepRow[];
|
|
631
|
+
break; // success
|
|
632
|
+
} catch (err) {
|
|
633
|
+
if (isSqliteBusyError(err)) {
|
|
634
|
+
// Transient WAL lock contention. Do NOT collapse to null on the first
|
|
635
|
+
// failure — the assistant answer is already persisted; treating a busy
|
|
636
|
+
// lock as "no session" is exactly what erased answers on re-query.
|
|
637
|
+
// Retry with a small backoff; only give up after attempts exhausted.
|
|
638
|
+
lastBusyErr = err;
|
|
639
|
+
if (attempt < AGY_DB_MAX_ATTEMPTS) {
|
|
640
|
+
sleepBusy(AGY_DB_RETRY_BACKOFF_MS[attempt - 1] ?? 150);
|
|
641
|
+
continue;
|
|
642
|
+
}
|
|
643
|
+
LOG.warn(
|
|
644
|
+
'NativeHistory',
|
|
645
|
+
`antigravity .db ${path.basename(filePath)} stayed locked (SQLITE_BUSY) after ${AGY_DB_MAX_ATTEMPTS} attempts: ${err instanceof Error ? err.message : String(err)} (WAL write/checkpoint lock contention — assistant answers may transiently not surface this read)`,
|
|
646
|
+
);
|
|
647
|
+
return null;
|
|
648
|
+
}
|
|
649
|
+
// `steps` table absent / unexpected schema, or a genuine open/parse
|
|
650
|
+
// failure that is not lock contention — a real (but recoverable) shape
|
|
651
|
+
// mismatch. Log at debug so a schema drift in a future antigravity
|
|
652
|
+
// release is diagnosable without spamming logs for every legacy db.
|
|
653
|
+
LOG.debug(
|
|
654
|
+
'NativeHistory',
|
|
655
|
+
`antigravity .db ${path.basename(filePath)} not readable: ${err instanceof Error ? err.message : String(err)}`,
|
|
656
|
+
);
|
|
657
|
+
return null;
|
|
658
|
+
} finally {
|
|
659
|
+
try { db?.close(); } catch { /* ignore */ }
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
void lastBusyErr; // referenced only for retry bookkeeping above
|
|
664
|
+
|
|
665
|
+
if (!Array.isArray(rows) || rows.length === 0) return null;
|
|
666
|
+
|
|
667
|
+
const normalizedWorkspace = typeof workspace === 'string' ? workspace.trim() : '';
|
|
668
|
+
const baseTs = statMtimeMs(filePath) || Date.now();
|
|
669
|
+
const messages: NativeHistoryMessage[] = [];
|
|
670
|
+
|
|
671
|
+
for (const row of rows) {
|
|
672
|
+
const payload = row.step_payload;
|
|
673
|
+
if (!payload || !Buffer.isBuffer(payload) || payload.length === 0) continue;
|
|
674
|
+
|
|
675
|
+
// Steps are ordered by idx; the db carries no per-step timestamp we can
|
|
676
|
+
// trust as ms, so synthesize a monotonically increasing receivedAt that
|
|
677
|
+
// preserves order (idx-derived) around the file mtime.
|
|
678
|
+
const receivedAt = baseTs + messages.length;
|
|
679
|
+
|
|
680
|
+
if (row.step_type === AGY_STEP_TYPE_USER) {
|
|
681
|
+
const content = extractUserPrompt(payload);
|
|
682
|
+
if (!content) continue;
|
|
683
|
+
const msg: NativeHistoryMessage = {
|
|
684
|
+
ts: new Date(receivedAt).toISOString(),
|
|
685
|
+
receivedAt,
|
|
686
|
+
role: 'user',
|
|
687
|
+
content,
|
|
688
|
+
kind: 'standard',
|
|
689
|
+
agent: 'antigravity-cli',
|
|
690
|
+
historySessionId: sessionId,
|
|
691
|
+
};
|
|
692
|
+
if (normalizedWorkspace) msg.workspace = normalizedWorkspace;
|
|
693
|
+
messages.push(msg);
|
|
694
|
+
} else if (row.step_type === AGY_STEP_TYPE_MODEL) {
|
|
695
|
+
const content = extractModelAnswer(payload);
|
|
696
|
+
if (!content) continue; // reasoning-only / tool-only model step — no answer text
|
|
697
|
+
const msg: NativeHistoryMessage = {
|
|
698
|
+
ts: new Date(receivedAt).toISOString(),
|
|
699
|
+
receivedAt,
|
|
700
|
+
role: 'assistant',
|
|
701
|
+
content,
|
|
702
|
+
kind: 'standard',
|
|
703
|
+
agent: 'antigravity-cli',
|
|
704
|
+
historySessionId: sessionId,
|
|
705
|
+
};
|
|
706
|
+
if (normalizedWorkspace) msg.workspace = normalizedWorkspace;
|
|
707
|
+
messages.push(msg);
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
return messages.length > 0 ? messages : null;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
// ─── Public API ─────────────────────────────────────────────────────────────
|
|
715
|
+
|
|
716
|
+
/**
|
|
717
|
+
* Read a single Antigravity CLI session by its source file path.
|
|
718
|
+
*
|
|
719
|
+
* `sessionPath` is the absolute path to one of:
|
|
720
|
+
* - A brain transcript JSONL: ~/.gemini/antigravity-cli/brain/<uuid>/.system_generated/logs/transcript*.jsonl
|
|
721
|
+
* - The shared history.jsonl: ~/.gemini/antigravity-cli/history.jsonl
|
|
722
|
+
* - A conversation SQLite db: ~/.gemini/antigravity-cli/conversations/<uuid>.db
|
|
723
|
+
* - A conversation protobuf: ~/.gemini/antigravity-cli/conversations/<uuid>.pb
|
|
724
|
+
*
|
|
725
|
+
* The session UUID is inferred from the directory name (brain), filename (pb), or
|
|
726
|
+
* must be provided explicitly via `sessionId` for history.jsonl paths.
|
|
727
|
+
*
|
|
728
|
+
* Returns `null` when the file is missing, empty, or yields no parseable messages.
|
|
729
|
+
*/
|
|
730
|
+
export function readSession(
|
|
731
|
+
sessionPath: string,
|
|
732
|
+
sessionId?: string,
|
|
733
|
+
workspace?: string,
|
|
734
|
+
): NativeHistorySession | null {
|
|
735
|
+
if (!sessionPath || !path.isAbsolute(sessionPath)) return null;
|
|
736
|
+
if (!fs.existsSync(sessionPath)) return null;
|
|
737
|
+
|
|
738
|
+
const sourceMtimeMs = statMtimeMs(sessionPath);
|
|
739
|
+
|
|
740
|
+
// ── Case 1: brain transcript JSONL ──────────────────────────────────────
|
|
741
|
+
const brainRootPath = brainRoot();
|
|
742
|
+
if (sessionPath.startsWith(brainRootPath + path.sep) && sessionPath.endsWith('.jsonl')) {
|
|
743
|
+
// Extract UUID from the path: brain/<uuid>/.system_generated/...
|
|
744
|
+
const relative = sessionPath.slice(brainRootPath.length + 1);
|
|
745
|
+
const uuidFromPath = relative.split(path.sep)[0];
|
|
746
|
+
const resolvedSessionId = sessionId || (isUuidLike(uuidFromPath) ? uuidFromPath : '');
|
|
747
|
+
if (!resolvedSessionId) return null;
|
|
748
|
+
|
|
749
|
+
const messages = parseBrainTranscript(sessionPath, resolvedSessionId, workspace);
|
|
750
|
+
if (!messages || messages.length === 0) return null;
|
|
751
|
+
|
|
752
|
+
return {
|
|
753
|
+
messages,
|
|
754
|
+
providerSessionId: resolvedSessionId,
|
|
755
|
+
source: 'provider-native',
|
|
756
|
+
sourcePath: sessionPath,
|
|
757
|
+
sourceMtimeMs,
|
|
758
|
+
nativeHistoryCoverage: 'full',
|
|
759
|
+
workspace,
|
|
760
|
+
};
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
// ── Case 2a: .db conversation file (current per-session SQLite format) ───
|
|
764
|
+
if (sessionPath.endsWith('.db')) {
|
|
765
|
+
const dbSessionId = sessionId || path.basename(sessionPath, '.db');
|
|
766
|
+
if (!isUuidLike(dbSessionId)) return null;
|
|
767
|
+
|
|
768
|
+
const messages = parseConversationDb(sessionPath, dbSessionId, workspace);
|
|
769
|
+
if (!messages || messages.length === 0) return null;
|
|
770
|
+
|
|
771
|
+
return {
|
|
772
|
+
messages,
|
|
773
|
+
providerSessionId: dbSessionId,
|
|
774
|
+
source: 'provider-native',
|
|
775
|
+
sourcePath: sessionPath,
|
|
776
|
+
sourceMtimeMs,
|
|
777
|
+
nativeHistoryCoverage: 'full',
|
|
778
|
+
workspace,
|
|
779
|
+
};
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
// ── Case 2b: .pb conversation file (legacy protobuf) ─────────────────────
|
|
783
|
+
if (sessionPath.endsWith('.pb')) {
|
|
784
|
+
const pbSessionId = sessionId || path.basename(sessionPath, '.pb');
|
|
785
|
+
if (!isUuidLike(pbSessionId)) return null;
|
|
786
|
+
|
|
787
|
+
const messages = parsePbFile(sessionPath, pbSessionId);
|
|
788
|
+
if (!messages || messages.length === 0) return null;
|
|
789
|
+
|
|
790
|
+
return {
|
|
791
|
+
messages,
|
|
792
|
+
providerSessionId: pbSessionId,
|
|
793
|
+
source: 'provider-native',
|
|
794
|
+
sourcePath: sessionPath,
|
|
795
|
+
sourceMtimeMs,
|
|
796
|
+
nativeHistoryCoverage: 'best-effort',
|
|
797
|
+
partialReason: 'antigravity_cli_pb_raw_text_extraction',
|
|
798
|
+
};
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
// ── Case 3: history.jsonl (user prompts index) ──────────────────────────
|
|
802
|
+
if (path.basename(sessionPath) === 'history.jsonl') {
|
|
803
|
+
const resolvedSessionId = sessionId || '';
|
|
804
|
+
if (!resolvedSessionId || !isUuidLike(resolvedSessionId)) return null;
|
|
805
|
+
|
|
806
|
+
const rows = readHistoryRows().filter((r) => r.conversationId === resolvedSessionId);
|
|
807
|
+
if (rows.length === 0) return null;
|
|
808
|
+
|
|
809
|
+
rows.sort((a, b) => a.receivedAt - b.receivedAt);
|
|
810
|
+
const firstWorkspace = workspace || rows.find((r) => r.workspace)?.workspace || '';
|
|
811
|
+
|
|
812
|
+
const messages: NativeHistoryMessage[] = [];
|
|
813
|
+
if (firstWorkspace) {
|
|
814
|
+
messages.push({
|
|
815
|
+
ts: new Date(rows[0].receivedAt).toISOString(),
|
|
816
|
+
receivedAt: rows[0].receivedAt,
|
|
817
|
+
role: 'system',
|
|
818
|
+
content: firstWorkspace,
|
|
819
|
+
kind: 'session_start',
|
|
820
|
+
agent: 'antigravity-cli',
|
|
821
|
+
historySessionId: resolvedSessionId,
|
|
822
|
+
workspace: firstWorkspace,
|
|
823
|
+
});
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
for (const row of rows) {
|
|
827
|
+
const msg: NativeHistoryMessage = {
|
|
828
|
+
ts: new Date(row.receivedAt).toISOString(),
|
|
829
|
+
receivedAt: row.receivedAt,
|
|
830
|
+
role: 'user',
|
|
831
|
+
content: row.display,
|
|
832
|
+
kind: 'standard',
|
|
833
|
+
agent: 'antigravity-cli',
|
|
834
|
+
historySessionId: resolvedSessionId,
|
|
835
|
+
};
|
|
836
|
+
if (row.workspace) msg.workspace = row.workspace;
|
|
837
|
+
messages.push(msg);
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
return {
|
|
841
|
+
messages,
|
|
842
|
+
providerSessionId: resolvedSessionId,
|
|
843
|
+
source: 'provider-native',
|
|
844
|
+
sourcePath: sessionPath,
|
|
845
|
+
sourceMtimeMs,
|
|
846
|
+
nativeHistoryCoverage: 'partial',
|
|
847
|
+
partialReason: 'antigravity_cli_history_jsonl_contains_user_prompts_only',
|
|
848
|
+
};
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
return null;
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
/**
|
|
855
|
+
* List all Antigravity CLI sessions found across all watchPath locations.
|
|
856
|
+
*
|
|
857
|
+
* Priority:
|
|
858
|
+
* 1. Brain transcript sessions (full coverage) — preferred source.
|
|
859
|
+
* 2. History JSONL entries without a brain transcript (partial coverage).
|
|
860
|
+
* 3. .pb conversation files without any other source (best-effort).
|
|
861
|
+
*
|
|
862
|
+
* Returns summary metadata sorted by most recently updated first.
|
|
863
|
+
*/
|
|
864
|
+
export async function listSessions(_watchPath: string): Promise<NativeHistorySessionMeta[]> {
|
|
865
|
+
const results: NativeHistorySessionMeta[] = [];
|
|
866
|
+
const seen = new Set<string>();
|
|
867
|
+
|
|
868
|
+
// ── Step 1: brain transcripts (full coverage) ──────────────────────────
|
|
869
|
+
const historyRows = readHistoryRows();
|
|
870
|
+
const workspaceBySession = new Map<string, string>();
|
|
871
|
+
for (const row of historyRows) {
|
|
872
|
+
if (row.workspace && !workspaceBySession.has(row.conversationId)) {
|
|
873
|
+
workspaceBySession.set(row.conversationId, row.workspace);
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
for (const { sessionId, transcriptPath, sourceMtimeMs } of listBrainSessions()) {
|
|
878
|
+
const workspace = workspaceBySession.get(sessionId);
|
|
879
|
+
const messages = parseBrainTranscript(transcriptPath, sessionId, workspace);
|
|
880
|
+
if (!messages || messages.length === 0) continue;
|
|
881
|
+
|
|
882
|
+
const visible = messages.filter((m) => m.kind !== 'session_start');
|
|
883
|
+
if (visible.length === 0) continue;
|
|
884
|
+
|
|
885
|
+
const firstMsg = visible[0];
|
|
886
|
+
const lastMsg = visible[visible.length - 1];
|
|
887
|
+
|
|
888
|
+
results.push({
|
|
889
|
+
historySessionId: sessionId,
|
|
890
|
+
sessionId,
|
|
891
|
+
sourcePath: transcriptPath,
|
|
892
|
+
sourceMtimeMs,
|
|
893
|
+
messageCount: visible.length,
|
|
894
|
+
firstMessageAt: firstMsg.receivedAt || sourceMtimeMs,
|
|
895
|
+
lastMessageAt: lastMsg.receivedAt || sourceMtimeMs,
|
|
896
|
+
sessionTitle: lastMsg.content,
|
|
897
|
+
preview: lastMsg.content,
|
|
898
|
+
workspace,
|
|
899
|
+
agent: 'antigravity-cli',
|
|
900
|
+
source: 'provider-native',
|
|
901
|
+
nativeHistoryCoverage: 'full',
|
|
902
|
+
});
|
|
903
|
+
seen.add(sessionId);
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
// ── Step 2: history.jsonl entries without brain transcripts ────────────
|
|
907
|
+
const grouped = new Map<
|
|
908
|
+
string,
|
|
909
|
+
{
|
|
910
|
+
firstMessageAt: number;
|
|
911
|
+
lastMessageAt: number;
|
|
912
|
+
messageCount: number;
|
|
913
|
+
sessionTitle?: string;
|
|
914
|
+
preview?: string;
|
|
915
|
+
workspace?: string;
|
|
916
|
+
}
|
|
917
|
+
>();
|
|
918
|
+
for (const row of historyRows) {
|
|
919
|
+
const existing = grouped.get(row.conversationId) ?? {
|
|
920
|
+
firstMessageAt: row.receivedAt,
|
|
921
|
+
lastMessageAt: row.receivedAt,
|
|
922
|
+
messageCount: 0,
|
|
923
|
+
};
|
|
924
|
+
existing.messageCount += 1;
|
|
925
|
+
existing.firstMessageAt = Math.min(existing.firstMessageAt, row.receivedAt);
|
|
926
|
+
if (row.receivedAt >= existing.lastMessageAt) {
|
|
927
|
+
existing.lastMessageAt = row.receivedAt;
|
|
928
|
+
existing.sessionTitle = row.display;
|
|
929
|
+
existing.preview = row.display;
|
|
930
|
+
if (row.workspace) existing.workspace = row.workspace;
|
|
931
|
+
}
|
|
932
|
+
grouped.set(row.conversationId, existing);
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
const historySrc = historyJsonlPath();
|
|
936
|
+
const historyMtime = statMtimeMs(historySrc);
|
|
937
|
+
|
|
938
|
+
for (const [sessionId, info] of grouped.entries()) {
|
|
939
|
+
if (seen.has(sessionId)) continue;
|
|
940
|
+
results.push({
|
|
941
|
+
historySessionId: sessionId,
|
|
942
|
+
sessionId,
|
|
943
|
+
sourcePath: historySrc,
|
|
944
|
+
sourceMtimeMs: historyMtime,
|
|
945
|
+
messageCount: info.messageCount,
|
|
946
|
+
firstMessageAt: info.firstMessageAt,
|
|
947
|
+
lastMessageAt: info.lastMessageAt,
|
|
948
|
+
sessionTitle: info.sessionTitle,
|
|
949
|
+
preview: info.preview,
|
|
950
|
+
workspace: info.workspace,
|
|
951
|
+
agent: 'antigravity-cli',
|
|
952
|
+
source: 'provider-native',
|
|
953
|
+
nativeHistoryCoverage: 'partial',
|
|
954
|
+
partialReason: 'antigravity_cli_history_jsonl_contains_user_prompts_only',
|
|
955
|
+
});
|
|
956
|
+
seen.add(sessionId);
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
// ── Step 3: conversations/<uuid>.db and .pb without any other source ────
|
|
960
|
+
//
|
|
961
|
+
// Current antigravity writes per-session SQLite dbs; older sessions kept a
|
|
962
|
+
// .pb protobuf. Both can coexist in conversations/. Discover both, but when
|
|
963
|
+
// the same uuid has a .db, prefer it (full coverage) over the .pb (best
|
|
964
|
+
// effort). brain/history sources already in `seen` still win over either.
|
|
965
|
+
const convRoot = conversationsRoot();
|
|
966
|
+
if (fs.existsSync(convRoot)) {
|
|
967
|
+
let entries: fs.Dirent[] = [];
|
|
968
|
+
try { entries = fs.readdirSync(convRoot, { withFileTypes: true }); } catch { /* ignore */ }
|
|
969
|
+
|
|
970
|
+
// Group by uuid so a .db supersedes a sibling .pb of the same session.
|
|
971
|
+
const byUuid = new Map<string, { db?: string; pb?: string }>();
|
|
972
|
+
for (const entry of entries) {
|
|
973
|
+
if (!entry.isFile()) continue;
|
|
974
|
+
const dbMatch = /^([0-9a-f-]+)\.db$/i.exec(entry.name);
|
|
975
|
+
const pbMatch = /^([0-9a-f-]+)\.pb$/i.exec(entry.name);
|
|
976
|
+
if (dbMatch && isUuidLike(dbMatch[1])) {
|
|
977
|
+
const g = byUuid.get(dbMatch[1]) ?? {};
|
|
978
|
+
g.db = path.join(convRoot, entry.name);
|
|
979
|
+
byUuid.set(dbMatch[1], g);
|
|
980
|
+
} else if (pbMatch && isUuidLike(pbMatch[1])) {
|
|
981
|
+
const g = byUuid.get(pbMatch[1]) ?? {};
|
|
982
|
+
g.pb = path.join(convRoot, entry.name);
|
|
983
|
+
byUuid.set(pbMatch[1], g);
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
for (const [uuid, files] of byUuid.entries()) {
|
|
988
|
+
if (seen.has(uuid)) continue;
|
|
989
|
+
|
|
990
|
+
if (files.db) {
|
|
991
|
+
// Parse the db so listSessions reports accurate counts/preview and the
|
|
992
|
+
// session surfaces with full coverage (assistant answers included).
|
|
993
|
+
const workspace = workspaceBySession.get(uuid);
|
|
994
|
+
const messages = parseConversationDb(files.db, uuid, workspace);
|
|
995
|
+
const dbMtime = statMtimeMs(files.db);
|
|
996
|
+
if (messages && messages.length > 0) {
|
|
997
|
+
const lastMsg = messages[messages.length - 1];
|
|
998
|
+
const firstMsg = messages[0];
|
|
999
|
+
results.push({
|
|
1000
|
+
historySessionId: uuid,
|
|
1001
|
+
sessionId: uuid,
|
|
1002
|
+
sourcePath: files.db,
|
|
1003
|
+
sourceMtimeMs: dbMtime,
|
|
1004
|
+
messageCount: messages.length,
|
|
1005
|
+
firstMessageAt: firstMsg.receivedAt || dbMtime,
|
|
1006
|
+
lastMessageAt: lastMsg.receivedAt || dbMtime,
|
|
1007
|
+
sessionTitle: lastMsg.content,
|
|
1008
|
+
preview: lastMsg.content,
|
|
1009
|
+
workspace,
|
|
1010
|
+
agent: 'antigravity-cli',
|
|
1011
|
+
source: 'provider-native',
|
|
1012
|
+
nativeHistoryCoverage: 'full',
|
|
1013
|
+
});
|
|
1014
|
+
seen.add(uuid);
|
|
1015
|
+
continue;
|
|
1016
|
+
}
|
|
1017
|
+
// db unreadable/empty (e.g. sqlite binding unavailable) → fall through
|
|
1018
|
+
// to the .pb best-effort entry below if one exists.
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
if (files.pb) {
|
|
1022
|
+
const pbMtime = statMtimeMs(files.pb);
|
|
1023
|
+
results.push({
|
|
1024
|
+
historySessionId: uuid,
|
|
1025
|
+
sessionId: uuid,
|
|
1026
|
+
sourcePath: files.pb,
|
|
1027
|
+
sourceMtimeMs: pbMtime,
|
|
1028
|
+
messageCount: 0,
|
|
1029
|
+
firstMessageAt: pbMtime,
|
|
1030
|
+
lastMessageAt: pbMtime,
|
|
1031
|
+
agent: 'antigravity-cli',
|
|
1032
|
+
source: 'provider-native',
|
|
1033
|
+
nativeHistoryCoverage: 'best-effort',
|
|
1034
|
+
partialReason: 'antigravity_cli_pb_raw_text_extraction',
|
|
1035
|
+
});
|
|
1036
|
+
seen.add(uuid);
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
results.sort((a, b) => (b.lastMessageAt || 0) - (a.lastMessageAt || 0));
|
|
1042
|
+
return results;
|
|
1043
|
+
}
|