toolroll 0.9.93 → 0.9.95
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 +79 -0
- package/dist/activity-line.d.ts +7 -4
- package/dist/activity-line.js +9 -7
- package/dist/agent-fence.d.ts +10 -0
- package/dist/agent-fence.js +6 -3
- package/dist/approval-authority.d.ts +7 -1
- package/dist/approval-authority.js +11 -2
- package/dist/approval-password.d.ts +31 -0
- package/dist/approval-password.js +26 -0
- package/dist/assignment-status.d.ts +1 -1
- package/dist/assignment-ui.js +3 -1
- package/dist/assignment.js +5 -0
- package/dist/attest.d.ts +3 -0
- package/dist/attest.js +10 -0
- package/dist/boot-identity.d.ts +2 -1
- package/dist/boot-identity.js +6 -4
- package/dist/bounded-spawn.d.ts +57 -0
- package/dist/bounded-spawn.js +220 -0
- package/dist/browser/terminal.js +42 -42
- package/dist/browser/workspace.css +1 -1
- package/dist/browser/workspace.js +32 -32
- package/dist/browser-crew.js +2 -0
- package/dist/browser-shell.d.ts +1 -1
- package/dist/browser-shell.js +2 -2
- package/dist/browser-workspace.d.ts +50 -2
- package/dist/browser-workspace.js +3 -3
- package/dist/build-review.js +2 -0
- package/dist/builder-restart.d.ts +21 -0
- package/dist/builder-restart.js +50 -0
- package/dist/builder.d.ts +11 -2
- package/dist/builder.js +234 -101
- package/dist/card-catalog.d.ts +209 -0
- package/dist/card-catalog.js +133 -0
- package/dist/card-cli.d.ts +43 -0
- package/dist/card-cli.js +44 -0
- package/dist/card-render.d.ts +26 -0
- package/dist/card-render.js +223 -0
- package/dist/card-sources.d.ts +68 -0
- package/dist/card-sources.js +234 -0
- package/dist/chat-acceptance.js +1 -1
- package/dist/chat-actions.d.ts +106 -9
- package/dist/chat-actions.js +473 -46
- package/dist/chat-approval.d.ts +13 -0
- package/dist/chat-approval.js +39 -9
- package/dist/chat-card-part.d.ts +11 -0
- package/dist/chat-card-part.js +18 -0
- package/dist/chat-card.d.ts +46 -0
- package/dist/chat-card.js +99 -0
- package/dist/chat-channel.d.ts +2 -0
- package/dist/chat-channel.js +37 -14
- package/dist/chat-controls.d.ts +4 -0
- package/dist/chat-controls.js +4 -0
- package/dist/chat-decide.d.ts +10 -16
- package/dist/chat-decide.js +79 -62
- package/dist/chat-delivery.js +13 -4
- package/dist/chat-flow.d.ts +0 -27
- package/dist/chat-flow.js +31 -2
- package/dist/chat-quiet.js +4 -4
- package/dist/chat-task-actions.d.ts +3 -2
- package/dist/chat-task-actions.js +19 -5
- package/dist/chat-terms.d.ts +14 -2
- package/dist/chat-terms.js +35 -0
- package/dist/chat-voice.d.ts +1 -1
- package/dist/chat-voice.js +2 -2
- package/dist/check-facts.d.ts +3 -1
- package/dist/check-facts.js +57 -11
- package/dist/check-failure.d.ts +24 -3
- package/dist/check-failure.js +89 -13
- package/dist/check-levels-ui.d.ts +4 -0
- package/dist/check-levels-ui.js +10 -1
- package/dist/check-load.d.ts +85 -0
- package/dist/check-load.js +147 -0
- package/dist/check-only-standing.d.ts +36 -0
- package/dist/check-only-standing.js +39 -0
- package/dist/check-only.d.ts +15 -0
- package/dist/check-only.js +99 -0
- package/dist/check-repair.d.ts +47 -0
- package/dist/check-repair.js +240 -0
- package/dist/check-report.d.ts +2 -0
- package/dist/check-report.js +1 -1
- package/dist/check-stamp.d.ts +46 -0
- package/dist/check-stamp.js +93 -0
- package/dist/checkout-cleanup.d.ts +36 -5
- package/dist/checkout-cleanup.js +166 -14
- package/dist/claim.d.ts +6 -0
- package/dist/claim.js +13 -2
- package/dist/cli-help.d.ts +4 -1
- package/dist/cli-help.js +49 -30
- package/dist/cli.js +15 -1
- package/dist/code-extract.d.ts +44 -0
- package/dist/code-extract.js +88 -0
- package/dist/code-map-acts.d.ts +24 -0
- package/dist/code-map-acts.js +53 -0
- package/dist/code-map-admission.d.ts +52 -0
- package/dist/code-map-admission.js +295 -0
- package/dist/code-map-build.d.ts +63 -0
- package/dist/code-map-build.js +415 -0
- package/dist/code-map-cli.d.ts +153 -0
- package/dist/code-map-cli.js +103 -0
- package/dist/code-map-main.d.ts +1 -0
- package/dist/code-map-main.js +48 -0
- package/dist/code-map-read.d.ts +106 -0
- package/dist/code-map-read.js +171 -0
- package/dist/code-map-report.d.ts +37 -0
- package/dist/code-map-report.js +95 -0
- package/dist/code-map-shape.d.ts +172 -0
- package/dist/code-map-shape.js +327 -0
- package/dist/code-map-store.d.ts +136 -0
- package/dist/code-map-store.js +233 -0
- package/dist/coding-provider.d.ts +3 -1
- package/dist/coding-provider.js +12 -7
- package/dist/coding-update.js +3 -2
- package/dist/coding-workspace.d.ts +1 -1
- package/dist/coding-workspace.js +8 -8
- package/dist/console-chrome.js +2 -2
- package/dist/containment.d.ts +5 -0
- package/dist/containment.js +14 -1
- package/dist/contracts/chat-actions.d.ts +92 -0
- package/dist/contracts/chat-actions.js +20 -0
- package/dist/contracts/chat-card.d.ts +91 -0
- package/dist/contracts/chat-card.js +54 -0
- package/dist/contracts/chat-content.d.ts +191 -0
- package/dist/contracts/chat-content.js +4 -0
- package/dist/contracts/cli.d.ts +52 -0
- package/dist/contracts/cli.js +4 -0
- package/dist/contracts/console-api.d.ts +12 -3
- package/dist/contracts/console-api.js +12 -3
- package/dist/contracts/flow-propose.d.ts +8 -1
- package/dist/contracts/flow-propose.js +3 -1
- package/dist/contracts/flow-send.d.ts +126 -0
- package/dist/contracts/flow-send.js +3 -0
- package/dist/contracts/gateway-tools.d.ts +17 -0
- package/dist/contracts/gateway-tools.js +3 -1
- package/dist/contracts/lead-tools.d.ts +234 -0
- package/dist/contracts/lead-tools.js +25 -1
- package/dist/contracts/project-knowledge.d.ts +38 -5
- package/dist/contracts/project-knowledge.js +11 -5
- package/dist/contracts/project-memory.d.ts +3 -1
- package/dist/contracts/project-memory.js +3 -2
- package/dist/contracts/storage.d.ts +14 -0
- package/dist/contracts/storage.js +3 -1
- package/dist/contracts/store-json.d.ts +18 -0
- package/dist/contracts/youiyoux-request.d.ts +1 -1
- package/dist/contracts/youiyoux-request.js +1 -1
- package/dist/desktop-access.js +6 -2
- package/dist/discord-chat.js +14 -0
- package/dist/dispatch.d.ts +1 -1
- package/dist/dispatch.js +25 -16
- package/dist/dispose.d.ts +19 -0
- package/dist/dispose.js +55 -9
- package/dist/easy-start.js +2 -3
- package/dist/eval-oracle.js +4 -1
- package/dist/evals.js +2 -1
- package/dist/evidence-pair.d.ts +53 -0
- package/dist/evidence-pair.js +128 -0
- package/dist/exhaustion.d.ts +17 -4
- package/dist/exhaustion.js +44 -4
- package/dist/export.js +1 -1
- package/dist/failing-first.d.ts +31 -0
- package/dist/failing-first.js +44 -0
- package/dist/fallback-schema.d.ts +8 -0
- package/dist/fallback-schema.js +40 -0
- package/dist/fallback.d.ts +138 -0
- package/dist/fallback.js +605 -0
- package/dist/fast-start.d.ts +136 -0
- package/dist/fast-start.js +431 -0
- package/dist/fast-terms.d.ts +9 -0
- package/dist/fast-terms.js +23 -0
- package/dist/fenced-check-coverage.d.ts +62 -0
- package/dist/fenced-check-coverage.js +54 -0
- package/dist/findings.d.ts +224 -0
- package/dist/findings.js +449 -0
- package/dist/first-run.js +1 -1
- package/dist/flaky-ci.d.ts +31 -0
- package/dist/flaky-ci.js +149 -0
- package/dist/flaky-cli.d.ts +60 -0
- package/dist/flaky-cli.js +60 -0
- package/dist/flaky.d.ts +75 -9
- package/dist/flaky.js +255 -0
- package/dist/fleet-schema.d.ts +17 -0
- package/dist/fleet-schema.js +135 -0
- package/dist/flow-browser.js +2 -2
- package/dist/flow-card-task.js +3 -0
- package/dist/flow-code.d.ts +2 -2
- package/dist/flow-code.js +9 -5
- package/dist/flow-result-card.d.ts +22 -0
- package/dist/flow-result-card.js +55 -0
- package/dist/flow-send.js +12 -9
- package/dist/flow-steps.js +1 -1
- package/dist/flow-triggers.d.ts +1 -1
- package/dist/flow-triggers.js +2 -2
- package/dist/flow-unattended.d.ts +26 -0
- package/dist/flow-unattended.js +12 -3
- package/dist/flows-ui.js +10 -2
- package/dist/follow-through-brief.d.ts +12 -0
- package/dist/follow-through-brief.js +31 -0
- package/dist/follow-through-cli.d.ts +23 -0
- package/dist/follow-through-cli.js +153 -0
- package/dist/git-incident.d.ts +16 -0
- package/dist/git-incident.js +69 -6
- package/dist/github-import.d.ts +62 -0
- package/dist/github-import.js +146 -0
- package/dist/guides.js +15 -10
- package/dist/handover-note.d.ts +33 -0
- package/dist/handover-note.js +106 -0
- package/dist/invoke.d.ts +25 -2
- package/dist/invoke.js +157 -58
- package/dist/job-object-helper.ps1 +10 -3
- package/dist/keymap.d.ts +41 -0
- package/dist/keymap.js +51 -0
- package/dist/knowledge-cli.js +1 -1
- package/dist/knowledge-ui.d.ts +21 -4
- package/dist/knowledge-ui.js +75 -7
- package/dist/laptop-background.d.ts +16 -0
- package/dist/laptop-background.js +108 -2
- package/dist/lead-cli.js +26 -24
- package/dist/lead-context.js +1 -1
- package/dist/lead-contract.d.ts +1 -1
- package/dist/lead-contract.js +3 -2
- package/dist/lead-doors.d.ts +4 -0
- package/dist/lead-doors.js +2 -2
- package/dist/lead-progress.js +5 -0
- package/dist/lead-status.d.ts +2 -0
- package/dist/lead-status.js +6 -2
- package/dist/lead-tools.js +162 -34
- package/dist/lead.js +2 -2
- package/dist/lesson-notes.d.ts +81 -0
- package/dist/lesson-notes.js +336 -0
- package/dist/limits-ui.js +1 -1
- package/dist/local-owner.d.ts +1 -1
- package/dist/local-owner.js +16 -0
- package/dist/machine-check.d.ts +16 -0
- package/dist/machine-check.js +26 -0
- package/dist/machine-identity.d.ts +75 -0
- package/dist/machine-identity.js +170 -0
- package/dist/mcp-core.js +11 -1
- package/dist/memory-cli.d.ts +300 -1
- package/dist/memory-cli.js +62 -9
- package/dist/memory-pass.d.ts +16 -0
- package/dist/memory-pass.js +67 -3
- package/dist/memory-tidy.d.ts +181 -0
- package/dist/memory-tidy.js +523 -0
- package/dist/memory-use.d.ts +35 -0
- package/dist/memory-use.js +109 -0
- package/dist/merge-checklist.d.ts +55 -0
- package/dist/merge-checklist.js +182 -0
- package/dist/modes.d.ts +6 -6
- package/dist/modes.js +3 -1
- package/dist/needs-you.d.ts +31 -9
- package/dist/needs-you.js +110 -41
- package/dist/nightly-github.d.ts +2 -0
- package/dist/nightly-github.js +4 -4
- package/dist/operate-remote-arguments.js +7 -1
- package/dist/operate-remote.js +3 -1
- package/dist/operate.d.ts +6 -1
- package/dist/operate.js +565 -64
- package/dist/phase-routing.js +2 -2
- package/dist/plain-schema.d.ts +23 -0
- package/dist/plain-schema.js +94 -0
- package/dist/plain-words.d.ts +20 -0
- package/dist/plain-words.js +66 -0
- package/dist/plan-login.d.ts +14 -0
- package/dist/plan-login.js +58 -0
- package/dist/planner.d.ts +1 -1
- package/dist/planner.js +4 -2
- package/dist/process-liveness.d.ts +2 -0
- package/dist/process-liveness.js +61 -17
- package/dist/project-delete-ui.d.ts +19 -2
- package/dist/project-delete-ui.js +29 -1
- package/dist/project-delete.js +14 -1
- package/dist/project-knowledge.d.ts +18 -11
- package/dist/project-knowledge.js +39 -9
- package/dist/project-learning.d.ts +2 -1
- package/dist/project-learning.js +15 -45
- package/dist/project-memory.d.ts +5 -0
- package/dist/project-memory.js +22 -2
- package/dist/project-tools.d.ts +4 -4
- package/dist/project-tools.js +23 -7
- package/dist/proof.js +30 -5
- package/dist/protected-storage-look.d.ts +1 -0
- package/dist/protected-storage-look.js +13 -0
- package/dist/protected-storage.d.ts +70 -39
- package/dist/protected-storage.js +196 -147
- package/dist/provider-auth.js +1 -1
- package/dist/provider-capture.d.ts +34 -0
- package/dist/provider-capture.js +53 -0
- package/dist/publish.js +9 -1
- package/dist/pull-request-flow.js +5 -0
- package/dist/push.d.ts +18 -2
- package/dist/push.js +40 -16
- package/dist/release-adapters.d.ts +9 -0
- package/dist/release-adapters.js +76 -0
- package/dist/release-auto.js +13 -13
- package/dist/release-card.d.ts +1 -2
- package/dist/release-card.js +11 -0
- package/dist/release-carry.d.ts +7 -2
- package/dist/release-carry.js +49 -10
- package/dist/release-cli.d.ts +4 -0
- package/dist/release-cli.js +29 -22
- package/dist/release-coverage.js +1 -1
- package/dist/release-next.d.ts +48 -0
- package/dist/release-next.js +161 -0
- package/dist/release.d.ts +20 -1
- package/dist/release.js +85 -9
- package/dist/remote-destination.d.ts +36 -0
- package/dist/remote-destination.js +217 -0
- package/dist/remote-main.d.ts +48 -0
- package/dist/remote-main.js +84 -5
- package/dist/repository-context.d.ts +3 -2
- package/dist/repository-context.js +112 -70
- package/dist/restart-certification.d.ts +1 -0
- package/dist/restart-certification.js +8 -6
- package/dist/result-acts.d.ts +27 -19
- package/dist/result-acts.js +36 -25
- package/dist/result-follow-ups.d.ts +2 -2
- package/dist/result-follow-ups.js +18 -4
- package/dist/result-review.d.ts +1 -1
- package/dist/route-change-check.d.ts +4 -0
- package/dist/route-change-check.js +13 -0
- package/dist/route-change.d.ts +22 -0
- package/dist/route-change.js +89 -0
- package/dist/route-hooks.js +3 -2
- package/dist/route-results.d.ts +6 -1
- package/dist/route-results.js +14 -5
- package/dist/run-closeout.d.ts +1 -1
- package/dist/run-closeout.js +7 -5
- package/dist/runner-process.js +3 -2
- package/dist/runner.js +2 -1
- package/dist/scope.d.ts +28 -4
- package/dist/scope.js +92 -28
- package/dist/scout.d.ts +1 -1
- package/dist/scout.js +2 -1
- package/dist/serve.d.ts +1 -1
- package/dist/serve.js +26 -18
- package/dist/server/chat.js +16 -14
- package/dist/server/chrome.d.ts +1 -1
- package/dist/server/chrome.js +26 -35
- package/dist/server/console.css +73 -0
- package/dist/server/guards.js +3 -0
- package/dist/server/home.d.ts +6 -0
- package/dist/server/home.js +21 -13
- package/dist/server/http.js +1 -1
- package/dist/server/kit.js +1 -1
- package/dist/server/merge-ready-view.d.ts +10 -0
- package/dist/server/merge-ready-view.js +30 -0
- package/dist/server/pages-assets.d.ts +1 -0
- package/dist/server/pages-assets.js +8 -5
- package/dist/server/pages.js +103 -82
- package/dist/server/render-chat.js +16 -3
- package/dist/server/render-code-map.d.ts +30 -0
- package/dist/server/render-code-map.js +137 -0
- package/dist/server/render-flaky.d.ts +20 -0
- package/dist/server/render-flaky.js +59 -0
- package/dist/server/render-follow-through.d.ts +11 -0
- package/dist/server/render-follow-through.js +68 -0
- package/dist/server/render-history.js +1 -1
- package/dist/server/render-new-task.d.ts +26 -1
- package/dist/server/render-new-task.js +30 -5
- package/dist/server/render-pages.d.ts +1 -175
- package/dist/server/render-pages.js +38 -313
- package/dist/server/render-projects.d.ts +2 -1
- package/dist/server/render-projects.js +9 -7
- package/dist/server/render-release.d.ts +2 -0
- package/dist/server/render-release.js +3 -3
- package/dist/server/render-results.d.ts +2 -0
- package/dist/server/render-results.js +68 -68
- package/dist/server/render-tasks.d.ts +16 -2
- package/dist/server/render-tasks.js +177 -141
- package/dist/server/retired-routes.d.ts +17 -0
- package/dist/server/retired-routes.js +53 -0
- package/dist/server/route-table.d.ts +3 -0
- package/dist/server/route-table.js +48 -12
- package/dist/server/settings.js +166 -34
- package/dist/server/task-views.js +66 -26
- package/dist/server/tasks.d.ts +2 -6
- package/dist/server/tasks.js +250 -321
- package/dist/server/welcome.js +1 -1
- package/dist/settings-groups-ui.d.ts +1 -0
- package/dist/settings-groups-ui.js +4 -2
- package/dist/settings-kit.js +3 -2
- package/dist/settings-proofs.js +3 -1
- package/dist/slack-chat.js +14 -1
- package/dist/steer-mailbox.d.ts +8 -1
- package/dist/steer-mailbox.js +164 -6
- package/dist/stop-proof.js +6 -4
- package/dist/storage-sweep-process.js +1 -1
- package/dist/storage-sweep.d.ts +5 -3
- package/dist/storage-sweep.js +30 -9
- package/dist/storage-ui.js +28 -17
- package/dist/storage.d.ts +27 -2
- package/dist/storage.js +39 -2
- package/dist/store.d.ts +89 -7
- package/dist/store.js +386 -114
- package/dist/surface.js +67 -6
- package/dist/sweep-quarantine.d.ts +1 -0
- package/dist/sweep-quarantine.js +6 -4
- package/dist/tamper-scan.d.ts +4 -0
- package/dist/tamper-scan.js +90 -0
- package/dist/task-aliases.d.ts +5 -0
- package/dist/task-aliases.js +9 -0
- package/dist/task-control.d.ts +2 -1
- package/dist/task-control.js +4 -1
- package/dist/task-outcome-cli.d.ts +2 -0
- package/dist/task-outcome-cli.js +17 -17
- package/dist/task-sizing.d.ts +5 -0
- package/dist/task-sizing.js +2 -0
- package/dist/task-source.d.ts +7 -0
- package/dist/task-source.js +14 -0
- package/dist/task-status.d.ts +9 -0
- package/dist/task-status.js +45 -49
- package/dist/teardown-git.d.ts +18 -0
- package/dist/teardown-git.js +105 -21
- package/dist/telegram-lead.js +23 -6
- package/dist/telegram-progress.js +5 -3
- package/dist/telegram-status.js +6 -4
- package/dist/telegram.js +12 -1
- package/dist/test-infra.d.ts +8 -0
- package/dist/test-infra.js +18 -0
- package/dist/tidy.js +1 -1
- package/dist/toolroll-update.js +19 -5
- package/dist/tui-contract.d.ts +24 -0
- package/dist/tui-source.js +67 -8
- package/dist/tui.js +51 -51
- package/dist/vanished-retry.d.ts +30 -0
- package/dist/vanished-retry.js +174 -0
- package/dist/work-index.d.ts +2 -0
- package/dist/work-index.js +95 -66
- package/dist/work-summary.d.ts +1 -1
- package/dist/work-summary.js +7 -2
- package/dist/workspace-ui.d.ts +7 -3
- package/dist/workspace-ui.js +38 -23
- package/dist/worktree.d.ts +12 -3
- package/dist/worktree.js +91 -14
- package/package.json +2 -1
- package/dist/server/render-next.d.ts +0 -63
- package/dist/server/render-next.js +0 -109
package/dist/invoke.js
CHANGED
|
@@ -21,13 +21,16 @@ import { adapterFor, auditOf, claudePluginEnv, isolationUnsupported, ALL_CREDENT
|
|
|
21
21
|
import { readProviderKey, readAuthModeStrict, PROVIDER_KEY_ENV, OWN_KEY_ENV } from "./keys.js";
|
|
22
22
|
import { ACP_AGENTS, isAcpProvider, isKeyedProvider } from "./acp-agents.js";
|
|
23
23
|
import { launchFromJson, verifyLaunch } from "./acp-attest.js";
|
|
24
|
-
import { classifyTerminal } from "./exhaustion.js";
|
|
24
|
+
import { classifyTerminal, terminalMatchesLimit } from "./exhaustion.js";
|
|
25
|
+
import { captureProviderFailure } from "./provider-capture.js";
|
|
26
|
+
import { chainCustodyProblem, closeWalkSignedOut, pinnedEntryOf } from "./fallback.js";
|
|
27
|
+
import { codexLoginBilling, livePlanLogin, PLAN_LOGIN_PROVABLE } from "./plan-login.js";
|
|
25
28
|
import { liftAuthPause } from "./provider-auth.js";
|
|
26
|
-
import { attestProvider } from "./attest.js";
|
|
29
|
+
import { attestProvider, reportedVersion } from "./attest.js";
|
|
27
30
|
import { run as runCommand, runOwnerTag, streamsInput } from "./exec.js";
|
|
28
31
|
import { spawnSync } from "node:child_process";
|
|
29
32
|
import { currentContainment } from "./containment.js";
|
|
30
|
-
import { recordRunBillingBasis, recordRunCostUnknown, recordRunModelReported } from "./store.js";
|
|
33
|
+
import { recordCheckStamp, recordRunBillingBasis, recordRunCostUnknown, recordRunModelReported } from "./store.js";
|
|
31
34
|
import { witnessedRunner } from "./process-custody.js";
|
|
32
35
|
import { underStopWatch } from "./task-control.js";
|
|
33
36
|
import { childDatabaseEnv, isolatedChildDatabase, removeChildDatabase as removeAgentDatabase } from "./child-database.js";
|
|
@@ -37,12 +40,15 @@ import { existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, realpathSy
|
|
|
37
40
|
import { homedir, tmpdir } from "node:os";
|
|
38
41
|
import { dirname, join } from "node:path";
|
|
39
42
|
import { poolRootOf } from "./worktree.js";
|
|
43
|
+
import { prepareProtectedStorage, storageRefusalOf } from "./protected-storage.js";
|
|
40
44
|
import { checkLaunchRoot, signInGrants, signInIsItself } from "./agent-fence.js";
|
|
41
45
|
import { localModeOn } from "./local-owner.js";
|
|
46
|
+
import { CHECK_STAMP_VERSION, EVERYWHERE, unfencedStamp } from "./check-stamp.js";
|
|
42
47
|
import { CHECK_ACCESS_DEFAULT, checkAccessHere, localModeCap, narrowestAccess, narrowestNetwork, signInsAvailable } from "./check-access.js";
|
|
43
48
|
import { readDenyPath } from "./project-sandbox.js";
|
|
44
49
|
import { sandboxRuntimeWords } from "./sandbox-runtime.js";
|
|
45
50
|
import { canonicalJson } from "./canonical-json.js";
|
|
51
|
+
import { deliveredFile } from "./code-map-admission.js";
|
|
46
52
|
import { buildCacheEnv, buildCacheFolder, grantFolder, writeAllowlist, WRITES_DEFAULT, CheckoutBindingError, trustedWorktreeBinding, checkoutGitEnv, HARDENED_GIT_ARGS, codexFenceArgv, fenceFor, fencedGitEnv, LEGACY_SANDBOX, launchSandboxOf, linuxFenceAvailable, macosFenceAvailable, NO_GRANTS, sandboxedSpawn, serverGrantsOf, runFolders, taskBoundary } from "./agent-fence.js";
|
|
47
53
|
import { proxyEnv, providerOnlyNetwork, repositoryEcosystems, resolveNetwork, startEgressProxy } from "./egress.js";
|
|
48
54
|
import { claudeConfigDir, claudeDefaults, codexDefaults, codexHomeDir, isolatedCodexHome } from "./provider-home.js";
|
|
@@ -271,7 +277,12 @@ export async function invokeAgent(store, runId, spec, invocation, options) {
|
|
|
271
277
|
diagnostic: strict.problem,
|
|
272
278
|
};
|
|
273
279
|
}
|
|
274
|
-
|
|
280
|
+
// A run on an approved chain spends under the auth mode that entry was pinned to, never the provider's current setting.
|
|
281
|
+
const pinned = invocation.phase === "review" ? null : pinnedEntryOf(store, runId);
|
|
282
|
+
if (pinned !== null && "problem" in pinned) {
|
|
283
|
+
return { kind: "refused", reason: "route-authority", providerVersion: attested === null ? null : attested.version, diagnostic: `the fallback agent's authority lapsed before spawn — ${pinned.problem}` };
|
|
284
|
+
}
|
|
285
|
+
const authMode = pinned === null ? strict.mode : pinned.entry.authMode;
|
|
275
286
|
if (spec.provider === "claude" && invocation.phase !== "review") {
|
|
276
287
|
const problem = claudeSettingsAuthProblem(options.env?.["CLAUDE_CONFIG_DIR"] ?? process.env["CLAUDE_CONFIG_DIR"] ?? join(keyHome ?? homedir(), ".claude"));
|
|
277
288
|
if (problem !== null)
|
|
@@ -284,6 +295,27 @@ export async function invokeAgent(store, runId, spec, invocation, options) {
|
|
|
284
295
|
const managedKey = authMode === "api-key" && keyed !== null && keyEnvName !== null
|
|
285
296
|
? readProviderKey(keyed, keyHome) ?? (process.env[keyEnvName] || null)
|
|
286
297
|
: null;
|
|
298
|
+
// A pinned API key that isn't there never falls through to some other credential.
|
|
299
|
+
if (pinned !== null && authMode === "api-key" && managedKey === null) {
|
|
300
|
+
return { kind: "refused", reason: "auth-mode", providerVersion: attested === null ? null : attested.version, diagnostic: `this fallback agent is approved on an API key, and ${spec.provider} has none here` };
|
|
301
|
+
}
|
|
302
|
+
// What decides the child's credential, computed once: the environment it starts from and every variable it sheds. The
|
|
303
|
+
// live plan proof below reads with exactly these, and the child launches with exactly these (childOmitEnv).
|
|
304
|
+
const childAuth = childAuthEnv({ provider: spec.provider, phase: invocation.phase, authMode, keyEnvName, ownKeyEnv,
|
|
305
|
+
extraOmitEnv: adapter.extraOmitEnv, runEnv: runOptions.env, runOmitEnv: runOptions.omitEnv });
|
|
306
|
+
// A fallback agent approved on its plan runs only on a login its CLI proves, live, is that plan: read with the run's
|
|
307
|
+
// own environment, before anything is started for it (no proxy, tools or homes to leave behind). A provider with no
|
|
308
|
+
// live proof never gets here (filing refuses it); if one does, it's refused too. A key is never read as paid.
|
|
309
|
+
if (pinned !== null && pinned.index > 0 && authMode === "subscription") {
|
|
310
|
+
const live = PLAN_LOGIN_PROVABLE.has(spec.provider)
|
|
311
|
+
? await livePlanLogin(spec.provider, attested !== null ? attested.executable : adapter.binary, childAuth.omitEnv, childAuth.env)
|
|
312
|
+
: null;
|
|
313
|
+
if (live !== "subscription") {
|
|
314
|
+
closeWalkSignedOut(store, runId, clock());
|
|
315
|
+
return { kind: "refused", reason: "auth-mode", providerVersion: attested === null ? null : attested.version,
|
|
316
|
+
diagnostic: `this fallback agent is approved on its plan, and ${spec.provider} isn't signed in to one here${live === "api-key" ? " (it's signed in another way)" : ""}` };
|
|
317
|
+
}
|
|
318
|
+
}
|
|
287
319
|
// Resume XOR mint, enforced at the GATEWAY (Codex gemini verify round 2,
|
|
288
320
|
// finding 3): a resume and a minted start id are mutually exclusive —
|
|
289
321
|
// geminiArgv silently prefers --resume, so a minted id that rides
|
|
@@ -362,12 +394,12 @@ export async function invokeAgent(store, runId, spec, invocation, options) {
|
|
|
362
394
|
// tool servers outside its command sandbox, so its own launch folder is
|
|
363
395
|
// fenced too; Claude reads its config from there, inside the sandbox.
|
|
364
396
|
const ownTools = tools?.privateDir === undefined ? null : realpathSync(tools.privateDir);
|
|
365
|
-
const fenceOf = invocation.phase === "review" ? { fence: [], fileTools: [], grants: NO_GRANTS, gitPaths: [], cache: undefined, problem: undefined } : runFence(store, runId, spec.provider, options.keyHome, codexHarness ? null : ownTools, launchWrites ?? WRITES_DEFAULT, launchSandbox ?? LEGACY_SANDBOX);
|
|
397
|
+
const fenceOf = invocation.phase === "review" ? { fence: [], fileTools: [], grants: NO_GRANTS, gitPaths: [], cache: undefined, problem: undefined } : await runFence(store, runId, spec.provider, options.keyHome, codexHarness ? null : ownTools, launchWrites ?? WRITES_DEFAULT, launchSandbox ?? LEGACY_SANDBOX);
|
|
366
398
|
if (fenceOf.problem !== undefined) {
|
|
367
399
|
tools?.cleanup();
|
|
368
400
|
codexHome?.cleanup();
|
|
369
401
|
removeAgentDatabase(isolatedDb.dir);
|
|
370
|
-
return { kind: "refused", reason: "containment", providerVersion: attested?.version ?? null, diagnostic: fenceOf.problem };
|
|
402
|
+
return { kind: "refused", reason: "storageRefused" in fenceOf && fenceOf.storageRefused === true ? "protected-storage" : "containment", providerVersion: attested?.version ?? null, diagnostic: fenceOf.problem };
|
|
371
403
|
}
|
|
372
404
|
const fence = [...fenceOf.fence, ...(fenceOf.fence.length > 0 && codexHarness ? [ownTools, codexHome === null ? null : realpathSync(codexHome.dir)].filter((path) => path !== null) : [])];
|
|
373
405
|
const grants = { ...fenceOf.grants };
|
|
@@ -497,20 +529,8 @@ export async function invokeAgent(store, runId, spec, invocation, options) {
|
|
|
497
529
|
// the live control database through a generic RunOptions override.
|
|
498
530
|
...childDatabaseEnv(isolatedDb.file),
|
|
499
531
|
};
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
...adapter.extraOmitEnv,
|
|
503
|
-
// A Claude build loads no plugin, from settings or the environment (a review's safe mode already loads none).
|
|
504
|
-
...(spec.provider === "claude" && invocation.phase !== "review" ? claudePluginEnv({ ...process.env, ...(runOptions.env ?? {}) }) : []),
|
|
505
|
-
// Subscription mode: shed this provider's OWN key too, so an ambient
|
|
506
|
-
// one cannot force API billing over the login the operator prefers.
|
|
507
|
-
// Api-key mode: shed every OTHER own-key alias (finding 1) — gemini
|
|
508
|
-
// reads GOOGLE_API_KEY as well as GEMINI_API_KEY, and a stray alias
|
|
509
|
-
// must not override the ONE canonical key this mode injected.
|
|
510
|
-
...(authMode === "subscription"
|
|
511
|
-
? ownKeyEnv
|
|
512
|
-
: ownKeyEnv.filter(name => name !== keyEnvName)),
|
|
513
|
-
];
|
|
532
|
+
// The one omit list (childAuthEnv): the live plan proof read with this same list.
|
|
533
|
+
const childOmitEnv = childAuth.omitEnv;
|
|
514
534
|
const launchOptions = {
|
|
515
535
|
...(fenced.wrap.length === 0 ? {} : { fence: fenced.wrap, fenceGrants: grants, confinement: "required" }),
|
|
516
536
|
...runOptions,
|
|
@@ -678,12 +698,23 @@ export async function invokeAgent(store, runId, spec, invocation, options) {
|
|
|
678
698
|
return { kind: "refused", reason: "provider-unattested", providerVersion: acpAttestation.agent.version, diagnostic: verified.problem };
|
|
679
699
|
}
|
|
680
700
|
}
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
701
|
+
// THE CHAIN'S LAST LOOK (fallback.ts): the walk's place, the approval, and for a paid entry the live grant, read in
|
|
702
|
+
// the one transaction that stamps the spend, after every await.
|
|
703
|
+
const chainRefusal = store.transact(() => {
|
|
704
|
+
const custody = invocation.phase === "review" ? null : chainCustodyProblem(store, runId);
|
|
705
|
+
if (custody !== null)
|
|
706
|
+
return custody;
|
|
707
|
+
if (attested !== null)
|
|
708
|
+
store.stampProviderStart(runId, clock(), attested.version);
|
|
709
|
+
else if (acpAttestation !== null)
|
|
710
|
+
store.stampProviderStart(runId, clock(), acpAttestation.agent.version);
|
|
711
|
+
else
|
|
712
|
+
store.stampProviderStart(runId, clock());
|
|
713
|
+
return null;
|
|
714
|
+
});
|
|
715
|
+
if (chainRefusal !== null) {
|
|
716
|
+
return { kind: "refused", reason: "route-authority", providerVersion: attested === null ? null : attested.version, diagnostic: `the fallback chain's authority lapsed before spawn — ${chainRefusal}` };
|
|
717
|
+
}
|
|
687
718
|
// The brief is queued only once the checkout has passed the fence check.
|
|
688
719
|
liveInput?.begin(invocation.brief);
|
|
689
720
|
spawnedAt = Date.now();
|
|
@@ -788,13 +819,28 @@ export async function invokeAgent(store, runId, spec, invocation, options) {
|
|
|
788
819
|
const quietEarlyExit = result.code !== 0 && !result.timedOut && !result.notFound &&
|
|
789
820
|
Date.now() - spawnedAt <= AUTH_EARLY_EXIT_MS &&
|
|
790
821
|
envelope.promptConsumed !== true && (envelope.tokensOut ?? 0) === 0;
|
|
822
|
+
// Claude has no attested range (attest.ts), so a terminal that reads as a plan limit asks the installed CLI its version.
|
|
823
|
+
const limitVersion = !terminalMatchesLimit(spec.provider, authMode, envelope.structuralTerminal) ? null
|
|
824
|
+
: attested !== null ? attested.version : await reportedVersion(adapter.binary, options.versionProbe);
|
|
791
825
|
const terminalClass = classifyTerminal({
|
|
792
826
|
terminal: envelope.structuralTerminal,
|
|
827
|
+
run: { provider: spec.provider, authMode, version: limitVersion },
|
|
793
828
|
// stderr and plain stdout only: a JSON event line can carry the agent's
|
|
794
829
|
// own command output (codex aggregated_output), never its sign-in.
|
|
795
830
|
earlyExit: quietEarlyExit ? `${result.stderr.slice(-4096)}\n${plainLines(result.stdout).slice(-4096)}` : null,
|
|
796
831
|
});
|
|
797
|
-
|
|
832
|
+
// A plan limit counts only from a run whose protocol and identity hold (fallback.ts moves work on it): a terminal from a
|
|
833
|
+
// conflicting session, a minted id it didn't echo, or a failure on exit 0 is refused below and is never exhaustion.
|
|
834
|
+
const protocolClean = envelope.protocolError === null
|
|
835
|
+
&& !(startSessionId !== undefined && envelope.initObserved === true && envelope.sessionId !== startSessionId)
|
|
836
|
+
&& sessionIdentityProblem === null
|
|
837
|
+
&& !(result.code === 0 && envelope.structuralTerminal?.failed === true);
|
|
838
|
+
store.stampTerminalClass(runId, authMode, terminalClass === "usage-exhausted" && !protocolClean ? "not-exhausted" : terminalClass);
|
|
839
|
+
// What the provider said on its way out, saved for a person (provider-capture.ts). Nothing routes on it.
|
|
840
|
+
if (terminalClass !== "auth-expired" && !result.timedOut && !result.notFound && (result.code !== 0 || envelope.structuralTerminal?.failed === true)) {
|
|
841
|
+
captureProviderFailure(store, { run: runId, provider: spec.provider, model: spec.model ?? null, authMode, exitCode: result.code, code: envelope.structuralTerminal?.code ?? null,
|
|
842
|
+
texts: [envelope.structuralTerminal?.text, envelope.finalMessage?.slice(-1_000), result.stderr.slice(-2_000), plainLines(result.stdout).slice(-1_000)] }, new Date());
|
|
843
|
+
}
|
|
798
844
|
// Contradictory structural identity is a provider protocol failure, not
|
|
799
845
|
// a session selection problem. Usage above remains recorded because the
|
|
800
846
|
// process ran and spent, but no downstream handoff or repair may trust
|
|
@@ -936,7 +982,7 @@ function linkedFor(store, worktree) {
|
|
|
936
982
|
/** The paths this run's agent may not reach: the state folder around the live database (but not its own worktree),
|
|
937
983
|
* ~/.toolroll (or its older name), its task boundary and other launches' private folders; what the fence gives back
|
|
938
984
|
* (FenceGrants); and what only Claude's file tools refuse. */
|
|
939
|
-
function runFence(store, runId, provider, keyHome, ownTools, writes = WRITES_DEFAULT, sandbox = LEGACY_SANDBOX) {
|
|
985
|
+
async function runFence(store, runId, provider, keyHome, ownTools, writes = WRITES_DEFAULT, sandbox = LEGACY_SANDBOX) {
|
|
940
986
|
try {
|
|
941
987
|
// A plane always has a database file; an in-memory store (a test) has nothing to fence.
|
|
942
988
|
const databaseFile = store.databaseFile();
|
|
@@ -949,7 +995,9 @@ function runFence(store, runId, provider, keyHome, ownTools, writes = WRITES_DEF
|
|
|
949
995
|
throw new CheckoutBindingError("The checkout's Git pointer was changed or its trusted binding could not be verified.");
|
|
950
996
|
const poolRoots = [join(dirname(databaseFile), "worktrees"), ...store.listWorktrees().map(record => poolRootOf(record.path, record.repo))];
|
|
951
997
|
const binding = worktree === null || row === null ? null : trustedWorktreeBinding(row.repo, worktree, poolRoots);
|
|
952
|
-
|
|
998
|
+
// The storage check, awaited off this thread (protected-storage.ts): a privacy prompt holds up only its child.
|
|
999
|
+
const storage = await prepareProtectedStorage(keyHome ?? homedir());
|
|
1000
|
+
const boundary = worktree === null ? null : taskBoundary({ worktree, provider, ...home, binding, policy: sandbox.policy, storage });
|
|
953
1001
|
const folders = runFolders(ownTools);
|
|
954
1002
|
// A build with a checkout writes only its workspace unless its approval said anywhere (arc 2): its checkout and
|
|
955
1003
|
// Git data, its launch folder, its project's caches and the provider state its CLI must write.
|
|
@@ -958,8 +1006,10 @@ function runFence(store, runId, provider, keyHome, ownTools, writes = WRITES_DEF
|
|
|
958
1006
|
// The shared install is granted only as the plane linked it: this checkout's project and stamped setup.
|
|
959
1007
|
// Every plane checkout pool is denied around this one: the plane's own beside the database, and the one this
|
|
960
1008
|
// checkout was recorded in (a --pool elsewhere), so another project's worktree, present at launch, is out of reach. Native wrappers also cover later creations.
|
|
961
|
-
const launch = fenceFor(provider === "command" ? "command" : "build", { databaseFile, worktree, ...home, boundary, binding, runFolders: folders, poolRoots,
|
|
1009
|
+
const launch = fenceFor(provider === "command" ? "command" : "build", { databaseFile, worktree, ...home, boundary, binding, runFolders: folders, poolRoots, storage,
|
|
962
1010
|
extra: allowed?.deny ?? [], linked: linkedFor(store, worktree), writes: allowed === null ? null : { writes: allowed.writes, readOnly: allowed.readOnly }, policy: sandbox.policy, provider,
|
|
1011
|
+
// This launch's code map file: exactly that file, read-only, inside the denied state folder. Commands don't get it.
|
|
1012
|
+
...(provider === "command" ? {} : { exactFile: deliveredFile(store, runId) }),
|
|
963
1013
|
// The sealed read denies, and whatever the project lists now too (adding a deny only narrows).
|
|
964
1014
|
readDeny: [...new Set([...sandbox.readDeny, ...(row === null ? [] : store.projectSandbox(row.repo).readDeny)])].map(one => readDenyPath(one, keyHome ?? homedir())), machLookup: sandbox.machLookup });
|
|
965
1015
|
return {
|
|
@@ -972,6 +1022,10 @@ function runFence(store, runId, provider, keyHome, ownTools, writes = WRITES_DEF
|
|
|
972
1022
|
};
|
|
973
1023
|
}
|
|
974
1024
|
catch (error) {
|
|
1025
|
+
// The storage check's own words: what didn't finish, and which program macOS may need to allow.
|
|
1026
|
+
const refused = storageRefusalOf(error);
|
|
1027
|
+
if (refused !== null)
|
|
1028
|
+
return { fence: [], fileTools: [], grants: NO_GRANTS, problem: refused.message, storageRefused: true };
|
|
975
1029
|
return { fence: [], fileTools: [], grants: NO_GRANTS, problem: error instanceof CheckoutBindingError ? error.message : "The build fence could not be prepared." };
|
|
976
1030
|
}
|
|
977
1031
|
}
|
|
@@ -992,6 +1046,7 @@ export function fencedCommands(store, runId, writes, inner, available = () => ma
|
|
|
992
1046
|
if (!available()) {
|
|
993
1047
|
if (store.unfencedAgentsAllowed()) {
|
|
994
1048
|
store.stampRunCheckAccess(runId, "unsandboxed");
|
|
1049
|
+
stampLaunch(store, [runId], unfencedStamp());
|
|
995
1050
|
return inner(file, args, options);
|
|
996
1051
|
}
|
|
997
1052
|
return { code: 1, stdout: "", stderr: "This machine can't confine the build's commands (no macOS sandbox, no working bubblewrap), so they didn't run. Install bubblewrap, socat and ripgrep (Linux), or an instance operator allows unconfined agents with `toolroll config unconfined-agents allow`.\n", timedOut: false, notFound: false };
|
|
@@ -1002,13 +1057,17 @@ export function fencedCommands(store, runId, writes, inner, available = () => ma
|
|
|
1002
1057
|
mkdirSync(temp, { mode: 0o700 });
|
|
1003
1058
|
// Not sandboxed (check-access.ts): the project's checks are trusted by the whole instance, as before 0.9.66. Never
|
|
1004
1059
|
// while local mode is on (easy-start C5): then they run in the fence, sandboxed.
|
|
1005
|
-
if (access === "unsandboxed" && !localModeOn(store))
|
|
1060
|
+
if (access === "unsandboxed" && !localModeOn(store)) {
|
|
1061
|
+
stampLaunch(store, [runId], unfencedStamp());
|
|
1006
1062
|
return await inner(file, args, { ...options, env: { ...(options?.env ?? {}), TMPDIR: temp, TMP: temp, TEMP: temp } });
|
|
1007
|
-
|
|
1063
|
+
}
|
|
1064
|
+
const fenced = await runFence(store, runId, "command", undefined, own, writes, sandbox);
|
|
1008
1065
|
if (fenced.problem !== undefined)
|
|
1009
1066
|
return { code: 1, stdout: "", stderr: `${fenced.problem}\n`, timedOut: false, notFound: false };
|
|
1010
|
-
if (fenced.fence.length === 0)
|
|
1067
|
+
if (fenced.fence.length === 0) {
|
|
1068
|
+
stampLaunch(store, [runId], unfencedStamp());
|
|
1011
1069
|
return inner(file, args, options);
|
|
1070
|
+
}
|
|
1012
1071
|
// A self-hosted check's own database (child-database.ts) is the command's to write too.
|
|
1013
1072
|
const database = options?.env?.["TOOLROLL_DB"];
|
|
1014
1073
|
const checkData = database === undefined || fenced.grants.writes === undefined ? null : grantFolder(dirname(database), false);
|
|
@@ -1020,12 +1079,14 @@ export function fencedCommands(store, runId, writes, inner, available = () => ma
|
|
|
1020
1079
|
if (signIns !== null && "problem" in signIns)
|
|
1021
1080
|
return { code: 1, stdout: "", stderr: `${signIns.problem}\n`, timedOut: false, notFound: false };
|
|
1022
1081
|
try {
|
|
1082
|
+
// Each sign-in file looked at again right before the launch: one no longer itself isn't granted.
|
|
1083
|
+
// Never the keychain: the sign-ins kept there come in the environment, read outside (keychainSignIns).
|
|
1084
|
+
const given = signIns === null ? grants : { ...grants, signIns: { ...signIns.grants, read: signIns.grants.read.filter(path => signInIsItself(realpathSync(homedir()), path)) }, network: signIns.network };
|
|
1085
|
+
stampLaunch(store, [runId], fencedStamp(signIns === null ? "sandboxed" : "sign-ins", fenced.fence, given, signIns === null ? undefined : network));
|
|
1023
1086
|
return await inner(file, args, {
|
|
1024
1087
|
...options,
|
|
1025
1088
|
fence: fenced.fence,
|
|
1026
|
-
|
|
1027
|
-
// Never the keychain: the sign-ins kept there come in the environment, read outside (keychainSignIns).
|
|
1028
|
-
fenceGrants: signIns === null ? grants : { ...grants, signIns: { ...signIns.grants, read: signIns.grants.read.filter(path => signInIsItself(realpathSync(homedir()), path)) }, network: signIns.network },
|
|
1089
|
+
fenceGrants: given,
|
|
1029
1090
|
confinement: "required",
|
|
1030
1091
|
env: { ...(options?.env ?? {}), TMPDIR: temp, TMP: temp, TEMP: temp, ...(fenced.cache === undefined || fenced.grants.writes === undefined ? {} : buildCacheEnv(fenced.cache)), ...(signIns?.env ?? {}) },
|
|
1031
1092
|
...(signIns === null ? {} : { omitEnv: [...(options?.omitEnv ?? []), ...SIGN_INS_OMITTED_ENV] }),
|
|
@@ -1046,26 +1107,32 @@ export function fencedCommands(store, runId, writes, inner, available = () => ma
|
|
|
1046
1107
|
* project's writes setting, only that checkout, its Git data, its own temp folder, the project's caches and its check
|
|
1047
1108
|
* database writable. Where nothing can confine them, they run only when an instance operator allowed unconfined agents.
|
|
1048
1109
|
*/
|
|
1049
|
-
export function fencedCheckoutCommands(store, repo, checkout, inner, available = () => macosFenceAvailable() || linuxFenceAvailable(), access = CHECK_ACCESS_DEFAULT, network, sandbox = LEGACY_SANDBOX) {
|
|
1110
|
+
export function fencedCheckoutCommands(store, repo, checkout, inner, available = () => macosFenceAvailable() || linuxFenceAvailable(), access = CHECK_ACCESS_DEFAULT, network, sandbox = LEGACY_SANDBOX, stampRuns = []) {
|
|
1050
1111
|
if (inner !== runCommand)
|
|
1051
1112
|
return inner;
|
|
1052
1113
|
return async (file, args, options) => {
|
|
1053
1114
|
if (!available()) {
|
|
1054
|
-
if (store.unfencedAgentsAllowed())
|
|
1115
|
+
if (store.unfencedAgentsAllowed()) {
|
|
1116
|
+
stampLaunch(store, stampRuns, unfencedStamp());
|
|
1055
1117
|
return inner(file, args, options);
|
|
1118
|
+
}
|
|
1056
1119
|
return { code: 1, stdout: "", stderr: "This machine can't confine the project's checks (no macOS sandbox, no working bubblewrap), so they didn't run. Install bubblewrap, socat and ripgrep (Linux), or an instance operator allows unconfined agents with `toolroll config unconfined-agents allow`.\n", timedOut: false, notFound: false };
|
|
1057
1120
|
}
|
|
1058
1121
|
const databaseFile = store.databaseFile();
|
|
1059
|
-
if (databaseFile === null)
|
|
1122
|
+
if (databaseFile === null) {
|
|
1123
|
+
stampLaunch(store, stampRuns, unfencedStamp());
|
|
1060
1124
|
return inner(file, args, options);
|
|
1125
|
+
}
|
|
1061
1126
|
const own = realpathSync(mkdtempSync(join(checkLaunchRoot(), "so-cmd-")));
|
|
1062
1127
|
try {
|
|
1063
1128
|
const temp = join(own, "tmp");
|
|
1064
1129
|
mkdirSync(temp, { mode: 0o700 });
|
|
1065
1130
|
// Not sandboxed (check-access.ts): the project's checks are trusted by the whole instance, as before 0.9.66. Never
|
|
1066
1131
|
// while local mode is on (easy-start C5): then they run in the fence, sandboxed.
|
|
1067
|
-
if (access === "unsandboxed" && !localModeOn(store))
|
|
1132
|
+
if (access === "unsandboxed" && !localModeOn(store)) {
|
|
1133
|
+
stampLaunch(store, stampRuns, unfencedStamp());
|
|
1068
1134
|
return await inner(file, args, { ...options, env: { ...(options?.env ?? {}), TMPDIR: temp, TMP: temp, TEMP: temp } });
|
|
1135
|
+
}
|
|
1069
1136
|
let binding;
|
|
1070
1137
|
try {
|
|
1071
1138
|
binding = trustedWorktreeBinding(repo, checkout);
|
|
@@ -1074,13 +1141,20 @@ export function fencedCheckoutCommands(store, repo, checkout, inner, available =
|
|
|
1074
1141
|
return { code: 1, stdout: "", stderr: "The check's checkout couldn't be bound to its project, so nothing ran.\n", timedOut: false, notFound: false };
|
|
1075
1142
|
}
|
|
1076
1143
|
const poolRoots = [join(dirname(databaseFile), "worktrees"), ...store.listWorktrees().map(record => poolRootOf(record.path, record.repo))];
|
|
1077
|
-
|
|
1144
|
+
let storage;
|
|
1145
|
+
try {
|
|
1146
|
+
storage = await prepareProtectedStorage(homedir());
|
|
1147
|
+
}
|
|
1148
|
+
catch (error) {
|
|
1149
|
+
return { code: 1, stdout: "", stderr: `${storageRefusalOf(error)?.message ?? "The check's fence could not be prepared, so nothing ran."}\n`, timedOut: false, notFound: false };
|
|
1150
|
+
}
|
|
1151
|
+
const boundary = taskBoundary({ worktree: checkout, provider: "command", binding, policy: sandbox.policy, storage });
|
|
1078
1152
|
const folders = runFolders(own);
|
|
1079
1153
|
const cache = buildCacheFolder(repo);
|
|
1080
1154
|
const allowed = store.projectWrites(repo) !== "workspace" ? null : writeAllowlist({ provider: "command", home: homedir(), worktree: checkout, binding, launchFolder: own, cache });
|
|
1081
1155
|
const database = options?.env?.["TOOLROLL_DB"];
|
|
1082
1156
|
const checkData = allowed === null || database === undefined ? null : grantFolder(dirname(database), false);
|
|
1083
|
-
const launch = fenceFor("check", { databaseFile, worktree: checkout, boundary, binding, runFolders: folders, poolRoots, extra: allowed?.deny ?? [], linked: linkedFor(store, checkout),
|
|
1157
|
+
const launch = fenceFor("check", { databaseFile, worktree: checkout, boundary, binding, runFolders: folders, poolRoots, storage, extra: allowed?.deny ?? [], linked: linkedFor(store, checkout),
|
|
1084
1158
|
readDeny: sandbox.readDeny.map(one => readDenyPath(one, homedir())), machLookup: sandbox.machLookup, policy: sandbox.policy,
|
|
1085
1159
|
writes: allowed === null ? null : { writes: { ...allowed.writes, subpaths: [...allowed.writes.subpaths, ...(checkData === null ? [] : [checkData])] }, readOnly: allowed.readOnly } });
|
|
1086
1160
|
const grants = launch.fenceGrants;
|
|
@@ -1091,12 +1165,14 @@ export function fencedCheckoutCommands(store, repo, checkout, inner, available =
|
|
|
1091
1165
|
if (signIns !== null && "problem" in signIns)
|
|
1092
1166
|
return { code: 1, stdout: "", stderr: `${signIns.problem}\n`, timedOut: false, notFound: false };
|
|
1093
1167
|
try {
|
|
1168
|
+
// Each sign-in file looked at again right before the launch: one no longer itself isn't granted.
|
|
1169
|
+
// Never the keychain: the sign-ins kept there come in the environment, read outside (keychainSignIns).
|
|
1170
|
+
const given = signIns === null ? grants : { ...grants, signIns: { ...signIns.grants, read: signIns.grants.read.filter(path => signInIsItself(realpathSync(homedir()), path)) }, network: signIns.network };
|
|
1171
|
+
stampLaunch(store, stampRuns, fencedStamp(signIns === null ? "sandboxed" : "sign-ins", launch.fence, given, signIns === null ? undefined : network));
|
|
1094
1172
|
return await inner(file, args, {
|
|
1095
1173
|
...options,
|
|
1096
1174
|
fence: launch.fence,
|
|
1097
|
-
|
|
1098
|
-
// Never the keychain: the sign-ins kept there come in the environment, read outside (keychainSignIns).
|
|
1099
|
-
fenceGrants: signIns === null ? grants : { ...grants, signIns: { ...signIns.grants, read: signIns.grants.read.filter(path => signInIsItself(realpathSync(homedir()), path)) }, network: signIns.network },
|
|
1175
|
+
fenceGrants: given,
|
|
1100
1176
|
confinement: launch.confinement,
|
|
1101
1177
|
env: { ...(options?.env ?? {}), TMPDIR: temp, TMP: temp, TEMP: temp, ...(allowed === null ? {} : buildCacheEnv(cache)), ...(signIns?.env ?? {}) },
|
|
1102
1178
|
...(signIns === null ? {} : { omitEnv: [...(options?.omitEnv ?? []), ...SIGN_INS_OMITTED_ENV] }),
|
|
@@ -1157,18 +1233,6 @@ async function runTools(store, runId, spec, keyHome, clock, readOnly, withheld,
|
|
|
1157
1233
|
return noToolsArgs(spec.provider);
|
|
1158
1234
|
}
|
|
1159
1235
|
}
|
|
1160
|
-
/** How Codex says it's signed in (`codex login status`): a ChatGPT sign-in is its plan; a key or Bedrock is a key;
|
|
1161
|
-
* null when it says neither (not signed in, or it didn't answer). */
|
|
1162
|
-
async function codexLoginBilling(binary, omitEnv) {
|
|
1163
|
-
try {
|
|
1164
|
-
const status = await runCommand(binary, ["login", "status"], { timeoutMs: 15_000, omitEnv, processGroup: true });
|
|
1165
|
-
const said = `${status.stdout}\n${status.stderr}`;
|
|
1166
|
-
return /using chatgpt/i.test(said) ? "subscription" : /api key|bedrock/i.test(said) ? "api-key" : null;
|
|
1167
|
-
}
|
|
1168
|
-
catch {
|
|
1169
|
-
return null;
|
|
1170
|
-
}
|
|
1171
|
-
}
|
|
1172
1236
|
/** The lines of a harness's stdout that are not JSON events. */
|
|
1173
1237
|
function plainLines(stdout) {
|
|
1174
1238
|
return stdout.split("\n").filter(line => !line.trimStart().startsWith("{")).join("\n");
|
|
@@ -1284,7 +1348,7 @@ export function checkoutRunner(store, repo, checkout, inner, runIds, ran, platfo
|
|
|
1284
1348
|
return (file, args, options) => {
|
|
1285
1349
|
const launch = checkoutLaunchOf(store, repo, runIds, platform);
|
|
1286
1350
|
ran(actualCheckAccess(store, launch.access));
|
|
1287
|
-
return fencedCheckoutCommands(store, repo, checkout, inner, undefined, launch.access, launch.network, checkoutSandboxOf(store, repo, runIds))(file, args, options);
|
|
1351
|
+
return fencedCheckoutCommands(store, repo, checkout, inner, undefined, launch.access, launch.network, checkoutSandboxOf(store, repo, runIds), runIds)(file, args, options);
|
|
1288
1352
|
};
|
|
1289
1353
|
}
|
|
1290
1354
|
/**
|
|
@@ -1308,6 +1372,22 @@ export function checkoutSandboxOf(store, repo, runIds) {
|
|
|
1308
1372
|
machLookup: common(one => one.machLookup, live.machLookup),
|
|
1309
1373
|
};
|
|
1310
1374
|
}
|
|
1375
|
+
/** A fenced launch's stamp: the level it runs at and exactly what it was given (check-stamp.ts), read from the grants the
|
|
1376
|
+
* launch itself takes, never from what was asked for. */
|
|
1377
|
+
export function fencedStamp(level, fence, grants, network) {
|
|
1378
|
+
const writes = grants.writes;
|
|
1379
|
+
return {
|
|
1380
|
+
version: CHECK_STAMP_VERSION, level, policyDigest: fenceDigest(fence, grants),
|
|
1381
|
+
writable: writes === undefined ? [EVERYWHERE] : [...new Set([...writes.subpaths, ...writes.prefixes.map(one => `${one.path}${one.suffix}`)])].sort(),
|
|
1382
|
+
readable: [...new Set([...grants.readable, ...(grants.signIns?.read ?? [])])].sort(),
|
|
1383
|
+
network: network === undefined ? { mode: "machine" } : { mode: network.mode, hosts: [...network.hosts].sort() },
|
|
1384
|
+
};
|
|
1385
|
+
}
|
|
1386
|
+
function stampLaunch(store, runs, stamp) {
|
|
1387
|
+
const now = new Date();
|
|
1388
|
+
for (const runId of runs)
|
|
1389
|
+
recordCheckStamp(store, runId, stamp, now);
|
|
1390
|
+
}
|
|
1311
1391
|
/** The digest of a launch's whole fence (its denied paths and grants, canonical), as stamped on its run (C11). */
|
|
1312
1392
|
export function fenceDigest(fence, grants) {
|
|
1313
1393
|
return createHash("sha256").update(`toolroll:fence:${canonicalJson({ fence: [...fence], grants: grants })}`).digest("hex").slice(0, 32);
|
|
@@ -1331,3 +1411,22 @@ export function observeClaudeBilling(seen, event) {
|
|
|
1331
1411
|
seen.answered = true;
|
|
1332
1412
|
}
|
|
1333
1413
|
}
|
|
1414
|
+
/**
|
|
1415
|
+
* What decides a child's credential: the environment it starts from (the plane's, then the caller's) and every variable
|
|
1416
|
+
* it sheds. Subscription: this provider's own keys too, so an ambient key can't force API billing over the login.
|
|
1417
|
+
* API key: every other own-key alias, so a stray one can't override the one key injected. Foreign keys and (for a
|
|
1418
|
+
* Claude build) plugin variables always. The live plan proof and the launch both use this one answer.
|
|
1419
|
+
*/
|
|
1420
|
+
export function childAuthEnv(args) {
|
|
1421
|
+
const env = { ...process.env, ...(args.runEnv ?? {}) };
|
|
1422
|
+
return {
|
|
1423
|
+
env,
|
|
1424
|
+
omitEnv: [
|
|
1425
|
+
...(args.runOmitEnv ?? []),
|
|
1426
|
+
...args.extraOmitEnv,
|
|
1427
|
+
// A Claude build loads no plugin, from settings or the environment (a review's safe mode already loads none).
|
|
1428
|
+
...(args.provider === "claude" && args.phase !== "review" ? claudePluginEnv(env) : []),
|
|
1429
|
+
...(args.authMode === "subscription" ? args.ownKeyEnv : args.ownKeyEnv.filter(name => name !== args.keyEnvName)),
|
|
1430
|
+
],
|
|
1431
|
+
};
|
|
1432
|
+
}
|
|
@@ -25,7 +25,9 @@ param(
|
|
|
25
25
|
# a space or a percent sign the controller passed.
|
|
26
26
|
[Parameter(Position = 1)] [string] $Encoded,
|
|
27
27
|
[string] $JobName,
|
|
28
|
-
[string] $QueryName
|
|
28
|
+
[string] $QueryName,
|
|
29
|
+
# A whole-job memory limit in bytes (the code map's bounded reads); 0 is none.
|
|
30
|
+
[long] $MemoryLimit = 0
|
|
29
31
|
)
|
|
30
32
|
|
|
31
33
|
$ErrorActionPreference = "Stop"
|
|
@@ -68,6 +70,7 @@ public static class SoJob {
|
|
|
68
70
|
public const int JobObjectBasicAccountingInformation = 1;
|
|
69
71
|
public const int JobObjectExtendedLimitInformation = 9;
|
|
70
72
|
public const uint JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE = 0x2000;
|
|
73
|
+
public const uint JOB_OBJECT_LIMIT_JOB_MEMORY = 0x200;
|
|
71
74
|
public const uint CREATE_SUSPENDED = 0x4;
|
|
72
75
|
public const uint CREATE_UNICODE_ENVIRONMENT = 0x400;
|
|
73
76
|
public const int STARTF_USESTDHANDLES = 0x100;
|
|
@@ -111,7 +114,7 @@ public static class SoJob {
|
|
|
111
114
|
catch { return "unknown"; }
|
|
112
115
|
finally { CloseHandle(job); }
|
|
113
116
|
}
|
|
114
|
-
public static IntPtr MakeJob(string name) {
|
|
117
|
+
public static IntPtr MakeJob(string name, long memoryLimit) {
|
|
115
118
|
IntPtr job = CreateJobObjectW(IntPtr.Zero, name);
|
|
116
119
|
int createdError = Marshal.GetLastWin32Error();
|
|
117
120
|
if (job != IntPtr.Zero && createdError == 183) { CloseHandle(job); throw new Exception("job name already exists"); }
|
|
@@ -119,6 +122,10 @@ public static class SoJob {
|
|
|
119
122
|
var info = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION();
|
|
120
123
|
// Kill on close; NO breakaway flags, so nothing inside may leave the job.
|
|
121
124
|
info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
|
|
125
|
+
if (memoryLimit > 0) {
|
|
126
|
+
info.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_JOB_MEMORY;
|
|
127
|
+
info.JobMemoryLimit = new UIntPtr((ulong)memoryLimit);
|
|
128
|
+
}
|
|
122
129
|
int size = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
|
|
123
130
|
IntPtr buffer = Marshal.AllocHGlobal(size);
|
|
124
131
|
try {
|
|
@@ -177,7 +184,7 @@ try {
|
|
|
177
184
|
[string[]]$targetArguments = ConvertFrom-Json -InputObject ([System.Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($Encoded)))
|
|
178
185
|
if ($targetArguments.Count -lt 1) { throw "the target argv is empty" }
|
|
179
186
|
# Admission arrives only after the controller durably records this helper.
|
|
180
|
-
$job = [SoJob]::MakeJob($JobName)
|
|
187
|
+
$job = [SoJob]::MakeJob($JobName, $MemoryLimit)
|
|
181
188
|
Send "ready"
|
|
182
189
|
$admission = $reader.ReadLineAsync()
|
|
183
190
|
if (-not $admission.Wait(15000) -or $admission.Result -cne "go") { throw "job admission refused" }
|
package/dist/keymap.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/** One keymap for the console and the terminal UI (docs/plans/plain-toolroll.md P1.7). A shortcut only ever presses a
|
|
2
|
+
* control the page already rendered, the same press a person's click makes, so any password field or confirm it opens
|
|
3
|
+
* still appears; with no such control on the page the key does nothing. Shortcuts sleep in text fields, with a
|
|
4
|
+
* modifier held, and while the shortcuts list is open. Navigation keys go to a page and post nothing. */
|
|
5
|
+
/** What a key does in the console. `press`: the first visible, enabled control matching these, in order. */
|
|
6
|
+
export type WebShortcut = {
|
|
7
|
+
kind: "go";
|
|
8
|
+
href: string;
|
|
9
|
+
} | {
|
|
10
|
+
kind: "press";
|
|
11
|
+
selectors: readonly string[];
|
|
12
|
+
} | {
|
|
13
|
+
kind: "search";
|
|
14
|
+
} | {
|
|
15
|
+
kind: "help";
|
|
16
|
+
};
|
|
17
|
+
/** What a key does in the terminal UI, or null with the reason it has none there (a named gap). */
|
|
18
|
+
export type TuiShortcut = "new" | "ink" | "send-back" | "retry" | "list" | "help";
|
|
19
|
+
export type Shortcut = {
|
|
20
|
+
keys: string;
|
|
21
|
+
says: string;
|
|
22
|
+
web: WebShortcut;
|
|
23
|
+
tui: TuiShortcut | {
|
|
24
|
+
none: string;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
/** The page's ink act: Approve (the approve control itself, password popover and all) or Accept. */
|
|
28
|
+
export declare const INK_ACT_SELECTORS: readonly ["[data-ink-approve]", '[data-ink-act="accept"]', "[data-accept-result][data-primary-action]"];
|
|
29
|
+
/** Send back: the control that opens the Send back form (never its submit: the note comes first). */
|
|
30
|
+
export declare const SEND_BACK_SELECTORS: readonly ['[data-act="request-changes"]', '[data-act="revise"]', 'a.result-feedback-link[href="#request-changes"]'];
|
|
31
|
+
/** Retry: the result's Retry, or the failed box's own Retry form. */
|
|
32
|
+
export declare const RETRY_SELECTORS: readonly ['[data-act="retry"]', 'form[data-retry] button[type="submit"]', 'form[data-rebuild] button[type="submit"]'];
|
|
33
|
+
export declare const SHORTCUTS: readonly Shortcut[];
|
|
34
|
+
/** The keys typed, as the help list shows them: "g h" reads "g then h". */
|
|
35
|
+
export declare const keyWords: (keys: string) => string;
|
|
36
|
+
/**
|
|
37
|
+
* The console's key routing, as an inline script (it runs inside chromeScript's closure, which defines `show` for the
|
|
38
|
+
* jump-to palette, `toggleHelp`, `helpOpen`, `close` and `go`). Generated from SHORTCUTS, so the table is the only
|
|
39
|
+
* place a key is defined.
|
|
40
|
+
*/
|
|
41
|
+
export declare function webKeymapScript(): string;
|
package/dist/keymap.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/** One keymap for the console and the terminal UI (docs/plans/plain-toolroll.md P1.7). A shortcut only ever presses a
|
|
2
|
+
* control the page already rendered, the same press a person's click makes, so any password field or confirm it opens
|
|
3
|
+
* still appears; with no such control on the page the key does nothing. Shortcuts sleep in text fields, with a
|
|
4
|
+
* modifier held, and while the shortcuts list is open. Navigation keys go to a page and post nothing. */
|
|
5
|
+
/** The page's ink act: Approve (the approve control itself, password popover and all) or Accept. */
|
|
6
|
+
export const INK_ACT_SELECTORS = ["[data-ink-approve]", '[data-ink-act="accept"]', "[data-accept-result][data-primary-action]"];
|
|
7
|
+
/** Send back: the control that opens the Send back form (never its submit: the note comes first). */
|
|
8
|
+
export const SEND_BACK_SELECTORS = ['[data-act="request-changes"]', '[data-act="revise"]', 'a.result-feedback-link[href="#request-changes"]'];
|
|
9
|
+
/** Retry: the result's Retry, or the failed box's own Retry form. */
|
|
10
|
+
export const RETRY_SELECTORS = ['[data-act="retry"]', 'form[data-retry] button[type="submit"]', 'form[data-rebuild] button[type="submit"]'];
|
|
11
|
+
export const SHORTCUTS = [
|
|
12
|
+
{ keys: "n", says: "new task", web: { kind: "go", href: "/tasks/new" }, tui: "new" },
|
|
13
|
+
{ keys: "a", says: "Approve or Accept (presses the button on the page)", web: { kind: "press", selectors: INK_ACT_SELECTORS }, tui: "ink" },
|
|
14
|
+
{ keys: "b", says: "Send back (opens its note)", web: { kind: "press", selectors: SEND_BACK_SELECTORS }, tui: "send-back" },
|
|
15
|
+
{ keys: "r", says: "Retry (presses the button on the page)", web: { kind: "press", selectors: RETRY_SELECTORS }, tui: "retry" },
|
|
16
|
+
{ keys: "/", says: "search", web: { kind: "search" }, tui: { none: "The terminal has no search; press w to open the console." } },
|
|
17
|
+
{ keys: "g h", says: "go Home", web: { kind: "go", href: "/" }, tui: "list" },
|
|
18
|
+
{ keys: "g t", says: "go to Tasks", web: { kind: "go", href: "/tasks" }, tui: "list" },
|
|
19
|
+
{ keys: "?", says: "these shortcuts", web: { kind: "help" }, tui: "help" },
|
|
20
|
+
];
|
|
21
|
+
/** The keys typed, as the help list shows them: "g h" reads "g then h". */
|
|
22
|
+
export const keyWords = (keys) => keys.split(" ").join(" then ");
|
|
23
|
+
/**
|
|
24
|
+
* The console's key routing, as an inline script (it runs inside chromeScript's closure, which defines `show` for the
|
|
25
|
+
* jump-to palette, `toggleHelp`, `helpOpen`, `close` and `go`). Generated from SHORTCUTS, so the table is the only
|
|
26
|
+
* place a key is defined.
|
|
27
|
+
*/
|
|
28
|
+
export function webKeymapScript() {
|
|
29
|
+
const single = {}, after = {};
|
|
30
|
+
for (const one of SHORTCUTS) {
|
|
31
|
+
const parts = one.keys.split(" ");
|
|
32
|
+
if (parts.length === 1)
|
|
33
|
+
single[parts[0]] = one.web;
|
|
34
|
+
else if (parts[0] === "g")
|
|
35
|
+
after[parts[1]] = one.web;
|
|
36
|
+
}
|
|
37
|
+
return `var KEYS=${JSON.stringify(single)},GKEYS=${JSON.stringify(after)};` +
|
|
38
|
+
// A control a person could press right now: rendered, shown, and not disabled.
|
|
39
|
+
`function pressable(el){if(!el||el.disabled||el.getAttribute("aria-disabled")==="true")return false;` +
|
|
40
|
+
`if(el.closest("[hidden],[inert]"))return false;var r=el.getClientRects();return r.length>0;}` +
|
|
41
|
+
// Press it exactly as a click would; a submit whose form isn't complete (an empty password, a required reason)
|
|
42
|
+
// shows the browser's own prompt for it instead of posting.
|
|
43
|
+
`function press(selectors){for(var s=0;s<selectors.length;s++){var all=document.querySelectorAll(selectors[s]);` +
|
|
44
|
+
`for(var i=0;i<all.length;i++){var el=all[i];if(!pressable(el))continue;` +
|
|
45
|
+
`var form=el.form||null;var submits=el.tagName.toLowerCase()==="button"&&(el.type==="submit"||el.getAttribute("type")===null)&&form;` +
|
|
46
|
+
`if(submits&&!form.checkValidity()){form.reportValidity();return true;}` +
|
|
47
|
+
`el.click();return true;}}return false;}` +
|
|
48
|
+
`function search(){if(document.querySelector("[data-workspace-shell]")){window.dispatchEvent(new CustomEvent("so:open-search",{detail:document.activeElement}));return;}show();}` +
|
|
49
|
+
`function run(act){if(act.kind==="go"){go(act.href);return true;}if(act.kind==="press")return press(act.selectors);` +
|
|
50
|
+
`if(act.kind==="search"){search();return true;}if(act.kind==="help"){toggleHelp();return true;}return false;}`;
|
|
51
|
+
}
|
package/dist/knowledge-cli.js
CHANGED
|
@@ -112,7 +112,7 @@ export async function runKnowledgeCommand(positional, options, context) {
|
|
|
112
112
|
admittedToken = token;
|
|
113
113
|
}
|
|
114
114
|
const cacheRoot = context.evidenceRoot ? join(dirname(context.evidenceRoot), 'repository-context') : namedPath(join(homedir(), '.cache'), ['repository-context']);
|
|
115
|
-
const result = await repositoryContext({ repo: resolve(repo), query, mode: spec.action === 'impact' ? 'impact' : 'search', refresh: spec.action === 'refresh', cacheRoot, ...(typeof base === 'string' ? { baseRevision: base } : {}) });
|
|
115
|
+
const result = await repositoryContext({ repo: resolve(repo), ...(admittedToken ? { project: resolve(repo) } : {}), query, mode: spec.action === 'impact' ? 'impact' : 'search', refresh: spec.action === 'refresh', cacheRoot, ...(typeof base === 'string' ? { baseRevision: base } : {}) });
|
|
116
116
|
if (admittedToken) {
|
|
117
117
|
const current = authenticateCoordinator(context.store, admittedToken);
|
|
118
118
|
if (!current.ok)
|
package/dist/knowledge-ui.d.ts
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import { type KnowledgeDraft, type KnowledgeSelection, type KnowledgeView } from './project-knowledge.js';
|
|
2
2
|
import { type Html } from './html.js';
|
|
3
|
+
import { type MemoryUse } from './memory-use.js';
|
|
3
4
|
/** A section as a frame (after ReUI's Frame, MIT): its heading on the raised well, its body one paper panel. The
|
|
4
5
|
* cards inside become hairline-divided rows of that panel, never cards in a card. */
|
|
5
6
|
export declare function framedSection(heading: string, body: Html | string, extra?: string): Html;
|
|
6
7
|
export declare const KNOWLEDGE_CSS = ".knowledge{max-width:780px;min-width:0;overflow-wrap:anywhere}.knowledge .card{padding:20px;min-width:0;margin:16px 0}.knowledge h2{margin:0 0 12px}.knowledge form{display:grid;gap:12px;margin:0}.knowledge label{display:grid;gap:6px}.knowledge textarea,.knowledge input,.knowledge select{box-sizing:border-box;width:100%;min-width:0;max-width:100%}.knowledge textarea{resize:vertical;min-height:110px}.knowledge button,.knowledge summary,.knowledge .button-link{min-height:44px}.knowledge button{justify-self:start;white-space:nowrap}.knowledge summary{padding:12px 0;cursor:pointer}.knowledge pre{white-space:pre-wrap;overflow-wrap:anywhere;font:inherit}.knowledge .knowledge-actions{display:flex;gap:12px;flex-wrap:wrap}.knowledge .meta{font-size:.86rem}.knowledge .knowledge-source{display:block}.knowledge details+details{border-top:1px solid var(--so-line)}.memory-stats{display:flex;flex-wrap:wrap;gap:.4rem}.memory-stats span+span::before{content:\"\u00B7\";margin-right:.4rem}.knowledge.frame{margin:0 0 16px}.knowledge.frame>.frame-head h2,.knowledge.frame>summary.frame-head{margin:0;font-size:13px;font-weight:600;line-height:1.4}.knowledge.frame>summary.frame-head{min-height:44px;padding:4px 10px}.knowledge.frame>.frame-panel{min-width:0}.knowledge.frame>.frame-panel>:first-child{margin-top:0}.knowledge.frame>.frame-panel>:last-child{margin-bottom:0}.knowledge .frame-panel>details.card,.knowledge .frame-panel .learning .card{margin:0;padding:0;border:0;border-radius:0;background:none;box-shadow:none}.knowledge .frame-panel>details.card+details.card,.knowledge .frame-panel .learning .card+.card{border-top:1px solid var(--so-line);padding-top:4px}.knowledge.frame>summary.frame-head{justify-content:flex-start}.knowledge form.field-row{grid-template-columns:minmax(0,1fr) auto;align-items:end}.knowledge form.field-row>label,.knowledge form.field-row :is(input,select,button){margin:0}@media(max-width:600px){.knowledge .card{padding:16px}.knowledge h2{font-size:1.1rem}}";
|
|
7
|
-
export declare function knowledgeHtml(view: KnowledgeView, csrf: string, canManage: boolean, draft?: KnowledgeDraft, error?: string): Html;
|
|
8
|
+
export declare function knowledgeHtml(view: KnowledgeView, csrf: string, canManage: boolean, draft?: KnowledgeDraft, error?: string, use?: ReadonlyMap<string, MemoryUse>): Html;
|
|
8
9
|
export declare function knowledgeContextHtml(snapshot: KnowledgeSelection | null): Html | '';
|
|
9
10
|
import type { Decision, MemoryHit } from './project-memory.js';
|
|
10
|
-
export declare const MEMORY_INTRO = "What the lead and crew read before they work. Every change keeps its history.";
|
|
11
|
+
export declare const MEMORY_INTRO = "What the lead and crew read before they work. Every change keeps its history. Last included is counted in runs.";
|
|
11
12
|
export declare function decisionsHtml(repo: string, decisions: Decision[], csrf: string, canManage: boolean, draft?: {
|
|
12
13
|
claim?: string;
|
|
13
14
|
why?: string;
|
|
14
|
-
}, error?: string): Html;
|
|
15
|
+
}, error?: string, use?: ReadonlyMap<string, MemoryUse>, afterwards?: (decision: Decision) => string | null): Html;
|
|
15
16
|
export declare function memorySearchHtml(repo: string, query: string, hits: MemoryHit[] | null): Html;
|
|
16
17
|
import type { MemoryProposal } from './memory-pass.js';
|
|
17
18
|
export declare function proposalsHtml(repo: string, proposals: MemoryProposal[], status: {
|
|
@@ -25,4 +26,20 @@ export declare function proposalsHtml(repo: string, proposals: MemoryProposal[],
|
|
|
25
26
|
};
|
|
26
27
|
openGaps: number;
|
|
27
28
|
pending: number;
|
|
28
|
-
} | null, csrf: string, canManage: boolean): Html;
|
|
29
|
+
} | null, csrf: string, canManage: boolean, who?: string): Html;
|
|
30
|
+
import type { TidyProposal } from './memory-tidy.js';
|
|
31
|
+
export declare function cleanUpHtml(repo: string, proposals: readonly TidyProposal[], manage: boolean): Html;
|
|
32
|
+
/** On a saved version's page: the references that aren't in current knowledge, each back as a new version. */
|
|
33
|
+
export declare function referenceRestoreHtml(repo: string, revision: number, references: readonly {
|
|
34
|
+
id: string;
|
|
35
|
+
title: string;
|
|
36
|
+
}[], manage: boolean): Html | '';
|
|
37
|
+
/** Retired decisions, undoable merges and disabled lessons, each with its way back. */
|
|
38
|
+
export declare function restoreHtml(repo: string, retired: readonly Decision[], merges: readonly {
|
|
39
|
+
merge: number;
|
|
40
|
+
successor: number;
|
|
41
|
+
predecessors: number[];
|
|
42
|
+
}[], lessons: readonly {
|
|
43
|
+
id: number;
|
|
44
|
+
observation: string;
|
|
45
|
+
}[], manage: boolean): Html | '';
|