@piagent/platform 1.3.1 → 1.4.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/CHANGELOG.md +132 -0
- package/README.md +10 -4
- package/README.vi.md +27 -0
- package/architecture/layers.json +96 -1
- package/docs/README.md +1 -0
- package/docs/architecture.md +1 -0
- package/docs/authority-manifest.md +1 -0
- package/docs/auto-delegation-policy.md +1 -0
- package/docs/capability-packs.md +1 -0
- package/docs/command-reference-vietnamese.md +5 -4
- package/docs/context-engine.md +1 -0
- package/docs/context-window-policy.md +1 -0
- package/docs/distribution-standard.md +8 -7
- package/docs/en/architecture.md +13 -0
- package/docs/herdr-workflow.md +1 -0
- package/docs/languages.json +20 -4
- package/docs/mcp-and-tools.md +125 -9
- package/docs/memory-policy.md +1 -0
- package/docs/model-options.md +1 -0
- package/docs/oauth-providers.md +1 -0
- package/docs/operator-manual-vietnamese.md +15 -12
- package/docs/package-architecture-notes.md +1 -0
- package/docs/project-adapters.md +1 -0
- package/docs/project-onboarding.md +1 -0
- package/docs/publishing-for-teams.md +37 -0
- package/docs/quality-benchmark.md +1 -0
- package/docs/quickstart-vietnamese.md +3 -2
- package/docs/release-install-policy.md +22 -8
- package/docs/runtime-harness-standard.md +2 -1
- package/docs/runtime-policy-design.md +1 -0
- package/docs/runtime-quality-baseline.md +1 -0
- package/docs/security-redaction-benchmark.md +1 -0
- package/docs/security-threat-model.md +2 -1
- package/docs/subagent-orchestration-capabilities.md +1 -0
- package/docs/subagents-and-multiagent.md +1 -0
- package/docs/task-implementation-contract.md +12 -4
- package/docs/task-lifecycle-vietnamese.md +1 -0
- package/docs/team-onboarding.md +3 -2
- package/docs/third-party-neutrality.md +26 -0
- package/docs/usage-observability.md +71 -0
- package/docs/vercel-docs-site.md +1 -0
- package/docs/vi/architecture.md +13 -0
- package/docs/workflow-recipes.md +1 -0
- package/docs-site/architecture.html +5 -1
- package/docs-site/benchmark.html +5 -1
- package/docs-site/commands.html +5 -1
- package/docs-site/content/en/index.html +49 -0
- package/docs-site/content/en/runtime.html +13 -0
- package/docs-site/content/index.html +49 -0
- package/docs-site/content/runtime.html +28 -0
- package/docs-site/context-engine.html +5 -1
- package/docs-site/en/architecture.html +5 -1
- package/docs-site/en/benchmark.html +5 -1
- package/docs-site/en/commands.html +5 -1
- package/docs-site/en/context-engine.html +5 -1
- package/docs-site/en/index.html +54 -1
- package/docs-site/en/mcp-approval.html +5 -1
- package/docs-site/en/mcp-auth.html +5 -1
- package/docs-site/en/mcp-servers.html +5 -1
- package/docs-site/en/mcp.html +5 -1
- package/docs-site/en/permissions.html +5 -1
- package/docs-site/en/profiles.html +5 -1
- package/docs-site/en/quickstart.html +5 -1
- package/docs-site/en/runtime.html +18 -1
- package/docs-site/en/scope.html +5 -1
- package/docs-site/en/security.html +5 -1
- package/docs-site/en/team.html +5 -1
- package/docs-site/en/whats-new.html +5 -1
- package/docs-site/en/workflows.html +5 -1
- package/docs-site/index.html +54 -1
- package/docs-site/mcp-approval.html +5 -1
- package/docs-site/mcp-auth.html +5 -1
- package/docs-site/mcp-servers.html +5 -1
- package/docs-site/mcp.html +5 -1
- package/docs-site/permissions.html +5 -1
- package/docs-site/profiles.html +5 -1
- package/docs-site/quickstart.html +5 -1
- package/docs-site/runtime.html +33 -1
- package/docs-site/scope.html +5 -1
- package/docs-site/security.html +5 -1
- package/docs-site/team.html +5 -1
- package/docs-site/whats-new.html +5 -1
- package/docs-site/workflows.html +5 -1
- package/evals/fixtures/mutation-provenance-record.invalid.json +17 -0
- package/evals/fixtures/mutation-provenance-record.valid.json +27 -0
- package/evals/fixtures/piagent-webui/approval-v1.invalid.json +54 -0
- package/evals/fixtures/piagent-webui/approval-v1.valid.json +58 -0
- package/evals/fixtures/piagent-webui/attachment-v1.invalid.json +25 -0
- package/evals/fixtures/piagent-webui/attachment-v1.valid.json +25 -0
- package/evals/fixtures/piagent-webui/capabilities-v1.invalid.json +51 -0
- package/evals/fixtures/piagent-webui/capabilities-v1.valid.json +122 -0
- package/evals/fixtures/piagent-webui/commit-summary-v1.invalid.json +10 -0
- package/evals/fixtures/piagent-webui/commit-summary-v1.valid.json +25 -0
- package/evals/fixtures/piagent-webui/control-command-v1.invalid.json +37 -0
- package/evals/fixtures/piagent-webui/control-command-v1.valid.json +35 -0
- package/evals/fixtures/piagent-webui/diff-v1.invalid.json +13 -0
- package/evals/fixtures/piagent-webui/diff-v1.valid.json +141 -0
- package/evals/fixtures/piagent-webui/gateway-capabilities-v1.invalid.json +35 -0
- package/evals/fixtures/piagent-webui/gateway-capabilities-v1.valid.json +35 -0
- package/evals/fixtures/piagent-webui/gateway-protocol-v1.invalid.json +14 -0
- package/evals/fixtures/piagent-webui/gateway-protocol-v1.valid.json +14 -0
- package/evals/fixtures/piagent-webui/handoff-history-v1.invalid.json +16 -0
- package/evals/fixtures/piagent-webui/handoff-history-v1.valid.json +16 -0
- package/evals/fixtures/piagent-webui/model-catalog-v1.invalid.json +31 -0
- package/evals/fixtures/piagent-webui/model-catalog-v1.valid.json +43 -0
- package/evals/fixtures/piagent-webui/queue-v1.invalid.json +29 -0
- package/evals/fixtures/piagent-webui/queue-v1.valid.json +44 -0
- package/evals/fixtures/piagent-webui/recovery-history-v1.invalid.json +17 -0
- package/evals/fixtures/piagent-webui/recovery-history-v1.valid.json +20 -0
- package/evals/fixtures/piagent-webui/release-monitor-v1.invalid.json +12 -0
- package/evals/fixtures/piagent-webui/release-monitor-v1.valid.json +29 -0
- package/evals/fixtures/piagent-webui/review-state-v1.invalid.json +25 -0
- package/evals/fixtures/piagent-webui/review-state-v1.valid.json +38 -0
- package/evals/fixtures/piagent-webui/runtime-event-v2.invalid.json +49 -0
- package/evals/fixtures/piagent-webui/runtime-event-v2.valid.json +49 -0
- package/evals/fixtures/piagent-webui/session-catalog-v1.invalid.json +17 -0
- package/evals/fixtures/piagent-webui/session-catalog-v1.valid.json +58 -0
- package/evals/fixtures/piagent-webui/session-command-v1.invalid.json +19 -0
- package/evals/fixtures/piagent-webui/session-command-v1.valid.json +19 -0
- package/evals/fixtures/piagent-webui/snapshot-v1.invalid.json +5 -0
- package/evals/fixtures/piagent-webui/snapshot-v1.valid.json +191 -0
- package/evals/fixtures/piagent-webui/source-change-v1.invalid.json +57 -0
- package/evals/fixtures/piagent-webui/source-change-v1.valid.json +98 -0
- package/evals/fixtures/piagent-webui/source-mutation-v1.invalid.json +19 -0
- package/evals/fixtures/piagent-webui/source-mutation-v1.valid.json +38 -0
- package/evals/fixtures/piagent-webui/source-revert-v1.invalid.json +20 -0
- package/evals/fixtures/piagent-webui/source-revert-v1.valid.json +62 -0
- package/evals/fixtures/piagent-webui/subagent-tree-v1.invalid.json +26 -0
- package/evals/fixtures/piagent-webui/subagent-tree-v1.valid.json +39 -0
- package/evals/fixtures/piagent-webui/task-index-v1.invalid.json +15 -0
- package/evals/fixtures/piagent-webui/task-index-v1.valid.json +15 -0
- package/evals/fixtures/piagent-webui/task-timeline-v1.invalid.json +14 -0
- package/evals/fixtures/piagent-webui/task-timeline-v1.valid.json +16 -0
- package/evals/fixtures/piagent-webui/transcript-v1.invalid.json +35 -0
- package/evals/fixtures/piagent-webui/transcript-v1.valid.json +55 -0
- package/evals/fixtures/task-baseline-manifest.invalid.json +16 -0
- package/evals/fixtures/task-baseline-manifest.valid.json +41 -0
- package/evals/fixtures/verifier-file-snapshot.invalid.json +21 -0
- package/evals/fixtures/verifier-file-snapshot.valid.json +21 -0
- package/package.json +43 -4
- package/packages/piagent-core/README.md +3 -2
- package/packages/piagent-core/benchmark/benchmark-candidate.d.ts +1 -0
- package/packages/piagent-core/benchmark/benchmark-forensics.d.ts +1 -0
- package/packages/piagent-core/benchmark/benchmark-ledger.d.ts +4 -0
- package/packages/piagent-core/benchmark/benchmark-record-validation.d.ts +1 -0
- package/packages/piagent-core/benchmark/benchmark-runtime.d.ts +1 -0
- package/packages/piagent-core/capabilities/capability-core.js +2 -2
- package/packages/piagent-core/capabilities/capability-verification-cache.js +17 -7
- package/packages/piagent-core/capabilities/runtime-integrity.js +190 -0
- package/packages/piagent-core/extensions/piagent-guard.ts +80 -272
- package/packages/piagent-core/extensions/redaction-core.d.ts +2 -0
- package/packages/piagent-core/extensions/runtime-evidence.js +12 -1
- package/packages/piagent-core/extensions/shell-reach.ts +250 -0
- package/packages/piagent-core/extensions/task-journal.js +30 -1
- package/packages/piagent-core/extensions/task-state.js +33 -36
- package/packages/piagent-core/mcp/mcp-command-actions.js +1 -1
- package/packages/piagent-core/mcp/mcp-server-catalog.js +139 -9
- package/packages/piagent-core/package.json +1 -1
- package/packages/piagent-core/runtime/hooks/session-start-hook.ts +2 -2
- package/packages/piagent-core/runtime/hooks/tool-call-hook.ts +12 -2
- package/packages/piagent-core/runtime/hooks/tool-result-hook.ts +20 -19
- package/packages/piagent-core/runtime/hooks/tool-result-metadata.ts +13 -0
- package/packages/piagent-core/runtime/input/chat-images.ts +2 -2
- package/packages/piagent-core/runtime/inspection/activity-event-adapter.ts +94 -0
- package/packages/piagent-core/runtime/inspection/approval-broker.ts +318 -0
- package/packages/piagent-core/runtime/inspection/commit-summary-projection.ts +59 -0
- package/packages/piagent-core/runtime/inspection/context-telemetry-inspection.ts +37 -0
- package/packages/piagent-core/runtime/inspection/criteria-links.ts +295 -0
- package/packages/piagent-core/runtime/inspection/diff-projection.ts +499 -0
- package/packages/piagent-core/runtime/inspection/git-filter-safety.ts +38 -0
- package/packages/piagent-core/runtime/inspection/git-numstat-adapter.ts +71 -0
- package/packages/piagent-core/runtime/inspection/git-status-adapter.ts +493 -0
- package/packages/piagent-core/runtime/inspection/git-tree-adapter.ts +108 -0
- package/packages/piagent-core/runtime/inspection/mcp-control.ts +10 -0
- package/packages/piagent-core/runtime/inspection/mutation-provenance-contract.ts +131 -0
- package/packages/piagent-core/runtime/inspection/mutation-provenance-recorder.ts +48 -0
- package/packages/piagent-core/runtime/inspection/mutation-provenance-store.ts +161 -0
- package/packages/piagent-core/runtime/inspection/project-runtime-inspection.ts +36 -0
- package/packages/piagent-core/runtime/inspection/review-state-contract.ts +108 -0
- package/packages/piagent-core/runtime/inspection/review-state-projection.ts +93 -0
- package/packages/piagent-core/runtime/inspection/review-state-store.ts +112 -0
- package/packages/piagent-core/runtime/inspection/runtime-event-store.ts +262 -0
- package/packages/piagent-core/runtime/inspection/same-process-bridge-proof.ts +103 -0
- package/packages/piagent-core/runtime/inspection/source-change-projection.ts +471 -0
- package/packages/piagent-core/runtime/inspection/source-evidence-contract.ts +139 -0
- package/packages/piagent-core/runtime/inspection/source-evidence-store.ts +315 -0
- package/packages/piagent-core/runtime/inspection/source-handoff-store.ts +70 -0
- package/packages/piagent-core/runtime/inspection/source-inspection-plan.ts +34 -0
- package/packages/piagent-core/runtime/inspection/source-mutation-projection.ts +164 -0
- package/packages/piagent-core/runtime/inspection/source-mutation-store.ts +168 -0
- package/packages/piagent-core/runtime/inspection/source-open-target.ts +38 -0
- package/packages/piagent-core/runtime/inspection/source-revert-projection.ts +147 -0
- package/packages/piagent-core/runtime/inspection/task-baseline-start-capture.ts +22 -0
- package/packages/piagent-core/runtime/inspection/task-compaction-history.ts +160 -0
- package/packages/piagent-core/runtime/inspection/task-control-journal.ts +131 -0
- package/packages/piagent-core/runtime/inspection/task-handoff-history.ts +95 -0
- package/packages/piagent-core/runtime/inspection/task-provenance-projection.ts +60 -0
- package/packages/piagent-core/runtime/inspection/task-recovery-timeline.ts +88 -0
- package/packages/piagent-core/runtime/inspection/task-run-index.ts +87 -0
- package/packages/piagent-core/runtime/inspection/task-source-projection.ts +401 -0
- package/packages/piagent-core/runtime/inspection/task-subagent-tree.ts +101 -0
- package/packages/piagent-core/runtime/inspection/text-diff.ts +60 -0
- package/packages/piagent-core/runtime/inspection/verifier-snapshot-contract.ts +114 -0
- package/packages/piagent-core/runtime/inspection/verifier-snapshot-store.ts +189 -0
- package/packages/piagent-core/runtime/inspection/webui-snapshot.ts +320 -0
- package/packages/piagent-core/runtime/inspection/workspace-file-reader.ts +132 -0
- package/packages/piagent-core/runtime/inspection/workspace-source-projection.ts +96 -0
- package/packages/piagent-core/runtime/inspection/zero-turn-conformance.ts +175 -0
- package/packages/piagent-core/runtime/orchestration/owned-work-budget.ts +93 -6
- package/packages/piagent-core/runtime/policy/block-remedy.ts +139 -0
- package/packages/piagent-core/runtime/policy/source-index-transaction.ts +173 -0
- package/packages/piagent-core/runtime/policy/source-mutation-guard-binding.ts +42 -0
- package/packages/piagent-core/runtime/policy/source-mutation-guard.ts +184 -0
- package/packages/piagent-core/runtime/policy/source-worktree-transaction.ts +105 -0
- package/packages/piagent-core/runtime/product/activity-inspector-footer.ts +5 -3
- package/packages/piagent-core/runtime/product/activity-inspector.ts +85 -343
- package/packages/piagent-core/runtime/recovery/resume-state.ts +38 -1
- package/packages/piagent-core/runtime/registration/activity-inspector-command.ts +68 -14
- package/packages/piagent-core/runtime/registration/task-start-tool.ts +22 -27
- package/packages/piagent-core/runtime/session/model-authorship-state.ts +31 -3
- package/packages/piagent-core/runtime/session/runtime-state.ts +11 -1
- package/packages/piagent-core/runtime/session/token-economics.js +34 -0
- package/packages/piagent-core/runtime/tools/phase-tool-runtime.ts +2 -1
- package/packages/piagent-core/runtime/workflows/task-intake.ts +5 -0
- package/packages/piagent-core/security/sensitive-data.js +7 -0
- package/packages/piagent-webui/dist/client/assets/index-DwKCWbwd.css +1 -0
- package/packages/piagent-webui/dist/client/assets/index-bu7XpD9Q.js +172 -0
- package/packages/piagent-webui/dist/client/index.html +16 -0
- package/packages/piagent-webui/extension/attachment-store.ts +323 -0
- package/packages/piagent-webui/extension/held-message-queue.ts +311 -0
- package/packages/piagent-webui/extension/lifecycle-controller.ts +423 -0
- package/packages/piagent-webui/extension/lifecycle-event-adapter.ts +23 -0
- package/packages/piagent-webui/extension/piagent-webui.ts +470 -0
- package/packages/piagent-webui/extension/review-controller.ts +160 -0
- package/packages/piagent-webui/extension/same-session-bridge.ts +500 -0
- package/packages/piagent-webui/extension/session-options-controller.ts +281 -0
- package/packages/piagent-webui/extension/session-stream-adapter.ts +249 -0
- package/packages/piagent-webui/extension/source-mutation-controller.ts +215 -0
- package/packages/piagent-webui/extension/source-open-controller.ts +136 -0
- package/packages/piagent-webui/extension/source-revert-controller.ts +189 -0
- package/packages/piagent-webui/extension/terminal-session-adapter.ts +73 -0
- package/packages/piagent-webui/extension/vscode-handoff.ts +51 -0
- package/packages/piagent-webui/gateway/control-socket.ts +92 -0
- package/packages/piagent-webui/gateway/extension-authority.ts +47 -0
- package/packages/piagent-webui/gateway/gateway-events.ts +60 -0
- package/packages/piagent-webui/gateway/gateway-protocol-service.ts +64 -0
- package/packages/piagent-webui/gateway/gateway-service.ts +215 -0
- package/packages/piagent-webui/gateway/gateway-session-stream.ts +110 -0
- package/packages/piagent-webui/gateway/mcp-auth-broker.ts +158 -0
- package/packages/piagent-webui/gateway/native-project-picker.ts +29 -0
- package/packages/piagent-webui/gateway/pi-host.ts +41 -0
- package/packages/piagent-webui/gateway/profile-state.ts +1 -0
- package/packages/piagent-webui/gateway/project-registry.ts +108 -0
- package/packages/piagent-webui/gateway/provider-auth-broker.ts +172 -0
- package/packages/piagent-webui/gateway/session-catalog.ts +128 -0
- package/packages/piagent-webui/gateway/session-command-controller.ts +237 -0
- package/packages/piagent-webui/gateway/session-command-store.ts +224 -0
- package/packages/piagent-webui/gateway/session-inspection-registry.ts +239 -0
- package/packages/piagent-webui/gateway/session-lease-store.ts +1 -0
- package/packages/piagent-webui/gateway/session-metadata-store.ts +152 -0
- package/packages/piagent-webui/gateway/session-runtime-supervisor.ts +489 -0
- package/packages/piagent-webui/ownership/gateway-runtime-context.ts +8 -0
- package/packages/piagent-webui/ownership/profile-state.ts +117 -0
- package/packages/piagent-webui/ownership/session-lease-store.ts +329 -0
- package/packages/piagent-webui/ownership/session-refs.ts +8 -0
- package/packages/piagent-webui/package.json +33 -0
- package/packages/piagent-webui/server/README.md +4 -0
- package/packages/piagent-webui/server/benchmark-release-monitor.ts +230 -0
- package/packages/piagent-webui/server/core-inspection-provider.ts +403 -0
- package/packages/piagent-webui/server/gateway-websocket.ts +143 -0
- package/packages/piagent-webui/server/http-security.ts +27 -0
- package/packages/piagent-webui/server/ipc-read-model-client.ts +89 -0
- package/packages/piagent-webui/server/loopback-server.ts +368 -0
- package/packages/piagent-webui/server/read-model-provider.ts +37 -0
- package/packages/piagent-webui/server/read-only-router.ts +105 -0
- package/packages/piagent-webui/server/session-auth.ts +88 -0
- package/packages/piagent-webui/server/sidecar-main.ts +86 -0
- package/packages/piagent-webui/server/sse-hub.ts +76 -0
- package/packages/piagent-webui/server/static-bundle.ts +40 -0
- package/packages/piagent-webui/server/transcript-projection.ts +125 -0
- package/schemas/mutation-provenance-record.schema.json +100 -0
- package/schemas/piagent-webui/approval-v1.schema.json +654 -0
- package/schemas/piagent-webui/attachment-v1.schema.json +267 -0
- package/schemas/piagent-webui/capabilities-v1.schema.json +729 -0
- package/schemas/piagent-webui/catalog-v1.json +240 -0
- package/schemas/piagent-webui/commit-summary-v1.schema.json +63 -0
- package/schemas/piagent-webui/common-v1.schema.json +479 -0
- package/schemas/piagent-webui/control-command-v1.schema.json +1780 -0
- package/schemas/piagent-webui/diff-v1.schema.json +826 -0
- package/schemas/piagent-webui/gateway-capabilities-v1.schema.json +136 -0
- package/schemas/piagent-webui/gateway-protocol-v1.schema.json +456 -0
- package/schemas/piagent-webui/handoff-history-v1.schema.json +92 -0
- package/schemas/piagent-webui/model-catalog-v1.schema.json +95 -0
- package/schemas/piagent-webui/queue-v1.schema.json +99 -0
- package/schemas/piagent-webui/recovery-history-v1.schema.json +144 -0
- package/schemas/piagent-webui/release-monitor-v1.schema.json +173 -0
- package/schemas/piagent-webui/review-state-v1.schema.json +206 -0
- package/schemas/piagent-webui/runtime-event-v2.schema.json +5206 -0
- package/schemas/piagent-webui/session-catalog-v1.schema.json +222 -0
- package/schemas/piagent-webui/session-command-v1.schema.json +390 -0
- package/schemas/piagent-webui/snapshot-v1.schema.json +2622 -0
- package/schemas/piagent-webui/source-change-v1.schema.json +763 -0
- package/schemas/piagent-webui/source-mutation-v1.schema.json +96 -0
- package/schemas/piagent-webui/source-revert-v1.schema.json +130 -0
- package/schemas/piagent-webui/subagent-tree-v1.schema.json +88 -0
- package/schemas/piagent-webui/task-index-v1.schema.json +74 -0
- package/schemas/piagent-webui/task-timeline-v1.schema.json +49 -0
- package/schemas/piagent-webui/transcript-v1.schema.json +243 -0
- package/schemas/task-baseline-manifest.schema.json +122 -0
- package/schemas/verifier-file-snapshot.schema.json +81 -0
- package/scripts/build-docs-site.mjs +21 -1
- package/scripts/check-architecture.mjs +1 -0
- package/scripts/check-doc-languages.mjs +19 -2
- package/scripts/check-third-party-neutrality.mjs +43 -0
- package/scripts/explain-command.mjs +240 -0
- package/scripts/install-global.sh +105 -4
- package/scripts/mcp-manage.mjs +17 -4
- package/scripts/pi-session-stats.sh +10 -2
- package/scripts/piagent-cli.mjs +30 -3
- package/scripts/piagent-dashboard.mjs +261 -0
- package/scripts/piagent-webui-launcher.mjs +121 -0
- package/scripts/setup.sh +69 -33
- package/scripts/traffic-snapshot.mjs +214 -0
- package/scripts/typescript-loader.mjs +42 -1
- package/scripts/uninstall-global.sh +53 -2
- package/scripts/update-global.mjs +33 -1
- package/scripts/verify-local.sh +257 -0
- package/scripts/verify-release-identity.mjs +4 -1
- package/scripts/verify-translation-pairing.mjs +121 -0
|
@@ -1,14 +1,18 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
|
|
1
3
|
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
4
|
|
|
3
5
|
import type { TaskContract } from "../../extensions/guard-types.ts";
|
|
4
6
|
import {
|
|
5
7
|
buildActivityInspector,
|
|
6
8
|
formatActivityInspector,
|
|
7
|
-
inspectWorkingTreeFiles,
|
|
8
9
|
type ActivityInspectorEvent,
|
|
9
10
|
type CurrentActivity
|
|
10
11
|
} from "../product/activity-inspector.ts";
|
|
11
12
|
import { formatActivityFooter, formatActivityPanel } from "../product/activity-inspector-footer.ts";
|
|
13
|
+
import { adaptActivityTelemetryEvent, runtimeStartedEventDraft } from "../inspection/activity-event-adapter.ts";
|
|
14
|
+
import { RuntimeEventStore, type RuntimeEventRevision } from "../inspection/runtime-event-store.ts";
|
|
15
|
+
import { WEBUI_RUNTIME_INSTANCE_REF, webUiProjectRef, webUiSessionRef } from "../inspection/webui-snapshot.ts";
|
|
12
16
|
import { prefixCompletions, registerRuntimeCommand } from "./extension-registration.ts";
|
|
13
17
|
|
|
14
18
|
const INSPECTOR_ACTIONS = ["summary", "files", "commands", "security", "context", "toggle", "help"] as const;
|
|
@@ -25,12 +29,30 @@ type InspectorDependencies = {
|
|
|
25
29
|
defaultValue?: string
|
|
26
30
|
) => Promise<string | undefined>;
|
|
27
31
|
emit: (ctx: ExtensionContext, customType: string, content: string, details?: Record<string, unknown>) => void;
|
|
32
|
+
protectedPaths?: (ctx: ExtensionContext) => string[];
|
|
28
33
|
};
|
|
29
34
|
|
|
30
35
|
function sessionKey(ctx: ExtensionContext): string {
|
|
31
36
|
return `${ctx.cwd}\u0000${ctx.sessionManager.getSessionId()}`;
|
|
32
37
|
}
|
|
33
38
|
|
|
39
|
+
function revisionToken(prefix: string, value: unknown): string {
|
|
40
|
+
return `${prefix}.${createHash("sha256").update(JSON.stringify(value)).digest("hex")}`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function eventRevision(ctx: ExtensionContext, task?: TaskContract): RuntimeEventRevision {
|
|
44
|
+
return {
|
|
45
|
+
runtimeRevision: revisionToken("runtime-rev", [WEBUI_RUNTIME_INSTANCE_REF, ctx.sessionManager.getSessionId()]),
|
|
46
|
+
taskRevision: task ? revisionToken("task-rev", [task.taskRunId, task.updatedAt, task.trace.outcome]) : null,
|
|
47
|
+
controlRevision: task ? revisionToken("control-rev", [task.taskRunId, task.trace.outcome]) : null,
|
|
48
|
+
workspaceRevision: null,
|
|
49
|
+
indexRevision: null,
|
|
50
|
+
approvalRevision: null,
|
|
51
|
+
sessionOptionRevision: null,
|
|
52
|
+
queueRevision: null
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
34
56
|
function clipped(value: unknown, maximum = 96): string | undefined {
|
|
35
57
|
if (typeof value !== "string") return undefined;
|
|
36
58
|
const normalized = value.replace(/\s+/g, " ").trim();
|
|
@@ -67,7 +89,23 @@ export function registerActivityInspector(pi: ExtensionAPI, dependencies: Inspec
|
|
|
67
89
|
const running = new Map<string, Map<string, CurrentActivity>>();
|
|
68
90
|
const lastActivity = new Map<string, CurrentActivity>();
|
|
69
91
|
const enabled = new Map<string, boolean>();
|
|
70
|
-
const cached = new Map<string, { at: number; view: ReturnType<typeof buildActivityInspector
|
|
92
|
+
const cached = new Map<string, { at: number; view: Awaited<ReturnType<typeof buildActivityInspector>> }>();
|
|
93
|
+
const eventStores = new Map<string, RuntimeEventStore>();
|
|
94
|
+
|
|
95
|
+
function eventStore(ctx: ExtensionContext): RuntimeEventStore {
|
|
96
|
+
const key = sessionKey(ctx);
|
|
97
|
+
const existing = eventStores.get(key);
|
|
98
|
+
if (existing) return existing;
|
|
99
|
+
const projectRef = webUiProjectRef(ctx.cwd), sessionRef = webUiSessionRef(ctx.sessionManager.getSessionId());
|
|
100
|
+
const created = new RuntimeEventStore({ projectRoot: ctx.cwd, projectRef, runtimeInstanceId: WEBUI_RUNTIME_INSTANCE_REF, sessionRef });
|
|
101
|
+
eventStores.set(key, created);
|
|
102
|
+
if (!created.resyncRequired() && created.replay(null, 1).lastAvailableSequence === null) {
|
|
103
|
+
const now = new Date().toISOString();
|
|
104
|
+
created.append(runtimeStartedEventDraft({ identity: { projectRef, runtimeInstanceId: WEBUI_RUNTIME_INSTANCE_REF, sessionRef },
|
|
105
|
+
revision: eventRevision(ctx), sourceObservedAt: now, buildRef: "runtime-build.unavailable", capabilitySnapshotRef: "capabilities.inspect-only" }), now);
|
|
106
|
+
}
|
|
107
|
+
return created;
|
|
108
|
+
}
|
|
71
109
|
|
|
72
110
|
function sessionEvents(ctx: ExtensionContext): ActivityInspectorEvent[] {
|
|
73
111
|
let persisted: ActivityInspectorEvent[] = [];
|
|
@@ -85,14 +123,15 @@ export function registerActivityInspector(pi: ExtensionAPI, dependencies: Inspec
|
|
|
85
123
|
return active.length > 0 ? active : lastActivity.has(key) ? [lastActivity.get(key) as CurrentActivity] : [];
|
|
86
124
|
}
|
|
87
125
|
|
|
88
|
-
function project(ctx: ExtensionContext, force = false
|
|
126
|
+
async function project(ctx: ExtensionContext, force = false) {
|
|
89
127
|
const key = sessionKey(ctx);
|
|
90
128
|
const previous = cached.get(key);
|
|
91
129
|
const current = currentFor(ctx);
|
|
92
|
-
if (!
|
|
130
|
+
if (!force && previous && Date.now() - previous.at < 400) {
|
|
93
131
|
return { ...previous.view, state: { ...previous.view.state, running: current.filter((activity) => activity.status === "running").length, current } };
|
|
94
132
|
}
|
|
95
|
-
const
|
|
133
|
+
const events = eventStore(ctx);
|
|
134
|
+
const view = await buildActivityInspector({
|
|
96
135
|
cwd: ctx.cwd,
|
|
97
136
|
sessionId: ctx.sessionManager.getSessionId(),
|
|
98
137
|
task: dependencies.activeTask(ctx),
|
|
@@ -100,8 +139,11 @@ export function registerActivityInspector(pi: ExtensionAPI, dependencies: Inspec
|
|
|
100
139
|
sessionEntries: ctx.sessionManager.getBranch(),
|
|
101
140
|
contextUsage: ctx.getContextUsage(),
|
|
102
141
|
current,
|
|
103
|
-
|
|
104
|
-
|
|
142
|
+
protectedPaths: dependencies.protectedPaths?.(ctx),
|
|
143
|
+
runtimeInstanceId: WEBUI_RUNTIME_INSTANCE_REF,
|
|
144
|
+
eventCursor: events.currentCursor(),
|
|
145
|
+
resyncRequired: events.resyncRequired(),
|
|
146
|
+
eventReplay: events.retention()
|
|
105
147
|
});
|
|
106
148
|
cached.set(key, { at: Date.now(), view });
|
|
107
149
|
return view;
|
|
@@ -112,7 +154,8 @@ export function registerActivityInspector(pi: ExtensionAPI, dependencies: Inspec
|
|
|
112
154
|
if (typeof ctx.ui?.setWidget === "function") ctx.ui.setWidget("piagent-inspector", undefined);
|
|
113
155
|
}
|
|
114
156
|
|
|
115
|
-
function renderSurface(ctx: ExtensionContext, force = false): void {
|
|
157
|
+
async function renderSurface(ctx: ExtensionContext, force = false): Promise<void> {
|
|
158
|
+
if (ctx.mode !== "tui") return;
|
|
116
159
|
const canUseStatus = typeof ctx.ui?.setStatus === "function";
|
|
117
160
|
const canUseWidget = typeof ctx.ui?.setWidget === "function";
|
|
118
161
|
if (ctx.hasUI === false || (!canUseStatus && !canUseWidget)) return;
|
|
@@ -122,7 +165,7 @@ export function registerActivityInspector(pi: ExtensionAPI, dependencies: Inspec
|
|
|
122
165
|
return;
|
|
123
166
|
}
|
|
124
167
|
try {
|
|
125
|
-
const view = project(ctx, force);
|
|
168
|
+
const view = await project(ctx, force);
|
|
126
169
|
const color = process.env.NO_COLOR === undefined && process.env.TERM !== "dumb";
|
|
127
170
|
if (canUseWidget) {
|
|
128
171
|
ctx.ui.setWidget("piagent-inspector", formatActivityPanel(view, { color }), { placement: "belowEditor" });
|
|
@@ -148,6 +191,15 @@ export function registerActivityInspector(pi: ExtensionAPI, dependencies: Inspec
|
|
|
148
191
|
};
|
|
149
192
|
const stored = [...(memoryEvents.get(key) ?? []), event].slice(-1_000);
|
|
150
193
|
memoryEvents.set(key, stored);
|
|
194
|
+
try {
|
|
195
|
+
const store = eventStore(ctx);
|
|
196
|
+
const draft = adaptActivityTelemetryEvent({ event, identity: { projectRef: webUiProjectRef(ctx.cwd), runtimeInstanceId: WEBUI_RUNTIME_INSTANCE_REF,
|
|
197
|
+
sessionRef: webUiSessionRef(ctx.sessionManager.getSessionId()), taskId: task?.taskId ?? null, taskRunId: task?.taskRunId ?? null },
|
|
198
|
+
revision: eventRevision(ctx, task) });
|
|
199
|
+
if (draft) store.append(draft);
|
|
200
|
+
} catch {
|
|
201
|
+
// Event replay is an optional projection. Terminal execution remains authoritative.
|
|
202
|
+
}
|
|
151
203
|
let active = running.get(key);
|
|
152
204
|
if (!active) {
|
|
153
205
|
active = new Map();
|
|
@@ -182,7 +234,7 @@ export function registerActivityInspector(pi: ExtensionAPI, dependencies: Inspec
|
|
|
182
234
|
}
|
|
183
235
|
}
|
|
184
236
|
cached.delete(key);
|
|
185
|
-
renderSurface(ctx, event.event === "tool_result" || event.event === "tool_decision");
|
|
237
|
+
void renderSurface(ctx, event.event === "tool_result" || event.event === "tool_decision");
|
|
186
238
|
}
|
|
187
239
|
|
|
188
240
|
function emitHelp(ctx: ExtensionContext): void {
|
|
@@ -235,14 +287,14 @@ export function registerActivityInspector(pi: ExtensionAPI, dependencies: Inspec
|
|
|
235
287
|
const key = sessionKey(ctx);
|
|
236
288
|
const next = enabled.get(key) === false;
|
|
237
289
|
enabled.set(key, next);
|
|
238
|
-
renderSurface(ctx, true);
|
|
290
|
+
await renderSurface(ctx, true);
|
|
239
291
|
ctx.ui.notify(`Piagent Inspector footer panel: ${next ? "on" : "off"}`, "info");
|
|
240
292
|
return;
|
|
241
293
|
}
|
|
242
|
-
const view = project(ctx, true
|
|
294
|
+
const view = await project(ctx, true);
|
|
243
295
|
const content = json ? JSON.stringify({ action, inspector: view }) : formatActivityInspector(view, action);
|
|
244
296
|
dependencies.emit(ctx, `piagent-inspector-${action}`, content, { action, inspector: view });
|
|
245
|
-
renderSurface(ctx);
|
|
297
|
+
void renderSurface(ctx);
|
|
246
298
|
}
|
|
247
299
|
});
|
|
248
300
|
|
|
@@ -254,12 +306,14 @@ export function registerActivityInspector(pi: ExtensionAPI, dependencies: Inspec
|
|
|
254
306
|
lastActivity.delete(key);
|
|
255
307
|
enabled.delete(key);
|
|
256
308
|
cached.delete(key);
|
|
309
|
+
eventStores.delete(key);
|
|
257
310
|
}
|
|
258
311
|
|
|
259
312
|
return {
|
|
260
313
|
observe,
|
|
261
314
|
refresh: (ctx: ExtensionContext) => renderSurface(ctx, true),
|
|
262
|
-
project: (ctx: ExtensionContext) => project(ctx, true
|
|
315
|
+
project: (ctx: ExtensionContext) => project(ctx, true),
|
|
316
|
+
replay: (ctx: ExtensionContext, afterCursor: string | null, limit?: number) => eventStore(ctx).replay(afterCursor, limit),
|
|
263
317
|
dispose
|
|
264
318
|
};
|
|
265
319
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
|
|
3
2
|
import { exactFinalOutputGuidance } from "../quality/exact-output-contract.ts";
|
|
4
3
|
import { taskPerformanceAssurance } from "../quality/performance-assurance.ts";
|
|
5
4
|
import { automaticTaskSummary, boundedRuntimeIntakeMessage } from "../workflows/task-intake.ts";
|
|
@@ -7,18 +6,16 @@ import { WORKING_TREE_DIGEST_ALGORITHM } from "../../extensions/working-tree-dig
|
|
|
7
6
|
import { createEnvironmentBoundTaskAuthority } from "../policy/task-authority-runtime.ts";
|
|
8
7
|
import { authorityReplacementState } from "../policy/authority-resume-policy.ts";
|
|
9
8
|
import { compileCriterionGraph, criterionGraphContextSelection, criterionGraphGuidance, criterionGraphMode } from "../../extensions/criterion-graph.js";
|
|
10
|
-
|
|
9
|
+
import { captureTaskStartBaseline } from "../inspection/task-baseline-start-capture.ts";
|
|
11
10
|
type ExtensionContext = any;
|
|
12
11
|
type TaskContract = any;
|
|
13
12
|
type TaskStartParameters = any;
|
|
14
|
-
|
|
15
13
|
function sameStringRecord(left: Record<string, string>, right: Record<string, string>): boolean {
|
|
16
14
|
const leftEntries = Object.entries(left).sort(([a], [b]) => a.localeCompare(b));
|
|
17
15
|
const rightEntries = Object.entries(right).sort(([a], [b]) => a.localeCompare(b));
|
|
18
16
|
return leftEntries.length === rightEntries.length
|
|
19
17
|
&& leftEntries.every(([key, value], index) => rightEntries[index]?.[0] === key && rightEntries[index]?.[1] === value);
|
|
20
18
|
}
|
|
21
|
-
|
|
22
19
|
function satisfiesAuthorityReplacement(task: TaskContract, state: ReturnType<typeof authorityReplacementState>): boolean {
|
|
23
20
|
if (!task.authoritySnapshot) return false;
|
|
24
21
|
if (state.reason === "mechanical-rollback-requested") {
|
|
@@ -31,7 +28,6 @@ function satisfiesAuthorityReplacement(task: TaskContract, state: ReturnType<typ
|
|
|
31
28
|
}
|
|
32
29
|
return true;
|
|
33
30
|
}
|
|
34
|
-
|
|
35
31
|
export function registerTaskStartTool(pi: ExtensionAPI, deps: Record<string, any>): any {
|
|
36
32
|
const {
|
|
37
33
|
DEFAULT_MAX_TASK_ATTEMPTS, ORCHESTRATION_ROLES, REVIEW_LENSES, StringEnum, Type,
|
|
@@ -39,19 +35,20 @@ export function registerTaskStartTool(pi: ExtensionAPI, deps: Record<string, any
|
|
|
39
35
|
applyRuntimeLifecycleObservation, automaticAcceptanceCriteria, automaticReadOnlyTaskScope, automaticReviewLenses, automaticTaskIntakeMode, automaticTaskRiskLane, automaticTaskScope,
|
|
40
36
|
acceptanceBaselineGuidance, acceptanceProofGuidance, bindSessionTask, buildAcceptanceReceipt, compactTaskDetails, contextBudgetConfig, createTaskRunId,
|
|
41
37
|
currentSessionName, defaultWorkPlan, effectiveProtectedPaths, hasGitEvidenceRoot, hasOperatorSessionName,
|
|
42
|
-
loadProfileFromContext, normalizeReviewLenses, normalizeWorkPlanSteps, nowIso, policy,
|
|
38
|
+
loadProfileFromContext, matchesProtectedPath, normalizeReviewLenses, normalizeWorkPlanSteps, nowIso, policy,
|
|
43
39
|
priorTaskAttempts, recordTaskStartCheckpoint, redactText, redactTextArray, registerRuntimeTool,
|
|
44
40
|
repositoryFileManifest, resolveOrchestrationPolicy, resolveTaskScopePatterns, runtimeLifecycleMode, runtimeState, safeTaskId, selectVerificationPlan,
|
|
45
|
-
summarizeAttempt, validTaskScopePattern, validateNewWorkPlan, verifierCommandInstructions, workingTreeSnapshot, workingTreeSnapshotHasUnavailableEvidence,
|
|
41
|
+
summarizeAttempt, validTaskScopePattern, validateNewWorkPlan, verifierCommandInstructions, workingTreeEvidenceDigest, workingTreeSnapshot, workingTreeSnapshotHasUnavailableEvidence,
|
|
46
42
|
writeTask
|
|
47
43
|
} = deps;
|
|
48
44
|
const taskStartTool = {
|
|
49
45
|
name: "piagent_task_start",
|
|
50
46
|
label: "Piagent Task Start",
|
|
51
|
-
description: "Create a Task Implementation Contract
|
|
52
|
-
promptSnippet: "Start a governed
|
|
47
|
+
description: "Create a Task Implementation Contract before governed project inspection, command execution, or source changes.",
|
|
48
|
+
promptSnippet: "Start a governed project task and persist the task contract.",
|
|
53
49
|
promptGuidelines: [
|
|
54
50
|
"Call this exactly once before source edits in a project managed by Pi Agent Platform.",
|
|
51
|
+
"Use source-change for project command execution, including tests, builds, lint, typecheck, verification, and package dry-runs, even when the operator requires source files to remain unchanged. Read-only intentionally blocks shell execution.",
|
|
55
52
|
"Do not call context, status, policy, evidence-recording, trace, or gate tools first; runtime hooks provide those checks automatically.",
|
|
56
53
|
"Use tiny for a bounded low-risk change, normal for ordinary multi-file work, and high-risk for security, data, release, migration, or external-impact work.",
|
|
57
54
|
"Every scope entry must be a project-relative path or glob such as src/file.ts, src/**, or test/**; never put prose in scope.",
|
|
@@ -62,7 +59,9 @@ export function registerTaskStartTool(pi: ExtensionAPI, deps: Record<string, any
|
|
|
62
59
|
taskId: Type.Optional(Type.String({ minLength: 1 })),
|
|
63
60
|
summary: Type.String({ minLength: 10 }),
|
|
64
61
|
riskLane: StringEnum(["tiny", "normal", "high-risk"] as const),
|
|
65
|
-
changeMode: Type.Optional(StringEnum(["source-change", "read-only"] as const
|
|
62
|
+
changeMode: Type.Optional(StringEnum(["source-change", "read-only"] as const, {
|
|
63
|
+
description: "Use source-change for project command execution as well as edits; use read-only only for inspection that needs no shell execution."
|
|
64
|
+
})),
|
|
66
65
|
maxAttempts: Type.Optional(Type.Number({ minimum: 1, maximum: 10 })),
|
|
67
66
|
expectedOutput: Type.String({ minLength: 10 }),
|
|
68
67
|
acceptanceCriteria: Type.Array(Type.String({ minLength: 1 }), { minItems: 1, maxItems: 12 }),
|
|
@@ -107,15 +106,8 @@ export function registerTaskStartTool(pi: ExtensionAPI, deps: Record<string, any
|
|
|
107
106
|
const migrationRetry = active?.taskId === taskId
|
|
108
107
|
&& active?.workingTreeDigestMigration?.status === "new-attempt-required";
|
|
109
108
|
const authorityRetry = active?.taskId === taskId && activeAuthorityReplacement?.required === true;
|
|
110
|
-
if (active && !migrationRetry && !authorityRetry) {
|
|
109
|
+
if (active?.trace.outcome === "pending" && !migrationRetry && !authorityRetry) {
|
|
111
110
|
if (active.taskId === taskId) {
|
|
112
|
-
if (active.trace.outcome !== "pending") {
|
|
113
|
-
return {
|
|
114
|
-
content: [{ type: "text", text: `Session already belongs to terminal task ${active.taskId} (${active.trace.outcome}). Start a fresh Pi session for a retry or new task.` }],
|
|
115
|
-
details: active,
|
|
116
|
-
isError: true
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
111
|
const manifest = repositoryFileManifest(ctx.cwd);
|
|
120
112
|
const currentScopeResolution = resolveTaskScopePatterns(active.scope, manifest);
|
|
121
113
|
const proposedScopeResolution = Array.isArray(params.scope)
|
|
@@ -155,7 +147,7 @@ export function registerTaskStartTool(pi: ExtensionAPI, deps: Record<string, any
|
|
|
155
147
|
};
|
|
156
148
|
}
|
|
157
149
|
return {
|
|
158
|
-
content: [{ type: "text", text: `Session already
|
|
150
|
+
content: [{ type: "text", text: `Session already has pending task ${active.taskId} (${active.taskRunId}). Complete or stop it before starting another task in this conversation.` }],
|
|
159
151
|
details: active,
|
|
160
152
|
isError: true
|
|
161
153
|
};
|
|
@@ -287,6 +279,15 @@ export function registerTaskStartTool(pi: ExtensionAPI, deps: Record<string, any
|
|
|
287
279
|
if (changeMode === "source-change" && workingTreeSnapshotHasUnavailableEvidence(baselineFileDigests)) {
|
|
288
280
|
return { content: [{ type: "text", text: "Task start refused: the baseline contains unavailable working-tree content evidence. Restore readable repository files before starting source changes." }], isError: true };
|
|
289
281
|
}
|
|
282
|
+
if (changeMode === "source-change") {
|
|
283
|
+
const protectedPaths = effectiveProtectedPaths(policy, profile).readProtectedPaths;
|
|
284
|
+
try {
|
|
285
|
+
await captureTaskStartBaseline({ projectRoot: ctx.cwd, taskId, taskRunId, sessionId, capturedAt: createdAt,
|
|
286
|
+
baselineTreeDigest: workingTreeEvidenceDigest(baselineFileDigests), protectedPaths, matchesProtectedPath });
|
|
287
|
+
} catch {
|
|
288
|
+
return { content: [{ type: "text", text: "Task start refused: the private Task Baseline Manifest could not be captured safely. Inspect local-state permissions and retry before editing." }], isError: true };
|
|
289
|
+
}
|
|
290
|
+
}
|
|
290
291
|
const acceptance = buildAcceptanceReceipt({
|
|
291
292
|
summary: safeSummary,
|
|
292
293
|
expectedOutput: redactText(params.expectedOutput),
|
|
@@ -369,7 +370,6 @@ export function registerTaskStartTool(pi: ExtensionAPI, deps: Record<string, any
|
|
|
369
370
|
appendTrace(ctx.cwd, { taskId, taskRunId, sessionId, sessionName, attempt, event: "task_start", summary: task.summary, riskLane: params.riskLane, intakeMode: task.intakeMode, changeMode: task.changeMode, lifecycleMode, criterionGraphMode: written.criterionGraph.mode, criterionGraphDigest: written.criterionGraph.graphDigest, authorityProfile: written.authoritySnapshot.profile, authoritySnapshotDigest: written.authoritySnapshot.snapshotDigest, scopeMappings, seededContext: seededContext.map((item) => item.path) });
|
|
370
371
|
appendSessionTrace(pi, { taskId, taskRunId, sessionId, sessionName, attempt, event: "task_start", summary: task.summary, riskLane: params.riskLane, intakeMode: task.intakeMode, changeMode: task.changeMode, lifecycleMode, criterionGraphMode: written.criterionGraph.mode, criterionGraphDigest: written.criterionGraph.graphDigest, authorityProfile: written.authoritySnapshot.profile, authoritySnapshotDigest: written.authoritySnapshot.snapshotDigest, scopeMappings, seededContext: seededContext.map((item) => item.path) });
|
|
371
372
|
recordTaskStartCheckpoint(ctx, written, firstReady.id, lifecycleMode);
|
|
372
|
-
|
|
373
373
|
return {
|
|
374
374
|
content: [{
|
|
375
375
|
type: "text",
|
|
@@ -408,18 +408,13 @@ export function registerTaskStartTool(pi: ExtensionAPI, deps: Record<string, any
|
|
|
408
408
|
}
|
|
409
409
|
};
|
|
410
410
|
registerRuntimeTool(pi, taskStartTool);
|
|
411
|
-
|
|
412
|
-
async function maybeStartAutomaticTask(
|
|
413
|
-
prompt: string,
|
|
414
|
-
ctx: ExtensionContext
|
|
415
|
-
): Promise<{ started: boolean; text: string; task?: TaskContract } | undefined> {
|
|
411
|
+
async function maybeStartAutomaticTask(prompt: string, ctx: ExtensionContext): Promise<{ started: boolean; text: string; task?: TaskContract } | undefined> {
|
|
416
412
|
const profile = loadProfileFromContext(ctx);
|
|
417
413
|
const readProtectedPaths = effectiveProtectedPaths(policy, profile).readProtectedPaths;
|
|
418
414
|
const intakeMode = automaticTaskIntakeMode(prompt, readProtectedPaths);
|
|
419
415
|
if (!intakeMode) return undefined;
|
|
420
416
|
const active = activeSessionTask(ctx.cwd, ctx.sessionManager.getSessionId()) as TaskContract | undefined;
|
|
421
417
|
if (active?.trace.outcome === "pending") return undefined;
|
|
422
|
-
|
|
423
418
|
const summary = redactText(automaticTaskSummary(prompt));
|
|
424
419
|
const sessionName = currentSessionName(ctx);
|
|
425
420
|
const projectFiles = repositoryFileManifest(ctx.cwd);
|
|
@@ -429,7 +424,7 @@ export function registerTaskStartTool(pi: ExtensionAPI, deps: Record<string, any
|
|
|
429
424
|
const started = await taskStartTool.execute(
|
|
430
425
|
`runtime-intake-${ctx.sessionManager.getSessionId()}`,
|
|
431
426
|
{
|
|
432
|
-
taskId: hasOperatorSessionName(sessionName) ? sessionName : summary,
|
|
427
|
+
taskId: active && active.trace.outcome !== "pending" ? summary : hasOperatorSessionName(sessionName) ? sessionName : summary,
|
|
433
428
|
summary,
|
|
434
429
|
riskLane: automaticTaskRiskLane(prompt),
|
|
435
430
|
intakeMode: "runtime",
|
|
@@ -17,6 +17,12 @@ type AuthorizedMutation = {
|
|
|
17
17
|
};
|
|
18
18
|
|
|
19
19
|
export type ModelMutationCompletion = { changedPaths: string[]; recordedDigests: Record<string, string> };
|
|
20
|
+
export type ModelMutationEvidenceCompletion = ModelMutationCompletion & {
|
|
21
|
+
beforeSnapshot: Record<string, string> | null;
|
|
22
|
+
targetPaths: string[];
|
|
23
|
+
recordedContentDigests: Record<string, string>;
|
|
24
|
+
proofModes: Record<string, "full-content" | "exact-replacement">;
|
|
25
|
+
};
|
|
20
26
|
|
|
21
27
|
function uniquePaths(values: string[]): string[] {
|
|
22
28
|
return [...new Set(values.map((value) => String(value ?? "").replaceAll("\\", "/").replace(/^\.\//, "").trim()).filter(Boolean))].sort();
|
|
@@ -95,16 +101,30 @@ export class ModelAuthorshipState {
|
|
|
95
101
|
currentSnapshot: Record<string, string>,
|
|
96
102
|
currentContentDigests: Record<string, string> = {}
|
|
97
103
|
): ModelMutationCompletion {
|
|
104
|
+
const result = this.completeWithEvidence(identity, toolCallId, success, currentSnapshot, currentContentDigests);
|
|
105
|
+
return { changedPaths: result.changedPaths, recordedDigests: result.recordedDigests };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
completeWithEvidence(
|
|
109
|
+
identity: ModelMutationIdentity,
|
|
110
|
+
toolCallId: string,
|
|
111
|
+
success: boolean,
|
|
112
|
+
currentSnapshot: Record<string, string>,
|
|
113
|
+
currentContentDigests: Record<string, string> = {}
|
|
114
|
+
): ModelMutationEvidenceCompletion {
|
|
98
115
|
const key = this.#key(identity.taskRunId, toolCallId);
|
|
99
116
|
const reservation = this.#reservations.get(key);
|
|
100
117
|
this.#reservations.delete(key);
|
|
101
|
-
|
|
118
|
+
const empty: ModelMutationEvidenceCompletion = {
|
|
119
|
+
changedPaths: [], recordedDigests: {}, beforeSnapshot: null, targetPaths: [], recordedContentDigests: {}, proofModes: {}
|
|
120
|
+
};
|
|
121
|
+
if (!reservation) return empty;
|
|
102
122
|
const changed = changedPaths(reservation.workingTreeSnapshot, currentSnapshot);
|
|
103
123
|
const evidence = this.#taskEvidence(reservation.identity.taskRunId);
|
|
104
124
|
const proofPaths = uniquePaths([...reservation.proof.fullContentPaths, ...reservation.proof.replacePaths]);
|
|
105
125
|
const invalidate = () => {
|
|
106
126
|
for (const file of uniquePaths([...reservation.targetPaths, ...changed])) evidence.delete(file);
|
|
107
|
-
return { changedPaths: changed,
|
|
127
|
+
return { ...empty, changedPaths: changed, beforeSnapshot: { ...reservation.workingTreeSnapshot }, targetPaths: [...reservation.targetPaths] };
|
|
108
128
|
};
|
|
109
129
|
if (!sameIdentity(reservation.identity, identity) || !success || changed.length === 0 || !exactPaths(changed, proofPaths)) return invalidate();
|
|
110
130
|
const candidates: Array<{ file: string; workingTreeDigest: string; contentDigest: string }> = [];
|
|
@@ -116,12 +136,20 @@ export class ModelAuthorshipState {
|
|
|
116
136
|
candidates.push({ file, workingTreeDigest, contentDigest: content });
|
|
117
137
|
}
|
|
118
138
|
const recordedDigests: Record<string, string> = {};
|
|
139
|
+
const recordedContentDigests: Record<string, string> = {};
|
|
140
|
+
const proofModes: Record<string, "full-content" | "exact-replacement"> = {};
|
|
119
141
|
for (const candidate of candidates) {
|
|
120
142
|
evidence.set(candidate.file, { ...reservation.identity, ...candidate });
|
|
121
143
|
recordedDigests[candidate.file] = candidate.workingTreeDigest;
|
|
144
|
+
recordedContentDigests[candidate.file] = candidate.contentDigest;
|
|
145
|
+
proofModes[candidate.file] = reservation.proof.fullContentPaths.includes(candidate.file) ? "full-content" : "exact-replacement";
|
|
122
146
|
}
|
|
123
147
|
while (this.#evidence.size > 100) this.#evidence.delete(this.#evidence.keys().next().value as string);
|
|
124
|
-
return {
|
|
148
|
+
return {
|
|
149
|
+
changedPaths: changed, recordedDigests,
|
|
150
|
+
beforeSnapshot: { ...reservation.workingTreeSnapshot }, targetPaths: [...reservation.targetPaths],
|
|
151
|
+
recordedContentDigests, proofModes
|
|
152
|
+
};
|
|
125
153
|
}
|
|
126
154
|
|
|
127
155
|
digests(identity: ModelMutationIdentity, currentSnapshot?: Record<string, string>): Record<string, string> {
|
|
@@ -6,7 +6,7 @@ import type { TaskContract } from "../../extensions/guard-types.ts";
|
|
|
6
6
|
import type { RecoveryHistoryEntry } from "../recovery/recovery-policy.ts";
|
|
7
7
|
import type { ResumeState } from "../recovery/resume-state.ts";
|
|
8
8
|
import { ModelAuthorshipState } from "./model-authorship-state.ts";
|
|
9
|
-
import type { ModelMutationIdentity } from "./model-authorship-state.ts";
|
|
9
|
+
import type { ModelMutationEvidenceCompletion, ModelMutationIdentity } from "./model-authorship-state.ts";
|
|
10
10
|
import type { ModelMutationProof } from "../quality/model-mutation-proof.ts";
|
|
11
11
|
import { PerformanceReviewState } from "./performance-review-state.ts";
|
|
12
12
|
import type {
|
|
@@ -239,6 +239,16 @@ export class RuntimeSessionState {
|
|
|
239
239
|
return this.#modelAuthorship.complete(identity, toolCallId, success, currentSnapshot, currentContentDigests);
|
|
240
240
|
}
|
|
241
241
|
|
|
242
|
+
completeAuthorizedModelMutationEvidence(
|
|
243
|
+
identity: ModelMutationIdentity,
|
|
244
|
+
toolCallId: string,
|
|
245
|
+
success: boolean,
|
|
246
|
+
currentSnapshot: Record<string, string>,
|
|
247
|
+
currentContentDigests: Record<string, string> = {}
|
|
248
|
+
): ModelMutationEvidenceCompletion {
|
|
249
|
+
return this.#modelAuthorship.completeWithEvidence(identity, toolCallId, success, currentSnapshot, currentContentDigests);
|
|
250
|
+
}
|
|
251
|
+
|
|
242
252
|
successfulModelMutationDigests(identity: ModelMutationIdentity, currentSnapshot?: Record<string, string>): Record<string, string> {
|
|
243
253
|
return this.#modelAuthorship.digests(identity, currentSnapshot);
|
|
244
254
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// What a session's token counts mean, in one place.
|
|
2
|
+
//
|
|
3
|
+
// `fresh` is the benchmark's own definition -- input + output, the tokens billed
|
|
4
|
+
// at full rate -- restated here rather than recomputed differently by each
|
|
5
|
+
// reader. The published benchmark and the terminal must never mean two things by
|
|
6
|
+
// the same word.
|
|
7
|
+
//
|
|
8
|
+
// The cache hit rate is reported beside `fresh`, never instead of it. In that
|
|
9
|
+
// same benchmark the baseline surface held the *higher* hit rate, 84.9% against
|
|
10
|
+
// 53.0%, while spending more than twice the fresh tokens: a high rate on a large
|
|
11
|
+
// prompt is not a cheap session. The rate explains a cost, it does not report one.
|
|
12
|
+
|
|
13
|
+
const finite = (value) => typeof value === "number" && Number.isFinite(value);
|
|
14
|
+
|
|
15
|
+
export function sessionTokenEconomics(tokens = {}) {
|
|
16
|
+
const { input, output, cacheRead } = tokens ?? {};
|
|
17
|
+
const fresh = finite(input) && finite(output) ? input + output : null;
|
|
18
|
+
const prompt = finite(cacheRead) && finite(input) ? cacheRead + input : null;
|
|
19
|
+
return {
|
|
20
|
+
fresh,
|
|
21
|
+
// A session that has read nothing has no rate to report, which is not the
|
|
22
|
+
// same as a rate of zero.
|
|
23
|
+
cacheHitRate: prompt !== null && prompt > 0 ? cacheRead / prompt : null
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function formatTokenEconomics(economics, formatNumber, formatPercent) {
|
|
28
|
+
return {
|
|
29
|
+
fresh: economics.fresh === null ? "unknown" : `${formatNumber(economics.fresh)} billed at full rate`,
|
|
30
|
+
cache: economics.cacheHitRate === null
|
|
31
|
+
? "unknown"
|
|
32
|
+
: `${formatPercent(100 * economics.cacheHitRate)} of prompt tokens served from cache`
|
|
33
|
+
};
|
|
34
|
+
}
|
|
@@ -80,7 +80,8 @@ export class PhaseToolRuntime {
|
|
|
80
80
|
|
|
81
81
|
toolDecision(ctx: ExtensionContext, toolName: string): { block: true; reason: string } | undefined {
|
|
82
82
|
if (this.effectiveMode(ctx) !== "on") return undefined;
|
|
83
|
-
if (toolName === "piagent_task_start"
|
|
83
|
+
if (toolName === "piagent_task_start"
|
|
84
|
+
&& (this.digestMigrationStatus(ctx) === "new-attempt-required" || this.states.get(this.key(ctx))?.currentPhase === "terminal")) return undefined;
|
|
84
85
|
const state = this.states.get(this.key(ctx));
|
|
85
86
|
if (!state) return undefined;
|
|
86
87
|
const policy = phaseToolPolicy(state.currentPhase, state.changeMode);
|
|
@@ -12,6 +12,7 @@ const AUTO_INTAKE_READ_ONLY_LEAD = /^\s*\/?(?:analy[sz]e|audit|check|discuss|exp
|
|
|
12
12
|
const AUTO_INTAKE_MANUAL_RISK = /\b(?:credential|database|deploy|destructive|encryption|external provider|payment|permission|production|publish|secret|token rotation)\b/i;
|
|
13
13
|
const AUTO_READ_ONLY_INTENT = /\b(?:analy[sz]e|audit|check|diagnos(?:e|is)|explain|inspect|investigate|plan|research|review|scout|summari[sz]e|triage|kiem tra|nghien cuu|giai thich|danh gia)\b/i;
|
|
14
14
|
const AUTO_READ_ONLY_BOUNDARY = /\b(?:read-only|no edits?|do not edit(?: files?| source| project| repo)?|do not change (?:files?|source|project|repo)|do not mutate (?:files?|source|project|repo|workspace)|khong sua(?: file| source| project)?|khong edit(?: file| source| project)?|khong doi(?: file| source| project)?)\b/i;
|
|
15
|
+
const AUTO_EXECUTION_INTENT = /(?:\b(?:run|execute|execution|rerun|re-run|chay)\b.{0,80}\b(?:tests?|build|checks?|gates?|lint|typecheck|package|pack|verify|verification)\b|\b(?:npm|pnpm|yarn|bun)\s+(?:test|pack|run\s+(?:build|check|lint|typecheck|verify))\b)/i;
|
|
15
16
|
|
|
16
17
|
export const AUTO_INTAKE_SNAPSHOT_PATTERNS = [
|
|
17
18
|
"src/**", "app/**", "lib/**", "packages/**", "test/**", "tests/**", "spec/**", "__tests__/**"
|
|
@@ -49,6 +50,10 @@ export function automaticTaskIntakeEligible(prompt: string, readProtectedPaths:
|
|
|
49
50
|
const text = String(prompt ?? "").trim();
|
|
50
51
|
if (!text || text.length > AUTO_INTAKE_MAX_PROMPT_CHARS) return false;
|
|
51
52
|
const signal = classifyContextTask(text);
|
|
53
|
+
if (AUTO_EXECUTION_INTENT.test(text)) {
|
|
54
|
+
if (/\bpiagent_task_start\b/i.test(text)) return false;
|
|
55
|
+
return !signal.paths.some((candidate) => matchesProtectedPath(candidate, readProtectedPaths));
|
|
56
|
+
}
|
|
52
57
|
if (signal.workflow !== "task" || !AUTO_INTAKE_CHANGE_INTENT.test(text)) return false;
|
|
53
58
|
if (AUTO_INTAKE_READ_ONLY_LEAD.test(text) || AUTO_INTAKE_MANUAL_RISK.test(text)) return false;
|
|
54
59
|
if (/\bpiagent_task_start\b/i.test(text)) return false;
|
|
@@ -8,6 +8,13 @@ const TOKEN_PATTERNS = [
|
|
|
8
8
|
/\bsk-(?:(?:proj|svcacct|admin|ant)-)?[A-Za-z0-9_-]{20,}\b/g,
|
|
9
9
|
/\bsk_(?:live|test)_[A-Za-z0-9]{16,}\b/g,
|
|
10
10
|
/\bxox[baprs]-[A-Za-z0-9-]{10,}\b/g,
|
|
11
|
+
// An npm token was recognised only where something named it -- `NPM_TOKEN=...`
|
|
12
|
+
// caught the assignment, a field called `token` caught the field -- so the one
|
|
13
|
+
// place it actually appears was missed: `//registry.npmjs.org/:_authToken=npm_…`
|
|
14
|
+
// is the whole of an `.npmrc`, and a `cat` of that file or a failing publish log
|
|
15
|
+
// carried the credential through unredacted. Every other provider here is
|
|
16
|
+
// recognised by the shape of its token; npm was the gap.
|
|
17
|
+
/\bnpm_[A-Za-z0-9]{30,}\b/g,
|
|
11
18
|
/\bAIza[0-9A-Za-z_-]{20,}\b/g,
|
|
12
19
|
/\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/g
|
|
13
20
|
];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
:root{color:#f4f4ef;font-synthesis:none;text-rendering:optimizelegibility;--bg:#050505;--surface:#101114;--surface-strong:#15171b;--surface-2:#15171b;--line:#24262d;--line-strong:#343740;--text:#f4f4ef;--muted:#a4a6ad;--quiet:#898c95;--accent:#d8ff7a;--accent-soft:#d8ff7a1c;--blue:#8fa7ff;--amber:#ffd166;--warning:#ffd166;--red:#ff8f8f;--danger:#ff8f8f;--positive:#8be9c8;--page-glow:#8be9c81a;background:#050505;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}:root[data-piagent-color-mode=light]{color:#1c2018;--bg:#f5f6f0;--surface:#fff;--surface-strong:#f0f2eb;--surface-2:#f0f2eb;--line:#dfe2d8;--line-strong:#c8cdc0;--text:#1c2018;--muted:#62685c;--quiet:#6b7165;--accent:#627400;--accent-soft:#6274001a;--blue:#506db7;--amber:#9c6200;--warning:#9c6200;--red:#c43f4c;--danger:#c43f4c;--positive:#14775f;--page-glow:#d8ff7a33;background:#f5f6f0}*{box-sizing:border-box}html{background:var(--bg)}body{min-width:320px;min-height:100vh;color:var(--text);background:radial-gradient(circle at 62% -20%, var(--page-glow) 0, transparent 36rem), var(--bg);margin:0}button,input,select,textarea{font:inherit}strong,h1,h2,h3,p{overflow-wrap:anywhere}button:focus-visible,a:focus-visible,summary:focus-visible{outline:2px solid var(--accent);outline-offset:3px}.skip-link{z-index:100;color:#08130f;background:var(--accent);border-radius:7px;padding:9px 12px;font-size:11px;font-weight:800;position:fixed;top:8px;left:8px;transform:translateY(-160%)}.skip-link:focus{transform:translateY(0)}.app-shell{min-height:100vh}.topbar{z-index:10;-webkit-backdrop-filter:blur(20px);backdrop-filter:blur(20px);background:#090c12db;border-bottom:1px solid #354052b8;grid-template-columns:1fr minmax(220px,auto) 1fr;align-items:center;gap:24px;min-height:68px;padding:0 28px;display:grid;position:sticky;top:0}.brand,.session-title,.topbar-actions,.runtime-strip>div,.task-meta,.health-line{align-items:center;display:flex}.brand{gap:11px}.brand>div,.session-title{flex-direction:column;display:flex}.brand strong{letter-spacing:.01em;font-size:15px}.brand small,.session-title small{color:var(--quiet);letter-spacing:.04em;font-size:11px}.brand-mark{width:34px;height:34px;color:var(--accent);background:var(--accent-soft);border:1px solid #64e0b56b;border-radius:10px;place-items:center;font-family:Georgia,serif;font-size:21px;display:grid}.session-title{text-align:center;min-width:0}.session-title strong{text-overflow:ellipsis;white-space:nowrap;max-width:360px;font-size:13px;overflow:hidden}.topbar-actions{justify-content:flex-end;gap:10px}.read-only-badge{border:1px solid var(--line);color:var(--muted);text-transform:uppercase;letter-spacing:.08em;border-radius:7px;padding:5px 9px;font-size:11px;font-weight:700}.workspace{width:min(1460px,100% - 48px);margin:0 auto;padding:28px 0 64px}.runtime-strip{border:1px solid var(--line);background:#11161fb8;border-radius:13px;grid-template-columns:repeat(6,minmax(0,1fr));display:grid;overflow:hidden}.runtime-strip>div{border-right:1px solid var(--line);flex-direction:column;justify-content:center;align-items:flex-start;gap:7px;min-width:0;min-height:70px;padding:13px 16px}.runtime-strip>div:last-child{border-right:0}.runtime-strip span:first-child{color:var(--quiet);letter-spacing:.1em;text-transform:uppercase;font-size:10px;font-weight:800}.runtime-strip strong{color:#dfe6f2;text-overflow:ellipsis;white-space:nowrap;max-width:100%;font-size:13px;overflow:hidden}.content-grid{grid-template-columns:minmax(0,1fr) 290px;align-items:start;gap:18px;margin-top:18px;display:grid}.surface{border:1px solid var(--line);background:var(--surface);border-radius:15px;box-shadow:0 22px 60px #0000001f}.task-dashboard{padding:clamp(20px,3vw,34px)}.task-heading{grid-template-columns:minmax(0,1fr) auto;align-items:start;gap:24px;display:grid}.section-kicker{color:var(--accent);letter-spacing:.14em;text-transform:uppercase;margin:0 0 7px;font-size:10px;font-weight:800}.task-heading h2,.task-empty h2{color:#f2f5fa;letter-spacing:-.035em;max-width:900px;margin:0;font-size:clamp(23px,3vw,38px);font-weight:650;line-height:1.18}.task-meta{color:var(--muted);flex-wrap:wrap;gap:12px;margin-top:15px;font-size:12px}.task-meta>span:not(.status-pill){border-left:1px solid var(--line-strong);padding-left:12px}.progress-number{text-align:right;min-width:116px}.progress-number strong{color:var(--accent);letter-spacing:-.05em;font-size:32px;font-weight:650;display:block}.progress-number span{color:var(--muted);font-size:11px}.progress-track{background:#242b36;border-radius:9px;height:5px;margin:26px 0 30px;overflow:hidden}.progress-track span{border-radius:inherit;background:linear-gradient(90deg,#45c89b,#7ee6bd);height:100%;transition:width .25s;display:block;box-shadow:0 0 20px #64e0b552}.status-pill{color:#aab4c4;white-space:nowrap;align-items:center;gap:6px;width:-moz-fit-content;width:fit-content;min-width:0;font-size:11px;font-weight:700;line-height:1.2;display:inline-flex}.status-dot{width:6px;height:6px;box-shadow:0 0 0 3px color-mix(in srgb, currentColor 13%, transparent);background:currentColor;border-radius:50%;flex:none}.tone-positive{color:var(--accent)}.tone-warning{color:var(--amber)}.tone-danger{color:var(--red)}.tone-info{color:var(--blue)}.tone-neutral{color:#919cad}.criteria-heading{justify-content:space-between;align-items:baseline;gap:16px;margin-bottom:12px;display:flex}.criteria-heading h2{letter-spacing:.01em;margin:0;font-size:13px}.criteria-heading>span{color:var(--quiet);font-size:11px}.criteria-list{gap:8px;margin:0;padding:0;list-style:none;display:grid}.criterion-card{border:1px solid var(--line);background:#090c1257;border-radius:11px;grid-template-columns:38px minmax(0,1fr) auto;align-items:center;gap:14px;min-height:72px;padding:12px 15px;display:grid}.criterion-index{border:1px solid color-mix(in srgb, currentColor 28%, transparent);background:color-mix(in srgb, currentColor 7%, transparent);border-radius:9px;place-items:center;width:32px;height:32px;font-size:10px;font-weight:800;display:grid}.criterion-copy{min-width:0}.criterion-copy>div{align-items:center;gap:9px;display:flex}.criterion-copy p{color:#dce3ed;margin:0;font-size:13px;line-height:1.45}.criterion-copy small{color:var(--quiet);margin-top:4px;font-size:10px;display:block}.critical-tag{color:var(--amber);letter-spacing:.08em;text-transform:uppercase;border:1px solid #efba633d;border-radius:4px;flex:none;padding:3px 6px;font-size:8px;font-weight:800}.blocker{background:#ff7f8612;border:1px solid #ff7f8640;border-radius:9px;grid-template-columns:auto 1fr;gap:14px;margin:-9px 0 24px;padding:12px 14px;display:grid}.blocker span{color:var(--red);text-transform:uppercase;font-size:10px;font-weight:800}.blocker p{color:#e6c3c6;margin:0;font-size:12px}.work-plan{border-top:1px solid var(--line);color:var(--muted);margin-top:16px;padding-top:15px}.work-plan summary{cursor:pointer;font-size:12px;font-weight:700}.work-plan ol{gap:9px;margin:14px 0 0;padding:0;list-style:none;display:grid}.work-plan li{grid-template-columns:100px 1fr;align-items:center;gap:12px;font-size:12px;display:grid}.context-rail{gap:18px;display:grid}.compact-panel{padding:18px}.compact-panel dl{margin:0}.compact-panel dl>div{border-bottom:1px solid var(--line);justify-content:space-between;align-items:center;gap:16px;min-height:39px;display:flex}.compact-panel dl>div:last-child{border-bottom:0}.compact-panel dt{color:var(--muted);font-size:11px}.compact-panel dd{color:#d9e0eb;text-overflow:ellipsis;white-space:nowrap;max-width:170px;margin:0;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:10px;overflow:hidden}.health-line{justify-content:space-between;gap:12px}.health-line>span{color:var(--quiet);font-size:10px}.health-issue{border-top:1px solid var(--line);color:var(--muted);margin:13px 0 0;padding-top:12px;font-size:11px;line-height:1.5}.task-empty{grid-template-columns:auto minmax(0,620px);place-content:center;gap:24px;min-height:420px;padding:48px;display:grid}.task-empty .empty-mark{width:64px;height:64px;color:var(--accent);background:var(--accent-soft);border:1px solid #64e0b547;border-radius:18px;place-items:center;font-family:Georgia,serif;font-size:38px;display:grid}.task-empty p:last-child{color:var(--muted);margin:13px 0 0;font-size:13px;line-height:1.6}.chat-panel{margin-top:18px;overflow:hidden}.chat-heading{border-bottom:1px solid var(--line);justify-content:space-between;align-items:center;gap:20px;min-height:78px;padding:17px 22px;display:flex}.chat-heading h2{letter-spacing:-.02em;margin:0;font-size:18px}.chat-heading>div:last-child{color:var(--quiet);align-items:center;gap:12px;font-size:10px;display:flex}.chat-read-state{color:var(--amber);background:#efba630f;border:1px solid #efba633b;border-radius:7px;padding:5px 8px}.chat-body{overscroll-behavior:contain;background:#090c1238;gap:11px;max-height:620px;padding:18px 20px;display:grid;overflow:auto}.chat-empty{color:var(--quiet);text-align:center;margin:0;padding:28px;font-size:11px}.load-older{border:1px solid var(--line);color:var(--muted);background:var(--surface);cursor:pointer;border-radius:7px;justify-self:center;padding:6px 10px;font-size:10px}.chat-message{border:1px solid var(--line);background:#111720;border-radius:11px;width:min(82%,900px);padding:12px 14px}.chat-message>div{justify-content:space-between;align-items:center;gap:12px;display:flex}.chat-message strong{color:#e3e9f2;letter-spacing:.04em;text-transform:uppercase;font-size:10px}.chat-message time,.chat-message small{color:var(--quiet);font-size:9px}.chat-message p{color:#cbd3df;white-space:pre-wrap;margin:7px 0 0;font-size:12px;line-height:1.6}.chat-message small{color:var(--amber);margin-top:7px;display:block}.chat-user{background:#75a7ff12;border-color:#75a7ff38;justify-self:end}.chat-assistant{justify-self:start}.chat-streaming{box-shadow:inset 2px 0 var(--accent);border-color:#64e0b542}.stream-indicator{color:var(--accent);font-size:9px}.chat-tool-live,.chat-tool-result{border:1px solid var(--line);width:-moz-fit-content;width:fit-content;max-width:82%;color:var(--quiet);background:#0d1219;border-radius:8px;align-items:center;gap:9px;padding:8px 11px;font-size:9px;display:flex}.chat-tool-live strong,.chat-tool-result strong{color:#bac4d2;font-family:ui-monospace,monospace;font-size:10px}.tool-spinner{border:1px solid var(--blue);border-right-color:#0000;border-radius:50%;width:7px;height:7px;animation:.8s linear infinite spin}.held-queue{border-top:1px solid var(--line);background:#efba6309;padding:12px 16px}.held-queue-heading{justify-content:space-between;align-items:center;gap:12px;margin-bottom:8px;display:flex}.held-queue-heading strong{color:#dce3ed;letter-spacing:.04em;text-transform:uppercase;font-size:10px}.held-queue-heading span{color:var(--amber);font-size:9px}.held-queue ol{gap:7px;max-height:260px;margin:0;padding:0;list-style:none;display:grid;overflow:auto}.held-queue li{border:1px solid var(--line);background:#0d1219;border-radius:8px;grid-template-columns:minmax(0,1fr) auto;gap:7px 12px;padding:9px 10px;display:grid}.held-queue li[data-state=quarantined]{border-color:#efba6340}.held-queue p{color:#c4cdd9;text-overflow:ellipsis;white-space:nowrap;margin:0;font-size:10px;line-height:1.45;overflow:hidden}.held-queue textarea{resize:vertical;border:1px solid var(--line);color:#dce3ed;background:#090d13;border-radius:7px;min-width:0;padding:8px 9px;font-size:10px}.held-queue li>div{align-items:start;gap:5px;display:flex}.held-queue button{border:1px solid var(--line);color:var(--muted);background:var(--surface);cursor:pointer;border-radius:6px;padding:4px 7px;font-size:9px}.held-queue button:disabled{cursor:not-allowed;opacity:.45}.held-queue small{color:var(--amber);grid-column:1/-1;font-size:8px}.chat-composer-preview{border-top:1px solid var(--line);background:#10151d;grid-template-columns:minmax(0,1fr) auto;gap:10px;padding:14px 16px;display:grid}.attachment-list{flex-wrap:wrap;grid-column:1/-1;gap:7px;margin:0;padding:0;list-style:none;display:flex}.attachment-list li{background:#75a7ff0f;border:1px solid #75a7ff38;border-radius:8px;align-items:center;gap:8px;max-width:260px;padding:7px 8px;display:flex}.attachment-list span{flex-direction:column;min-width:0;display:flex}.attachment-list strong{color:#dce3ed;text-overflow:ellipsis;white-space:nowrap;font-size:9px;overflow:hidden}.attachment-list small{color:var(--quiet);font-size:8px}.attachment-list button{min-width:0;padding:3px 6px;font-size:8px}.chat-composer-preview textarea{resize:none;border:1px solid var(--line);color:var(--quiet);background:#0b0f15;border-radius:9px;padding:10px 12px;font-size:11px}.chat-composer-preview button{border:1px solid var(--line);min-width:76px;color:var(--quiet);background:var(--surface-strong);border-radius:9px}.composer-actions{grid-template-columns:repeat(2,minmax(76px,auto));gap:6px;display:grid}.composer-actions .interrupt-send{color:var(--amber);grid-column:1/-1}.attachment-picker{border:1px solid var(--line);width:-moz-fit-content;width:fit-content;color:var(--blue);cursor:pointer;border-radius:8px;align-items:center;padding:6px 9px;font-size:9px;display:inline-flex}.attachment-picker.disabled{cursor:not-allowed;opacity:.45}.attachment-picker input{opacity:0;width:1px;height:1px;position:absolute;overflow:hidden}.chat-composer-preview>small{color:var(--muted);grid-column:1/-1;font-size:9px}@keyframes spin{to{transform:rotate(360deg)}}.source-workspace{margin-top:0;overflow:hidden}.source-heading{border-bottom:1px solid var(--line);justify-content:space-between;align-items:center;gap:18px;min-height:82px;padding:18px 22px;display:flex}.source-heading h2{letter-spacing:-.02em;margin:0;font-size:18px}.source-heading>span{color:var(--quiet);letter-spacing:.06em;text-transform:uppercase;font-size:10px;font-weight:700}.source-tabs{border-bottom:1px solid var(--line);background:#090c1252;min-width:0;padding:0 14px;display:flex}.source-tabs button{min-width:0;color:var(--muted);cursor:pointer;background:0 0;border:0;border-bottom:2px solid #0000;align-items:center;gap:9px;padding:14px 15px 12px;font-size:11px;display:flex}.source-tabs button:hover{color:#dce3ed}.source-tabs button[aria-selected=true]{border-bottom-color:var(--accent);color:var(--accent)}.source-tabs button b{min-width:20px;color:inherit;background:color-mix(in srgb, currentColor 10%, transparent);text-align:center;border-radius:8px;padding:2px 6px;font-size:9px}.source-body{grid-template-columns:minmax(248px,304px) minmax(0,1fr);min-height:480px;display:grid}.file-panel{border-right:1px solid var(--line);background:color-mix(in srgb, var(--bg) 72%, var(--surface));min-width:0}.file-panel-meta{border-bottom:1px solid var(--line);min-height:42px;color:var(--muted);justify-content:space-between;align-items:center;gap:14px;padding:0 14px;font-size:10px;display:flex}.file-panel-meta small{color:var(--quiet);letter-spacing:.08em;text-transform:uppercase;font-weight:800}.file-list{overscroll-behavior:contain;max-height:620px;overflow:auto}.file-group-label{min-height:28px;color:var(--quiet);align-items:center;gap:6px;padding:5px 10px 3px}.file-group-label svg{width:14px;height:14px}.file-group-label span{min-width:0;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:9px;font-weight:700}.file-row{width:100%;min-height:54px;color:inherit;cursor:pointer;text-align:left;background:0 0;border:0;border-radius:7px;grid-template-columns:26px minmax(0,1fr) auto;align-items:center;gap:9px;margin:0;padding:8px 9px;display:grid}.file-row:hover{background:#75a7ff0d}.file-row[data-selected=true]{box-shadow:inset 2px 0 var(--accent);background:#64e0b513}.file-status{border:1px solid var(--line-strong);width:23px;height:23px;color:var(--muted);border-radius:6px;place-items:center;font-family:ui-monospace,monospace;font-size:10px;font-weight:900;display:grid}.status-A,.status-U{color:var(--accent);border-color:#64e0b54d}.status-M,.status-R{color:var(--blue);border-color:#75a7ff4d}.status-D,.status-C{color:var(--red);border-color:#ff7f864d}.file-name{flex-direction:column;min-width:0;display:flex}.file-name strong{color:var(--text);text-overflow:ellipsis;white-space:nowrap;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;font-weight:600;overflow:hidden}.file-name small,.file-provenance,.file-evidence{color:var(--quiet);text-overflow:ellipsis;white-space:nowrap;font-size:9px;overflow:hidden}.file-provenance,.file-evidence{display:none}.file-stats{color:var(--muted);white-space:nowrap;font-family:ui-monospace,monospace;font-size:9px}.files-empty,.diff-empty{min-height:220px;color:var(--quiet);text-align:center;place-items:center;padding:28px;font-size:12px;display:grid}.diff-empty{background:repeating-linear-gradient(-45deg,#0000,#0000 13px,#ffffff03 13px 14px);min-height:100%}.error-state{color:var(--red)}.diff-viewer{background:var(--surface);min-width:0;overflow:auto}.diff-toolbar{border-bottom:1px solid var(--line);background:var(--surface);justify-content:space-between;align-items:center;gap:15px;min-height:55px;padding:9px 13px;display:flex}.diff-toolbar>div:first-child{align-items:center;gap:10px;min-width:0;display:flex}.diff-actions{align-items:center;gap:10px;display:flex}.review-state{color:var(--quiet);align-items:center;gap:8px;font-size:9px;display:flex}.review-state>span{white-space:nowrap}.review-state button{border:1px solid var(--line);color:#d8e1ee;background:#18202b;border-radius:7px;min-height:28px;padding:0 9px;font-size:9px}.review-state button:disabled{cursor:not-allowed;opacity:.45}.review-reviewed>span{color:var(--accent)}.review-stale>span{color:var(--amber)}.source-mutation-preview{color:#aeb9c8;align-items:center;gap:8px;max-width:290px;font-size:9px;display:flex}.source-mutation-preview>span{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.source-mutation-preview button{color:#b8ead5;background:#42c2921a;border:1px solid #42c29266;border-radius:7px;flex:none;min-height:28px;padding:0 10px;font-size:9px;font-weight:750}.source-mutation-preview button:disabled{cursor:not-allowed;opacity:.45}.source-revert-preview{color:#d3b29a;align-items:center;gap:8px;max-width:290px;font-size:9px;display:flex}.source-revert-preview>span{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.source-revert-preview button,.hunk-header .revert-action{color:#f3b4b8;background:#ff7f8617;border:1px solid #ff7f8661;border-radius:7px;flex:none;min-height:28px;padding:0 10px;font-size:9px;font-weight:750}.source-revert-preview button:disabled{cursor:not-allowed;opacity:.45}.source-open-action button{color:#c2d5f8;white-space:nowrap;background:#75a7ff1a;border:1px solid #75a7ff61;border-radius:7px;min-height:28px;padding:0 10px;font-size:9px;font-weight:750}.source-open-action button:disabled{cursor:wait;opacity:.55}.source-open-status{color:#bfd1f2;background:#75a7ff14;border-bottom:1px solid #75a7ff38;padding:8px 14px;font-size:10px}.commit-summary-panel{border-bottom:1px solid var(--line);background:linear-gradient(120deg,#42c29214,#75a7ff0d);grid-template-columns:minmax(230px,.8fr) minmax(300px,1.5fr);align-items:start;gap:18px;padding:14px 16px;display:grid}.commit-summary-panel h3{color:#edf2f7;margin:1px 0 4px;font-size:14px}.commit-summary-panel>div>span{color:var(--quiet);font-size:9px}.commit-summary-panel>button{color:#b8ead5;background:#42c2921a;border:1px solid #42c29266;border-radius:8px;justify-self:start;min-height:32px;padding:0 12px;font-size:10px;font-weight:750}.commit-summary-output{background:#070a0f8c;border:1px solid #75a7ff33;border-radius:9px;gap:5px;padding:10px 12px;display:grid}.commit-summary-output>strong{color:#edf2f7;font:700 12px ui-monospace,monospace}.commit-summary-output>code{color:#aeb9c8;white-space:pre-wrap;font-size:9px}.commit-summary-output>div{flex-wrap:wrap;gap:7px;margin-top:4px;display:flex}.commit-summary-output button{border:1px solid var(--line);color:#d8e1ee;background:#18202b;border-radius:7px;min-height:28px;padding:0 10px;font-size:9px}.commit-summary-output button:last-child{color:#efd19e;background:#ecb35b17;border-color:#ecb35b59}.commit-summary-status{color:#bfd1f2;grid-column:1/-1;margin:0;font-size:10px}.model-summary-dialog{border-color:#ecb35b57}.review-error{color:#f2b0b4;background:#ff687414;border-bottom:1px solid #ff68743d;padding:8px 14px;font-size:10px}.source-revert-error{color:#f2b0b4;background:#ff687414;border-top:1px solid #ff68743d;padding:9px 14px;font-size:10px}.diff-toolbar strong{color:#e3e9f2;text-overflow:ellipsis;white-space:nowrap;font-family:ui-monospace,monospace;font-size:11px;overflow:hidden}.access-badge{text-transform:uppercase;font-size:9px;font-weight:800}.mode-switch{border:1px solid var(--line);background:var(--bg);border-radius:7px;padding:2px;display:flex}.mode-switch button{color:var(--quiet);cursor:pointer;background:0 0;border:0;border-radius:5px;padding:5px 8px;font-size:9px}.mode-switch button[aria-pressed=true]{color:#e5ebf4;background:var(--surface-strong)}.mode-switch .MuiToggleButton-root.Mui-selected{color:#eef3f8;background:#293241;box-shadow:inset 0 0 0 1px #75a7ff4d}.diff-context{border-bottom:1px solid var(--line);min-height:37px;color:var(--quiet);align-items:center;gap:14px;padding:0 14px;font-size:9px;display:flex}.diff-context .additions{color:var(--accent);margin-left:auto}.diff-context .deletions{color:var(--red)}.diff-hunk{min-width:0;overflow:auto}.hunk-header{color:var(--blue);background:#75a7ff12;border-bottom:1px solid #75a7ff24;justify-content:space-between;align-items:center;gap:12px;padding:6px 12px;font-family:ui-monospace,monospace;font-size:9px;display:flex;position:sticky;left:0}.hunk-header button{color:#bcd0f5;background:#75a7ff1a;border:1px solid #75a7ff57;border-radius:6px;min-height:25px;padding:0 9px;font:700 9px Inter,sans-serif}.hunk-header button:disabled{cursor:not-allowed;opacity:.45}.hunk-actions{align-items:center;gap:6px;display:inline-flex}.dialog-backdrop{z-index:80;-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);background:#030508c2;place-items:center;padding:20px;display:grid;position:fixed;inset:0}.confirm-dialog{background:#151a23;border:1px solid #ff7f8652;border-radius:15px;width:min(520px,100%);padding:24px;box-shadow:0 30px 100px #00000094}.confirm-dialog h3{color:#f1f4f8;margin:0;font-size:21px}.confirm-dialog>p:not(.section-kicker){color:#cbd3de;margin:14px 0;font-size:12px;line-height:1.65}.confirm-dialog code{color:#e6bdc0}.revert-diff{border:1px solid var(--line);background:#0c1016;border-radius:9px;max-height:min(300px,42vh);margin:14px 0;overflow:auto}.revert-diff section>div{color:#9bb9ec;background:#182130;padding:6px 9px;font:9px ui-monospace,monospace;position:sticky;top:0}.revert-line{white-space:pre-wrap;min-height:19px;padding:2px 9px;font:10px/1.5 ui-monospace,monospace;display:block;color:#b9c3d0!important}.revert-line b{width:14px;color:inherit;display:inline-block}.revert-added{background:#42c29214;color:#a7e1c9!important}.revert-deleted{background:#ff687414;color:#efafb3!important}.revert-meta{color:#8b96a8!important}.revert-safety{background:#efba630f;border:1px solid #efba6338;border-radius:9px;gap:5px;padding:12px;display:grid}.revert-safety strong{color:#efd19e;font-size:11px}.revert-safety span{color:#a99c87;font-size:10px}.confirm-actions{justify-content:flex-end;gap:9px;margin-top:20px;display:flex}.confirm-actions button{border:1px solid var(--line-strong);color:#cbd3df;background:#1b222d;border-radius:8px;min-height:34px;padding:0 14px;font-size:11px;font-weight:750}.confirm-actions .danger-action{color:#ffd2d5;background:#ff7f8621;border-color:#ff7f866b}.confirm-actions button:disabled{cursor:not-allowed;opacity:.48}.diff-line{grid-template-columns:42px 42px 18px minmax(500px,1fr);min-width:max-content;min-height:21px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:10px;line-height:21px;display:grid}.diff-line>span{color:#566173;text-align:right;-webkit-user-select:none;user-select:none;padding:0 7px}.diff-line b{color:var(--quiet);text-align:center;-webkit-user-select:none;user-select:none}.diff-line code,.split-line code{color:#bdc6d3;white-space:pre;padding:0 8px}.syntax-keyword{color:#c8a5ff;font-weight:650}.syntax-string{color:#a8d98b}.syntax-number{color:#efbd78}.syntax-comment{color:#778294;font-style:italic}.syntax-literal{color:#81b7ff}.syntax-property{color:#8ed8cf}.diff-added{background:#42c2921a}.diff-added code,.diff-added b{color:#a7e6ca}.diff-deleted{background:#ff68741a}.diff-deleted code,.diff-deleted b{color:#f2b0b4}.split-line{border-bottom:1px solid #242c394d;grid-template-columns:42px minmax(350px,1fr) 42px minmax(350px,1fr);min-width:860px;min-height:21px;font-family:ui-monospace,monospace;font-size:10px;line-height:21px;display:grid}.split-line>span{color:#566173;text-align:right;border-right:1px solid #242c39b3;padding:0 7px}.split-line code:nth-child(2){border-right:1px solid var(--line)}.unchanged-region{border-block:1px solid var(--line);color:var(--quiet);text-align:center;background:#10151d;padding:7px 12px;font-size:9px}.diff-fallback{border:1px solid var(--line);color:var(--muted);background:var(--surface);border-radius:9px;gap:6px;margin:18px;padding:16px;font-size:11px;display:grid}.diff-fallback strong{color:var(--amber);text-transform:uppercase;font-size:10px}.activity-panel{margin-top:18px;overflow:hidden}.panel-heading{border-bottom:1px solid var(--line);justify-content:space-between;align-items:center;gap:18px;min-height:82px;padding:18px 22px;display:flex}.panel-heading h2{letter-spacing:-.02em;margin:0;font-size:18px}.panel-heading>div:last-child{color:var(--quiet);align-items:center;gap:14px;font-size:10px;display:flex}.running-count{color:var(--blue)}.activity-list{display:grid}.activity-row{border-bottom:1px solid var(--line);background:#090c1233;grid-template-columns:10px minmax(0,1fr) 150px;align-items:start;gap:14px;padding:15px 18px;display:grid}.activity-row:last-child{border-bottom:0}.activity-state{width:7px;height:7px;box-shadow:0 0 0 4px color-mix(in srgb, currentColor 10%, transparent);background:currentColor;border-radius:50%;margin-top:5px}.activity-copy{min-width:0}.activity-copy>div{align-items:center;gap:9px;display:flex}.activity-copy strong{color:#dce3ed;font-size:12px}.activity-copy>div>span{border:1px solid var(--line);color:var(--quiet);letter-spacing:.06em;text-transform:uppercase;border-radius:4px;padding:2px 5px;font-size:8px;font-weight:800}.activity-copy>p{color:var(--muted);text-overflow:ellipsis;white-space:nowrap;margin:4px 0 0;font-family:ui-monospace,monospace;font-size:10px;line-height:1.45;overflow:hidden}.activity-result{text-align:right;grid-template-columns:1fr auto;align-items:center;gap:4px 9px;display:grid}.activity-result strong{color:#c9d1dd;font-size:10px}.activity-result>span{color:var(--quiet);font-family:ui-monospace,monospace;font-size:9px}.activity-result button{color:var(--blue);cursor:pointer;background:0 0;border:0;grid-column:1/-1;justify-self:end;padding:2px 0;font-size:9px}.log-preview{border:1px solid var(--line);color:#aeb8c7;white-space:pre-wrap;background:#090d13;border-radius:7px;margin-top:10px;padding:10px 12px;font-family:ui-monospace,monospace;font-size:9px;line-height:1.55}.log-preview small{color:var(--amber);margin-top:5px;display:block}.activity-empty{min-height:150px;color:var(--quiet);place-items:center;padding:24px;font-size:11px;display:grid}.panel-footnote{border-top:1px solid var(--line);color:var(--quiet);text-align:center;padding:9px 18px;font-size:9px}.evidence-section{margin-top:34px}.section-title-row{justify-content:space-between;align-items:end;gap:24px;margin-bottom:14px;padding:0 3px;display:flex}.section-title-row h2{letter-spacing:-.025em;margin:0;font-size:20px}.section-title-row>span{max-width:440px;color:var(--muted);text-align:right;text-overflow:ellipsis;white-space:nowrap;font-size:10px;overflow:hidden}.evidence-grid{grid-template-columns:repeat(4,minmax(0,1fr));gap:12px;display:grid}.evidence-card{min-width:0;min-height:250px;padding:18px}.evidence-card h3{color:#e1e7f0;min-height:38px;margin:0 0 16px;font-size:14px;line-height:1.35}.evidence-list{margin:0}.evidence-list>div{border-bottom:1px solid var(--line);grid-template-columns:88px minmax(0,1fr);align-items:center;gap:8px;min-height:38px;display:grid}.evidence-list>div:last-child{border-bottom:0}.evidence-list dt,.budget-grid dt{color:var(--quiet);font-size:9px}.evidence-list dd{color:var(--muted);text-align:right;text-overflow:ellipsis;white-space:nowrap;margin:0;font-family:ui-monospace,monospace;font-size:9px;overflow:hidden}.context-meter{background:#28303c;border-radius:4px;height:4px;margin:-3px 0 12px;overflow:hidden}.context-meter span{border-radius:inherit;background:linear-gradient(90deg, var(--blue), var(--accent));height:100%;display:block}.budget-grid{grid-template-columns:repeat(3,1fr);gap:7px;margin:0;display:grid}.budget-grid>div{border:1px solid var(--line);background:#090c1259;border-radius:8px;padding:12px 8px}.budget-grid dt{margin-bottom:5px}.budget-grid dd{color:#dbe2ec;margin:0;font-family:ui-monospace,monospace;font-size:12px}.card-note,.card-empty{color:var(--quiet);margin:14px 0 0;font-size:10px;line-height:1.5}.handoff-copy>p{color:var(--muted);margin:0 0 13px;font-size:11px;line-height:1.55}.handoff-blocker,.next-action{background:#ff7f860d;border:1px solid #ff7f8638;border-radius:7px;gap:4px;margin-top:9px;padding:9px 10px;display:grid}.next-action{background:#64e0b50d;border-color:#64e0b533}.handoff-blocker strong,.next-action strong{color:var(--red);letter-spacing:.08em;text-transform:uppercase;font-size:8px}.next-action strong{color:var(--accent)}.handoff-blocker span,.next-action span{color:#b7c0ce;font-size:10px;line-height:1.4}.loading-shell{width:min(560px,100% - 40px);margin:0 auto;padding-top:22vh}.loading-shell .brand-mark{border-radius:14px;width:48px;height:48px;margin-bottom:25px;font-size:29px}.loading-shell h1{color:#f0f3f8;letter-spacing:-.045em;margin:0;font-size:clamp(30px,6vw,50px);line-height:1.05}.loading-shell>p:not(.section-kicker){max-width:460px;color:var(--muted);margin:18px 0 22px;font-size:14px;line-height:1.6}.session-options{grid-template-columns:minmax(220px,.75fr) minmax(420px,1.25fr);gap:22px;padding:20px;display:grid}.session-options-copy h2{color:#edf1f7;margin:2px 0 8px;font-size:16px}.session-options-copy>p:last-child{max-width:520px;color:var(--quiet);margin:0;font-size:10px;line-height:1.55}.session-options-controls{grid-template-columns:minmax(0,1.4fr) minmax(130px,.6fr);align-items:end;gap:10px;display:grid}.session-options-controls>label:not(.effect-ack){color:var(--quiet);gap:5px;font-size:9px;display:grid}.session-options-controls select{border:1px solid var(--line);color:#e4e9f1;background:#121821;border-radius:7px;width:100%;min-height:36px;padding:0 10px}.effect-ack{color:#b9c1cd;grid-column:1/-1;align-items:flex-start;gap:8px;font-size:10px;line-height:1.4;display:flex}.effect-ack input{accent-color:var(--accent);margin-top:1px}.session-option-actions{grid-column:1/-1;gap:8px;display:flex}.session-option-actions button{border:1px solid var(--line);color:#dbe2ec;background:#1a222d;border-radius:7px;min-height:34px;padding:0 13px}.session-option-actions button:disabled{opacity:.42}.session-options-controls>small{color:var(--quiet);grid-column:1/-1;font-size:9px}.session-options-unavailable{color:var(--quiet);flex-direction:column;justify-content:center;gap:5px;font-size:10px;display:flex}.session-options-unavailable strong{color:#d7dde7;font-size:12px}.lifecycle-panel{grid-template-columns:minmax(260px,1fr) minmax(150px,auto) minmax(360px,auto);align-items:center;gap:20px;margin-top:18px;padding:18px 20px;display:grid}.lifecycle-copy h2{color:#edf1f7;margin:2px 0 7px;font-size:16px}.lifecycle-copy>p:last-child{max-width:620px;color:var(--quiet);margin:0;font-size:10px;line-height:1.55}.lifecycle-state{border-left:1px solid var(--line);gap:4px;min-width:150px;padding-left:18px;display:grid}.lifecycle-state>span{color:var(--quiet);text-transform:uppercase;font-size:9px;font-weight:800}.lifecycle-state strong{color:#e5eaf2;font-size:12px}.lifecycle-state small{color:var(--quiet);font-size:9px}.lifecycle-actions{flex-wrap:wrap;justify-content:flex-end;gap:8px;display:flex}.lifecycle-actions button{cursor:pointer;border-radius:7px;min-height:36px;padding:0 13px;font-size:10px;font-weight:750}.lifecycle-actions button:disabled{cursor:not-allowed;opacity:.4}.primary-action{color:#07130f;background:var(--accent);border:1px solid #3cae88}.lifecycle-result{border-radius:7px;grid-column:1/-1;margin:-6px 0 0;padding:9px 11px;font-size:10px;line-height:1.45}.lifecycle-result.result-ok{color:#bcebd9;background:#64e0b512}.lifecycle-result.result-warning{color:#efd09b;background:#efba6314}.approval-stack{gap:12px;display:grid}.approval-card{background:linear-gradient(135deg,#412518eb,#19181bf5);border:1px solid #70462e;border-radius:13px;padding:20px;overflow:hidden;box-shadow:0 16px 42px #00000038}.approval-card.risk-critical,.approval-card.risk-high{border-color:#9d4d3f}.approval-card>header,.approval-card>footer{justify-content:space-between;align-items:center;gap:12px;display:flex}.approval-card h2{color:#fff1e7;margin:2px 0 0;font-size:18px}.approval-risk{color:#ffbd91;text-transform:uppercase;border:1px solid #ffad7c59;border-radius:999px;padding:5px 9px;font-size:10px;font-weight:800}.approval-reason{color:#f1d8c7;margin:13px 0;font-size:12px;line-height:1.55}.approval-preview{color:#f3e7df;white-space:pre-wrap;word-break:break-word;background:#141316;border:1px solid #ffffff17;border-radius:8px;max-height:180px;margin:0 0 14px;padding:12px;font:11px/1.55 ui-monospace,SFMono-Regular,Menlo,monospace;overflow:auto}.approval-facts{grid-template-columns:repeat(3,minmax(0,1fr));gap:8px;margin:0 0 13px;display:grid}.approval-facts>div{background:#ffffff09;border-radius:7px;min-width:0;padding:9px}.approval-facts dt{color:#bca393;text-transform:uppercase;font-size:9px;font-weight:800}.approval-facts dd{color:#f0e7e1;text-overflow:ellipsis;margin:4px 0 0;font-size:10px;overflow:hidden}.approval-targets,.approval-consequences{color:#d9c8bc;font-size:10px;line-height:1.55}.approval-targets ul{margin:6px 0 12px;padding-left:18px}.approval-consequences{grid-template-columns:1fr 1fr;gap:10px;margin:12px 0;display:grid}.approval-consequences p{background:#ffffff09;border-radius:7px;margin:0;padding:10px}.approval-card>footer{justify-content:flex-end;padding-top:4px}.approval-card button{border-radius:7px;min-height:36px;padding:0 14px;font-weight:750}.secondary-action{border:1px solid var(--line);color:#dbe2ec;background:#1a222d}.danger-action{color:#fff5ee;background:#8f3e31;border:1px solid #c56b52}.approval-card button:disabled{cursor:wait;opacity:.48}.task-run-index,.release-monitor{margin-top:18px;overflow:hidden}.release-monitor .source-heading button{border:1px solid var(--line);color:var(--text);background:var(--surface-2);border-radius:8px;padding:8px 12px}.release-monitor .source-heading button:disabled{opacity:.55}.release-monitor-grid{border-top:1px solid var(--line);grid-template-columns:minmax(0,1.15fr) minmax(320px,.85fr);display:grid}.release-monitor-column+.release-monitor-column{border-left:1px solid var(--line)}.release-monitor-column>header{border-bottom:1px solid var(--line);justify-content:space-between;align-items:center;gap:14px;padding:16px 20px;display:flex}.release-monitor-column h3{margin:2px 0 0}.benchmark-run-card{border-bottom:1px solid var(--line);padding:14px 20px}.benchmark-run-card>div:first-child{justify-content:space-between;align-items:center;gap:12px;display:flex}.benchmark-run-card small{color:var(--muted);margin-top:4px;display:block}.benchmark-run-facts{color:var(--muted);flex-wrap:wrap;gap:8px 18px;margin-top:9px;font-size:12px;display:flex}.release-readiness-facts{margin:0;padding:4px 20px 12px}.release-readiness-facts>div{border-bottom:1px solid var(--line);justify-content:space-between;align-items:center;gap:16px;padding:10px 0;display:flex}.release-readiness-facts dt{color:var(--muted)}.release-readiness-facts dd{text-align:right;margin:0}.release-blockers{border:1px solid color-mix(in srgb, var(--warning) 35%, var(--line));background:color-mix(in srgb, var(--warning) 7%, transparent);border-radius:8px;margin:0 20px 14px;padding:12px 14px}.release-blockers ul{color:var(--muted);margin:8px 0 0;padding-left:18px}.release-blockers li+li{margin-top:5px}.task-index-controls{border-bottom:1px solid var(--line);gap:8px;padding:0 20px 16px;display:flex}.task-index-controls button{border:1px solid var(--line);color:var(--muted);background:var(--surface-2);border-radius:999px;padding:7px 12px}.task-index-controls button[aria-pressed=true]{color:var(--text);border-color:var(--accent);background:color-mix(in srgb, var(--accent) 15%, var(--surface-2))}.task-run-list{display:grid}.task-run-card{border-bottom:1px solid var(--line);grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:18px;padding:15px 20px;display:grid}.task-run-card[data-active=true]{box-shadow:inset 3px 0 var(--accent);background:color-mix(in srgb, var(--accent) 5%, transparent)}.task-run-card[data-selected=true]{outline:1px solid var(--accent);outline-offset:-1px}.task-run-main{min-width:0}.task-run-main>div{align-items:center;gap:9px;display:flex}.task-run-main strong,.task-run-main small{text-overflow:ellipsis;white-space:nowrap;display:block;overflow:hidden}.task-run-main small,.task-run-meta{color:var(--muted)}.active-run-tag{border:1px solid var(--accent);color:var(--accent);border-radius:999px;padding:2px 7px;font-size:11px}.task-run-meta{justify-content:flex-end;align-items:center;gap:12px;font-size:12px;display:flex}.task-index-empty,.task-index-warning{color:var(--muted);margin:0;padding:18px 20px}.task-index-warning{border-top:1px solid var(--line);color:var(--warning)}.task-run-meta button{border:1px solid var(--line);color:var(--text);background:var(--surface-2);border-radius:7px;padding:6px 9px}.task-run-meta button[aria-pressed=true]{border-color:var(--accent);color:var(--accent)}.task-timeline{border-top:1px solid var(--line)}.task-timeline>header{border-bottom:1px solid var(--line);justify-content:space-between;align-items:center;gap:16px;padding:18px 20px;display:flex}.task-timeline h3{margin:2px 0 0}.timeline-continuity{color:var(--muted);border-bottom:1px solid var(--line);flex-wrap:wrap;gap:10px 24px;padding:12px 20px;display:flex}.timeline-events{margin:0;padding:0;list-style:none}.timeline-events li{border-bottom:1px solid var(--line);grid-template-columns:12px minmax(0,1fr) auto;align-items:start;gap:12px;padding:13px 20px;display:grid}.timeline-events small,.timeline-events p{color:var(--muted);margin:3px 0 0;display:block}.timeline-dot{background:var(--muted);border-radius:999px;width:9px;height:9px;margin-top:5px}.timeline-dot.tone-positive{background:var(--positive)}.timeline-dot.tone-warning{background:var(--warning)}.timeline-dot.tone-danger{background:var(--danger)}.timeline-dot.tone-info{background:var(--accent)}.recovery-summary{grid-template-columns:repeat(4,minmax(0,1fr));display:grid}.retained-content-note{color:var(--muted);border-bottom:1px solid var(--line);margin:0;padding:10px 20px;font-size:12px}.next-action-card{border-bottom:1px solid var(--line);background:color-mix(in srgb, var(--accent) 7%, transparent);grid-template-columns:minmax(180px,.45fr) 1fr auto;align-items:center;gap:18px;padding:16px 20px;display:grid}.next-action-card span,.next-action-card strong{display:block}.next-action-card span,.next-action-card p,.next-action-card small{color:var(--muted)}.next-action-card p{margin:0}.handoff-summary{grid-template-columns:repeat(4,minmax(0,1fr));display:grid}.workspace-stack>.surface,.workspace-view>.surface,.workspace-stack>.evidence-section,.workspace-view>.evidence-section{margin-top:0}:root[data-piagent-color-mode=light] .task-heading h2,:root[data-piagent-color-mode=light] .task-empty h2,:root[data-piagent-color-mode=light] .criterion-copy p,:root[data-piagent-color-mode=light] .compact-panel dd,:root[data-piagent-color-mode=light] .chat-heading h2,:root[data-piagent-color-mode=light] .chat-message strong,:root[data-piagent-color-mode=light] .chat-message p,:root[data-piagent-color-mode=light] .attachment-list strong,:root[data-piagent-color-mode=light] .diff-toolbar strong,:root[data-piagent-color-mode=light] .activity-copy strong,:root[data-piagent-color-mode=light] .activity-result strong,:root[data-piagent-color-mode=light] .evidence-card h3,:root[data-piagent-color-mode=light] .budget-grid dd,:root[data-piagent-color-mode=light] .session-options-copy h2,:root[data-piagent-color-mode=light] .session-options-unavailable strong,:root[data-piagent-color-mode=light] .lifecycle-copy h2,:root[data-piagent-color-mode=light] .lifecycle-state strong{color:var(--text)}:root[data-piagent-color-mode=light] .chat-body,:root[data-piagent-color-mode=light] .activity-row{background:#62740006}:root[data-piagent-color-mode=light] .source-tabs{background:#f8f9f4}:root[data-piagent-color-mode=light] .source-tabs button:hover{color:var(--text)}:root[data-piagent-color-mode=light] .chat-message,:root[data-piagent-color-mode=light] .chat-tool-live,:root[data-piagent-color-mode=light] .chat-tool-result,:root[data-piagent-color-mode=light] .held-queue li,:root[data-piagent-color-mode=light] .log-preview{color:var(--text);background:#fafbf7}:root[data-piagent-color-mode=light] .chat-user{background:#506db712}:root[data-piagent-color-mode=light] .chat-composer-preview,:root[data-piagent-color-mode=light] .unchanged-region{background:#f7f8f3}:root[data-piagent-color-mode=light] .chat-composer-preview textarea,:root[data-piagent-color-mode=light] .session-options-controls select{color:var(--text);background:#fff}:root[data-piagent-color-mode=light] .review-state button,:root[data-piagent-color-mode=light] .commit-summary-output button,:root[data-piagent-color-mode=light] .confirm-actions button,:root[data-piagent-color-mode=light] .session-option-actions button{color:var(--text);background:var(--surface-strong)}:root[data-piagent-color-mode=light] .commit-summary-panel h3,:root[data-piagent-color-mode=light] .commit-summary-output>strong{color:var(--text)}:root[data-piagent-color-mode=light] .commit-summary-output{background:#ffffffbf}:root[data-piagent-color-mode=light] .diff-line code,:root[data-piagent-color-mode=light] .split-line code{color:#3f443a}:root[data-piagent-color-mode=light] .diff-line>span,:root[data-piagent-color-mode=light] .split-line>span{color:#92988d}:root[data-piagent-color-mode=light] .syntax-keyword{color:#6e3bb2}:root[data-piagent-color-mode=light] .syntax-string{color:#317040}:root[data-piagent-color-mode=light] .syntax-number{color:#9b5a12}:root[data-piagent-color-mode=light] .syntax-comment{color:#7b8176}:root[data-piagent-color-mode=light] .syntax-literal{color:#235fa8}:root[data-piagent-color-mode=light] .syntax-property{color:#176f69}:root[data-piagent-color-mode=light] .mode-switch .MuiToggleButton-root.Mui-selected{color:#1c2018;background:#e8ecdf}:root[data-piagent-color-mode=light] .diff-added{background:#14775f1a}:root[data-piagent-color-mode=light] .diff-added code,:root[data-piagent-color-mode=light] .diff-added b{color:#0a654e}:root[data-piagent-color-mode=light] .diff-deleted{background:#c43f4c1a}:root[data-piagent-color-mode=light] .diff-deleted code,:root[data-piagent-color-mode=light] .diff-deleted b{color:#a02734}:root[data-piagent-color-mode=light] .confirm-dialog{color:var(--text);background:#fff}:root[data-piagent-color-mode=light] .confirm-dialog h3,:root[data-piagent-color-mode=light] .confirm-dialog>p:not(.section-kicker){color:var(--text)}:root[data-piagent-color-mode=light] .revert-diff{background:#f8f9f4}:root[data-piagent-color-mode=light] .approval-card{color:#3f2b20;background:linear-gradient(135deg,#fff7ef,#fff);border-color:#d7b18f}:root[data-piagent-color-mode=light] .approval-card h2,:root[data-piagent-color-mode=light] .approval-preview,:root[data-piagent-color-mode=light] .approval-facts dd{color:#3f2b20}:root[data-piagent-color-mode=light] .approval-preview{background:#fffaf5}:root[data-piagent-color-mode=light] .approval-reason,:root[data-piagent-color-mode=light] .approval-targets,:root[data-piagent-color-mode=light] .approval-consequences{color:#6d4d3c}@media (max-width:1080px){.lifecycle-panel{grid-template-columns:1fr auto}.lifecycle-actions{grid-column:1/-1;justify-content:flex-start}.lifecycle-result{grid-column:1/-1}.runtime-strip{grid-template-columns:repeat(3,1fr)}.runtime-strip>div:nth-child(3){border-right:0}.runtime-strip>div:nth-child(-n+3){border-bottom:1px solid var(--line)}.content-grid{grid-template-columns:1fr}.context-rail,.evidence-grid{grid-template-columns:repeat(2,1fr)}}@media (max-width:899px){.source-body{grid-template-columns:1fr}.file-panel{border-right:0;border-bottom:1px solid var(--line)}.file-list{max-height:300px}.diff-empty{min-height:300px}}@media (max-width:720px){.release-monitor-grid{grid-template-columns:1fr}.release-monitor-column+.release-monitor-column{border-left:0;border-top:1px solid var(--line)}.recovery-summary{grid-template-columns:repeat(2,minmax(0,1fr))}.handoff-summary,.next-action-card,.commit-summary-panel,.approval-facts,.approval-consequences{grid-template-columns:1fr}.approval-card>header,.approval-card>footer{flex-direction:column;align-items:flex-start}.approval-card>footer button{width:100%}.session-options,.session-options-controls{grid-template-columns:1fr}.effect-ack,.session-option-actions,.session-options-controls>small{grid-column:1}.lifecycle-panel{grid-template-columns:1fr;align-items:stretch}.lifecycle-state{border-left:0;padding:0}.lifecycle-actions{grid-column:1}.lifecycle-actions button{flex:150px}.lifecycle-result{grid-column:1}.topbar{grid-template-columns:1fr auto;min-height:60px;padding:0 16px}.session-title,.read-only-badge{display:none}.workspace{width:min(100% - 24px,1460px);padding-top:12px}.runtime-strip{grid-template-columns:repeat(2,1fr)}.runtime-strip>div{border-bottom:1px solid var(--line);min-height:62px;padding:11px}.runtime-strip>div:nth-child(2n){border-right:0}.runtime-strip>div:nth-last-child(-n+2){border-bottom:0}.task-dashboard{padding:19px 14px}.chat-heading{flex-direction:column;align-items:flex-start;padding:15px}.chat-heading>div:last-child{flex-direction:column;align-items:flex-start}.chat-body{padding:13px}.chat-message{width:94%}.chat-composer-preview,.task-heading{grid-template-columns:1fr}.progress-number{text-align:left;align-items:baseline;gap:10px;display:flex}.progress-number strong{font-size:25px}.progress-track{margin:18px 0 24px}.criterion-card{grid-template-columns:32px minmax(0,1fr);gap:10px}.criterion-card>.status-pill{grid-column:2}.criterion-copy>div{flex-direction:column;align-items:flex-start;gap:5px}.context-rail{grid-template-columns:1fr}.task-empty{grid-template-columns:1fr;min-height:340px;padding:28px 22px}.source-tabs{padding:0 8px;overflow-x:auto}.source-tabs button{flex:none}.source-heading{padding:16px}.source-heading>span,.file-provenance,.file-evidence{display:none}.file-row{grid-template-columns:26px minmax(0,1fr) auto}.diff-toolbar{flex-direction:column;align-items:flex-start}.panel-heading{flex-direction:column;align-items:flex-start;padding:16px}.activity-row{grid-template-columns:10px minmax(0,1fr)}.activity-result{text-align:left;grid-column:2;grid-template-columns:auto auto 1fr}.activity-result button{grid-column:auto;justify-self:end}.section-title-row{flex-direction:column;align-items:flex-start;gap:8px}.section-title-row>span{text-align:left;max-width:100%}.evidence-grid{grid-template-columns:1fr}.evidence-card{min-height:auto}.task-run-card{grid-template-columns:1fr;gap:9px}.task-run-meta{flex-wrap:wrap;justify-content:flex-start}}@media (prefers-reduced-motion:reduce){*,:before,:after{scroll-behavior:auto!important;transition:none!important;animation:none!important}}@media (forced-colors:active){.status-dot,.activity-state,.progress-track span,.context-meter span{forced-color-adjust:none;background:highlight}.file-row[data-selected=true],.source-tabs button[aria-selected=true]{outline:2px solid highlight}}
|