@moretea-labs/forge 1.8.1 → 1.8.2
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 +5 -2
- package/CHANGELOG.md +20 -0
- package/README.en.md +24 -3
- package/README.md +10 -6
- package/README.zh-CN.md +5 -3
- package/THIRD_PARTY_NOTICES.md +1 -2
- package/adapters/chatgpt/browser-delivery-runtime.ts +5 -3
- package/adapters/chatgpt/provider-delivery.ts +1 -1
- package/adapters/chatgpt/work-conversation-binding-store.ts +20 -1
- package/adapters/computer/chatgpt-extension-target.ts +295 -50
- package/adapters/computer/chrome-extension/background.js +227 -71
- package/adapters/computer/chrome-extension/content.js +181 -157
- package/adapters/computer/chrome-extension/manifest.json +3 -1
- package/adapters/computer/desktop-operator-provider.ts +18 -6
- package/adapters/computer/interaction-target-authority.ts +39 -9
- package/adapters/mcp/access-mode.ts +18 -39
- package/adapters/mcp/auth.ts +2 -6
- package/adapters/mcp/instructions.ts +2 -2
- package/adapters/mcp/multi-repository.ts +7 -28
- package/adapters/mcp/runtime-gateway/context-adapter.ts +79 -40
- package/adapters/mcp/runtime-gateway/context-pack-sidecar.ts +7 -15
- package/adapters/mcp/runtime-gateway/execution-tools.ts +7 -13
- package/adapters/mcp/runtime-gateway/plugin-adapter.ts +16 -3
- package/adapters/mcp/runtime-gateway/process-tools.ts +3 -21
- package/adapters/mcp/runtime-gateway/router.ts +3 -5
- package/adapters/mcp/runtime-gateway/routing-policy.ts +17 -10
- package/adapters/mcp/runtime-gateway/runtime-observation-adapter.ts +1 -1
- package/adapters/mcp/runtime-gateway/runtime-tool-definitions.ts +14 -9
- package/adapters/mcp/runtime-gateway/runtime-tools.ts +1 -5
- package/adapters/mcp/runtime-gateway/scheduler-adapter.ts +36 -1
- package/adapters/mcp/runtime-gateway/status-inbox-adapter.ts +58 -4
- package/adapters/mcp/runtime-gateway/typescript-navigation-sidecar.ts +7 -3
- package/adapters/mcp/runtime-gateway/work-adapter.ts +16 -16
- package/adapters/mcp/runtime-gateway/work-input-wire-migration.ts +0 -17
- package/adapters/mcp/runtime-gateway/work-semantic-operations.ts +69 -12
- package/adapters/mcp/runtime-gateway/workflow-supervisor-adapter.ts +26 -2
- package/adapters/mcp/server.ts +24 -3
- package/adapters/mcp/setup.ts +4 -10
- package/adapters/mcp/tool-mapping/access-tools.ts +0 -6
- package/adapters/mcp/tool-mapping/legacy-tool-service.ts +672 -2935
- package/adapters/mcp/tool-mapping/post-finalize-work-attribution.ts +1 -1
- package/adapters/mcp/tool-mapping/repository-tools.ts +52 -86
- package/adapters/mcp/toolset-names.ts +11 -42
- package/adapters/mcp/toolset.ts +16 -90
- package/{src/cli → adapters}/mcp/transports/AGENTS.md +5 -5
- package/{src/cli → adapters}/mcp/transports/CLAUDE.md +5 -5
- package/adapters/mcp/transports/http-observation.ts +18 -6
- package/adapters/mcp/transports/http-post-lifecycle.ts +19 -0
- package/adapters/mcp/transports/http.ts +54 -14
- package/adapters/mcp/types.ts +0 -6
- package/assets/hooks/lib/session-state.sh +0 -38
- package/assets/hooks/post-bash.sh +1 -1
- package/assets/hooks/post-tool-observer.sh +0 -5
- package/assets/hooks/prompt-guard.sh +0 -55
- package/assets/partials/04-project-structure.partial.md +1 -1
- package/assets/partials/07-footer.partial.md +1 -1
- package/assets/partials-agents/02-operating-mode.partial.md +1 -1
- package/assets/partials-agents/08-deep-docs.partial.md +1 -1
- package/assets/reference-configs/agentic-development-flow.md +1 -1
- package/assets/reference-configs/external-tooling.md +0 -76
- package/assets/reference-configs/release-deploy.md +1 -1
- package/assets/templates/helpers/architecture-queue.sh +1 -1
- package/assets/templates/helpers/check-agent-tooling.sh +1 -327
- package/assets/templates/helpers/check-task-workflow.sh +1 -1
- package/assets/templates/helpers/ensure-task-workflow.sh +0 -14
- package/assets/templates/helpers/migrate-project-template.sh +2 -2
- package/assets/workflow-contract.v1.json +0 -2
- package/docs/README.md +5 -2
- package/docs/architecture/CURRENT.md +114 -30
- package/docs/architecture/decisions/20260904-ios-device-migration-baseline.md +42 -0
- package/docs/architecture/decisions/20260918-stable-candidate-release-lifecycle.md +15 -2
- package/docs/architecture/decisions/20260924-thin-semantic-working-context.md +4 -2
- package/docs/architecture/decisions/20261001-supervisor-turn-authority.md +109 -4
- package/docs/architecture/decisions/20261009-exact-code-context-discovery.md +19 -0
- package/docs/architecture/decisions/20261009-generated-xcode-cache-retention.md +37 -0
- package/docs/architecture/decisions/20261011-autonomous-continuation-convergence.md +597 -0
- package/docs/architecture/decisions/20261017-edit-session-finalization-reconciliation.md +17 -0
- package/docs/architecture/decisions/20261019-retire-host-skills.md +16 -0
- package/docs/architecture/diagrams/agentic-dev-plugin-architecture.html +1 -1
- package/docs/architecture/domains/public-surface.md +3 -5
- package/docs/architecture/domains/verification.md +5 -5
- package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +2 -2
- package/docs/architecture/requests/2026-09-04-computer-plugin-workflow-taxonomy.md +4 -4
- package/docs/architecture/reviews/20261015-code-search-runtime-acceptance.md +75 -0
- package/docs/forge-plugin-management.md +14 -2
- package/docs/operations/controller-browser-plugin.md +23 -0
- package/docs/operations/features.md +6 -7
- package/docs/operations/features.zh-CN.md +1 -1
- package/docs/operations/github-repository.md +4 -4
- package/docs/operations/github-semantic-release-projection.md +20 -0
- package/docs/operations/homebrew.md +2 -2
- package/docs/operations/mcp-tool-exposure.md +13 -54
- package/docs/operations/runtime-performance-diagnostics.md +8 -0
- package/docs/operations/standalone-disaster-recovery.md +1 -1
- package/docs/tutorials/01-install-and-start.zh-CN.md +1 -1
- package/docs/tutorials/03-first-repository-task.md +2 -0
- package/docs/wiki/Computer-and-Plugins.md +78 -0
- package/docs/wiki/Computer-and-Plugins.zh-CN.md +88 -0
- package/docs/wiki/Home.md +16 -2
- package/docs/wiki/Implementation.md +24 -12
- package/docs/wiki/Installation.md +28 -32
- package/docs/wiki/Integrations.md +4 -0
- package/docs/wiki/Quick-Start.md +35 -22
- package/docs/wiki/Releases-and-Upgrades.md +15 -25
- package/docs/wiki/_Sidebar.md +5 -1
- package/package.json +1 -12
- package/packages/kernel/controller/infrastructure/controller-session-store.ts +63 -38
- package/packages/kernel/identity/infrastructure/activity-projection-store.ts +10 -0
- package/packages/kernel/scheduler/application/eligibility.ts +4 -1
- package/packages/kernel/scheduler/infrastructure/schedule-store.ts +13 -10
- package/packages/kernel/work/application/work-service.ts +1 -0
- package/packages/kernel/work/domain/repository-completion-receipt.ts +1 -1
- package/packages/kernel/work/domain/state-machine.ts +9 -2
- package/packages/kernel/work/infrastructure/work-contract-store.ts +28 -1
- package/packages/plugin-runtime/browser/provider-registry.ts +12 -36
- package/packages/plugin-runtime/computer/conversation-target.ts +17 -7
- package/scripts/assemble-template.ts +11 -72
- package/scripts/check-mcp-compatibility.ts +55 -74
- package/scripts/check-package-identity.mjs +0 -5
- package/scripts/check-runtime-architecture.mjs +305 -28
- package/scripts/init-project.sh +0 -46
- package/scripts/install-ios-hid-toolchain.sh +60 -0
- package/scripts/install-standalone-recovery.ts +1 -1
- package/scripts/lib/project-init-lib.sh +3 -3
- package/scripts/migrate-windows-wsl-controller-home.ts +2 -0
- package/scripts/open-source-audit-allowlist.txt +0 -1
- package/scripts/run-governed-gate.ts +2 -0
- package/scripts/smoke-ios-physical-device.ts +38 -10
- package/scripts/smoke-mcp-http-runtime.ts +7 -8
- package/scripts/smoke-mcp-tool-surface.ts +27 -41
- package/scripts/smoke-workflow-supervisor-chrome-adapter.ts +6 -3
- package/scripts/sync-generated-authority.mjs +0 -1
- package/src/cli/agent-jobs/executor-health.ts +1 -1
- package/src/cli/agent-jobs/integration.ts +2 -2
- package/src/cli/chatgpt-browser/file-policy.ts +2 -2
- package/src/cli/commands/doctor.ts +1 -143
- package/src/cli/commands/global-runtime.ts +3 -196
- package/src/cli/commands/init-hook.ts +6 -23
- package/src/cli/commands/init.ts +3 -252
- package/src/cli/commands/mcp.ts +0 -5
- package/src/cli/commands/prompt-guard-decision.ts +0 -2
- package/src/cli/commands/recovery.ts +2 -14
- package/src/cli/commands/repository.ts +17 -1
- package/src/cli/commands/setup-execution.ts +41 -0
- package/src/cli/controller/completion-orchestrator.ts +1 -1
- package/src/cli/controller/composite-operations.ts +1 -1
- package/src/cli/controller/context/instruction-context.ts +1 -1
- package/src/cli/controller/context/known-paths.ts +2 -2
- package/src/cli/controller/context/query-planning.ts +11 -4
- package/src/cli/controller/context/rule-resolution.ts +2 -2
- package/src/cli/controller/context/source-materializer.ts +2 -2
- package/src/cli/controller/context/structural-provider.ts +84 -0
- package/src/cli/controller/context/structural.ts +6 -6
- package/src/cli/controller/context/types.ts +8 -9
- package/src/cli/controller/context-pack.ts +97 -76
- package/src/cli/controller/operational-plan.ts +1 -1
- package/src/cli/controller/systemd-user.ts +4 -10
- package/src/cli/editing/edit-session.ts +17 -5
- package/src/cli/github/github.ts +4 -4
- package/src/cli/github/semantic-projection.ts +174 -0
- package/src/cli/hook/prompt-intents.ts +0 -9
- package/src/cli/index.ts +1 -43
- package/src/cli/local-bridge/connector-freshness.ts +1 -12
- package/src/cli/local-bridge/console-automations.ts +1 -1
- package/src/cli/local-bridge/console-view-models.ts +1 -1
- package/src/cli/local-bridge/facade-api.ts +0 -2
- package/src/cli/repositories/command-classifier.ts +0 -9
- package/src/cli/repositories/command-scope.ts +2 -2
- package/src/cli/repositories/locks.ts +20 -0
- package/src/cli/repositories/registry.ts +2 -1
- package/src/cli/repositories/safe-patch.ts +2 -12
- package/src/cli/repositories/structured-git.ts +39 -3
- package/src/cli/repository/inspector.ts +185 -343
- package/src/effects/process-runner.ts +17 -1
- package/src/runtime/context/generic-lsp-provider.ts +1 -1
- package/src/runtime/context/semantic-navigation-contract.ts +7 -0
- package/src/runtime/context/semantic-navigation.ts +1 -1
- package/src/runtime/control-plane/cleanup-artifact-retention.ts +4 -4
- package/src/runtime/control-plane/execution/work-delivery-service.ts +150 -31
- package/src/runtime/control-plane/execution/work-handle-authority.ts +12 -4
- package/src/runtime/control-plane/execution/work-handle-store.ts +53 -1
- package/src/runtime/control-plane/execution/work-operation-service.ts +20 -10
- package/src/runtime/control-plane/execution/work-preparation-service.ts +108 -4
- package/src/runtime/control-plane/execution/work-process-evidence.ts +2 -2
- package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +28 -13
- package/src/runtime/control-plane/execution/work-validation-reconciler.ts +5 -1
- package/src/runtime/control-plane/execution/work-verification-snapshot.ts +1 -1
- package/src/runtime/control-plane/facade/capability-registry.ts +2 -0
- package/src/runtime/control-plane/facade/work-continuation.ts +1 -1
- package/src/runtime/control-plane/generated-cache-retention.ts +114 -63
- package/src/runtime/control-plane/global-scheduler/maintenance.ts +20 -8
- package/src/runtime/control-plane/global-scheduler/reconciliation.ts +1 -11
- package/src/runtime/control-plane/governance/external-effects.ts +1 -1
- package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +1 -0
- package/src/runtime/control-plane/runtime-cleanup.ts +12 -93
- package/src/runtime/evidence/sensitive-output.ts +22 -2
- package/src/runtime/execution/jobs/child-reference.ts +11 -0
- package/src/runtime/execution/jobs/receipt-recovery.ts +1 -11
- package/src/runtime/execution/process-runtime/command-facade.ts +3 -3
- package/src/runtime/execution/process-runtime/runtime.ts +20 -16
- package/src/runtime/execution/thin-harness/execution-router.ts +16 -16
- package/src/runtime/execution/thin-harness/fast-executor.ts +2 -2
- package/src/runtime/execution/thin-harness/search-worker.ts +2 -2
- package/src/runtime/execution/workers/executor.ts +1 -2
- package/src/runtime/maintenance/cleanup.ts +41 -5
- package/src/runtime/plugins/action-application.ts +12 -1
- package/src/runtime/plugins/app-store-connect-adapter.ts +252 -4
- package/src/runtime/plugins/browser-adapter.ts +381 -313
- package/src/runtime/plugins/browser-macos-bridge.ts +114 -112
- package/src/runtime/plugins/browser-manifest-surface.ts +2 -1
- package/src/runtime/plugins/browser-registration.ts +2 -0
- package/src/runtime/plugins/browser-runtime.ts +26 -35
- package/src/runtime/plugins/computer-chatgpt-browser-adapter-installer.ts +11 -2
- package/src/runtime/plugins/computer-registration.ts +76 -9
- package/src/runtime/plugins/github-adapter.ts +30 -3
- package/src/runtime/plugins/gmail-adapter.ts +166 -1
- package/src/runtime/plugins/google-shared.ts +10 -5
- package/src/runtime/plugins/ios/remote-xpc-hid.ts +70 -30
- package/src/runtime/plugins/ios-adapter.ts +45 -40
- package/src/runtime/plugins/ios-agent-device.ts +54 -4
- package/src/runtime/plugins/ios-device-ownership.ts +103 -0
- package/src/runtime/plugins/ios-physical-device.ts +497 -79
- package/src/runtime/plugins/local-system-adapter.ts +1 -99
- package/src/runtime/plugins/store.ts +80 -7
- package/src/runtime/plugins/types.ts +4 -0
- package/src/runtime/projections/git-status-sampler.ts +2 -19
- package/src/runtime/recovery/actions.ts +1 -1
- package/src/runtime/recovery/lifecycle-inventory.ts +1 -11
- package/src/runtime/recovery/probe.ts +2 -2
- package/src/runtime/release/release-coordinator.ts +20 -4
- package/src/runtime/root/controller-progression-composition.ts +67 -5
- package/src/runtime/root/package-connector-service.ts +123 -11
- package/src/runtime/root/package-runtime-release.ts +6 -18
- package/src/runtime/root/package-runtime-service.ts +7 -13
- package/src/runtime/root/release-execution-canary.ts +18 -5
- package/src/runtime/root/release-manifest.ts +0 -34
- package/src/runtime/root/release-materialize.ts +0 -78
- package/src/runtime/root/runtime.ts +1 -2
- package/src/runtime/root/scheduled-controller-composition.ts +2 -1
- package/src/runtime/root/service.ts +90 -11
- package/src/runtime/root/types.ts +0 -6
- package/src/runtime/root/workflow-supervisor-composition.ts +207 -19
- package/src/runtime/root/workflow-supervisor-runtime.ts +365 -10
- package/src/runtime/safe-tooling/google-credential-store.ts +1 -11
- package/src/runtime/safe-tooling/ios-development.ts +22 -8
- package/src/runtime/safe-tooling/job-summary.ts +1 -1
- package/src/runtime/safe-tooling/workspace-auth.ts +4 -1
- package/src/runtime/shared/atomic-text-file.ts +21 -0
- package/src/runtime/shared/json-files.ts +8 -3
- package/src/runtime/standalone-recovery/core.ts +189 -174
- package/src/runtime/standalone-recovery/entry.ts +62 -30
- package/src/runtime/standalone-recovery/operation-lock.ts +11 -1
- package/src/runtime/workflow/schedules/chatgpt-automation-migration.ts +1 -0
- package/src/runtime/workflow/schedules/engine.ts +36 -14
- package/src/runtime/workflow/schedules/pending-activation.ts +11 -4
- package/src/runtime/workflow/schedules/work-continuation.ts +51 -6
- package/supervisor/client.ts +31 -14
- package/supervisor/control-plane.ts +210 -115
- package/supervisor/forge-validators.ts +88 -21
- package/supervisor/native-browser-adapter.ts +393 -28
- package/supervisor/native-messaging/host.ts +21 -2
- package/supervisor/protocol.ts +74 -26
- package/supervisor/server.ts +16 -8
- package/supervisor/store.ts +476 -57
- package/supervisor/types.ts +19 -13
- package/SKILL.md +0 -427
- package/adapters/computer/chatgpt-target-router.ts +0 -103
- package/adapters/mcp/runtime-gateway/automation-receipt-adapter.ts +0 -87
- package/adapters/mcp/runtime-gateway/work-validation-reconciler.ts +0 -1
- package/adapters/mcp/toolset-selection.ts +0 -41
- package/agents/openai.yaml +0 -4
- package/assets/skill-hooks.json +0 -41
- package/assets/skill-version.json +0 -189
- package/assets/skills/claude-review/SKILL.md +0 -231
- package/assets/skills/codex-review/SKILL.md +0 -103
- package/assets/templates/helpers/check-skill-version.ts +0 -193
- package/scripts/check-skill-version.ts +0 -5
- package/scripts/ensure-codegraph.sh +0 -16
- package/scripts/run-skill-hook.ts +0 -244
- package/scripts/sync-codex-installed-copies.sh +0 -182
- package/src/cli/commands/tools.ts +0 -167
- package/src/cli/mcp/access-mode.ts +0 -2
- package/src/cli/mcp/access-tools.ts +0 -2
- package/src/cli/mcp/audit.ts +0 -2
- package/src/cli/mcp/auth.ts +0 -2
- package/src/cli/mcp/instructions.ts +0 -2
- package/src/cli/mcp/legacy-context.ts +0 -2
- package/src/cli/mcp/legacy-tool-service.ts +0 -2
- package/src/cli/mcp/multi-repository.ts +0 -2
- package/src/cli/mcp/oauth.ts +0 -2
- package/src/cli/mcp/openai-secure-tunnel.ts +0 -2
- package/src/cli/mcp/paths.ts +0 -2
- package/src/cli/mcp/policy.ts +0 -2
- package/src/cli/mcp/post-finalize-work-attribution.ts +0 -2
- package/src/cli/mcp/proxy-env.ts +0 -2
- package/src/cli/mcp/readiness-projection.ts +0 -2
- package/src/cli/mcp/redaction.ts +0 -2
- package/src/cli/mcp/repo.ts +0 -2
- package/src/cli/mcp/repository-tools.ts +0 -2
- package/src/cli/mcp/server.ts +0 -2
- package/src/cli/mcp/setup.ts +0 -2
- package/src/cli/mcp/tool-capability-cache.ts +0 -2
- package/src/cli/mcp/tool-contract.ts +0 -2
- package/src/cli/mcp/tools.ts +0 -2
- package/src/cli/mcp/toolset-names.ts +0 -2
- package/src/cli/mcp/toolset-selection.ts +0 -2
- package/src/cli/mcp/toolset.ts +0 -2
- package/src/cli/mcp/transports/http.ts +0 -2
- package/src/cli/mcp/transports/session-registry.ts +0 -2
- package/src/cli/mcp/transports/stdio.ts +0 -2
- package/src/cli/mcp/types.ts +0 -2
- package/src/cli/mcp/umbrella-tools.ts +0 -2
- package/src/cli/tools/codegraph.ts +0 -695
- package/src/core/adoption/helper-wrapper-plan.ts +0 -159
- package/src/runtime/context/codegraph-cache-boundary.ts +0 -255
- package/src/runtime/context/codegraph-read-provider.ts +0 -611
- package/src/runtime/context/codegraph-sidecar.cjs +0 -248
- package/src/runtime/control-plane/codegraph-cache-retention.ts +0 -164
- package/src/runtime/gateway/mcp/execution-tools.ts +0 -2
- package/src/runtime/gateway/mcp/legacy-ios-tool-adapter.ts +0 -2
- package/src/runtime/gateway/mcp/persisted-check-process.ts +0 -2
- package/src/runtime/gateway/mcp/process-tools.ts +0 -2
- package/src/runtime/gateway/mcp/router.ts +0 -2
- package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +0 -2
- package/src/runtime/gateway/mcp/runtime-tools.ts +0 -2
- package/src/runtime/gateway/mcp/work-validation-reconciler.ts +0 -2
- package/src/runtime/plugins/browser-provider-registry.ts +0 -2
- package/src/runtime/plugins/computer-chatgpt-macos-target.ts +0 -891
|
@@ -59,8 +59,8 @@ const SENSITIVE_PATTERNS: readonly SensitivePattern[] = [
|
|
|
59
59
|
},
|
|
60
60
|
{
|
|
61
61
|
type: 'secret_assignment',
|
|
62
|
-
pattern: /((?:^|[^\w]))(["']?)([A-Za-z0-9_.-]*(?:api[_-]?key|access[_-]?key|access[_-]?token|refresh[_-]?token|client[_-]?secret|private[_-]?key|password|passwd|secret|credentials?|auth[_-]?token|token|cookie)[A-Za-z0-9_.-]*)\2(\s*(?:=>|:=|=|:)\s*)(?:"[^"\r\n]*"|'[^'\r\n]*'|[^\s,}\r\n]+)/gim,
|
|
63
|
-
replacement: '$1$2$3$2$4[REDACTED]',
|
|
62
|
+
pattern: /((?:^|[^\w]))(["']?)([A-Za-z0-9_.-]*(?:api[_-]?key|access[_-]?key|access[_-]?token|refresh[_-]?token|client[_-]?secret|private[_-]?key|password|passwd|secret|credentials?|auth[_-]?token|token|cookie)[A-Za-z0-9_.-]*)\2(\s*(?:=>|:=|=|:)\s*)((?:[A-Z][A-Za-z0-9_]*(?:[?!])?\s*=\s*)?)(?:"[^"\r\n]*"|'[^'\r\n]*'|[^\s,}\r\n]+)/gim,
|
|
63
|
+
replacement: '$1$2$3$2$4$5[REDACTED]',
|
|
64
64
|
},
|
|
65
65
|
{
|
|
66
66
|
type: 'database_url_assignment',
|
|
@@ -136,6 +136,26 @@ export function redactSensitiveText(input: string): SensitiveTextRedactionResult
|
|
|
136
136
|
let text = input;
|
|
137
137
|
const counts = new Map<string, number>();
|
|
138
138
|
for (const entry of SENSITIVE_PATTERNS) {
|
|
139
|
+
if (entry.type === 'secret_assignment') {
|
|
140
|
+
let redactions = 0;
|
|
141
|
+
text = text.replace(entry.pattern, (matched, boundary: string, quote: string, key: string, separator: string, typeDefault: string) => {
|
|
142
|
+
// Preserve non-credential data flow even when a source symbol ends in "Token".
|
|
143
|
+
// An unquoted floating-point counter or a reference to the local token
|
|
144
|
+
// variable is not a secret literal. Keep unknown opaque/quoted token values
|
|
145
|
+
// redacted, including compound token keys, rather than whitelisting names.
|
|
146
|
+
const prefix = `${boundary}${quote}${key}${quote}${separator}${typeDefault}`;
|
|
147
|
+
const rhs = matched.slice(prefix.length);
|
|
148
|
+
const genericToken = /token$/i.test(key) && !/^(?:token|(?:api|access|refresh|auth|session|bearer|csrf|id|jwt|oauth)[_-]?token)$/i.test(key);
|
|
149
|
+
const sourceCounter = /^[+-]?(?:\d+\.\d*|\.\d+)(?:e[+-]?\d+)?[;)]?$/i.test(rhs);
|
|
150
|
+
const sourceReference = /^token[;)]?$/.test(rhs);
|
|
151
|
+
const sourceType = separator.trim() === ':' && !typeDefault && /^(?:Double|Float|Int|Int8|Int16|Int32|Int64|Bool|String|Character|CGFloat|Date|UUID)[?!]?[),{;]?$/.test(rhs);
|
|
152
|
+
if (genericToken && (sourceCounter || sourceReference || sourceType)) return matched;
|
|
153
|
+
redactions += 1;
|
|
154
|
+
return `${prefix}[REDACTED]`;
|
|
155
|
+
});
|
|
156
|
+
if (redactions) counts.set(entry.type, (counts.get(entry.type) ?? 0) + redactions);
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
139
159
|
const matches = text.match(entry.pattern);
|
|
140
160
|
if (!matches?.length) continue;
|
|
141
161
|
counts.set(entry.type, (counts.get(entry.type) ?? 0) + matches.length);
|
|
@@ -38,6 +38,17 @@ export function isAgentDelegationLocalAction(action: string | undefined): boolea
|
|
|
38
38
|
return AGENT_DELEGATION_LOCAL_ACTIONS.has(action);
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
/** Shared delegation classification for Scheduler replay and durable receipt recovery. */
|
|
42
|
+
export function isAgentDelegationJob(job: ExecutionJob): boolean {
|
|
43
|
+
return isAgentDelegationOperation(job.payload.operation)
|
|
44
|
+
|| job.type === 'agent-run'
|
|
45
|
+
|| job.type === 'dispatch-task'
|
|
46
|
+
|| job.payload.arguments?.agentDelegation === true
|
|
47
|
+
|| (job.payload.operation === 'legacy-local-job'
|
|
48
|
+
&& typeof job.payload.arguments?.localAction === 'string'
|
|
49
|
+
&& isAgentDelegationLocalAction(job.payload.arguments.localAction));
|
|
50
|
+
}
|
|
51
|
+
|
|
41
52
|
function stringField(value: unknown): string | undefined {
|
|
42
53
|
if (typeof value !== 'string') return undefined;
|
|
43
54
|
const trimmed = value.trim();
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
childReferenceFromJob,
|
|
5
5
|
childReferenceFromReceipt,
|
|
6
6
|
hasDurableChildReference,
|
|
7
|
-
|
|
7
|
+
isAgentDelegationJob,
|
|
8
8
|
mergeChildReferences,
|
|
9
9
|
} from './child-reference';
|
|
10
10
|
import { operationReceiptMatchesJobOwnership, readOperationReceipt } from './receipt-store';
|
|
@@ -18,16 +18,6 @@ export type RecoveredReceiptOutcome = {
|
|
|
18
18
|
outcome: 'succeeded' | 'failed' | 'delegated';
|
|
19
19
|
};
|
|
20
20
|
|
|
21
|
-
function isAgentDelegationJob(job: ExecutionJob): boolean {
|
|
22
|
-
return isAgentDelegationOperation(job.payload.operation)
|
|
23
|
-
|| job.type === 'agent-run'
|
|
24
|
-
|| job.type === 'dispatch-task'
|
|
25
|
-
|| job.payload.arguments?.agentDelegation === true
|
|
26
|
-
|| (job.payload.operation === 'legacy-local-job'
|
|
27
|
-
&& typeof job.payload.arguments?.localAction === 'string'
|
|
28
|
-
&& ['launch-task', 'quick-agent-session'].includes(String(job.payload.arguments.localAction)));
|
|
29
|
-
}
|
|
30
|
-
|
|
31
21
|
/**
|
|
32
22
|
* Recover a running Job from a matching completed/delegated Worker receipt.
|
|
33
23
|
*
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* execute_command → three explicit execution lanes.
|
|
3
3
|
*
|
|
4
4
|
* - Ephemeral Direct: bounded ordinary reads and commands that finish inside
|
|
5
5
|
* the interactive budget.
|
|
@@ -345,7 +345,7 @@ export async function executeRepositoryCommandViaProcessRuntime(
|
|
|
345
345
|
durableSideEffects: emptyEffects,
|
|
346
346
|
suggestedOperation: decision.reason === 'standalone_recovery_lifecycle_required'
|
|
347
347
|
? 'Use the standalone Forge Recovery connector for canonical Runtime/connector lifecycle operations.'
|
|
348
|
-
: '
|
|
348
|
+
: 'execute_command via Durable Work / Local Job',
|
|
349
349
|
externalEffect: decision.route === 'durable'
|
|
350
350
|
? {
|
|
351
351
|
outcome: 'not_started',
|
|
@@ -550,7 +550,7 @@ export async function executeRepositoryCommandViaProcessRuntime(
|
|
|
550
550
|
resourceClaims: toProcessClaims(claims),
|
|
551
551
|
origin: {
|
|
552
552
|
surface: 'command',
|
|
553
|
-
toolName: '
|
|
553
|
+
toolName: 'execute_command',
|
|
554
554
|
requestId: input.requestId,
|
|
555
555
|
correlationId: input.workId,
|
|
556
556
|
},
|
|
@@ -1584,17 +1584,19 @@ export async function spawnManagedProcess(input: SpawnManagedProcessInput): Prom
|
|
|
1584
1584
|
}
|
|
1585
1585
|
|
|
1586
1586
|
const completed = await new Promise<ManagedProcessRecord | 'timeout'>((resolve) => {
|
|
1587
|
-
const
|
|
1588
|
-
timer.unref?.();
|
|
1589
|
-
monitor.waiters.push((done) => {
|
|
1587
|
+
const finish = (done: ManagedProcessRecord | 'timeout') => {
|
|
1590
1588
|
clearTimeout(timer);
|
|
1589
|
+
const index = monitor.waiters.indexOf(finish);
|
|
1590
|
+
if (index >= 0) monitor.waiters.splice(index, 1);
|
|
1591
1591
|
resolve(done);
|
|
1592
|
-
}
|
|
1592
|
+
};
|
|
1593
|
+
const timer = setTimeout(() => finish('timeout'), interactiveWaitMs);
|
|
1594
|
+
timer.unref?.();
|
|
1595
|
+
monitor.waiters.push(finish);
|
|
1593
1596
|
if (monitor.settled) {
|
|
1594
1597
|
const current = getProcessRecord(input.controllerHome, input.repoId, processId);
|
|
1595
1598
|
if (current?.terminalWritten) {
|
|
1596
|
-
|
|
1597
|
-
resolve(current);
|
|
1599
|
+
finish(current);
|
|
1598
1600
|
}
|
|
1599
1601
|
}
|
|
1600
1602
|
});
|
|
@@ -1693,27 +1695,29 @@ export async function waitForProcess(
|
|
|
1693
1695
|
if (monitor) {
|
|
1694
1696
|
const waitMs = options.timeoutMs ?? existing.timeoutMs;
|
|
1695
1697
|
const done = await new Promise<ManagedProcessRecord | 'timeout'>((resolve) => {
|
|
1696
|
-
const
|
|
1698
|
+
const finish = (record: ManagedProcessRecord | 'timeout') => {
|
|
1699
|
+
clearTimeout(timer);
|
|
1700
|
+
options.signal?.removeEventListener('abort', onAbort);
|
|
1701
|
+
const index = monitor.waiters.indexOf(finish);
|
|
1702
|
+
if (index >= 0) monitor.waiters.splice(index, 1);
|
|
1703
|
+
resolve(record);
|
|
1704
|
+
};
|
|
1705
|
+
const timer = setTimeout(() => finish('timeout'), Math.max(1, waitMs));
|
|
1697
1706
|
timer.unref?.();
|
|
1698
1707
|
const onAbort = () => {
|
|
1699
1708
|
clearTimeout(timer);
|
|
1700
1709
|
void cancelProcess(controllerHome, repoId, processId).then(() => {
|
|
1701
1710
|
const current = getProcessRecord(controllerHome, repoId, processId);
|
|
1702
|
-
if (current)
|
|
1703
|
-
else
|
|
1711
|
+
if (current) finish(current);
|
|
1712
|
+
else finish('timeout');
|
|
1704
1713
|
});
|
|
1705
1714
|
};
|
|
1706
1715
|
options.signal?.addEventListener('abort', onAbort, { once: true });
|
|
1707
|
-
monitor.waiters.push(
|
|
1708
|
-
clearTimeout(timer);
|
|
1709
|
-
options.signal?.removeEventListener('abort', onAbort);
|
|
1710
|
-
resolve(record);
|
|
1711
|
-
});
|
|
1716
|
+
monitor.waiters.push(finish);
|
|
1712
1717
|
if (monitor.settled) {
|
|
1713
1718
|
const current = getProcessRecord(controllerHome, repoId, processId);
|
|
1714
1719
|
if (current?.terminalWritten) {
|
|
1715
|
-
|
|
1716
|
-
resolve(current);
|
|
1720
|
+
finish(current);
|
|
1717
1721
|
}
|
|
1718
1722
|
}
|
|
1719
1723
|
});
|
|
@@ -317,7 +317,7 @@ export function routeExecution(input: RouteExecutionInput): ExecutionDecision {
|
|
|
317
317
|
}
|
|
318
318
|
|
|
319
319
|
if (commandLooksDestructive(input.command) || operation.includes('destructive')) {
|
|
320
|
-
if (input.approvalContinuation === true && operation === '
|
|
320
|
+
if (input.approvalContinuation === true && operation === 'execute_command') {
|
|
321
321
|
return decisionBase({
|
|
322
322
|
mode: 'durable',
|
|
323
323
|
reasons: ['destructive_approval_continuation'],
|
|
@@ -325,7 +325,7 @@ export function routeExecution(input: RouteExecutionInput): ExecutionDecision {
|
|
|
325
325
|
estimatedClass: 'unknown',
|
|
326
326
|
requiresIsolation: true,
|
|
327
327
|
requiresRecovery: false,
|
|
328
|
-
suggestedOperation: '
|
|
328
|
+
suggestedOperation: 'execute_command through Durable Work / Local Job',
|
|
329
329
|
effects: {
|
|
330
330
|
readsWorkspace: true,
|
|
331
331
|
mutatesWorkspace: true,
|
|
@@ -342,7 +342,7 @@ export function routeExecution(input: RouteExecutionInput): ExecutionDecision {
|
|
|
342
342
|
requiresIsolation: true,
|
|
343
343
|
requiresRecovery: false,
|
|
344
344
|
rejectCode: 'DESTRUCTIVE_REJECTED',
|
|
345
|
-
suggestedOperation: '
|
|
345
|
+
suggestedOperation: 'execute_command(dry_run=true) + explicit strong confirmation via Durable Work',
|
|
346
346
|
effects: {
|
|
347
347
|
readsWorkspace: true,
|
|
348
348
|
mutatesWorkspace: true,
|
|
@@ -381,7 +381,7 @@ export function routeExecution(input: RouteExecutionInput): ExecutionDecision {
|
|
|
381
381
|
|
|
382
382
|
let commandEffectsValue: ExecutionEffects | undefined;
|
|
383
383
|
|
|
384
|
-
if (operation === '
|
|
384
|
+
if (operation === 'execute_command' || operation === 'run_short_command' || operation === 'run_focused_check') {
|
|
385
385
|
if (input.command !== undefined) {
|
|
386
386
|
const classification = classifyRepositoryCommand(input.command, input.defaultBranch);
|
|
387
387
|
const canonical = normalizeRepositoryCommand(input.command);
|
|
@@ -401,7 +401,7 @@ export function routeExecution(input: RouteExecutionInput): ExecutionDecision {
|
|
|
401
401
|
requiresIsolation: true,
|
|
402
402
|
requiresRecovery: false,
|
|
403
403
|
rejectCode: 'UNSAFE_SHELL',
|
|
404
|
-
suggestedOperation: '
|
|
404
|
+
suggestedOperation: 'execute_command(dry_run=true)',
|
|
405
405
|
effects: commandEffectsValue,
|
|
406
406
|
}, operation);
|
|
407
407
|
}
|
|
@@ -411,7 +411,7 @@ export function routeExecution(input: RouteExecutionInput): ExecutionDecision {
|
|
|
411
411
|
}
|
|
412
412
|
if (classification.risk === 'remote_write') reasons.push('command_classified_remote_write');
|
|
413
413
|
if (classification.risk === 'destructive') {
|
|
414
|
-
if (input.approvalContinuation === true && operation === '
|
|
414
|
+
if (input.approvalContinuation === true && operation === 'execute_command') {
|
|
415
415
|
return decisionBase({
|
|
416
416
|
mode: 'durable',
|
|
417
417
|
reasons: ['destructive_approval_continuation', ...classification.reasons],
|
|
@@ -419,7 +419,7 @@ export function routeExecution(input: RouteExecutionInput): ExecutionDecision {
|
|
|
419
419
|
estimatedClass: 'unknown',
|
|
420
420
|
requiresIsolation: true,
|
|
421
421
|
requiresRecovery: false,
|
|
422
|
-
suggestedOperation: '
|
|
422
|
+
suggestedOperation: 'execute_command through Durable Work / Local Job',
|
|
423
423
|
effects: commandEffectsValue,
|
|
424
424
|
}, operation);
|
|
425
425
|
}
|
|
@@ -431,7 +431,7 @@ export function routeExecution(input: RouteExecutionInput): ExecutionDecision {
|
|
|
431
431
|
requiresIsolation: true,
|
|
432
432
|
requiresRecovery: false,
|
|
433
433
|
rejectCode: 'DESTRUCTIVE_COMMAND',
|
|
434
|
-
suggestedOperation: '
|
|
434
|
+
suggestedOperation: 'execute_command(dry_run=true)',
|
|
435
435
|
effects: commandEffectsValue,
|
|
436
436
|
}, operation);
|
|
437
437
|
}
|
|
@@ -440,14 +440,14 @@ export function routeExecution(input: RouteExecutionInput): ExecutionDecision {
|
|
|
440
440
|
reasons.push('not_a_strict_focused_check');
|
|
441
441
|
}
|
|
442
442
|
if (classification.risk === 'workspace_write' && !focused && !safeShellCombo) {
|
|
443
|
-
if (operation === '
|
|
443
|
+
if (operation === 'execute_command' || operation === 'run_short_command' || operation === 'run_focused_check') {
|
|
444
444
|
reasons.push('mutating_or_unfocused_command');
|
|
445
445
|
}
|
|
446
446
|
}
|
|
447
447
|
if (classification.risk !== 'readonly' && !focused && !safeShellCombo && !FAST_OPERATIONS.has(operation)) {
|
|
448
448
|
reasons.push('untrusted_or_unclassified_command');
|
|
449
449
|
}
|
|
450
|
-
} else if (operation === '
|
|
450
|
+
} else if (operation === 'execute_command' || operation === 'run_focused_check') {
|
|
451
451
|
reasons.push('missing_command');
|
|
452
452
|
}
|
|
453
453
|
}
|
|
@@ -564,7 +564,7 @@ export function routeExecution(input: RouteExecutionInput): ExecutionDecision {
|
|
|
564
564
|
}, operation);
|
|
565
565
|
}
|
|
566
566
|
|
|
567
|
-
if (!FAST_OPERATIONS.has(operation) && operation !== '
|
|
567
|
+
if (!FAST_OPERATIONS.has(operation) && operation !== 'execute_command') {
|
|
568
568
|
return decisionBase({
|
|
569
569
|
mode: 'durable',
|
|
570
570
|
reasons: ['operation_not_in_fast_allowlist'],
|
|
@@ -576,7 +576,7 @@ export function routeExecution(input: RouteExecutionInput): ExecutionDecision {
|
|
|
576
576
|
}, operation);
|
|
577
577
|
}
|
|
578
578
|
|
|
579
|
-
if (operation === '
|
|
579
|
+
if (operation === 'execute_command' || operation === 'run_short_command' || operation === 'run_focused_check') {
|
|
580
580
|
if (input.command === undefined) {
|
|
581
581
|
return decisionBase({
|
|
582
582
|
mode: 'durable',
|
|
@@ -585,7 +585,7 @@ export function routeExecution(input: RouteExecutionInput): ExecutionDecision {
|
|
|
585
585
|
estimatedClass: 'unknown',
|
|
586
586
|
requiresIsolation: false,
|
|
587
587
|
requiresRecovery: false,
|
|
588
|
-
suggestedOperation: '
|
|
588
|
+
suggestedOperation: 'execute_command via Durable Work',
|
|
589
589
|
}, operation);
|
|
590
590
|
}
|
|
591
591
|
const classification = classifyRepositoryCommand(input.command, input.defaultBranch);
|
|
@@ -602,7 +602,7 @@ export function routeExecution(input: RouteExecutionInput): ExecutionDecision {
|
|
|
602
602
|
estimatedClass: 'long',
|
|
603
603
|
requiresIsolation: false,
|
|
604
604
|
requiresRecovery: false,
|
|
605
|
-
suggestedOperation: '
|
|
605
|
+
suggestedOperation: 'execute_command via Durable Work / Local Job',
|
|
606
606
|
effects,
|
|
607
607
|
}, operation);
|
|
608
608
|
}
|
|
@@ -632,7 +632,7 @@ export function routeExecution(input: RouteExecutionInput): ExecutionDecision {
|
|
|
632
632
|
estimatedClass: 'long',
|
|
633
633
|
requiresIsolation: false,
|
|
634
634
|
requiresRecovery: false,
|
|
635
|
-
suggestedOperation: '
|
|
635
|
+
suggestedOperation: 'execute_command via Durable Work / Local Job',
|
|
636
636
|
effects,
|
|
637
637
|
}, operation);
|
|
638
638
|
}
|
|
@@ -673,7 +673,7 @@ export function routeExecution(input: RouteExecutionInput): ExecutionDecision {
|
|
|
673
673
|
function durableSuggestion(operation: string): string {
|
|
674
674
|
if (operation === 'run_check' || operation === 'run_focused_check') return 'run_check through Durable Work';
|
|
675
675
|
if (operation.includes('patch')) return 'repository_safe_patch_apply with apply_mode=async or Durable Work';
|
|
676
|
-
if (operation.includes('command')) return '
|
|
676
|
+
if (operation.includes('command')) return 'execute_command through Durable Work / Local Job';
|
|
677
677
|
if (operation.includes('agent') || operation === 'quick_agent_session') return 'quick_agent_session / dispatch_task';
|
|
678
678
|
return 'Durable Work (ExecutionJob path)';
|
|
679
679
|
}
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
previewRepositoryCommandExecutionAsync,
|
|
7
7
|
type RepositoryCommandSnapshot,
|
|
8
8
|
} from '../../../cli/repositories/command-executor';
|
|
9
|
-
import { getMcpPolicy } from '
|
|
9
|
+
import { getMcpPolicy } from '../../../../adapters/mcp/policy';
|
|
10
10
|
import { readRepositoryRange } from '../../../cli/repository/inspector';
|
|
11
11
|
import type { RepositoryRecord } from '../../../cli/repositories/types';
|
|
12
12
|
import { getLeaseSideEffectMetrics } from '../../resources/leases/store';
|
|
@@ -878,7 +878,7 @@ export async function executeFast(
|
|
|
878
878
|
} else if (
|
|
879
879
|
operation === 'run_short_command'
|
|
880
880
|
|| operation === 'run_focused_check'
|
|
881
|
-
|| operation === '
|
|
881
|
+
|| operation === 'execute_command'
|
|
882
882
|
) {
|
|
883
883
|
const command = request.input.command as string | string[];
|
|
884
884
|
const preview = await trace.measure('policyMs', async () => previewRepositoryCommandExecutionAsync(ctx.repository, {
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { isMainThread, parentPort, workerData, Worker } from 'worker_threads';
|
|
6
6
|
import { fileURLToPath } from 'url';
|
|
7
|
-
import { getMcpPolicy } from '
|
|
8
|
-
import type { McpPolicy } from '
|
|
7
|
+
import { getMcpPolicy } from '../../../../adapters/mcp/policy';
|
|
8
|
+
import type { McpPolicy } from '../../../../adapters/mcp/types';
|
|
9
9
|
import {
|
|
10
10
|
searchRepository,
|
|
11
11
|
searchRepositoryMany,
|
|
@@ -518,8 +518,7 @@ export async function executeExecutionJob(controllerHome: string, job: Execution
|
|
|
518
518
|
...context,
|
|
519
519
|
controllerHome,
|
|
520
520
|
explicitRepository: repository,
|
|
521
|
-
toolset:
|
|
522
|
-
toolsetLocked: true,
|
|
521
|
+
toolset: 'full',
|
|
523
522
|
};
|
|
524
523
|
// Durable Work mutations execute through control-plane application services.
|
|
525
524
|
// The Worker must never import MCP transport to obtain Work semantics.
|
|
@@ -88,12 +88,42 @@ function isSafeForgeTemp(path: string): boolean {
|
|
|
88
88
|
const name = basename(path);
|
|
89
89
|
if (!name.startsWith('forge')) return false;
|
|
90
90
|
const parent = dirname(path);
|
|
91
|
-
const allowedParents = new Set([
|
|
92
|
-
return allowedParents.has(
|
|
91
|
+
const allowedParents = new Set([tmpdir(), '/private/tmp', '/tmp'].filter((root) => existsSync(root)).map((root) => realpathSync(root)));
|
|
92
|
+
try { return allowedParents.has(realpathSync(parent)); } catch { return false; }
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Temp cleanup cannot dispose of a Git worktree just because its directory
|
|
96
|
+
// begins with "forge". Bounded traversal fails closed on nested repositories,
|
|
97
|
+
// symlinks and uninspectable or high-cardinality source.
|
|
98
|
+
function protectsGitSource(root: string): boolean {
|
|
99
|
+
const pending: Array<{ path: string; depth: number }> = [{ path: root, depth: 0 }];
|
|
100
|
+
let visited = 0;
|
|
101
|
+
while (pending.length > 0) {
|
|
102
|
+
const current = pending.pop()!;
|
|
103
|
+
if (++visited > 256) return true;
|
|
104
|
+
try {
|
|
105
|
+
const stat = lstatSync(current.path);
|
|
106
|
+
if (stat.isSymbolicLink()) return true;
|
|
107
|
+
if (!stat.isDirectory()) continue;
|
|
108
|
+
if (existsSync(join(current.path, '.git'))
|
|
109
|
+
|| (existsSync(join(current.path, 'HEAD')) && existsSync(join(current.path, 'objects')) && existsSync(join(current.path, 'refs')))) return true;
|
|
110
|
+
const entries = readdirSync(current.path, { withFileTypes: true });
|
|
111
|
+
if (entries.length + visited > 256) return true;
|
|
112
|
+
for (const entry of entries) {
|
|
113
|
+
if (entry.isSymbolicLink()) return true;
|
|
114
|
+
if (!entry.isDirectory()) continue;
|
|
115
|
+
if (current.depth >= 5) return true;
|
|
116
|
+
pending.push({ path: join(current.path, entry.name), depth: current.depth + 1 });
|
|
117
|
+
}
|
|
118
|
+
} catch {
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return false;
|
|
93
123
|
}
|
|
94
124
|
|
|
95
125
|
function scanTempDirs(minAgeMinutes: number, limit: number): RuntimeCleanupCandidate[] {
|
|
96
|
-
const roots = Array.from(new Set([tmpdir(), '/private/tmp', '/tmp'].filter((root) => existsSync(root)).map((root) =>
|
|
126
|
+
const roots = Array.from(new Set([tmpdir(), '/private/tmp', '/tmp'].filter((root) => existsSync(root)).map((root) => realpathSync(root))));
|
|
97
127
|
const processes = processCommands();
|
|
98
128
|
const at = Date.now();
|
|
99
129
|
const candidates: RuntimeCleanupCandidate[] = [];
|
|
@@ -108,11 +138,14 @@ function scanTempDirs(minAgeMinutes: number, limit: number): RuntimeCleanupCandi
|
|
|
108
138
|
if (!stat.isDirectory()) continue;
|
|
109
139
|
const occupied = processes.find((process) => process.command.includes(path));
|
|
110
140
|
const ageMinutes = Math.max(0, Math.round((at - stat.mtimeMs) / 60_000));
|
|
111
|
-
const
|
|
141
|
+
const gitSourceProtected = protectsGitSource(path);
|
|
142
|
+
const safe = !occupied && ageMinutes >= minAgeMinutes && !gitSourceProtected;
|
|
112
143
|
candidates.push({
|
|
113
144
|
kind: 'temp_dir',
|
|
114
145
|
path,
|
|
115
|
-
reason:
|
|
146
|
+
reason: gitSourceProtected
|
|
147
|
+
? 'Git worktree, repository, symlink or uninspectable source is protected; use its Work/Git owner.'
|
|
148
|
+
: safe
|
|
116
149
|
? `Forge temp directory is older than ${minAgeMinutes} minutes and not referenced by a running process.`
|
|
117
150
|
: 'Temp directory is too new or still referenced by a running process.',
|
|
118
151
|
ageMinutes,
|
|
@@ -368,6 +401,9 @@ export function applyRuntimeCleanup(repoRoot: string, options: RuntimeCleanupOpt
|
|
|
368
401
|
const applied: Array<RuntimeCleanupCandidate & { applied: boolean; error?: string }> = eligible.slice(0, normalized.maxRemovals).map((candidate) => {
|
|
369
402
|
try {
|
|
370
403
|
if (candidate.kind === 'temp_dir' && candidate.path && isSafeForgeTemp(candidate.path)) {
|
|
404
|
+
// A preview is not a mutation lease. Recheck source and liveness.
|
|
405
|
+
if (protectsGitSource(candidate.path)) throw new Error('RUNTIME_CLEANUP_GIT_SOURCE_PROTECTED');
|
|
406
|
+
if (processCommands().some((process) => process.command.includes(candidate.path!))) throw new Error('RUNTIME_CLEANUP_TEMP_PROCESS_OCCUPIED');
|
|
371
407
|
rmSync(candidate.path, { recursive: true, force: true });
|
|
372
408
|
return { ...candidate, applied: true };
|
|
373
409
|
}
|
|
@@ -16,6 +16,8 @@ import {
|
|
|
16
16
|
getControllerPluginManifest,
|
|
17
17
|
isDirectNonPersistentPluginAction,
|
|
18
18
|
isDirectPluginReadAction,
|
|
19
|
+
preflightPluginActionWorkBinding,
|
|
20
|
+
readPluginActionReceiptForRequest,
|
|
19
21
|
submitAssistantPluginAction,
|
|
20
22
|
submitControllerPluginAction,
|
|
21
23
|
} from './store';
|
|
@@ -112,6 +114,12 @@ export async function executeAssistantPluginActionApplication(input: {
|
|
|
112
114
|
};
|
|
113
115
|
}
|
|
114
116
|
|
|
117
|
+
// Reject an invalid Work binding before allocating an immutable Process request ID.
|
|
118
|
+
// Reattach any existing authoritative provider receipt without fresh preflight.
|
|
119
|
+
if (action && input.request.workId && !readPluginActionReceiptForRequest(input.controllerHome, input.request.requestId)) {
|
|
120
|
+
preflightPluginActionWorkBinding(input.controllerHome, input.scope, action, input.request);
|
|
121
|
+
}
|
|
122
|
+
|
|
115
123
|
if (action?.executionMode === 'runtime') {
|
|
116
124
|
// Stateful providers may own in-memory resources whose identity is valid only
|
|
117
125
|
// inside the canonical Runtime process. Keep the normal durable request/effect
|
|
@@ -169,7 +177,10 @@ export async function executeAssistantPluginActionApplication(input: {
|
|
|
169
177
|
process: handle,
|
|
170
178
|
};
|
|
171
179
|
}
|
|
172
|
-
|
|
180
|
+
// Provider settlement remains authoritative across a sidecar failure.
|
|
181
|
+
// Re-enter the store below to validate exact args/scope/Work before reading
|
|
182
|
+
// any recorded outcome; process failure alone never authorizes redispatch.
|
|
183
|
+
if (!handle.ok && !readPluginActionReceiptForRequest(input.controllerHome, input.request.requestId)) {
|
|
173
184
|
return {
|
|
174
185
|
kind: 'managed_failed',
|
|
175
186
|
manifest,
|