@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
|
@@ -0,0 +1,1247 @@
|
|
|
1
|
+
import cp from 'node:child_process';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import { createWriteStream } from 'node:fs';
|
|
4
|
+
import { access, mkdir, readFile, rm, writeFile } from 'node:fs/promises';
|
|
5
|
+
import { createServer as createHttpServer } from 'node:http';
|
|
6
|
+
import { syncBuiltinESMExports } from 'node:module';
|
|
7
|
+
import net from 'node:net';
|
|
8
|
+
import os from 'node:os';
|
|
9
|
+
import { basename, join, resolve } from 'node:path';
|
|
10
|
+
import { performance } from 'node:perf_hooks';
|
|
11
|
+
import { promisify } from 'node:util';
|
|
12
|
+
export class NativeTerminalBenchmarkError extends Error {
|
|
13
|
+
code;
|
|
14
|
+
constructor(code, message) {
|
|
15
|
+
super(message);
|
|
16
|
+
this.code = code;
|
|
17
|
+
this.name = 'NativeTerminalBenchmarkError';
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function validateInitialGeometry(geometry) {
|
|
21
|
+
if (geometry && (!Number.isFinite(geometry.width) || !Number.isFinite(geometry.height)
|
|
22
|
+
|| geometry.width < 520 || geometry.width > 8192 || geometry.height < 320 || geometry.height > 8192)) {
|
|
23
|
+
throw new NativeTerminalBenchmarkError('INVALID_OPTIONS', 'Initial geometry must be finite content points within 520..8192 by 320..8192');
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function initialGeometryArgs(geometry) {
|
|
27
|
+
return geometry ? ['--initial-size', `${geometry.width}x${geometry.height}`] : [];
|
|
28
|
+
}
|
|
29
|
+
/** Milliseconds in, milliseconds out. Empty observations are unavailable. */
|
|
30
|
+
export function summarizeNativeSamples(samples) {
|
|
31
|
+
if (samples.some(value => !Number.isFinite(value) || value < 0)) {
|
|
32
|
+
throw new RangeError('Timing samples must be finite nonnegative numbers');
|
|
33
|
+
}
|
|
34
|
+
if (samples.length === 0)
|
|
35
|
+
return { count: 0, mean: null, p50: null, p95: null, max: null };
|
|
36
|
+
const sorted = [...samples].sort((a, b) => a - b);
|
|
37
|
+
return { count: sorted.length, mean: sorted.reduce((sum, value) => sum + value, 0) / sorted.length,
|
|
38
|
+
p50: sorted[Math.ceil(sorted.length * 0.5) - 1], p95: sorted[Math.ceil(sorted.length * 0.95) - 1], max: sorted[sorted.length - 1] };
|
|
39
|
+
}
|
|
40
|
+
const execFile = promisify(cp.execFile);
|
|
41
|
+
const pause = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
|
42
|
+
async function within(promise, ms) {
|
|
43
|
+
let timer;
|
|
44
|
+
try {
|
|
45
|
+
return await Promise.race([promise, new Promise(resolve => { timer = setTimeout(() => resolve(null), ms); })]);
|
|
46
|
+
}
|
|
47
|
+
finally {
|
|
48
|
+
if (timer)
|
|
49
|
+
clearTimeout(timer);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const sha256 = (bytes) => createHash('sha256').update(bytes).digest('hex');
|
|
53
|
+
/** Public peer marker vocabulary shared by the fixture producer, observer and workload manifest. */
|
|
54
|
+
export function nativeBenchmarkPeerMarkers(name) {
|
|
55
|
+
const identity = sha256(basename(name)).slice(0, 6);
|
|
56
|
+
return { ready: 'R' + identity, output: 'O' + identity, bulk: 'B' + identity };
|
|
57
|
+
}
|
|
58
|
+
function ownedEnvironment(root) {
|
|
59
|
+
return { HOME: join(root, 'home'), TMPDIR: join(root, 'tmp'), PATH: '/usr/bin:/bin:/opt/homebrew/bin', SHELL: '/bin/sh',
|
|
60
|
+
LANG: 'en_US.UTF-8', LC_ALL: 'en_US.UTF-8', ZTC_CONFIG_DIR: join(root, 'config'), ZTC_SESSIONS_DIR: join(root, 'sessions'),
|
|
61
|
+
ZTC_TODO_STORE: join(root, 'todos.json'), ZTC_DISABLE_AUTH_REFRESH: '1', ZTC_DISABLE_UPDATE: '1',
|
|
62
|
+
ZTC_EXTENSION_DISCOVERY: 'explicit', ZTC_SIM_DISABLE_EMBEDDINGS: '1', ZTC_AGENT_TOOL_ALLOWLIST: '[]', ZTC_PANE_ID: '0' };
|
|
63
|
+
}
|
|
64
|
+
async function prepareRoot(root) {
|
|
65
|
+
await mkdir(root, { recursive: true });
|
|
66
|
+
await Promise.all(['home', 'tmp', 'config', 'sessions', 'project'].map(name => mkdir(join(root, name), { recursive: true })));
|
|
67
|
+
await writeFile(join(root, 'settings.json'), JSON.stringify({ check_for_updates_on_startup: false }));
|
|
68
|
+
}
|
|
69
|
+
class Ipc {
|
|
70
|
+
socket;
|
|
71
|
+
log;
|
|
72
|
+
sequence = 0;
|
|
73
|
+
buffer = '';
|
|
74
|
+
events = [];
|
|
75
|
+
failure = null;
|
|
76
|
+
constructor(socket, log) {
|
|
77
|
+
this.socket = socket;
|
|
78
|
+
this.log = log;
|
|
79
|
+
socket.setEncoding('utf8');
|
|
80
|
+
socket.on('data', (chunk) => {
|
|
81
|
+
this.buffer += chunk;
|
|
82
|
+
if (this.buffer.length > 16 * 1024 * 1024) {
|
|
83
|
+
this.failure = new Error('Native IPC packet exceeds bounded measurement size');
|
|
84
|
+
socket.destroy();
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
let newline;
|
|
88
|
+
while ((newline = this.buffer.indexOf('\n')) >= 0) {
|
|
89
|
+
const line = this.buffer.slice(0, newline);
|
|
90
|
+
this.buffer = this.buffer.slice(newline + 1);
|
|
91
|
+
try {
|
|
92
|
+
const event = JSON.parse(line);
|
|
93
|
+
this.events.push(event);
|
|
94
|
+
this.log.write(JSON.stringify({ direction: 'event', atMs: performance.now(), event }) + '\n');
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
this.failure = new Error('Malformed native IPC JSON: ' + String(error));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
socket.on('error', error => { this.failure = error; });
|
|
102
|
+
socket.on('close', () => { this.failure ??= new Error('Native IPC closed'); });
|
|
103
|
+
}
|
|
104
|
+
send(command) {
|
|
105
|
+
if (this.failure)
|
|
106
|
+
throw this.failure;
|
|
107
|
+
this.log.write(JSON.stringify({ direction: 'command', atMs: performance.now(), command }) + '\n');
|
|
108
|
+
this.socket.write(JSON.stringify(command) + '\n');
|
|
109
|
+
}
|
|
110
|
+
async event(type, requestId, timeoutMs = 5000) {
|
|
111
|
+
const deadline = performance.now() + timeoutMs;
|
|
112
|
+
while (performance.now() < deadline) {
|
|
113
|
+
const index = this.events.findIndex(event => event.type === type && (requestId === undefined || event.request_id === requestId));
|
|
114
|
+
if (index >= 0)
|
|
115
|
+
return this.events.splice(index, 1)[0];
|
|
116
|
+
const errorIndex = this.events.findIndex(event => event.type === 'error' && (requestId === undefined || event.request_id === requestId));
|
|
117
|
+
if (errorIndex >= 0)
|
|
118
|
+
throw new Error('Native command rejected: ' + JSON.stringify(this.events.splice(errorIndex, 1)[0]));
|
|
119
|
+
if (this.failure)
|
|
120
|
+
throw this.failure;
|
|
121
|
+
await pause(1);
|
|
122
|
+
}
|
|
123
|
+
throw new NativeTerminalBenchmarkError('READINESS_FAILED', `Native ${type} did not reply within ${timeoutMs} ms`);
|
|
124
|
+
}
|
|
125
|
+
async request(type, resultType, fields = {}, timeoutMs) {
|
|
126
|
+
const id = 'benchmark-145-' + ++this.sequence;
|
|
127
|
+
this.send({ type, request_id: id, ...fields });
|
|
128
|
+
return this.event(resultType, id, timeoutMs);
|
|
129
|
+
}
|
|
130
|
+
close() { this.socket.destroy(); this.log.end(); }
|
|
131
|
+
}
|
|
132
|
+
class NativeSession {
|
|
133
|
+
root;
|
|
134
|
+
agent;
|
|
135
|
+
ipc;
|
|
136
|
+
ready;
|
|
137
|
+
socketReadyMs = 0;
|
|
138
|
+
child;
|
|
139
|
+
exit;
|
|
140
|
+
stderr = '';
|
|
141
|
+
started = performance.now();
|
|
142
|
+
ownedPids = new Set();
|
|
143
|
+
frameReadinessObservations = [];
|
|
144
|
+
peerMarkerOwners = new Map();
|
|
145
|
+
nativePid;
|
|
146
|
+
log;
|
|
147
|
+
constructor(root, binary, agent = false, geometry) {
|
|
148
|
+
this.root = root;
|
|
149
|
+
this.agent = agent;
|
|
150
|
+
this.log = createWriteStream(join(root, 'ipc.jsonl'));
|
|
151
|
+
const stderrLog = createWriteStream(join(root, 'process.stderr.log'));
|
|
152
|
+
const env = ownedEnvironment(root);
|
|
153
|
+
const args = agent ? ['--import', 'tsx', resolve('scripts/benchmark-zterm.ts'), '--owned-agent-runtime', root, binary, ...initialGeometryArgs(geometry)]
|
|
154
|
+
: ['--socket', join(root, 'native.sock'), '--settings', join(root, 'settings.json'), '--session', join(root, 'native-session.json'), '--title', 'ZTC 145 owned performance baseline', ...initialGeometryArgs(geometry)];
|
|
155
|
+
this.child = cp.spawn(agent ? process.execPath : binary, args, { cwd: agent ? process.cwd() : root, env,
|
|
156
|
+
stdio: ['ignore', 'ignore', 'pipe'] });
|
|
157
|
+
if (this.child.pid) {
|
|
158
|
+
this.ownedPids.add(this.child.pid);
|
|
159
|
+
if (!agent)
|
|
160
|
+
this.nativePid = this.child.pid;
|
|
161
|
+
}
|
|
162
|
+
this.child.stderr?.on('data', (chunk) => { stderrLog.write(chunk); this.stderr = (this.stderr + String(chunk)).slice(-8192); });
|
|
163
|
+
this.exit = new Promise((resolveExit, reject) => {
|
|
164
|
+
this.child.once('error', reject);
|
|
165
|
+
this.child.once('close', (code, signal) => { stderrLog.end(); resolveExit({ code, signal }); });
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
async connect() {
|
|
169
|
+
const socketPath = join(this.root, this.agent ? 'driver.sock' : 'native.sock');
|
|
170
|
+
let exited;
|
|
171
|
+
let spawnError;
|
|
172
|
+
void this.exit.then(value => { exited = value; }, error => { spawnError = error; });
|
|
173
|
+
const deadline = performance.now() + 15_000;
|
|
174
|
+
while (performance.now() < deadline) {
|
|
175
|
+
if (spawnError || exited) {
|
|
176
|
+
this.log.end();
|
|
177
|
+
throw new NativeTerminalBenchmarkError('STARTUP_FAILED', `Renderer exited ${exited?.code ?? 'spawn error'}: ${this.stderr || String(spawnError ?? exited?.signal)}`);
|
|
178
|
+
}
|
|
179
|
+
const socket = await new Promise(resolveSocket => {
|
|
180
|
+
const candidate = net.createConnection(socketPath);
|
|
181
|
+
candidate.once('connect', () => resolveSocket(candidate));
|
|
182
|
+
candidate.once('error', () => { candidate.destroy(); resolveSocket(null); });
|
|
183
|
+
});
|
|
184
|
+
if (socket) {
|
|
185
|
+
this.ipc = new Ipc(socket, this.log);
|
|
186
|
+
this.ready = await this.ipc.event('ready');
|
|
187
|
+
this.socketReadyMs = performance.now() - this.started;
|
|
188
|
+
if (this.agent) {
|
|
189
|
+
const info = await this.ipc.event('benchmark_agent_ready');
|
|
190
|
+
if (typeof info.native_pid === 'number') {
|
|
191
|
+
this.ownedPids.add(info.native_pid);
|
|
192
|
+
this.nativePid = info.native_pid;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
await pause(5);
|
|
198
|
+
}
|
|
199
|
+
throw new NativeTerminalBenchmarkError('STARTUP_FAILED', `Renderer startup deadline: ${this.stderr}`);
|
|
200
|
+
}
|
|
201
|
+
async createPeer(python, mode, name, extra = [], newWindow = false) {
|
|
202
|
+
const markers = nativeBenchmarkPeerMarkers(name);
|
|
203
|
+
const priorOwner = this.peerMarkerOwners.get(markers.ready);
|
|
204
|
+
if (priorOwner !== undefined && priorOwner !== name)
|
|
205
|
+
throw new NativeTerminalBenchmarkError('MARKER_COLLISION', 'Owned peer marker collision: ' + priorOwner + ' / ' + name);
|
|
206
|
+
this.peerMarkerOwners.set(markers.ready, name);
|
|
207
|
+
await writeFile(join(this.root, name + '.markers.json'), JSON.stringify(markers));
|
|
208
|
+
const event = await this.ipc.request('create_pane', 'pane_created', { command: python,
|
|
209
|
+
args: ['-I', '-u', join(this.root, 'peer.py'), mode, join(this.root, name), ...extra], cwd: join(this.root, 'project'),
|
|
210
|
+
title: 'Owned 145 ' + name, pane_type: 'process', new_window: newWindow, env: { LC_ALL: 'en_US.UTF-8' } });
|
|
211
|
+
const pane = Number(event.pane_id);
|
|
212
|
+
await this.waitText(pane, nativeBenchmarkPeerMarkers(name).ready);
|
|
213
|
+
const record = await readJson(join(this.root, name + '.ready.json'));
|
|
214
|
+
this.ownedPids.add(Number(record.pid));
|
|
215
|
+
return pane;
|
|
216
|
+
}
|
|
217
|
+
async read(pane) {
|
|
218
|
+
const event = await this.ipc.request('read_pane', 'pane_read', { pane_id: pane, viewport: false, lines: 100 });
|
|
219
|
+
if (event.error)
|
|
220
|
+
throw new Error(String(event.error));
|
|
221
|
+
return String(event.content);
|
|
222
|
+
}
|
|
223
|
+
async write(pane, text) {
|
|
224
|
+
const result = await this.ipc.request('write_pane', 'pane_write_result', { pane_id: pane, input: text, submit: false, control: false }, 15_000);
|
|
225
|
+
if (result.error || (!result.delivered && !result.queued))
|
|
226
|
+
throw new Error('Managed write failed: ' + JSON.stringify(result));
|
|
227
|
+
}
|
|
228
|
+
async waitText(pane, marker, timeoutMs = 15_000) {
|
|
229
|
+
const deadline = performance.now() + timeoutMs;
|
|
230
|
+
let text = '';
|
|
231
|
+
while (performance.now() < deadline) {
|
|
232
|
+
text = await this.read(pane);
|
|
233
|
+
if (text.includes(marker))
|
|
234
|
+
return text;
|
|
235
|
+
await pause(2);
|
|
236
|
+
}
|
|
237
|
+
throw new NativeTerminalBenchmarkError('READINESS_FAILED', 'Owned pane marker not observed: ' + marker + '; tail=' + text.slice(-512));
|
|
238
|
+
}
|
|
239
|
+
async layout() {
|
|
240
|
+
this.ipc.send({ type: 'get_session_state' });
|
|
241
|
+
const event = await this.ipc.event('session_state');
|
|
242
|
+
return event.state;
|
|
243
|
+
}
|
|
244
|
+
async metrics() {
|
|
245
|
+
const counter = (value) => typeof value === 'number' && Number.isSafeInteger(value) && value >= 0;
|
|
246
|
+
const positive = (value) => typeof value === 'number' && Number.isFinite(value) && value > 0;
|
|
247
|
+
const deadline = performance.now() + 5000;
|
|
248
|
+
while (true) {
|
|
249
|
+
const event = await this.ipc.request('get_terminal_metrics', 'terminal_metrics');
|
|
250
|
+
const values = event.frame_cpu_samples_ms;
|
|
251
|
+
const envelopeValid = Array.isArray(values) && values.length <= 120
|
|
252
|
+
&& values.every(value => typeof value === 'number' && Number.isFinite(value) && value >= 0)
|
|
253
|
+
&& counter(event.frame_count) && counter(event.frame_cpu_sample_count)
|
|
254
|
+
&& event.frame_cpu_sample_count <= event.frame_count && values.length <= event.frame_cpu_sample_count
|
|
255
|
+
&& counter(event.pane_count) && event.pane_count >= 1
|
|
256
|
+
&& counter(event.viewport_cols) && counter(event.viewport_rows)
|
|
257
|
+
&& positive(event.window_width_points) && positive(event.window_height_points) && positive(event.pixels_per_point)
|
|
258
|
+
&& typeof event.focused === 'boolean';
|
|
259
|
+
const beforeFirstFrame = envelopeValid && event.frame_count === 0 && event.frame_cpu_sample_count === 0
|
|
260
|
+
&& values.length === 0 && event.viewport_cols === 0 && event.viewport_rows === 0;
|
|
261
|
+
if (beforeFirstFrame) {
|
|
262
|
+
this.frameReadinessObservations.push({ observedAfterSpawnMs: performance.now() - this.started, metrics: event });
|
|
263
|
+
if (performance.now() >= deadline)
|
|
264
|
+
throw new NativeTerminalBenchmarkError('READINESS_FAILED', 'Native first frame/grid was not observed within 5000 ms');
|
|
265
|
+
await pause(10);
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
if (!envelopeValid || Number(event.frame_count) < 1 || Number(event.viewport_cols) < 1 || Number(event.viewport_rows) < 1) {
|
|
269
|
+
throw new NativeTerminalBenchmarkError('INVALID_METRICS', 'Malformed native frame metrics; no observation was recorded');
|
|
270
|
+
}
|
|
271
|
+
return event;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
async stop() {
|
|
275
|
+
const started = performance.now();
|
|
276
|
+
let fallback = false;
|
|
277
|
+
try {
|
|
278
|
+
this.ipc?.send({ type: this.agent ? 'benchmark_stop' : 'shutdown' });
|
|
279
|
+
}
|
|
280
|
+
catch { /* A startup failure can have no IPC connection. It remains a failed attempt. */ }
|
|
281
|
+
let result = await within(this.exit, 5000);
|
|
282
|
+
if (!result) {
|
|
283
|
+
fallback = true;
|
|
284
|
+
this.child.kill('SIGTERM');
|
|
285
|
+
result = await within(this.exit, 2000);
|
|
286
|
+
}
|
|
287
|
+
if (!result) {
|
|
288
|
+
this.child.kill('SIGKILL');
|
|
289
|
+
result = await this.exit;
|
|
290
|
+
}
|
|
291
|
+
this.ipc?.close();
|
|
292
|
+
this.log.end();
|
|
293
|
+
const after = [...this.ownedPids].map(pid => { let absent = false; try {
|
|
294
|
+
process.kill(pid, 0);
|
|
295
|
+
}
|
|
296
|
+
catch (error) {
|
|
297
|
+
absent = error.code === 'ESRCH';
|
|
298
|
+
} return { pid, absent }; });
|
|
299
|
+
let socketAbsent = false;
|
|
300
|
+
try {
|
|
301
|
+
await access(join(this.root, this.agent ? 'driver.sock' : 'native.sock'));
|
|
302
|
+
}
|
|
303
|
+
catch (error) {
|
|
304
|
+
socketAbsent = error.code === 'ENOENT';
|
|
305
|
+
}
|
|
306
|
+
let agentCleanup = {};
|
|
307
|
+
if (this.agent) {
|
|
308
|
+
try {
|
|
309
|
+
const recorded = JSON.parse(await readFile(join(this.root, 'agent-result.json'), 'utf8'));
|
|
310
|
+
agentCleanup = { rendererExit: recorded.rendererExit, nativeSocketAbsent: recorded.nativeSocketAbsent,
|
|
311
|
+
rendererSocketPath: recorded.rendererSocketPath };
|
|
312
|
+
}
|
|
313
|
+
catch (error) {
|
|
314
|
+
agentCleanup = { rendererExit: null, nativeSocketAbsent: false, cleanupRecordError: String(error).slice(0, 512) };
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
const receipt = { code: result.code, signal: result.signal, durationMs: performance.now() - started, fallback, pids: after, socketAbsent, ...agentCleanup };
|
|
318
|
+
await writeFile(join(this.root, 'cleanup.json'), JSON.stringify(receipt, null, 2));
|
|
319
|
+
return receipt;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
async function readJson(path) {
|
|
323
|
+
const deadline = performance.now() + 5000;
|
|
324
|
+
while (performance.now() < deadline) {
|
|
325
|
+
try {
|
|
326
|
+
return JSON.parse(await readFile(path, 'utf8'));
|
|
327
|
+
}
|
|
328
|
+
catch (error) {
|
|
329
|
+
if (!(error instanceof SyntaxError) && error.code !== 'ENOENT')
|
|
330
|
+
throw error;
|
|
331
|
+
await pause(5);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
throw new Error('Owned completion record not published: ' + path);
|
|
335
|
+
}
|
|
336
|
+
const PEER = String.raw `
|
|
337
|
+
import hashlib,json,os,select,sys,time,tty
|
|
338
|
+
mode,path=sys.argv[1:3];name=os.path.basename(path);markers=json.load(open(path+'.markers.json'));tty.setraw(0)
|
|
339
|
+
def record(suffix,value):
|
|
340
|
+
temp=path+suffix+'.tmp';open(temp,'w').write(json.dumps(value));os.replace(temp,path+suffix)
|
|
341
|
+
def line():
|
|
342
|
+
value=b''
|
|
343
|
+
while not value.endswith(b'\n'):
|
|
344
|
+
chunk=os.read(0,1)
|
|
345
|
+
if not chunk:raise EOFError()
|
|
346
|
+
value+=chunk
|
|
347
|
+
return value[:-1]
|
|
348
|
+
record('.ready.json',{'pid':os.getpid(),'mode':mode});os.write(1,(markers['ready']+'\r\n').encode())
|
|
349
|
+
try:
|
|
350
|
+
if mode=='echo':
|
|
351
|
+
received=[]
|
|
352
|
+
while True:
|
|
353
|
+
value=line()
|
|
354
|
+
if value==b'QUIT':break
|
|
355
|
+
received.append(value.hex());os.write(1,b'\r\nECHO_145_'+value+b'\r\n')
|
|
356
|
+
record('.done.json',{'pid':os.getpid(),'received':received})
|
|
357
|
+
elif mode=='output':
|
|
358
|
+
payload=open(sys.argv[3],'rb').read();cadence=float(sys.argv[4]);chunk_size=int(sys.argv[5]);line()
|
|
359
|
+
started=time.monotonic_ns();record('.active.json',{'pid':os.getpid(),'startedMonotonicNs':str(started)})
|
|
360
|
+
for offset in range(0,len(payload),chunk_size):
|
|
361
|
+
data=payload[offset:offset+chunk_size];written=0
|
|
362
|
+
while written<len(data):written+=os.write(1,data[written:])
|
|
363
|
+
if cadence:time.sleep(cadence)
|
|
364
|
+
os.write(1,('\r\n'+markers['output']+'\r\n').encode())
|
|
365
|
+
record('.done.json',{'pid':os.getpid(),'bytes':len(payload),'sha256':hashlib.sha256(payload).hexdigest(),'startedMonotonicNs':str(started),'endedMonotonicNs':str(time.monotonic_ns())})
|
|
366
|
+
line()
|
|
367
|
+
elif mode=='bulk':
|
|
368
|
+
size=int(sys.argv[3]);bracketed=sys.argv[4]=='true';os.write(1,b'\x1b[?2004h' if bracketed else b'\x1b[?2004l');data=b''
|
|
369
|
+
while len(data)<size:data+=os.read(0,min(65536,size-len(data)))
|
|
370
|
+
record('.done.json',{'pid':os.getpid(),'bytes':len(data),'sha256':hashlib.sha256(data).hexdigest(),'bracketed':bracketed})
|
|
371
|
+
os.write(1,('\r\n'+markers['bulk']+'\r\n').encode());line()
|
|
372
|
+
else:raise ValueError('unknown peer mode')
|
|
373
|
+
except EOFError:pass
|
|
374
|
+
`;
|
|
375
|
+
function payloadBytes(size, kind) {
|
|
376
|
+
const row = kind === 'ansi' ? '\x1b[38;2;12;34;56mOWNED145café\x1b[0m\n'
|
|
377
|
+
: kind === 'unicode' ? 'OWNED145 café é 中文 🦀\n' : 'OWNED145 ASCII output row\n';
|
|
378
|
+
const seed = Buffer.from(row);
|
|
379
|
+
const result = Buffer.alloc(size, 0x20);
|
|
380
|
+
for (let offset = 0; offset + seed.length <= size; offset += seed.length)
|
|
381
|
+
seed.copy(result, offset);
|
|
382
|
+
result[size - 1] = 10;
|
|
383
|
+
return result;
|
|
384
|
+
}
|
|
385
|
+
function bulkBytes(size) {
|
|
386
|
+
const seed = Buffer.from('café中文🦀');
|
|
387
|
+
const result = Buffer.alloc(size, 0x78);
|
|
388
|
+
for (let offset = 0; offset + seed.length <= size; offset += seed.length)
|
|
389
|
+
seed.copy(result, offset);
|
|
390
|
+
return result;
|
|
391
|
+
}
|
|
392
|
+
async function frameSamples(session, condition, active, smoke = false) {
|
|
393
|
+
const initial = await session.metrics();
|
|
394
|
+
const warmup = smoke ? 5 : 100;
|
|
395
|
+
const count = smoke ? 10 : 120;
|
|
396
|
+
const deadline = performance.now() + 15_000;
|
|
397
|
+
let start;
|
|
398
|
+
let end = initial;
|
|
399
|
+
let validLoad = true;
|
|
400
|
+
while (performance.now() < deadline) {
|
|
401
|
+
end = await session.metrics();
|
|
402
|
+
if (active && !await active())
|
|
403
|
+
validLoad = false;
|
|
404
|
+
if (!start && end.frame_count - initial.frame_count >= warmup)
|
|
405
|
+
start = end;
|
|
406
|
+
if (start && end.frame_cpu_sample_count - start.frame_cpu_sample_count >= count)
|
|
407
|
+
break;
|
|
408
|
+
await pause(50);
|
|
409
|
+
}
|
|
410
|
+
const sufficient = !!start && end.frame_cpu_sample_count - start.frame_cpu_sample_count >= count;
|
|
411
|
+
const values = sufficient ? end.frame_cpu_samples_ms.slice(-count) : [];
|
|
412
|
+
const layoutState = await session.layout();
|
|
413
|
+
const visiblePanes = nativeVisiblePanes(layoutState);
|
|
414
|
+
const expectedVisibleCount = condition === 'idle_one_pane' ? 1 : condition === 'four_panes' ? 4 : undefined;
|
|
415
|
+
const layoutQualified = visiblePanes !== null && (expectedVisibleCount === undefined || visiblePanes.length === expectedVisibleCount);
|
|
416
|
+
return { name: 'native_frame_cpu', condition, status: sufficient && validLoad && layoutQualified ? 'measured' : 'unqualified', layoutState, visiblePanes, expectedVisibleCount, layoutQualified,
|
|
417
|
+
initial, afterWarmup: start ?? null, final: end, samplesMs: values, summary: summarizeNativeSamples(values), producerActiveThroughout: validLoad,
|
|
418
|
+
minimumDistinctSamples: count, warmupFrames: warmup, cacheSequenceFirst: end.frame_cpu_sample_count - values.length + 1,
|
|
419
|
+
cacheSequenceLast: end.frame_cpu_sample_count, boundary: 'Previous frame CPU App/update/render; excludes vsync/GPU/presentation' };
|
|
420
|
+
}
|
|
421
|
+
async function echoSamples(session, pane, condition, count, active) {
|
|
422
|
+
const samples = [];
|
|
423
|
+
const payloads = [];
|
|
424
|
+
let validLoad = true;
|
|
425
|
+
const intervalStarted = performance.now();
|
|
426
|
+
for (let index = 0; index < count; index++) {
|
|
427
|
+
const targetTime = intervalStarted + index * 40;
|
|
428
|
+
await pause(Math.max(0, targetTime - performance.now()));
|
|
429
|
+
if (active && !await active())
|
|
430
|
+
validLoad = false;
|
|
431
|
+
const text = `${condition.replace(/[^a-z]/g, '')}-${String(index).padStart(4, '0')}`;
|
|
432
|
+
payloads.push(text);
|
|
433
|
+
const started = performance.now();
|
|
434
|
+
await session.write(pane, text + '\n');
|
|
435
|
+
await session.waitText(pane, 'ECHO_145_' + text);
|
|
436
|
+
samples.push(performance.now() - started);
|
|
437
|
+
if (active && !await active())
|
|
438
|
+
validLoad = false;
|
|
439
|
+
}
|
|
440
|
+
return { name: 'managed_raw_input_echo', condition, status: validLoad ? 'measured' : 'unqualified', samplesMs: samples,
|
|
441
|
+
summary: summarizeNativeSamples(samples), payloads, targetPane: pane, producerActiveThroughout: validLoad,
|
|
442
|
+
sampleIntervalMs: 40, readPollDelayMs: 2, boundary: 'Public write_pane to public read_pane echoed marker; includes IPC/polling; no physical keyboard' };
|
|
443
|
+
}
|
|
444
|
+
async function resources(pid) {
|
|
445
|
+
const { stdout } = await execFile('/bin/ps', ['-o', 'rss=,time=', '-p', String(pid)]);
|
|
446
|
+
const [rss, clock] = stdout.trim().split(/\s+/);
|
|
447
|
+
const pieces = clock.split(':').map(Number);
|
|
448
|
+
const cpuSeconds = pieces.reduce((total, value) => total * 60 + value, 0);
|
|
449
|
+
if (!Number.isFinite(Number(rss)) || !Number.isFinite(cpuSeconds))
|
|
450
|
+
throw new Error('Malformed owned PID resource observation');
|
|
451
|
+
return { rssBytes: Number(rss) * 1024, cpuSeconds };
|
|
452
|
+
}
|
|
453
|
+
async function resourceWindow(session, seconds, condition, active) {
|
|
454
|
+
const pid = session.nativePid;
|
|
455
|
+
const before = await resources(pid);
|
|
456
|
+
const started = performance.now();
|
|
457
|
+
const samples = [];
|
|
458
|
+
let producerActiveThroughout = true;
|
|
459
|
+
while (performance.now() - started < seconds * 1000) {
|
|
460
|
+
if (active && !await active())
|
|
461
|
+
producerActiveThroughout = false;
|
|
462
|
+
const value = await resources(pid);
|
|
463
|
+
samples.push({ atMs: performance.now() - started, ...value });
|
|
464
|
+
await pause(100);
|
|
465
|
+
}
|
|
466
|
+
const after = await resources(pid);
|
|
467
|
+
const elapsedMs = performance.now() - started;
|
|
468
|
+
const layoutState = await session.layout();
|
|
469
|
+
const visiblePanes = nativeVisiblePanes(layoutState);
|
|
470
|
+
const expectedVisibleCount = condition.startsWith('four_') ? 4 : 1;
|
|
471
|
+
const layoutQualified = visiblePanes !== null && visiblePanes.length === expectedVisibleCount;
|
|
472
|
+
return { name: 'native_resources', condition, status: producerActiveThroughout && layoutQualified ? 'measured' : 'unqualified', producerActiveThroughout, metrics: await session.metrics(), layout: layoutState, visiblePanes, expectedVisibleCount, layoutQualified, pid, before, after, elapsedMs, samples,
|
|
473
|
+
cpuPercentOneCore: (after.cpuSeconds - before.cpuSeconds) / (elapsedMs / 1000) * 100,
|
|
474
|
+
maximumRssMiB: Math.max(...samples.map(sample => Number(sample.rssBytes))) / 1048576, samplingIntervalMs: 100,
|
|
475
|
+
limit: 'Native PID only; sampled RSS peak lower bound; CPU excludes owned children/Node/GPU/energy' };
|
|
476
|
+
}
|
|
477
|
+
async function fileAbsent(path) {
|
|
478
|
+
try {
|
|
479
|
+
await access(path);
|
|
480
|
+
return false;
|
|
481
|
+
}
|
|
482
|
+
catch (error) {
|
|
483
|
+
if (error.code === 'ENOENT')
|
|
484
|
+
return true;
|
|
485
|
+
throw error;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
async function hostInventory() {
|
|
489
|
+
const query = async (command, args) => {
|
|
490
|
+
try {
|
|
491
|
+
return (await execFile(command, args)).stdout.trim();
|
|
492
|
+
}
|
|
493
|
+
catch {
|
|
494
|
+
return null;
|
|
495
|
+
}
|
|
496
|
+
};
|
|
497
|
+
return { platform: process.platform, architecture: process.arch, node: process.version,
|
|
498
|
+
model: await query('/usr/sbin/sysctl', ['-n', 'hw.model']), cpu: await query('/usr/sbin/sysctl', ['-n', 'machdep.cpu.brand_string']),
|
|
499
|
+
memoryBytes: os.totalmem(), cores: os.cpus().length, osVersion: await query('/usr/bin/sw_vers', ['-productVersion']),
|
|
500
|
+
osBuild: await query('/usr/bin/sw_vers', ['-buildVersion']), power: await query('/usr/bin/pmset', ['-g', 'batt']),
|
|
501
|
+
displayRefreshHz: null, displayPixels: null, thermalState: null,
|
|
502
|
+
exclusions: ['No device serial/UUID collected', 'Display refresh and occlusion are not inferred from grid or CPU samples'] };
|
|
503
|
+
}
|
|
504
|
+
/** Evaluate portable observed-data records; this verdict does not qualify the host/GUI fixture. */
|
|
505
|
+
export function evaluateNativeBudget(id, attempts, budget, smoke, cleanup) {
|
|
506
|
+
const unavailable = (reason) => ({ id, status: 'unqualified', reason });
|
|
507
|
+
if (smoke)
|
|
508
|
+
return unavailable('Smoke validates the harness, not the predeclared baseline counts');
|
|
509
|
+
const validNumber = (value) => typeof value === 'number' && Number.isFinite(value) && value >= 0;
|
|
510
|
+
const expectedCleanupNames = [...Array.from({ length: 12 }, (_, index) => 'startup-' + index), 'main', 'actual-agent'];
|
|
511
|
+
const normalCleanup = expectedCleanupNames.every(name => cleanup.some(row => row.name === name)) && cleanup.length === 14 && new Set(cleanup.map(row => row.name)).size === 14 && cleanup.every(row => row.code === 0 && row.signal === null && row.fallback === false && row.socketAbsent === true && validNumber(row.durationMs)
|
|
512
|
+
&& Array.isArray(row.pids) && row.pids.length > 0 && row.pids.every((pid) => Number.isSafeInteger(pid.pid) && Number(pid.pid) > 0 && pid.absent === true)
|
|
513
|
+
&& (row.name !== 'actual-agent' || (row.rendererExit !== null && typeof row.rendererExit === 'object'
|
|
514
|
+
&& row.rendererExit.code === 0 && row.rendererExit.signal === null && row.nativeSocketAbsent === true)));
|
|
515
|
+
if (!normalCleanup)
|
|
516
|
+
return unavailable('Mandatory normal process/socket cleanup is missing, unknown or unsuccessful');
|
|
517
|
+
const limits = budget.limits;
|
|
518
|
+
let relevant = attempts.filter(attempt => attempt.name === id);
|
|
519
|
+
let values = [];
|
|
520
|
+
const conditions = [];
|
|
521
|
+
const witnesses = ['echo_exact_bytes', 'paced_output_correctness', 'concurrent_bulk_correctness', 'actual_agent_correctness'];
|
|
522
|
+
const completeWitnesses = () => witnesses.every(name => attempts.filter(row => row.name === name && row.status === 'measured').length === 1);
|
|
523
|
+
if (id === 'normal_shutdown')
|
|
524
|
+
values = cleanup.map(row => Number(row.durationMs));
|
|
525
|
+
else if (id === 'native_rss_after_close') {
|
|
526
|
+
const row = relevant[0];
|
|
527
|
+
const reference = attempts.find(attempt => attempt.name === 'native_resources' && attempt.condition === 'one_usable_pane_plus_protocol_owner' && attempt.status === 'measured');
|
|
528
|
+
if (relevant.length !== 1 || !reference || !row || row.status !== 'measured' || row.cycles !== 5
|
|
529
|
+
|| row.beforeCondition !== 'one_usable_pane_plus_protocol_owner' || !validNumber(row.baselineSettledMs) || row.baselineSettledMs < 2000
|
|
530
|
+
|| !Array.isArray(row.samples) || row.samples.length !== 5)
|
|
531
|
+
return unavailable('First settled one-pane RSS reference or five close-cycle observations missing');
|
|
532
|
+
const referenceBytes = reference.after?.rssBytes;
|
|
533
|
+
const capturedBytes = row.before?.rssBytes;
|
|
534
|
+
if (!validNumber(referenceBytes) || !validNumber(capturedBytes) || capturedBytes !== referenceBytes
|
|
535
|
+
|| row.samples.some((sample) => !validNumber(sample.rssBytes)))
|
|
536
|
+
return unavailable('RSS reference or samples malformed');
|
|
537
|
+
const growth = row.samples.map((sample) => (Number(sample.rssBytes) - capturedBytes) / 1048576);
|
|
538
|
+
const maximumGrowthMiB = Math.max(...growth);
|
|
539
|
+
return { id, status: maximumGrowthMiB > limits.max_growth ? 'failed' : 'passed', growthMiB: growth, maximumGrowthMiB, limits };
|
|
540
|
+
}
|
|
541
|
+
else if (id.startsWith('native_rss_') || id === 'native_idle_cpu') {
|
|
542
|
+
const required = id === 'native_rss_four_panes' ? ['four_panes_including_owner0', 'four_panes_output']
|
|
543
|
+
: id === 'native_idle_cpu' ? ['one_usable_pane_plus_protocol_owner', 'four_panes_including_owner0'] : ['one_usable_pane_plus_protocol_owner'];
|
|
544
|
+
relevant = required.flatMap(condition => attempts.filter(row => row.name === 'native_resources' && row.condition === condition));
|
|
545
|
+
if (relevant.length !== required.length || required.some(condition => relevant.filter(row => row.condition === condition).length !== 1)
|
|
546
|
+
|| relevant.some(row => row.status !== 'measured' || !validNumber(row.elapsedMs) || row.elapsedMs < 10000))
|
|
547
|
+
return unavailable('Required complete idle/output resource intervals missing');
|
|
548
|
+
if (id === 'native_rss_four_panes' && (relevant.find(row => row.condition === 'four_panes_output').producerActiveThroughout !== true
|
|
549
|
+
|| !attempts.some(row => row.name === 'four_pane_output_correctness' && row.status === 'measured')))
|
|
550
|
+
return unavailable('Four-pane output producer evidence missing');
|
|
551
|
+
values = relevant.map(row => Number(row[id === 'native_idle_cpu' ? 'cpuPercentOneCore' : 'maximumRssMiB']));
|
|
552
|
+
}
|
|
553
|
+
else {
|
|
554
|
+
if (!relevant.length || relevant.some(row => row.status !== 'measured'))
|
|
555
|
+
return unavailable('Metric attempts missing or not measured');
|
|
556
|
+
if (typeof budget.sampleCount === 'number') {
|
|
557
|
+
const warmupName = id === 'warm_shell_ready' ? 'shell_warmup' : 'startup_warmup';
|
|
558
|
+
const warmups = attempts.filter(row => row.name === warmupName);
|
|
559
|
+
const expectedIndexes = Array.from({ length: Number(budget.sampleCount) }, (_, index) => index + Number(budget.warmupLaunches));
|
|
560
|
+
const measuredIndexes = relevant.map(row => row.index);
|
|
561
|
+
if (expectedIndexes.some(index => measuredIndexes.filter(value => value === index).length !== 1)
|
|
562
|
+
|| [0, 1].some(index => warmups.filter(row => row.index === index).length !== 1)
|
|
563
|
+
|| relevant.length !== budget.sampleCount || warmups.length !== budget.warmupLaunches
|
|
564
|
+
|| warmups.some(row => row.status !== 'measured' || !validNumber(row.durationMs))
|
|
565
|
+
|| relevant.some(row => !validNumber(row.durationMs)))
|
|
566
|
+
return unavailable('Declared measured startup/warmup counts or values missing');
|
|
567
|
+
values = relevant.map(row => Number(row.durationMs));
|
|
568
|
+
}
|
|
569
|
+
else if (Array.isArray(budget.conditions)) {
|
|
570
|
+
const expectedCount = Number(budget.sampleCountPerCondition ?? budget.minimumDistinctSamplesPerCondition);
|
|
571
|
+
if (!completeWitnesses())
|
|
572
|
+
return unavailable('Exact-byte producer correctness witnesses missing or failed');
|
|
573
|
+
for (const condition of budget.conditions) {
|
|
574
|
+
const rows = relevant.filter(row => row.condition === condition);
|
|
575
|
+
const row = rows[0];
|
|
576
|
+
if (rows.length !== 1 || !Array.isArray(row.samplesMs) || row.samplesMs.length !== expectedCount
|
|
577
|
+
|| row.samplesMs.some(value => !validNumber(value)) || row.producerActiveThroughout !== true)
|
|
578
|
+
return unavailable('Declared condition count/active producer/sample values missing');
|
|
579
|
+
if (id === 'native_frame_cpu') {
|
|
580
|
+
const initial = row.initial;
|
|
581
|
+
const warm = row.afterWarmup;
|
|
582
|
+
const end = row.final;
|
|
583
|
+
const validCounter = (value) => typeof value === 'number' && Number.isSafeInteger(value) && value >= 0;
|
|
584
|
+
if (!initial || !warm || !end || !validCounter(row.warmupFrames)
|
|
585
|
+
|| [initial, warm, end].some(snapshot => !validCounter(snapshot.frame_count) || !validCounter(snapshot.frame_cpu_sample_count))
|
|
586
|
+
|| !validCounter(row.cacheSequenceFirst) || !validCounter(row.cacheSequenceLast)
|
|
587
|
+
|| Number(row.warmupFrames) < Number(budget.warmupFramesPerCondition)
|
|
588
|
+
|| Number(warm.frame_count) - Number(initial.frame_count) < Number(budget.warmupFramesPerCondition)
|
|
589
|
+
|| Number(end.frame_cpu_sample_count) - Number(warm.frame_cpu_sample_count) < expectedCount
|
|
590
|
+
|| Number(row.cacheSequenceLast) - Number(row.cacheSequenceFirst) + 1 !== expectedCount
|
|
591
|
+
|| row.cacheSequenceLast !== end.frame_cpu_sample_count)
|
|
592
|
+
return unavailable('Full warmup or distinct frame observation span missing');
|
|
593
|
+
}
|
|
594
|
+
const samples = row.samplesMs;
|
|
595
|
+
values.push(...samples);
|
|
596
|
+
conditions.push({ condition, summary: summarizeNativeSamples(samples) });
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
else if (Array.isArray(budget.payloadClasses)) {
|
|
600
|
+
const layouts = budget.layouts;
|
|
601
|
+
if (['ascii', 'ansi', 'unicode'].some(kind => layouts.some(layout => relevant.filter(row => row.kind === kind && row.layout === layout).length !== 5))
|
|
602
|
+
|| relevant.some(row => !validNumber(row.durationMs) || row.layoutQualification !== true))
|
|
603
|
+
return unavailable('Full output payload/layout matrix missing or not observed');
|
|
604
|
+
values = relevant.map(row => Number(row.durationMs));
|
|
605
|
+
}
|
|
606
|
+
else if (Array.isArray(budget.bracketedPasteModes)) {
|
|
607
|
+
return unavailable('Per-mode/per-repetition200 concurrent sentinel matrix not measured by this runner');
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
if (!values.length || values.some(value => !validNumber(value)))
|
|
611
|
+
return unavailable('Finite metric values missing');
|
|
612
|
+
const summary = summarizeNativeSamples(values);
|
|
613
|
+
const summaries = conditions.length ? conditions.map(row => row.summary) : [summary];
|
|
614
|
+
const breached = summaries.some(row => (limits.max !== undefined && Number(row.max) > limits.max)
|
|
615
|
+
|| (limits.p95 !== undefined && Number(row.p95) > limits.p95)
|
|
616
|
+
|| (limits.mean !== undefined && Number(row.max) > limits.mean));
|
|
617
|
+
return { id, status: breached ? 'failed' : 'passed', summary, limits, ...(conditions.length ? { conditions } : {}) };
|
|
618
|
+
}
|
|
619
|
+
/** Actual native process/Unix IPC measurements. All attempts and policy gaps are retained. */
|
|
620
|
+
export async function runNativeTerminalBenchmark(options) {
|
|
621
|
+
if (!options.binary.trim() || !options.outputDirectory.trim() || !options.budgetsPath.trim()) {
|
|
622
|
+
throw new NativeTerminalBenchmarkError('INVALID_OPTIONS', 'binary, output directory and budgets path must be nonempty');
|
|
623
|
+
}
|
|
624
|
+
validateInitialGeometry(options.initialGeometry);
|
|
625
|
+
const outputDirectory = resolve(options.outputDirectory);
|
|
626
|
+
const binary = resolve(options.binary);
|
|
627
|
+
const budgetBytes = await readFile(options.budgetsPath);
|
|
628
|
+
const budgets = JSON.parse(budgetBytes.toString());
|
|
629
|
+
if (budgets.schemaVersion !== 1 || !Array.isArray(budgets.budgets))
|
|
630
|
+
throw new NativeTerminalBenchmarkError('INVALID_OPTIONS', 'budgets must contain schemaVersion 1 and predeclared budgets');
|
|
631
|
+
const smoke = options.profile === 'smoke';
|
|
632
|
+
const python = options.python ?? '/opt/homebrew/bin/python3';
|
|
633
|
+
await mkdir(outputDirectory, { recursive: true });
|
|
634
|
+
const attempts = [];
|
|
635
|
+
const cleanup = [];
|
|
636
|
+
const sourceFiles = ['src/diagnostics/native_terminal_benchmark.ts', 'scripts/benchmark-zterm.ts', 'src/cli_ratatui.ts',
|
|
637
|
+
'src/ui/ratatui/process.ts', 'src/ui/ratatui/protocol.ts', 'packages/ztc-core/src/protocol.rs', 'packages/zterm/src/main.rs', 'packages/zterm/src/launch_geometry.rs', 'packages/zterm/Cargo.lock'];
|
|
638
|
+
const sourceHashes = {};
|
|
639
|
+
for (const path of sourceFiles)
|
|
640
|
+
sourceHashes[path] = sha256(await readFile(resolve(path)));
|
|
641
|
+
const payloadManifest = [];
|
|
642
|
+
for (const size of [1048576, 4194304])
|
|
643
|
+
for (const kind of ['ascii', 'ansi', 'unicode']) {
|
|
644
|
+
const data = payloadBytes(size, kind);
|
|
645
|
+
const path = join(outputDirectory, `output-${size}-${kind}.bin`);
|
|
646
|
+
await writeFile(path, data);
|
|
647
|
+
payloadManifest.push({ path, size, kind, sha256: sha256(data) });
|
|
648
|
+
}
|
|
649
|
+
for (const size of [65536, 1048576]) {
|
|
650
|
+
const data = bulkBytes(size);
|
|
651
|
+
const path = join(outputDirectory, `input-${size}.bin`);
|
|
652
|
+
await writeFile(path, data);
|
|
653
|
+
payloadManifest.push({ path, size, kind: 'input_unicode', sha256: sha256(data) });
|
|
654
|
+
}
|
|
655
|
+
const manifest = { budgetSha256: sha256(budgetBytes), binary, binarySha256: sha256(await readFile(binary)), sourceHashes,
|
|
656
|
+
initialGeometry: options.initialGeometry ?? null,
|
|
657
|
+
buildProfile: options.buildProfile ?? 'unknown', compilerCommand: options.compilerCommand ?? null,
|
|
658
|
+
profile: smoke ? 'smoke' : 'baseline', startupWarmups: smoke ? 0 : 2, startupSamples: smoke ? 1 : 10, echoCount: smoke ? 5 : 200,
|
|
659
|
+
echoCadenceMs: 40, outputRepeatsPerClassLayout: smoke ? 1 : 5, bulkRepeatsPerMode: smoke ? 1 : 5,
|
|
660
|
+
payloadManifest, outputLoad: { bytes: 4194304, chunkBytes: 16384, intervalMs: 62.5, nominalDurationSeconds: 16 },
|
|
661
|
+
concurrentBulk: { bytes: 1048576, payloadByte: 120, sha256: sha256(Buffer.alloc(1048576, 0x78)), chunkBytes: 2048, intervalAfterAckMs: 32 },
|
|
662
|
+
agentLoad: { actualApp: 'RatatuiApp', providerBoundary: 'owned loopback HTTP SSE', chunkText: 'OWNED145 STREAM café 中文\n', intervalMs: 40, maxChunks: 2000 },
|
|
663
|
+
peerMarkers: { byteLength: 7, identity: 'first6 lowercase SHA256 hex of UTF8 basename', stages: { ready: 'R', output: 'O', bulk: 'B' },
|
|
664
|
+
producer: 'Python reads the same owned name.markers.json emitted by nativeBenchmarkPeerMarkers', collisionPolicy: 'Reject different-name marker collisions within an owned native session' },
|
|
665
|
+
measurementVocabulary: 'The logical send_pane_input budget boundary uses existing wire write_pane{submit:false,control:false}',
|
|
666
|
+
emittedAt: new Date().toISOString() };
|
|
667
|
+
// Freeze policy, byte payloads and cadence BEFORE the first measured process launch.
|
|
668
|
+
await writeFile(join(outputDirectory, 'workload-manifest.json'), JSON.stringify(manifest, null, 2));
|
|
669
|
+
await writeFile(join(outputDirectory, 'budgets.json'), budgetBytes);
|
|
670
|
+
const report = { schemaVersion: 1, status: 'measured', outputDirectory, manifest,
|
|
671
|
+
budgets, host: await hostInventory(), attempts, cleanup, startedAt: new Date().toISOString(),
|
|
672
|
+
limits: ['Managed IPC is not physical keyboard/clipboard/input-to-photon', 'Previous-frame CPU is not GPU/display time',
|
|
673
|
+
options.initialGeometry ? 'Explicit pre-launch content geometry requested; per-launch native metrics retained; physical measurement contract remains unqualified' : 'Native default requested content size1120x760; policy1280x800 fixed geometry remains unqualified',
|
|
674
|
+
'Focus is observed; occlusion/display-refresh/thermal state unknown', 'No signed installed app/other OS/comparator or accessibility qualification'] };
|
|
675
|
+
let ordinal = 0;
|
|
676
|
+
const start = async (name, agent = false) => {
|
|
677
|
+
// Short Unix path remains below macOS sockaddr_un capacity independently of the artifact directory.
|
|
678
|
+
const root = join('/tmp', 'ztc145-' + process.pid + '-' + ++ordinal);
|
|
679
|
+
await prepareRoot(root);
|
|
680
|
+
await writeFile(join(root, 'peer.py'), PEER);
|
|
681
|
+
const session = new NativeSession(root, binary, agent, options.initialGeometry);
|
|
682
|
+
try {
|
|
683
|
+
await session.connect();
|
|
684
|
+
const observed = await session.metrics();
|
|
685
|
+
const requested = options.initialGeometry ?? null;
|
|
686
|
+
const matches = requested !== null && Math.abs(observed.window_width_points - requested.width) < 0.01
|
|
687
|
+
&& Math.abs(observed.window_height_points - requested.height) < 0.01 && observed.focused;
|
|
688
|
+
const geometry = { name: 'launch_geometry', condition: name, status: matches ? 'measured' : 'unqualified', requested, observed,
|
|
689
|
+
nativePid: session.nativePid, setup: 'native viewport before first frame', boundary: 'First usable native IPC metrics; not GPU presentation or physical measurement' };
|
|
690
|
+
await writeFile(join(root, 'launch-geometry.json'), JSON.stringify(geometry, null, 2));
|
|
691
|
+
attempts.push(geometry);
|
|
692
|
+
return session;
|
|
693
|
+
}
|
|
694
|
+
catch (error) {
|
|
695
|
+
attempts.push({ name: 'launch_geometry', condition: name, status: 'unqualified', requested: options.initialGeometry ?? null, observed: null, reason: String(error) });
|
|
696
|
+
const receipt = await session.stop();
|
|
697
|
+
cleanup.push({ name, ...receipt });
|
|
698
|
+
await copyArtifacts(root, join(outputDirectory, name));
|
|
699
|
+
await rm(root, { recursive: true, force: true });
|
|
700
|
+
throw error;
|
|
701
|
+
}
|
|
702
|
+
};
|
|
703
|
+
const finish = async (session, name) => {
|
|
704
|
+
if (session.frameReadinessObservations.length)
|
|
705
|
+
attempts.push({ name: 'native_frame_readiness', condition: name, status: 'unqualified',
|
|
706
|
+
observations: session.frameReadinessObservations, reason: 'Pre-first-frame zero counters/grid are startup diagnostics only; never timing/frame quality samples' });
|
|
707
|
+
const receipt = await session.stop();
|
|
708
|
+
cleanup.push({ name, root: session.root, ...receipt });
|
|
709
|
+
await copyArtifacts(session.root, join(outputDirectory, name));
|
|
710
|
+
await rm(session.root, { recursive: true, force: true });
|
|
711
|
+
cleanup.at(-1).rootRemoved = await fileAbsent(session.root);
|
|
712
|
+
if (receipt.code !== 0 || receipt.fallback || receipt.pids.some(value => !value.absent))
|
|
713
|
+
report.status = 'failed';
|
|
714
|
+
};
|
|
715
|
+
try {
|
|
716
|
+
for (let index = 0; index < (smoke ? 1 : 12); index++) {
|
|
717
|
+
const name = 'startup-' + index;
|
|
718
|
+
const session = await start(name);
|
|
719
|
+
try {
|
|
720
|
+
const pane = await session.createPeer(python, 'echo', 'startup-echo');
|
|
721
|
+
const elapsed = performance.now() - session.started;
|
|
722
|
+
const warmup = !smoke && index < 2;
|
|
723
|
+
attempts.push({ name: warmup ? 'startup_warmup' : 'warm_socket_startup', status: 'measured', durationMs: session.socketReadyMs, index, ready: session.ready });
|
|
724
|
+
attempts.push({ name: warmup ? 'shell_warmup' : 'warm_shell_ready', status: 'measured', durationMs: elapsed, index, pane,
|
|
725
|
+
boundary: 'Spawn to raw-child READY readback; includes child creation and IPC polling, not shell prompt/physicalpaint' });
|
|
726
|
+
const metrics = await session.metrics();
|
|
727
|
+
attempts.push({ name: 'first_native_metrics_observation', status: 'measured', index, metrics,
|
|
728
|
+
observedAfterSpawnMs: performance.now() - session.started, boundary: 'Observed final App update counter, not first GPU presentation' });
|
|
729
|
+
await session.write(pane, 'QUIT\n');
|
|
730
|
+
await readJson(join(session.root, 'startup-echo.done.json'));
|
|
731
|
+
}
|
|
732
|
+
finally {
|
|
733
|
+
await finish(session, name);
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
const session = await start('main');
|
|
737
|
+
try {
|
|
738
|
+
attempts.push(await awaitGeometry(session, options.awaitGeometry, outputDirectory, 'main'));
|
|
739
|
+
const echo = await session.createPeer(python, 'echo', 'echo');
|
|
740
|
+
session.ipc.send({ type: 'toggle_zoom', pane_id: echo });
|
|
741
|
+
attempts.push(...await Promise.all([echoSamples(session, echo, 'idle', smoke ? 5 : 200), frameSamples(session, 'idle_one_pane', undefined, smoke)]));
|
|
742
|
+
const firstSettledResources = await resourceWindow(session, smoke ? 0.5 : 10, 'one_usable_pane_plus_protocol_owner');
|
|
743
|
+
attempts.push(firstSettledResources);
|
|
744
|
+
const producer = await session.createPeer(python, 'output', 'paced-output', [join(outputDirectory, 'output-4194304-ascii.bin'), '0.0625', '16384']);
|
|
745
|
+
await session.write(producer, 'GO\n');
|
|
746
|
+
await readJson(join(session.root, 'paced-output.active.json'));
|
|
747
|
+
const outputActive = () => fileAbsent(join(session.root, 'paced-output.done.json'));
|
|
748
|
+
attempts.push(...await Promise.all([echoSamples(session, echo, 'bounded_other_pane_PTY_output', smoke ? 5 : 200, outputActive),
|
|
749
|
+
frameSamples(session, 'bounded_other_pane_PTY_output', outputActive, smoke)]));
|
|
750
|
+
await session.waitText(producer, nativeBenchmarkPeerMarkers('paced-output').output, 25_000);
|
|
751
|
+
await session.write(producer, 'QUIT\n');
|
|
752
|
+
const producerRecord = await readJson(join(session.root, 'paced-output.done.json'));
|
|
753
|
+
attempts.push({ name: 'paced_output_correctness', status: producerRecord.sha256 === sha256(payloadBytes(4194304, 'ascii')) && producerRecord.bytes === 4194304 ? 'measured' : 'failed', record: producerRecord });
|
|
754
|
+
session.ipc.send({ type: 'close_pane', pane_id: producer });
|
|
755
|
+
// Output matrix is bounded and predeclared; inactive tab is a real new window then focus returns to the raw foreground.
|
|
756
|
+
for (const size of (smoke ? [1048576] : [1048576, 4194304]))
|
|
757
|
+
for (const kind of (smoke ? ['ascii'] : ['ascii', 'ansi', 'unicode'])) {
|
|
758
|
+
for (const layout of (smoke ? ['one_pane'] : ['one_pane', 'two_split_panes', 'inactive_tab']))
|
|
759
|
+
for (let repeat = 0; repeat < (smoke ? 1 : 5); repeat++) {
|
|
760
|
+
const key = `ingest-${size}-${kind}-${layout}-${repeat}`;
|
|
761
|
+
const pane = await session.createPeer(python, 'output', key, [join(outputDirectory, `output-${size}-${kind}.bin`), '0', '16384'], layout === 'inactive_tab');
|
|
762
|
+
if (layout === 'inactive_tab')
|
|
763
|
+
session.ipc.send({ type: 'focus_pane', pane_id: echo });
|
|
764
|
+
if (layout === 'one_pane')
|
|
765
|
+
session.ipc.send({ type: 'toggle_zoom', pane_id: pane });
|
|
766
|
+
const layoutState = await session.layout();
|
|
767
|
+
const layoutQualification = qualifyNativeOutputLayout(layoutState, pane, layout);
|
|
768
|
+
const started = performance.now();
|
|
769
|
+
await session.write(pane, 'GO\n');
|
|
770
|
+
await session.waitText(pane, nativeBenchmarkPeerMarkers(key).output);
|
|
771
|
+
const durationMs = performance.now() - started;
|
|
772
|
+
const record = await readJson(join(session.root, key + '.done.json'));
|
|
773
|
+
attempts.push({ name: size === 1048576 ? 'output_ingest_1mib' : 'output_ingest_4mib', kind, layout, repeat, durationMs,
|
|
774
|
+
status: record.bytes !== size || record.sha256 !== sha256(payloadBytes(size, kind)) ? 'failed' : layoutQualification ? 'measured' : 'unqualified', record, layoutState, layoutQualification,
|
|
775
|
+
boundary: 'Parent GO write to native last-marker readback upper bound; includes child command scheduling; exact child output count/hash, not clipped history' });
|
|
776
|
+
await session.write(pane, 'QUIT\n');
|
|
777
|
+
session.ipc.send({ type: 'close_pane', pane_id: pane });
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
for (const size of (smoke ? [65536] : [65536, 1048576]))
|
|
781
|
+
for (const bracketed of [false, true])
|
|
782
|
+
for (let repeat = 0; repeat < (smoke ? 1 : 5); repeat++) {
|
|
783
|
+
const key = `bulk-${size}-${bracketed}-${repeat}`;
|
|
784
|
+
const pane = await session.createPeer(python, 'bulk', key, [String(size), String(bracketed)]);
|
|
785
|
+
const data = bulkBytes(size);
|
|
786
|
+
const started = performance.now();
|
|
787
|
+
await session.write(pane, data.toString());
|
|
788
|
+
const record = await readJson(join(session.root, key + '.done.json'));
|
|
789
|
+
const durationMs = performance.now() - started;
|
|
790
|
+
attempts.push({ name: size === 65536 ? 'bulk_raw_input_64kib' : 'bulk_raw_input_1mib', durationMs, bracketed, repeat,
|
|
791
|
+
status: record.bytes === size && record.sha256 === sha256(data) ? 'measured' : 'failed', record,
|
|
792
|
+
boundary: 'Public literal write_pane to child exact-byte completion; bracketed mode toggled, no OS clipboard' });
|
|
793
|
+
await session.write(pane, 'QUIT\n');
|
|
794
|
+
session.ipc.send({ type: 'close_pane', pane_id: pane });
|
|
795
|
+
}
|
|
796
|
+
// Sustained bulk-input interval uses paced chunks so load stays active during all echo/frame samples.
|
|
797
|
+
const bulkSize = 1048576;
|
|
798
|
+
const bulkPane = await session.createPeer(python, 'bulk', 'paced-bulk', [String(bulkSize), 'false']);
|
|
799
|
+
const data = Buffer.alloc(bulkSize, 0x78);
|
|
800
|
+
let bulkActive = true;
|
|
801
|
+
const bulkStarted = performance.now();
|
|
802
|
+
const feeding = (async () => {
|
|
803
|
+
for (let offset = 0; offset < data.length; offset += 2048) {
|
|
804
|
+
await session.write(bulkPane, data.subarray(offset, offset + 2048).toString());
|
|
805
|
+
await pause(32);
|
|
806
|
+
}
|
|
807
|
+
bulkActive = false;
|
|
808
|
+
})();
|
|
809
|
+
// Chunk boundaries use ASCII for this concurrent load to avoid splitting a UTF8 codepoint across JS strings.
|
|
810
|
+
// The isolated bulk matrix above independently verifies exact UTF8 payloads.
|
|
811
|
+
attempts.push(...await Promise.all([echoSamples(session, echo, 'concurrent_bulk_input', smoke ? 5 : 200, async () => bulkActive && await fileAbsent(join(session.root, 'paced-bulk.done.json'))),
|
|
812
|
+
frameSamples(session, 'concurrent_bulk_input', async () => bulkActive && await fileAbsent(join(session.root, 'paced-bulk.done.json')), smoke)]));
|
|
813
|
+
await feeding;
|
|
814
|
+
const bulkRecord = await readJson(join(session.root, 'paced-bulk.done.json'));
|
|
815
|
+
attempts.push({ name: 'concurrent_bulk_correctness', status: bulkRecord.sha256 === sha256(data) ? 'measured' : 'failed', record: bulkRecord, producerStartedMs: bulkStarted });
|
|
816
|
+
await session.write(bulkPane, 'QUIT\n');
|
|
817
|
+
session.ipc.send({ type: 'close_pane', pane_id: bulkPane });
|
|
818
|
+
const extras = [await session.createPeer(python, 'echo', 'four-a'), await session.createPeer(python, 'echo', 'four-b')];
|
|
819
|
+
attempts.push(await frameSamples(session, 'four_panes', undefined, smoke));
|
|
820
|
+
attempts.push(await resourceWindow(session, smoke ? 0.5 : 10, 'four_panes_including_owner0'));
|
|
821
|
+
await session.write(extras[0], 'QUIT\n');
|
|
822
|
+
session.ipc.send({ type: 'close_pane', pane_id: extras[0] });
|
|
823
|
+
const fourOutput = await session.createPeer(python, 'output', 'four-output', [join(outputDirectory, 'output-4194304-ascii.bin'), '0.0625', '16384']);
|
|
824
|
+
await session.write(fourOutput, 'GO\n');
|
|
825
|
+
await readJson(join(session.root, 'four-output.active.json'));
|
|
826
|
+
attempts.push(await resourceWindow(session, smoke ? 0.5 : 10, 'four_panes_output', () => fileAbsent(join(session.root, 'four-output.done.json'))));
|
|
827
|
+
await session.waitText(fourOutput, nativeBenchmarkPeerMarkers('four-output').output, 25_000);
|
|
828
|
+
await session.write(fourOutput, 'QUIT\n');
|
|
829
|
+
session.ipc.send({ type: 'close_pane', pane_id: fourOutput });
|
|
830
|
+
const fourOutputRecord = await readJson(join(session.root, 'four-output.done.json'));
|
|
831
|
+
attempts.push({ name: 'four_pane_output_correctness', status: fourOutputRecord.bytes === 4194304 && fourOutputRecord.sha256 === sha256(payloadBytes(4194304, 'ascii')) ? 'measured' : 'failed', record: fourOutputRecord });
|
|
832
|
+
for (const pane of extras.slice(1)) {
|
|
833
|
+
await session.write(pane, 'QUIT\n');
|
|
834
|
+
session.ipc.send({ type: 'close_pane', pane_id: pane });
|
|
835
|
+
}
|
|
836
|
+
const beforeClose = firstSettledResources.after;
|
|
837
|
+
const cycleSamples = [];
|
|
838
|
+
for (let cycle = 0; cycle < (smoke ? 1 : 5); cycle++) {
|
|
839
|
+
const pane = await session.createPeer(python, 'output', 'cycle-' + cycle, [join(outputDirectory, 'output-1048576-ascii.bin'), '0', '16384']);
|
|
840
|
+
await session.write(pane, 'GO\n');
|
|
841
|
+
await session.waitText(pane, nativeBenchmarkPeerMarkers('cycle-' + cycle).output);
|
|
842
|
+
await session.write(pane, 'QUIT\n');
|
|
843
|
+
session.ipc.send({ type: 'close_pane', pane_id: pane });
|
|
844
|
+
await pause(smoke ? 100 : 2000);
|
|
845
|
+
cycleSamples.push({ cycle, ...await resources(session.nativePid) });
|
|
846
|
+
}
|
|
847
|
+
attempts.push({ name: 'native_rss_after_close', status: 'measured', before: beforeClose, samples: cycleSamples,
|
|
848
|
+
growthMiB: Math.max(...cycleSamples.map(sample => Number(sample.rssBytes) - beforeClose.rssBytes)) / 1048576, cycles: cycleSamples.length,
|
|
849
|
+
beforeCondition: firstSettledResources.condition, baselineSettledMs: firstSettledResources.elapsedMs,
|
|
850
|
+
baselineProvenance: { condition: firstSettledResources.condition, elapsedMs: firstSettledResources.elapsedMs, snapshot: firstSettledResources.after, layout: firstSettledResources.layout }, settleSeconds: smoke ? 0.1 : 2 });
|
|
851
|
+
await session.write(echo, 'QUIT\n');
|
|
852
|
+
const echoed = await readJson(join(session.root, 'echo.done.json'));
|
|
853
|
+
const expected = attempts.filter(attempt => attempt.name === 'managed_raw_input_echo').flatMap(attempt => attempt.payloads.map(text => Buffer.from(text).toString('hex')));
|
|
854
|
+
attempts.push({ name: 'echo_exact_bytes', status: JSON.stringify(echoed.received) === JSON.stringify(expected) ? 'measured' : 'failed', expected, record: echoed });
|
|
855
|
+
}
|
|
856
|
+
catch (error) {
|
|
857
|
+
attempts.push({ name: 'main_workload_failure', status: 'failed', error: String(error) });
|
|
858
|
+
report.status = 'failed';
|
|
859
|
+
}
|
|
860
|
+
finally {
|
|
861
|
+
await finish(session, 'main');
|
|
862
|
+
}
|
|
863
|
+
const agentSession = await start('actual-agent', true);
|
|
864
|
+
try {
|
|
865
|
+
attempts.push(await awaitGeometry(agentSession, options.awaitGeometry, outputDirectory, 'actual-agent'));
|
|
866
|
+
const pane = await agentSession.createPeer(python, 'echo', 'agent-echo');
|
|
867
|
+
const active = async () => { const status = await readJson(join(agentSession.root, 'agent-status.json')); return status.active === true && status.requests === 1 && Array.isArray(status.errors) && status.errors.length === 0; };
|
|
868
|
+
const agentProducerBefore = await readJson(join(agentSession.root, 'agent-status.json'));
|
|
869
|
+
attempts.push(...await Promise.all([echoSamples(agentSession, pane, 'actual_owned_agent_stream', smoke ? 5 : 200, active),
|
|
870
|
+
frameSamples(agentSession, 'actual_owned_agent_stream', active, smoke)]));
|
|
871
|
+
const agentProducerAfter = await readJson(join(agentSession.root, 'agent-status.json'));
|
|
872
|
+
attempts.push({ name: 'actual_agent_stream_progress', status: Number(agentProducerAfter.chunks) > Number(agentProducerBefore.chunks) && await active() ? 'measured' : 'unqualified', before: agentProducerBefore, after: agentProducerAfter });
|
|
873
|
+
attempts.push(...await actualShellEditorContinuation(agentSession, active));
|
|
874
|
+
await agentSession.write(pane, 'QUIT\n');
|
|
875
|
+
const echoed = await readJson(join(agentSession.root, 'agent-echo.done.json'));
|
|
876
|
+
attempts.push({ name: 'actual_agent_correctness', status: await active() && JSON.stringify(echoed.received) === JSON.stringify(attempts.find(attempt => attempt.name === 'managed_raw_input_echo' && attempt.condition === 'actual_owned_agent_stream').payloads.map(text => Buffer.from(text).toString('hex'))) ? 'measured' : 'failed', record: echoed,
|
|
877
|
+
provider: await readJson(join(agentSession.root, 'agent-status.json')) });
|
|
878
|
+
}
|
|
879
|
+
catch (error) {
|
|
880
|
+
attempts.push({ name: 'actual_agent_failure', status: 'failed', error: String(error) });
|
|
881
|
+
report.status = 'failed';
|
|
882
|
+
}
|
|
883
|
+
finally {
|
|
884
|
+
await finish(agentSession, 'actual-agent');
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
catch (error) {
|
|
888
|
+
report.status = 'failed';
|
|
889
|
+
report.failure = String(error);
|
|
890
|
+
await writeFile(join(outputDirectory, 'results.json'), JSON.stringify(report, null, 2));
|
|
891
|
+
throw error;
|
|
892
|
+
}
|
|
893
|
+
const afterHashes = {};
|
|
894
|
+
for (const path of sourceFiles)
|
|
895
|
+
afterHashes[path] = sha256(await readFile(resolve(path)));
|
|
896
|
+
report.binarySha256After = sha256(await readFile(binary));
|
|
897
|
+
report.binaryUnchanged = report.binarySha256After === manifest.binarySha256;
|
|
898
|
+
report.sourceHashesAfter = afterHashes;
|
|
899
|
+
report.sourcesUnchanged = JSON.stringify(sourceHashes) === JSON.stringify(afterHashes);
|
|
900
|
+
report.gates = budgets.budgets.map(budget => evaluateNativeBudget(String(budget.id), attempts, budget, smoke, cleanup));
|
|
901
|
+
report.releaseGate = 'unqualified';
|
|
902
|
+
report.unqualifiedMandatoryConditions = [options.initialGeometry ? 'Requested pre-launch geometry is recorded separately; frozen physical/native quality contract remains unqualified' : 'Fixed1280x800 policy geometry not controlled by this public runner',
|
|
903
|
+
'Independent physical keyboard/resize/scroll/clipboard/accessibility/comparator gates',
|
|
904
|
+
'One-pane native layout is unqualified when public snapshot still contains owner0 plus raw child; zoom intents are not assumed effective',
|
|
905
|
+
'Bulk matrix lacks200 concurrent sentinel samples for every repetition/mode; dedicated sustained bulk condition recorded',
|
|
906
|
+
'Window content points/scale/focus from passive metrics do not establish occlusion/display pixels',
|
|
907
|
+
'Native PID resource measurements do not qualify Node/owned-child CPU/RSS or total-memory budgets'];
|
|
908
|
+
if (attempts.some(attempt => attempt.status === 'failed') || !report.sourcesUnchanged || !report.binaryUnchanged)
|
|
909
|
+
report.status = 'failed';
|
|
910
|
+
report.finishedAt = new Date().toISOString();
|
|
911
|
+
await writeFile(join(outputDirectory, 'results.json'), JSON.stringify(report, null, 2));
|
|
912
|
+
return report;
|
|
913
|
+
}
|
|
914
|
+
async function copyArtifacts(root, destination) {
|
|
915
|
+
const { cp: copy, readdir } = await import('node:fs/promises');
|
|
916
|
+
await mkdir(destination, { recursive: true });
|
|
917
|
+
for (const entry of await readdir(root, { withFileTypes: true })) {
|
|
918
|
+
if (entry.isFile() && !entry.name.endsWith('.sock'))
|
|
919
|
+
await copy(join(root, entry.name), join(destination, entry.name));
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
/** Child-only actual App/Agent/native bridge; process environment is isolated before imports. */
|
|
923
|
+
export async function runOwnedNativeAgentRuntime(root, binary, geometry) {
|
|
924
|
+
validateInitialGeometry(geometry);
|
|
925
|
+
const repository = process.cwd();
|
|
926
|
+
const rendererBinary = resolve(binary);
|
|
927
|
+
const sockets = new Set();
|
|
928
|
+
let activeResponse;
|
|
929
|
+
let requests = 0;
|
|
930
|
+
let chunks = 0;
|
|
931
|
+
let bytes = 0;
|
|
932
|
+
let active = false;
|
|
933
|
+
let interval;
|
|
934
|
+
const errors = [];
|
|
935
|
+
let rendererPid;
|
|
936
|
+
let rendererExit;
|
|
937
|
+
let rendererSocketPath;
|
|
938
|
+
let statusWriting = Promise.resolve();
|
|
939
|
+
const status = () => {
|
|
940
|
+
const payload = JSON.stringify({ requests, chunks, bytes, active, errors, nativePid: rendererPid,
|
|
941
|
+
monotonicAtMs: performance.now(), cadenceMs: 40, chunkText: 'OWNED145 STREAM café 中文\n' });
|
|
942
|
+
statusWriting = statusWriting.then(async () => {
|
|
943
|
+
await writeFile(join(root, 'agent-status.json.tmp'), payload);
|
|
944
|
+
const { rename } = await import('node:fs/promises');
|
|
945
|
+
await rename(join(root, 'agent-status.json.tmp'), join(root, 'agent-status.json'));
|
|
946
|
+
}).catch(error => { errors.push(String(error)); });
|
|
947
|
+
};
|
|
948
|
+
const provider = createHttpServer(async (request, response) => {
|
|
949
|
+
if (request.method === 'GET' && request.url === '/v1/models') {
|
|
950
|
+
response.setHeader('content-type', 'application/json');
|
|
951
|
+
response.end(JSON.stringify({ data: [{ id: 'owned145' }] }));
|
|
952
|
+
return;
|
|
953
|
+
}
|
|
954
|
+
if (request.method !== 'POST' || request.url !== '/v1/chat/completions') {
|
|
955
|
+
errors.push('Unexpected provider route');
|
|
956
|
+
response.writeHead(500);
|
|
957
|
+
response.end();
|
|
958
|
+
return;
|
|
959
|
+
}
|
|
960
|
+
let body = '';
|
|
961
|
+
for await (const chunk of request)
|
|
962
|
+
body += String(chunk);
|
|
963
|
+
const parsed = JSON.parse(body);
|
|
964
|
+
requests++;
|
|
965
|
+
await writeFile(join(root, 'agent-request-' + requests + '.json'), body);
|
|
966
|
+
if (requests !== 1 || parsed.model !== 'owned145' || !parsed.messages.some(message => message.role === 'user'
|
|
967
|
+
&& typeof message.content === 'string' && message.content.startsWith('OWNED145_AGENT_TASK')))
|
|
968
|
+
errors.push('Unexpected actual App provider task');
|
|
969
|
+
activeResponse = response;
|
|
970
|
+
active = true;
|
|
971
|
+
response.writeHead(200, { 'content-type': 'text/event-stream', 'cache-control': 'no-cache' });
|
|
972
|
+
response.flushHeaders();
|
|
973
|
+
const emit = () => {
|
|
974
|
+
if (!active || response.destroyed || response.writableEnded)
|
|
975
|
+
return;
|
|
976
|
+
const content = 'OWNED145 STREAM café 中文\n';
|
|
977
|
+
response.write('data: ' + JSON.stringify({ choices: [{ index: 0, delta: { content }, finish_reason: null }] }) + '\n\n');
|
|
978
|
+
chunks++;
|
|
979
|
+
bytes += Buffer.byteLength(content);
|
|
980
|
+
status();
|
|
981
|
+
if (chunks >= 2000) {
|
|
982
|
+
active = false;
|
|
983
|
+
clearInterval(interval);
|
|
984
|
+
response.end('data: [DONE]\n\n');
|
|
985
|
+
status();
|
|
986
|
+
}
|
|
987
|
+
};
|
|
988
|
+
emit();
|
|
989
|
+
interval = setInterval(emit, 40);
|
|
990
|
+
response.once('close', () => { active = false; clearInterval(interval); status(); });
|
|
991
|
+
});
|
|
992
|
+
provider.on('connection', socket => { sockets.add(socket); socket.once('close', () => sockets.delete(socket)); });
|
|
993
|
+
await new Promise((resolveListen, reject) => { provider.once('error', reject); provider.listen(0, '127.0.0.1', resolveListen); });
|
|
994
|
+
const address = provider.address();
|
|
995
|
+
if (!address || typeof address === 'string')
|
|
996
|
+
throw new Error('Owned provider did not listen');
|
|
997
|
+
await writeFile(join(root, 'config', 'config.json'), JSON.stringify({ provider: 'openai_compatible', model: 'owned145', authMode: 'direct',
|
|
998
|
+
apiKeys: { openai_compatible: 'OWNED_LOCAL_145' }, openaiCompatible: { baseUrl: `http://127.0.0.1:${address.port}/v1` },
|
|
999
|
+
permissionProfile: 'trusted', skills: { enabled: false }, workflows: { enabled: false }, sfx: { enabled: false },
|
|
1000
|
+
recapsEnabled: false, autoContinue: false, messageDisplay: { smoothStreaming: false } }));
|
|
1001
|
+
const originalSpawn = cp.spawn;
|
|
1002
|
+
// The host supplies the owned executable directly; the process boundary adds benchmark options.
|
|
1003
|
+
cp.spawn = ((...args) => {
|
|
1004
|
+
if (String(args[0]) === rendererBinary) {
|
|
1005
|
+
if (Array.isArray(args[1]))
|
|
1006
|
+
args[1] = [...args[1], '--settings', join(root, 'settings.json'), '--session', join(root, 'native-session.json'), ...initialGeometryArgs(geometry)];
|
|
1007
|
+
if (Array.isArray(args[1])) {
|
|
1008
|
+
const socketOption = args[1].indexOf('--socket');
|
|
1009
|
+
if (socketOption >= 0)
|
|
1010
|
+
rendererSocketPath = args[1][socketOption + 1];
|
|
1011
|
+
}
|
|
1012
|
+
const child = originalSpawn(...args);
|
|
1013
|
+
rendererPid = child.pid;
|
|
1014
|
+
child.once('close', (code, signal) => { rendererExit = { code, signal }; });
|
|
1015
|
+
return child;
|
|
1016
|
+
}
|
|
1017
|
+
return originalSpawn(...args);
|
|
1018
|
+
});
|
|
1019
|
+
syncBuiltinESMExports();
|
|
1020
|
+
const { pathToFileURL } = await import('node:url');
|
|
1021
|
+
const { configStore } = await import(pathToFileURL(join(repository, 'src/config.ts')).href);
|
|
1022
|
+
const { RatatuiApp } = await import(pathToFileURL(join(repository, 'src/cli_ratatui.ts')).href);
|
|
1023
|
+
const { TuiProcess } = await import(pathToFileURL(join(repository, 'src/ui/ratatui/process.ts')).href);
|
|
1024
|
+
await configStore.refresh();
|
|
1025
|
+
process.chdir(join(root, 'project'));
|
|
1026
|
+
const tui = new TuiProcess({ desktopBinaryPath: rendererBinary });
|
|
1027
|
+
let ready;
|
|
1028
|
+
let driver;
|
|
1029
|
+
const pending = [];
|
|
1030
|
+
const publish = (event) => { if (driver && !driver.destroyed)
|
|
1031
|
+
driver.write(JSON.stringify(event) + '\n');
|
|
1032
|
+
else
|
|
1033
|
+
pending.push(event); };
|
|
1034
|
+
tui.on(event => {
|
|
1035
|
+
if (event.type === 'ready')
|
|
1036
|
+
ready = event;
|
|
1037
|
+
if (['ready', 'pane_created', 'pane_read', 'pane_write_result', 'pane_state', 'pane_list', 'session_state', 'terminal_metrics', 'error'].includes(event.type))
|
|
1038
|
+
publish(event);
|
|
1039
|
+
});
|
|
1040
|
+
const app = new RatatuiApp({ sessionId: 'owned-native-performance-145', sessionFile: join(root, 'project', 'agent-session.json'),
|
|
1041
|
+
initialPrompt: 'OWNED145_AGENT_TASK Explain this owned benchmark stream.', desktop: true, mux: true, mirror: false,
|
|
1042
|
+
noBrowserOpen: true, navigatorHidden: true, tuiProcess: tui });
|
|
1043
|
+
let stopping = false;
|
|
1044
|
+
const stop = async () => {
|
|
1045
|
+
if (stopping)
|
|
1046
|
+
return;
|
|
1047
|
+
stopping = true;
|
|
1048
|
+
clearInterval(interval);
|
|
1049
|
+
active = false;
|
|
1050
|
+
if (activeResponse && !activeResponse.destroyed && !activeResponse.writableEnded)
|
|
1051
|
+
activeResponse.end('data: [DONE]\n\n');
|
|
1052
|
+
await app.exit(false, { announce: false, persistSession: true, terminateProcess: false });
|
|
1053
|
+
};
|
|
1054
|
+
const server = net.createServer(socket => {
|
|
1055
|
+
driver = socket;
|
|
1056
|
+
let buffer = '';
|
|
1057
|
+
for (const event of pending.splice(0))
|
|
1058
|
+
publish(event);
|
|
1059
|
+
publish({ type: 'benchmark_agent_ready', native_pid: rendererPid, app_pid: process.pid });
|
|
1060
|
+
socket.setEncoding('utf8');
|
|
1061
|
+
socket.on('error', error => errors.push(String(error)));
|
|
1062
|
+
socket.on('data', (chunk) => {
|
|
1063
|
+
buffer += chunk;
|
|
1064
|
+
let newline;
|
|
1065
|
+
while ((newline = buffer.indexOf('\n')) >= 0) {
|
|
1066
|
+
const packet = buffer.slice(0, newline);
|
|
1067
|
+
buffer = buffer.slice(newline + 1);
|
|
1068
|
+
const command = JSON.parse(packet);
|
|
1069
|
+
if (command.type === 'benchmark_stop')
|
|
1070
|
+
void stop();
|
|
1071
|
+
else if (!tui.send(command))
|
|
1072
|
+
publish({ type: 'error', request_id: command.request_id, message: 'Actual native transport unavailable' });
|
|
1073
|
+
}
|
|
1074
|
+
});
|
|
1075
|
+
});
|
|
1076
|
+
const running = app.start();
|
|
1077
|
+
let failure;
|
|
1078
|
+
void running.catch(error => { failure = error; });
|
|
1079
|
+
try {
|
|
1080
|
+
const deadline = performance.now() + 20_000;
|
|
1081
|
+
while ((!ready || !active || !rendererPid) && !failure && performance.now() < deadline)
|
|
1082
|
+
await pause(10);
|
|
1083
|
+
if (failure)
|
|
1084
|
+
throw failure;
|
|
1085
|
+
if (!ready || !active || !rendererPid)
|
|
1086
|
+
throw new Error('Actual App/native/provider startup did not complete');
|
|
1087
|
+
await new Promise((resolveListen, reject) => { server.once('error', reject); server.listen(join(root, 'driver.sock'), resolveListen); });
|
|
1088
|
+
await running;
|
|
1089
|
+
}
|
|
1090
|
+
finally {
|
|
1091
|
+
await stop();
|
|
1092
|
+
clearInterval(interval);
|
|
1093
|
+
active = false;
|
|
1094
|
+
for (const socket of sockets)
|
|
1095
|
+
socket.destroy();
|
|
1096
|
+
if (provider.listening)
|
|
1097
|
+
await new Promise(resolveClose => provider.close(() => resolveClose()));
|
|
1098
|
+
driver?.destroy();
|
|
1099
|
+
if (server.listening)
|
|
1100
|
+
await new Promise(resolveClose => server.close(() => resolveClose()));
|
|
1101
|
+
await rm(join(root, 'driver.sock'), { force: true });
|
|
1102
|
+
cp.spawn = originalSpawn;
|
|
1103
|
+
syncBuiltinESMExports();
|
|
1104
|
+
await statusWriting;
|
|
1105
|
+
await writeFile(join(root, 'agent-result.json'), JSON.stringify({ requests, chunks, bytes, errors, rendererPid, rendererExit, rendererSocketPath,
|
|
1106
|
+
nativeSocketAbsent: rendererSocketPath !== undefined && await fileAbsent(rendererSocketPath),
|
|
1107
|
+
providerSockets: sockets.size, active: false, cleanupCompleted: true }, null, 2));
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
async function awaitGeometry(session, geometry, output, condition) {
|
|
1111
|
+
const initial = await session.metrics();
|
|
1112
|
+
if (!geometry)
|
|
1113
|
+
return { name: 'window_geometry', condition, status: 'unqualified', initial, reason: 'No controlled geometry request; actual native metrics retained' };
|
|
1114
|
+
if (![geometry.width, geometry.height].every(value => Number.isFinite(value) && value > 0))
|
|
1115
|
+
throw new NativeTerminalBenchmarkError('INVALID_OPTIONS', 'Window geometry must be positive finite content points');
|
|
1116
|
+
const marker = { condition, root: session.root, nativePid: session.nativePid, requested: geometry, initial };
|
|
1117
|
+
await writeFile(join(output, 'awaiting-geometry.json'), JSON.stringify(marker, null, 2));
|
|
1118
|
+
process.stderr.write('BENCHMARK_GEOMETRY_REQUIRED ' + JSON.stringify(marker) + '\n');
|
|
1119
|
+
const deadline = performance.now() + (geometry.timeoutMs ?? 120_000);
|
|
1120
|
+
let observed = initial;
|
|
1121
|
+
while (performance.now() < deadline) {
|
|
1122
|
+
observed = await session.metrics();
|
|
1123
|
+
if (Math.abs(observed.window_width_points - geometry.width) < 0.01 && Math.abs(observed.window_height_points - geometry.height) < 0.01 && observed.focused) {
|
|
1124
|
+
const result = { name: 'window_geometry', condition, status: 'measured', requested: geometry, initial, observed };
|
|
1125
|
+
await writeFile(join(output, 'geometry-' + condition + '.json'), JSON.stringify(result, null, 2));
|
|
1126
|
+
return result;
|
|
1127
|
+
}
|
|
1128
|
+
await pause(100);
|
|
1129
|
+
}
|
|
1130
|
+
return { name: 'window_geometry', condition, status: 'unqualified', requested: geometry, initial, observed, reason: 'Actual requested geometry/focus was not observed before bounded deadline' };
|
|
1131
|
+
}
|
|
1132
|
+
function nativeLayoutLeaves(node) {
|
|
1133
|
+
if (!node || typeof node !== 'object')
|
|
1134
|
+
return null;
|
|
1135
|
+
const layout = node;
|
|
1136
|
+
if (layout.type === 'leaf')
|
|
1137
|
+
return Number.isSafeInteger(layout.pane_id) && Number(layout.pane_id) >= 0 ? [Number(layout.pane_id)] : null;
|
|
1138
|
+
if (layout.type !== 'split')
|
|
1139
|
+
return null;
|
|
1140
|
+
const first = nativeLayoutLeaves(layout.first);
|
|
1141
|
+
const second = nativeLayoutLeaves(layout.second);
|
|
1142
|
+
if (!first || !second)
|
|
1143
|
+
return null;
|
|
1144
|
+
const combined = [...first, ...second];
|
|
1145
|
+
return new Set(combined).size === combined.length ? combined : null;
|
|
1146
|
+
}
|
|
1147
|
+
function nativeWindows(state) {
|
|
1148
|
+
return Array.isArray(state.windows) && state.windows.length ? state.windows
|
|
1149
|
+
: [{ id: 0, layout: state.layout, focused_pane_id: state.focused_pane_id }];
|
|
1150
|
+
}
|
|
1151
|
+
function nativeVisiblePanes(state) {
|
|
1152
|
+
const active = nativeWindows(state).find(window => window.id === (state.active_window_id ?? 0));
|
|
1153
|
+
if (!active)
|
|
1154
|
+
return null;
|
|
1155
|
+
const leaves = nativeLayoutLeaves(active.layout);
|
|
1156
|
+
if (!leaves)
|
|
1157
|
+
return null;
|
|
1158
|
+
if (active.zoomed_pane_id !== undefined && active.zoomed_pane_id !== null) {
|
|
1159
|
+
if (typeof active.zoomed_pane_id !== 'number' || !leaves.includes(active.zoomed_pane_id))
|
|
1160
|
+
return null;
|
|
1161
|
+
return [active.zoomed_pane_id];
|
|
1162
|
+
}
|
|
1163
|
+
return leaves;
|
|
1164
|
+
}
|
|
1165
|
+
/** Interpret the canonical Mux leaf/split tree observed through get_session_state. */
|
|
1166
|
+
export function qualifyNativeOutputLayout(state, pane, requested) {
|
|
1167
|
+
const visible = nativeVisiblePanes(state);
|
|
1168
|
+
if (!visible)
|
|
1169
|
+
return false;
|
|
1170
|
+
if (requested === 'inactive_tab')
|
|
1171
|
+
return !visible.includes(pane) && nativeWindows(state).some(window => window.id !== (state.active_window_id ?? 0) && nativeLayoutLeaves(window.layout)?.includes(pane) === true);
|
|
1172
|
+
if (requested !== 'one_pane' && requested !== 'two_split_panes')
|
|
1173
|
+
return false;
|
|
1174
|
+
return visible.includes(pane) && visible.length === (requested === 'one_pane' ? 1 : 2);
|
|
1175
|
+
}
|
|
1176
|
+
const shellQuote = (value) => "'" + value.replace(/'/g, "'\\''") + "'";
|
|
1177
|
+
async function waitExactFile(path, expected) {
|
|
1178
|
+
const deadline = performance.now() + 5000;
|
|
1179
|
+
while (performance.now() < deadline) {
|
|
1180
|
+
try {
|
|
1181
|
+
if (await readFile(path, 'utf8') === expected)
|
|
1182
|
+
return;
|
|
1183
|
+
}
|
|
1184
|
+
catch (error) {
|
|
1185
|
+
if (error.code !== 'ENOENT')
|
|
1186
|
+
throw error;
|
|
1187
|
+
}
|
|
1188
|
+
await pause(5);
|
|
1189
|
+
}
|
|
1190
|
+
throw new Error('Actual owned shell/editor did not preserve exact requested file bytes: ' + path);
|
|
1191
|
+
}
|
|
1192
|
+
async function actualShellEditorContinuation(session, active) {
|
|
1193
|
+
const attempts = [];
|
|
1194
|
+
const shell = await session.ipc.request('create_pane', 'pane_created', { command: '/bin/sh', args: ['-i'], cwd: join(session.root, 'project'),
|
|
1195
|
+
title: 'Owned 145 real shell', pane_type: 'process', env: { PS1: 'OWNED145_SH> ', LC_ALL: 'en_US.UTF-8' } });
|
|
1196
|
+
const shellPane = Number(shell.pane_id);
|
|
1197
|
+
await session.waitText(shellPane, 'OWNED145_SH>');
|
|
1198
|
+
const shellFile = join(session.root, 'shell-exact.txt');
|
|
1199
|
+
const shellPid = join(session.root, 'shell-pid.txt');
|
|
1200
|
+
const text = 'SHELL145 café 中文 🦀\n';
|
|
1201
|
+
const before = await active();
|
|
1202
|
+
const started = performance.now();
|
|
1203
|
+
await session.write(shellPane, `printf '%s' ${shellQuote(text)} > ${shellQuote(shellFile)}; printf '%s' "$$" > ${shellQuote(shellPid)}\n`);
|
|
1204
|
+
await waitExactFile(shellFile, text);
|
|
1205
|
+
const pid = Number(await readFile(shellPid, 'utf8'));
|
|
1206
|
+
session.ownedPids.add(pid);
|
|
1207
|
+
attempts.push({ name: 'actual_shell_during_agent', status: before && await active() ? 'measured' : 'unqualified', durationMs: performance.now() - started,
|
|
1208
|
+
command: '/bin/sh -i', expectedUtf8Sha256: sha256(text), actualUtf8Sha256: sha256(await readFile(shellFile)), pid,
|
|
1209
|
+
boundary: 'Managed command to exact real shell file completion; includes IPC/file polling; not physical typing latency' });
|
|
1210
|
+
await session.write(shellPane, 'exit\n');
|
|
1211
|
+
session.ipc.send({ type: 'close_pane', pane_id: shellPane });
|
|
1212
|
+
const editorPath = join(session.root, 'editor-exact.txt');
|
|
1213
|
+
await writeFile(editorPath, '');
|
|
1214
|
+
const editor = await session.ipc.request('create_pane', 'pane_created', { command: '/usr/bin/vim', args: ['-Nu', 'NONE', '-n', editorPath],
|
|
1215
|
+
cwd: join(session.root, 'project'), title: 'Owned 145 real Vim', pane_type: 'process', env: { LC_ALL: 'en_US.UTF-8' } });
|
|
1216
|
+
const editorPane = Number(editor.pane_id);
|
|
1217
|
+
const editorPid = join(session.root, 'editor-pid.txt');
|
|
1218
|
+
await session.write(editorPane, `:call writefile([string(getpid())], ${shellQuote(editorPid)})\r`);
|
|
1219
|
+
const pidDeadline = performance.now() + 5000;
|
|
1220
|
+
let ownedEditorPid = 0;
|
|
1221
|
+
while (performance.now() < pidDeadline) {
|
|
1222
|
+
try {
|
|
1223
|
+
ownedEditorPid = Number((await readFile(editorPid, 'utf8')).trim());
|
|
1224
|
+
if (ownedEditorPid > 0)
|
|
1225
|
+
break;
|
|
1226
|
+
}
|
|
1227
|
+
catch (error) {
|
|
1228
|
+
if (error.code !== 'ENOENT')
|
|
1229
|
+
throw error;
|
|
1230
|
+
}
|
|
1231
|
+
await pause(10);
|
|
1232
|
+
}
|
|
1233
|
+
if (!ownedEditorPid)
|
|
1234
|
+
throw new Error('Real editor PID publication failed');
|
|
1235
|
+
session.ownedPids.add(ownedEditorPid);
|
|
1236
|
+
await session.write(editorPane, ':set paste\r');
|
|
1237
|
+
const editorText = 'EDITOR145 café 中文 🦀\n';
|
|
1238
|
+
const editorBefore = await active();
|
|
1239
|
+
const editorStarted = performance.now();
|
|
1240
|
+
await session.write(editorPane, 'i' + editorText.trimEnd() + '\x1b:wq\r');
|
|
1241
|
+
await waitExactFile(editorPath, editorText);
|
|
1242
|
+
attempts.push({ name: 'actual_editor_during_agent', status: editorBefore && await active() ? 'measured' : 'unqualified', durationMs: performance.now() - editorStarted,
|
|
1243
|
+
command: '/usr/bin/vim -Nu NONE -n', pid: ownedEditorPid, expectedUtf8Sha256: sha256(editorText), actualUtf8Sha256: sha256(await readFile(editorPath)),
|
|
1244
|
+
boundary: 'Managed native IPC input to exact actual Vim save; no physical GUI keyboard/presentation qualification' });
|
|
1245
|
+
session.ipc.send({ type: 'close_pane', pane_id: editorPane });
|
|
1246
|
+
return attempts;
|
|
1247
|
+
}
|