@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,805 @@
|
|
|
1
|
+
import { validateMeetingAdjudications } from './dynamic_meetings.js';
|
|
2
|
+
import { isDeepStrictEqual } from 'node:util';
|
|
3
|
+
import { validFailedAdjudicationCauses } from './dynamic_agents.js';
|
|
4
|
+
import { parseDynamicCharacterProposal, parseDynamicGmProposal, parseDynamicGmConsequences, parseDynamicGmWorldConsequences, } from './dynamic_state.js';
|
|
5
|
+
export class DynamicGameContractError extends Error {
|
|
6
|
+
code;
|
|
7
|
+
constructor(code, message) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.code = code;
|
|
10
|
+
this.name = 'DynamicGameContractError';
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export function validateSimulationInferenceRouting(routing) {
|
|
14
|
+
const allowed = (routing.serviceTier === undefined || routing.serviceTier === 'priority')
|
|
15
|
+
&& routing.provider === 'codex_legacy'
|
|
16
|
+
&& ((routing.role === 'character' && routing.model === 'gpt-5.6-luna' && routing.reasoningEffort === 'low')
|
|
17
|
+
|| (routing.role === 'game-master' && routing.model === 'gpt-5.6-sol' && routing.reasoningEffort === 'none'));
|
|
18
|
+
if (!allowed) {
|
|
19
|
+
throw new DynamicGameContractError('route-not-allowed', 'Character and Game Master requests require approved models and reasoning effort and an omitted or priority service tier');
|
|
20
|
+
}
|
|
21
|
+
return structuredClone(routing);
|
|
22
|
+
}
|
|
23
|
+
export function validateCharacterProposal(proposal, context) {
|
|
24
|
+
assertCharacterProposalShape(proposal, context.agencyVersion === 'emergent-v1', context.interactionVersion === 'meetings-2');
|
|
25
|
+
if (proposal.attemptedActions.length > 4) {
|
|
26
|
+
fail('action-count-invalid', 'A character may attempt at most four actions');
|
|
27
|
+
}
|
|
28
|
+
proposal = assertCanonicalProposal(() => parseDynamicCharacterProposal(proposal));
|
|
29
|
+
assertRevision(proposal.basedOnRevision, context.revision);
|
|
30
|
+
assertId(proposal.proposalId, 'proposalId');
|
|
31
|
+
assertNewId(proposal.proposalId, context.knownCharacterProposalIds);
|
|
32
|
+
assertActor(proposal.actorId, context);
|
|
33
|
+
if (context.interactionVersion === 'meetings-2' && proposal.discussionInvitation !== null) {
|
|
34
|
+
const invitation = proposal.discussionInvitation;
|
|
35
|
+
if (!context.knownActorIds.includes(invitation.recipientActorId)
|
|
36
|
+
|| invitation.recipientActorId === proposal.actorId
|
|
37
|
+
|| context.humanControlledActorIds.includes(invitation.recipientActorId)
|
|
38
|
+
|| context.humanControlledActorIds.includes(proposal.actorId)) {
|
|
39
|
+
fail('discussion-recipient-invalid', 'A discussion invitation must address another known autonomous character');
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (context.agencyVersion !== 'emergent-v1') {
|
|
43
|
+
if (proposal.intent === null)
|
|
44
|
+
fail('intent-required', 'Legacy proposals require an intent');
|
|
45
|
+
if (proposal.intent.goalId === ''
|
|
46
|
+
|| !context.knownGoalIds[proposal.actorId]?.includes(proposal.intent.goalId)) {
|
|
47
|
+
fail('unknown-goal', `Character goal ${proposal.intent.goalId} is not active for ${proposal.actorId}`);
|
|
48
|
+
}
|
|
49
|
+
assertActorMechanic(proposal.actorId, proposal.intent.mechanic, context);
|
|
50
|
+
assertText(proposal.intent.summary, 'intent summary', 400);
|
|
51
|
+
assertTarget(proposal.actorId, proposal.intent.targetActorId, context);
|
|
52
|
+
assertMechanicTarget(proposal.intent.mechanic, proposal.intent.targetActorId, context);
|
|
53
|
+
const currentIntent = context.knownCurrentIntents[proposal.actorId] ?? null;
|
|
54
|
+
if (currentIntent !== null && (proposal.intent.mechanic !== currentIntent.mechanic
|
|
55
|
+
|| proposal.intent.goalId !== currentIntent.goalId
|
|
56
|
+
|| proposal.intent.targetActorId !== currentIntent.targetActorId)) {
|
|
57
|
+
fail('character-intent-discontinuous', 'A character must continue its committed intent until a causal Game Master update replaces it');
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (!Array.isArray(proposal.attemptedActions) || proposal.attemptedActions.length > 4) {
|
|
61
|
+
fail('action-count-invalid', 'A character may attempt at most four actions');
|
|
62
|
+
}
|
|
63
|
+
assertUnique(proposal.attemptedActions.map(action => action.id), 'duplicate-action');
|
|
64
|
+
for (const action of proposal.attemptedActions) {
|
|
65
|
+
assertNewId(action.id, context.knownActionIds);
|
|
66
|
+
assertDomainAction(action, context);
|
|
67
|
+
if (action.actorId !== proposal.actorId) {
|
|
68
|
+
fail('actor-not-authorized', 'A character may only propose its own actions');
|
|
69
|
+
}
|
|
70
|
+
if (context.agencyVersion !== 'emergent-v1')
|
|
71
|
+
validateActionMotivation(action, proposal, context);
|
|
72
|
+
}
|
|
73
|
+
if (context.agencyVersion !== 'emergent-v1' && proposal.intent !== null && proposal.attemptedActions.length > 0 && !proposal.attemptedActions.some(action => (action.mechanic === proposal.intent?.mechanic
|
|
74
|
+
&& action.targetActorId === proposal.intent?.targetActorId))) {
|
|
75
|
+
fail('character-intent-unlinked', 'A character intent must match at least one attempted action');
|
|
76
|
+
}
|
|
77
|
+
assertText(proposal.internalReason, 'internalReason', 800);
|
|
78
|
+
return structuredClone(proposal);
|
|
79
|
+
}
|
|
80
|
+
export function validateGmProposal(proposal, context) {
|
|
81
|
+
assertGmProposalShape(proposal, true);
|
|
82
|
+
return validateGmContents(proposal, context, parseDynamicGmProposal);
|
|
83
|
+
}
|
|
84
|
+
export function validateGmConsequences(proposal, context) {
|
|
85
|
+
assertGmProposalShape(proposal, false);
|
|
86
|
+
return validateGmContents(proposal, context, parseDynamicGmConsequences);
|
|
87
|
+
}
|
|
88
|
+
export function validateGmWorldConsequences(proposal, context) {
|
|
89
|
+
assertGmProposalShape(proposal, false, false);
|
|
90
|
+
return validateGmContents(proposal, context, parseDynamicGmWorldConsequences);
|
|
91
|
+
}
|
|
92
|
+
function hasAgentUpdates(proposal) {
|
|
93
|
+
return Object.hasOwn(proposal, 'agentUpdates');
|
|
94
|
+
}
|
|
95
|
+
function validateGmContents(proposal, context, parse) {
|
|
96
|
+
if (proposal.ending !== null
|
|
97
|
+
&& new Set(proposal.ending.causalEventIds).size !== proposal.ending.causalEventIds.length) {
|
|
98
|
+
fail('duplicate-ending-cause', 'Ending causes must be unique');
|
|
99
|
+
}
|
|
100
|
+
if (proposal.domainActions.length > 16) {
|
|
101
|
+
fail('action-count-invalid', 'A Game Master may commit at most sixteen actions');
|
|
102
|
+
}
|
|
103
|
+
if (!Number.isSafeInteger(proposal.pacing.advanceSteps)
|
|
104
|
+
|| proposal.pacing.advanceSteps < 1
|
|
105
|
+
|| proposal.pacing.advanceSteps > 12) {
|
|
106
|
+
fail('pacing-invalid', 'Pacing advanceSteps must be an integer from one to twelve');
|
|
107
|
+
}
|
|
108
|
+
proposal = assertCanonicalProposal(() => parse(proposal));
|
|
109
|
+
assertRevision(proposal.basedOnRevision, context.revision);
|
|
110
|
+
assertId(proposal.proposalId, 'proposalId');
|
|
111
|
+
assertNewId(proposal.proposalId, context.knownGmProposalIds);
|
|
112
|
+
assertId(proposal.event.id, 'event.id');
|
|
113
|
+
assertNewId(proposal.event.id, context.knownHistoryEventIds);
|
|
114
|
+
assertText(proposal.event.title, 'event title', 160);
|
|
115
|
+
assertText(proposal.event.narrative, 'event narrative', 2_000);
|
|
116
|
+
if (!['low', 'medium', 'high', 'critical'].includes(proposal.event.urgency)) {
|
|
117
|
+
fail('urgency-invalid', 'Event urgency is invalid');
|
|
118
|
+
}
|
|
119
|
+
if (!Array.isArray(proposal.event.openQuestions) || proposal.event.openQuestions.length > 6) {
|
|
120
|
+
fail('question-count-invalid', 'An event may expose at most six questions');
|
|
121
|
+
}
|
|
122
|
+
for (const question of proposal.event.openQuestions)
|
|
123
|
+
assertText(question, 'open question', 240);
|
|
124
|
+
if ('availableActions' in proposal)
|
|
125
|
+
validateGmChoices(proposal, context);
|
|
126
|
+
assertUnique(proposal.domainActions.map(action => action.id), 'duplicate-action');
|
|
127
|
+
const occupiedActionIds = [
|
|
128
|
+
...context.knownActionIds,
|
|
129
|
+
...context.characterProposals.flatMap(character => (character.attemptedActions.map(action => action.actionId))),
|
|
130
|
+
];
|
|
131
|
+
for (const action of proposal.domainActions) {
|
|
132
|
+
assertNewId(action.id, occupiedActionIds);
|
|
133
|
+
assertDomainAction(action, context);
|
|
134
|
+
assertGmActionLineage(action, context);
|
|
135
|
+
}
|
|
136
|
+
assertUnique(proposal.domainActions
|
|
137
|
+
.map(action => action.sourceAttemptedActionId)
|
|
138
|
+
.filter((id) => id !== undefined), 'duplicate-attempt-lineage');
|
|
139
|
+
assertUnique(proposal.domainActions
|
|
140
|
+
.map(action => action.sourcePlayerActionId)
|
|
141
|
+
.filter((id) => id !== undefined), 'duplicate-player-lineage');
|
|
142
|
+
const actionIds = new Set(proposal.domainActions.map(action => action.id));
|
|
143
|
+
if (proposal.characterResponses.length > context.knownActorIds.length) {
|
|
144
|
+
fail('response-count-invalid', 'Character response count exceeds the cast');
|
|
145
|
+
}
|
|
146
|
+
assertUnique(proposal.characterResponses.map(response => response.actorId), 'duplicate-character-response');
|
|
147
|
+
for (const response of proposal.characterResponses) {
|
|
148
|
+
assertActor(response.actorId, context);
|
|
149
|
+
if (context.humanControlledActorIds.includes(response.actorId)) {
|
|
150
|
+
fail('character-response-actor-invalid', 'The Game Master cannot author a response for the human player');
|
|
151
|
+
}
|
|
152
|
+
assertText(response.text, 'character response', 600);
|
|
153
|
+
assertText(response.mood, 'character mood', 80);
|
|
154
|
+
}
|
|
155
|
+
const requiredResponseActorIds = new Set([
|
|
156
|
+
...context.characterProposals.map(character => character.actorId),
|
|
157
|
+
...proposal.domainActions.flatMap(action => (context.actionParticipantActorIds[action.id] ?? [])),
|
|
158
|
+
].filter(actorId => !context.humanControlledActorIds.includes(actorId)));
|
|
159
|
+
const responseActorIds = new Set(proposal.characterResponses.map(response => response.actorId));
|
|
160
|
+
if (context.agencyVersion !== 'emergent-v1' && [...requiredResponseActorIds].some(actorId => !responseActorIds.has(actorId))) {
|
|
161
|
+
fail('character-response-missing', 'Every autonomous proposer and affected character needs one visible response');
|
|
162
|
+
}
|
|
163
|
+
const expectedProposals = new Set(context.characterProposals.map(item => item.proposalId));
|
|
164
|
+
validateMeetingAdjudications(proposal, context.meetings ?? [], context.characterProposals.map(character => ({ ...character, attemptedActions: character.attemptedActions })), context.agencyVersion);
|
|
165
|
+
const adjudicated = proposal.adjudications.map(item => item.characterProposalId);
|
|
166
|
+
assertUnique(adjudicated, 'duplicate-adjudication');
|
|
167
|
+
if (adjudicated.length !== expectedProposals.size
|
|
168
|
+
|| adjudicated.some(id => !expectedProposals.has(id))) {
|
|
169
|
+
fail('unadjudicated-character', 'Every character proposal must be adjudicated exactly once');
|
|
170
|
+
}
|
|
171
|
+
for (const adjudication of proposal.adjudications) {
|
|
172
|
+
assertText(adjudication.reason, 'adjudication reason', 500);
|
|
173
|
+
if (!['succeeded', 'partial', 'failed', 'deferred'].includes(adjudication.outcome)) {
|
|
174
|
+
fail('adjudication-outcome-invalid', 'Character adjudication outcome is invalid');
|
|
175
|
+
}
|
|
176
|
+
if (adjudication.actionIds.some(id => !actionIds.has(id))) {
|
|
177
|
+
fail('unlinked-adjudication', 'Adjudications may only cite proposed domain actions');
|
|
178
|
+
}
|
|
179
|
+
if (['failed', 'deferred'].includes(adjudication.outcome) && adjudication.actionIds.length > 0) {
|
|
180
|
+
fail('failed-action-committed', 'A failed character action cannot commit a domain action');
|
|
181
|
+
}
|
|
182
|
+
const source = context.characterProposals.find(item => (item.proposalId === adjudication.characterProposalId));
|
|
183
|
+
if (!source)
|
|
184
|
+
fail('unadjudicated-character', 'Adjudication source is unknown');
|
|
185
|
+
if (adjudication.actionIds.some((id) => {
|
|
186
|
+
const action = proposal.domainActions.find(candidate => candidate.id === id);
|
|
187
|
+
return action?.sourceCharacterProposalId !== source.proposalId;
|
|
188
|
+
})) {
|
|
189
|
+
fail('action-lineage-invalid', 'An adjudication may cite only its own character actions');
|
|
190
|
+
}
|
|
191
|
+
if (!['failed', 'deferred'].includes(adjudication.outcome) && adjudication.actionIds.length === 0) {
|
|
192
|
+
fail('adjudication-inconsistent', 'A successful or partial character ruling must commit a linked action');
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
const adjudicatedActions = proposal.adjudications.flatMap(item => item.actionIds);
|
|
196
|
+
assertUnique(adjudicatedActions, 'duplicate-adjudicated-action');
|
|
197
|
+
const unadjudicatedNpcAction = proposal.domainActions.find(action => (action.sourceCharacterProposalId !== undefined
|
|
198
|
+
&& !adjudicatedActions.includes(action.id)));
|
|
199
|
+
if (unadjudicatedNpcAction) {
|
|
200
|
+
fail('unadjudicated-action', 'Every committed character action needs one adjudication');
|
|
201
|
+
}
|
|
202
|
+
const playerAdjudication = proposal.playerAdjudication;
|
|
203
|
+
const selectedPlayerAction = context.selectedPlayerAction;
|
|
204
|
+
if ((playerAdjudication === null) !== (selectedPlayerAction === null)) {
|
|
205
|
+
fail('player-adjudication-invalid', 'Every selected player action needs one Game Master adjudication');
|
|
206
|
+
}
|
|
207
|
+
if (playerAdjudication !== null && selectedPlayerAction !== null) {
|
|
208
|
+
if (playerAdjudication.playerActionId !== selectedPlayerAction.actionId) {
|
|
209
|
+
fail('player-adjudication-invalid', 'Player adjudication must cite the selected action');
|
|
210
|
+
}
|
|
211
|
+
if (!['succeeded', 'partial', 'failed'].includes(playerAdjudication.outcome)) {
|
|
212
|
+
fail('adjudication-outcome-invalid', 'Player adjudication outcome is invalid');
|
|
213
|
+
}
|
|
214
|
+
assertText(playerAdjudication.reason, 'player adjudication reason', 500);
|
|
215
|
+
assertUnique(playerAdjudication.actionIds, 'duplicate-player-adjudicated-action');
|
|
216
|
+
if (playerAdjudication.actionIds.some(id => !actionIds.has(id))) {
|
|
217
|
+
fail('unlinked-player-adjudication', 'Player adjudication may only cite proposed domain actions');
|
|
218
|
+
}
|
|
219
|
+
const playerActions = proposal.domainActions.filter(action => (action.sourcePlayerActionId === selectedPlayerAction.actionId));
|
|
220
|
+
if (playerAdjudication.actionIds.some(id => !playerActions.some(action => action.id === id))
|
|
221
|
+
|| playerActions.some(action => !playerAdjudication.actionIds.includes(action.id))) {
|
|
222
|
+
fail('player-action-lineage-invalid', 'Player action effects must appear in its adjudication');
|
|
223
|
+
}
|
|
224
|
+
if (playerAdjudication.outcome === 'failed' && playerAdjudication.actionIds.length > 0 && selectedPlayerAction.mechanic !== context.freeformMechanic) {
|
|
225
|
+
fail('failed-action-committed', 'A failed player action cannot commit a domain action');
|
|
226
|
+
}
|
|
227
|
+
if (playerAdjudication.outcome !== 'failed' && playerAdjudication.actionIds.length === 0) {
|
|
228
|
+
fail('adjudication-inconsistent', 'A successful or partial player ruling must commit a linked action');
|
|
229
|
+
}
|
|
230
|
+
if (selectedPlayerAction.mechanic === context.freeformMechanic && playerAdjudication.actionIds.length === 0) {
|
|
231
|
+
fail('free-attempt-unresolved', 'A free player attempt needs recorded consequences, including when its objective fails');
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
if (hasAgentUpdates(proposal)) {
|
|
235
|
+
assertUnique(proposal.agentUpdates.map(update => update.actorId), 'duplicate-agent-update');
|
|
236
|
+
for (const update of proposal.agentUpdates)
|
|
237
|
+
validateAgentUpdate(update, proposal, context);
|
|
238
|
+
}
|
|
239
|
+
if (!Number.isFinite(proposal.pacing.tension)
|
|
240
|
+
|| proposal.pacing.tension < 0
|
|
241
|
+
|| proposal.pacing.tension > 100) {
|
|
242
|
+
fail('tension-out-of-bounds', 'Pacing tension must stay between zero and one hundred');
|
|
243
|
+
}
|
|
244
|
+
assertText(proposal.pacing.timeAdvance, 'time advance', 100);
|
|
245
|
+
if (!Number.isSafeInteger(proposal.pacing.advanceSteps)
|
|
246
|
+
|| proposal.pacing.advanceSteps < 1
|
|
247
|
+
|| proposal.pacing.advanceSteps > 12) {
|
|
248
|
+
fail('pacing-invalid', 'Pacing advance steps must be an integer from one to twelve');
|
|
249
|
+
}
|
|
250
|
+
if ((proposal.ending !== null) !== (proposal.pacing.mode === 'resolve')) {
|
|
251
|
+
fail('resolution-inconsistent', 'Only a resolve proposal may include an ending');
|
|
252
|
+
}
|
|
253
|
+
if (proposal.ending) {
|
|
254
|
+
assertText(proposal.ending.title, 'ending title', 160);
|
|
255
|
+
assertText(proposal.ending.narrative, 'ending narrative', 2_000);
|
|
256
|
+
if (!['triumph', 'mixed', 'collapse'].includes(proposal.ending.outcome)) {
|
|
257
|
+
fail('ending-outcome-invalid', 'Ending outcome is invalid');
|
|
258
|
+
}
|
|
259
|
+
assertUnique(proposal.ending.causalEventIds, 'duplicate-ending-cause');
|
|
260
|
+
if (!proposal.ending.causalEventIds.includes(proposal.event.id)) {
|
|
261
|
+
fail('ending-current-cause-missing', 'An ending must cite its resolving event');
|
|
262
|
+
}
|
|
263
|
+
if (proposal.ending.causalEventIds.length === 0
|
|
264
|
+
|| proposal.ending.causalEventIds.some(id => (id !== proposal.event.id && !context.knownHistoryEventIds.includes(id)))) {
|
|
265
|
+
fail('ending-cause-invalid', 'An ending must cite known committed or current events');
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return structuredClone(proposal);
|
|
269
|
+
}
|
|
270
|
+
function validateAgentUpdate(update, proposal, context) {
|
|
271
|
+
const currentEventId = proposal.event.id;
|
|
272
|
+
assertActor(update.actorId, context);
|
|
273
|
+
if (update.sourceEventId !== currentEventId) {
|
|
274
|
+
fail('agent-update-cause-invalid', 'An agent consequence must cite the current event');
|
|
275
|
+
}
|
|
276
|
+
assertUnique(update.sourceActionIds, 'duplicate-agent-update-cause');
|
|
277
|
+
if (!validFailedAdjudicationCauses(update, proposal, context.characterProposals, context.humanControlledActorIds, context.agencyVersion)) {
|
|
278
|
+
fail('agent-update-cause-invalid', 'An agent consequence must cite at least one committed domain action');
|
|
279
|
+
}
|
|
280
|
+
for (const actionId of update.sourceActionIds) {
|
|
281
|
+
const action = proposal.domainActions.find(candidate => candidate.id === actionId);
|
|
282
|
+
const participantActorIds = context.actionParticipantActorIds[actionId] ?? [];
|
|
283
|
+
if (!action || !participantActorIds.includes(update.actorId)) {
|
|
284
|
+
fail('agent-update-cause-invalid', 'An agent consequence must cite an involved committed action');
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
if (update.sourceActionIds.length === 0 && Object.values(update.resourceDeltas).some(delta => delta !== 0)) {
|
|
288
|
+
fail('agent-update-cause-invalid', 'A failed adjudication cannot change personal resources');
|
|
289
|
+
}
|
|
290
|
+
const allGoalIds = context.knownAllGoalIds[update.actorId] ?? [];
|
|
291
|
+
const activeGoalIds = new Set(context.knownGoalIds[update.actorId] ?? []);
|
|
292
|
+
assertUnique(update.goalUpdates.map(goal => goal.goalId), 'duplicate-goal-update');
|
|
293
|
+
for (const goal of update.goalUpdates) {
|
|
294
|
+
if (!allGoalIds.includes(goal.goalId)) {
|
|
295
|
+
fail('unknown-goal', `Agent update cites unknown goal ${goal.goalId}`);
|
|
296
|
+
}
|
|
297
|
+
if (goal.priority !== undefined && (!Number.isSafeInteger(goal.priority) || goal.priority < 0 || goal.priority > 100)) {
|
|
298
|
+
fail('priority-out-of-bounds', 'Goal priority must stay between zero and one hundred');
|
|
299
|
+
}
|
|
300
|
+
if (context.agencyVersion !== 'emergent-v1' && goal.priority === undefined && goal.status === undefined) {
|
|
301
|
+
fail('agent-update-noop', 'A goal update must change priority or status');
|
|
302
|
+
}
|
|
303
|
+
if (goal.status === 'active')
|
|
304
|
+
activeGoalIds.add(goal.goalId);
|
|
305
|
+
else if (goal.status !== undefined)
|
|
306
|
+
activeGoalIds.delete(goal.goalId);
|
|
307
|
+
}
|
|
308
|
+
if (update.goalsToAppend.length > 2 || allGoalIds.length + update.goalsToAppend.length > 16) {
|
|
309
|
+
fail('goal-count-invalid', 'Agent goals must remain within the shared bound');
|
|
310
|
+
}
|
|
311
|
+
assertUnique(update.goalsToAppend.map(goal => goal.id), 'duplicate-goal');
|
|
312
|
+
for (const goal of update.goalsToAppend) {
|
|
313
|
+
assertId(goal.id, 'goal id');
|
|
314
|
+
assertNewId(goal.id, allGoalIds);
|
|
315
|
+
assertText(goal.description, 'goal description', 500);
|
|
316
|
+
if (!Number.isSafeInteger(goal.priority) || goal.priority < 0 || goal.priority > 100) {
|
|
317
|
+
fail('priority-out-of-bounds', 'Goal priority must stay between zero and one hundred');
|
|
318
|
+
}
|
|
319
|
+
if (goal.status !== 'active')
|
|
320
|
+
fail('inactive-new-goal', 'A new goal must start active');
|
|
321
|
+
if (!['public', 'player-known', 'private'].includes(goal.visibility)) {
|
|
322
|
+
fail('visibility-invalid', 'Goal visibility is invalid');
|
|
323
|
+
}
|
|
324
|
+
activeGoalIds.add(goal.id);
|
|
325
|
+
}
|
|
326
|
+
assertUnique(update.beliefUpdates.map(belief => belief.beliefId), 'duplicate-belief-update');
|
|
327
|
+
for (const belief of update.beliefUpdates) {
|
|
328
|
+
if (!(context.knownBeliefIds[update.actorId] ?? []).includes(belief.beliefId)) {
|
|
329
|
+
fail('unknown-belief', `Agent update cites unknown belief ${belief.beliefId}`);
|
|
330
|
+
}
|
|
331
|
+
if (!Number.isSafeInteger(belief.confidence) || belief.confidence < 0 || belief.confidence > 100) {
|
|
332
|
+
fail('confidence-out-of-bounds', 'Belief confidence must stay between zero and one hundred');
|
|
333
|
+
}
|
|
334
|
+
assertUnique(belief.evidenceEventIds, 'duplicate-belief-evidence');
|
|
335
|
+
assertEvidenceEvents(belief.evidenceEventIds, currentEventId, context);
|
|
336
|
+
if (!belief.evidenceEventIds.includes(currentEventId)) {
|
|
337
|
+
fail('belief-current-evidence-required', 'A changed belief must cite the event that changed it');
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
const knownBeliefIds = context.knownBeliefIds[update.actorId] ?? [];
|
|
341
|
+
if (update.beliefsToAppend.length > 4 || knownBeliefIds.length + update.beliefsToAppend.length > 32) {
|
|
342
|
+
fail('belief-count-invalid', 'Agent beliefs must remain within the shared bound');
|
|
343
|
+
}
|
|
344
|
+
assertUnique(update.beliefsToAppend.map(belief => belief.id), 'duplicate-belief');
|
|
345
|
+
for (const belief of update.beliefsToAppend) {
|
|
346
|
+
assertId(belief.id, 'belief id');
|
|
347
|
+
assertNewId(belief.id, knownBeliefIds);
|
|
348
|
+
assertText(belief.subject, 'belief subject', 240);
|
|
349
|
+
assertText(belief.claim, 'belief claim', 500);
|
|
350
|
+
if (!Number.isSafeInteger(belief.confidence) || belief.confidence < 0 || belief.confidence > 100) {
|
|
351
|
+
fail('confidence-out-of-bounds', 'Belief confidence must stay between zero and one hundred');
|
|
352
|
+
}
|
|
353
|
+
if (belief.evidenceEventIds.length === 0) {
|
|
354
|
+
fail('belief-evidence-required', 'A new belief must cite at least one committed or current event');
|
|
355
|
+
}
|
|
356
|
+
assertUnique(belief.evidenceEventIds, 'duplicate-belief-evidence');
|
|
357
|
+
assertEvidenceEvents(belief.evidenceEventIds, currentEventId, context);
|
|
358
|
+
if (!belief.evidenceEventIds.includes(currentEventId)) {
|
|
359
|
+
fail('belief-current-evidence-required', 'A new belief must cite the event that created it');
|
|
360
|
+
}
|
|
361
|
+
if (!['public', 'player-known', 'private'].includes(belief.visibility)) {
|
|
362
|
+
fail('visibility-invalid', 'Belief visibility is invalid');
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
assertUnique(update.relationshipDeltas.map(item => item.targetActorId), 'duplicate-relationship-update');
|
|
366
|
+
for (const relationship of update.relationshipDeltas) {
|
|
367
|
+
assertTarget(update.actorId, relationship.targetActorId, context);
|
|
368
|
+
const sharesCitedAction = update.sourceActionIds.some(actionId => ((context.actionParticipantActorIds[actionId] ?? []).includes(relationship.targetActorId)));
|
|
369
|
+
if (context.agencyVersion !== 'emergent-v1' && !sharesCitedAction) {
|
|
370
|
+
fail('relationship-cause-invalid', 'A relationship consequence must connect participants in one cited action');
|
|
371
|
+
}
|
|
372
|
+
for (const value of [relationship.trust, relationship.affinity, relationship.obligation]) {
|
|
373
|
+
if (!Number.isFinite(value) || value < -100 || value > 100) {
|
|
374
|
+
fail('relationship-delta-out-of-bounds', 'Relationship deltas must stay between minus and plus one hundred');
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
if (context.agencyVersion !== 'emergent-v1' && relationship.trust === 0
|
|
378
|
+
&& relationship.affinity === 0
|
|
379
|
+
&& relationship.obligation === 0) {
|
|
380
|
+
fail('agent-update-noop', 'A relationship update must change one relationship value');
|
|
381
|
+
}
|
|
382
|
+
assertText(relationship.reason, 'relationship reason', 300);
|
|
383
|
+
}
|
|
384
|
+
if (update.memoriesToAppend.length > 6)
|
|
385
|
+
fail('memory-count-invalid', 'At most six memories may be added per agent');
|
|
386
|
+
assertUnique(update.memoriesToAppend.map(memory => memory.id), 'duplicate-memory');
|
|
387
|
+
for (const memory of update.memoriesToAppend) {
|
|
388
|
+
assertId(memory.id, 'memory id');
|
|
389
|
+
assertNewId(memory.id, context.knownMemoryIds[update.actorId] ?? []);
|
|
390
|
+
if (memory.eventId !== currentEventId && !context.knownHistoryEventIds.includes(memory.eventId)) {
|
|
391
|
+
fail('unknown-memory-event', 'Memory must cite a known event');
|
|
392
|
+
}
|
|
393
|
+
assertText(memory.summary, 'memory summary', 400);
|
|
394
|
+
if (!Number.isFinite(memory.salience) || memory.salience < 0 || memory.salience > 100) {
|
|
395
|
+
fail('salience-out-of-bounds', 'Memory salience must stay between zero and one hundred');
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
for (const value of Object.values(update.resourceDeltas)) {
|
|
399
|
+
if (!Number.isFinite(value) || value < -1_000_000 || value > 1_000_000) {
|
|
400
|
+
fail('resource-delta-out-of-bounds', 'Resource deltas must be finite and bounded');
|
|
401
|
+
}
|
|
402
|
+
if (value === 0) {
|
|
403
|
+
fail('agent-update-noop', 'An agent resource update must change its resource');
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
if (update.nextIntent && context.agencyVersion !== 'emergent-v1') {
|
|
407
|
+
assertActorMechanic(update.actorId, update.nextIntent.mechanic, context);
|
|
408
|
+
if (!activeGoalIds.has(update.nextIntent.goalId)) {
|
|
409
|
+
if (allGoalIds.includes(update.nextIntent.goalId)) {
|
|
410
|
+
fail('inactive-next-intent', 'Next intent cannot cite an inactive goal');
|
|
411
|
+
}
|
|
412
|
+
fail('unknown-goal', 'Next intent must cite an active goal');
|
|
413
|
+
}
|
|
414
|
+
assertTarget(update.actorId, update.nextIntent.targetActorId, context);
|
|
415
|
+
assertMechanicTarget(update.nextIntent.mechanic, update.nextIntent.targetActorId, context);
|
|
416
|
+
}
|
|
417
|
+
const currentIntent = context.knownCurrentIntents[update.actorId] ?? null;
|
|
418
|
+
const autonomous = !context.humanControlledActorIds.includes(update.actorId);
|
|
419
|
+
if (context.agencyVersion !== 'emergent-v1' && proposal.pacing.mode === 'continue' && autonomous) {
|
|
420
|
+
if (currentIntent !== null
|
|
421
|
+
&& !activeGoalIds.has(currentIntent.goalId)
|
|
422
|
+
&& update.nextIntent === null) {
|
|
423
|
+
fail('intent-successor-required', 'A resolved current intent needs one causal successor while the run continues');
|
|
424
|
+
}
|
|
425
|
+
if (activeGoalIds.size === 0) {
|
|
426
|
+
fail('autonomous-agent-retired', 'A continuing run cannot retire an autonomous character without an active successor goal');
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
function assertEvidenceEvents(eventIds, currentEventId, context) {
|
|
431
|
+
if (eventIds.some(id => !context.knownHistoryEventIds.includes(id) && id !== currentEventId)) {
|
|
432
|
+
fail('unknown-memory-event', 'Belief evidence must cite a known event');
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
function assertCharacterProposalShape(proposal, emergent, invitations) {
|
|
436
|
+
const value = assertExactObject(proposal, ['schemaVersion', 'proposalId', 'basedOnRevision', 'actorId', 'intent', 'attemptedActions', 'internalReason', ...(invitations ? ['discussionInvitation'] : [])], [], 'character proposal');
|
|
437
|
+
if (value.schemaVersion !== 1)
|
|
438
|
+
fail('proposal-shape-invalid', 'Character proposal schema version must be one');
|
|
439
|
+
if (!emergent || value.intent !== null)
|
|
440
|
+
assertIntentShape(value.intent, 'character intent');
|
|
441
|
+
for (const action of assertArray(value.attemptedActions, 'character attemptedActions')) {
|
|
442
|
+
assertCharacterActionShape(action, emergent);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
function assertCharacterActionShape(action, emergent) {
|
|
446
|
+
const value = assertExactObject(action, [
|
|
447
|
+
'id', 'mechanic', 'actorId', 'targetActorId', 'parameters', 'reason', 'motivation',
|
|
448
|
+
], ['targetActorId'], 'character attempted action');
|
|
449
|
+
assertJsonObject(value.parameters, 'character attempted action parameters');
|
|
450
|
+
if (!emergent || value.motivation !== null)
|
|
451
|
+
assertExactObject(value.motivation, ['goalId', 'beliefId', 'memoryId', 'relationshipTargetActorId', 'resourceId'], [], 'character action motivation');
|
|
452
|
+
}
|
|
453
|
+
function validateGmChoices(proposal, context) {
|
|
454
|
+
const resolving = proposal.pacing.mode === 'resolve';
|
|
455
|
+
if (!Array.isArray(proposal.availableActions)
|
|
456
|
+
|| (!resolving && (proposal.availableActions.length < (context.allowScarcePlayerChoices ? 0 : 2) || proposal.availableActions.length > 4))
|
|
457
|
+
|| (resolving && proposal.availableActions.length !== 0)) {
|
|
458
|
+
fail('available-action-count-invalid', 'Active proposals need two to four actions; resolved proposals need none');
|
|
459
|
+
}
|
|
460
|
+
assertUnique(proposal.availableActions.map(action => action.id), 'duplicate-available-action');
|
|
461
|
+
for (let leftIndex = 0; leftIndex < proposal.availableActions.length; leftIndex += 1) {
|
|
462
|
+
const left = proposal.availableActions[leftIndex];
|
|
463
|
+
if (!left)
|
|
464
|
+
continue;
|
|
465
|
+
for (let rightIndex = leftIndex + 1; rightIndex < proposal.availableActions.length; rightIndex += 1) {
|
|
466
|
+
const right = proposal.availableActions[rightIndex];
|
|
467
|
+
if (!right)
|
|
468
|
+
continue;
|
|
469
|
+
if (left.mechanic === right.mechanic
|
|
470
|
+
&& left.targetActorId === right.targetActorId
|
|
471
|
+
&& isDeepStrictEqual(left.parameters, right.parameters)) {
|
|
472
|
+
fail('duplicate-available-action-semantics', 'Available actions must differ in mechanic, target, or parameters');
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
for (const action of proposal.availableActions) {
|
|
477
|
+
assertId(action.id, 'available action id');
|
|
478
|
+
assertPlayerMechanic(action.mechanic, context);
|
|
479
|
+
assertTarget(context.humanControlledActorIds[0], action.targetActorId, context);
|
|
480
|
+
assertText(action.label, 'action label', 120);
|
|
481
|
+
assertText(action.intent, 'action intent', 240);
|
|
482
|
+
assertText(action.description, 'action description', 500);
|
|
483
|
+
if (!['game-master', 'fallback'].includes(action.authoredBy)) {
|
|
484
|
+
fail('action-author-invalid', 'Available action author is invalid');
|
|
485
|
+
}
|
|
486
|
+
if (action.risks.length > 6 || action.requirements.length > 6) {
|
|
487
|
+
fail('action-detail-count-invalid', 'Action risks and requirements are bounded to six each');
|
|
488
|
+
}
|
|
489
|
+
for (const risk of action.risks)
|
|
490
|
+
assertText(risk, 'action risk', 300);
|
|
491
|
+
for (const requirement of action.requirements)
|
|
492
|
+
assertText(requirement, 'action requirement', 300);
|
|
493
|
+
assertBoundedParameters(action.parameters);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
function assertGmProposalShape(proposal, choices, updates = true) {
|
|
497
|
+
const value = assertExactObject(proposal, [
|
|
498
|
+
'schemaVersion', 'proposalId', 'basedOnRevision', 'event', ...(choices ? ['availableActions'] : []),
|
|
499
|
+
'characterResponses', 'adjudications', 'playerAdjudication', 'domainActions',
|
|
500
|
+
...(updates ? ['agentUpdates'] : []), 'pacing', 'ending',
|
|
501
|
+
], [], 'Game Master proposal');
|
|
502
|
+
if (value.schemaVersion !== 1)
|
|
503
|
+
fail('proposal-shape-invalid', 'Game Master proposal schema version must be one');
|
|
504
|
+
assertExactObject(value.event, ['id', 'title', 'narrative', 'urgency', 'openQuestions'], [], 'event');
|
|
505
|
+
for (const action of choices ? assertArray(value.availableActions, 'availableActions') : []) {
|
|
506
|
+
const item = assertExactObject(action, ['id', 'label', 'intent', 'description', 'mechanic', 'targetActorId', 'parameters', 'risks', 'requirements', 'authoredBy'], ['targetActorId'], 'available action');
|
|
507
|
+
assertJsonObject(item.parameters, 'available action parameters');
|
|
508
|
+
assertArray(item.risks, 'available action risks');
|
|
509
|
+
assertArray(item.requirements, 'available action requirements');
|
|
510
|
+
}
|
|
511
|
+
for (const response of assertArray(value.characterResponses, 'characterResponses')) {
|
|
512
|
+
assertExactObject(response, ['actorId', 'text', 'mood'], [], 'character response');
|
|
513
|
+
}
|
|
514
|
+
for (const adjudication of assertArray(value.adjudications, 'adjudications')) {
|
|
515
|
+
const item = assertExactObject(adjudication, ['characterProposalId', 'outcome', 'reason', 'actionIds', 'meetingId'], ['meetingId'], 'character adjudication');
|
|
516
|
+
assertArray(item.actionIds, 'adjudication actionIds');
|
|
517
|
+
}
|
|
518
|
+
if (value.playerAdjudication !== null) {
|
|
519
|
+
const adjudication = assertExactObject(value.playerAdjudication, ['playerActionId', 'outcome', 'reason', 'actionIds'], [], 'player adjudication');
|
|
520
|
+
assertArray(adjudication.actionIds, 'player adjudication actionIds');
|
|
521
|
+
}
|
|
522
|
+
for (const action of assertArray(value.domainActions, 'domainActions')) {
|
|
523
|
+
assertDomainActionShape(action, 'Game Master domain action');
|
|
524
|
+
}
|
|
525
|
+
if (updates) {
|
|
526
|
+
for (const update of assertArray(value.agentUpdates, 'agentUpdates'))
|
|
527
|
+
assertAgentUpdateShape(update);
|
|
528
|
+
}
|
|
529
|
+
assertExactObject(value.pacing, ['mode', 'tension', 'timeAdvance', 'advanceSteps'], [], 'pacing');
|
|
530
|
+
if (value.ending !== null) {
|
|
531
|
+
const ending = assertExactObject(value.ending, ['title', 'narrative', 'outcome', 'causalEventIds', 'endingBasis'], ['endingBasis'], 'ending');
|
|
532
|
+
assertArray(ending.causalEventIds, 'ending causalEventIds');
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
function assertAgentUpdateShape(update) {
|
|
536
|
+
const value = assertExactObject(update, [
|
|
537
|
+
'actorId', 'sourceEventId', 'sourceActionIds', 'sourceAdjudicationProposalIds', 'goalUpdates', 'goalsToAppend', 'beliefUpdates', 'beliefsToAppend', 'relationshipDeltas',
|
|
538
|
+
'memoriesToAppend', 'resourceDeltas', 'nextIntent',
|
|
539
|
+
], ['sourceAdjudicationProposalIds'], 'agent update');
|
|
540
|
+
assertArray(value.sourceActionIds, 'agent update sourceActionIds');
|
|
541
|
+
if (value.sourceAdjudicationProposalIds !== undefined)
|
|
542
|
+
assertArray(value.sourceAdjudicationProposalIds, 'agent update sourceAdjudicationProposalIds');
|
|
543
|
+
for (const goal of assertArray(value.goalUpdates, 'goalUpdates')) {
|
|
544
|
+
assertExactObject(goal, ['goalId', 'priority', 'status'], ['priority', 'status'], 'goal update');
|
|
545
|
+
}
|
|
546
|
+
for (const goal of assertArray(value.goalsToAppend, 'goalsToAppend')) {
|
|
547
|
+
assertExactObject(goal, ['id', 'description', 'priority', 'status', 'visibility'], [], 'new goal');
|
|
548
|
+
}
|
|
549
|
+
for (const belief of assertArray(value.beliefUpdates, 'beliefUpdates')) {
|
|
550
|
+
const item = assertExactObject(belief, ['beliefId', 'confidence', 'evidenceEventIds'], [], 'belief update');
|
|
551
|
+
assertArray(item.evidenceEventIds, 'belief evidenceEventIds');
|
|
552
|
+
}
|
|
553
|
+
for (const belief of assertArray(value.beliefsToAppend, 'beliefsToAppend')) {
|
|
554
|
+
const item = assertExactObject(belief, ['id', 'subject', 'claim', 'confidence', 'evidenceEventIds', 'visibility'], [], 'new belief');
|
|
555
|
+
assertArray(item.evidenceEventIds, 'new belief evidenceEventIds');
|
|
556
|
+
}
|
|
557
|
+
for (const relationship of assertArray(value.relationshipDeltas, 'relationshipDeltas')) {
|
|
558
|
+
assertExactObject(relationship, ['targetActorId', 'trust', 'affinity', 'obligation', 'reason'], [], 'relationship delta');
|
|
559
|
+
}
|
|
560
|
+
for (const memory of assertArray(value.memoriesToAppend, 'memoriesToAppend')) {
|
|
561
|
+
assertExactObject(memory, ['id', 'eventId', 'kind', 'summary', 'salience', 'visibility'], [], 'memory');
|
|
562
|
+
}
|
|
563
|
+
assertJsonObject(value.resourceDeltas, 'agent resourceDeltas');
|
|
564
|
+
if (value.nextIntent !== null)
|
|
565
|
+
assertIntentShape(value.nextIntent, 'next intent');
|
|
566
|
+
}
|
|
567
|
+
function assertIntentShape(intent, label) {
|
|
568
|
+
assertExactObject(intent, ['mechanic', 'goalId', 'summary', 'targetActorId'], ['targetActorId'], label);
|
|
569
|
+
}
|
|
570
|
+
function assertDomainActionShape(action, label) {
|
|
571
|
+
const value = assertExactObject(action, [
|
|
572
|
+
'id', 'mechanic', 'actorId', 'targetActorId', 'sourceCharacterProposalId',
|
|
573
|
+
'sourceAttemptedActionId', 'sourcePlayerActionId', 'parameters', 'reason',
|
|
574
|
+
], [
|
|
575
|
+
'actorId', 'targetActorId', 'sourceCharacterProposalId',
|
|
576
|
+
'sourceAttemptedActionId', 'sourcePlayerActionId',
|
|
577
|
+
], label);
|
|
578
|
+
assertJsonObject(value.parameters, `${label} parameters`);
|
|
579
|
+
}
|
|
580
|
+
function assertExactObject(value, allowedKeys, optionalKeys, label) {
|
|
581
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
582
|
+
fail('proposal-shape-invalid', `${label} must be an object`);
|
|
583
|
+
}
|
|
584
|
+
const record = value;
|
|
585
|
+
const actualKeys = Object.keys(record);
|
|
586
|
+
if (actualKeys.some(key => !allowedKeys.includes(key))) {
|
|
587
|
+
fail('proposal-shape-invalid', `${label} contains an unknown field`);
|
|
588
|
+
}
|
|
589
|
+
if (allowedKeys.some(key => !optionalKeys.includes(key)
|
|
590
|
+
&& !Object.prototype.hasOwnProperty.call(record, key))) {
|
|
591
|
+
fail('proposal-shape-invalid', `${label} is missing a required field`);
|
|
592
|
+
}
|
|
593
|
+
return record;
|
|
594
|
+
}
|
|
595
|
+
function assertJsonObject(value, label) {
|
|
596
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
597
|
+
fail('proposal-shape-invalid', `${label} must be an object`);
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
function assertArray(value, label) {
|
|
601
|
+
if (!Array.isArray(value))
|
|
602
|
+
fail('proposal-shape-invalid', `${label} must be an array`);
|
|
603
|
+
return value;
|
|
604
|
+
}
|
|
605
|
+
function assertDomainAction(action, context) {
|
|
606
|
+
assertId(action.id, 'domain action id');
|
|
607
|
+
if (action.actorId === undefined)
|
|
608
|
+
assertMechanic(action.mechanic, context);
|
|
609
|
+
else
|
|
610
|
+
assertActorMechanic(action.actorId, action.mechanic, context);
|
|
611
|
+
if (action.actorId !== undefined)
|
|
612
|
+
assertActor(action.actorId, context);
|
|
613
|
+
assertTarget(action.actorId, action.targetActorId, context);
|
|
614
|
+
assertText(action.reason, 'domain action reason', 500);
|
|
615
|
+
assertBoundedParameters(action.parameters);
|
|
616
|
+
}
|
|
617
|
+
function validateActionMotivation(action, proposal, context) {
|
|
618
|
+
const motivation = action.motivation;
|
|
619
|
+
if (motivation === null || proposal.intent === null)
|
|
620
|
+
fail('motivation-required', 'Legacy actions require an intent and motivation');
|
|
621
|
+
if (motivation.goalId !== proposal.intent.goalId) {
|
|
622
|
+
fail('action-motivation-goal-invalid', 'An autonomous action must cite its active intent goal');
|
|
623
|
+
}
|
|
624
|
+
if (!(context.knownBeliefIds[proposal.actorId] ?? []).includes(motivation.beliefId)) {
|
|
625
|
+
fail('action-motivation-belief-invalid', 'An autonomous action must cite one belief owned by its actor');
|
|
626
|
+
}
|
|
627
|
+
if (!(context.knownMemoryIds[proposal.actorId] ?? []).includes(motivation.memoryId)) {
|
|
628
|
+
fail('action-motivation-memory-invalid', 'An autonomous action must cite one memory owned by its actor');
|
|
629
|
+
}
|
|
630
|
+
if (!(context.knownRelationshipActorIds[proposal.actorId] ?? []).includes(motivation.relationshipTargetActorId) || (action.targetActorId !== undefined
|
|
631
|
+
&& motivation.relationshipTargetActorId !== action.targetActorId)) {
|
|
632
|
+
fail('action-motivation-relationship-invalid', 'An autonomous action must cite its involved persistent relationship');
|
|
633
|
+
}
|
|
634
|
+
if (!(context.knownResourceIds[proposal.actorId] ?? []).includes(motivation.resourceId)) {
|
|
635
|
+
fail('action-motivation-resource-invalid', 'An autonomous action must cite one resource owned by its actor');
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
function assertGmActionLineage(action, context) {
|
|
639
|
+
if (action.actorId === undefined) {
|
|
640
|
+
if (action.sourceCharacterProposalId !== undefined
|
|
641
|
+
|| action.sourceAttemptedActionId !== undefined
|
|
642
|
+
|| action.sourcePlayerActionId !== undefined) {
|
|
643
|
+
fail('action-lineage-invalid', 'A world action cannot claim an actor action source');
|
|
644
|
+
}
|
|
645
|
+
return;
|
|
646
|
+
}
|
|
647
|
+
if (context.humanControlledActorIds.includes(action.actorId)) {
|
|
648
|
+
const selected = context.selectedPlayerAction;
|
|
649
|
+
if (selected === null
|
|
650
|
+
|| selected.actorId !== action.actorId
|
|
651
|
+
|| selected.mechanic !== action.mechanic
|
|
652
|
+
|| selected.targetActorId !== action.targetActorId
|
|
653
|
+
|| selected.actionId !== action.sourcePlayerActionId
|
|
654
|
+
|| action.sourceCharacterProposalId !== undefined
|
|
655
|
+
|| action.sourceAttemptedActionId !== undefined) {
|
|
656
|
+
fail('human-action-not-selected', 'A human actor action must match the selected player action');
|
|
657
|
+
}
|
|
658
|
+
assertIntentPreserved(selected.parameters, action.parameters);
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
661
|
+
if (action.sourcePlayerActionId !== undefined) {
|
|
662
|
+
fail('action-lineage-invalid', 'An autonomous actor cannot claim a player action source');
|
|
663
|
+
}
|
|
664
|
+
const proposal = context.characterProposals.find(item => (item.proposalId === action.sourceCharacterProposalId));
|
|
665
|
+
const attempted = proposal?.attemptedActions.find(item => (item.actionId === action.sourceAttemptedActionId));
|
|
666
|
+
if (!proposal
|
|
667
|
+
|| proposal.actorId !== action.actorId
|
|
668
|
+
|| !attempted
|
|
669
|
+
|| attempted.mechanic !== action.mechanic
|
|
670
|
+
|| attempted.targetActorId !== action.targetActorId) {
|
|
671
|
+
fail('action-lineage-invalid', 'A character action must match its proposed actor, mechanic, and target');
|
|
672
|
+
}
|
|
673
|
+
assertIntentPreserved(attempted.parameters, action.parameters);
|
|
674
|
+
}
|
|
675
|
+
function assertIntentPreserved(sourceParameters, committedParameters) {
|
|
676
|
+
const sourceIntent = Object.prototype.hasOwnProperty.call(sourceParameters, 'intent')
|
|
677
|
+
? sourceParameters.intent
|
|
678
|
+
: sourceParameters;
|
|
679
|
+
const committedIntent = Object.prototype.hasOwnProperty.call(committedParameters, 'intent')
|
|
680
|
+
? committedParameters.intent
|
|
681
|
+
: committedParameters;
|
|
682
|
+
if (!isDeepStrictEqual(sourceIntent, committedIntent)) {
|
|
683
|
+
fail('action-intent-changed', 'The Game Master cannot replace a source action intent');
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
function assertRevision(actual, expected) {
|
|
687
|
+
if (!Number.isSafeInteger(actual) || actual !== expected) {
|
|
688
|
+
fail('stale-revision', `Proposal revision ${actual} does not match ${expected}`);
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
function assertMechanic(mechanic, context) {
|
|
692
|
+
if (!context.allowedMechanics.includes(mechanic)) {
|
|
693
|
+
fail('mechanic-not-allowed', `Mechanic ${mechanic} is not allowed`);
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
function assertActorMechanic(actorId, mechanic, context) {
|
|
697
|
+
assertMechanic(mechanic, context);
|
|
698
|
+
if (!context.actorMechanics[actorId]?.includes(mechanic)) {
|
|
699
|
+
fail('actor-mechanic-not-allowed', `Actor ${actorId} cannot use mechanic ${mechanic}`);
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
function assertMechanicTarget(mechanic, targetActorId, context) {
|
|
703
|
+
const target = context.mechanicTargets[mechanic];
|
|
704
|
+
if (target === undefined) {
|
|
705
|
+
fail('mechanic-not-allowed', `Mechanic ${mechanic} has no target contract`);
|
|
706
|
+
}
|
|
707
|
+
if (target === 'required' && targetActorId === undefined) {
|
|
708
|
+
fail('mechanic-target-invalid', `Mechanic ${mechanic} needs a target actor`);
|
|
709
|
+
}
|
|
710
|
+
if (target === 'none' && targetActorId !== undefined) {
|
|
711
|
+
fail('mechanic-target-invalid', `Mechanic ${mechanic} cannot target an actor`);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
function assertPlayerMechanic(mechanic, context) {
|
|
715
|
+
assertMechanic(mechanic, context);
|
|
716
|
+
if (!context.humanControlledActorIds.some(actorId => (context.actorMechanics[actorId]?.includes(mechanic)))) {
|
|
717
|
+
fail('player-mechanic-not-allowed', `Players cannot use mechanic ${mechanic}`);
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
function assertActor(actorId, context) {
|
|
721
|
+
if (!context.knownActorIds.includes(actorId))
|
|
722
|
+
fail('unknown-actor', `Actor ${actorId} is unknown`);
|
|
723
|
+
}
|
|
724
|
+
function assertTarget(actorId, targetActorId, context) {
|
|
725
|
+
if (targetActorId === undefined)
|
|
726
|
+
return;
|
|
727
|
+
assertActor(targetActorId, context);
|
|
728
|
+
if (actorId === targetActorId)
|
|
729
|
+
fail('self-target-invalid', 'An actor cannot target itself');
|
|
730
|
+
}
|
|
731
|
+
function assertId(value, label) {
|
|
732
|
+
if (typeof value !== 'string' || value.trim() === '' || value.length > 128) {
|
|
733
|
+
fail('id-invalid', `${label} must be a non-empty identifier of at most 128 characters`);
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
function assertText(value, label, maximum) {
|
|
737
|
+
if (typeof value !== 'string' || value.trim() === '' || value.length > maximum) {
|
|
738
|
+
fail('text-invalid', `${label} must contain bounded text`);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
function assertUnique(values, code) {
|
|
742
|
+
if (new Set(values).size !== values.length)
|
|
743
|
+
fail(code, 'Identifiers must be unique');
|
|
744
|
+
}
|
|
745
|
+
function assertNewId(value, known) {
|
|
746
|
+
if (known.includes(value))
|
|
747
|
+
fail('identifier-reused', `Identifier ${value} was already committed`);
|
|
748
|
+
}
|
|
749
|
+
function assertCanonicalProposal(parse) {
|
|
750
|
+
try {
|
|
751
|
+
return parse();
|
|
752
|
+
}
|
|
753
|
+
catch (error) {
|
|
754
|
+
const message = error instanceof Error ? error.message : 'Dynamic proposal shape is invalid';
|
|
755
|
+
fail('proposal-shape-invalid', message);
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
function assertBoundedParameters(value) {
|
|
759
|
+
let serialized;
|
|
760
|
+
try {
|
|
761
|
+
serialized = JSON.stringify(value);
|
|
762
|
+
}
|
|
763
|
+
catch {
|
|
764
|
+
fail('parameters-out-of-bounds', 'Action parameters must be valid bounded JSON');
|
|
765
|
+
}
|
|
766
|
+
if (new TextEncoder().encode(serialized).byteLength > 2_048) {
|
|
767
|
+
fail('parameters-out-of-bounds', 'Action parameters exceed 2,048 bytes');
|
|
768
|
+
}
|
|
769
|
+
visitJson(value, 0, { keys: 0 });
|
|
770
|
+
}
|
|
771
|
+
function visitJson(value, depth, budget) {
|
|
772
|
+
if (depth > 4)
|
|
773
|
+
fail('parameters-out-of-bounds', 'Action parameters exceed four levels');
|
|
774
|
+
if (value === null || typeof value === 'boolean')
|
|
775
|
+
return;
|
|
776
|
+
if (typeof value === 'string') {
|
|
777
|
+
if (value.length > 1_000)
|
|
778
|
+
fail('parameters-out-of-bounds', 'Action parameter text is too long');
|
|
779
|
+
return;
|
|
780
|
+
}
|
|
781
|
+
if (typeof value === 'number') {
|
|
782
|
+
if (!Number.isFinite(value))
|
|
783
|
+
fail('parameters-out-of-bounds', 'Action parameter numbers must be finite');
|
|
784
|
+
return;
|
|
785
|
+
}
|
|
786
|
+
if (Array.isArray(value)) {
|
|
787
|
+
if (value.length > 32)
|
|
788
|
+
fail('parameters-out-of-bounds', 'Action parameter arrays are limited to 32 items');
|
|
789
|
+
for (const item of value)
|
|
790
|
+
visitJson(item, depth + 1, budget);
|
|
791
|
+
return;
|
|
792
|
+
}
|
|
793
|
+
if (typeof value !== 'object') {
|
|
794
|
+
fail('parameters-out-of-bounds', 'Action parameters must contain JSON values');
|
|
795
|
+
}
|
|
796
|
+
const entries = Object.entries(value);
|
|
797
|
+
budget.keys += entries.length;
|
|
798
|
+
if (budget.keys > 32)
|
|
799
|
+
fail('parameters-out-of-bounds', 'Action parameters are limited to 32 keys');
|
|
800
|
+
for (const [, item] of entries)
|
|
801
|
+
visitJson(item, depth + 1, budget);
|
|
802
|
+
}
|
|
803
|
+
function fail(code, message) {
|
|
804
|
+
throw new DynamicGameContractError(code, message);
|
|
805
|
+
}
|