gentle-pi 4.0.0 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +157 -18
- package/assets/agents/gentle-ai-explore.md +11 -4
- package/assets/agents/gentle-ai-verify.md +23 -4
- package/assets/agents/gentle-ai-worker.md +6 -6
- package/assets/install-wizard/index.html +47 -0
- package/assets/install-wizard/wizard.css +1279 -0
- package/assets/install-wizard/wizard.js +951 -0
- package/assets/orchestrator-delegation.md +70 -163
- package/assets/orchestrator-memory.md +11 -4
- package/assets/orchestrator-prompts.md +48 -0
- package/assets/orchestrator-tracking.md +53 -0
- package/assets/orchestrator-verification.md +28 -0
- package/assets/orchestrator-writer.md +38 -0
- package/assets/orchestrator.md +29 -25
- package/assets/sounds/LICENSE.md +52 -0
- package/assets/sounds/attention.wav +0 -0
- package/assets/sounds/error.wav +0 -0
- package/assets/sounds/success.wav +0 -0
- package/bin/engram-guard +9 -0
- package/bin/gentle-shell-install.mjs +302 -0
- package/bin/gentle-shell.mjs +708 -22
- package/contracts/review-provider-contract-mirror/provider-contract.lock.json +7 -7
- package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/manifest.json +2 -2
- package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/orchestration/pi.md +16 -4
- package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/generated/provider-capabilities.baseline.json +2 -2
- package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/generated/provider-roles.baseline.json +1 -1
- package/docs/auto-permission-answers.md +25 -0
- package/docs/auto-permission-evidence.md +39 -0
- package/docs/bundled-install.md +399 -0
- package/docs/delegated-verification.md +3 -5
- package/docs/gentle-agents-activity.md +513 -0
- package/docs/gentle-shell-uninstall.md +136 -0
- package/docs/gentle-shell.md +90 -33
- package/docs/install-wizard.md +2040 -0
- package/docs/native-authority-architecture.md +1 -1
- package/docs/permission-modes.md +127 -0
- package/docs/prompt-history.md +1 -1
- package/docs/readme-reference.md +310 -47
- package/docs/render-cache-performance.md +28 -0
- package/docs/session-profile-format.md +39 -0
- package/docs/sound-notifications-proposal.md +191 -0
- package/docs/sound-notifications.md +114 -0
- package/docs/yolo-mode.md +11 -10
- package/extensions/ask-user-choice.ts +4 -0
- package/extensions/ask-user-question.ts +6 -1
- package/extensions/child-context.ts +11 -2
- package/extensions/child-safety.ts +4 -2
- package/extensions/codegraph-tools.ts +18 -5
- package/extensions/gentle-agents.ts +718 -87
- package/extensions/gentle-ai.ts +991 -263
- package/extensions/gentle-herdr-activity.ts +81 -0
- package/extensions/gentle-notifications.ts +104 -0
- package/extensions/gentle-shell.ts +271 -80
- package/extensions/gentle-todo.ts +123 -18
- package/extensions/history/index.ts +11 -6
- package/extensions/pi-pretty.ts +39 -3
- package/extensions/quiet-tools.ts +91 -21
- package/extensions/skill-registry.ts +175 -46
- package/extensions/startup-banner.ts +11 -2
- package/lib/agent-home.ts +10 -1
- package/lib/agent-model-resolution.ts +80 -0
- package/lib/agent-profiles.ts +13 -3
- package/lib/agents-completion-delivery.ts +11 -3
- package/lib/agents-messaging.ts +1 -1
- package/lib/agents-protocol.ts +40 -1
- package/lib/agents-runner.ts +90 -15
- package/lib/auto-permission-answers.ts +133 -0
- package/lib/auto-permission-classifier.ts +459 -0
- package/lib/auto-permission-evidence.ts +88 -0
- package/lib/auto-permission-memory.ts +69 -0
- package/lib/auto-permission-runtime.ts +484 -0
- package/lib/auto-permission-tool-context.ts +61 -0
- package/lib/background-jobs-tools.ts +202 -0
- package/lib/background-jobs.ts +253 -0
- package/lib/background-monitor.ts +154 -0
- package/lib/bounded-writer-admission.ts +83 -10
- package/lib/builtin-codemode-optout.ts +166 -0
- package/lib/card-content-policy.ts +85 -0
- package/lib/child-package-injection.ts +62 -0
- package/lib/codemode-renderer.ts +14 -4
- package/lib/command-palette-catalog.ts +3 -2
- package/lib/context-threshold.ts +26 -0
- package/lib/destructive-command-guard.ts +104 -28
- package/lib/gentle-ai-binary.ts +109 -4
- package/lib/gentle-shell-bundled.ts +535 -0
- package/lib/gentle-shell-launcher.ts +75 -24
- package/lib/gentle-shell-startup-flow.ts +54 -0
- package/lib/gentle-shell-startup-update.ts +92 -0
- package/lib/gentle-shell-uninstall.ts +840 -0
- package/lib/gentle-shell-update-panel.ts +231 -0
- package/lib/herdr-activity.ts +183 -0
- package/lib/inprocess-reviewer.ts +3 -0
- package/lib/jobs-sidebar-state.ts +20 -0
- package/lib/jobs-view.ts +161 -0
- package/lib/model-price-ratio.ts +83 -0
- package/lib/model-routing-authority.ts +6 -1
- package/lib/nan-provider.ts +33 -11
- package/lib/native-review-cli.ts +194 -73
- package/lib/notification-audio-native.ts +162 -0
- package/lib/notification-audio-windows.ts +399 -0
- package/lib/notification-audio.ts +332 -0
- package/lib/notification-customize.ts +241 -0
- package/lib/notification-events.ts +113 -0
- package/lib/notification-policy.ts +148 -0
- package/lib/notification-pulse-client.ts +367 -0
- package/lib/notification-pulse-protocol.ts +305 -0
- package/lib/notification-pulse-stream.ts +213 -0
- package/lib/notification-pulse-worker.ts +66 -0
- package/lib/notification-scheduler.ts +173 -0
- package/lib/notification-service.ts +202 -0
- package/lib/notification-ui.ts +87 -0
- package/lib/odd-phase.ts +1 -1
- package/lib/orchestrator-alias.ts +20 -0
- package/lib/orchestrator-catalog.ts +79 -0
- package/lib/orchestrator-consultation.ts +61 -0
- package/lib/orchestrator-discovery.ts +49 -0
- package/lib/orchestrator-helper-consent.ts +122 -0
- package/lib/orchestrator-helper.ts +167 -0
- package/lib/orchestrator-message-card.ts +105 -0
- package/lib/orchestrator-presence.ts +115 -14
- package/lib/orchestrator-scope.ts +80 -0
- package/lib/orchestrator-state.ts +107 -0
- package/lib/orchestrator-tool-card.ts +280 -0
- package/lib/orchestrator-work-search.ts +218 -0
- package/lib/orchestrator-work.ts +79 -0
- package/lib/permission-mode-policy.ts +59 -0
- package/lib/review-candidate-view-owner.ts +8 -6
- package/lib/review-candidate-view.ts +3 -2
- package/lib/review-compact-contract.ts +2 -2
- package/lib/review-host-relay.ts +7 -2
- package/lib/review-integration-v2.ts +100 -22
- package/lib/review-publication-gate.ts +1 -1
- package/lib/review-risk-assessment.ts +77 -51
- package/lib/review-sidebar-state.ts +2 -1
- package/lib/review-transaction.ts +72 -3
- package/lib/runtime-metrics-children.ts +1 -1
- package/lib/runtime-metrics-native.ts +3 -2
- package/lib/selection-engine.ts +22 -7
- package/lib/session-change-capture.ts +5 -4
- package/lib/session-changes.ts +27 -22
- package/lib/session-profile-binding.ts +83 -0
- package/lib/session-profile-freeze.ts +261 -0
- package/lib/session-profile-persistence.ts +187 -0
- package/lib/session-worktree-registry.ts +74 -8
- package/lib/shell-bar.ts +41 -11
- package/lib/shell-changes-view.ts +40 -13
- package/lib/shell-changes.ts +88 -71
- package/lib/shell-prompt.ts +6 -4
- package/lib/shell-sidebar-layout.ts +5 -1
- package/lib/shell-todo.ts +81 -26
- package/lib/shell-usage.ts +4 -3
- package/lib/status-timing-diagnostics.ts +201 -0
- package/lib/telemetry-trigger.ts +3 -1
- package/lib/vim-editor-adapter.ts +27 -5
- package/lib/visual-customization-policy.ts +20 -9
- package/lib/visual-customize-view.ts +171 -20
- package/lib/writer-surfaces.ts +162 -0
- package/lib/yolo-session-policy.ts +198 -121
- package/package.json +5 -5
- package/runtime/child-package-injection.mjs +63 -0
- package/runtime/gentle-ai-binary.mjs +109 -4
- package/runtime/gentle-shell-bundled.mjs +536 -0
- package/runtime/gentle-shell-launcher.mjs +74 -23
- package/runtime/gentle-shell-startup-flow.mjs +55 -0
- package/runtime/gentle-shell-startup-update.mjs +93 -0
- package/runtime/gentle-shell-uninstall.mjs +841 -0
- package/runtime/gentle-shell-update-panel.mjs +232 -0
- package/runtime/native-review-cli.mjs +191 -70
- package/runtime/review-integration-v2.mjs +100 -22
- package/runtime/review-risk-assessment.mjs +77 -51
- package/runtime/status-timing-diagnostics.mjs +202 -0
- package/runtime/telemetry-trigger.mjs +3 -1
- package/scripts/bootstrap.cmd +255 -0
- package/scripts/bootstrap.sh +168 -0
- package/scripts/build-distribution.mjs +157 -0
- package/scripts/build-installer-bundles.mjs +136 -0
- package/scripts/build-runtime-modules.mjs +7 -0
- package/scripts/bundled-install.mjs +1308 -0
- package/scripts/bundled-wizard.mjs +399 -0
- package/scripts/check-types.mjs +4 -6
- package/scripts/gentle-ai-installer.mjs +79 -32
- package/scripts/install-gentle-ai.mjs +48 -1
- package/scripts/install-tui-mode-setting.mjs +10 -1
- package/scripts/install-wizard-preview.mjs +154 -0
- package/scripts/installer-downloads.mjs +648 -0
- package/scripts/installer-preflight.mjs +387 -0
- package/scripts/installer-probes.mjs +564 -0
- package/scripts/installer-runner.mjs +1102 -0
- package/scripts/installer-server.mjs +881 -0
- package/scripts/installer-windows-artifacts.json +15 -0
- package/scripts/installer-windows.mjs +752 -0
- package/scripts/main-channel.mjs +452 -0
- package/scripts/npm-pack-result.mjs +31 -0
- package/scripts/pi-update.mjs +223 -0
- package/scripts/render-cache-performance-worker.mjs +205 -0
- package/scripts/render-cache-performance.mjs +115 -0
- package/scripts/run-test-suite.mjs +22 -8
- package/scripts/test-installer-acceptance.sh +161 -0
- package/scripts/test-packed-runner.mjs +23 -5
- package/scripts/types-baseline.json +2 -92
- package/scripts/verify-package-files.mjs +82 -18
- package/skills/gentle-ai/SKILL.md +12 -11
- package/tests/agent-home.test.ts +56 -3
- package/tests/agent-model-resolution.test.ts +113 -0
- package/tests/agent-profiles.test.ts +12 -0
- package/tests/agent-risk-escalation.test.ts +123 -0
- package/tests/agents-completion-delivery.test.ts +17 -0
- package/tests/agents-protocol.test.ts +57 -0
- package/tests/agents-queries.test.ts +9 -9
- package/tests/agents-runner-process.test.ts +31 -0
- package/tests/agents-runner.test.ts +89 -4
- package/tests/append-system-prompt-route.test.ts +18 -1
- package/tests/auto-mode-human-answers.test.ts +68 -0
- package/tests/auto-mode-invariants.test.ts +276 -0
- package/tests/auto-mode-sdk.test.ts +84 -0
- package/tests/auto-mode.test.ts +49 -0
- package/tests/auto-permission-answers.test.ts +170 -0
- package/tests/auto-permission-classifier.test.ts +634 -0
- package/tests/auto-permission-compound.test.ts +24 -0
- package/tests/auto-permission-evidence.test.ts +88 -0
- package/tests/auto-permission-memory.test.ts +46 -0
- package/tests/auto-permission-public-regressions.test.ts +111 -0
- package/tests/auto-permission-runtime.test.ts +454 -0
- package/tests/autonomous-guard.test.ts +86 -16
- package/tests/background-cache-warming.test.ts +2 -1
- package/tests/background-jobs.test.ts +227 -0
- package/tests/background-monitor.test.ts +195 -0
- package/tests/background-subagents-default-mode.test.ts +28 -5
- package/tests/bounded-stop-contract.test.ts +85 -0
- package/tests/bounded-writer-admission.test.ts +52 -1
- package/tests/bridge-wake-lifecycle.test.ts +129 -0
- package/tests/bridge-wake-rendering.test.ts +56 -0
- package/tests/build-distribution.test.ts +269 -0
- package/tests/builtin-codemode-optout.test.ts +242 -0
- package/tests/bundled-install.test.ts +1319 -0
- package/tests/bundled-wizard.test.ts +505 -0
- package/tests/card-content-policy.test.ts +68 -0
- package/tests/card-style-policy.test.ts +34 -2
- package/tests/child-codegraph-bootstrap.test.ts +74 -0
- package/tests/child-package-entrypoints.test.ts +65 -0
- package/tests/child-package-injection.test.ts +74 -0
- package/tests/child-safety.test.ts +12 -0
- package/tests/child-web-package.test.ts +224 -0
- package/tests/codegraph-tools.test.ts +35 -0
- package/tests/codemode-rendering.test.ts +107 -2
- package/tests/command-palette.test.ts +5 -5
- package/tests/context-threshold.test.ts +31 -0
- package/tests/delegate-for-reason-contract.test.ts +327 -0
- package/tests/delegated-key-learnings-contract.test.ts +3 -2
- package/tests/devbinary/native-review-parity.devtest.ts +1 -1
- package/tests/devbinary/pi-host-relay.devtest.ts +173 -3
- package/tests/engram-safety.test.ts +316 -0
- package/tests/generic-agent-tools.test.ts +16 -1
- package/tests/gentle-agents.test.ts +1895 -76
- package/tests/gentle-ai-binary-isolation.test.ts +135 -0
- package/tests/gentle-ai-binary.test.ts +19 -2
- package/tests/gentle-ai-child-guards.test.ts +202 -0
- package/tests/gentle-ai-dev-binary-surfacing.test.ts +125 -15
- package/tests/gentle-ai-installer.test.ts +548 -58
- package/tests/gentle-ai-prompt-model-routing.test.ts +123 -0
- package/tests/gentle-ai-renderer.test.ts +6 -6
- package/tests/gentle-ai.test.ts +999 -31
- package/tests/gentle-herdr-activity.test.ts +100 -0
- package/tests/gentle-notifications.test.ts +427 -0
- package/tests/gentle-shell-bin.test.ts +263 -19
- package/tests/gentle-shell-bundled.test.ts +1036 -0
- package/tests/gentle-shell-launcher.test.ts +251 -25
- package/tests/gentle-shell-startup-flow.test.ts +75 -0
- package/tests/gentle-shell-startup-update.test.ts +85 -0
- package/tests/gentle-shell-uninstall.test.ts +978 -0
- package/tests/gentle-shell-update-panel.test.ts +158 -0
- package/tests/gentle-shell.test.ts +611 -100
- package/tests/gentle-todo.test.ts +317 -14
- package/tests/herdr-activity.test.ts +243 -0
- package/tests/history-agent-home.test.ts +81 -0
- package/tests/history-child-guard.test.ts +70 -0
- package/tests/inprocess-reviewer.test.ts +20 -0
- package/tests/install-gentle-ai-failure.test.ts +69 -0
- package/tests/install-tui-mode-setting.test.ts +15 -0
- package/tests/install-wizard.test.ts +1223 -0
- package/tests/installer-bundles.test.ts +122 -0
- package/tests/installer-posix-bootstrap.test.ts +1201 -0
- package/tests/installer-preflight.test.ts +750 -0
- package/tests/installer-probes.test.ts +1106 -0
- package/tests/installer-runner.test.ts +2159 -0
- package/tests/installer-server.test.ts +1567 -0
- package/tests/installer-windows-bootstrap.test.ts +2410 -0
- package/tests/jobs-view.test.ts +160 -0
- package/tests/main-channel-upgrade.test.ts +395 -0
- package/tests/main-channel.test.ts +344 -0
- package/tests/maintainer/provider-relay.maintest.ts +13 -12
- package/tests/model-price-ratio.test.ts +116 -0
- package/tests/model-profiles-preservation.test.ts +153 -0
- package/tests/model-routing-package.test.ts +10 -0
- package/tests/nan-provider-runtime.test.ts +156 -0
- package/tests/nan-provider.test.ts +28 -18
- package/tests/native-binary-gate.test.ts +2 -0
- package/tests/native-review-capability-contract.test.ts +7 -1
- package/tests/native-review-cli.test.ts +236 -1
- package/tests/native-review-consent.test.ts +2 -2
- package/tests/native-review-parity-runtime.test.ts +1 -1
- package/tests/native-review-parity.test.ts +12 -48
- package/tests/native-risk-evidence-parity.test.ts +21 -0
- package/tests/no-self-pause-contract.test.ts +87 -0
- package/tests/notification-audio-native.test.ts +281 -0
- package/tests/notification-audio-windows.test.ts +581 -0
- package/tests/notification-audio.test.ts +548 -0
- package/tests/notification-customize.test.ts +655 -0
- package/tests/notification-events.test.ts +115 -0
- package/tests/notification-policy.test.ts +201 -0
- package/tests/notification-pulse-client.test.ts +356 -0
- package/tests/notification-pulse-protocol.test.ts +213 -0
- package/tests/notification-pulse-stream.test.ts +320 -0
- package/tests/notification-scheduler.test.ts +258 -0
- package/tests/notification-ui.test.ts +157 -0
- package/tests/npm-pack-result.test.ts +41 -0
- package/tests/odd-integration.test.ts +14 -5
- package/tests/odd-routing-canonical-ratchet.test.ts +53 -12
- package/tests/odd-routing-contract.test.ts +80 -23
- package/tests/odd-runtime-delegation-gate.test.ts +4 -1
- package/tests/orchestrator-budget.test.ts +29 -6
- package/tests/orchestrator-catalog.test.ts +146 -0
- package/tests/orchestrator-consultation-sdk.test.ts +614 -0
- package/tests/orchestrator-consultation.test.ts +111 -0
- package/tests/orchestrator-discovery.test.ts +241 -0
- package/tests/orchestrator-helper-consent.test.ts +171 -0
- package/tests/orchestrator-helper.test.ts +297 -0
- package/tests/orchestrator-message-card.test.ts +190 -0
- package/tests/orchestrator-presence.test.ts +23 -0
- package/tests/orchestrator-rdd-ownership.test.ts +6 -5
- package/tests/orchestrator-scope.test.ts +107 -0
- package/tests/orchestrator-state.test.ts +114 -0
- package/tests/orchestrator-status-publication-contract.test.ts +34 -0
- package/tests/orchestrator-tool-card.test.ts +398 -0
- package/tests/orchestrator-work-search.test.ts +318 -0
- package/tests/orchestrator-work.test.ts +147 -0
- package/tests/package-manifest.test.ts +105 -21
- package/tests/packed-runner-owned-path.test.ts +35 -1
- package/tests/parallel-reason-contract.test.ts +21 -0
- package/tests/parallel-writer-admission.test.ts +210 -0
- package/tests/permission-mode-policy.test.ts +194 -0
- package/tests/persona-single-channel.test.ts +4 -12
- package/tests/pi-pretty.test.ts +56 -0
- package/tests/pi-update-launcher.test.ts +87 -0
- package/tests/pi-update.test.ts +186 -0
- package/tests/provider-defect-handoff.test.ts +4 -3
- package/tests/quiet-tool-rendering.test.ts +160 -3
- package/tests/rdd-aware-verification-contract.test.ts +29 -41
- package/tests/rdd-status-line.test.ts +52 -0
- package/tests/render-cache-performance.test.ts +48 -0
- package/tests/review-agent-end-preflight.test.ts +62 -10
- package/tests/review-candidate-view.test.ts +101 -12
- package/tests/review-contract-prompt.test.ts +33 -4
- package/tests/review-controller-native-recovery.test.ts +327 -17
- package/tests/review-controller-native-routing.test.ts +255 -1
- package/tests/review-controller-workspace-root.test.ts +43 -2
- package/tests/review-controller.test.ts +2 -2
- package/tests/review-correction-lifecycle.test.ts +2 -1
- package/tests/review-gate.test.ts +7 -4
- package/tests/review-graph-schema.test.ts +2 -2
- package/tests/review-host-relay-routing.test.ts +5 -3
- package/tests/review-host-relay.test.ts +49 -2
- package/tests/review-integration-v2-forward.test.ts +21 -1
- package/tests/review-json-arguments.test.ts +167 -0
- package/tests/review-last-event-closure.test.ts +157 -8
- package/tests/review-ledger-contract.test.ts +5 -4
- package/tests/review-policy-judgment-day.test.ts +1 -1
- package/tests/review-policy-ordinary.test.ts +2 -2
- package/tests/review-relay-transport-agent.test.ts +66 -1
- package/tests/review-risk-assessment.test.ts +109 -33
- package/tests/review-session-standing-permission-controller.test.ts +16 -17
- package/tests/review-session-standing-permission-ipc.test.ts +6 -5
- package/tests/review-test-fixtures.ts +7 -1
- package/tests/review-transaction.test.ts +18 -1
- package/tests/run-test-suite.test.ts +83 -5
- package/tests/runtime-harness.mjs +42 -15
- package/tests/runtime-metrics-extension.test.ts +1 -1
- package/tests/runtime-metrics-native.test.ts +31 -1
- package/tests/selection-engine.test.ts +65 -5
- package/tests/session-change-capture.test.ts +10 -10
- package/tests/session-changes-shell.test.ts +242 -48
- package/tests/session-changes.test.ts +17 -24
- package/tests/session-profile-binding.test.ts +163 -0
- package/tests/session-profile-freeze.test.ts +394 -0
- package/tests/session-profile-persistence.test.ts +334 -0
- package/tests/session-subject-routing-contract.test.ts +34 -0
- package/tests/session-worktree-registry.test.ts +57 -2
- package/tests/shell-bar.test.ts +40 -0
- package/tests/shell-changes-view.test.ts +85 -34
- package/tests/shell-changes.test.ts +79 -62
- package/tests/shell-render-cache-runtime.test.ts +69 -0
- package/tests/shell-render-cache.test.ts +129 -0
- package/tests/shell-sidebar-layout.test.ts +27 -2
- package/tests/shell-todo.test.ts +132 -11
- package/tests/shell-usage-view.test.ts +1 -1
- package/tests/shell-usage.test.ts +35 -0
- package/tests/short-check-routing-contract.test.ts +49 -0
- package/tests/skill-registry-watcher.test.ts +119 -0
- package/tests/skill-registry.test.ts +311 -5
- package/tests/startup-banner.test.ts +117 -3
- package/tests/startup-home-repo.test.ts +170 -0
- package/tests/status-timing-diagnostics.test.ts +283 -0
- package/tests/support/orchestrator-modules.ts +19 -0
- package/tests/task-size-routing-contract.test.ts +241 -0
- package/tests/telemetry-trigger.test.ts +27 -1
- package/tests/test-strength-contract.test.ts +124 -0
- package/tests/verify-agent-contract.test.ts +90 -0
- package/tests/verify-first-launch-contract.test.ts +139 -0
- package/tests/verify-package-files.test.ts +56 -0
- package/tests/vim-editor-adapter.test.ts +47 -21
- package/tests/visual-customization-policy.test.ts +32 -3
- package/tests/visual-customize-view.test.ts +288 -10
- package/tests/writer-edit-surface-scope.test.ts +59 -2
- package/tests/writer-surfaces.test.ts +199 -0
- package/tests/yolo-customize.test.ts +18 -3
- package/tests/yolo-mode-runtime.test.ts +37 -10
- package/tests/yolo-mode.test.ts +38 -5
- package/tests/yolo-session-policy.test.ts +1 -1
- /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/README.md +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/schemas/lens.schema.json +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/schemas/refuter.schema.json +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/schemas/targeted-validator.schema.json +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/vectors/lens.json +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/vectors/refuter.json +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/vectors/targeted-validator.json +0 -0
package/lib/agents-runner.ts
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import { isSessionChangeEvidence, type SessionChangeEvidence } from "./session-changes.ts";
|
|
2
|
-
import { chmodSync, existsSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
2
|
+
import { chmodSync, existsSync, mkdtempSync, realpathSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { DefaultPackageManager, SettingsManager } from "@earendil-works/pi-coding-agent";
|
|
4
|
+
import { tmpdir, userInfo } from "node:os";
|
|
5
|
+
import { join, resolve } from "node:path";
|
|
6
|
+
import { Duplex, type Readable, type Writable } from "node:stream";
|
|
6
7
|
import { stripVTControlCharacters } from "node:util";
|
|
7
8
|
import { withoutInteractiveHost } from "./rpc-host.ts";
|
|
9
|
+
import { childPackageExtensionArgs } from "./child-package-injection.ts";
|
|
8
10
|
import { AGENT_MODE, formatModelRef, type AgentDefinition, type AgentMode, type ModelRef } from "./agents-config.ts";
|
|
9
11
|
import { CHILD_QUERY_MAX_INFLIGHT, CHILD_QUERY_TIMEOUT_MS, parseChildFrame, validChildMessage, validChildQueryId } from "./agents-messaging.ts";
|
|
10
12
|
import { ParentStandingReviewPermissionBroker } from "./review-session-standing-permission-ipc.ts";
|
|
11
13
|
import { isFinished, normalizeRpcEvent, ToolArgumentProgress, TASK_EVENT, TASK_STATUS, taskLabel, type AskRequest, type ChildResponseObservation, type TaskRecord, type TaskStore } from "./agents-protocol.ts";
|
|
14
|
+
import { WriterSurfaceRegistry, writerSurfaceConflictMessage } from "./writer-surfaces.ts";
|
|
12
15
|
|
|
13
16
|
// Gentle Agents runner. Every subagent is its own `pi --mode rpc` process:
|
|
14
17
|
// the host never runs subagent work on the TUI thread. It writes JSON
|
|
@@ -16,12 +19,12 @@ import { isFinished, normalizeRpcEvent, ToolArgumentProgress, TASK_EVENT, TASK_S
|
|
|
16
19
|
// and enforces an inactivity watchdog per task.
|
|
17
20
|
|
|
18
21
|
export interface ChildLike {
|
|
19
|
-
pid
|
|
22
|
+
pid?: number;
|
|
20
23
|
connected?: boolean;
|
|
21
|
-
stdin: Writable;
|
|
22
|
-
stdout: Readable;
|
|
24
|
+
stdin: Writable | null;
|
|
25
|
+
stdout: Readable | null;
|
|
23
26
|
stderr: Readable | null | undefined;
|
|
24
|
-
stdio?: Array<
|
|
27
|
+
stdio?: Array<Readable | Writable | null | undefined>;
|
|
25
28
|
kill(signal?: NodeJS.Signals): boolean;
|
|
26
29
|
send?(message: Record<string, unknown>, callback?: (error: Error | null) => void): boolean;
|
|
27
30
|
disconnect?(): void;
|
|
@@ -35,6 +38,7 @@ export interface SpawnOptions {
|
|
|
35
38
|
cwd: string;
|
|
36
39
|
env: NodeJS.ProcessEnv;
|
|
37
40
|
detached?: boolean;
|
|
41
|
+
windowsHide?: boolean;
|
|
38
42
|
stdio?: Array<"pipe" | "ignore" | "inherit" | "ipc" | "overlapped">;
|
|
39
43
|
}
|
|
40
44
|
|
|
@@ -126,8 +130,18 @@ export interface TaskRequest {
|
|
|
126
130
|
sessionDir: string;
|
|
127
131
|
resumeSessionPath: string | undefined;
|
|
128
132
|
env: NodeJS.ProcessEnv;
|
|
129
|
-
//
|
|
133
|
+
// Host-provided only: the launcher's package injection signal (#1690) or
|
|
134
|
+
// the curated fallback. Never derived from tool input or agent definitions.
|
|
130
135
|
extensionPaths?: string[];
|
|
136
|
+
// Same host-only provenance as extensionPaths (#1690).
|
|
137
|
+
noExtensions?: boolean;
|
|
138
|
+
// Parsed `## Allowed edit surfaces` of a bounded writer. While the task is
|
|
139
|
+
// queued or running it claims them in `cwd`; run() rejects an overlapping
|
|
140
|
+
// claim (gentle-shell#1731). Read-only agents leave this unset.
|
|
141
|
+
writerSurfaces?: readonly string[];
|
|
142
|
+
// Canonical worktree root the claim is keyed to; the parent computes it before
|
|
143
|
+
// run() so admission stays synchronous. Defaults to `cwd`.
|
|
144
|
+
writerRoot?: string;
|
|
131
145
|
// Synchronous admission recheck at dequeue, before any OS spawn. Throws fail
|
|
132
146
|
// only this task; unlike onLaunch, it must never persist Changes evidence.
|
|
133
147
|
beforeSpawn?: () => void;
|
|
@@ -216,6 +230,8 @@ const MAX_TRANSPORT_PREFIX_CHARS = 64;
|
|
|
216
230
|
const CHILD_MARKER = "GENTLE_PI_AGENTS_CHILD";
|
|
217
231
|
const IPC_MARKER = "GENTLE_PI_AGENTS_OWNED_IPC";
|
|
218
232
|
const PARENT_NOTIFICATION_TOOL = "subagent_parent_message";
|
|
233
|
+
/** The exact --tools list, so the child can report requested names Pi dropped silently (#1690). */
|
|
234
|
+
export const REQUESTED_TOOLS_ENV = "GENTLE_PI_AGENTS_REQUESTED_TOOLS";
|
|
219
235
|
const DEFAULT_TOOLS: readonly string[] = [];
|
|
220
236
|
const TERMINATION_GRACE_MS = 250;
|
|
221
237
|
const GROUP_CONFIRM_MS = 25;
|
|
@@ -255,14 +271,53 @@ export function formatChildExit(code: number | null | undefined, signal?: string
|
|
|
255
271
|
|
|
256
272
|
const hostProcess: ProcessControl = { platform: process.platform, kill: (pid, signal) => process.kill(pid, signal) };
|
|
257
273
|
|
|
274
|
+
// The --tools value, or undefined when Pi keeps its default tools.
|
|
275
|
+
function requestedTools(request: TaskRequest): string | undefined {
|
|
276
|
+
const tools = request.agent.tools.length > 0 ? [...new Set([...request.agent.tools, PARENT_NOTIFICATION_TOOL])] : DEFAULT_TOOLS;
|
|
277
|
+
return tools.length > 0 ? tools.join(",") : undefined;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const WEB_TOOL_NAMES = ["web_enable", "web_search", "source_check", "fetch_content", "get_search_content"];
|
|
281
|
+
|
|
282
|
+
/** Resolve one already installed, explicitly requested package. Pi's own loader
|
|
283
|
+
* follows its manifest and supplies SDK aliases; no install or ambient discovery. */
|
|
284
|
+
function requestedWebPackagePaths(request: TaskRequest): string[] {
|
|
285
|
+
if (!WEB_TOOL_NAMES.every(name => request.agent.tools.includes(name))) return [];
|
|
286
|
+
try {
|
|
287
|
+
let agentDir = request.env.PI_CODING_AGENT_DIR;
|
|
288
|
+
if (!agentDir || /^~(?:$|[/\\])/.test(agentDir)) {
|
|
289
|
+
const home = (process.platform === "win32" ? request.env.USERPROFILE : request.env.HOME) || userInfo().homedir;
|
|
290
|
+
agentDir = agentDir ? agentDir.replace(/^~/, home) : join(home, ".pi", "agent");
|
|
291
|
+
}
|
|
292
|
+
const manager = new DefaultPackageManager({
|
|
293
|
+
cwd: request.cwd,
|
|
294
|
+
agentDir,
|
|
295
|
+
settingsManager: SettingsManager.inMemory(),
|
|
296
|
+
});
|
|
297
|
+
const installed = manager.getInstalledPath("npm:pi-web-access", "user");
|
|
298
|
+
return installed ? [realpathSync(installed)] : [];
|
|
299
|
+
} catch {
|
|
300
|
+
// Missing/incompatible package remains an observable child capability gap.
|
|
301
|
+
return [];
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
258
305
|
export function childArguments(request: TaskRequest, instructionsPath?: string): string[] {
|
|
259
306
|
const args = ["--mode", "rpc", "--session-dir", request.sessionDir];
|
|
260
|
-
|
|
307
|
+
const extensionPaths = [...new Set(request.extensionPaths ?? [])];
|
|
308
|
+
for (const packagePath of requestedWebPackagePaths(request)) {
|
|
309
|
+
const injected = extensionPaths.some(path => {
|
|
310
|
+
try { return realpathSync(resolve(request.cwd, path)) === packagePath; }
|
|
311
|
+
catch { return false; }
|
|
312
|
+
});
|
|
313
|
+
if (!injected) extensionPaths.push(packagePath);
|
|
314
|
+
}
|
|
315
|
+
args.push(...childPackageExtensionArgs({ noExtensions: request.noExtensions === true, extensionPaths }));
|
|
261
316
|
if (request.resumeSessionPath) args.push("--session", request.resumeSessionPath);
|
|
262
317
|
if (request.model) args.push("--model", request.thinking ? `${formatModelRef(request.model)}:${request.thinking}` : formatModelRef(request.model));
|
|
263
318
|
else if (request.thinking) args.push("--thinking", request.thinking);
|
|
264
|
-
const tools =
|
|
265
|
-
if (tools
|
|
319
|
+
const tools = requestedTools(request);
|
|
320
|
+
if (tools !== undefined) args.push("--tools", tools);
|
|
266
321
|
if (instructionsPath) {
|
|
267
322
|
args.push("--append-system-prompt", instructionsPath);
|
|
268
323
|
} else if (request.agent.instructions.length > 0) {
|
|
@@ -325,6 +380,7 @@ export class AgentRunner {
|
|
|
325
380
|
private readonly waiters = new Map<string, Array<(task: TaskRecord) => void>>();
|
|
326
381
|
private readonly queryWaiters = new Map<string, Array<(query: TaskQuery | undefined) => void>>();
|
|
327
382
|
private readonly firstQueries = new Map<string, TaskQuery>();
|
|
383
|
+
private readonly writers = new WriterSurfaceRegistry();
|
|
328
384
|
private counter = 0;
|
|
329
385
|
|
|
330
386
|
constructor(store: TaskStore, limits: RunnerLimits, deps: RunnerDeps, hooks: RunnerHooks) {
|
|
@@ -366,8 +422,15 @@ export class AgentRunner {
|
|
|
366
422
|
return task;
|
|
367
423
|
}
|
|
368
424
|
|
|
425
|
+
// Check and claim happen in this one synchronous call, so two launches can
|
|
426
|
+
// never both pass admission for overlapping surfaces; finish() releases.
|
|
369
427
|
run(request: TaskRequest): TaskRecord {
|
|
428
|
+
if (request.writerSurfaces) {
|
|
429
|
+
const conflicts = this.writers.conflicts(request.writerRoot ?? request.cwd, request.writerSurfaces);
|
|
430
|
+
if (conflicts.length) throw new Error(writerSurfaceConflictMessage(conflicts));
|
|
431
|
+
}
|
|
370
432
|
const task = this.createTask(request);
|
|
433
|
+
if (request.writerSurfaces) this.writers.claim(task.id, request.writerRoot ?? request.cwd, request.writerSurfaces);
|
|
371
434
|
this.queue.push({ task, request });
|
|
372
435
|
queueMicrotask(() => this.pump());
|
|
373
436
|
return task;
|
|
@@ -477,6 +540,9 @@ export class AgentRunner {
|
|
|
477
540
|
// Do not forward stale legacy child selection or authorization.
|
|
478
541
|
delete env.GENTLE_PI_SDD_REMEDIATION_PLAN;
|
|
479
542
|
delete env.GENTLE_PI_RESEARCH_SELECTION;
|
|
543
|
+
const tools = requestedTools(request);
|
|
544
|
+
if (tools !== undefined) env[REQUESTED_TOOLS_ENV] = tools;
|
|
545
|
+
else delete env[REQUESTED_TOOLS_ENV];
|
|
480
546
|
let instructionsTransportDir: string | undefined;
|
|
481
547
|
let instructionsTransportPath: string | undefined;
|
|
482
548
|
if (Buffer.byteLength(request.agent.instructions, "utf8") > MAX_INLINE_INSTRUCTIONS_BYTES) {
|
|
@@ -503,6 +569,7 @@ export class AgentRunner {
|
|
|
503
569
|
cwd: request.cwd,
|
|
504
570
|
env,
|
|
505
571
|
detached,
|
|
572
|
+
windowsHide: true,
|
|
506
573
|
stdio: hasParentPermissionChannel ? ["pipe", "pipe", "pipe", permissionChannelStdio, "ipc"] : ["pipe", "pipe", "pipe", "ipc"],
|
|
507
574
|
});
|
|
508
575
|
} catch (error) {
|
|
@@ -528,7 +595,7 @@ export class AgentRunner {
|
|
|
528
595
|
}
|
|
529
596
|
this.live.set(id, live);
|
|
530
597
|
const permissionPipe = child.stdio?.[3];
|
|
531
|
-
if (hasParentPermissionChannel && permissionPipe
|
|
598
|
+
if (hasParentPermissionChannel && permissionPipe instanceof Duplex) {
|
|
532
599
|
live.permissionBroker = new ParentStandingReviewPermissionBroker(
|
|
533
600
|
{ readable: permissionPipe, writable: permissionPipe },
|
|
534
601
|
(repositoryIdentity) => this.live.get(id) === live && !live.terminal && request.authorizeParentStandingReviewPermission?.(repositoryIdentity) === true,
|
|
@@ -546,6 +613,11 @@ export class AgentRunner {
|
|
|
546
613
|
try { request.onLaunch?.(); }
|
|
547
614
|
catch (error) { this.requestStop(id, TASK_STATUS.FAILED, `could not register launched worktree: ${error instanceof Error ? error.message : String(error)}`); }
|
|
548
615
|
});
|
|
616
|
+
child.on("exit", (code, signal) => this.exited(id, code, signal));
|
|
617
|
+
if (!child.stdin || !child.stdout) {
|
|
618
|
+
this.requestStop(id, TASK_STATUS.FAILED, "could not start pi: missing RPC streams");
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
549
621
|
child.stdin.on("error", () => {});
|
|
550
622
|
this.armStall(id, live);
|
|
551
623
|
const lines = new JsonLines((value) => this.receive(id, request, value));
|
|
@@ -556,7 +628,6 @@ export class AgentRunner {
|
|
|
556
628
|
const tail = live.stderrTail + chunk;
|
|
557
629
|
live.stderrTail = tail.length > STDERR_TAIL_MAX ? tail.slice(-STDERR_TAIL_MAX) : tail;
|
|
558
630
|
});
|
|
559
|
-
child.on("exit", (code, signal) => this.exited(id, code, signal));
|
|
560
631
|
void this.send(id, { type: "get_state" }).then((response) => {
|
|
561
632
|
const data = response.data as { sessionFile?: unknown; model?: { provider?: unknown; id?: unknown } | null; thinkingLevel?: unknown } | undefined;
|
|
562
633
|
if (response.success !== true || live.terminal || this.live.get(id) !== live || !data) return;
|
|
@@ -726,7 +797,7 @@ export class AgentRunner {
|
|
|
726
797
|
|
|
727
798
|
private write(live: LiveTask, payload: Record<string, unknown>): void {
|
|
728
799
|
try {
|
|
729
|
-
live.child.stdin
|
|
800
|
+
live.child.stdin?.write(`${JSON.stringify(payload)}\n`);
|
|
730
801
|
} catch {
|
|
731
802
|
// the child is gone; the exit handler settles the task
|
|
732
803
|
}
|
|
@@ -969,6 +1040,7 @@ export class AgentRunner {
|
|
|
969
1040
|
this.live.delete(id);
|
|
970
1041
|
// Quarantine already notified completion, but its retained slot is now free.
|
|
971
1042
|
if (live.quarantined) {
|
|
1043
|
+
this.writers.release(id);
|
|
972
1044
|
queueMicrotask(() => this.pump());
|
|
973
1045
|
return;
|
|
974
1046
|
}
|
|
@@ -978,6 +1050,9 @@ export class AgentRunner {
|
|
|
978
1050
|
}
|
|
979
1051
|
|
|
980
1052
|
private finish(id: string, status: TaskRecord["status"], error: string | null, live?: LiveTask): void {
|
|
1053
|
+
// A quarantined child may still be writing: its claim lives as long as its
|
|
1054
|
+
// reserved slot and is released in completeExit on proven exit.
|
|
1055
|
+
if (!live?.quarantined) this.writers.release(id);
|
|
981
1056
|
const current = this.store.get(id);
|
|
982
1057
|
if (!current || isFinished(current.status)) return;
|
|
983
1058
|
const finished = this.store.update(id, { status, endedAt: this.deps.now(), error, lastStep: error ?? "done" });
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import type { QuestionParams } from "./questionnaire/schema.ts";
|
|
3
|
+
import type { AnswerRow } from "./questionnaire/questionnaire-view.ts";
|
|
4
|
+
import { isInteractiveMode } from "./rpc-host.ts";
|
|
5
|
+
|
|
6
|
+
type AnswerContext = Pick<ExtensionContext, "sessionManager" | "cwd" | "mode" | "hasUI">;
|
|
7
|
+
export type HumanAnswer =
|
|
8
|
+
| { readonly kind: "option" | "custom"; readonly text: string; readonly selected?: readonly string[] }
|
|
9
|
+
| { readonly kind: "multi"; readonly selected: readonly string[] };
|
|
10
|
+
export interface HumanAnswerReceipt {
|
|
11
|
+
readonly sessionManager: ExtensionContext["sessionManager"];
|
|
12
|
+
readonly sessionId: string;
|
|
13
|
+
readonly cwd: string;
|
|
14
|
+
readonly branchAnchor: string | null;
|
|
15
|
+
readonly toolName: "ask_user_choice" | "ask_user_question";
|
|
16
|
+
readonly toolCallId: string;
|
|
17
|
+
readonly questionIndex: number;
|
|
18
|
+
/** Untrusted referent, never an instruction or an authorization. */
|
|
19
|
+
readonly question: string;
|
|
20
|
+
/** Full original prompt context, untrusted and inseparable from its answer. */
|
|
21
|
+
readonly prompt: string;
|
|
22
|
+
/** Authenticated human response; opaque tokens and previews are excluded. */
|
|
23
|
+
readonly answer: HumanAnswer;
|
|
24
|
+
}
|
|
25
|
+
interface Subscription {
|
|
26
|
+
ctx: AnswerContext;
|
|
27
|
+
sessionId: string;
|
|
28
|
+
cwd: string;
|
|
29
|
+
callback: (receipt: HumanAnswerReceipt) => void;
|
|
30
|
+
}
|
|
31
|
+
const subscriptions = new WeakMap<object, Set<Subscription>>();
|
|
32
|
+
|
|
33
|
+
/** Subscribe from a trusted host context; dispose on session shutdown/replacement. */
|
|
34
|
+
export function subscribeHumanAnswers(ctx: AnswerContext, callback: (receipt: HumanAnswerReceipt) => void): () => void {
|
|
35
|
+
if (!ctx.sessionManager || !isInteractiveMode(ctx.mode, process.env) || ctx.hasUI === false) return () => {};
|
|
36
|
+
const sessionId = ctx.sessionManager.getSessionId();
|
|
37
|
+
if (!sessionId || !ctx.cwd) return () => {};
|
|
38
|
+
let listeners = subscriptions.get(ctx.sessionManager);
|
|
39
|
+
if (!listeners) subscriptions.set(ctx.sessionManager, listeners = new Set());
|
|
40
|
+
const subscription = { ctx, sessionId, cwd: ctx.cwd, callback };
|
|
41
|
+
listeners.add(subscription);
|
|
42
|
+
return () => { listeners.delete(subscription); };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Host-only capture seam. Call before mounting UI, never from tool_result. */
|
|
46
|
+
function beginCapture(ctx: AnswerContext, toolName: HumanAnswerReceipt["toolName"], toolCallId: string, questions: ReadonlyArray<{ question: string; prompt: string }>, signal?: AbortSignal) {
|
|
47
|
+
const manager = ctx.sessionManager;
|
|
48
|
+
const sessionId = manager?.getSessionId();
|
|
49
|
+
const cwd = ctx.cwd;
|
|
50
|
+
const branchAnchor = manager?.getLeafId?.();
|
|
51
|
+
const listeners = manager ? subscriptions.get(manager) : undefined;
|
|
52
|
+
const eligible = [...(listeners ?? [])].filter((entry) => entry.sessionId === sessionId && entry.cwd === cwd);
|
|
53
|
+
const interactive = isInteractiveMode(ctx.mode, process.env) && ctx.hasUI !== false;
|
|
54
|
+
let finished = false;
|
|
55
|
+
return (answers: ReadonlyArray<{ questionIndex: number; answer: HumanAnswer }>) => {
|
|
56
|
+
if (finished) return;
|
|
57
|
+
finished = true;
|
|
58
|
+
if (!interactive || !isInteractiveMode(ctx.mode, process.env) || ctx.hasUI === false ||
|
|
59
|
+
!manager || !sessionId || signal?.aborted || ctx.sessionManager !== manager ||
|
|
60
|
+
manager.getSessionId() !== sessionId || ctx.cwd !== cwd || branchAnchor === undefined || manager.getLeafId() !== branchAnchor) return;
|
|
61
|
+
for (const { questionIndex, answer } of answers) {
|
|
62
|
+
const referent = questions[questionIndex];
|
|
63
|
+
if (referent === undefined) continue;
|
|
64
|
+
const { question, prompt } = referent;
|
|
65
|
+
const receipt = Object.freeze({ sessionManager: manager, sessionId, cwd, branchAnchor, toolName, toolCallId, questionIndex, question, prompt,
|
|
66
|
+
answer: Object.freeze({ ...answer, ...(answer.selected ? { selected: Object.freeze([...answer.selected]) } : {}) }) });
|
|
67
|
+
for (const entry of eligible) {
|
|
68
|
+
if (!listeners?.has(entry) || !isInteractiveMode(entry.ctx.mode, process.env) || entry.ctx.hasUI === false ||
|
|
69
|
+
entry.ctx.sessionManager !== manager || entry.ctx.cwd !== cwd ||
|
|
70
|
+
manager.getSessionId() !== sessionId || manager.getLeafId() !== branchAnchor || signal?.aborted) continue;
|
|
71
|
+
// Observers must not change the existing public tool outcome.
|
|
72
|
+
try { entry.callback(receipt); } catch { /* Isolate host observer failures. */ }
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
interface ChoicePrompt {
|
|
79
|
+
question: string;
|
|
80
|
+
options: Array<{ label: string; description?: string; value: string }>;
|
|
81
|
+
allowCustomResponse?: boolean;
|
|
82
|
+
}
|
|
83
|
+
export function captureChoiceAnswer(ctx: AnswerContext, toolCallId: string, params: ChoicePrompt, signal?: AbortSignal) {
|
|
84
|
+
const options = params.options.map((option) => ({ ...option }));
|
|
85
|
+
const allowCustom = params.allowCustomResponse === true;
|
|
86
|
+
const prompt = JSON.stringify({ question: params.question,
|
|
87
|
+
options: options.map(({ label, description }) => ({ label, description })), allowCustomResponse: allowCustom });
|
|
88
|
+
const publish = beginCapture(ctx, "ask_user_choice", toolCallId, [{ question: params.question, prompt }], signal);
|
|
89
|
+
return (selection: { value: string; label: string; index: number } | { customResponse: string } | undefined) => {
|
|
90
|
+
if (!selection) return;
|
|
91
|
+
if ("customResponse" in selection) {
|
|
92
|
+
if (allowCustom && typeof selection.customResponse === "string" && selection.customResponse.trim())
|
|
93
|
+
publish([{ questionIndex: 0, answer: { kind: "custom", text: selection.customResponse } }]);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const option = options[selection.index - 1];
|
|
97
|
+
if (Number.isInteger(selection.index) && option && selection.label === option.label && selection.value === option.value)
|
|
98
|
+
publish([{ questionIndex: 0, answer: { kind: "option", text: option.label } }]);
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function captureQuestionnaireAnswers(ctx: AnswerContext, toolCallId: string, params: QuestionParams, signal?: AbortSignal) {
|
|
103
|
+
const questions = params.questions.map((question) => ({ ...question, options: question.options.map((option) => ({ ...option })) }));
|
|
104
|
+
const referents = questions.map((question) => ({ question: question.question, prompt: JSON.stringify({
|
|
105
|
+
question: question.question, header: question.header, multiSelect: question.multiSelect === true,
|
|
106
|
+
options: question.options.map(({ label, description, preview }) => ({ label, description,
|
|
107
|
+
...(ctx.mode === "tui" && preview !== undefined ? { preview } : {}) })),
|
|
108
|
+
}) }));
|
|
109
|
+
const publish = beginCapture(ctx, "ask_user_question", toolCallId, referents, signal);
|
|
110
|
+
return (rows: AnswerRow[] | undefined) => {
|
|
111
|
+
if (!rows?.length) return;
|
|
112
|
+
const seen = new Set<number>();
|
|
113
|
+
const answers: Array<{ questionIndex: number; answer: HumanAnswer }> = [];
|
|
114
|
+
for (const row of rows) {
|
|
115
|
+
const question = questions[row.questionIndex];
|
|
116
|
+
if (!Number.isInteger(row.questionIndex) || !question || seen.has(row.questionIndex) || row.question !== question.question) return;
|
|
117
|
+
seen.add(row.questionIndex);
|
|
118
|
+
const labels = question.options.map((option) => option.label);
|
|
119
|
+
const selected = row.selected;
|
|
120
|
+
if (selected && (!question.multiSelect || new Set(selected).size !== selected.length || selected.some((label) => !labels.includes(label)))) return;
|
|
121
|
+
let answer: HumanAnswer;
|
|
122
|
+
if (row.kind === "option" && typeof row.answer === "string" && labels.includes(row.answer) && !question.multiSelect && !selected)
|
|
123
|
+
answer = { kind: "option", text: row.answer };
|
|
124
|
+
else if (row.kind === "custom" && typeof row.answer === "string" && row.answer.trim())
|
|
125
|
+
answer = { kind: "custom", text: row.answer, ...(selected ? { selected } : {}) };
|
|
126
|
+
else if (row.kind === "multi" && question.multiSelect && selected && row.answer === null)
|
|
127
|
+
answer = { kind: "multi", selected };
|
|
128
|
+
else return;
|
|
129
|
+
answers.push({ questionIndex: row.questionIndex, answer });
|
|
130
|
+
}
|
|
131
|
+
publish(answers);
|
|
132
|
+
};
|
|
133
|
+
}
|