@moretea-labs/forge 1.5.0-rc.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/AGENTS.md +120 -0
- package/CHANGELOG.md +36 -0
- package/CLAUDE.md +120 -0
- package/CODE_OF_CONDUCT.md +23 -0
- package/CONTRIBUTING.md +77 -0
- package/LICENSE +22 -0
- package/NOTICE +9 -0
- package/README.en.md +5 -0
- package/README.es.md +12 -0
- package/README.fr.md +12 -0
- package/README.ja.md +12 -0
- package/README.md +132 -0
- package/README.zh-CN.md +84 -0
- package/SECURITY.md +50 -0
- package/SKILL.md +431 -0
- package/SUPPORT.md +31 -0
- package/THIRD_PARTY_NOTICES.md +18 -0
- package/agents/openai.yaml +4 -0
- package/assets/AGENTS.md +68 -0
- package/assets/CLAUDE.md +68 -0
- package/assets/branding/README.md +10 -0
- package/assets/branding/forge-controller-flow.mmd +8 -0
- package/assets/hooks/AGENTS.md +75 -0
- package/assets/hooks/CLAUDE.md +75 -0
- package/assets/hooks/anti-simplification.sh +12 -0
- package/assets/hooks/changelog-guard.sh +80 -0
- package/assets/hooks/codex.hooks.template.json +60 -0
- package/assets/hooks/first-principles-guard.sh +72 -0
- package/assets/hooks/hook-input.sh +618 -0
- package/assets/hooks/lib/session-state.sh +106 -0
- package/assets/hooks/lib/workflow-state.sh +1730 -0
- package/assets/hooks/post-bash.sh +215 -0
- package/assets/hooks/post-edit-guard.sh +256 -0
- package/assets/hooks/post-tool-observer.sh +99 -0
- package/assets/hooks/pre-edit-guard.sh +240 -0
- package/assets/hooks/prompt-guard.sh +1077 -0
- package/assets/hooks/run-hook.sh +67 -0
- package/assets/hooks/security-sentinel.sh +115 -0
- package/assets/hooks/session-start-context.sh +593 -0
- package/assets/hooks/settings.template.json +60 -0
- package/assets/hooks/stop-orchestrator.sh +171 -0
- package/assets/hooks/subagent-return-channel-guard.sh +107 -0
- package/assets/hooks/worktree-guard.sh +39 -0
- package/assets/initializer-question-pack.v1.json +132 -0
- package/assets/initializer-question-pack.v1.schema.json +46 -0
- package/assets/initializer-question-pack.v2.json +174 -0
- package/assets/initializer-question-pack.v3.json +206 -0
- package/assets/initializer-question-pack.v3.schema.json +92 -0
- package/assets/initializer-question-pack.v4.json +557 -0
- package/assets/initializer-question-pack.v4.schema.json +169 -0
- package/assets/partials/01-header.partial.md +10 -0
- package/assets/partials/02-iron-rules.partial.md +30 -0
- package/assets/partials/03-philosophy.partial.md +32 -0
- package/assets/partials/04-project-structure.partial.md +56 -0
- package/assets/partials/05-workflow.partial.md +79 -0
- package/assets/partials/06-cloudflare.partial.md +20 -0
- package/assets/partials/07-footer.partial.md +38 -0
- package/assets/partials/08-orchestration.partial.md +49 -0
- package/assets/partials/09-compact-instructions.partial.md +11 -0
- package/assets/partials/_assembly-order.md +40 -0
- package/assets/partials-agents/01-header.partial.md +8 -0
- package/assets/partials-agents/02-operating-mode.partial.md +44 -0
- package/assets/partials-agents/03-orchestration.partial.md +45 -0
- package/assets/partials-agents/04-task-protocol.partial.md +69 -0
- package/assets/partials-agents/05-coding-constraints.partial.md +20 -0
- package/assets/partials-agents/06-quality-safety.partial.md +23 -0
- package/assets/partials-agents/07-cloudflare.partial.md +9 -0
- package/assets/partials-agents/08-deep-docs.partial.md +32 -0
- package/assets/plan-map.json +301 -0
- package/assets/plugin-registry.v1.json +8 -0
- package/assets/project-structures/ai-native-collaborative-editor.txt +25 -0
- package/assets/project-structures/ai-native-product-copilot.txt +21 -0
- package/assets/project-structures/ai-native-runtime-console.txt +25 -0
- package/assets/project-structures/ai-native-sidecar-kernel.txt +19 -0
- package/assets/project-structures/astro-ssr.txt +25 -0
- package/assets/project-structures/expo-nativewind.txt +52 -0
- package/assets/project-structures/monorepo.txt +63 -0
- package/assets/project-structures/remix.txt +50 -0
- package/assets/project-structures/tanstack-start-workers.txt +51 -0
- package/assets/project-structures/vite-tanstack.txt +58 -0
- package/assets/reference-configs/AGENTS.md +13 -0
- package/assets/reference-configs/CLAUDE.md +13 -0
- package/assets/reference-configs/agentic-development-flow.md +94 -0
- package/assets/reference-configs/ai-workflows.md +14 -0
- package/assets/reference-configs/changelog-versioning.md +13 -0
- package/assets/reference-configs/coding-standards.md +14 -0
- package/assets/reference-configs/development-protocol.md +21 -0
- package/assets/reference-configs/document-generation.md +38 -0
- package/assets/reference-configs/evaluator-rubric.md +19 -0
- package/assets/reference-configs/external-tooling.md +374 -0
- package/assets/reference-configs/git-strategy.md +14 -0
- package/assets/reference-configs/global-working-rules.md +64 -0
- package/assets/reference-configs/handoff-protocol.md +38 -0
- package/assets/reference-configs/harness-overview.md +124 -0
- package/assets/reference-configs/heartbeat-triage.md +57 -0
- package/assets/reference-configs/hook-operations.md +96 -0
- package/assets/reference-configs/release-deploy.md +40 -0
- package/assets/reference-configs/spa-day-protocol.md +14 -0
- package/assets/reference-configs/sprint-contracts.md +89 -0
- package/assets/reference-configs/workflow-orchestration.md +14 -0
- package/assets/skill-hooks.json +41 -0
- package/assets/skill-version.json +189 -0
- package/assets/skills/claude-review/SKILL.md +231 -0
- package/assets/skills/codex-review/SKILL.md +103 -0
- package/assets/templates/AGENTS.md +13 -0
- package/assets/templates/CLAUDE.md +13 -0
- package/assets/templates/architecture.template.md +80 -0
- package/assets/templates/brief.template.md +38 -0
- package/assets/templates/contract.template.md +104 -0
- package/assets/templates/decisions.template.md +85 -0
- package/assets/templates/factor-factory/factor-backtest-report.template.md +36 -0
- package/assets/templates/factor-factory/factor-hypothesis.template.md +30 -0
- package/assets/templates/factor-factory/factor-registry.template.json +5 -0
- package/assets/templates/gitignore.template +53 -0
- package/assets/templates/guides/metro-esm-gotchas.template.md +46 -0
- package/assets/templates/helpers/architecture-event.ts +822 -0
- package/assets/templates/helpers/architecture-queue.sh +647 -0
- package/assets/templates/helpers/archive-architecture-request.sh +244 -0
- package/assets/templates/helpers/archive-workflow.sh +295 -0
- package/assets/templates/helpers/capability-config.ts +394 -0
- package/assets/templates/helpers/capability-resolver.ts +505 -0
- package/assets/templates/helpers/capture-plan.sh +335 -0
- package/assets/templates/helpers/check-agent-tooling.sh +1533 -0
- package/assets/templates/helpers/check-architecture-sync.sh +287 -0
- package/assets/templates/helpers/check-brain-manifest.sh +255 -0
- package/assets/templates/helpers/check-context-files.sh +49 -0
- package/assets/templates/helpers/check-deploy-sql-order.sh +117 -0
- package/assets/templates/helpers/check-skill-version.ts +193 -0
- package/assets/templates/helpers/check-task-sync.sh +65 -0
- package/assets/templates/helpers/check-task-workflow.sh +1002 -0
- package/assets/templates/helpers/codex-handoff-resume.sh +269 -0
- package/assets/templates/helpers/context-contract-sync.sh +516 -0
- package/assets/templates/helpers/contract-run.ts +465 -0
- package/assets/templates/helpers/contract-worktree.sh +725 -0
- package/assets/templates/helpers/ensure-task-workflow.sh +1218 -0
- package/assets/templates/helpers/harness-trace-grade.sh +150 -0
- package/assets/templates/helpers/heartbeat-triage.sh +361 -0
- package/assets/templates/helpers/inspect-project-state.ts +280 -0
- package/assets/templates/helpers/maintenance-triage.sh +167 -0
- package/assets/templates/helpers/migrate-project-template.sh +54 -0
- package/assets/templates/helpers/migrate-workflow-docs.ts +413 -0
- package/assets/templates/helpers/new-plan.sh +183 -0
- package/assets/templates/helpers/new-spec.sh +50 -0
- package/assets/templates/helpers/new-sprint.sh +49 -0
- package/assets/templates/helpers/plan-to-todo.sh +799 -0
- package/assets/templates/helpers/prepare-codex-handoff.sh +203 -0
- package/assets/templates/helpers/prepare-handoff.sh +81 -0
- package/assets/templates/helpers/refresh-current-status.sh +469 -0
- package/assets/templates/helpers/select-agent-context-blocks.sh +66 -0
- package/assets/templates/helpers/ship-worktrees.sh +561 -0
- package/assets/templates/helpers/sprint-backlog.sh +837 -0
- package/assets/templates/helpers/summarize-failures.sh +125 -0
- package/assets/templates/helpers/switch-plan.sh +153 -0
- package/assets/templates/helpers/sync-brain-docs.sh +381 -0
- package/assets/templates/helpers/verify-contract.sh +705 -0
- package/assets/templates/helpers/verify-sprint.sh +680 -0
- package/assets/templates/helpers/workflow-contract.ts +202 -0
- package/assets/templates/helpers/workstream-sync.sh +263 -0
- package/assets/templates/implementation-notes.template.md +37 -0
- package/assets/templates/packages.template.md +45 -0
- package/assets/templates/plan.template.md +77 -0
- package/assets/templates/prd.template.md +165 -0
- package/assets/templates/research.template.md +25 -0
- package/assets/templates/review.template.md +78 -0
- package/assets/templates/spec.template.md +31 -0
- package/assets/templates/sprint.template.md +69 -0
- package/assets/templates/tech-stack.template.md +57 -0
- package/assets/versions.json +77 -0
- package/assets/workflow-contract.v1.json +671 -0
- package/bin/forge-desktop-helper.mjs +204 -0
- package/bin/forge-hook.mjs +32 -0
- package/bin/forge-runtime-service.mjs +34 -0
- package/bin/forge-runtime.mjs +30 -0
- package/bin/forge.mjs +32 -0
- package/docs/CHANGELOG.md +978 -0
- package/docs/README.md +72 -0
- package/docs/README.zh-CN.md +58 -0
- package/docs/architecture/chatgpt-handoff-facade.md +393 -0
- package/docs/architecture/current/README.md +158 -0
- package/docs/architecture/current/approved-target-architecture.zh-CN.md +1501 -0
- package/docs/architecture/current/architecture-invariants.md +331 -0
- package/docs/architecture/current/automation-and-schedule-engine.md +453 -0
- package/docs/architecture/current/codegraph-context-provider.md +226 -0
- package/docs/architecture/current/control-plane-authority-inventory.md +82 -0
- package/docs/architecture/current/controller-repository-decoupling.md +148 -0
- package/docs/architecture/current/dispatch-and-agent-strategy.md +354 -0
- package/docs/architecture/current/entity-model.md +619 -0
- package/docs/architecture/current/external-plugin-governance.md +396 -0
- package/docs/architecture/current/failure-recovery.md +471 -0
- package/docs/architecture/current/governance.md +213 -0
- package/docs/architecture/current/human-interaction-plane.md +87 -0
- package/docs/architecture/current/implementation-status.md +128 -0
- package/docs/architecture/current/job-and-run-lifecycle.md +413 -0
- package/docs/architecture/current/local-workspace-target-grants.md +70 -0
- package/docs/architecture/current/migration-roadmap.md +105 -0
- package/docs/architecture/current/multi-repository-execution.md +371 -0
- package/docs/architecture/current/operations-runbook.md +45 -0
- package/docs/architecture/current/peer-supercontroller-runtime.md +32 -0
- package/docs/architecture/current/personal-assistant-plugin-baseline.md +207 -0
- package/docs/architecture/current/repository-access-modes.md +102 -0
- package/docs/architecture/current/route-policy.md +15 -0
- package/docs/architecture/current/route-session-concurrency.md +85 -0
- package/docs/architecture/current/runtime-architecture-simplification.md +214 -0
- package/docs/architecture/current/runtime-directory-map.md +49 -0
- package/docs/architecture/current/runtime-health-and-resource-lifecycle.md +85 -0
- package/docs/architecture/current/scheduler-and-resource-claims.md +432 -0
- package/docs/architecture/current/session-aware-execution-and-authorization.md +77 -0
- package/docs/architecture/current/system-overview.md +144 -0
- package/docs/architecture/current/target-requirements-traceability.md +202 -0
- package/docs/architecture/current/thin-harness-governance-policy.md +32 -0
- package/docs/architecture/current/thin-harness-v1.md +313 -0
- package/docs/architecture/current/verification-and-release-gates.md +391 -0
- package/docs/architecture/decisions/20260801-controller-home-sqlite-state.md +100 -0
- package/docs/architecture/decisions/20260803-canonical-skill-surface.md +40 -0
- package/docs/architecture/decisions/20260803-release-identity-binding-and-exit-policy.md +53 -0
- package/docs/architecture/diagrams/.gitkeep +1 -0
- package/docs/architecture/diagrams/agentic-dev-plugin-architecture.html +186 -0
- package/docs/architecture/domains/.gitkeep +0 -0
- package/docs/architecture/domains/public-surface.md +30 -0
- package/docs/architecture/domains/runtime-harness.md +33 -0
- package/docs/architecture/domains/verification.md +33 -0
- package/docs/architecture/domains/workflow-engine.md +30 -0
- package/docs/architecture/global-hook-runtime.md +162 -0
- package/docs/architecture/history.md +38 -0
- package/docs/architecture/index.md +114 -0
- package/docs/architecture/ios-semantic-automation-provider-v2.md +166 -0
- package/docs/architecture/modules/.gitkeep +0 -0
- package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +24 -0
- package/docs/architecture/requests/.gitkeep +1 -0
- package/docs/architecture/requests/archive/2026/20260528-154740-ai-hooks-ai-hooks-hook-input-sh.md +59 -0
- package/docs/architecture/requests/archive/2026/20260528-154816-assets-hooks-assets-hooks-hook-input-sh.md +59 -0
- package/docs/architecture/requests/archive/2026/20260528-161851-ai-harness-workflow-contract-json-ai-harness-workflow-contract-json.md +59 -0
- package/docs/architecture/requests/archive/2026/20260528-161853-scripts-check-task-workflow-sh-scripts-check-task-workflow-sh.md +59 -0
- package/docs/architecture/requests/archive/2026/20260528-161911-assets-workflow-contract-v1-json-assets-workflow-contract-v1-json.md +59 -0
- package/docs/architecture/requests/archive/2026/20260528-222416-ai-hooks-ai-hooks-hook-input-sh.md +59 -0
- package/docs/architecture/requests/archive/2026/20260528-222444-ai-hooks-ai-hooks-pre-edit-guard-sh.md +59 -0
- package/docs/architecture/requests/archive/2026/20260528-222457-ai-hooks-ai-hooks-pre-edit-guard-sh.md +59 -0
- package/docs/architecture/requests/archive/2026/20260528-222648-ai-hooks-ai-hooks-pre-edit-guard-sh.md +59 -0
- package/docs/architecture/requests/archive/2026/20260528-222705-ai-hooks-ai-hooks-worktree-guard-sh.md +59 -0
- package/docs/architecture/requests/archive/2026/20260528-222726-ai-hooks-ai-hooks-prompt-guard-sh.md +59 -0
- package/docs/architecture/requests/archive/2026/20260528-222727-ai-hooks-ai-hooks-prompt-guard-sh.md +59 -0
- package/docs/architecture/requests/archive/2026/20260528-224736-ai-hooks-ai-hooks-hook-input-sh.md +59 -0
- package/docs/architecture/requests/archive/2026/20260528-233259-ai-hooks-ai-hooks-prompt-guard-sh.md +59 -0
- package/docs/architecture/requests/archive/2026/20260528-233335-assets-hooks-assets-hooks-prompt-guard-sh.md +59 -0
- package/docs/architecture/requests/archive/2026/20260528-233456-ai-hooks-ai-hooks-prompt-guard-sh.md +59 -0
- package/docs/architecture/requests/archive/2026/20260528-233457-assets-hooks-assets-hooks-prompt-guard-sh.md +59 -0
- package/docs/architecture/requests/archive/2026/20260528-233637-ai-hooks-ai-hooks-prompt-guard-sh.md +59 -0
- package/docs/architecture/requests/archive/2026/20260528-233638-assets-hooks-assets-hooks-prompt-guard-sh.md +59 -0
- package/docs/architecture/requests/archive/2026/20260529-000322-root-package-json.md +59 -0
- package/docs/architecture/requests/archive/2026/20260529-004238-ai-hooks-ai-hooks-lib-workflow-state-sh.md +59 -0
- package/docs/architecture/requests/archive/2026/20260529-020654-ai-harness-policy-json-ai-harness-policy-json.md +63 -0
- package/docs/architecture/requests/archive/2026/20260529-094236-ai-hooks-ai-hooks-prompt-guard-sh.md +60 -0
- package/docs/architecture/requests/archive/2026/20260529-094323-assets-hooks-assets-hooks-prompt-guard-sh.md +60 -0
- package/docs/architecture/requests/archive/2026/20260529-094331-ai-hooks-ai-hooks-prompt-guard-sh.md +60 -0
- package/docs/architecture/requests/archive/2026/20260529-094339-assets-hooks-assets-hooks-prompt-guard-sh.md +60 -0
- package/docs/architecture/requests/archive/2026/20260529-094430-assets-hooks-assets-hooks-session-start-context-sh.md +60 -0
- package/docs/architecture/requests/archive/2026/20260529-094446-ai-hooks-ai-hooks-session-start-context-sh.md +60 -0
- package/docs/architecture/requests/archive/2026/20260613-002040-runtime-harness-hook-adapters.md +68 -0
- package/docs/architecture/requests/archive/2026/root.md +68 -0
- package/docs/architecture/requests/archive/2026/runtime-harness-hook-adapters.md +76 -0
- package/docs/architecture/requests/archive/2026/verification-evals-checks.md +68 -0
- package/docs/architecture/requests/archive/2026/workflow-engine-contract-assets.md +69 -0
- package/docs/architecture/reviews/2026-07-11-access-surface-review.md +99 -0
- package/docs/architecture/snapshots/.gitkeep +1 -0
- package/docs/forge-autonomous-goal-loop.md +7 -0
- package/docs/forge-chatgpt-browser-engine.md +11 -0
- package/docs/forge-chatgpt-mcp-setup.md +239 -0
- package/docs/forge-chatgpt-supervised-automation.md +11 -0
- package/docs/forge-github-issue-launcher.md +7 -0
- package/docs/forge-plugin-management.md +95 -0
- package/docs/forge-provider-configuration.md +7 -0
- package/docs/forge-runtime-self-healing-loop.md +7 -0
- package/docs/images/forge-banner-cn.svg +1 -0
- package/docs/images/forge-banner.svg +1 -0
- package/docs/images/forge-controller-flow.svg +1 -0
- package/docs/images/image.png +0 -0
- package/docs/images/repo-harness-gptpro-cn.png +0 -0
- package/docs/images/repo-harness-gptpro.png +0 -0
- package/docs/images/repo-harness-hook-carrot.png +0 -0
- package/docs/operations/chatgpt-personal-assistant-runtime.md +83 -0
- package/docs/operations/checkout-route-integrity.md +22 -0
- package/docs/operations/controller-browser-plugin.md +299 -0
- package/docs/operations/controller-desktop-plugin.md +112 -0
- package/docs/operations/controller-performance-and-502.md +126 -0
- package/docs/operations/controller-reliability-runbook.md +134 -0
- package/docs/operations/environment-hardening.md +50 -0
- package/docs/operations/features.md +45 -0
- package/docs/operations/features.zh-CN.md +45 -0
- package/docs/operations/github-repository.md +60 -0
- package/docs/operations/gmail-assistant-routines.md +186 -0
- package/docs/operations/homebrew.md +26 -0
- package/docs/operations/local-system-assistant.md +46 -0
- package/docs/operations/local-system-assistant.zh-CN.md +46 -0
- package/docs/operations/mcp-tool-exposure.md +58 -0
- package/docs/operations/mobile-intents-shortcuts.md +117 -0
- package/docs/operations/open-source-release-hygiene.md +31 -0
- package/docs/operations/personal-assistant-operator-guide.md +61 -0
- package/docs/operations/platform-support.md +62 -0
- package/docs/operations/platform-support.zh-CN.md +61 -0
- package/docs/operations/provider-configuration.md +31 -0
- package/docs/operations/releasing.md +86 -0
- package/docs/operations/releasing.zh-CN.md +86 -0
- package/docs/operations/route-session-concurrency-baseline.json +893 -0
- package/docs/operations/route-session-concurrency-baseline.md +94 -0
- package/docs/operations/runtime-performance-diagnostics.md +14 -0
- package/docs/operations/sqlite-control-plane-recovery.md +65 -0
- package/docs/operations/standalone-disaster-recovery.md +136 -0
- package/docs/operations/troubleshooting.md +72 -0
- package/docs/operations/troubleshooting.zh-CN.md +70 -0
- package/docs/public-usage-guide.md +33 -0
- package/docs/public-usage-guide.zh-CN.md +33 -0
- package/docs/tutorials/01-install-and-start.md +93 -0
- package/docs/tutorials/01-install-and-start.zh-CN.md +93 -0
- package/docs/tutorials/02-connect-chatgpt.md +82 -0
- package/docs/tutorials/02-connect-chatgpt.zh-CN.md +80 -0
- package/docs/tutorials/03-first-repository-task.md +57 -0
- package/docs/tutorials/03-first-repository-task.zh-CN.md +57 -0
- package/docs/tutorials/README.md +13 -0
- package/docs/tutorials/README.zh-CN.md +13 -0
- package/docs/wiki/Architecture.md +85 -0
- package/docs/wiki/Core-Concepts.md +27 -0
- package/docs/wiki/Home.md +36 -0
- package/docs/wiki/Implementation.md +172 -0
- package/docs/wiki/Installation.md +50 -0
- package/docs/wiki/Integrations.md +29 -0
- package/docs/wiki/Operations.md +26 -0
- package/docs/wiki/Quick-Start.md +46 -0
- package/docs/wiki/Releases-and-Upgrades.md +34 -0
- package/docs/wiki/Runtime-Architecture.md +50 -0
- package/docs/wiki/Security-Model.md +31 -0
- package/docs/wiki/Troubleshooting.md +38 -0
- package/docs/wiki/Work-Lifecycle.md +31 -0
- package/docs/wiki/_Sidebar.md +21 -0
- package/install.ps1 +48 -0
- package/install.sh +66 -0
- package/package.json +156 -0
- package/scripts/activate-source-baseline.ts +335 -0
- package/scripts/architecture-event.ts +822 -0
- package/scripts/architecture-queue.sh +647 -0
- package/scripts/archive-architecture-request.sh +244 -0
- package/scripts/archive-workflow.sh +295 -0
- package/scripts/assemble-template.ts +1172 -0
- package/scripts/benchmark-controller-hotpath.ts +212 -0
- package/scripts/benchmark-controller-round2.ts +139 -0
- package/scripts/benchmark-ios-agent-device.ts +514 -0
- package/scripts/benchmark-route-session-concurrency.ts +714 -0
- package/scripts/benchmark-thin-harness-gateway-ab.ts +632 -0
- package/scripts/benchmark-thin-harness-mcp-e2e.ts +479 -0
- package/scripts/benchmark-thin-harness.ts +251 -0
- package/scripts/bootstrap-runtime-maintenance-recovery.sh +183 -0
- package/scripts/canary-global-hook.sh +263 -0
- package/scripts/capability-config.ts +394 -0
- package/scripts/capability-resolver.ts +505 -0
- package/scripts/capture-plan.sh +335 -0
- package/scripts/check-agent-tooling.sh +1549 -0
- package/scripts/check-architecture-sync.sh +373 -0
- package/scripts/check-brain-manifest.sh +255 -0
- package/scripts/check-ci.sh +16 -0
- package/scripts/check-context-files.sh +49 -0
- package/scripts/check-deploy-sql-order.sh +117 -0
- package/scripts/check-mcp-compatibility.ts +93 -0
- package/scripts/check-npm-release.sh +32 -0
- package/scripts/check-open-source-tracked-surface.sh +146 -0
- package/scripts/check-package-identity.mjs +117 -0
- package/scripts/check-platform-support.mjs +79 -0
- package/scripts/check-public-docs.sh +105 -0
- package/scripts/check-public-release-surface.sh +19 -0
- package/scripts/check-release-published.sh +57 -0
- package/scripts/check-release-readiness.sh +43 -0
- package/scripts/check-release-version.mjs +74 -0
- package/scripts/check-runtime-architecture.mjs +593 -0
- package/scripts/check-runtime-env.mjs +89 -0
- package/scripts/check-skill-version.ts +193 -0
- package/scripts/check-tarball-install-smoke.sh +66 -0
- package/scripts/check-task-sync.sh +65 -0
- package/scripts/check-task-workflow.sh +1002 -0
- package/scripts/check-third-party-notices.mjs +36 -0
- package/scripts/codex-handoff-resume.sh +269 -0
- package/scripts/complete-control-plane-cutover.ts +126 -0
- package/scripts/context-contract-sync.sh +516 -0
- package/scripts/contract-run.ts +465 -0
- package/scripts/contract-worktree.sh +725 -0
- package/scripts/controller-ngrok-rotation.sh +678 -0
- package/scripts/create-project-dirs.sh +282 -0
- package/scripts/ensure-codegraph.sh +16 -0
- package/scripts/ensure-task-workflow.sh +1218 -0
- package/scripts/export-requirement-portfolio.ts +28 -0
- package/scripts/factor-lab-check.sh +73 -0
- package/scripts/factor-lab-new.sh +96 -0
- package/scripts/factor-lab-promote.sh +86 -0
- package/scripts/factor-lab-reject.sh +68 -0
- package/scripts/harness-trace-grade.sh +150 -0
- package/scripts/heartbeat-triage.sh +361 -0
- package/scripts/hook-dispatch-diet-report.ts +234 -0
- package/scripts/hook-shim.sh +93 -0
- package/scripts/init-project.sh +505 -0
- package/scripts/initializer-question-pack.ts +200 -0
- package/scripts/inspect-project-state.ts +280 -0
- package/scripts/install-desktop-operator-registration.ts +45 -0
- package/scripts/install-standalone-recovery.ts +74 -0
- package/scripts/lib/AGENTS.md +13 -0
- package/scripts/lib/CLAUDE.md +13 -0
- package/scripts/lib/controller-home-env.sh +69 -0
- package/scripts/lib/project-init-lib.sh +2759 -0
- package/scripts/load-standalone-recovery.sh +17 -0
- package/scripts/loop-engine-cutover-gate.ts +324 -0
- package/scripts/maintenance-triage.sh +167 -0
- package/scripts/migrate-project-template.sh +1189 -0
- package/scripts/migrate-requirement-portfolio.ts +86 -0
- package/scripts/migrate-workflow-docs.ts +413 -0
- package/scripts/new-plan.sh +183 -0
- package/scripts/new-spec.sh +50 -0
- package/scripts/new-sprint.sh +49 -0
- package/scripts/open-source-audit-allowlist.txt +46 -0
- package/scripts/package-source-archive.sh +147 -0
- package/scripts/plan-to-todo.sh +799 -0
- package/scripts/prepare-codex-handoff.sh +203 -0
- package/scripts/prepare-handoff.sh +81 -0
- package/scripts/public-release-files.txt +39 -0
- package/scripts/public-release.ts +174 -0
- package/scripts/publish-release-tarball.sh +27 -0
- package/scripts/refresh-current-status.sh +469 -0
- package/scripts/refresh-ios-physical-runtime.ts +196 -0
- package/scripts/repair-executable-modes.ts +8 -0
- package/scripts/rollout-all-registered-repos.sh +7 -0
- package/scripts/route-nl-vs-ts-eval.ts +679 -0
- package/scripts/run-bun-test-file.ts +182 -0
- package/scripts/run-governed-gate.ts +140 -0
- package/scripts/run-mobile-monitor-bridge.ts +71 -0
- package/scripts/run-skill-evals.ts +1022 -0
- package/scripts/run-skill-hook.ts +244 -0
- package/scripts/run-supervised-command.ts +24 -0
- package/scripts/run-tests-portable.sh +20 -0
- package/scripts/select-agent-context-blocks.sh +66 -0
- package/scripts/setup-plugins.sh +43 -0
- package/scripts/ship-worktrees.sh +561 -0
- package/scripts/smoke-browser-cdp-bridge.ts +93 -0
- package/scripts/smoke-chatgpt-supervised-automation.ts +208 -0
- package/scripts/smoke-ios-physical-device.ts +114 -0
- package/scripts/smoke-mcp-http-runtime.ts +216 -0
- package/scripts/smoke-mcp-tool-surface.ts +117 -0
- package/scripts/smoke-runtime-recovery.ts +220 -0
- package/scripts/smoke-schedule-engine.ts +283 -0
- package/scripts/sprint-backlog.sh +837 -0
- package/scripts/stage-runtime-release.ts +38 -0
- package/scripts/summarize-failures.sh +125 -0
- package/scripts/switch-plan.sh +153 -0
- package/scripts/sync-brain-docs.sh +381 -0
- package/scripts/sync-codex-installed-copies.sh +183 -0
- package/scripts/test-governance.ts +75 -0
- package/scripts/verify-contract.sh +705 -0
- package/scripts/verify-forge-runtime.sh +31 -0
- package/scripts/verify-sprint.sh +680 -0
- package/scripts/windows-paths.d.mts +7 -0
- package/scripts/windows-paths.mjs +28 -0
- package/scripts/workflow-contract.ts +206 -0
- package/scripts/workstream-sync.sh +263 -0
- package/src/cli/agent-jobs/executable-resolver.ts +289 -0
- package/src/cli/agent-jobs/executor-health.ts +252 -0
- package/src/cli/agent-jobs/integration.ts +963 -0
- package/src/cli/agent-jobs/job-manager.ts +2030 -0
- package/src/cli/agent-jobs/types.ts +219 -0
- package/src/cli/agent-jobs/worker-lifecycle.ts +288 -0
- package/src/cli/chatgpt-browser/bind-server.ts +504 -0
- package/src/cli/chatgpt-browser/binding.ts +140 -0
- package/src/cli/chatgpt-browser/bridge-extension.ts +221 -0
- package/src/cli/chatgpt-browser/bridge-provider.ts +268 -0
- package/src/cli/chatgpt-browser/engine.ts +569 -0
- package/src/cli/chatgpt-browser/file-policy.ts +158 -0
- package/src/cli/chatgpt-browser/native-provider.ts +553 -0
- package/src/cli/chatgpt-browser/oracle-provider.ts +437 -0
- package/src/cli/chatgpt-browser/prompt-assembler.ts +29 -0
- package/src/cli/chatgpt-browser/session-store.ts +301 -0
- package/src/cli/chatgpt-browser/types.ts +172 -0
- package/src/cli/commands/adopt-plan.ts +186 -0
- package/src/cli/commands/brain-root.ts +132 -0
- package/src/cli/commands/brain.ts +609 -0
- package/src/cli/commands/capability-context.ts +613 -0
- package/src/cli/commands/chatgpt.ts +424 -0
- package/src/cli/commands/controller.ts +680 -0
- package/src/cli/commands/docs.ts +103 -0
- package/src/cli/commands/doctor.ts +476 -0
- package/src/cli/commands/global-runtime.ts +323 -0
- package/src/cli/commands/hook.ts +34 -0
- package/src/cli/commands/init-hook.ts +870 -0
- package/src/cli/commands/init.ts +829 -0
- package/src/cli/commands/install.ts +86 -0
- package/src/cli/commands/mcp.ts +315 -0
- package/src/cli/commands/migrate.ts +154 -0
- package/src/cli/commands/plugin.ts +74 -0
- package/src/cli/commands/prompt-guard-decision.ts +238 -0
- package/src/cli/commands/recovery.ts +667 -0
- package/src/cli/commands/repository.ts +224 -0
- package/src/cli/commands/run.ts +23 -0
- package/src/cli/commands/runtime.ts +191 -0
- package/src/cli/commands/security.ts +440 -0
- package/src/cli/commands/status.ts +137 -0
- package/src/cli/commands/tools.ts +167 -0
- package/src/cli/controller/check-runner.ts +1022 -0
- package/src/cli/controller/codex-continuation.ts +153 -0
- package/src/cli/controller/completion-backlog.ts +380 -0
- package/src/cli/controller/completion-orchestrator.ts +1807 -0
- package/src/cli/controller/completion-target.ts +96 -0
- package/src/cli/controller/composite-operations.ts +196 -0
- package/src/cli/controller/composite-result.ts +130 -0
- package/src/cli/controller/context-pack.ts +652 -0
- package/src/cli/controller/execution-completion.ts +175 -0
- package/src/cli/controller/execution-policy.ts +392 -0
- package/src/cli/controller/governance.ts +594 -0
- package/src/cli/controller/issue-store.ts +1421 -0
- package/src/cli/controller/launch-agents.ts +410 -0
- package/src/cli/controller/legacy-issue-cutover.ts +415 -0
- package/src/cli/controller/lifecycle-authority.ts +16 -0
- package/src/cli/controller/migrated-issue-decision.ts +237 -0
- package/src/cli/controller/operational-plan.ts +226 -0
- package/src/cli/controller/postcondition.ts +134 -0
- package/src/cli/controller/progress.ts +656 -0
- package/src/cli/controller/project-state.ts +147 -0
- package/src/cli/controller/requirement-portfolio-export.ts +146 -0
- package/src/cli/controller/requirement-portfolio-migration.ts +613 -0
- package/src/cli/controller/run-evidence.ts +43 -0
- package/src/cli/controller/runtime-config.ts +92 -0
- package/src/cli/controller/stuck-state-migration.ts +396 -0
- package/src/cli/controller/task-ledger.ts +632 -0
- package/src/cli/controller/task-status-resolver.ts +403 -0
- package/src/cli/controller/types.ts +273 -0
- package/src/cli/controller/work-mode.ts +186 -0
- package/src/cli/controller/worklog.ts +201 -0
- package/src/cli/diagnostic-entry.ts +39 -0
- package/src/cli/editing/edit-session.ts +1606 -0
- package/src/cli/editing/executable-modes.ts +70 -0
- package/src/cli/github/github.ts +274 -0
- package/src/cli/github/plugin.ts +345 -0
- package/src/cli/github/session.ts +104 -0
- package/src/cli/hook/prompt-guard-decision.ts +238 -0
- package/src/cli/hook/prompt-intents.ts +534 -0
- package/src/cli/hook/route-registry.ts +107 -0
- package/src/cli/hook/runtime.ts +303 -0
- package/src/cli/hook/state-snapshot.ts +388 -0
- package/src/cli/hook-entry.ts +51 -0
- package/src/cli/index.ts +574 -0
- package/src/cli/installer/managed-entries.ts +78 -0
- package/src/cli/installer/shared.ts +65 -0
- package/src/cli/installer/targets/claude.ts +136 -0
- package/src/cli/installer/targets/codex.ts +174 -0
- package/src/cli/installer/targets/registry.ts +28 -0
- package/src/cli/installer/types.ts +72 -0
- package/src/cli/local-bridge/connector-freshness.ts +529 -0
- package/src/cli/local-bridge/console-view-models.ts +324 -0
- package/src/cli/local-bridge/dashboard.ts +1312 -0
- package/src/cli/local-bridge/facade-api.ts +1877 -0
- package/src/cli/local-bridge/job-store.ts +1451 -0
- package/src/cli/local-bridge/mobile-intents.ts +344 -0
- package/src/cli/local-bridge/mobile-monitor.ts +209 -0
- package/src/cli/local-bridge/server.ts +3183 -0
- package/src/cli/local-bridge/types.ts +176 -0
- package/src/cli/mcp/access-mode.ts +132 -0
- package/src/cli/mcp/access-tools.ts +387 -0
- package/src/cli/mcp/audit.ts +32 -0
- package/src/cli/mcp/auth.ts +402 -0
- package/src/cli/mcp/instructions.ts +33 -0
- package/src/cli/mcp/legacy-context.ts +16 -0
- package/src/cli/mcp/legacy-tool-service.ts +5516 -0
- package/src/cli/mcp/multi-repository.ts +423 -0
- package/src/cli/mcp/oauth.ts +226 -0
- package/src/cli/mcp/paths.ts +134 -0
- package/src/cli/mcp/policy.ts +230 -0
- package/src/cli/mcp/proxy-env.ts +62 -0
- package/src/cli/mcp/readiness-projection.ts +24 -0
- package/src/cli/mcp/redaction.ts +21 -0
- package/src/cli/mcp/repo.ts +24 -0
- package/src/cli/mcp/repository-tools.ts +1295 -0
- package/src/cli/mcp/server.ts +341 -0
- package/src/cli/mcp/setup.ts +793 -0
- package/src/cli/mcp/tool-capability-cache.ts +87 -0
- package/src/cli/mcp/tools.ts +9 -0
- package/src/cli/mcp/toolset-names.ts +70 -0
- package/src/cli/mcp/toolset-selection.ts +41 -0
- package/src/cli/mcp/toolset.ts +300 -0
- package/src/cli/mcp/transports/AGENTS.md +39 -0
- package/src/cli/mcp/transports/CLAUDE.md +39 -0
- package/src/cli/mcp/transports/http.ts +1234 -0
- package/src/cli/mcp/transports/session-registry.ts +428 -0
- package/src/cli/mcp/transports/stdio.ts +8 -0
- package/src/cli/mcp/types.ts +41 -0
- package/src/cli/mcp/umbrella-tools.ts +117 -0
- package/src/cli/repo-adoption/reclaim-runtime.ts +681 -0
- package/src/cli/repositories/branch-name-policy.ts +57 -0
- package/src/cli/repositories/command-classifier.ts +600 -0
- package/src/cli/repositories/command-executor.ts +1066 -0
- package/src/cli/repositories/command-normalization.ts +53 -0
- package/src/cli/repositories/command-scope.ts +264 -0
- package/src/cli/repositories/controller-home.ts +88 -0
- package/src/cli/repositories/entity-migration.ts +199 -0
- package/src/cli/repositories/ephemeral-workspace.ts +62 -0
- package/src/cli/repositories/git-command-executor.ts +301 -0
- package/src/cli/repositories/goal-registry.ts +239 -0
- package/src/cli/repositories/identity.ts +64 -0
- package/src/cli/repositories/local-project-onboarding.ts +539 -0
- package/src/cli/repositories/locks.ts +262 -0
- package/src/cli/repositories/registry.ts +1057 -0
- package/src/cli/repositories/runtime-storage.ts +411 -0
- package/src/cli/repositories/safe-patch.ts +421 -0
- package/src/cli/repositories/selected-path-actions.ts +341 -0
- package/src/cli/repositories/structured-git.ts +257 -0
- package/src/cli/repositories/types.ts +155 -0
- package/src/cli/repositories/umbrella.ts +134 -0
- package/src/cli/repositories/workbench.ts +140 -0
- package/src/cli/repositories/worktree-storage.ts +137 -0
- package/src/cli/repository/inspector.ts +637 -0
- package/src/cli/repository/session-cache.ts +453 -0
- package/src/cli/runtime/helper-runner.ts +172 -0
- package/src/cli/runtime-invocation.ts +318 -0
- package/src/cli/tools/codegraph.ts +676 -0
- package/src/cli/v81-entry.ts +29 -0
- package/src/core/adoption/gitignore-plan.ts +127 -0
- package/src/core/adoption/helper-wrapper-plan.ts +159 -0
- package/src/core/adoption/manifest-templates.ts +60 -0
- package/src/core/adoption/modes.ts +7 -0
- package/src/core/adoption/operations.ts +126 -0
- package/src/core/adoption/plan.ts +149 -0
- package/src/core/adoption/render.ts +70 -0
- package/src/core/adoption/rollback.ts +62 -0
- package/src/core/adoption/summary.ts +43 -0
- package/src/core/adoption/workflow-contract-asset.ts +14 -0
- package/src/core/adoption/workflow-contract-plan.ts +31 -0
- package/src/effects/fs-transaction.ts +540 -0
- package/src/effects/git-executable.ts +59 -0
- package/src/effects/managed-block.ts +86 -0
- package/src/effects/path-safety.ts +47 -0
- package/src/effects/process-runner.ts +192 -0
- package/src/runtime/assistant/action-proposals.ts +279 -0
- package/src/runtime/assistant/intent.ts +395 -0
- package/src/runtime/assistant/model-provider.ts +460 -0
- package/src/runtime/assistant/openapi.ts +223 -0
- package/src/runtime/assistant/policy.ts +89 -0
- package/src/runtime/assistant/readiness.ts +258 -0
- package/src/runtime/assistant/routine-runtime.ts +477 -0
- package/src/runtime/assistant/schedule-binding.ts +171 -0
- package/src/runtime/assistant/standing-grants.ts +375 -0
- package/src/runtime/assistant/store.ts +243 -0
- package/src/runtime/assistant/types.ts +135 -0
- package/src/runtime/context/codegraph-read-provider.ts +347 -0
- package/src/runtime/context/codegraph-sidecar.cjs +207 -0
- package/src/runtime/control-plane/dispatch-priority.ts +67 -0
- package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +151 -0
- package/src/runtime/control-plane/execution/execution-identity.ts +456 -0
- package/src/runtime/control-plane/execution/session-store.ts +175 -0
- package/src/runtime/control-plane/execution/validation.ts +117 -0
- package/src/runtime/control-plane/execution/verification-evidence.ts +191 -0
- package/src/runtime/control-plane/execution/work-handle-store.ts +225 -0
- package/src/runtime/control-plane/execution/work-prepare-request-store.ts +159 -0
- package/src/runtime/control-plane/execution/work-task-receipt.ts +445 -0
- package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +821 -0
- package/src/runtime/control-plane/facade/capability-registry.ts +279 -0
- package/src/runtime/control-plane/facade/check-normalization.ts +233 -0
- package/src/runtime/control-plane/facade/codex-delegation.ts +196 -0
- package/src/runtime/control-plane/facade/controller-session-store.ts +199 -0
- package/src/runtime/control-plane/facade/facade-result.ts +41 -0
- package/src/runtime/control-plane/facade/goal-workloop-access.ts +177 -0
- package/src/runtime/control-plane/facade/goal-workloop.ts +1216 -0
- package/src/runtime/control-plane/facade/handoff-inbox-store.ts +221 -0
- package/src/runtime/control-plane/facade/index.ts +16 -0
- package/src/runtime/control-plane/facade/operation-digest.ts +528 -0
- package/src/runtime/control-plane/facade/plan-contract-store.ts +403 -0
- package/src/runtime/control-plane/facade/policy-gate.ts +149 -0
- package/src/runtime/control-plane/facade/requirement-board.ts +280 -0
- package/src/runtime/control-plane/facade/self-healing-loop.ts +555 -0
- package/src/runtime/control-plane/facade/suggested-actions.ts +108 -0
- package/src/runtime/control-plane/facade/types.ts +729 -0
- package/src/runtime/control-plane/facade/work-admission-policy.ts +93 -0
- package/src/runtime/control-plane/facade/work-continuation.ts +104 -0
- package/src/runtime/control-plane/facade/work-contract-store.ts +1118 -0
- package/src/runtime/control-plane/facade/work-task-projection.ts +66 -0
- package/src/runtime/control-plane/global-scheduler/reconciliation.ts +365 -0
- package/src/runtime/control-plane/global-scheduler/scheduler.ts +920 -0
- package/src/runtime/control-plane/global-scheduler/wake-signal.ts +89 -0
- package/src/runtime/control-plane/goal-loop/config-facade.ts +700 -0
- package/src/runtime/control-plane/goal-loop/config-store.ts +570 -0
- package/src/runtime/control-plane/goal-loop/config-types.ts +213 -0
- package/src/runtime/control-plane/goal-loop/executor-router.ts +109 -0
- package/src/runtime/control-plane/goal-loop/failure-taxonomy.ts +149 -0
- package/src/runtime/control-plane/goal-loop/goal-contract-store.ts +304 -0
- package/src/runtime/control-plane/goal-loop/goal-loop-engine.ts +1001 -0
- package/src/runtime/control-plane/goal-loop/handoff-packet.ts +160 -0
- package/src/runtime/control-plane/goal-loop/index.ts +13 -0
- package/src/runtime/control-plane/goal-loop/local-tools.ts +290 -0
- package/src/runtime/control-plane/goal-loop/policy-gates.ts +203 -0
- package/src/runtime/control-plane/goal-loop/provider-dispatch.ts +93 -0
- package/src/runtime/control-plane/goal-loop/provider-registry.ts +545 -0
- package/src/runtime/control-plane/goal-loop/types.ts +378 -0
- package/src/runtime/control-plane/governance/access-policy.ts +147 -0
- package/src/runtime/control-plane/governance/authorization.ts +195 -0
- package/src/runtime/control-plane/governance/external-effects.ts +43 -0
- package/src/runtime/control-plane/launcher/thin-launcher.ts +97 -0
- package/src/runtime/control-plane/persistence/metadata-payload-policy.ts +53 -0
- package/src/runtime/control-plane/persistence/requirement-store.ts +241 -0
- package/src/runtime/control-plane/persistence/sqlite-store.ts +539 -0
- package/src/runtime/control-plane/release-retention.ts +447 -0
- package/src/runtime/control-plane/repo-actor/actor.ts +181 -0
- package/src/runtime/control-plane/repo-actor/registry.ts +20 -0
- package/src/runtime/control-plane/routing/index.ts +1 -0
- package/src/runtime/control-plane/routing/route-policy.ts +379 -0
- package/src/runtime/control-plane/runtime-cleanup.ts +727 -0
- package/src/runtime/control-plane/runtime-generation.ts +509 -0
- package/src/runtime/control-plane/runtime-pid-ownership.ts +11 -0
- package/src/runtime/control-plane/runtime-status-client.ts +90 -0
- package/src/runtime/control-plane/startup-recovery.ts +198 -0
- package/src/runtime/diagnostics/connectivity-observability.ts +475 -0
- package/src/runtime/diagnostics/mcp-timing.ts +64 -0
- package/src/runtime/diagnostics/performance.ts +379 -0
- package/src/runtime/diagnostics/process-facade.ts +350 -0
- package/src/runtime/diagnostics/read-only-tool.ts +226 -0
- package/src/runtime/evidence/artifact-store.ts +154 -0
- package/src/runtime/evidence/event-ledger.ts +80 -0
- package/src/runtime/evidence/evidence-store.ts +68 -0
- package/src/runtime/evidence/process-check-receipt.ts +43 -0
- package/src/runtime/evidence/result-store.ts +209 -0
- package/src/runtime/evidence/sensitive-output.ts +353 -0
- package/src/runtime/execution/jobs/child-reference.ts +124 -0
- package/src/runtime/execution/jobs/legacy-adapter.ts +22 -0
- package/src/runtime/execution/jobs/receipt-recovery.ts +119 -0
- package/src/runtime/execution/jobs/receipt-store.ts +243 -0
- package/src/runtime/execution/jobs/store.ts +737 -0
- package/src/runtime/execution/jobs/timeouts.ts +109 -0
- package/src/runtime/execution/jobs/types.ts +230 -0
- package/src/runtime/execution/jobs/wait.ts +45 -0
- package/src/runtime/execution/process-runtime/canary.ts +26 -0
- package/src/runtime/execution/process-runtime/check-facade.ts +272 -0
- package/src/runtime/execution/process-runtime/check-receipt.ts +185 -0
- package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +60 -0
- package/src/runtime/execution/process-runtime/command-facade.ts +321 -0
- package/src/runtime/execution/process-runtime/gc.ts +171 -0
- package/src/runtime/execution/process-runtime/index.ts +9 -0
- package/src/runtime/execution/process-runtime/interactive-admission.ts +23 -0
- package/src/runtime/execution/process-runtime/process-runner-entry.ts +466 -0
- package/src/runtime/execution/process-runtime/resource-claims.ts +440 -0
- package/src/runtime/execution/process-runtime/runtime.ts +1816 -0
- package/src/runtime/execution/process-runtime/store.ts +687 -0
- package/src/runtime/execution/process-runtime/types.ts +352 -0
- package/src/runtime/execution/thin-harness/async-process.ts +181 -0
- package/src/runtime/execution/thin-harness/batch-executor.ts +671 -0
- package/src/runtime/execution/thin-harness/execution-router.ts +700 -0
- package/src/runtime/execution/thin-harness/fast-executor.ts +1263 -0
- package/src/runtime/execution/thin-harness/fast-receipt.ts +235 -0
- package/src/runtime/execution/thin-harness/fingerprint-worker.ts +191 -0
- package/src/runtime/execution/thin-harness/index.ts +13 -0
- package/src/runtime/execution/thin-harness/latency-trace.ts +107 -0
- package/src/runtime/execution/thin-harness/lightweight-lanes.ts +745 -0
- package/src/runtime/execution/thin-harness/mutation-gate.ts +578 -0
- package/src/runtime/execution/thin-harness/proposal-store.ts +409 -0
- package/src/runtime/execution/thin-harness/request-ledger.ts +595 -0
- package/src/runtime/execution/thin-harness/search-worker.ts +114 -0
- package/src/runtime/execution/thin-harness/types.ts +313 -0
- package/src/runtime/execution/thin-harness/workspace-savepoint.ts +333 -0
- package/src/runtime/execution/workers/executor.ts +687 -0
- package/src/runtime/execution/workers/ownership.ts +94 -0
- package/src/runtime/execution/workers/worker-entry.ts +277 -0
- package/src/runtime/gateway/mcp/execution-tools.ts +2009 -0
- package/src/runtime/gateway/mcp/persisted-check-process.ts +255 -0
- package/src/runtime/gateway/mcp/process-tools.ts +230 -0
- package/src/runtime/gateway/mcp/resource-policy.ts +195 -0
- package/src/runtime/gateway/mcp/router.ts +1088 -0
- package/src/runtime/gateway/mcp/runtime-tools.ts +5994 -0
- package/src/runtime/gateway/mcp/work-validation-reconciler.ts +219 -0
- package/src/runtime/health/evaluator.ts +474 -0
- package/src/runtime/health/index.ts +2 -0
- package/src/runtime/health/operational-view.ts +198 -0
- package/src/runtime/maintenance/cleanup.ts +419 -0
- package/src/runtime/model-clients/deepseek-adapter.ts +312 -0
- package/src/runtime/model-clients/index.ts +2 -0
- package/src/runtime/model-clients/types.ts +101 -0
- package/src/runtime/observability/gateway-contention-metrics.ts +129 -0
- package/src/runtime/observability/route-session-benchmark.ts +110 -0
- package/src/runtime/personal-assistant/gmail-triage-manager.ts +231 -0
- package/src/runtime/personal-assistant/reporting-runtime.ts +344 -0
- package/src/runtime/personal-assistant/triage-runtime.ts +748 -0
- package/src/runtime/plugins/app-store-connect-adapter.ts +868 -0
- package/src/runtime/plugins/browser-adapter.ts +2812 -0
- package/src/runtime/plugins/browser-automation-service.ts +46 -0
- package/src/runtime/plugins/browser-handoff-host.ts +304 -0
- package/src/runtime/plugins/browser-handoff.ts +371 -0
- package/src/runtime/plugins/browser-macos-bridge.ts +865 -0
- package/src/runtime/plugins/browser-node-bridge-host.ts +64 -0
- package/src/runtime/plugins/browser-node-bridge.ts +249 -0
- package/src/runtime/plugins/desktop-adapter.ts +374 -0
- package/src/runtime/plugins/desktop-operator-registration.ts +418 -0
- package/src/runtime/plugins/errors.ts +39 -0
- package/src/runtime/plugins/execution-origin.ts +26 -0
- package/src/runtime/plugins/external-adapter.ts +322 -0
- package/src/runtime/plugins/external-registration.ts +308 -0
- package/src/runtime/plugins/external-unix-socket-probe.cjs +79 -0
- package/src/runtime/plugins/external-unix-socket.ts +238 -0
- package/src/runtime/plugins/first-party-registry.ts +37 -0
- package/src/runtime/plugins/github-adapter.ts +266 -0
- package/src/runtime/plugins/gmail-adapter.ts +729 -0
- package/src/runtime/plugins/google-calendar-adapter.ts +468 -0
- package/src/runtime/plugins/google-shared.ts +578 -0
- package/src/runtime/plugins/google-tasks-adapter.ts +535 -0
- package/src/runtime/plugins/interaction-session.ts +252 -0
- package/src/runtime/plugins/ios/agent-device-capabilities.ts +258 -0
- package/src/runtime/plugins/ios/agent-device-failures.ts +95 -0
- package/src/runtime/plugins/ios/agent-device-provider.ts +63 -0
- package/src/runtime/plugins/ios/agent-device-typed-provider.ts +268 -0
- package/src/runtime/plugins/ios/app-adapters.ts +122 -0
- package/src/runtime/plugins/ios-adapter.ts +509 -0
- package/src/runtime/plugins/ios-agent-device.ts +2311 -0
- package/src/runtime/plugins/ios-physical-device.ts +955 -0
- package/src/runtime/plugins/local-system-adapter.ts +1016 -0
- package/src/runtime/plugins/macos-capability-broker.ts +73 -0
- package/src/runtime/plugins/managed-process-adapter.ts +366 -0
- package/src/runtime/plugins/resend-adapter.ts +506 -0
- package/src/runtime/plugins/store.ts +1025 -0
- package/src/runtime/plugins/types.ts +138 -0
- package/src/runtime/projections/controller-context.ts +596 -0
- package/src/runtime/projections/git-status-sampler.ts +84 -0
- package/src/runtime/projections/invalidation.ts +250 -0
- package/src/runtime/projections/materialized-view.ts +694 -0
- package/src/runtime/recovery/actions.ts +158 -0
- package/src/runtime/recovery/audit.ts +38 -0
- package/src/runtime/recovery/classifier.ts +163 -0
- package/src/runtime/recovery/index.ts +9 -0
- package/src/runtime/recovery/local-jobs-repair.ts +393 -0
- package/src/runtime/recovery/maintenance-executor.ts +822 -0
- package/src/runtime/recovery/patch-handoff.ts +50 -0
- package/src/runtime/recovery/probe.ts +308 -0
- package/src/runtime/recovery/store.ts +35 -0
- package/src/runtime/recovery/types.ts +175 -0
- package/src/runtime/release/identity.ts +101 -0
- package/src/runtime/release/release-gate.ts +137 -0
- package/src/runtime/resources/claims/conflicts.ts +205 -0
- package/src/runtime/resources/index.ts +1 -0
- package/src/runtime/resources/leases/store.ts +539 -0
- package/src/runtime/resources/leases/types.ts +50 -0
- package/src/runtime/resources/ownership.ts +72 -0
- package/src/runtime/root/controller-services.ts +42 -0
- package/src/runtime/root/entry.ts +70 -0
- package/src/runtime/root/gateway-adapter.ts +46 -0
- package/src/runtime/root/local-bridge.ts +72 -0
- package/src/runtime/root/mcp-transport.ts +198 -0
- package/src/runtime/root/ownership.ts +94 -0
- package/src/runtime/root/readiness.ts +69 -0
- package/src/runtime/root/release-manifest.ts +147 -0
- package/src/runtime/root/release-materialize.ts +509 -0
- package/src/runtime/root/release-store.ts +236 -0
- package/src/runtime/root/runtime.ts +318 -0
- package/src/runtime/root/scheduler.ts +58 -0
- package/src/runtime/root/service-runner-entry.ts +20 -0
- package/src/runtime/root/service-runner.ts +69 -0
- package/src/runtime/root/service.ts +291 -0
- package/src/runtime/root/status.ts +127 -0
- package/src/runtime/root/types.ts +112 -0
- package/src/runtime/root/write-fence.ts +309 -0
- package/src/runtime/safe-tooling/browser-safe.ts +133 -0
- package/src/runtime/safe-tooling/external-filesystem.ts +304 -0
- package/src/runtime/safe-tooling/google-credential-store.ts +95 -0
- package/src/runtime/safe-tooling/google-oauth-broker.ts +286 -0
- package/src/runtime/safe-tooling/index.ts +11 -0
- package/src/runtime/safe-tooling/ios-development.ts +1039 -0
- package/src/runtime/safe-tooling/job-summary.ts +361 -0
- package/src/runtime/safe-tooling/plugin-summary.ts +53 -0
- package/src/runtime/safe-tooling/resend-oauth.ts +387 -0
- package/src/runtime/safe-tooling/review-artifacts.ts +133 -0
- package/src/runtime/safe-tooling/types.ts +103 -0
- package/src/runtime/safe-tooling/workspace-auth.ts +116 -0
- package/src/runtime/shared/bounded-child-supervisor.ts +235 -0
- package/src/runtime/shared/idempotent-retry.ts +128 -0
- package/src/runtime/shared/json-files.ts +43 -0
- package/src/runtime/shared/local-bridge-surface.ts +231 -0
- package/src/runtime/shared/managed-env.ts +154 -0
- package/src/runtime/shared/node-ts-loader.mjs +50 -0
- package/src/runtime/shared/process-environment.ts +97 -0
- package/src/runtime/shared/process-identity.ts +72 -0
- package/src/runtime/shared/process-tree.ts +227 -0
- package/src/runtime/shared/response-budget.ts +246 -0
- package/src/runtime/standalone-recovery/core.ts +2275 -0
- package/src/runtime/standalone-recovery/entry.ts +686 -0
- package/src/runtime/standalone-recovery/http-transport.ts +317 -0
- package/src/runtime/standalone-recovery/installer.ts +747 -0
- package/src/runtime/standalone-recovery/release.ts +241 -0
- package/src/runtime/watchdog/workflow-watchdog.ts +118 -0
- package/src/runtime/workflow/campaigns/cleanup.ts +263 -0
- package/src/runtime/workflow/campaigns/engine.ts +112 -0
- package/src/runtime/workflow/campaigns/normalize.ts +100 -0
- package/src/runtime/workflow/campaigns/review.ts +315 -0
- package/src/runtime/workflow/campaigns/store.ts +526 -0
- package/src/runtime/workflow/campaigns/supervisor.ts +112 -0
- package/src/runtime/workflow/campaigns/types.ts +290 -0
- package/src/runtime/workflow/campaigns/workspace-agent.ts +155 -0
- package/src/runtime/workflow/campaigns/workspace.ts +200 -0
- package/src/runtime/workflow/findings/store.ts +165 -0
- package/src/runtime/workflow/findings/types.ts +36 -0
- package/src/runtime/workflow/portfolio/engine.ts +20 -0
- package/src/runtime/workflow/portfolio/store.ts +185 -0
- package/src/runtime/workflow/portfolio/types.ts +29 -0
- package/src/runtime/workflow/schedules/engine.ts +620 -0
- package/src/runtime/workflow/schedules/settlement.ts +144 -0
- package/src/runtime/workflow/schedules/store.ts +417 -0
- package/src/runtime/workflow/schedules/types.ts +124 -0
- package/src/runtime/workspace-targets/index.ts +24 -0
- package/src/runtime/workspace-targets/target-grant.ts +721 -0
- package/src/testing/test-governance.ts +626 -0
- package/src/version.ts +21 -0
|
@@ -0,0 +1,1016 @@
|
|
|
1
|
+
import { spawnSync } from 'child_process';
|
|
2
|
+
import { createHash } from 'crypto';
|
|
3
|
+
import {
|
|
4
|
+
constants,
|
|
5
|
+
copyFileSync,
|
|
6
|
+
existsSync,
|
|
7
|
+
mkdirSync,
|
|
8
|
+
readFileSync,
|
|
9
|
+
readdirSync,
|
|
10
|
+
renameSync,
|
|
11
|
+
realpathSync,
|
|
12
|
+
rmdirSync,
|
|
13
|
+
statSync,
|
|
14
|
+
unlinkSync,
|
|
15
|
+
writeFileSync,
|
|
16
|
+
} from 'fs';
|
|
17
|
+
import { dirname, extname, join, resolve } from 'path';
|
|
18
|
+
import {
|
|
19
|
+
WorkspaceTargetGrantError,
|
|
20
|
+
authorizeWorkspaceTargetGrant,
|
|
21
|
+
getActiveWorkspaceTargetGrant,
|
|
22
|
+
listActiveWorkspaceTargetGrants,
|
|
23
|
+
resolveWorkspaceTargetCwd,
|
|
24
|
+
resolveWorkspaceTargetPath,
|
|
25
|
+
revokeWorkspaceTargetGrant,
|
|
26
|
+
withWorkspaceTargetMutationLocks,
|
|
27
|
+
type ActiveWorkspaceTargetGrant,
|
|
28
|
+
type WorkspaceTargetAccess,
|
|
29
|
+
type WorkspaceTargetGrantScope,
|
|
30
|
+
type WorkspaceTargetOperation,
|
|
31
|
+
} from '../workspace-targets';
|
|
32
|
+
import type {
|
|
33
|
+
AssistantPluginActionDescriptor,
|
|
34
|
+
AssistantPluginActionExecutionInput,
|
|
35
|
+
AssistantPluginCapability,
|
|
36
|
+
AssistantPluginHealth,
|
|
37
|
+
AssistantPluginManifest,
|
|
38
|
+
AssistantPluginPermissionScope,
|
|
39
|
+
} from './types';
|
|
40
|
+
import { AssistantPluginError } from './errors';
|
|
41
|
+
import {
|
|
42
|
+
classifyRepositoryCommand,
|
|
43
|
+
classifyRepositoryCommandReplay,
|
|
44
|
+
} from '../../cli/repositories/command-classifier';
|
|
45
|
+
import {
|
|
46
|
+
assertCommandPathOperandsStayInRepository,
|
|
47
|
+
assertRepositoryCommandInputAllowed,
|
|
48
|
+
} from '../../cli/repositories/command-scope';
|
|
49
|
+
import { runCanonicalCommand } from '../../cli/repositories/command-executor';
|
|
50
|
+
|
|
51
|
+
const PLUGIN_ID = 'local_system';
|
|
52
|
+
const MAX_OUTPUT_BYTES = 64 * 1024;
|
|
53
|
+
const MAX_TEXT_CHARS = 100_000;
|
|
54
|
+
const MAX_DIRECTORY_ENTRIES = 200;
|
|
55
|
+
const TARGET_SAFE_FILESYSTEM_MUTATORS = new Set(['touch', 'mkdir', 'cp', 'mv', 'install', 'tee', 'truncate']);
|
|
56
|
+
const PROJECT_SCRIPT_RUNTIMES = new Set(['node', 'bun', 'python3', 'ruby', 'bash', 'sh']);
|
|
57
|
+
const AUTO_OPEN_DENIED_EXTENSIONS = new Set([
|
|
58
|
+
'.command', '.sh', '.bash', '.zsh', '.fish', '.tool',
|
|
59
|
+
'.app', '.pkg', '.mpkg', '.dmg', '.workflow',
|
|
60
|
+
]);
|
|
61
|
+
|
|
62
|
+
interface CommandResult {
|
|
63
|
+
ok: boolean;
|
|
64
|
+
status: number | null;
|
|
65
|
+
stdout: string;
|
|
66
|
+
stderr: string;
|
|
67
|
+
command: string[];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface LocalSystemPluginHooks {
|
|
71
|
+
now?: () => Date;
|
|
72
|
+
runCommand?: (command: string, args: string[], timeoutMs?: number) => CommandResult;
|
|
73
|
+
signalProcess?: (pid: number, signal: NodeJS.Signals) => void;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
let hooks: LocalSystemPluginHooks = {};
|
|
77
|
+
|
|
78
|
+
export function setLocalSystemPluginHooksForTest(next: LocalSystemPluginHooks): void {
|
|
79
|
+
hooks = next;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function resetLocalSystemPluginHooksForTest(): void {
|
|
83
|
+
hooks = {};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function currentDate(): Date {
|
|
87
|
+
return hooks.now?.() ?? new Date();
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function now(): string {
|
|
91
|
+
return currentDate().toISOString();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const GENERIC_CONTROLLER_ACTORS = new Set([
|
|
95
|
+
'',
|
|
96
|
+
'anonymous',
|
|
97
|
+
'plugin_action_execute',
|
|
98
|
+
]);
|
|
99
|
+
|
|
100
|
+
function requestOwnerScope(input: AssistantPluginActionExecutionInput): string {
|
|
101
|
+
const actor = input.origin.actor?.trim() || '';
|
|
102
|
+
if (GENERIC_CONTROLLER_ACTORS.has(actor)) return 'controller:shared';
|
|
103
|
+
return `${input.origin.surface}:${actor}`;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function activeTargets(input: AssistantPluginActionExecutionInput): ActiveWorkspaceTargetGrant[] {
|
|
107
|
+
try {
|
|
108
|
+
return listActiveWorkspaceTargetGrants(
|
|
109
|
+
input.controllerHome,
|
|
110
|
+
currentDate(),
|
|
111
|
+
requestOwnerScope(input),
|
|
112
|
+
);
|
|
113
|
+
} catch (error) {
|
|
114
|
+
return rethrowTargetError(error);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function requiredString(args: Record<string, unknown>, key: string): string {
|
|
119
|
+
const value = typeof args[key] === 'string' ? String(args[key]).trim() : '';
|
|
120
|
+
if (!value) throw new AssistantPluginError('PLUGIN_ACTION_ARGUMENT_INVALID', `${key} is required.`, { retryable: false });
|
|
121
|
+
return value;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function optionalString(args: Record<string, unknown>, key: string): string | undefined {
|
|
125
|
+
const value = typeof args[key] === 'string' ? String(args[key]).trim() : '';
|
|
126
|
+
return value || undefined;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function requiredStringArray(args: Record<string, unknown>, key: string): string[] {
|
|
130
|
+
const value = args[key];
|
|
131
|
+
if (!Array.isArray(value) || value.length === 0 || value.some((entry) => typeof entry !== 'string' || !entry.trim())) {
|
|
132
|
+
throw new AssistantPluginError('PLUGIN_ACTION_ARGUMENT_INVALID', `${key} must be a non-empty string array.`, { retryable: false });
|
|
133
|
+
}
|
|
134
|
+
return value.map((entry) => String(entry));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function optionalStringArray(args: Record<string, unknown>, key: string): string[] {
|
|
138
|
+
const value = args[key];
|
|
139
|
+
if (value === undefined) return [];
|
|
140
|
+
if (!Array.isArray(value) || value.some((entry) => typeof entry !== 'string')) {
|
|
141
|
+
throw new AssistantPluginError('PLUGIN_ACTION_ARGUMENT_INVALID', `${key} must be a string array.`, { retryable: false });
|
|
142
|
+
}
|
|
143
|
+
return value.map(String);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function bounded(value: string, maxBytes = MAX_OUTPUT_BYTES): { content: string; truncated: boolean; byteLength: number } {
|
|
147
|
+
const source = Buffer.from(value, 'utf8');
|
|
148
|
+
if (source.byteLength <= maxBytes) return { content: value, truncated: false, byteLength: source.byteLength };
|
|
149
|
+
return {
|
|
150
|
+
content: source.subarray(0, maxBytes).toString('utf8'),
|
|
151
|
+
truncated: true,
|
|
152
|
+
byteLength: source.byteLength,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function run(command: string, args: string[], timeoutMs = 30_000): CommandResult {
|
|
157
|
+
if (hooks.runCommand) return hooks.runCommand(command, args, timeoutMs);
|
|
158
|
+
const result = spawnSync(command, args, {
|
|
159
|
+
encoding: 'utf8',
|
|
160
|
+
timeout: Math.max(1_000, Math.min(Math.trunc(timeoutMs), 120_000)),
|
|
161
|
+
maxBuffer: MAX_OUTPUT_BYTES * 2,
|
|
162
|
+
});
|
|
163
|
+
return {
|
|
164
|
+
ok: result.status === 0,
|
|
165
|
+
status: result.status,
|
|
166
|
+
stdout: String(result.stdout ?? ''),
|
|
167
|
+
stderr: String(result.stderr ?? result.error?.message ?? ''),
|
|
168
|
+
command: [command, ...args],
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function projectScriptRuntimePath(runtime: string): string {
|
|
173
|
+
const home = process.env.HOME?.trim();
|
|
174
|
+
const candidates: Record<string, string[]> = {
|
|
175
|
+
node: ['/opt/homebrew/bin/node', '/usr/local/bin/node', '/usr/bin/node'],
|
|
176
|
+
bun: [
|
|
177
|
+
...(home ? [join(resolve(home), '.bun', 'bin', 'bun')] : []),
|
|
178
|
+
'/opt/homebrew/bin/bun',
|
|
179
|
+
'/usr/local/bin/bun',
|
|
180
|
+
],
|
|
181
|
+
python3: ['/opt/homebrew/bin/python3', '/usr/local/bin/python3', '/usr/bin/python3'],
|
|
182
|
+
ruby: ['/opt/homebrew/bin/ruby', '/usr/local/bin/ruby', '/usr/bin/ruby'],
|
|
183
|
+
bash: ['/opt/homebrew/bin/bash', '/usr/local/bin/bash', '/bin/bash'],
|
|
184
|
+
sh: ['/bin/sh'],
|
|
185
|
+
};
|
|
186
|
+
const executable = (candidates[runtime] ?? []).find((candidate) => {
|
|
187
|
+
try {
|
|
188
|
+
return existsSync(candidate) && statSync(candidate).isFile() && (statSync(candidate).mode & 0o111) !== 0;
|
|
189
|
+
} catch {
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
if (!executable) {
|
|
194
|
+
throw new AssistantPluginError(
|
|
195
|
+
'LOCAL_SYSTEM_SCRIPT_RUNTIME_UNAVAILABLE',
|
|
196
|
+
`The approved ${runtime} interpreter is not installed in a canonical system location.`,
|
|
197
|
+
{ retryable: false, details: { runtime } },
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
return realpathSync(executable);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const TARGET_ERROR_CODES: Record<WorkspaceTargetGrantError['code'], string> = {
|
|
204
|
+
TARGET_ROOT_INVALID: 'LOCAL_SYSTEM_TARGET_ROOT_INVALID',
|
|
205
|
+
TARGET_KEY_REQUIRED: 'LOCAL_SYSTEM_TARGET_KEY_REQUIRED',
|
|
206
|
+
TARGET_REASON_REQUIRED: 'LOCAL_SYSTEM_TARGET_REASON_REQUIRED',
|
|
207
|
+
TARGET_STORE_CORRUPT: 'LOCAL_SYSTEM_TARGET_STORE_CORRUPT',
|
|
208
|
+
TARGET_STORE_BUSY: 'LOCAL_SYSTEM_TARGET_STORE_BUSY',
|
|
209
|
+
TARGET_MUTATION_BUSY: 'LOCAL_SYSTEM_TARGET_MUTATION_BUSY',
|
|
210
|
+
TARGET_IDENTITY_MISMATCH: 'LOCAL_SYSTEM_TARGET_IDENTITY_MISMATCH',
|
|
211
|
+
TARGET_OWNER_SCOPE_REQUIRED: 'LOCAL_SYSTEM_TARGET_OWNER_INVALID',
|
|
212
|
+
TARGET_OWNER_MISMATCH: 'LOCAL_SYSTEM_TARGET_OWNER_MISMATCH',
|
|
213
|
+
TARGET_PROJECT_REQUIRED: 'LOCAL_SYSTEM_TARGET_PROJECT_REQUIRED',
|
|
214
|
+
TARGET_UNAVAILABLE: 'LOCAL_SYSTEM_TARGET_UNAVAILABLE',
|
|
215
|
+
PATH_OUTSIDE_TARGET: 'LOCAL_SYSTEM_PATH_OUTSIDE_TARGET',
|
|
216
|
+
SYMLINK_ESCAPE: 'LOCAL_SYSTEM_SYMLINK_ESCAPE',
|
|
217
|
+
PATH_NOT_FOUND: 'LOCAL_SYSTEM_PATH_NOT_FOUND',
|
|
218
|
+
PATH_NOT_DIRECTORY: 'LOCAL_SYSTEM_PATH_NOT_DIRECTORY',
|
|
219
|
+
PATH_NOT_FILE: 'LOCAL_SYSTEM_PATH_NOT_FILE',
|
|
220
|
+
TARGET_ACCESS_DENIED: 'LOCAL_SYSTEM_TARGET_READ_ONLY',
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
function rethrowTargetError(error: unknown): never {
|
|
224
|
+
if (error instanceof WorkspaceTargetGrantError) {
|
|
225
|
+
throw new AssistantPluginError(TARGET_ERROR_CODES[error.code], error.message, {
|
|
226
|
+
retryable: error.code === 'TARGET_STORE_BUSY' || error.code === 'TARGET_MUTATION_BUSY',
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
throw error;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function resolveTargetPath(
|
|
233
|
+
input: AssistantPluginActionExecutionInput,
|
|
234
|
+
targetKey: string,
|
|
235
|
+
relativePath: string | undefined,
|
|
236
|
+
options: {
|
|
237
|
+
mustExist: boolean;
|
|
238
|
+
directory?: boolean;
|
|
239
|
+
file?: boolean;
|
|
240
|
+
operation?: WorkspaceTargetOperation;
|
|
241
|
+
},
|
|
242
|
+
) {
|
|
243
|
+
try {
|
|
244
|
+
return resolveWorkspaceTargetPath(input.controllerHome, targetKey, relativePath, {
|
|
245
|
+
mustExist: options.mustExist,
|
|
246
|
+
ownerScope: requestOwnerScope(input),
|
|
247
|
+
at: currentDate(),
|
|
248
|
+
...(options.directory ? { kind: 'directory' as const } : {}),
|
|
249
|
+
...(options.file ? { kind: 'file' as const } : {}),
|
|
250
|
+
operation: options.operation ?? 'read',
|
|
251
|
+
});
|
|
252
|
+
} catch (error) {
|
|
253
|
+
return rethrowTargetError(error);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
async function withTargetMutation<T>(
|
|
258
|
+
input: AssistantPluginActionExecutionInput,
|
|
259
|
+
targetKeys: readonly string[],
|
|
260
|
+
operation: () => Promise<T> | T,
|
|
261
|
+
): Promise<T> {
|
|
262
|
+
try {
|
|
263
|
+
const ownerScope = requestOwnerScope(input);
|
|
264
|
+
const targets = targetKeys.map((targetKey) => getActiveWorkspaceTargetGrant(
|
|
265
|
+
input.controllerHome,
|
|
266
|
+
targetKey,
|
|
267
|
+
currentDate(),
|
|
268
|
+
ownerScope,
|
|
269
|
+
));
|
|
270
|
+
return await withWorkspaceTargetMutationLocks(
|
|
271
|
+
input.controllerHome,
|
|
272
|
+
targets,
|
|
273
|
+
`local-system:${input.requestId}`,
|
|
274
|
+
operation,
|
|
275
|
+
{ waitMs: 5_000 },
|
|
276
|
+
);
|
|
277
|
+
} catch (error) {
|
|
278
|
+
return rethrowTargetError(error);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function authorizeTarget(input: AssistantPluginActionExecutionInput): Record<string, unknown> {
|
|
283
|
+
const rawAccess = optionalString(input.args, 'access');
|
|
284
|
+
const rawScope = optionalString(input.args, 'scope') ?? 'auto';
|
|
285
|
+
if (rawAccess && rawAccess !== 'read_only' && rawAccess !== 'read_write') {
|
|
286
|
+
throw new AssistantPluginError(
|
|
287
|
+
'PLUGIN_ACTION_ARGUMENT_INVALID',
|
|
288
|
+
'access must be read_only or read_write.',
|
|
289
|
+
{ retryable: false },
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
if (!['auto', 'project', 'directory'].includes(rawScope)) {
|
|
293
|
+
throw new AssistantPluginError(
|
|
294
|
+
'PLUGIN_ACTION_ARGUMENT_INVALID',
|
|
295
|
+
'scope must be auto, project, or directory.',
|
|
296
|
+
{ retryable: false },
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
const ownerScope = requestOwnerScope(input);
|
|
300
|
+
const controllerInstanceId = process.env.FORGE_WRITER_INSTANCE_ID?.trim()
|
|
301
|
+
|| process.env.FORGE_RUNTIME_INSTANCE_ID?.trim()
|
|
302
|
+
|| process.env.FORGE_DAEMON_INSTANCE_ID?.trim();
|
|
303
|
+
try {
|
|
304
|
+
const target = authorizeWorkspaceTargetGrant(input.controllerHome, {
|
|
305
|
+
targetKey: requiredString(input.args, 'target_key'),
|
|
306
|
+
rootPath: requiredString(input.args, 'root_path'),
|
|
307
|
+
expiresInMinutes: Number(input.args.expires_in_minutes ?? 480),
|
|
308
|
+
reason: requiredString(input.args, 'reason'),
|
|
309
|
+
access: (rawAccess as WorkspaceTargetAccess | undefined) ?? 'read_write',
|
|
310
|
+
scope: rawScope as WorkspaceTargetGrantScope,
|
|
311
|
+
ownerScope,
|
|
312
|
+
...(controllerInstanceId ? { controllerInstanceId } : {}),
|
|
313
|
+
now: currentDate(),
|
|
314
|
+
});
|
|
315
|
+
return {
|
|
316
|
+
target,
|
|
317
|
+
authorizationScope: rawScope,
|
|
318
|
+
requestedRootPath: requiredString(input.args, 'root_path'),
|
|
319
|
+
effectiveRootPath: target.rootPath,
|
|
320
|
+
storage: 'controllerHome/system/local-system/targets.json',
|
|
321
|
+
repositoryRegistered: false,
|
|
322
|
+
};
|
|
323
|
+
} catch (error) {
|
|
324
|
+
return rethrowTargetError(error);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function systemSnapshot(): Record<string, unknown> {
|
|
329
|
+
const processes = run('ps', ['-Ao', 'pid=,ppid=,%cpu=,%mem=,comm=', '-r']);
|
|
330
|
+
const vm = run('vm_stat', []);
|
|
331
|
+
const pressure = run('memory_pressure', []);
|
|
332
|
+
return {
|
|
333
|
+
platform: process.platform,
|
|
334
|
+
generatedAt: now(),
|
|
335
|
+
processes: bounded(processes.stdout || processes.stderr),
|
|
336
|
+
virtualMemory: bounded(vm.stdout || vm.stderr),
|
|
337
|
+
memoryPressure: bounded(pressure.stdout || pressure.stderr),
|
|
338
|
+
commands: [processes.command, vm.command, pressure.command],
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function processDetail(pidValue: unknown): Record<string, unknown> {
|
|
343
|
+
const pid = Number(pidValue);
|
|
344
|
+
if (!Number.isInteger(pid) || pid <= 0) throw new AssistantPluginError('PLUGIN_ACTION_ARGUMENT_INVALID', 'pid must be a positive integer.', { retryable: false });
|
|
345
|
+
const detail = run('ps', ['-p', String(pid), '-o', 'pid=,ppid=,user=,%cpu=,%mem=,etime=,state=,command=']);
|
|
346
|
+
return { pid, found: detail.ok && Boolean(detail.stdout.trim()), output: bounded(detail.stdout || detail.stderr), command: detail.command };
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function requiredPositivePid(value: unknown): number {
|
|
350
|
+
const pid = Number(value);
|
|
351
|
+
if (!Number.isInteger(pid) || pid <= 0) {
|
|
352
|
+
throw new AssistantPluginError('PLUGIN_ACTION_ARGUMENT_INVALID', 'pid must be a positive integer.', { retryable: false });
|
|
353
|
+
}
|
|
354
|
+
if (pid === process.pid) {
|
|
355
|
+
throw new AssistantPluginError('LOCAL_SYSTEM_PROCESS_SELF_DENIED', 'The local_system host process cannot terminate itself.', { retryable: false });
|
|
356
|
+
}
|
|
357
|
+
return pid;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function verifiedProcessCommand(pid: number, expectedCommand: string): { found: boolean; commandLine: string; command: string[] } {
|
|
361
|
+
const expected = expectedCommand.trim();
|
|
362
|
+
if (expected.length < 4) {
|
|
363
|
+
throw new AssistantPluginError('PLUGIN_ACTION_ARGUMENT_INVALID', 'expected_command_contains must contain at least 4 characters.', { retryable: false });
|
|
364
|
+
}
|
|
365
|
+
const detail = run('ps', ['-p', String(pid), '-o', 'command=']);
|
|
366
|
+
const commandLine = detail.stdout.trim();
|
|
367
|
+
if (!detail.ok || !commandLine) return { found: false, commandLine: '', command: detail.command };
|
|
368
|
+
if (!commandLine.includes(expected)) {
|
|
369
|
+
throw new AssistantPluginError(
|
|
370
|
+
'LOCAL_SYSTEM_PROCESS_IDENTITY_MISMATCH',
|
|
371
|
+
`PID ${pid} does not match expected command identity.`,
|
|
372
|
+
{ retryable: false, details: { pid, expectedCommandContains: expected, observedCommand: bounded(commandLine, 4096).content } },
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
return { found: true, commandLine, command: detail.command };
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function terminateVerifiedProcess(pidValue: unknown, expectedCommandValue: unknown): Record<string, unknown> {
|
|
379
|
+
const pid = requiredPositivePid(pidValue);
|
|
380
|
+
const expectedCommand = typeof expectedCommandValue === 'string' ? expectedCommandValue.trim() : '';
|
|
381
|
+
const verified = verifiedProcessCommand(pid, expectedCommand);
|
|
382
|
+
if (!verified.found) {
|
|
383
|
+
return { terminated: false, alreadyExited: true, pid, signal: 'SIGTERM', verificationCommand: verified.command };
|
|
384
|
+
}
|
|
385
|
+
try {
|
|
386
|
+
if (hooks.signalProcess) hooks.signalProcess(pid, 'SIGTERM');
|
|
387
|
+
else process.kill(pid, 'SIGTERM');
|
|
388
|
+
} catch (error) {
|
|
389
|
+
const code = (error as NodeJS.ErrnoException)?.code;
|
|
390
|
+
if (code === 'ESRCH') return { terminated: false, alreadyExited: true, pid, signal: 'SIGTERM', verificationCommand: verified.command };
|
|
391
|
+
throw new AssistantPluginError('LOCAL_SYSTEM_PROCESS_TERMINATE_FAILED', error instanceof Error ? error.message : String(error), {
|
|
392
|
+
retryable: code === 'EBUSY', details: { pid, expectedCommandContains: expectedCommand },
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
return {
|
|
396
|
+
terminated: true,
|
|
397
|
+
alreadyExited: false,
|
|
398
|
+
pid,
|
|
399
|
+
signal: 'SIGTERM',
|
|
400
|
+
expectedCommandContains: expectedCommand,
|
|
401
|
+
observedCommand: bounded(verified.commandLine, 4096),
|
|
402
|
+
verificationCommand: verified.command,
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
function userLaunchAgentIdentity(labelValue: unknown, expectedProgramValue: unknown): { label: string; expectedProgram: string; domain: string; service: string; plistPath: string } {
|
|
407
|
+
if (process.platform !== 'darwin' || typeof process.getuid !== 'function') {
|
|
408
|
+
throw new AssistantPluginError('LOCAL_SYSTEM_LAUNCHD_UNAVAILABLE', 'User LaunchAgent lifecycle control is available only on macOS.', { retryable: false });
|
|
409
|
+
}
|
|
410
|
+
const label = typeof labelValue === 'string' ? labelValue.trim() : '';
|
|
411
|
+
if (!/^[A-Za-z0-9._-]{3,200}$/.test(label)) {
|
|
412
|
+
throw new AssistantPluginError('PLUGIN_ACTION_ARGUMENT_INVALID', 'label must be a concrete macOS LaunchAgent label.', { retryable: false });
|
|
413
|
+
}
|
|
414
|
+
const expectedProgram = typeof expectedProgramValue === 'string' ? expectedProgramValue.trim() : '';
|
|
415
|
+
if (expectedProgram.length < 4) {
|
|
416
|
+
throw new AssistantPluginError('PLUGIN_ACTION_ARGUMENT_INVALID', 'expected_program_contains must contain at least 4 characters.', { retryable: false });
|
|
417
|
+
}
|
|
418
|
+
const home = process.env.HOME?.trim();
|
|
419
|
+
if (!home) throw new AssistantPluginError('LOCAL_SYSTEM_HOME_UNAVAILABLE', 'HOME is required to resolve the current user LaunchAgents directory.', { retryable: false });
|
|
420
|
+
const domain = `gui/${process.getuid()}`;
|
|
421
|
+
return {
|
|
422
|
+
label,
|
|
423
|
+
expectedProgram,
|
|
424
|
+
domain,
|
|
425
|
+
service: `${domain}/${label}`,
|
|
426
|
+
plistPath: join(resolve(home), 'Library', 'LaunchAgents', `${label}.plist`),
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
function assertLaunchAgentObservedIdentity(identity: ReturnType<typeof userLaunchAgentIdentity>, observed: string): void {
|
|
431
|
+
if (!observed.includes(identity.expectedProgram)) {
|
|
432
|
+
throw new AssistantPluginError('LOCAL_SYSTEM_LAUNCH_AGENT_IDENTITY_MISMATCH', `LaunchAgent ${identity.label} does not match expected program identity.`, {
|
|
433
|
+
retryable: false,
|
|
434
|
+
details: { label: identity.label, expectedProgramContains: identity.expectedProgram, observed: bounded(observed, 8192).content },
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
function assertInstalledLaunchAgentIdentity(identity: ReturnType<typeof userLaunchAgentIdentity>): void {
|
|
440
|
+
if (!existsSync(identity.plistPath) || !statSync(identity.plistPath).isFile()) {
|
|
441
|
+
throw new AssistantPluginError('LOCAL_SYSTEM_LAUNCH_AGENT_PLIST_NOT_FOUND', `LaunchAgent plist for ${identity.label} is unavailable.`, {
|
|
442
|
+
retryable: false, details: { label: identity.label, plistPath: identity.plistPath },
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
const content = readFileSync(identity.plistPath, 'utf8');
|
|
446
|
+
if (!content.includes(identity.expectedProgram) || !content.includes(identity.label)) {
|
|
447
|
+
throw new AssistantPluginError('LOCAL_SYSTEM_LAUNCH_AGENT_IDENTITY_MISMATCH', `LaunchAgent plist for ${identity.label} does not match expected identity.`, {
|
|
448
|
+
retryable: false, details: { label: identity.label, expectedProgramContains: identity.expectedProgram, plistPath: identity.plistPath },
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
export function restartVerifiedUserLaunchAgent(labelValue: unknown, expectedProgramValue: unknown): Record<string, unknown> {
|
|
454
|
+
const identity = userLaunchAgentIdentity(labelValue, expectedProgramValue);
|
|
455
|
+
const inspected = run('/bin/launchctl', ['print', identity.service], 15_000);
|
|
456
|
+
if (!inspected.ok) {
|
|
457
|
+
throw new AssistantPluginError('LOCAL_SYSTEM_LAUNCH_AGENT_NOT_FOUND', `LaunchAgent ${identity.label} is not loaded.`, {
|
|
458
|
+
retryable: false, details: { label: identity.label, service: identity.service, stderr: bounded(inspected.stderr, 4096).content },
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
assertLaunchAgentObservedIdentity(identity, `${inspected.stdout}\n${inspected.stderr}`);
|
|
462
|
+
const restarted = run('/bin/launchctl', ['kickstart', '-k', identity.service], 30_000);
|
|
463
|
+
if (!restarted.ok) {
|
|
464
|
+
throw new AssistantPluginError('LOCAL_SYSTEM_LAUNCH_AGENT_RESTART_FAILED', restarted.stderr.trim() || restarted.stdout.trim() || `Failed to restart ${identity.label}.`, {
|
|
465
|
+
retryable: true, details: { label: identity.label, service: identity.service, exitCode: restarted.status },
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
return { restarted: true, label: identity.label, service: identity.service, expectedProgramContains: identity.expectedProgram, inspectCommand: inspected.command, restartCommand: restarted.command };
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
export function stopVerifiedUserLaunchAgent(labelValue: unknown, expectedProgramValue: unknown): Record<string, unknown> {
|
|
472
|
+
const identity = userLaunchAgentIdentity(labelValue, expectedProgramValue);
|
|
473
|
+
const inspected = run('/bin/launchctl', ['print', identity.service], 15_000);
|
|
474
|
+
if (!inspected.ok) {
|
|
475
|
+
assertInstalledLaunchAgentIdentity(identity);
|
|
476
|
+
return { stopped: false, alreadyStopped: true, label: identity.label, service: identity.service, plistPath: identity.plistPath };
|
|
477
|
+
}
|
|
478
|
+
assertLaunchAgentObservedIdentity(identity, `${inspected.stdout}\n${inspected.stderr}`);
|
|
479
|
+
const stopped = run('/bin/launchctl', ['bootout', identity.service], 30_000);
|
|
480
|
+
if (!stopped.ok) {
|
|
481
|
+
throw new AssistantPluginError('LOCAL_SYSTEM_LAUNCH_AGENT_STOP_FAILED', stopped.stderr.trim() || stopped.stdout.trim() || `Failed to stop ${identity.label}.`, {
|
|
482
|
+
retryable: true, details: { label: identity.label, service: identity.service, exitCode: stopped.status },
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
return { stopped: true, alreadyStopped: false, label: identity.label, service: identity.service, expectedProgramContains: identity.expectedProgram, inspectCommand: inspected.command, stopCommand: stopped.command };
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
export function startVerifiedUserLaunchAgent(labelValue: unknown, expectedProgramValue: unknown): Record<string, unknown> {
|
|
489
|
+
const identity = userLaunchAgentIdentity(labelValue, expectedProgramValue);
|
|
490
|
+
assertInstalledLaunchAgentIdentity(identity);
|
|
491
|
+
const inspected = run('/bin/launchctl', ['print', identity.service], 15_000);
|
|
492
|
+
let bootstrapCommand: string[] | undefined;
|
|
493
|
+
if (inspected.ok) {
|
|
494
|
+
assertLaunchAgentObservedIdentity(identity, `${inspected.stdout}\n${inspected.stderr}`);
|
|
495
|
+
} else {
|
|
496
|
+
const bootstrapped = run('/bin/launchctl', ['bootstrap', identity.domain, identity.plistPath], 30_000);
|
|
497
|
+
if (!bootstrapped.ok) {
|
|
498
|
+
throw new AssistantPluginError('LOCAL_SYSTEM_LAUNCH_AGENT_START_FAILED', bootstrapped.stderr.trim() || bootstrapped.stdout.trim() || `Failed to bootstrap ${identity.label}.`, {
|
|
499
|
+
retryable: true, details: { label: identity.label, service: identity.service, plistPath: identity.plistPath, exitCode: bootstrapped.status },
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
bootstrapCommand = bootstrapped.command;
|
|
503
|
+
}
|
|
504
|
+
const enabled = run('/bin/launchctl', ['enable', identity.service], 15_000);
|
|
505
|
+
if (!enabled.ok) throw new AssistantPluginError('LOCAL_SYSTEM_LAUNCH_AGENT_START_FAILED', enabled.stderr.trim() || `Failed to enable ${identity.label}.`, { retryable: true });
|
|
506
|
+
const started = run('/bin/launchctl', ['kickstart', identity.service], 30_000);
|
|
507
|
+
if (!started.ok) throw new AssistantPluginError('LOCAL_SYSTEM_LAUNCH_AGENT_START_FAILED', started.stderr.trim() || `Failed to start ${identity.label}.`, { retryable: true });
|
|
508
|
+
return { started: true, label: identity.label, service: identity.service, plistPath: identity.plistPath, bootstrapCommand, enableCommand: enabled.command, startCommand: started.command };
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
function actions(): AssistantPluginActionDescriptor[] {
|
|
512
|
+
const controllerRead = [{ resource: 'repo-state' as const, mode: 'read' as const }];
|
|
513
|
+
const controllerWrite = [{ resource: 'repo-state' as const, mode: 'write' as const }];
|
|
514
|
+
const targetProperties = {
|
|
515
|
+
target_key: { type: 'string' },
|
|
516
|
+
path: { type: 'string' },
|
|
517
|
+
};
|
|
518
|
+
return [
|
|
519
|
+
{ actionId: 'system_snapshot', title: 'System snapshot', description: 'Read bounded CPU, process, memory, and pressure diagnostics.', readOnly: true, risk: 'readonly', confirmation: 'none', defaultTimeoutMs: 30_000, cancellable: true, idempotent: true, scopes: ['local-system.read'], resourceClaims: [], argumentsSchema: { type: 'object', properties: {}, additionalProperties: false } },
|
|
520
|
+
{ actionId: 'process_detail', title: 'Process detail', description: 'Read bounded details for one process id.', readOnly: true, risk: 'readonly', confirmation: 'none', defaultTimeoutMs: 15_000, cancellable: true, idempotent: true, scopes: ['local-system.read'], resourceClaims: [], argumentsSchema: { type: 'object', properties: { pid: { type: 'number' } }, required: ['pid'], additionalProperties: false } },
|
|
521
|
+
{ actionId: 'terminate_process', title: 'Terminate verified process', description: 'Send SIGTERM to one exact PID only after its current command line matches the caller-provided expected identity.', readOnly: false, risk: 'workspace_write', confirmation: 'authorization', defaultTimeoutMs: 15_000, cancellable: true, idempotent: true, scopes: ['local-system.process'], resourceClaims: controllerWrite, argumentsSchema: { type: 'object', properties: { pid: { type: 'number' }, expected_command_contains: { type: 'string' } }, required: ['pid', 'expected_command_contains'], additionalProperties: false } },
|
|
522
|
+
{ actionId: 'restart_user_launch_agent', title: 'Restart verified user LaunchAgent', description: 'Restart one loaded user LaunchAgent by exact label only after launchctl evidence contains the expected program identity.', readOnly: false, risk: 'workspace_write', confirmation: 'authorization', defaultTimeoutMs: 30_000, cancellable: true, idempotent: false, scopes: ['local-system.process'], resourceClaims: controllerWrite, argumentsSchema: { type: 'object', properties: { label: { type: 'string' }, expected_program_contains: { type: 'string' } }, required: ['label', 'expected_program_contains'], additionalProperties: false } },
|
|
523
|
+
{ actionId: 'stop_user_launch_agent', title: 'Stop verified user LaunchAgent', description: 'Boot out one exact loaded user LaunchAgent only after its launchd identity matches the expected program.', readOnly: false, risk: 'workspace_write', confirmation: 'authorization', defaultTimeoutMs: 30_000, cancellable: true, idempotent: true, scopes: ['local-system.process'], resourceClaims: controllerWrite, argumentsSchema: { type: 'object', properties: { label: { type: 'string' }, expected_program_contains: { type: 'string' } }, required: ['label', 'expected_program_contains'], additionalProperties: false } },
|
|
524
|
+
{ actionId: 'start_user_launch_agent', title: 'Start verified user LaunchAgent', description: 'Bootstrap or start one exact current-user LaunchAgent from ~/Library/LaunchAgents only after the installed plist matches the expected program identity.', readOnly: false, risk: 'workspace_write', confirmation: 'authorization', defaultTimeoutMs: 30_000, cancellable: true, idempotent: true, scopes: ['local-system.process'], resourceClaims: controllerWrite, argumentsSchema: { type: 'object', properties: { label: { type: 'string' }, expected_program_contains: { type: 'string' } }, required: ['label', 'expected_program_contains'], additionalProperties: false } },
|
|
525
|
+
{ actionId: 'open_application', title: 'Open application', description: 'Open one macOS application by name or bundle id.', readOnly: false, risk: 'workspace_write', confirmation: 'authorization', defaultTimeoutMs: 30_000, cancellable: true, idempotent: false, scopes: ['local-system.open'], resourceClaims: controllerWrite, argumentsSchema: { type: 'object', properties: { app_name: { type: 'string' }, bundle_id: { type: 'string' } }, additionalProperties: false } },
|
|
526
|
+
{ actionId: 'list_targets', title: 'List filesystem targets', description: 'List active expiring local filesystem grants.', readOnly: true, risk: 'readonly', confirmation: 'none', defaultTimeoutMs: 10_000, cancellable: true, idempotent: true, scopes: ['local-system.files.read'], resourceClaims: controllerRead, argumentsSchema: { type: 'object', properties: {}, additionalProperties: false } },
|
|
527
|
+
{ actionId: 'authorize_target', title: 'Authorize filesystem target', description: 'Authorize an expiring filesystem target. By default, a path inside a Git project authorizes the whole project/repository root; use scope=directory only when intentionally granting a narrower directory.', readOnly: false, risk: 'workspace_write', confirmation: 'authorization', defaultTimeoutMs: 15_000, cancellable: true, idempotent: true, scopes: ['local-system.files.write'], resourceClaims: controllerWrite, argumentsSchema: { type: 'object', properties: { target_key: { type: 'string' }, root_path: { type: 'string' }, scope: { type: 'string', enum: ['auto', 'project', 'directory'] }, expires_in_minutes: { type: 'number' }, reason: { type: 'string' }, access: { type: 'string', enum: ['read_only', 'read_write'] } }, required: ['target_key', 'root_path', 'reason'], additionalProperties: false } },
|
|
528
|
+
{ actionId: 'revoke_target', title: 'Revoke filesystem target', description: 'Immediately revoke one active filesystem target owned by the authenticated caller.', readOnly: false, risk: 'workspace_write', confirmation: 'authorization', defaultTimeoutMs: 15_000, cancellable: true, idempotent: false, scopes: ['local-system.files.write'], resourceClaims: controllerWrite, argumentsSchema: { type: 'object', properties: { target_key: { type: 'string' } }, required: ['target_key'], additionalProperties: false } },
|
|
529
|
+
{ actionId: 'list_directory', title: 'List directory', description: 'List a bounded directory snapshot below an authorized target.', readOnly: true, risk: 'readonly', confirmation: 'none', defaultTimeoutMs: 15_000, cancellable: true, idempotent: true, scopes: ['local-system.files.read'], resourceClaims: controllerRead, argumentsSchema: { type: 'object', properties: targetProperties, required: ['target_key'], additionalProperties: false } },
|
|
530
|
+
{ actionId: 'read_text', title: 'Read text file', description: 'Read a bounded UTF-8 text file below an authorized target.', readOnly: true, risk: 'readonly', confirmation: 'none', defaultTimeoutMs: 15_000, cancellable: true, idempotent: true, scopes: ['local-system.files.read'], resourceClaims: controllerRead, argumentsSchema: { type: 'object', properties: { ...targetProperties, max_chars: { type: 'number' } }, required: ['target_key', 'path'], additionalProperties: false } },
|
|
531
|
+
{ actionId: 'write_text', title: 'Write text file', description: 'Create or explicitly overwrite one bounded UTF-8 text file below an authorized read-write target.', readOnly: false, risk: 'workspace_write', confirmation: 'authorization', defaultTimeoutMs: 15_000, cancellable: true, idempotent: true, scopes: ['local-system.files.write'], resourceClaims: controllerWrite, argumentsSchema: { type: 'object', properties: { ...targetProperties, content: { type: 'string' }, overwrite: { type: 'boolean' } }, required: ['target_key', 'path', 'content'], additionalProperties: false } },
|
|
532
|
+
{ actionId: 'delete_file', title: 'Delete file', description: 'Delete one exact regular file below an authorized read-write target. Arbitrary recursive or directory deletion is never exposed.', readOnly: false, risk: 'destructive', confirmation: 'strong_confirmation', requiredConfirmationText: 'delete-local-system-file', defaultTimeoutMs: 15_000, cancellable: true, idempotent: false, scopes: ['local-system.files.write'], resourceClaims: controllerWrite, argumentsSchema: { type: 'object', properties: targetProperties, required: ['target_key', 'path'], additionalProperties: false } },
|
|
533
|
+
{ actionId: 'delete_empty_directory', title: 'Delete empty directory', description: 'Delete one exact empty directory below an authorized read-write target. Target roots and recursive directory deletion are never allowed.', readOnly: false, risk: 'destructive', confirmation: 'strong_confirmation', requiredConfirmationText: 'delete-local-system-empty-directory', defaultTimeoutMs: 15_000, cancellable: true, idempotent: false, scopes: ['local-system.files.write'], resourceClaims: controllerWrite, argumentsSchema: { type: 'object', properties: targetProperties, required: ['target_key', 'path'], additionalProperties: false } },
|
|
534
|
+
{ actionId: 'initialize_git', title: 'Initialize local Git repository', description: 'Run only git init inside an authorized read-write target without registering it in Repository Registry.', readOnly: false, risk: 'workspace_write', confirmation: 'authorization', defaultTimeoutMs: 30_000, cancellable: true, idempotent: true, scopes: ['local-system.files.write'], resourceClaims: controllerWrite, argumentsSchema: { type: 'object', properties: { target_key: { type: 'string' }, cwd: { type: 'string' }, initial_branch: { type: 'string' } }, required: ['target_key'], additionalProperties: false } },
|
|
535
|
+
{ actionId: 'execute_command', title: 'Execute target command', description: 'Execute one bounded typed-argv command inside an authorized target without repository registration. Shell strings, target escapes, destructive/remote writes, and Git mutations other than the dedicated initialize_git action are denied.', readOnly: false, risk: 'workspace_write', confirmation: 'authorization', defaultTimeoutMs: 30_000, cancellable: true, idempotent: false, scopes: ['local-system.files.read', 'local-system.files.write'], resourceClaims: controllerWrite, argumentsSchema: { type: 'object', properties: { target_key: { type: 'string' }, command: { type: 'array', items: { type: 'string' } }, cwd: { type: 'string' }, max_output_bytes: { type: 'number' } }, required: ['target_key', 'command'], additionalProperties: false } },
|
|
536
|
+
{ actionId: 'execute_project_script', title: 'Execute verified project script', description: 'Execute one exact script below an authorized read-write target through a fixed interpreter after its SHA-256 digest and strong confirmation match. Eval flags and shell command strings are never accepted.', readOnly: false, risk: 'destructive', confirmation: 'strong_confirmation', requiredConfirmationText: 'execute-local-system-project-script', defaultTimeoutMs: 30_000, cancellable: true, idempotent: false, scopes: ['local-system.files.read', 'local-system.files.write'], resourceClaims: controllerWrite, argumentsSchema: { type: 'object', properties: { target_key: { type: 'string' }, runtime: { type: 'string', enum: ['node', 'bun', 'python3', 'ruby', 'bash', 'sh'] }, script_path: { type: 'string' }, expected_sha256: { type: 'string' }, arguments: { type: 'array', items: { type: 'string' } }, cwd: { type: 'string' }, max_output_bytes: { type: 'number' } }, required: ['target_key', 'runtime', 'script_path', 'expected_sha256'], additionalProperties: false } },
|
|
537
|
+
{ actionId: 'create_directory', title: 'Create directory', description: 'Create a directory below an authorized target without leaving that root.', readOnly: false, risk: 'workspace_write', confirmation: 'authorization', defaultTimeoutMs: 15_000, cancellable: true, idempotent: true, scopes: ['local-system.files.write'], resourceClaims: controllerWrite, argumentsSchema: { type: 'object', properties: targetProperties, required: ['target_key', 'path'], additionalProperties: false } },
|
|
538
|
+
{ actionId: 'copy_file', title: 'Copy file', description: 'Copy a file between authorized targets without overwriting.', readOnly: false, risk: 'workspace_write', confirmation: 'authorization', defaultTimeoutMs: 30_000, cancellable: true, idempotent: false, scopes: ['local-system.files.write'], resourceClaims: controllerWrite, argumentsSchema: { type: 'object', properties: { source_target_key: { type: 'string' }, source_path: { type: 'string' }, destination_target_key: { type: 'string' }, destination_path: { type: 'string' } }, required: ['source_target_key', 'source_path', 'destination_target_key', 'destination_path'], additionalProperties: false } },
|
|
539
|
+
{ actionId: 'move_file', title: 'Move file', description: 'Move a file between authorized targets without overwriting.', readOnly: false, risk: 'workspace_write', confirmation: 'authorization', defaultTimeoutMs: 30_000, cancellable: true, idempotent: false, scopes: ['local-system.files.write'], resourceClaims: controllerWrite, argumentsSchema: { type: 'object', properties: { source_target_key: { type: 'string' }, source_path: { type: 'string' }, destination_target_key: { type: 'string' }, destination_path: { type: 'string' } }, required: ['source_target_key', 'source_path', 'destination_target_key', 'destination_path'], additionalProperties: false } },
|
|
540
|
+
{ actionId: 'rename_file', title: 'Rename file', description: 'Rename a file inside one authorized target without overwriting.', readOnly: false, risk: 'workspace_write', confirmation: 'authorization', defaultTimeoutMs: 30_000, cancellable: true, idempotent: false, scopes: ['local-system.files.write'], resourceClaims: controllerWrite, argumentsSchema: { type: 'object', properties: { target_key: { type: 'string' }, source_path: { type: 'string' }, destination_path: { type: 'string' } }, required: ['target_key', 'source_path', 'destination_path'], additionalProperties: false } },
|
|
541
|
+
{ actionId: 'reveal_in_finder', title: 'Reveal in Finder', description: 'Reveal an authorized file or directory in Finder.', readOnly: false, risk: 'workspace_write', confirmation: 'authorization', defaultTimeoutMs: 30_000, cancellable: true, idempotent: false, scopes: ['local-system.open'], resourceClaims: controllerRead, argumentsSchema: { type: 'object', properties: targetProperties, required: ['target_key', 'path'], additionalProperties: false } },
|
|
542
|
+
{ actionId: 'open_file', title: 'Open document', description: 'Open one authorized non-executable local document with its default application. Scripts, .command files, packages, disk images, app bundles, and executable files are never auto-opened.', readOnly: false, risk: 'workspace_write', confirmation: 'authorization', defaultTimeoutMs: 30_000, cancellable: true, idempotent: false, scopes: ['local-system.open'], resourceClaims: controllerRead, argumentsSchema: { type: 'object', properties: targetProperties, required: ['target_key', 'path'], additionalProperties: false } },
|
|
543
|
+
];
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
function health(): AssistantPluginHealth {
|
|
547
|
+
const ready = process.platform === 'darwin';
|
|
548
|
+
return {
|
|
549
|
+
state: ready ? 'ready' : 'degraded',
|
|
550
|
+
checkedAt: now(),
|
|
551
|
+
ready,
|
|
552
|
+
probed: true,
|
|
553
|
+
errors: [],
|
|
554
|
+
warnings: ready ? [] : ['Application opening and macOS diagnostics require macOS.'],
|
|
555
|
+
details: {
|
|
556
|
+
provider: 'local-macos',
|
|
557
|
+
scope: 'controller',
|
|
558
|
+
repositoryRegistrationRequired: false,
|
|
559
|
+
targetAuthority: 'controllerHome/system/local-system/targets.json',
|
|
560
|
+
},
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
function permissions(): AssistantPluginPermissionScope[] {
|
|
565
|
+
return [
|
|
566
|
+
{ scope: 'local-system.read', mode: 'read', description: 'Read bounded local process and memory diagnostics.', granted: true, required: true },
|
|
567
|
+
{ scope: 'local-system.process', mode: 'write', description: 'Terminate one verified PID or control one verified current-user LaunchAgent after explicit authorization.', granted: true, required: false },
|
|
568
|
+
{ scope: 'local-system.open', mode: 'write', description: 'Open applications or authorized files.', granted: true, required: false },
|
|
569
|
+
{ scope: 'local-system.files.read', mode: 'read', description: 'Read files only below active target grants.', granted: true, required: false },
|
|
570
|
+
{ scope: 'local-system.files.write', mode: 'write', description: 'Create, copy, move, rename, or explicitly delete bounded files and empty directories below active read-write target grants.', granted: true, required: false },
|
|
571
|
+
];
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
function capabilities(): AssistantPluginCapability[] {
|
|
575
|
+
return [
|
|
576
|
+
{ capabilityId: 'local-system-diagnostics', title: 'Local diagnostics', description: 'Inspect CPU, processes, and memory with bounded typed commands.', scopes: ['local-system.read'], actions: ['system_snapshot', 'process_detail'] },
|
|
577
|
+
{ capabilityId: 'local-system-process-control', title: 'Verified process lifecycle', description: 'Terminate one verified PID or stop/start/restart one verified macOS user LaunchAgent without exposing arbitrary shell execution.', scopes: ['local-system.process'], actions: ['terminate_process', 'restart_user_launch_agent', 'stop_user_launch_agent', 'start_user_launch_agent'] },
|
|
578
|
+
{ capabilityId: 'local-system-open', title: 'Open local applications and files', description: 'Open applications and authorized files without arbitrary shell access.', scopes: ['local-system.open'], actions: ['open_application', 'reveal_in_finder', 'open_file'] },
|
|
579
|
+
{ capabilityId: 'local-system-files', title: 'Authorized local files', description: 'Use expiring target grants for bounded local file operations and typed-argv commands without repository registration.', scopes: ['local-system.files.read', 'local-system.files.write'], actions: ['list_targets', 'authorize_target', 'revoke_target', 'list_directory', 'read_text', 'write_text', 'delete_file', 'delete_empty_directory', 'initialize_git', 'execute_command', 'execute_project_script', 'create_directory', 'copy_file', 'move_file', 'rename_file'] },
|
|
580
|
+
];
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
export function buildLocalSystemPluginManifest(previousRevision = 0, previousUpdatedAt?: string): AssistantPluginManifest {
|
|
584
|
+
const currentHealth = health();
|
|
585
|
+
return {
|
|
586
|
+
schemaVersion: 1,
|
|
587
|
+
manifestVersion: 1,
|
|
588
|
+
revision: Math.max(1, previousRevision || 1),
|
|
589
|
+
pluginId: PLUGIN_ID,
|
|
590
|
+
provider: 'local-macos',
|
|
591
|
+
displayName: 'Local System Assistant',
|
|
592
|
+
pluginVersion: '1.5.1',
|
|
593
|
+
authority: { strategy: 'derived', duplicateStateAllowed: false, sourceOfTruth: ['controllerHome:system/local-system'] },
|
|
594
|
+
enabled: true,
|
|
595
|
+
lifecycle: { state: currentHealth.ready ? 'enabled' : 'degraded', reason: currentHealth.ready ? 'Local system capabilities are ready.' : currentHealth.warnings[0] },
|
|
596
|
+
health: currentHealth,
|
|
597
|
+
permissions: permissions(),
|
|
598
|
+
capabilities: capabilities(),
|
|
599
|
+
actions: actions(),
|
|
600
|
+
updatedAt: previousUpdatedAt ?? now(),
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
function filePair(
|
|
605
|
+
input: AssistantPluginActionExecutionInput,
|
|
606
|
+
sourceOperation: WorkspaceTargetOperation,
|
|
607
|
+
): { source: string; destination: string } {
|
|
608
|
+
const source = resolveTargetPath(
|
|
609
|
+
input,
|
|
610
|
+
requiredString(input.args, 'source_target_key'),
|
|
611
|
+
requiredString(input.args, 'source_path'),
|
|
612
|
+
{ mustExist: true, file: true, operation: sourceOperation },
|
|
613
|
+
);
|
|
614
|
+
const destination = resolveTargetPath(
|
|
615
|
+
input,
|
|
616
|
+
requiredString(input.args, 'destination_target_key'),
|
|
617
|
+
requiredString(input.args, 'destination_path'),
|
|
618
|
+
{ mustExist: false, operation: 'write' },
|
|
619
|
+
);
|
|
620
|
+
if (existsSync(destination.path)) throw new AssistantPluginError('LOCAL_SYSTEM_DESTINATION_EXISTS', 'Destination already exists; overwrite is not allowed.', { retryable: false });
|
|
621
|
+
mkdirSync(dirname(destination.path), { recursive: true });
|
|
622
|
+
return { source: source.path, destination: destination.path };
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
export async function executeLocalSystemPluginAction(input: AssistantPluginActionExecutionInput): Promise<Record<string, unknown>> {
|
|
626
|
+
switch (input.actionId) {
|
|
627
|
+
case 'system_snapshot': return systemSnapshot();
|
|
628
|
+
case 'process_detail': return processDetail(input.args.pid);
|
|
629
|
+
case 'terminate_process': return terminateVerifiedProcess(input.args.pid, input.args.expected_command_contains);
|
|
630
|
+
case 'restart_user_launch_agent': return restartVerifiedUserLaunchAgent(input.args.label, input.args.expected_program_contains);
|
|
631
|
+
case 'stop_user_launch_agent': return stopVerifiedUserLaunchAgent(input.args.label, input.args.expected_program_contains);
|
|
632
|
+
case 'start_user_launch_agent': return startVerifiedUserLaunchAgent(input.args.label, input.args.expected_program_contains);
|
|
633
|
+
case 'open_application': {
|
|
634
|
+
const appName = optionalString(input.args, 'app_name');
|
|
635
|
+
const bundleId = optionalString(input.args, 'bundle_id');
|
|
636
|
+
if ((!appName && !bundleId) || (appName && bundleId)) {
|
|
637
|
+
throw new AssistantPluginError('PLUGIN_ACTION_ARGUMENT_INVALID', 'Provide exactly one of app_name or bundle_id.', { retryable: false });
|
|
638
|
+
}
|
|
639
|
+
const command = bundleId ? ['open', '-b', bundleId] : ['open', '-a', appName as string];
|
|
640
|
+
const opened = run(command[0], command.slice(1));
|
|
641
|
+
if (!opened.ok) throw new AssistantPluginError('LOCAL_SYSTEM_OPEN_FAILED', opened.stderr || opened.stdout, { retryable: true, details: { command } });
|
|
642
|
+
return { opened: true, command };
|
|
643
|
+
}
|
|
644
|
+
case 'list_targets': return { targets: activeTargets(input), repositoryRegistered: false };
|
|
645
|
+
case 'authorize_target': return authorizeTarget(input);
|
|
646
|
+
case 'revoke_target': {
|
|
647
|
+
try {
|
|
648
|
+
const target = revokeWorkspaceTargetGrant(input.controllerHome, {
|
|
649
|
+
targetKey: requiredString(input.args, 'target_key'),
|
|
650
|
+
ownerScope: requestOwnerScope(input),
|
|
651
|
+
now: currentDate(),
|
|
652
|
+
});
|
|
653
|
+
return {
|
|
654
|
+
revoked: true,
|
|
655
|
+
targetKey: target.targetKey,
|
|
656
|
+
workspaceId: target.workspaceId,
|
|
657
|
+
identityFingerprint: target.identityFingerprint,
|
|
658
|
+
repositoryRegistered: false,
|
|
659
|
+
};
|
|
660
|
+
} catch (error) {
|
|
661
|
+
return rethrowTargetError(error);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
case 'list_directory': {
|
|
665
|
+
const resolved = resolveTargetPath(input, requiredString(input.args, 'target_key'), optionalString(input.args, 'path'), { mustExist: true, directory: true });
|
|
666
|
+
const entries = readdirSync(resolved.path, { withFileTypes: true }).slice(0, MAX_DIRECTORY_ENTRIES).map((entry) => ({
|
|
667
|
+
name: entry.name,
|
|
668
|
+
type: entry.isDirectory() ? 'directory' : entry.isFile() ? 'file' : entry.isSymbolicLink() ? 'symlink' : 'other',
|
|
669
|
+
}));
|
|
670
|
+
return { targetKey: resolved.target.targetKey, path: resolved.relativePath, entries, truncated: entries.length === MAX_DIRECTORY_ENTRIES };
|
|
671
|
+
}
|
|
672
|
+
case 'read_text': {
|
|
673
|
+
const resolved = resolveTargetPath(input, requiredString(input.args, 'target_key'), requiredString(input.args, 'path'), { mustExist: true, file: true });
|
|
674
|
+
const maxChars = Math.max(1, Math.min(Math.trunc(Number(input.args.max_chars ?? 20_000)), MAX_TEXT_CHARS));
|
|
675
|
+
const content = readFileSync(resolved.path, 'utf8');
|
|
676
|
+
return { targetKey: resolved.target.targetKey, path: resolved.relativePath, content: content.slice(0, maxChars), truncated: content.length > maxChars };
|
|
677
|
+
}
|
|
678
|
+
case 'write_text': {
|
|
679
|
+
const targetKey = requiredString(input.args, 'target_key');
|
|
680
|
+
const content = typeof input.args.content === 'string' ? input.args.content : undefined;
|
|
681
|
+
if (content === undefined) throw new AssistantPluginError('PLUGIN_ACTION_ARGUMENT_INVALID', 'content must be a string.', { retryable: false });
|
|
682
|
+
if (Buffer.byteLength(content, 'utf8') > MAX_TEXT_CHARS * 4) {
|
|
683
|
+
throw new AssistantPluginError('LOCAL_SYSTEM_TEXT_TOO_LARGE', `Text writes are limited to ${MAX_TEXT_CHARS * 4} UTF-8 bytes.`, { retryable: false });
|
|
684
|
+
}
|
|
685
|
+
const overwrite = input.args.overwrite === true;
|
|
686
|
+
return await withTargetMutation(input, [targetKey], () => {
|
|
687
|
+
const resolved = resolveTargetPath(input, targetKey, requiredString(input.args, 'path'), { mustExist: false, operation: 'write' });
|
|
688
|
+
if (existsSync(resolved.path) && !overwrite) {
|
|
689
|
+
throw new AssistantPluginError('LOCAL_SYSTEM_DESTINATION_EXISTS', 'Destination already exists; set overwrite=true to replace a text file.', { retryable: false });
|
|
690
|
+
}
|
|
691
|
+
if (existsSync(resolved.path) && !statSync(resolved.path).isFile()) {
|
|
692
|
+
throw new AssistantPluginError('LOCAL_SYSTEM_DESTINATION_EXISTS', 'Destination exists and is not a regular file.', { retryable: false });
|
|
693
|
+
}
|
|
694
|
+
mkdirSync(dirname(resolved.path), { recursive: true });
|
|
695
|
+
writeFileSync(resolved.path, content, { encoding: 'utf8', flag: overwrite ? 'w' : 'wx', mode: 0o600 });
|
|
696
|
+
return { written: true, targetKey: resolved.target.targetKey, workspaceId: resolved.target.workspaceId, path: resolved.relativePath, overwrite, bytes: Buffer.byteLength(content, 'utf8'), repositoryRegistered: false };
|
|
697
|
+
});
|
|
698
|
+
}
|
|
699
|
+
case 'delete_file': {
|
|
700
|
+
const targetKey = requiredString(input.args, 'target_key');
|
|
701
|
+
return await withTargetMutation(input, [targetKey], () => {
|
|
702
|
+
const resolved = resolveTargetPath(input, targetKey, requiredString(input.args, 'path'), {
|
|
703
|
+
mustExist: true,
|
|
704
|
+
file: true,
|
|
705
|
+
operation: 'write',
|
|
706
|
+
});
|
|
707
|
+
unlinkSync(resolved.path);
|
|
708
|
+
return {
|
|
709
|
+
deleted: true,
|
|
710
|
+
targetKey: resolved.target.targetKey,
|
|
711
|
+
workspaceId: resolved.target.workspaceId,
|
|
712
|
+
identityFingerprint: resolved.target.identityFingerprint,
|
|
713
|
+
path: resolved.relativePath,
|
|
714
|
+
repositoryRegistered: false,
|
|
715
|
+
};
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
case 'delete_empty_directory': {
|
|
719
|
+
const targetKey = requiredString(input.args, 'target_key');
|
|
720
|
+
return await withTargetMutation(input, [targetKey], () => {
|
|
721
|
+
const resolved = resolveTargetPath(input, targetKey, requiredString(input.args, 'path'), {
|
|
722
|
+
mustExist: true,
|
|
723
|
+
directory: true,
|
|
724
|
+
operation: 'write',
|
|
725
|
+
});
|
|
726
|
+
if (resolved.path === resolved.root) {
|
|
727
|
+
throw new AssistantPluginError(
|
|
728
|
+
'LOCAL_SYSTEM_TARGET_ROOT_DELETE_DENIED',
|
|
729
|
+
'Deleting the authorized target root is not allowed.',
|
|
730
|
+
{ retryable: false },
|
|
731
|
+
);
|
|
732
|
+
}
|
|
733
|
+
if (readdirSync(resolved.path).length > 0) {
|
|
734
|
+
throw new AssistantPluginError(
|
|
735
|
+
'LOCAL_SYSTEM_DIRECTORY_NOT_EMPTY',
|
|
736
|
+
'Only empty directories can be deleted.',
|
|
737
|
+
{ retryable: false, details: { path: resolved.relativePath } },
|
|
738
|
+
);
|
|
739
|
+
}
|
|
740
|
+
try {
|
|
741
|
+
rmdirSync(resolved.path);
|
|
742
|
+
} catch (error) {
|
|
743
|
+
const code = (error as NodeJS.ErrnoException)?.code;
|
|
744
|
+
if (code === 'ENOTEMPTY' || code === 'EEXIST') {
|
|
745
|
+
throw new AssistantPluginError(
|
|
746
|
+
'LOCAL_SYSTEM_DIRECTORY_NOT_EMPTY',
|
|
747
|
+
'Only empty directories can be deleted.',
|
|
748
|
+
{ retryable: false, details: { path: resolved.relativePath } },
|
|
749
|
+
);
|
|
750
|
+
}
|
|
751
|
+
throw new AssistantPluginError(
|
|
752
|
+
'LOCAL_SYSTEM_DIRECTORY_DELETE_FAILED',
|
|
753
|
+
error instanceof Error ? error.message : String(error),
|
|
754
|
+
{ retryable: code === 'EBUSY', details: { path: resolved.relativePath } },
|
|
755
|
+
);
|
|
756
|
+
}
|
|
757
|
+
return {
|
|
758
|
+
deleted: true,
|
|
759
|
+
targetKey: resolved.target.targetKey,
|
|
760
|
+
workspaceId: resolved.target.workspaceId,
|
|
761
|
+
identityFingerprint: resolved.target.identityFingerprint,
|
|
762
|
+
path: resolved.relativePath,
|
|
763
|
+
repositoryRegistered: false,
|
|
764
|
+
};
|
|
765
|
+
});
|
|
766
|
+
}
|
|
767
|
+
case 'initialize_git': {
|
|
768
|
+
const targetKey = requiredString(input.args, 'target_key');
|
|
769
|
+
const initialBranch = optionalString(input.args, 'initial_branch');
|
|
770
|
+
if (initialBranch && !/^[A-Za-z0-9._/-]+$/.test(initialBranch)) {
|
|
771
|
+
throw new AssistantPluginError('PLUGIN_ACTION_ARGUMENT_INVALID', 'initial_branch contains unsupported characters.', { retryable: false });
|
|
772
|
+
}
|
|
773
|
+
return await withTargetMutation(input, [targetKey], async () => {
|
|
774
|
+
let resolved;
|
|
775
|
+
try {
|
|
776
|
+
resolved = resolveWorkspaceTargetCwd(input.controllerHome, targetKey, requestOwnerScope(input), optionalString(input.args, 'cwd') ?? '.', 'write', currentDate());
|
|
777
|
+
} catch (error) {
|
|
778
|
+
return rethrowTargetError(error);
|
|
779
|
+
}
|
|
780
|
+
const command = ['git', 'init', ...(initialBranch ? ['-b', initialBranch] : [])];
|
|
781
|
+
const canonical = assertRepositoryCommandInputAllowed(command);
|
|
782
|
+
const executed = await runCanonicalCommand(canonical, resolved.path, Math.max(1_000, Math.min(Math.trunc(input.timeoutMs ?? 30_000), 30_000)), MAX_OUTPUT_BYTES, { signal: input.signal });
|
|
783
|
+
if (!executed.ok) {
|
|
784
|
+
throw new AssistantPluginError('LOCAL_SYSTEM_GIT_INIT_FAILED', executed.stderr.trim() || executed.stdout.trim() || `git init exited with code ${executed.exitCode}.`, { retryable: false, details: { exitCode: executed.exitCode } });
|
|
785
|
+
}
|
|
786
|
+
return { initialized: true, targetKey: resolved.target.targetKey, workspaceId: resolved.target.workspaceId, cwd: resolved.relativePath || '.', repositoryRegistered: false, command, stdout: executed.stdout, stderr: executed.stderr };
|
|
787
|
+
});
|
|
788
|
+
}
|
|
789
|
+
case 'execute_command': {
|
|
790
|
+
const targetKey = requiredString(input.args, 'target_key');
|
|
791
|
+
const argv = requiredStringArray(input.args, 'command');
|
|
792
|
+
const canonical = assertRepositoryCommandInputAllowed(argv);
|
|
793
|
+
if (canonical.kind !== 'argv') {
|
|
794
|
+
throw new AssistantPluginError('LOCAL_SYSTEM_COMMAND_TYPED_ARGV_REQUIRED', 'Target commands must use typed argv and cannot use a shell string.', { retryable: false });
|
|
795
|
+
}
|
|
796
|
+
const classification = classifyRepositoryCommand(canonical);
|
|
797
|
+
if (classification.risk === 'remote_write' || classification.risk === 'destructive') {
|
|
798
|
+
throw new AssistantPluginError('LOCAL_SYSTEM_COMMAND_RISK_DENIED', `Target command risk ${classification.risk} is not supported without repository registration.`, { retryable: false });
|
|
799
|
+
}
|
|
800
|
+
const executable = canonical.executable!.split(/[\\/]/).at(-1)?.toLowerCase() ?? '';
|
|
801
|
+
if (executable === 'git' && classification.risk !== 'readonly') {
|
|
802
|
+
throw new AssistantPluginError('LOCAL_SYSTEM_GIT_MUTATION_REQUIRES_REPOSITORY', 'Git mutations require a registered repository/checkout so refs and worktree ownership remain fenced.', { retryable: false });
|
|
803
|
+
}
|
|
804
|
+
if (classification.risk === 'workspace_write') {
|
|
805
|
+
const replay = classifyRepositoryCommandReplay(canonical);
|
|
806
|
+
const boundedFilesystemMutation = TARGET_SAFE_FILESYSTEM_MUTATORS.has(executable);
|
|
807
|
+
if (!boundedFilesystemMutation && !replay.replayable) {
|
|
808
|
+
throw new AssistantPluginError(
|
|
809
|
+
'LOCAL_SYSTEM_COMMAND_REPOSITORY_REQUIRED',
|
|
810
|
+
`Target command ${executable || '<unknown>'} has unbounded or unclassified side effects; register/promote the target before executing it.`,
|
|
811
|
+
{ retryable: false },
|
|
812
|
+
);
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
const operation: WorkspaceTargetOperation = classification.risk === 'readonly' ? 'read' : 'write';
|
|
816
|
+
const execute = async () => {
|
|
817
|
+
let resolved;
|
|
818
|
+
try {
|
|
819
|
+
resolved = resolveWorkspaceTargetCwd(
|
|
820
|
+
input.controllerHome,
|
|
821
|
+
targetKey,
|
|
822
|
+
requestOwnerScope(input),
|
|
823
|
+
optionalString(input.args, 'cwd') ?? '.',
|
|
824
|
+
operation,
|
|
825
|
+
currentDate(),
|
|
826
|
+
);
|
|
827
|
+
} catch (error) {
|
|
828
|
+
return rethrowTargetError(error);
|
|
829
|
+
}
|
|
830
|
+
assertCommandPathOperandsStayInRepository(canonical, resolved.path, resolved.root, []);
|
|
831
|
+
const maxOutputBytes = Math.max(1_024, Math.min(Math.trunc(Number(input.args.max_output_bytes ?? MAX_OUTPUT_BYTES)), 1024 * 1024));
|
|
832
|
+
const timeoutMs = Math.max(1_000, Math.min(Math.trunc(input.timeoutMs ?? 30_000), 30_000));
|
|
833
|
+
const executed = await runCanonicalCommand(canonical, resolved.path, timeoutMs, maxOutputBytes, { signal: input.signal });
|
|
834
|
+
if (!executed.ok) {
|
|
835
|
+
const code = executed.timedOut
|
|
836
|
+
? 'LOCAL_SYSTEM_COMMAND_TIMED_OUT'
|
|
837
|
+
: executed.cancelled
|
|
838
|
+
? 'LOCAL_SYSTEM_COMMAND_CANCELLED'
|
|
839
|
+
: 'LOCAL_SYSTEM_COMMAND_FAILED';
|
|
840
|
+
const message = executed.stderr.trim()
|
|
841
|
+
|| executed.stdout.trim()
|
|
842
|
+
|| `Target command exited with code ${executed.exitCode}.`;
|
|
843
|
+
throw new AssistantPluginError(code, bounded(message, maxOutputBytes).content, {
|
|
844
|
+
retryable: executed.timedOut,
|
|
845
|
+
details: {
|
|
846
|
+
command: argv,
|
|
847
|
+
exitCode: executed.exitCode,
|
|
848
|
+
timedOut: executed.timedOut,
|
|
849
|
+
cancelled: executed.cancelled,
|
|
850
|
+
},
|
|
851
|
+
});
|
|
852
|
+
}
|
|
853
|
+
return {
|
|
854
|
+
targetKey: resolved.target.targetKey,
|
|
855
|
+
workspaceId: resolved.target.workspaceId,
|
|
856
|
+
identityFingerprint: resolved.target.identityFingerprint,
|
|
857
|
+
repositoryRegistered: false,
|
|
858
|
+
cwd: resolved.relativePath || '.',
|
|
859
|
+
command: argv,
|
|
860
|
+
classification,
|
|
861
|
+
ok: executed.ok,
|
|
862
|
+
exitCode: executed.exitCode,
|
|
863
|
+
timedOut: executed.timedOut,
|
|
864
|
+
cancelled: executed.cancelled,
|
|
865
|
+
stdout: executed.stdout,
|
|
866
|
+
stderr: executed.stderr,
|
|
867
|
+
};
|
|
868
|
+
};
|
|
869
|
+
return operation === 'write'
|
|
870
|
+
? await withTargetMutation(input, [targetKey], execute)
|
|
871
|
+
: await execute();
|
|
872
|
+
}
|
|
873
|
+
case 'execute_project_script': {
|
|
874
|
+
const targetKey = requiredString(input.args, 'target_key');
|
|
875
|
+
const runtime = requiredString(input.args, 'runtime').toLowerCase();
|
|
876
|
+
if (!PROJECT_SCRIPT_RUNTIMES.has(runtime)) {
|
|
877
|
+
throw new AssistantPluginError('PLUGIN_ACTION_ARGUMENT_INVALID', 'runtime is not an approved project-script interpreter.', { retryable: false });
|
|
878
|
+
}
|
|
879
|
+
const expectedSha256 = requiredString(input.args, 'expected_sha256').toLowerCase();
|
|
880
|
+
if (!/^[a-f0-9]{64}$/.test(expectedSha256)) {
|
|
881
|
+
throw new AssistantPluginError('PLUGIN_ACTION_ARGUMENT_INVALID', 'expected_sha256 must be a 64-character hexadecimal digest.', { retryable: false });
|
|
882
|
+
}
|
|
883
|
+
const scriptArguments = optionalStringArray(input.args, 'arguments');
|
|
884
|
+
return await withTargetMutation(input, [targetKey], async () => {
|
|
885
|
+
const script = resolveTargetPath(input, targetKey, requiredString(input.args, 'script_path'), {
|
|
886
|
+
mustExist: true,
|
|
887
|
+
file: true,
|
|
888
|
+
operation: 'write',
|
|
889
|
+
});
|
|
890
|
+
let cwd;
|
|
891
|
+
try {
|
|
892
|
+
cwd = resolveWorkspaceTargetCwd(
|
|
893
|
+
input.controllerHome,
|
|
894
|
+
targetKey,
|
|
895
|
+
requestOwnerScope(input),
|
|
896
|
+
optionalString(input.args, 'cwd') ?? '.',
|
|
897
|
+
'write',
|
|
898
|
+
currentDate(),
|
|
899
|
+
);
|
|
900
|
+
} catch (error) {
|
|
901
|
+
return rethrowTargetError(error);
|
|
902
|
+
}
|
|
903
|
+
const observedSha256 = createHash('sha256').update(readFileSync(script.path)).digest('hex');
|
|
904
|
+
if (observedSha256 !== expectedSha256) {
|
|
905
|
+
throw new AssistantPluginError(
|
|
906
|
+
'LOCAL_SYSTEM_SCRIPT_DIGEST_MISMATCH',
|
|
907
|
+
'The project script changed after review; provide its current SHA-256 digest before retrying.',
|
|
908
|
+
{ retryable: false, details: { targetKey, path: script.relativePath, expectedSha256, observedSha256 } },
|
|
909
|
+
);
|
|
910
|
+
}
|
|
911
|
+
const argv = [projectScriptRuntimePath(runtime), script.path, ...scriptArguments];
|
|
912
|
+
const canonical = assertRepositoryCommandInputAllowed(argv);
|
|
913
|
+
assertCommandPathOperandsStayInRepository(canonical, cwd.path, cwd.root, []);
|
|
914
|
+
const maxOutputBytes = Math.max(1_024, Math.min(Math.trunc(Number(input.args.max_output_bytes ?? MAX_OUTPUT_BYTES)), 1024 * 1024));
|
|
915
|
+
const timeoutMs = Math.max(1_000, Math.min(Math.trunc(input.timeoutMs ?? 30_000), 30_000));
|
|
916
|
+
const executed = await runCanonicalCommand(canonical, cwd.path, timeoutMs, maxOutputBytes, { signal: input.signal });
|
|
917
|
+
if (!executed.ok) {
|
|
918
|
+
const code = executed.timedOut
|
|
919
|
+
? 'LOCAL_SYSTEM_SCRIPT_TIMED_OUT'
|
|
920
|
+
: executed.cancelled
|
|
921
|
+
? 'LOCAL_SYSTEM_SCRIPT_CANCELLED'
|
|
922
|
+
: 'LOCAL_SYSTEM_SCRIPT_FAILED';
|
|
923
|
+
throw new AssistantPluginError(
|
|
924
|
+
code,
|
|
925
|
+
bounded(executed.stderr.trim() || executed.stdout.trim() || `Project script exited with code ${executed.exitCode}.`, maxOutputBytes).content,
|
|
926
|
+
{ retryable: executed.timedOut, details: { runtime, path: script.relativePath, exitCode: executed.exitCode } },
|
|
927
|
+
);
|
|
928
|
+
}
|
|
929
|
+
return {
|
|
930
|
+
targetKey: script.target.targetKey,
|
|
931
|
+
workspaceId: script.target.workspaceId,
|
|
932
|
+
identityFingerprint: script.target.identityFingerprint,
|
|
933
|
+
repositoryRegistered: false,
|
|
934
|
+
cwd: cwd.relativePath || '.',
|
|
935
|
+
runtime,
|
|
936
|
+
scriptPath: script.relativePath,
|
|
937
|
+
scriptSha256: observedSha256,
|
|
938
|
+
arguments: scriptArguments,
|
|
939
|
+
ok: true,
|
|
940
|
+
exitCode: executed.exitCode,
|
|
941
|
+
timedOut: executed.timedOut,
|
|
942
|
+
cancelled: executed.cancelled,
|
|
943
|
+
stdout: executed.stdout,
|
|
944
|
+
stderr: executed.stderr,
|
|
945
|
+
};
|
|
946
|
+
});
|
|
947
|
+
}
|
|
948
|
+
case 'create_directory': {
|
|
949
|
+
const targetKey = requiredString(input.args, 'target_key');
|
|
950
|
+
return await withTargetMutation(input, [targetKey], () => {
|
|
951
|
+
const resolved = resolveTargetPath(input, targetKey, requiredString(input.args, 'path'), { mustExist: false, operation: 'write' });
|
|
952
|
+
if (existsSync(resolved.path) && !statSync(resolved.path).isDirectory()) throw new AssistantPluginError('LOCAL_SYSTEM_DESTINATION_EXISTS', 'A non-directory already exists at the destination.', { retryable: false });
|
|
953
|
+
mkdirSync(resolved.path, { recursive: true });
|
|
954
|
+
return { created: true, targetKey: resolved.target.targetKey, path: resolved.relativePath };
|
|
955
|
+
});
|
|
956
|
+
}
|
|
957
|
+
case 'copy_file': {
|
|
958
|
+
const sourceTargetKey = requiredString(input.args, 'source_target_key');
|
|
959
|
+
const destinationTargetKey = requiredString(input.args, 'destination_target_key');
|
|
960
|
+
return await withTargetMutation(input, [sourceTargetKey, destinationTargetKey], () => {
|
|
961
|
+
const pair = filePair(input, 'read');
|
|
962
|
+
copyFileSync(pair.source, pair.destination, constants.COPYFILE_EXCL);
|
|
963
|
+
return { copied: true, source: pair.source, destination: pair.destination, overwrite: false };
|
|
964
|
+
});
|
|
965
|
+
}
|
|
966
|
+
case 'move_file': {
|
|
967
|
+
const sourceTargetKey = requiredString(input.args, 'source_target_key');
|
|
968
|
+
const destinationTargetKey = requiredString(input.args, 'destination_target_key');
|
|
969
|
+
return await withTargetMutation(input, [sourceTargetKey, destinationTargetKey], () => {
|
|
970
|
+
const pair = filePair(input, 'write');
|
|
971
|
+
renameSync(pair.source, pair.destination);
|
|
972
|
+
return { moved: true, source: pair.source, destination: pair.destination, overwrite: false };
|
|
973
|
+
});
|
|
974
|
+
}
|
|
975
|
+
case 'rename_file': {
|
|
976
|
+
const targetKey = requiredString(input.args, 'target_key');
|
|
977
|
+
return await withTargetMutation(input, [targetKey], () => {
|
|
978
|
+
const source = resolveTargetPath(input, targetKey, requiredString(input.args, 'source_path'), { mustExist: true, operation: 'write' });
|
|
979
|
+
const destination = resolveTargetPath(input, targetKey, requiredString(input.args, 'destination_path'), { mustExist: false, operation: 'write' });
|
|
980
|
+
if (existsSync(destination.path)) throw new AssistantPluginError('LOCAL_SYSTEM_DESTINATION_EXISTS', 'Destination already exists; overwrite is not allowed.', { retryable: false });
|
|
981
|
+
mkdirSync(dirname(destination.path), { recursive: true });
|
|
982
|
+
renameSync(source.path, destination.path);
|
|
983
|
+
return { renamed: true, source: source.relativePath, destination: destination.relativePath, overwrite: false };
|
|
984
|
+
});
|
|
985
|
+
}
|
|
986
|
+
case 'reveal_in_finder':
|
|
987
|
+
case 'open_file': {
|
|
988
|
+
const resolved = resolveTargetPath(input, requiredString(input.args, 'target_key'), requiredString(input.args, 'path'), { mustExist: true });
|
|
989
|
+
if (input.actionId === 'open_file') {
|
|
990
|
+
const file = statSync(resolved.path);
|
|
991
|
+
const extension = extname(resolved.path).toLowerCase();
|
|
992
|
+
const executable = (file.mode & 0o111) !== 0;
|
|
993
|
+
if (!file.isFile() || executable || AUTO_OPEN_DENIED_EXTENSIONS.has(extension)) {
|
|
994
|
+
throw new AssistantPluginError(
|
|
995
|
+
'LOCAL_SYSTEM_EXECUTABLE_OPEN_DENIED',
|
|
996
|
+
'Executable/script-like files are never auto-opened. Use read_text or reveal_in_finder for inspection, or execute_command with explicit typed argv when execution is intended.',
|
|
997
|
+
{ retryable: false, details: { targetKey: resolved.target.targetKey, path: resolved.relativePath, extension, executable } },
|
|
998
|
+
);
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
const command = input.actionId === 'reveal_in_finder' ? ['open', '-R', resolved.path] : ['open', resolved.path];
|
|
1002
|
+
const opened = run(command[0], command.slice(1));
|
|
1003
|
+
if (!opened.ok) throw new AssistantPluginError('LOCAL_SYSTEM_OPEN_FAILED', opened.stderr || opened.stdout, { retryable: true, details: { command } });
|
|
1004
|
+
return { opened: true, targetKey: resolved.target.targetKey, path: resolved.relativePath, command };
|
|
1005
|
+
}
|
|
1006
|
+
default:
|
|
1007
|
+
throw new AssistantPluginError('PLUGIN_ACTION_NOT_SUPPORTED', `local_system/${input.actionId} is not supported.`, { retryable: false });
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
export const localSystemPluginAdapter = {
|
|
1012
|
+
pluginId: PLUGIN_ID,
|
|
1013
|
+
scope: 'controller' as const,
|
|
1014
|
+
buildManifest: buildLocalSystemPluginManifest,
|
|
1015
|
+
executeAction: executeLocalSystemPluginAction,
|
|
1016
|
+
};
|