@zergai/ztc 0.4.0-beta.1 → 0.5.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +398 -45
- package/RUST_THIRD_PARTY_LICENSES.txt +1080 -822
- package/THIRD_PARTY_NOTICES.md +273 -12
- package/bin/zmux-darwin-arm64 +0 -0
- package/bin/ztc-audio-darwin-arm64 +0 -0
- package/bin/ztc-capture-darwin-arm64 +0 -0
- package/bin/ztc-computer-darwin-arm64 +0 -0
- package/bin/ztc-llm-darwin-arm64 +0 -0
- package/bin/ztc-tts-darwin-arm64 +0 -0
- package/bin/ztc-tui-darwin-arm64 +0 -0
- package/bin/ztc-zlicky-overlay-darwin-arm64 +0 -0
- package/bin/zterm-darwin-arm64 +0 -0
- package/dist/App.js +1206 -209
- package/dist/agent/agent.d.ts +74 -8
- package/dist/agent/agent.js +1073 -184
- package/dist/agent/backends/account_rotation.d.ts +4 -0
- package/dist/agent/backends/account_rotation.js +8 -2
- package/dist/agent/backends/anthropic.d.ts +13 -0
- package/dist/agent/backends/anthropic.js +169 -28
- package/dist/agent/backends/claude_cli.js +18 -8
- package/dist/agent/backends/codex_app_server.d.ts +1 -0
- package/dist/agent/backends/codex_app_server.js +55 -3
- package/dist/agent/backends/codex_auth.d.ts +1 -0
- package/dist/agent/backends/codex_auth.js +67 -13
- package/dist/agent/backends/codex_buffered_recovery.d.ts +27 -0
- package/dist/agent/backends/codex_buffered_recovery.js +293 -0
- package/dist/agent/backends/gemini.js +3 -1
- package/dist/agent/backends/http_error.d.ts +26 -0
- package/dist/agent/backends/http_error.js +221 -0
- package/dist/agent/backends/index.d.ts +9 -2
- package/dist/agent/backends/index.js +22 -2
- package/dist/agent/backends/openai.d.ts +0 -6
- package/dist/agent/backends/openai.js +5 -2
- package/dist/agent/backends/openai_compatible.d.ts +5 -4
- package/dist/agent/backends/openai_compatible.js +26 -19
- package/dist/agent/backends/openai_responses.d.ts +8 -3
- package/dist/agent/backends/openai_responses.js +77 -33
- package/dist/agent/backends/provider_ids.d.ts +1 -0
- package/dist/agent/backends/provider_ids.js +4 -0
- package/dist/agent/backends/request_metrics.js +12 -2
- package/dist/agent/backends/types.d.ts +109 -6
- package/dist/agent/backends/types.js +5 -1
- package/dist/agent/backends/zrouter_stream.d.ts +6 -1
- package/dist/agent/backends/zrouter_stream.js +160 -42
- package/dist/agent/background_registry.d.ts +2 -0
- package/dist/agent/background_registry.js +1 -0
- package/dist/agent/claim_observation.d.ts +6 -0
- package/dist/agent/claim_observation.js +54 -0
- package/dist/agent/commands/accounts.js +46 -177
- package/dist/agent/commands/attention.d.ts +2 -0
- package/dist/agent/commands/attention.js +67 -0
- package/dist/agent/commands/auth.js +18 -2
- package/dist/agent/commands/board.d.ts +2 -0
- package/dist/agent/commands/board.js +169 -0
- package/dist/agent/commands/clipboard.js +50 -1
- package/dist/agent/commands/compact.js +21 -13
- package/dist/agent/commands/config.js +4 -1
- package/dist/agent/commands/context.js +6 -1
- package/dist/agent/commands/desktop.d.ts +2 -0
- package/dist/agent/commands/desktop.js +29 -0
- package/dist/agent/commands/effort.js +14 -6
- package/dist/agent/commands/environment.d.ts +3 -0
- package/dist/agent/commands/environment.js +109 -0
- package/dist/agent/commands/evidence.d.ts +2 -0
- package/dist/agent/commands/evidence.js +9 -0
- package/dist/agent/commands/fork.js +4 -3
- package/dist/agent/commands/goal.d.ts +33 -1
- package/dist/agent/commands/goal.js +174 -14
- package/dist/agent/commands/goal_exit.d.ts +36 -0
- package/dist/agent/commands/goal_exit.js +120 -0
- package/dist/agent/commands/help.js +3 -1
- package/dist/agent/commands/history.js +3 -1
- package/dist/agent/commands/inbox.d.ts +2 -0
- package/dist/agent/commands/inbox.js +148 -0
- package/dist/agent/commands/index.js +16 -1
- package/dist/agent/commands/input_mode.js +8 -6
- package/dist/agent/commands/job.d.ts +2 -0
- package/dist/agent/commands/job.js +93 -0
- package/dist/agent/commands/keybindings.js +4 -1
- package/dist/agent/commands/label.d.ts +2 -0
- package/dist/agent/commands/label.js +77 -0
- package/dist/agent/commands/layout.d.ts +2 -0
- package/dist/agent/commands/layout.js +20 -0
- package/dist/agent/commands/mirror.js +16 -1
- package/dist/agent/commands/model.js +12 -2
- package/dist/agent/commands/permissions.d.ts +3 -1
- package/dist/agent/commands/permissions.js +79 -89
- package/dist/agent/commands/plan.js +5 -15
- package/dist/agent/commands/profile.js +21 -2
- package/dist/agent/commands/provider_selection.js +20 -1
- package/dist/agent/commands/rename.js +1 -1
- package/dist/agent/commands/resources.d.ts +2 -0
- package/dist/agent/commands/resources.js +67 -0
- package/dist/agent/commands/rewind.d.ts +3 -0
- package/dist/agent/commands/rewind.js +16 -0
- package/dist/agent/commands/runbooks.d.ts +2 -0
- package/dist/agent/commands/runbooks.js +73 -0
- package/dist/agent/commands/runbooks_lazy.d.ts +2 -0
- package/dist/agent/commands/runbooks_lazy.js +8 -0
- package/dist/agent/commands/sessions.d.ts +1 -1
- package/dist/agent/commands/sessions.js +33 -5
- package/dist/agent/commands/settings.js +185 -39
- package/dist/agent/commands/setup.d.ts +2 -0
- package/dist/agent/commands/setup.js +78 -0
- package/dist/agent/commands/share.d.ts +2 -0
- package/dist/agent/commands/share.js +64 -0
- package/dist/agent/commands/status.js +14 -2
- package/dist/agent/commands/types.d.ts +99 -6
- package/dist/agent/commands/update.js +8 -0
- package/dist/agent/commands/usage.d.ts +2 -0
- package/dist/agent/commands/usage.js +8 -0
- package/dist/agent/factory.d.ts +11 -0
- package/dist/agent/factory.js +113 -88
- package/dist/agent/loop_policy.d.ts +6 -3
- package/dist/agent/loop_policy.js +12 -3
- package/dist/agent/measurement_guidance.d.ts +2 -0
- package/dist/agent/measurement_guidance.js +5 -0
- package/dist/agent/request_encoder.d.ts +8 -1
- package/dist/agent/request_encoder.js +95 -23
- package/dist/agent/runtime/benchmark_comparison.d.ts +30 -0
- package/dist/agent/runtime/benchmark_comparison.js +94 -0
- package/dist/agent/runtime/benchmark_contract.d.ts +48 -0
- package/dist/agent/runtime/benchmark_contract.js +90 -0
- package/dist/agent/runtime/checked_commands.d.ts +32 -0
- package/dist/agent/runtime/checked_commands.js +119 -0
- package/dist/agent/runtime/command_execution_policy.d.ts +26 -0
- package/dist/agent/runtime/command_execution_policy.js +91 -0
- package/dist/agent/runtime/command_jobs.d.ts +36 -18
- package/dist/agent/runtime/command_jobs.js +151 -13
- package/dist/agent/runtime/command_panes.d.ts +59 -0
- package/dist/agent/runtime/command_panes.js +398 -0
- package/dist/agent/runtime/command_wait.d.ts +19 -0
- package/dist/agent/runtime/command_wait.js +167 -0
- package/dist/agent/runtime/cpu_benchmark.d.ts +30 -0
- package/dist/agent/runtime/cpu_benchmark.js +126 -0
- package/dist/agent/runtime/delegated_evidence.d.ts +26 -0
- package/dist/agent/runtime/delegated_evidence.js +80 -0
- package/dist/agent/runtime/evidence.d.ts +127 -0
- package/dist/agent/runtime/evidence.js +468 -0
- package/dist/agent/runtime/execution_context.d.ts +7 -0
- package/dist/agent/runtime/execution_context.js +39 -3
- package/dist/agent/runtime/json_verifier_recipe.d.ts +8 -0
- package/dist/agent/runtime/json_verifier_recipe.js +66 -0
- package/dist/agent/runtime/measurements.d.ts +110 -0
- package/dist/agent/runtime/measurements.js +317 -0
- package/dist/agent/runtime/policy.d.ts +4 -0
- package/dist/agent/runtime/policy.js +31 -20
- package/dist/agent/runtime/provider_native_evidence.d.ts +20 -0
- package/dist/agent/runtime/provider_native_evidence.js +86 -0
- package/dist/agent/runtime/recovery.d.ts +2 -0
- package/dist/agent/runtime/recovery.js +45 -2
- package/dist/agent/runtime/remote_broker_transport.d.ts +10 -0
- package/dist/agent/runtime/remote_broker_transport.js +84 -0
- package/dist/agent/runtime/remote_deletion_receipts.d.ts +3 -0
- package/dist/agent/runtime/remote_deletion_receipts.js +43 -0
- package/dist/agent/runtime/remote_journal_replay.d.ts +13 -0
- package/dist/agent/runtime/remote_journal_replay.js +233 -0
- package/dist/agent/runtime/remote_mutation_journal.d.ts +25 -0
- package/dist/agent/runtime/remote_mutation_journal.js +208 -0
- package/dist/agent/runtime/remote_resource_endpoint.d.ts +7 -0
- package/dist/agent/runtime/remote_resource_endpoint.js +6 -0
- package/dist/agent/runtime/remote_resource_errors.d.ts +5 -0
- package/dist/agent/runtime/remote_resource_errors.js +9 -0
- package/dist/agent/runtime/remote_resource_script.d.ts +2 -0
- package/dist/agent/runtime/remote_resource_script.js +1009 -0
- package/dist/agent/runtime/remote_resources.d.ts +29 -0
- package/dist/agent/runtime/remote_resources.js +160 -0
- package/dist/agent/runtime/source_verification.d.ts +12 -0
- package/dist/agent/runtime/source_verification.js +210 -0
- package/dist/agent/runtime/source_verification_contract.d.ts +75 -0
- package/dist/agent/runtime/source_verification_contract.js +203 -0
- package/dist/agent/runtime/tool_checkpoints.d.ts +11 -0
- package/dist/agent/runtime/tool_checkpoints.js +26 -0
- package/dist/agent/runtime/tool_effects.d.ts +5 -0
- package/dist/agent/runtime/tool_progress.d.ts +12 -0
- package/dist/agent/runtime/tool_progress.js +76 -0
- package/dist/agent/runtime/tool_runtime.d.ts +16 -1
- package/dist/agent/runtime/tool_runtime.js +204 -10
- package/dist/agent/runtime/tracing.d.ts +1 -1
- package/dist/agent/runtime/verifier_capture.d.ts +9 -0
- package/dist/agent/runtime/verifier_capture.js +100 -0
- package/dist/agent/runtime/verifier_contract.d.ts +24 -0
- package/dist/agent/runtime/verifier_contract.js +41 -0
- package/dist/agent/subagent.d.ts +20 -10
- package/dist/agent/subagent.js +266 -29
- package/dist/agent/tool_response_directory.d.ts +24 -0
- package/dist/agent/tool_response_directory.js +61 -0
- package/dist/agent/tool_response_source.d.ts +8 -0
- package/dist/agent/tool_response_source.js +49 -0
- package/dist/agent/tool_response_store.d.ts +75 -4
- package/dist/agent/tool_response_store.js +583 -64
- package/dist/agent/tools/ask_user.d.ts +3 -1
- package/dist/agent/tools/ask_user.js +30 -11
- package/dist/agent/tools/background.js +1 -0
- package/dist/agent/tools/benchmark_cpu.d.ts +2 -0
- package/dist/agent/tools/benchmark_cpu.js +102 -0
- package/dist/agent/tools/compare_benchmarks.d.ts +2 -0
- package/dist/agent/tools/compare_benchmarks.js +53 -0
- package/dist/agent/tools/deferred.js +1 -0
- package/dist/agent/tools/delegate.js +18 -2
- package/dist/agent/tools/helper_requirements.d.ts +7 -0
- package/dist/agent/tools/helper_requirements.js +19 -0
- package/dist/agent/tools/index.d.ts +1 -0
- package/dist/agent/tools/index.js +3 -0
- package/dist/agent/tools/measurement_report.d.ts +22 -0
- package/dist/agent/tools/measurement_report.js +190 -0
- package/dist/agent/tools/pane.d.ts +0 -7
- package/dist/agent/tools/pane.js +5 -4
- package/dist/agent/tools/project_context.d.ts +3 -0
- package/dist/agent/tools/project_context.js +45 -0
- package/dist/agent/tools/recall_tool_response.d.ts +3 -5
- package/dist/agent/tools/recall_tool_response.js +19 -29
- package/dist/agent/tools/registry.d.ts +1 -0
- package/dist/agent/tools/registry.js +23 -0
- package/dist/agent/tools/related_sessions.d.ts +2 -0
- package/dist/agent/tools/related_sessions.js +156 -0
- package/dist/agent/tools/routine.js +13 -3
- package/dist/agent/tools/session_evidence.d.ts +3 -0
- package/dist/agent/tools/session_evidence.js +72 -0
- package/dist/agent/tools/shell.d.ts +4 -3
- package/dist/agent/tools/shell.js +174 -47
- package/dist/agent/tools/tool_search.js +3 -2
- package/dist/agent/tools/types.d.ts +28 -0
- package/dist/agent/tools/verify_artifact.d.ts +2 -0
- package/dist/agent/tools/verify_artifact.js +60 -0
- package/dist/agent/tools/workflow.js +1 -0
- package/dist/app/activity.d.ts +6 -1
- package/dist/app/activity.js +81 -12
- package/dist/app/agent_runner.d.ts +6 -1
- package/dist/app/agent_runner.js +34 -19
- package/dist/app/command_context.js +106 -10
- package/dist/app/controller.d.ts +15 -4
- package/dist/app/controller.js +29 -3
- package/dist/app/escape_stop.d.ts +11 -0
- package/dist/app/escape_stop.js +15 -0
- package/dist/app/liveness.d.ts +6 -4
- package/dist/app/liveness.js +13 -3
- package/dist/app/model_catalog_cache.d.ts +5 -0
- package/dist/app/model_catalog_cache.js +138 -0
- package/dist/app/ordered_transcript.js +43 -4
- package/dist/app/prompt_turn.d.ts +14 -0
- package/dist/app/prompt_turn.js +53 -0
- package/dist/app/recap.d.ts +3 -0
- package/dist/app/recap.js +9 -3
- package/dist/app/restart.d.ts +3 -2
- package/dist/app/restart.js +22 -7
- package/dist/app/rewind.d.ts +3 -3
- package/dist/app/rewind.js +3 -3
- package/dist/app/selection_completion.d.ts +17 -2
- package/dist/app/selection_completion.js +84 -9
- package/dist/app/session_manager.d.ts +38 -6
- package/dist/app/session_manager.js +263 -60
- package/dist/app/session_messages.d.ts +3 -0
- package/dist/app/session_messages.js +60 -0
- package/dist/app/task_scope_router.d.ts +20 -0
- package/dist/app/task_scope_router.js +173 -0
- package/dist/app/transcript_window.d.ts +2 -0
- package/dist/app/transcript_window.js +4 -0
- package/dist/app/workspace_admission_details.d.ts +3 -0
- package/dist/app/workspace_admission_details.js +26 -0
- package/dist/app/workspace_layout_controller.d.ts +11 -0
- package/dist/app/workspace_layout_controller.js +88 -0
- package/dist/app/workspace_monitor.d.ts +1 -0
- package/dist/app/workspace_monitor.js +216 -0
- package/dist/app/workspace_presets.d.ts +29 -0
- package/dist/app/workspace_presets.js +192 -0
- package/dist/attention/queue.d.ts +37 -0
- package/dist/attention/queue.js +183 -0
- package/dist/bench/rf_shared.d.ts +2 -0
- package/dist/board/binding.d.ts +42 -0
- package/dist/board/binding.js +38 -0
- package/dist/board/client.d.ts +42 -0
- package/dist/board/client.js +60 -0
- package/dist/cli.js +1 -1
- package/dist/cli_args.js +19 -17
- package/dist/cli_main.js +110 -13
- package/dist/cli_ratatui.d.ts +183 -16
- package/dist/cli_ratatui.js +3989 -1255
- package/dist/components/ActivityLine.d.ts +3 -2
- package/dist/components/ActivityLine.js +2 -2
- package/dist/components/InputArea.d.ts +19 -1
- package/dist/components/InputArea.js +264 -190
- package/dist/components/MessageList.d.ts +2 -0
- package/dist/components/MessageList.js +3 -1
- package/dist/components/SingleMessage.d.ts +2 -0
- package/dist/components/SingleMessage.js +4 -2
- package/dist/components/StatusBar.d.ts +4 -2
- package/dist/components/StatusBar.js +3 -1
- package/dist/conductor_branch.js +3 -0
- package/dist/config/api_retry.d.ts +6 -1
- package/dist/config/api_retry.js +19 -1
- package/dist/config/defaults.d.ts +2 -1
- package/dist/config/defaults.js +9 -2
- package/dist/config/message_display.d.ts +1 -0
- package/dist/config/message_display.js +17 -2
- package/dist/config/permissions.d.ts +21 -0
- package/dist/config/permissions.js +89 -0
- package/dist/config/resolved.d.ts +2 -2
- package/dist/config/types.d.ts +32 -5
- package/dist/config.d.ts +38 -4
- package/dist/config.js +290 -42
- package/dist/context/project_intelligence.d.ts +44 -0
- package/dist/context/project_intelligence.js +246 -0
- package/dist/desktop/cli_launch.d.ts +8 -0
- package/dist/desktop/cli_launch.js +6 -0
- package/dist/desktop/cli_shim.d.ts +8 -0
- package/dist/desktop/cli_shim.js +120 -0
- package/dist/desktop/runtime.d.ts +16 -0
- package/dist/desktop/runtime.js +161 -0
- package/dist/desktop/worker_launch.d.ts +7 -0
- package/dist/desktop/worker_launch.js +7 -0
- package/dist/diagnostics/cargo_mutants_selection.d.ts +27 -0
- package/dist/diagnostics/cargo_mutants_selection.js +48 -0
- package/dist/diagnostics/native_terminal_benchmark.d.ts +58 -0
- package/dist/diagnostics/native_terminal_benchmark.js +1247 -0
- package/dist/diagnostics/session_evidence_package.d.ts +40 -0
- package/dist/diagnostics/session_evidence_package.js +234 -0
- package/dist/diagnostics/session_export.d.ts +93 -0
- package/dist/diagnostics/session_export.js +634 -0
- package/dist/emulation/tool_adapters.js +48 -18
- package/dist/emulation/ztc_addendum.d.ts +1 -0
- package/dist/emulation/ztc_addendum.js +13 -12
- package/dist/environment/service_runner.d.ts +1 -0
- package/dist/environment/service_runner.js +68 -0
- package/dist/environment/store.d.ts +120 -0
- package/dist/environment/store.js +387 -0
- package/dist/events.d.ts +40 -1
- package/dist/events.js +10 -17
- package/dist/goal_contract.d.ts +25 -0
- package/dist/goal_contract.js +44 -0
- package/dist/headless.d.ts +31 -0
- package/dist/headless.js +578 -70
- package/dist/headless_cli.js +8 -0
- package/dist/headless_cli_args.d.ts +1 -0
- package/dist/headless_cli_args.js +1 -0
- package/dist/intake/cli.d.ts +1 -0
- package/dist/intake/cli.js +33 -0
- package/dist/intake/format.d.ts +2 -0
- package/dist/intake/format.js +14 -0
- package/dist/intake/notifications.d.ts +16 -0
- package/dist/intake/notifications.js +132 -0
- package/dist/intake/store.d.ts +56 -0
- package/dist/intake/store.js +238 -0
- package/dist/library.d.ts +8 -0
- package/dist/library.js +19 -10
- package/dist/mcp/client.d.ts +1 -0
- package/dist/mcp/client.js +20 -0
- package/dist/modes/builtin/plan.yaml +2 -2
- package/dist/modes/handlers.js +1 -1
- package/dist/onboarding/first_run.d.ts +3 -0
- package/dist/onboarding/first_run.js +33 -25
- package/dist/onboarding/setup.d.ts +32 -0
- package/dist/onboarding/setup.js +133 -0
- package/dist/onboarding/setup_agent_health.d.ts +17 -0
- package/dist/onboarding/setup_agent_health.js +79 -0
- package/dist/onboarding/setup_environment.d.ts +66 -0
- package/dist/onboarding/setup_environment.js +219 -0
- package/dist/onboarding/setup_workspace.d.ts +17 -0
- package/dist/onboarding/setup_workspace.js +129 -0
- package/dist/optimize/adapters/index.d.ts +4 -1
- package/dist/optimize/adapters/index.js +7 -0
- package/dist/optimize/adapters/kernelbench.js +14 -5
- package/dist/optimize/adapters/popcorn.js +3 -2
- package/dist/optimize/adapters/profile_benchmark.js +13 -7
- package/dist/optimize/adapters/rfbench.js +25 -14
- package/dist/optimize/adapters/types.d.ts +3 -0
- package/dist/optimize/adapters/zergpod.js +5 -2
- package/dist/optimize/baseline.js +10 -21
- package/dist/optimize/branch.js +9 -12
- package/dist/optimize/combine.js +2 -3
- package/dist/optimize/executor.js +34 -48
- package/dist/optimize/measurements.d.ts +55 -0
- package/dist/optimize/measurements.js +173 -0
- package/dist/optimize/results.d.ts +1 -0
- package/dist/optimize/results.js +11 -33
- package/dist/optimize/tge.js +2 -1
- package/dist/panes/initial_input.d.ts +19 -0
- package/dist/panes/initial_input.js +206 -0
- package/dist/panes/labels.d.ts +16 -0
- package/dist/panes/labels.js +42 -0
- package/dist/panes/model_catalog.d.ts +3 -3
- package/dist/panes/model_catalog.js +28 -4
- package/dist/panes/pane_registry.js +6 -2
- package/dist/panes/types.d.ts +3 -0
- package/dist/panes/ztc_launch.js +19 -0
- package/dist/plugin/optimization.d.ts +2 -0
- package/dist/plugin/optimization.js +1 -0
- package/dist/plugin/runtime.d.ts +2 -0
- package/dist/plugin/simulation.d.ts +6 -1
- package/dist/plugin/simulation_contract.d.ts +836 -1
- package/dist/plugin/simulation_documents.d.ts +22 -0
- package/dist/plugin/simulation_documents.js +1 -0
- package/dist/plugin/simulation_interventions.d.ts +103 -0
- package/dist/plugin/simulation_interventions.js +1 -0
- package/dist/plugin/simulation_living.d.ts +1 -0
- package/dist/plugin/simulation_living.js +3 -0
- package/dist/plugin/simulation_meetings.d.ts +121 -0
- package/dist/plugin/simulation_meetings.js +1 -0
- package/dist/plugin/simulation_story_review.d.ts +111 -0
- package/dist/plugin/simulation_story_review.js +1 -0
- package/dist/plugin/simulation_storyteller.d.ts +91 -0
- package/dist/plugin/simulation_storyteller.js +1 -0
- package/dist/plugin/simulation_structured.d.ts +2 -0
- package/dist/plugin/simulation_structured.js +2 -0
- package/dist/plugin/simulation_toolkit.d.ts +21 -0
- package/dist/plugin/simulation_toolkit.js +24 -0
- package/dist/prompts/tool_delegate_task.j2 +1 -1
- package/dist/remote-broker/Dockerfile +10 -0
- package/dist/remote-broker/archive.py +1227 -0
- package/dist/remote-broker/cutover.py +761 -0
- package/dist/remote-broker/requirements.txt +5 -0
- package/dist/remote-broker/resources.json +6 -0
- package/dist/remote-broker/resources.write-staging.example.json +18 -0
- package/dist/remote-broker/retained.py +686 -0
- package/dist/remote-broker/retained_cleanup.py +550 -0
- package/dist/remote-broker/server.py +648 -0
- package/dist/remote-broker/worker.py +1007 -0
- package/dist/router_auth.d.ts +31 -0
- package/dist/router_auth.js +155 -9
- package/dist/runbooks/catalog.d.ts +3 -0
- package/dist/runbooks/catalog.js +26 -0
- package/dist/runbooks/library.d.ts +11 -0
- package/dist/runbooks/library.js +95 -0
- package/dist/runbooks/package.d.ts +43 -0
- package/dist/runbooks/package.js +102 -0
- package/dist/session.d.ts +65 -13
- package/dist/session.js +548 -43
- package/dist/session_handoff.d.ts +25 -0
- package/dist/session_handoff.js +322 -0
- package/dist/session_handoff_contract.d.ts +89 -0
- package/dist/session_handoff_contract.js +179 -0
- package/dist/session_media.d.ts +41 -0
- package/dist/session_media.js +357 -0
- package/dist/session_ownership.d.ts +16 -0
- package/dist/session_ownership.js +221 -0
- package/dist/session_public.d.ts +10 -2
- package/dist/session_public.js +6 -1
- package/dist/simulation/dynamic_history.d.ts +42 -0
- package/dist/simulation/dynamic_history.js +173 -0
- package/dist/simulation/engines/swarm/pane_manager.js +7 -6
- package/dist/simulation/inference.d.ts +3 -1
- package/dist/simulation/inference.js +19 -1
- package/dist/simulation/intervention_generation.d.ts +27 -0
- package/dist/simulation/intervention_generation.js +246 -0
- package/dist/simulation/intervention_material.d.ts +4 -0
- package/dist/simulation/intervention_material.js +81 -0
- package/dist/simulation/living/context.d.ts +3130 -0
- package/dist/simulation/living/context.js +944 -0
- package/dist/simulation/living/definer.d.ts +12 -0
- package/dist/simulation/living/definer.js +223 -0
- package/dist/simulation/living/definition.d.ts +257 -0
- package/dist/simulation/living/definition.js +341 -0
- package/dist/simulation/living/frame.d.ts +259 -0
- package/dist/simulation/living/frame.js +223 -0
- package/dist/simulation/living/identifiers.d.ts +2 -0
- package/dist/simulation/living/identifiers.js +12 -0
- package/dist/simulation/living/index.d.ts +15 -0
- package/dist/simulation/living/index.js +15 -0
- package/dist/simulation/living/inference.d.ts +219 -0
- package/dist/simulation/living/inference.js +576 -0
- package/dist/simulation/living/mechanics.d.ts +54 -0
- package/dist/simulation/living/mechanics.js +588 -0
- package/dist/simulation/living/model-input.d.ts +25 -0
- package/dist/simulation/living/model-input.js +215 -0
- package/dist/simulation/living/open-world.d.ts +257 -0
- package/dist/simulation/living/open-world.js +102 -0
- package/dist/simulation/living/projection.d.ts +7 -0
- package/dist/simulation/living/projection.js +31 -0
- package/dist/simulation/living/recall.d.ts +17 -0
- package/dist/simulation/living/recall.js +25 -0
- package/dist/simulation/living/reduce.d.ts +15 -0
- package/dist/simulation/living/reduce.js +1853 -0
- package/dist/simulation/living/replay.d.ts +6 -0
- package/dist/simulation/living/replay.js +22 -0
- package/dist/simulation/living/runtime.d.ts +140 -0
- package/dist/simulation/living/runtime.js +3020 -0
- package/dist/simulation/living/shared-kinds.d.ts +24 -0
- package/dist/simulation/living/shared-kinds.js +96 -0
- package/dist/simulation/living/types.d.ts +2785 -0
- package/dist/simulation/living/types.js +669 -0
- package/dist/simulation/llm.d.ts +3 -1
- package/dist/simulation/llm.js +90 -30
- package/dist/simulation/memory/embedding.d.ts +1 -1
- package/dist/simulation/memory/embedding.js +9 -3
- package/dist/simulation/memory/filesystem.d.ts +2 -0
- package/dist/simulation/memory/filesystem.js +5 -1
- package/dist/simulation/memory/filesystem_file_tree.d.ts +4 -1
- package/dist/simulation/memory/filesystem_file_tree.js +41 -10
- package/dist/simulation/memory/filesystem_invariants.js +1 -2
- package/dist/simulation/memory/filesystem_types.d.ts +1 -0
- package/dist/simulation/memory/memory.d.ts +2 -0
- package/dist/simulation/memory/memory.js +5 -1
- package/dist/simulation/orchestration/intent.js +0 -2
- package/dist/simulation/orchestration/lifecycle.js +0 -2
- package/dist/simulation/plugin_host.d.ts +25 -1
- package/dist/simulation/plugin_host.js +334 -17
- package/dist/simulation/session_documents.d.ts +20 -0
- package/dist/simulation/session_documents.js +62 -0
- package/dist/simulation/session_input_validation.d.ts +43 -0
- package/dist/simulation/session_input_validation.js +364 -0
- package/dist/simulation/session_inputs.d.ts +32 -0
- package/dist/simulation/session_inputs.js +154 -0
- package/dist/simulation/telemetry.d.ts +11 -14
- package/dist/simulation/telemetry.js +177 -54
- package/dist/simulation/toolkit/bounded_turns.d.ts +13 -0
- package/dist/simulation/toolkit/bounded_turns.js +80 -0
- package/dist/simulation/toolkit/dynamic_agents.d.ts +25 -0
- package/dist/simulation/toolkit/dynamic_agents.js +218 -0
- package/dist/simulation/toolkit/dynamic_digest.d.ts +1 -0
- package/dist/simulation/toolkit/dynamic_digest.js +8 -0
- package/dist/simulation/toolkit/dynamic_documents.d.ts +7 -0
- package/dist/simulation/toolkit/dynamic_documents.js +71 -0
- package/dist/simulation/toolkit/dynamic_driver.d.ts +8 -0
- package/dist/simulation/toolkit/dynamic_driver.js +269 -0
- package/dist/simulation/toolkit/dynamic_game.d.ts +53 -0
- package/dist/simulation/toolkit/dynamic_game.js +805 -0
- package/dist/simulation/toolkit/dynamic_knowledge.d.ts +15 -0
- package/dist/simulation/toolkit/dynamic_knowledge.js +91 -0
- package/dist/simulation/toolkit/dynamic_mechanics.d.ts +22 -0
- package/dist/simulation/toolkit/dynamic_mechanics.js +910 -0
- package/dist/simulation/toolkit/dynamic_meetings.d.ts +239 -0
- package/dist/simulation/toolkit/dynamic_meetings.js +350 -0
- package/dist/simulation/toolkit/dynamic_progress.d.ts +131 -0
- package/dist/simulation/toolkit/dynamic_progress.js +214 -0
- package/dist/simulation/toolkit/dynamic_projection.d.ts +9 -0
- package/dist/simulation/toolkit/dynamic_projection.js +219 -0
- package/dist/simulation/toolkit/dynamic_runtime.d.ts +412 -0
- package/dist/simulation/toolkit/dynamic_runtime.js +6536 -0
- package/dist/simulation/toolkit/dynamic_state.d.ts +111 -0
- package/dist/simulation/toolkit/dynamic_state.js +1967 -0
- package/dist/simulation/toolkit/dynamic_story_review.d.ts +508 -0
- package/dist/simulation/toolkit/dynamic_story_review.js +333 -0
- package/dist/simulation/toolkit/dynamic_storyteller.d.ts +77 -0
- package/dist/simulation/toolkit/dynamic_storyteller.js +114 -0
- package/dist/simulation/toolkit/dynamic_storyteller_runtime.d.ts +26 -0
- package/dist/simulation/toolkit/dynamic_storyteller_runtime.js +126 -0
- package/dist/simulation/toolkit/dynamic_world.d.ts +22 -0
- package/dist/simulation/toolkit/dynamic_world.js +377 -0
- package/dist/simulation/toolkit/structured_output.d.ts +5 -0
- package/dist/simulation/toolkit/structured_output.js +321 -0
- package/dist/simulation/types/world.d.ts +2 -0
- package/dist/simulation/world/artifact_store.js +1 -1
- package/dist/simulation/world/memory_manager.d.ts +5 -0
- package/dist/simulation/world/memory_manager.js +17 -2
- package/dist/simulation/world/state.d.ts +3 -2
- package/dist/simulation/world/state.js +15 -5
- package/dist/skills/bundled/measurement-validation/SKILL.md +144 -0
- package/dist/terminal_attach.d.ts +1 -0
- package/dist/terminal_attach.js +113 -0
- package/dist/tool_progress.d.ts +3 -0
- package/dist/tool_progress.js +27 -0
- package/dist/types.d.ts +145 -10
- package/dist/types.js +3 -0
- package/dist/ui/core/input.d.ts +3 -0
- package/dist/ui/core/input_segments.d.ts +18 -1
- package/dist/ui/core/input_segments.js +131 -47
- package/dist/ui/core/input_state.d.ts +12 -0
- package/dist/ui/core/layout_yoga.d.ts +1 -1
- package/dist/ui/core/turn_metadata.d.ts +5 -0
- package/dist/ui/core/turn_metadata.js +22 -0
- package/dist/ui/ink/render.d.ts +2 -1
- package/dist/ui/ink/render.js +6 -5
- package/dist/ui/ratatui/headless_pane_manager.d.ts +14 -1
- package/dist/ui/ratatui/headless_pane_manager.js +293 -104
- package/dist/ui/ratatui/index.d.ts +1 -0
- package/dist/ui/ratatui/input.d.ts +4 -0
- package/dist/ui/ratatui/input.js +15 -0
- package/dist/ui/ratatui/ipc_path.d.ts +2 -0
- package/dist/ui/ratatui/ipc_path.js +29 -1
- package/dist/ui/ratatui/mux_persistence.d.ts +3 -1
- package/dist/ui/ratatui/mux_persistence.js +139 -1
- package/dist/ui/ratatui/pane_relay.d.ts +1 -1
- package/dist/ui/ratatui/pane_relay.js +55 -27
- package/dist/ui/ratatui/process.d.ts +17 -1
- package/dist/ui/ratatui/process.js +80 -7
- package/dist/ui/ratatui/protocol.d.ts +236 -1
- package/dist/ui/ratatui/protocol.js +90 -0
- package/dist/ui/scrollback.d.ts +2 -1
- package/dist/ui/scrollback.js +9 -4
- package/dist/ui/stream_typewriter.d.ts +8 -3
- package/dist/ui/stream_typewriter.js +30 -1
- package/dist/ui/streaming_scrollback.d.ts +1 -0
- package/dist/ui/streaming_scrollback.js +1 -1
- package/dist/ui/transcript_render_cache.js +10 -0
- package/dist/ui/views/activity_line.d.ts +5 -3
- package/dist/ui/views/activity_line.js +65 -7
- package/dist/ui/views/app.d.ts +6 -3
- package/dist/ui/views/app.js +17 -6
- package/dist/ui/views/completion_menu.js +10 -5
- package/dist/ui/views/input_area.d.ts +7 -3
- package/dist/ui/views/input_area.js +15 -9
- package/dist/ui/views/message_list.d.ts +4 -1
- package/dist/ui/views/message_list.js +89 -19
- package/dist/ui/views/queue_panel.d.ts +3 -1
- package/dist/ui/views/queue_panel.js +9 -2
- package/dist/ui/views/status_bar.d.ts +4 -2
- package/dist/ui/views/status_bar.js +28 -13
- package/dist/ui/views/theme.d.ts +2 -0
- package/dist/ui/views/theme.js +2 -0
- package/dist/ui/widgets/color_panel.js +18 -0
- package/dist/ui/widgets/form.js +8 -0
- package/dist/ui/widgets/log_view.js +7 -0
- package/dist/ui/widgets/navigator_panel.js +8 -0
- package/dist/ui/widgets/prompt_viewer.js +2 -0
- package/dist/ui/widgets/questionnaire.d.ts +2 -0
- package/dist/ui/widgets/questionnaire.js +79 -16
- package/dist/ui/widgets/resume_panel.d.ts +2 -0
- package/dist/ui/widgets/resume_panel.js +26 -15
- package/dist/ui/widgets/rewind_panel.d.ts +2 -2
- package/dist/ui/widgets/rewind_panel.js +4 -2
- package/dist/ui/widgets/select.js +16 -27
- package/dist/ui/widgets/settings_panel.js +16 -0
- package/dist/ui/widgets/status_line_panel.d.ts +1 -0
- package/dist/ui/widgets/status_line_panel.js +30 -4
- package/dist/ui/widgets/text_input.d.ts +2 -0
- package/dist/ui/widgets/text_input.js +15 -1
- package/dist/ui/widgets/types.d.ts +2 -0
- package/dist/usage_projection.d.ts +24 -0
- package/dist/usage_projection.js +130 -0
- package/dist/usage_snapshot.d.ts +36 -0
- package/dist/usage_snapshot.js +131 -0
- package/dist/utils/assistant_message.d.ts +2 -0
- package/dist/utils/assistant_message.js +5 -0
- package/dist/utils/atomic_write.d.ts +6 -2
- package/dist/utils/atomic_write.js +29 -4
- package/dist/utils/checked_shell.d.ts +25 -0
- package/dist/utils/checked_shell.js +107 -0
- package/dist/utils/compaction.d.ts +4 -1
- package/dist/utils/compaction.js +31 -7
- package/dist/utils/context.d.ts +4 -4
- package/dist/utils/context.js +14 -12
- package/dist/utils/deepseek.d.ts +2 -2
- package/dist/utils/deepseek.js +13 -5
- package/dist/utils/ephemeral.js +4 -0
- package/dist/utils/ephemeral_tools.d.ts +18 -0
- package/dist/utils/ephemeral_tools.js +97 -0
- package/dist/utils/jev_client.d.ts +4 -0
- package/dist/utils/jev_client.js +15 -0
- package/dist/utils/model_cache.d.ts +15 -0
- package/dist/utils/model_cache.js +143 -0
- package/dist/utils/models.d.ts +4 -2
- package/dist/utils/models.js +12 -16
- package/dist/utils/reasoning.d.ts +8 -1
- package/dist/utils/reasoning.js +31 -11
- package/dist/utils/router_catalog.d.ts +49 -0
- package/dist/utils/router_catalog.js +271 -0
- package/dist/utils/sandbox.d.ts +9 -0
- package/dist/utils/sandbox.js +118 -15
- package/dist/utils/shell.d.ts +7 -0
- package/dist/utils/shell.js +98 -34
- package/dist/utils/subprocess_env.d.ts +3 -0
- package/dist/utils/subprocess_env.js +27 -1
- package/dist/web/accounts_mirror.d.ts +3 -1
- package/dist/web/accounts_mirror.js +13 -9
- package/dist/web/child_publication.d.ts +17 -0
- package/dist/web/child_publication.js +45 -0
- package/dist/web/mirror_authenticator.d.ts +31 -11
- package/dist/web/mirror_authenticator.js +67 -17
- package/dist/web/mirror_client.d.ts +31 -4
- package/dist/web/mirror_client.js +189 -23
- package/dist/web/mirror_stream.d.ts +84 -22
- package/dist/web/mirror_stream.js +676 -95
- package/dist/web/observer_query_redaction.d.ts +6 -0
- package/dist/web/observer_query_redaction.js +58 -0
- package/dist/web/pane_registry.d.ts +2 -0
- package/dist/web/pane_registry.js +86 -22
- package/dist/web/tool_image.d.ts +6 -0
- package/dist/web/tool_image.js +17 -0
- package/dist/web/view_plugin.d.ts +14 -0
- package/dist/web/view_plugin.js +23 -1
- package/dist/worker/executor.js +31 -12
- package/dist/worker/index.d.ts +1 -0
- package/dist/worker/toolsets.js +3 -1
- package/dist/worker/types.d.ts +12 -1
- package/dist/workflows/registry.js +1 -0
- package/dist/workflows/registry_handle.d.ts +2 -0
- package/dist/workflows/runtime.d.ts +2 -0
- package/dist/workflows/runtime.js +1 -0
- package/dist/workspace_agent.d.ts +7 -0
- package/dist/workspace_agent.js +36 -0
- package/dist/workspace_collision.d.ts +76 -0
- package/dist/workspace_collision.js +688 -0
- package/dist/workspace_layout.d.ts +18 -0
- package/dist/workspace_layout.js +121 -0
- package/dist/workspace_runtime.d.ts +44 -0
- package/dist/workspace_runtime.js +82 -0
- package/dist/workspace_session_context.d.ts +62 -0
- package/dist/workspace_session_context.js +160 -0
- package/dist/zlm-embed.mjs +32518 -9615
- package/dist/zlm.js +1 -0
- package/node_modules/@zerg/model-contracts/LICENSE.md +375 -0
- package/node_modules/@zerg/model-contracts/README.md +90 -0
- package/node_modules/@zerg/model-contracts/index.d.ts +20 -0
- package/node_modules/@zerg/model-contracts/index.js +223 -0
- package/node_modules/@zerg/model-contracts/package.json +8 -0
- package/node_modules/glob/node_modules/brace-expansion/index.js +78 -22
- package/node_modules/glob/node_modules/brace-expansion/package.json +1 -1
- package/node_modules/readdir-glob/node_modules/brace-expansion/index.js +78 -22
- package/node_modules/readdir-glob/node_modules/brace-expansion/package.json +1 -1
- package/package.json +45 -24
- package/packages/model-contracts/LICENSE.md +375 -0
- package/packages/model-contracts/README.md +90 -0
- package/packages/model-contracts/index.d.ts +20 -0
- package/packages/model-contracts/index.js +223 -0
- package/packages/model-contracts/package.json +8 -0
- package/remote-broker/Dockerfile +10 -0
- package/remote-broker/README.md +308 -0
- package/remote-broker/archive.py +1227 -0
- package/remote-broker/cutover.py +761 -0
- package/remote-broker/requirements.txt +5 -0
- package/remote-broker/resources.example.json +6 -0
- package/remote-broker/resources.write-staging.example.json +18 -0
- package/remote-broker/retained.py +686 -0
- package/remote-broker/retained_cleanup.py +550 -0
- package/remote-broker/server.py +648 -0
- package/scripts/build-remote-broker.mjs +13 -0
- package/scripts/ensure-local-packages.mjs +6 -0
- package/scripts/node-pty-helpers.mjs +103 -0
package/dist/session.js
CHANGED
|
@@ -14,12 +14,22 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import { mkdir, open, readFile, readdir, stat, rename, unlink, rm } from 'fs/promises';
|
|
16
16
|
import { existsSync } from 'fs';
|
|
17
|
-
import { dirname, join, resolve } from 'path';
|
|
17
|
+
import { basename, dirname, join, relative, resolve, sep } from 'path';
|
|
18
18
|
import { homedir } from 'os';
|
|
19
19
|
import { createHash } from 'crypto';
|
|
20
|
+
import { parseEvidenceSnapshot } from './agent/runtime/evidence.js';
|
|
21
|
+
import { mergeDiagnosticSnapshots, parseDiagnosticSnapshot } from './diagnostics/session_export.js';
|
|
22
|
+
import { mergeUsageSnapshots, parseUsageSnapshot } from './usage_snapshot.js';
|
|
20
23
|
import { writeFileAtomic } from './utils/atomic_write.js';
|
|
24
|
+
import { withPathLock } from './utils/path_lock.js';
|
|
25
|
+
import { prepareSessionMedia } from './session_media.js';
|
|
21
26
|
import { stripEphemeralMessages } from './utils/ephemeral.js';
|
|
27
|
+
import { stripEphemeralToolPayloads } from './utils/ephemeral_tools.js';
|
|
22
28
|
import { activeProfile } from './config/profiles.js';
|
|
29
|
+
import { retainSessionForProcess, retainedSessionIds, withSessionCleanupBarrier } from './session_ownership.js';
|
|
30
|
+
import { admitTaskHandoff, canonicalHandoffCwd, deriveTaskHandoff, taskHandoffFingerprints, TaskHandoffAdmissionError } from './session_handoff.js';
|
|
31
|
+
import { projectTaskHandoff } from './session_handoff_contract.js';
|
|
32
|
+
import { admitBoardSessionBinding } from './board/binding.js';
|
|
23
33
|
/**
|
|
24
34
|
* Schema version for on-disk Session payloads.
|
|
25
35
|
*
|
|
@@ -34,6 +44,18 @@ export const MAX_SESSION_FILE_BYTES = 4 * 1024 * 1024;
|
|
|
34
44
|
function buildRuntimeMetadata(runtimeState, existingSession) {
|
|
35
45
|
const metadata = {};
|
|
36
46
|
const existingMetadata = existingSession?.metadata;
|
|
47
|
+
if (runtimeState && Object.prototype.hasOwnProperty.call(runtimeState, 'environment'))
|
|
48
|
+
metadata.environment = runtimeState.environment;
|
|
49
|
+
else if (existingMetadata?.environment !== undefined)
|
|
50
|
+
metadata.environment = existingMetadata.environment;
|
|
51
|
+
if (existingMetadata?.labels)
|
|
52
|
+
metadata.labels = { ...existingMetadata.labels };
|
|
53
|
+
const authMode = runtimeState?.authMode ?? existingMetadata?.authMode;
|
|
54
|
+
if (authMode)
|
|
55
|
+
metadata.authMode = authMode;
|
|
56
|
+
const routerOrigin = runtimeState?.routerOrigin ?? existingMetadata?.routerOrigin;
|
|
57
|
+
if (authMode !== 'direct' && routerOrigin)
|
|
58
|
+
metadata.routerOrigin = routerOrigin;
|
|
37
59
|
const mode = runtimeState?.mode ?? existingMetadata?.mode;
|
|
38
60
|
if (mode) {
|
|
39
61
|
metadata.mode = mode;
|
|
@@ -83,15 +105,147 @@ function isSessionShape(value) {
|
|
|
83
105
|
return false;
|
|
84
106
|
if (typeof meta.id !== 'string' || !meta.id)
|
|
85
107
|
return false;
|
|
108
|
+
if (meta.environment !== undefined && meta.environment !== null) {
|
|
109
|
+
if (typeof meta.environment !== 'object')
|
|
110
|
+
return false;
|
|
111
|
+
const environment = meta.environment;
|
|
112
|
+
if (typeof environment.id !== 'string' || !/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/.test(environment.id)
|
|
113
|
+
|| typeof environment.name !== 'string' || !/^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/.test(environment.name))
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
86
116
|
if (!Array.isArray(v.messages))
|
|
87
117
|
return false;
|
|
88
118
|
return true;
|
|
89
119
|
}
|
|
90
|
-
function
|
|
120
|
+
function admitSessionDiagnostics(value, path) {
|
|
121
|
+
const snapshot = parseDiagnosticSnapshot(value);
|
|
122
|
+
if (snapshot === null) {
|
|
123
|
+
throw new SessionLoadError(`Session file ${path} contains invalid or unsupported diagnostics`, 'diagnostics-invalid', path);
|
|
124
|
+
}
|
|
125
|
+
return snapshot;
|
|
126
|
+
}
|
|
127
|
+
function mergeSessionDiagnostics(runtimeState, existing, path) {
|
|
128
|
+
const prior = existing?.diagnostics === undefined ? undefined : admitSessionDiagnostics(existing.diagnostics, path);
|
|
129
|
+
const current = runtimeState?.diagnostics === undefined ? undefined : admitSessionDiagnostics(runtimeState.diagnostics, path);
|
|
130
|
+
if (current === undefined)
|
|
131
|
+
return prior;
|
|
132
|
+
if (prior === undefined)
|
|
133
|
+
return current;
|
|
134
|
+
try {
|
|
135
|
+
return mergeDiagnosticSnapshots(prior, current);
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
throw new SessionLoadError(`Session file ${path} contains conflicting diagnostic identities`, 'diagnostics-invalid', path, error);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
function normalizeLoadedSession(session, path) {
|
|
142
|
+
if (session.board_binding !== undefined) {
|
|
143
|
+
try {
|
|
144
|
+
session.board_binding = admitBoardSessionBinding(session.board_binding, session.metadata.id);
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
throw new SessionLoadError('Invalid Zergboard session binding', 'board-binding-invalid', path, error);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (session.evidence !== undefined) {
|
|
151
|
+
session.evidence = parseEvidenceSnapshot(session.evidence) ?? undefined;
|
|
152
|
+
}
|
|
153
|
+
if (session.diagnostics !== undefined)
|
|
154
|
+
session.diagnostics = admitSessionDiagnostics(session.diagnostics, path);
|
|
155
|
+
if (session.usage !== undefined)
|
|
156
|
+
session.usage = admitSessionUsage(session.usage, path);
|
|
91
157
|
session.messages = stripEphemeralMessages(session.messages);
|
|
158
|
+
// Admit the complete saved payload before normalizing display-only clocks.
|
|
159
|
+
// A changed execution source must never be repaired by rehashing it.
|
|
160
|
+
if (session.task_handoff !== undefined)
|
|
161
|
+
session.task_handoff = admitTaskHandoff(session.task_handoff, session);
|
|
162
|
+
const savedAt = Date.parse(session.metadata.updatedAt);
|
|
163
|
+
session.messages = stripEphemeralMessages(session.messages).map(message => {
|
|
164
|
+
const turn = message.turn;
|
|
165
|
+
if (!turn)
|
|
166
|
+
return message;
|
|
167
|
+
if (typeof turn.id !== 'string' || !Number.isFinite(turn.startedAt)
|
|
168
|
+
|| !Number.isFinite(turn.tokens) || turn.tokens < 0 || typeof turn.estimated !== 'boolean'
|
|
169
|
+
|| (turn.finishedAt !== undefined && !Number.isFinite(turn.finishedAt))) {
|
|
170
|
+
return { ...message, turn: undefined };
|
|
171
|
+
}
|
|
172
|
+
if (turn.finishedAt !== undefined)
|
|
173
|
+
return message;
|
|
174
|
+
// A saved snapshot is history: a crashed process cannot keep its clock live.
|
|
175
|
+
return { ...message, turn: Number.isFinite(savedAt) ? { ...turn, finishedAt: Math.max(turn.startedAt, savedAt) } : undefined };
|
|
176
|
+
});
|
|
92
177
|
session.metadata.messageCount = session.messages.length;
|
|
178
|
+
if (session.task_handoff !== undefined) {
|
|
179
|
+
session.task_handoff = { ...session.task_handoff, source: {
|
|
180
|
+
...session.task_handoff.source, ...taskHandoffFingerprints(session),
|
|
181
|
+
} };
|
|
182
|
+
}
|
|
183
|
+
if (isCompactionTitle(session.metadata.title)) {
|
|
184
|
+
session.metadata.title = deriveSessionTitle(session.messages);
|
|
185
|
+
}
|
|
93
186
|
return session;
|
|
94
187
|
}
|
|
188
|
+
function admitSessionUsage(value, path) {
|
|
189
|
+
const snapshot = parseUsageSnapshot(value);
|
|
190
|
+
if (snapshot === null) {
|
|
191
|
+
throw new SessionLoadError(`Session file ${path} contains invalid or unsupported usage`, 'usage-invalid', path);
|
|
192
|
+
}
|
|
193
|
+
return snapshot;
|
|
194
|
+
}
|
|
195
|
+
function mergeSessionUsage(runtimeState, existing, path) {
|
|
196
|
+
const prior = existing?.usage === undefined ? undefined : admitSessionUsage(existing.usage, path);
|
|
197
|
+
const current = runtimeState?.usage === undefined ? undefined : admitSessionUsage(runtimeState.usage, path);
|
|
198
|
+
if (current === undefined)
|
|
199
|
+
return prior;
|
|
200
|
+
if (prior === undefined)
|
|
201
|
+
return current;
|
|
202
|
+
// A conflicting logical-call identity is a caller error, not a replaceable observation.
|
|
203
|
+
return mergeUsageSnapshots(prior, current);
|
|
204
|
+
}
|
|
205
|
+
function isCompactionTitle(title) {
|
|
206
|
+
// Includes the truncated 50-character title written by older versions.
|
|
207
|
+
return typeof title === 'string' && title.startsWith('[Conversation summary');
|
|
208
|
+
}
|
|
209
|
+
/** Recover only the explicit original-user record in the local summary format. */
|
|
210
|
+
function titleFromCompactedStart(content, depth = 0) {
|
|
211
|
+
if (depth >= 8)
|
|
212
|
+
return undefined;
|
|
213
|
+
const record = /\*\*Conversation start\*\*\r?\n([^\r\n]+)/.exec(content)?.[1];
|
|
214
|
+
if (!record)
|
|
215
|
+
return undefined;
|
|
216
|
+
try {
|
|
217
|
+
const parsed = JSON.parse(record);
|
|
218
|
+
if (!isPlainJsonObject(parsed) || parsed.role !== 'user' || typeof parsed.content !== 'string')
|
|
219
|
+
return undefined;
|
|
220
|
+
return isCompactionTitle(parsed.content)
|
|
221
|
+
? titleFromCompactedStart(parsed.content, depth + 1)
|
|
222
|
+
: parsed.content.slice(0, 50) || undefined;
|
|
223
|
+
}
|
|
224
|
+
catch {
|
|
225
|
+
// A bounded summary may have elided this record. Use surviving dialogue.
|
|
226
|
+
return undefined;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
/** Session identity belongs to the original request, not a summary carrier. */
|
|
230
|
+
function deriveSessionTitle(messages, previousTitle) {
|
|
231
|
+
if (typeof previousTitle === 'string' && previousTitle && previousTitle !== 'Untitled session' && previousTitle !== 'Untitled'
|
|
232
|
+
&& !isCompactionTitle(previousTitle))
|
|
233
|
+
return previousTitle;
|
|
234
|
+
for (const message of messages) {
|
|
235
|
+
if (message.role !== 'user' || typeof message.content !== 'string'
|
|
236
|
+
|| message.metadata?.ephemeral || message.metadata?.toolResult)
|
|
237
|
+
continue;
|
|
238
|
+
if (message.metadata?.compactionSummary === true || isCompactionTitle(message.content)) {
|
|
239
|
+
const recovered = titleFromCompactedStart(message.content);
|
|
240
|
+
if (recovered)
|
|
241
|
+
return recovered;
|
|
242
|
+
}
|
|
243
|
+
else if (message.content) {
|
|
244
|
+
return message.content.slice(0, 50);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return 'Untitled session';
|
|
248
|
+
}
|
|
95
249
|
export const COLONY_WORKTREE_MARKER = '.colony-worktree.json';
|
|
96
250
|
/**
|
|
97
251
|
* Bounded prefix read for session heads.
|
|
@@ -264,6 +418,7 @@ function summaryFromHeadMetadata(metadata, entry) {
|
|
|
264
418
|
cwd: typeof metadata.cwd === 'string' ? metadata.cwd : '',
|
|
265
419
|
title: typeof metadata.title === 'string' && metadata.title ? metadata.title : 'Untitled',
|
|
266
420
|
...(typeof metadata.name === 'string' && metadata.name ? { name: metadata.name } : {}),
|
|
421
|
+
...(metadata.labels && typeof metadata.labels === 'object' ? { labels: metadata.labels } : {}),
|
|
267
422
|
messageCount,
|
|
268
423
|
updatedAt: typeof metadata.updatedAt === 'string'
|
|
269
424
|
? metadata.updatedAt
|
|
@@ -391,6 +546,13 @@ export function getToolResponsesDir(projectHash, sessionId) {
|
|
|
391
546
|
function getSessionDirForStorage(storage, sessionId) {
|
|
392
547
|
return join(storage.sessionDir, sessionId);
|
|
393
548
|
}
|
|
549
|
+
function getSessionCoordinationDirectory(storage) {
|
|
550
|
+
// Legacy colony transcripts are readable in the worktree, but new runtime
|
|
551
|
+
// metadata belongs in global colony storage, like new chat and mux data.
|
|
552
|
+
return storage.kind === 'legacy-colony'
|
|
553
|
+
? makeGlobalColonyStorage(storage.projectHash).sessionDir
|
|
554
|
+
: storage.sessionDir;
|
|
555
|
+
}
|
|
394
556
|
function getSessionJsonPath(storage, sessionId) {
|
|
395
557
|
return join(getSessionDirForStorage(storage, sessionId), 'session.json');
|
|
396
558
|
}
|
|
@@ -421,7 +583,7 @@ async function ensureSessionDir(projectHash, sessionId) {
|
|
|
421
583
|
}
|
|
422
584
|
async function readSessionJson(path, muxStatePath) {
|
|
423
585
|
try {
|
|
424
|
-
const session = await loadSessionFromFile(path);
|
|
586
|
+
const session = await loadSessionFromFile(path, false);
|
|
425
587
|
if (!session)
|
|
426
588
|
return null;
|
|
427
589
|
if (muxStatePath && existsSync(muxStatePath)) {
|
|
@@ -435,7 +597,9 @@ async function readSessionJson(path, muxStatePath) {
|
|
|
435
597
|
}
|
|
436
598
|
return session;
|
|
437
599
|
}
|
|
438
|
-
catch {
|
|
600
|
+
catch (error) {
|
|
601
|
+
if (error instanceof SessionLoadError && (error.reason.startsWith('handoff-') || error.reason === 'diagnostics-invalid' || error.reason === 'usage-invalid' || error.reason === 'board-binding-invalid'))
|
|
602
|
+
throw error;
|
|
439
603
|
return null;
|
|
440
604
|
}
|
|
441
605
|
}
|
|
@@ -443,8 +607,62 @@ async function readSessionJson(path, muxStatePath) {
|
|
|
443
607
|
* Atomic session write: pretty-JSON-encode `session` to a temp file and rename
|
|
444
608
|
* into place. A crash mid-write never leaves the canonical session truncated.
|
|
445
609
|
*/
|
|
446
|
-
async function writeSessionAtomic(path, session) {
|
|
447
|
-
|
|
610
|
+
async function writeSessionAtomic(path, session, signal) {
|
|
611
|
+
const checkpoint = JSON.stringify(session, null, 2);
|
|
612
|
+
await writeFileAtomic(path, checkpoint, { signal });
|
|
613
|
+
}
|
|
614
|
+
async function writeSessionWithMedia(path, session, runtimeState, existingSession, signal) {
|
|
615
|
+
await withPathLock(path, async () => {
|
|
616
|
+
signal?.throwIfAborted();
|
|
617
|
+
// An autosave may have captured messages before a board request yielded.
|
|
618
|
+
// Merge the latest binding while holding the same canonical-file lock.
|
|
619
|
+
let latest = null;
|
|
620
|
+
try {
|
|
621
|
+
latest = await loadSessionFromFile(path, false);
|
|
622
|
+
}
|
|
623
|
+
catch (error) {
|
|
624
|
+
if (!(error instanceof SessionLoadError) || error.reason === 'board-binding-invalid')
|
|
625
|
+
throw error;
|
|
626
|
+
// Preserve existing writer recovery of previously unreadable checkpoints.
|
|
627
|
+
}
|
|
628
|
+
if (latest?.metadata.id === session.metadata.id)
|
|
629
|
+
session.board_binding = latest.board_binding;
|
|
630
|
+
session.messages = stripEphemeralToolPayloads(session.messages, runtimeState?.ephemeralToolNames, runtimeState?.ephemeralToolRules);
|
|
631
|
+
const media = await prepareSessionMedia({ sessionFile: resolve(path), sessionId: session.metadata.id, messages: session.messages });
|
|
632
|
+
try {
|
|
633
|
+
session.messages = media.messages;
|
|
634
|
+
// Task identity and consumed Clear events must come from the same admitted
|
|
635
|
+
// predecessor. A save prepared before the lock can have an older capture.
|
|
636
|
+
const predecessor = latest ?? existingSession;
|
|
637
|
+
session.task_handoff = deriveTaskHandoff(session, runtimeState?.taskHandoffInput, predecessor?.task_handoff, predecessor?.messages) ?? undefined;
|
|
638
|
+
await writeSessionAtomic(path, session, signal);
|
|
639
|
+
// Dedicated mux persistence runs afterward; it must never roll back
|
|
640
|
+
// image bytes that this canonical checkpoint already owns.
|
|
641
|
+
media.markCommitted();
|
|
642
|
+
}
|
|
643
|
+
finally {
|
|
644
|
+
await media.rollback();
|
|
645
|
+
}
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
/** Compare-and-set the association without rewriting captured chat/media state. */
|
|
649
|
+
export async function updateSessionBoardBinding(path, sessionId, expected, next) {
|
|
650
|
+
await withPathLock(path, async () => {
|
|
651
|
+
const session = await loadSessionFromFile(path, false);
|
|
652
|
+
if (!session || session.metadata.id !== sessionId)
|
|
653
|
+
throw new Error('Zergboard requires the current saved session checkpoint');
|
|
654
|
+
const admittedExpected = expected === undefined ? undefined : admitBoardSessionBinding(expected, sessionId);
|
|
655
|
+
if (JSON.stringify(session.board_binding) !== JSON.stringify(admittedExpected))
|
|
656
|
+
throw new Error('Zergboard session association changed; reload before retrying');
|
|
657
|
+
if (next === undefined)
|
|
658
|
+
delete session.board_binding;
|
|
659
|
+
else
|
|
660
|
+
session.board_binding = admitBoardSessionBinding(next, sessionId);
|
|
661
|
+
const text = JSON.stringify(session, null, 2);
|
|
662
|
+
if (Buffer.byteLength(text) > MAX_SESSION_FILE_BYTES)
|
|
663
|
+
throw new Error('Zergboard checkpoint exceeds the session size limit; export evidence to a file instead');
|
|
664
|
+
await writeSessionAtomic(path, session);
|
|
665
|
+
});
|
|
448
666
|
}
|
|
449
667
|
/**
|
|
450
668
|
* Migrate a legacy flat `{sessionId}.json` into the directory layout.
|
|
@@ -479,8 +697,11 @@ async function migrateLegacyFlatFile(storage, sessionId) {
|
|
|
479
697
|
return false;
|
|
480
698
|
}
|
|
481
699
|
}
|
|
482
|
-
async function loadSessionFromStorage(storage, sessionId) {
|
|
700
|
+
async function loadSessionFromStorage(storage, sessionId, retain = true) {
|
|
483
701
|
const sessionPath = getSessionJsonPath(storage, sessionId);
|
|
702
|
+
if (retain && (existsSync(sessionPath) || existsSync(getLegacyFlatPath(storage, sessionId)))) {
|
|
703
|
+
await retainSessionForProcess(getSessionCoordinationDirectory(storage), sessionId);
|
|
704
|
+
}
|
|
484
705
|
const muxStatePath = getMuxStateFilePath(storage, sessionId);
|
|
485
706
|
const session = await readSessionJson(sessionPath, muxStatePath);
|
|
486
707
|
if (session)
|
|
@@ -533,8 +754,14 @@ async function listSessionEntries(storage) {
|
|
|
533
754
|
}
|
|
534
755
|
async function readSessionSummaryEntry(entry, maxFileBytes) {
|
|
535
756
|
const head = await readSessionHeadSummary(entry);
|
|
536
|
-
if (head.kind === 'summary')
|
|
537
|
-
|
|
757
|
+
if (head.kind === 'summary') {
|
|
758
|
+
if (!isCompactionTitle(head.summary.title))
|
|
759
|
+
return head.summary;
|
|
760
|
+
// Repair old titles for display without rewriting another session's file.
|
|
761
|
+
// Keep oversized entries visible even when their transcript cannot be read.
|
|
762
|
+
return await readSessionSummaryFromFullParse(entry, maxFileBytes)
|
|
763
|
+
?? { ...head.summary, title: 'Untitled session' };
|
|
764
|
+
}
|
|
538
765
|
if (head.kind === 'excluded')
|
|
539
766
|
return null;
|
|
540
767
|
return readSessionSummaryFromFullParse(entry, maxFileBytes);
|
|
@@ -567,13 +794,14 @@ async function readSessionSummaryFromFullParse(entry, maxFileBytes) {
|
|
|
567
794
|
if (typeof parsed.schemaVersion === 'number' && parsed.schemaVersion > SESSION_SCHEMA_VERSION) {
|
|
568
795
|
return null;
|
|
569
796
|
}
|
|
570
|
-
const session = normalizeLoadedSession(parsed);
|
|
797
|
+
const session = normalizeLoadedSession(parsed, entry.path);
|
|
571
798
|
const metadata = session.metadata;
|
|
572
799
|
return {
|
|
573
800
|
id: metadata.id,
|
|
574
801
|
cwd: typeof metadata.cwd === 'string' ? metadata.cwd : '',
|
|
575
802
|
title: typeof metadata.title === 'string' && metadata.title ? metadata.title : 'Untitled',
|
|
576
803
|
...(typeof metadata.name === 'string' && metadata.name ? { name: metadata.name } : {}),
|
|
804
|
+
...(metadata.labels && typeof metadata.labels === 'object' ? { labels: metadata.labels } : {}),
|
|
577
805
|
messageCount: session.messages.length,
|
|
578
806
|
updatedAt: typeof metadata.updatedAt === 'string'
|
|
579
807
|
? metadata.updatedAt
|
|
@@ -620,6 +848,149 @@ export async function findRecentSessionSummary(cwd, options = {}) {
|
|
|
620
848
|
}
|
|
621
849
|
return undefined;
|
|
622
850
|
}
|
|
851
|
+
/**
|
|
852
|
+
* Inspect a bounded same-project window and offer exact, validated checkpoints.
|
|
853
|
+
* Metadata inspection is bounded to 128 heads and, for canonical alias recovery,
|
|
854
|
+
* 128 project directories. File mtime orders the selected window; checkpoint
|
|
855
|
+
* as-of and session ID determine offer ordering. This is not a complete history
|
|
856
|
+
* search. Selection never imports a task or asserts liveness.
|
|
857
|
+
*/
|
|
858
|
+
export async function findSessionHandoffOffers(cwd, options = {}) {
|
|
859
|
+
const limit = options.maxCandidates ?? 16;
|
|
860
|
+
if (!Number.isSafeInteger(limit) || limit < 0 || limit > 16)
|
|
861
|
+
throw new RangeError('Handoff candidate limit must be an integer from 0 to 16.');
|
|
862
|
+
if (limit === 0)
|
|
863
|
+
return [];
|
|
864
|
+
const canonicalCwd = canonicalHandoffCwd(cwd);
|
|
865
|
+
const entries = [];
|
|
866
|
+
const seenPaths = new Set();
|
|
867
|
+
const offeredSessionIds = new Set();
|
|
868
|
+
const storageDirectories = new Set();
|
|
869
|
+
const storages = [...getStorageCandidates(cwd), ...getStorageCandidates(canonicalCwd)]
|
|
870
|
+
.filter(storage => {
|
|
871
|
+
if (storageDirectories.has(storage.sessionDir))
|
|
872
|
+
return false;
|
|
873
|
+
storageDirectories.add(storage.sessionDir);
|
|
874
|
+
return true;
|
|
875
|
+
});
|
|
876
|
+
for (const storage of storages)
|
|
877
|
+
for (const entry of await listSessionEntries(storage)) {
|
|
878
|
+
if (entry.sessionId === options.excludeSessionId || entry.size >= MAX_SESSION_FILE_BYTES)
|
|
879
|
+
continue;
|
|
880
|
+
entries.push(entry);
|
|
881
|
+
}
|
|
882
|
+
const order = (a, b) => b.mtimeMs - a.mtimeMs || a.sessionId.localeCompare(b.sessionId) || a.path.localeCompare(b.path);
|
|
883
|
+
entries.sort(order);
|
|
884
|
+
const offers = [];
|
|
885
|
+
let headReads = 0;
|
|
886
|
+
let fullReads = 0;
|
|
887
|
+
const inspect = async (entry) => {
|
|
888
|
+
if (headReads >= 128 || fullReads >= limit || seenPaths.has(entry.path) || offeredSessionIds.has(entry.sessionId)
|
|
889
|
+
|| entry.sessionId === options.excludeSessionId || entry.size >= MAX_SESSION_FILE_BYTES)
|
|
890
|
+
return;
|
|
891
|
+
seenPaths.add(entry.path);
|
|
892
|
+
headReads += 1;
|
|
893
|
+
try {
|
|
894
|
+
const handle = await open(entry.path, 'r');
|
|
895
|
+
let session;
|
|
896
|
+
try {
|
|
897
|
+
const current = await handle.stat();
|
|
898
|
+
if (!current.isFile() || current.size >= MAX_SESSION_FILE_BYTES)
|
|
899
|
+
return;
|
|
900
|
+
const head = Buffer.alloc(Math.min(current.size, SESSION_HEAD_MAX_BYTES));
|
|
901
|
+
const read = await handle.read(head, 0, head.length, 0);
|
|
902
|
+
const text = head.subarray(0, read.bytesRead).toString('utf8');
|
|
903
|
+
// Current writers put this bounded object before messages; old or
|
|
904
|
+
// incomplete payloads are not guessed from conversation prose.
|
|
905
|
+
const handoffText = extractTopLevelJsonObject(text, 'task_handoff');
|
|
906
|
+
const metadataText = extractTopLevelJsonObject(text, 'metadata');
|
|
907
|
+
if (!handoffText || !metadataText)
|
|
908
|
+
return;
|
|
909
|
+
const metadata = JSON.parse(metadataText);
|
|
910
|
+
if (metadata.id !== entry.sessionId || typeof metadata.cwd !== 'string' || canonicalHandoffCwd(metadata.cwd) !== canonicalCwd)
|
|
911
|
+
return;
|
|
912
|
+
fullReads += 1;
|
|
913
|
+
const buffer = Buffer.alloc(current.size + 1);
|
|
914
|
+
let count = 0;
|
|
915
|
+
while (count < buffer.length) {
|
|
916
|
+
const chunk = await handle.read(buffer, count, buffer.length - count, count);
|
|
917
|
+
if (chunk.bytesRead === 0)
|
|
918
|
+
break;
|
|
919
|
+
count += chunk.bytesRead;
|
|
920
|
+
}
|
|
921
|
+
if (count !== current.size)
|
|
922
|
+
return;
|
|
923
|
+
const raw = JSON.parse(buffer.subarray(0, count).toString('utf8'));
|
|
924
|
+
if (!isSessionShape(raw) || (typeof raw.schemaVersion === 'number' && raw.schemaVersion > SESSION_SCHEMA_VERSION))
|
|
925
|
+
return;
|
|
926
|
+
session = normalizeLoadedSession(raw, entry.path);
|
|
927
|
+
}
|
|
928
|
+
finally {
|
|
929
|
+
await handle.close();
|
|
930
|
+
}
|
|
931
|
+
if (!session.task_handoff || session.task_handoff.taskStatus === 'complete')
|
|
932
|
+
return;
|
|
933
|
+
offeredSessionIds.add(entry.sessionId);
|
|
934
|
+
offers.push(projectTaskHandoff(session.task_handoff));
|
|
935
|
+
}
|
|
936
|
+
catch (error) {
|
|
937
|
+
if (error instanceof TaskHandoffAdmissionError || error instanceof SyntaxError || error.code === 'ENOENT')
|
|
938
|
+
return;
|
|
939
|
+
// Unreadable candidates cannot be offered; the explicit load path still
|
|
940
|
+
// raises its source-specific diagnostic without rewriting the file.
|
|
941
|
+
if (['EACCES', 'EPERM', 'EISDIR'].includes(error.code ?? ''))
|
|
942
|
+
return;
|
|
943
|
+
throw error;
|
|
944
|
+
}
|
|
945
|
+
};
|
|
946
|
+
for (const entry of entries.slice(0, limit))
|
|
947
|
+
await inspect(entry);
|
|
948
|
+
if (offers.length === 0 && fullReads < limit) {
|
|
949
|
+
// Older saves used lexical project hashes. A different alias cannot derive
|
|
950
|
+
// that hash, so inspect a small deterministic directory window and require
|
|
951
|
+
// exact canonical cwd association before admitting any complete payload.
|
|
952
|
+
const roots = [{ root: getGlobalSessionsRoot(), colony: false }];
|
|
953
|
+
if (storages.some(storage => storage.kind === 'colony'))
|
|
954
|
+
roots.push({ root: join(getGlobalColonyRoot(), 'worktrees'), colony: true });
|
|
955
|
+
const projects = [];
|
|
956
|
+
for (const location of roots) {
|
|
957
|
+
try {
|
|
958
|
+
for (const project of await readdir(location.root, { withFileTypes: true })) {
|
|
959
|
+
if (!project.isDirectory())
|
|
960
|
+
continue;
|
|
961
|
+
projects.push({ storage: location.colony ? makeGlobalColonyStorage(project.name) : makeGlobalStorage(project.name), key: `${location.root}/${project.name}` });
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
catch (error) {
|
|
965
|
+
if (!['ENOENT', 'EACCES', 'EPERM'].includes(error.code ?? ''))
|
|
966
|
+
throw error;
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
projects.sort((a, b) => a.key.localeCompare(b.key));
|
|
970
|
+
const candidates = [];
|
|
971
|
+
for (const project of projects.slice(0, 128)) {
|
|
972
|
+
if (candidates.length >= 128)
|
|
973
|
+
break;
|
|
974
|
+
try {
|
|
975
|
+
const names = await readdir(project.storage.sessionDir, { withFileTypes: true });
|
|
976
|
+
names.sort((a, b) => a.name.localeCompare(b.name));
|
|
977
|
+
for (const name of names.slice(0, 128 - candidates.length)) {
|
|
978
|
+
const entry = await resolveSessionEntry(project.storage, name);
|
|
979
|
+
if (entry)
|
|
980
|
+
candidates.push(entry);
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
catch (error) {
|
|
984
|
+
if (!['ENOENT', 'EACCES', 'EPERM', 'ENOTDIR'].includes(error.code ?? ''))
|
|
985
|
+
throw error;
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
candidates.sort(order);
|
|
989
|
+
for (const entry of candidates)
|
|
990
|
+
await inspect(entry);
|
|
991
|
+
}
|
|
992
|
+
return offers.sort((a, b) => b.asOf.localeCompare(a.asOf) || a.snapshot.source.sessionId.localeCompare(b.snapshot.source.sessionId));
|
|
993
|
+
}
|
|
623
994
|
export function getSessionDirForCwd(cwd) {
|
|
624
995
|
return getStorageCandidates(cwd)[0].sessionDir;
|
|
625
996
|
}
|
|
@@ -650,32 +1021,56 @@ export function getMuxStatePathForCwd(cwd, sessionId) {
|
|
|
650
1021
|
* ~/.ztc/colony/worktrees; normal projects bind to ~/.ztc/sessions.
|
|
651
1022
|
*/
|
|
652
1023
|
export async function bindToolResponseStoreToSession(store, discovered, cwd, sessionId, config, sessionFile) {
|
|
1024
|
+
const invalidateRestoredCoverage = () => {
|
|
1025
|
+
const coverage = store.evidence.getSnapshot().verificationCoverage;
|
|
1026
|
+
if (coverage?.length)
|
|
1027
|
+
store.evidence.setVerificationCoverage(coverage.map(projection => ({
|
|
1028
|
+
...projection, source_id: null, applicability: 'unknown',
|
|
1029
|
+
reason: 'Restored source applicability is historical; a fresh admitted host check is required.',
|
|
1030
|
+
})));
|
|
1031
|
+
};
|
|
1032
|
+
const restoreExportedEvidence = async (path) => {
|
|
1033
|
+
const session = await loadSessionFromFile(path, false);
|
|
1034
|
+
const exported = session?.evidence;
|
|
1035
|
+
if (!exported || exported.revision <= store.evidence.getSnapshot().revision)
|
|
1036
|
+
return;
|
|
1037
|
+
store.evidence.restore(exported);
|
|
1038
|
+
for (const record of exported.records) {
|
|
1039
|
+
if (record.kind === 'observation' && !store.getMeta(record.source.toolCallId ?? record.id)) {
|
|
1040
|
+
store.evidence.setAvailability(record.id, 'unavailable');
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
};
|
|
653
1044
|
if (config)
|
|
654
1045
|
store.setConfig(config);
|
|
655
1046
|
if (sessionFile) {
|
|
656
1047
|
const dir = join(dirname(sessionFile), `${sessionId}.tool_responses`);
|
|
657
1048
|
await mkdir(dir, { recursive: true });
|
|
658
1049
|
await store.setSessionDir(dir);
|
|
1050
|
+
await restoreExportedEvidence(sessionFile);
|
|
1051
|
+
invalidateRestoredCoverage();
|
|
659
1052
|
discovered.clear();
|
|
660
1053
|
return;
|
|
661
1054
|
}
|
|
662
1055
|
const storage = getStorageCandidates(cwd)[0];
|
|
1056
|
+
await retainSessionForProcess(getSessionCoordinationDirectory(storage), sessionId);
|
|
663
1057
|
await ensureSessionStorage(storage, sessionId);
|
|
664
1058
|
await store.setSessionDir(getToolResponsesDirForStorage(storage, sessionId));
|
|
1059
|
+
await restoreExportedEvidence(getSessionJsonPath(storage, sessionId));
|
|
1060
|
+
invalidateRestoredCoverage();
|
|
665
1061
|
discovered.clear();
|
|
666
1062
|
}
|
|
667
|
-
export async function saveSession(sessionId, cwd, messages, model, provider, muxState, paneSpecs, runtimeState, hostedSettings) {
|
|
1063
|
+
export async function saveSession(sessionId, cwd, messages, model, provider, muxState, paneSpecs, runtimeState, hostedSettings, signal) {
|
|
668
1064
|
const storage = getStorageCandidates(cwd)[0];
|
|
1065
|
+
await retainSessionForProcess(getSessionCoordinationDirectory(storage), sessionId);
|
|
669
1066
|
await ensureSessionStorage(storage, sessionId);
|
|
670
1067
|
const sessionPath = getSessionJsonPath(storage, sessionId);
|
|
671
1068
|
const existingSession = await loadSessionById(sessionId, cwd);
|
|
672
|
-
const
|
|
673
|
-
|
|
674
|
-
// Preserve user-set name across re-saves. saveSession is called every turn
|
|
675
|
-
// and re-derives `title` from the latest first-user-message; `name` is the
|
|
676
|
-
// opposite — set explicitly via /rename and must NOT be clobbered here.
|
|
1069
|
+
const title = deriveSessionTitle(messages, existingSession?.metadata.title);
|
|
1070
|
+
// User-set names remain independently controlled by /rename.
|
|
677
1071
|
const session = {
|
|
678
1072
|
schemaVersion: SESSION_SCHEMA_VERSION,
|
|
1073
|
+
evidence: runtimeState?.evidence ?? existingSession?.evidence,
|
|
679
1074
|
metadata: {
|
|
680
1075
|
id: sessionId,
|
|
681
1076
|
projectHash: storage.projectHash,
|
|
@@ -690,12 +1085,15 @@ export async function saveSession(sessionId, cwd, messages, model, provider, mux
|
|
|
690
1085
|
...(existingSession?.metadata.name ? { name: existingSession.metadata.name } : {}),
|
|
691
1086
|
...buildRuntimeMetadata(runtimeState, existingSession),
|
|
692
1087
|
},
|
|
693
|
-
|
|
1088
|
+
diagnostics: mergeSessionDiagnostics(runtimeState, existingSession, sessionPath),
|
|
1089
|
+
usage: mergeSessionUsage(runtimeState, existingSession, sessionPath),
|
|
1090
|
+
task_handoff: undefined,
|
|
1091
|
+
messages: stripEphemeralMessages(messages),
|
|
694
1092
|
mux_state: muxState,
|
|
695
1093
|
pane_specs: paneSpecs,
|
|
696
1094
|
hosted_settings: hostedSettings ?? existingSession?.hosted_settings,
|
|
697
1095
|
};
|
|
698
|
-
await
|
|
1096
|
+
await writeSessionWithMedia(sessionPath, session, runtimeState, existingSession, signal);
|
|
699
1097
|
const muxStatePath = getMuxStateFilePath(storage, sessionId);
|
|
700
1098
|
if (muxStatePath) {
|
|
701
1099
|
if (muxState) {
|
|
@@ -713,11 +1111,39 @@ export async function saveSession(sessionId, cwd, messages, model, provider, mux
|
|
|
713
1111
|
export async function loadSession(projectHash, sessionId) {
|
|
714
1112
|
return loadSessionFromStorage(makeGlobalStorage(projectHash), sessionId);
|
|
715
1113
|
}
|
|
716
|
-
/**
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
1114
|
+
/** Retain explicit file loads only when the path belongs to managed storage. */
|
|
1115
|
+
async function retainManagedSessionFile(path) {
|
|
1116
|
+
const absolute = resolve(path);
|
|
1117
|
+
const candidates = [
|
|
1118
|
+
{ root: getGlobalSessionsRoot(), colony: false },
|
|
1119
|
+
{ root: join(getGlobalColonyRoot(), 'worktrees'), colony: true },
|
|
1120
|
+
];
|
|
1121
|
+
for (const { root, colony } of candidates) {
|
|
1122
|
+
const parts = relative(resolve(root), absolute).split(sep);
|
|
1123
|
+
if (parts[0] === '..' || (colony && parts[1] !== 'sessions'))
|
|
1124
|
+
continue;
|
|
1125
|
+
const prefix = colony ? 2 : 1;
|
|
1126
|
+
const storage = join(root, ...parts.slice(0, prefix));
|
|
1127
|
+
if (parts.length === prefix + 2 && parts[prefix + 1] === 'session.json') {
|
|
1128
|
+
await retainSessionForProcess(storage, parts[prefix]);
|
|
1129
|
+
return;
|
|
1130
|
+
}
|
|
1131
|
+
if (parts.length === prefix + 1 && parts[prefix].endsWith('.json')) {
|
|
1132
|
+
await retainSessionForProcess(storage, parts[prefix].slice(0, -5));
|
|
1133
|
+
return;
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
const storage = basename(absolute) === 'session.json' ? dirname(dirname(absolute)) : dirname(absolute);
|
|
1137
|
+
if (basename(storage) === 'sessions' && basename(dirname(storage)) === '.colony'
|
|
1138
|
+
&& (basename(absolute) === 'session.json' || absolute.endsWith('.json'))) {
|
|
1139
|
+
const id = basename(absolute) === 'session.json' ? basename(dirname(absolute)) : basename(absolute, '.json');
|
|
1140
|
+
await retainSessionForProcess(makeGlobalColonyStorage(getProjectHash(dirname(dirname(storage)))).sessionDir, id);
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
/** Load a managed session or an arbitrary file owned by an embedder. */
|
|
1144
|
+
export async function loadSessionFromFile(path, retain = true) {
|
|
1145
|
+
if (retain && existsSync(path))
|
|
1146
|
+
await retainManagedSessionFile(path);
|
|
721
1147
|
let content;
|
|
722
1148
|
try {
|
|
723
1149
|
content = await readFile(path, 'utf-8');
|
|
@@ -743,15 +1169,22 @@ export async function loadSessionFromFile(path) {
|
|
|
743
1169
|
if (declaredVersion > SESSION_SCHEMA_VERSION) {
|
|
744
1170
|
throw new SessionLoadError(`Session file ${path} declares schemaVersion=${declaredVersion}; this ZTC build only understands up to v${SESSION_SCHEMA_VERSION}`, 'schema-mismatch', path);
|
|
745
1171
|
}
|
|
746
|
-
|
|
1172
|
+
try {
|
|
1173
|
+
return normalizeLoadedSession(parsed, path);
|
|
1174
|
+
}
|
|
1175
|
+
catch (error) {
|
|
1176
|
+
if (!(error instanceof TaskHandoffAdmissionError))
|
|
1177
|
+
throw error;
|
|
1178
|
+
const reason = error.reason === 'handoff_fingerprint_mismatch' ? 'handoff-source-mismatch'
|
|
1179
|
+
: error.reason === 'unsupported_handoff_version' ? 'handoff-schema-mismatch' : 'handoff-invalid';
|
|
1180
|
+
throw new SessionLoadError(error.message, reason, path, error);
|
|
1181
|
+
}
|
|
747
1182
|
}
|
|
748
1183
|
/**
|
|
749
1184
|
* Save a session to an arbitrary file path (bypasses the ~/.ztc/sessions tree).
|
|
750
1185
|
*/
|
|
751
|
-
export async function saveSessionToFile(path, sessionId, cwd, messages, model, provider, muxState, paneSpecs, runtimeState, hostedSettings) {
|
|
1186
|
+
export async function saveSessionToFile(path, sessionId, cwd, messages, model, provider, muxState, paneSpecs, runtimeState, hostedSettings, signal) {
|
|
752
1187
|
const projectHash = getProjectHash(cwd);
|
|
753
|
-
const firstUserMsg = messages.find(m => m.role === 'user');
|
|
754
|
-
const title = firstUserMsg?.content?.slice(0, 50) || 'Untitled session';
|
|
755
1188
|
let createdAt = new Date().toISOString();
|
|
756
1189
|
let preservedName;
|
|
757
1190
|
let existingSession = null;
|
|
@@ -765,12 +1198,16 @@ export async function saveSessionToFile(path, sessionId, cwd, messages, model, p
|
|
|
765
1198
|
preservedName = existingSession.metadata.name;
|
|
766
1199
|
}
|
|
767
1200
|
}
|
|
768
|
-
catch {
|
|
1201
|
+
catch (error) {
|
|
1202
|
+
if (error instanceof SessionLoadError && (error.reason.startsWith('handoff-') || error.reason === 'diagnostics-invalid' || error.reason === 'usage-invalid' || error.reason === 'board-binding-invalid'))
|
|
1203
|
+
throw error;
|
|
769
1204
|
// Existing file is broken; proceed with fresh createdAt.
|
|
770
1205
|
}
|
|
771
1206
|
}
|
|
1207
|
+
const title = deriveSessionTitle(messages, existingSession?.metadata.title);
|
|
772
1208
|
const session = {
|
|
773
1209
|
schemaVersion: SESSION_SCHEMA_VERSION,
|
|
1210
|
+
evidence: runtimeState?.evidence ?? existingSession?.evidence,
|
|
774
1211
|
metadata: {
|
|
775
1212
|
id: sessionId,
|
|
776
1213
|
projectHash,
|
|
@@ -785,7 +1222,10 @@ export async function saveSessionToFile(path, sessionId, cwd, messages, model, p
|
|
|
785
1222
|
...(preservedName ? { name: preservedName } : {}),
|
|
786
1223
|
...buildRuntimeMetadata(runtimeState, existingSession),
|
|
787
1224
|
},
|
|
788
|
-
|
|
1225
|
+
diagnostics: mergeSessionDiagnostics(runtimeState, existingSession, path),
|
|
1226
|
+
usage: mergeSessionUsage(runtimeState, existingSession, path),
|
|
1227
|
+
task_handoff: undefined,
|
|
1228
|
+
messages: stripEphemeralMessages(messages),
|
|
789
1229
|
mux_state: muxState,
|
|
790
1230
|
pane_specs: paneSpecs,
|
|
791
1231
|
hosted_settings: hostedSettings ?? existingSession?.hosted_settings,
|
|
@@ -794,7 +1234,7 @@ export async function saveSessionToFile(path, sessionId, cwd, messages, model, p
|
|
|
794
1234
|
if (dir && dir !== '.' && !existsSync(dir)) {
|
|
795
1235
|
await mkdir(dir, { recursive: true });
|
|
796
1236
|
}
|
|
797
|
-
await
|
|
1237
|
+
await writeSessionWithMedia(path, session, runtimeState, existingSession, signal);
|
|
798
1238
|
return path;
|
|
799
1239
|
}
|
|
800
1240
|
export async function saveMuxStateOnly(sessionId, cwd, muxState) {
|
|
@@ -802,6 +1242,7 @@ export async function saveMuxStateOnly(sessionId, cwd, muxState) {
|
|
|
802
1242
|
await ensureSessionStorage(storage);
|
|
803
1243
|
const muxStatePath = getMuxStateFilePath(storage, sessionId);
|
|
804
1244
|
if (muxStatePath) {
|
|
1245
|
+
await retainSessionForProcess(getSessionCoordinationDirectory(storage), sessionId);
|
|
805
1246
|
await writeFileAtomic(muxStatePath, JSON.stringify(muxState, null, 2));
|
|
806
1247
|
return true;
|
|
807
1248
|
}
|
|
@@ -894,7 +1335,7 @@ export async function loadSessionByIdOrName(idOrName, cwd) {
|
|
|
894
1335
|
if (head.kind === 'excluded')
|
|
895
1336
|
return null;
|
|
896
1337
|
if (head.kind === 'unknown') {
|
|
897
|
-
const session = await loadSessionFromStorage(entry.storage, entry.sessionId);
|
|
1338
|
+
const session = await loadSessionFromStorage(entry.storage, entry.sessionId, false);
|
|
898
1339
|
return session?.metadata.name === idOrName ? entry : null;
|
|
899
1340
|
}
|
|
900
1341
|
const name = head.kind === 'summary' ? head.summary.name : head.name;
|
|
@@ -952,6 +1393,7 @@ export async function setSessionName(sessionId, cwd, name, defaults) {
|
|
|
952
1393
|
metadata: {
|
|
953
1394
|
...session.metadata,
|
|
954
1395
|
updatedAt: new Date().toISOString(),
|
|
1396
|
+
labels: { ...session.metadata.labels, updated_at_ms: Math.max(Date.now(), (session.metadata.labels?.updated_at_ms ?? 0) + 1) },
|
|
955
1397
|
...(trimmed ? { name: trimmed } : { name: undefined }),
|
|
956
1398
|
},
|
|
957
1399
|
};
|
|
@@ -963,6 +1405,25 @@ export async function setSessionName(sessionId, cwd, name, defaults) {
|
|
|
963
1405
|
await writeSessionAtomic(path, updated);
|
|
964
1406
|
return updated;
|
|
965
1407
|
}
|
|
1408
|
+
/** Atomically persist label choices; metadata.name remains the resume-name owner. */
|
|
1409
|
+
export async function setSessionLabels(sessionId, cwd, labels, defaults) {
|
|
1410
|
+
const storage = getStorageCandidates(cwd)[0];
|
|
1411
|
+
let session = await loadSessionFromStorage(storage, sessionId);
|
|
1412
|
+
if (!session) {
|
|
1413
|
+
await saveSession(sessionId, cwd, [], defaults.model, defaults.provider);
|
|
1414
|
+
session = await loadSessionFromStorage(storage, sessionId);
|
|
1415
|
+
}
|
|
1416
|
+
if (!session)
|
|
1417
|
+
throw new Error(`Unable to create session ${sessionId}`);
|
|
1418
|
+
const { manual_title, ...preferences } = labels;
|
|
1419
|
+
const updated = { ...session, metadata: { ...session.metadata,
|
|
1420
|
+
labels: { ...preferences, updated_at_ms: Math.max(Date.now(), (preferences.updated_at_ms ?? 0) + 1) }, name: manual_title || undefined, updatedAt: new Date().toISOString(),
|
|
1421
|
+
} };
|
|
1422
|
+
if (!manual_title)
|
|
1423
|
+
delete updated.metadata.name;
|
|
1424
|
+
await writeSessionAtomic(getSessionJsonPath(storage, sessionId), updated);
|
|
1425
|
+
return updated;
|
|
1426
|
+
}
|
|
966
1427
|
/**
|
|
967
1428
|
* List all sessions for a project
|
|
968
1429
|
*/
|
|
@@ -990,11 +1451,11 @@ export async function listSessions(cwd) {
|
|
|
990
1451
|
*/
|
|
991
1452
|
async function readSessionSummaryForListing(entry) {
|
|
992
1453
|
const head = await readSessionHead(entry);
|
|
993
|
-
if (head.kind === 'summary')
|
|
1454
|
+
if (head.kind === 'summary' && !isCompactionTitle(head.summary.title))
|
|
994
1455
|
return head.summary;
|
|
995
1456
|
if (head.kind === 'excluded')
|
|
996
1457
|
return null;
|
|
997
|
-
const session = await loadSessionFromStorage(entry.storage, entry.sessionId);
|
|
1458
|
+
const session = await loadSessionFromStorage(entry.storage, entry.sessionId, false);
|
|
998
1459
|
if (!session)
|
|
999
1460
|
return null;
|
|
1000
1461
|
return {
|
|
@@ -1002,6 +1463,7 @@ async function readSessionSummaryForListing(entry) {
|
|
|
1002
1463
|
cwd: session.metadata.cwd,
|
|
1003
1464
|
title: session.metadata.title || 'Untitled',
|
|
1004
1465
|
...(session.metadata.name ? { name: session.metadata.name } : {}),
|
|
1466
|
+
...(session.metadata.labels ? { labels: session.metadata.labels } : {}),
|
|
1005
1467
|
messageCount: session.metadata.messageCount,
|
|
1006
1468
|
updatedAt: session.metadata.updatedAt,
|
|
1007
1469
|
};
|
|
@@ -1155,17 +1617,60 @@ export async function listAllSessions(options = {}) {
|
|
|
1155
1617
|
}
|
|
1156
1618
|
return collectCatalogEntries(storages, options);
|
|
1157
1619
|
}
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1620
|
+
/** Protect the current session even before its first save. */
|
|
1621
|
+
export async function retainCurrentSession(cwd, sessionId) {
|
|
1622
|
+
const storage = getStorageCandidates(cwd)[0];
|
|
1623
|
+
await retainSessionForProcess(getSessionCoordinationDirectory(storage), sessionId);
|
|
1624
|
+
}
|
|
1625
|
+
/** Opt-in retention of unowned, unnamed sessions; null disables pruning. */
|
|
1626
|
+
export async function cleanupOldSessions(cwd, keepCount = null) {
|
|
1627
|
+
if (keepCount === null)
|
|
1161
1628
|
return 0;
|
|
1629
|
+
if (!Number.isSafeInteger(keepCount) || keepCount < 0) {
|
|
1630
|
+
throw new RangeError('Session retention count must be a non-negative safe integer or null.');
|
|
1162
1631
|
}
|
|
1163
|
-
const
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
if (
|
|
1167
|
-
|
|
1632
|
+
const storages = getStorageCandidates(cwd);
|
|
1633
|
+
// Publish barriers in every visible storage before listing or selecting IDs.
|
|
1634
|
+
const protectedCleanup = async (index) => {
|
|
1635
|
+
if (index < storages.length) {
|
|
1636
|
+
return withSessionCleanupBarrier(getSessionCoordinationDirectory(storages[index]), () => protectedCleanup(index + 1));
|
|
1168
1637
|
}
|
|
1169
|
-
|
|
1170
|
-
|
|
1638
|
+
const owned = new Set();
|
|
1639
|
+
for (const storage of storages) {
|
|
1640
|
+
for (const id of await retainedSessionIds(getSessionCoordinationDirectory(storage)))
|
|
1641
|
+
owned.add(id);
|
|
1642
|
+
}
|
|
1643
|
+
const sessions = await listSessions(cwd);
|
|
1644
|
+
const candidates = sessions.filter(session => !session.name && !owned.has(session.id));
|
|
1645
|
+
let deleted = 0;
|
|
1646
|
+
for (const session of candidates.slice(keepCount)) {
|
|
1647
|
+
// An owner may publish its pin while waiting on our barrier. Recheck it
|
|
1648
|
+
// and the name immediately before pruning, retaining uncertainty.
|
|
1649
|
+
let protectedNow = false;
|
|
1650
|
+
for (const storage of storages) {
|
|
1651
|
+
if ((await retainedSessionIds(getSessionCoordinationDirectory(storage))).has(session.id))
|
|
1652
|
+
protectedNow = true;
|
|
1653
|
+
for (const path of [getSessionJsonPath(storage, session.id), getLegacyFlatPath(storage, session.id)]) {
|
|
1654
|
+
let currentStat;
|
|
1655
|
+
try {
|
|
1656
|
+
currentStat = await stat(path);
|
|
1657
|
+
}
|
|
1658
|
+
catch (error) {
|
|
1659
|
+
if (error.code === 'ENOENT')
|
|
1660
|
+
continue;
|
|
1661
|
+
throw error;
|
|
1662
|
+
}
|
|
1663
|
+
const current = await readSessionSummaryForListing({
|
|
1664
|
+
storage, sessionId: session.id, path, mtimeMs: currentStat.mtimeMs, size: currentStat.size,
|
|
1665
|
+
});
|
|
1666
|
+
if (!current || current.name)
|
|
1667
|
+
protectedNow = true;
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1670
|
+
if (!protectedNow && await deleteSession(cwd, session.id))
|
|
1671
|
+
deleted++;
|
|
1672
|
+
}
|
|
1673
|
+
return deleted;
|
|
1674
|
+
};
|
|
1675
|
+
return protectedCleanup(0);
|
|
1171
1676
|
}
|