@caupulican/pi-adaptative 0.81.37 → 0.81.39
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 +91 -0
- package/README.md +1 -1
- package/dist/bundled-resources/extensions/tmux-agent-manager/README.md +18 -1
- package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.d.ts +131 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.d.ts.map +1 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.js +196 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.js.map +1 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.ts +308 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/index.d.ts +22 -2
- package/dist/bundled-resources/extensions/tmux-agent-manager/index.d.ts.map +1 -1
- package/dist/bundled-resources/extensions/tmux-agent-manager/index.js +585 -24
- package/dist/bundled-resources/extensions/tmux-agent-manager/index.js.map +1 -1
- package/dist/bundled-resources/extensions/tmux-agent-manager/index.ts +749 -27
- package/dist/bundled-resources/runtimes/pi-shell-engine/commands/__init__.py +43 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/commands/fs.py +270 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/commands/search.py +252 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/commands/strings.py +399 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/commands/text.py +575 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/context.py +52 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/errors.py +49 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/exec.py +734 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/expand.py +238 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/main.py +132 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/nodes.py +116 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/parser.py +287 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/proc.py +137 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/state.py +100 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/tokens.py +579 -0
- package/dist/bundled-resources/skills/tool-call-repair/SKILL.md +19 -9
- package/dist/bundled-resources/skills/tool-call-repair/references/failure-grammar.md +22 -5
- package/dist/bundled-resources/skills/tool-call-repair/references/repair-catalogue.md +5 -5
- package/dist/bundled-resources/skills/tool-call-repair/references/text-protocol-grammar.md +28 -7
- package/dist/cli/args.d.ts +3 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +15 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/core/agent-paths.d.ts +49 -0
- package/dist/core/agent-paths.d.ts.map +1 -0
- package/dist/core/agent-paths.js +107 -0
- package/dist/core/agent-paths.js.map +1 -0
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +3 -3
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session.d.ts +234 -17
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +484 -62
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/autonomy/contracts.d.ts +9 -0
- package/dist/core/autonomy/contracts.d.ts.map +1 -1
- package/dist/core/autonomy/contracts.js.map +1 -1
- package/dist/core/autonomy/lane-tracker.d.ts +10 -1
- package/dist/core/autonomy/lane-tracker.d.ts.map +1 -1
- package/dist/core/autonomy/lane-tracker.js +5 -1
- package/dist/core/autonomy/lane-tracker.js.map +1 -1
- package/dist/core/background-lane-controller.d.ts +122 -6
- package/dist/core/background-lane-controller.d.ts.map +1 -1
- package/dist/core/background-lane-controller.js +447 -90
- package/dist/core/background-lane-controller.js.map +1 -1
- package/dist/core/bash-execution-controller.d.ts +4 -0
- package/dist/core/bash-execution-controller.d.ts.map +1 -1
- package/dist/core/bash-execution-controller.js +7 -1
- package/dist/core/bash-execution-controller.js.map +1 -1
- package/dist/core/compaction-support.d.ts +13 -3
- package/dist/core/compaction-support.d.ts.map +1 -1
- package/dist/core/compaction-support.js +43 -7
- package/dist/core/compaction-support.js.map +1 -1
- package/dist/core/context/context-audit.d.ts +33 -1
- package/dist/core/context/context-audit.d.ts.map +1 -1
- package/dist/core/context/context-audit.js +31 -5
- package/dist/core/context/context-audit.js.map +1 -1
- package/dist/core/context/sqlite-runtime-index.d.ts.map +1 -1
- package/dist/core/context/sqlite-runtime-index.js +13 -7
- package/dist/core/context/sqlite-runtime-index.js.map +1 -1
- package/dist/core/context-gc.d.ts.map +1 -1
- package/dist/core/context-gc.js +16 -1
- package/dist/core/context-gc.js.map +1 -1
- package/dist/core/context-pipeline.d.ts +26 -4
- package/dist/core/context-pipeline.d.ts.map +1 -1
- package/dist/core/context-pipeline.js +137 -14
- package/dist/core/context-pipeline.js.map +1 -1
- package/dist/core/cost-guard.d.ts +19 -3
- package/dist/core/cost-guard.d.ts.map +1 -1
- package/dist/core/cost-guard.js +18 -3
- package/dist/core/cost-guard.js.map +1 -1
- package/dist/core/delegation/session-worker-result.d.ts +34 -4
- package/dist/core/delegation/session-worker-result.d.ts.map +1 -1
- package/dist/core/delegation/session-worker-result.js +64 -4
- package/dist/core/delegation/session-worker-result.js.map +1 -1
- package/dist/core/delegation/worker-actions.d.ts.map +1 -1
- package/dist/core/delegation/worker-actions.js +1 -1
- package/dist/core/delegation/worker-actions.js.map +1 -1
- package/dist/core/delegation/worker-result.d.ts +42 -1
- package/dist/core/delegation/worker-result.d.ts.map +1 -1
- package/dist/core/delegation/worker-result.js +68 -0
- package/dist/core/delegation/worker-result.js.map +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +23 -7
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +1 -0
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +51 -0
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/goal-loop-controller.d.ts +17 -1
- package/dist/core/goal-loop-controller.d.ts.map +1 -1
- package/dist/core/goal-loop-controller.js +79 -11
- package/dist/core/goal-loop-controller.js.map +1 -1
- package/dist/core/goals/goal-continuation-controller.d.ts +48 -2
- package/dist/core/goals/goal-continuation-controller.d.ts.map +1 -1
- package/dist/core/goals/goal-continuation-controller.js +77 -0
- package/dist/core/goals/goal-continuation-controller.js.map +1 -1
- package/dist/core/goals/goal-continuation-defaults.d.ts +39 -0
- package/dist/core/goals/goal-continuation-defaults.d.ts.map +1 -1
- package/dist/core/goals/goal-continuation-defaults.js +42 -0
- package/dist/core/goals/goal-continuation-defaults.js.map +1 -1
- package/dist/core/goals/goal-continuation-prompt.d.ts +4 -0
- package/dist/core/goals/goal-continuation-prompt.d.ts.map +1 -1
- package/dist/core/goals/goal-continuation-prompt.js +51 -10
- package/dist/core/goals/goal-continuation-prompt.js.map +1 -1
- package/dist/core/goals/goal-runtime-snapshot.d.ts +101 -2
- package/dist/core/goals/goal-runtime-snapshot.d.ts.map +1 -1
- package/dist/core/goals/goal-runtime-snapshot.js +87 -4
- package/dist/core/goals/goal-runtime-snapshot.js.map +1 -1
- package/dist/core/goals/goal-state.d.ts +89 -1
- package/dist/core/goals/goal-state.d.ts.map +1 -1
- package/dist/core/goals/goal-state.js +71 -5
- package/dist/core/goals/goal-state.js.map +1 -1
- package/dist/core/goals/goal-tool-core.d.ts +56 -2
- package/dist/core/goals/goal-tool-core.d.ts.map +1 -1
- package/dist/core/goals/goal-tool-core.js +111 -5
- package/dist/core/goals/goal-tool-core.js.map +1 -1
- package/dist/core/goals/session-goal-state.d.ts +11 -2
- package/dist/core/goals/session-goal-state.d.ts.map +1 -1
- package/dist/core/goals/session-goal-state.js +32 -17
- package/dist/core/goals/session-goal-state.js.map +1 -1
- package/dist/core/keybindings.d.ts +10 -0
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +10 -2
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/learning/observation-store.d.ts +12 -3
- package/dist/core/learning/observation-store.d.ts.map +1 -1
- package/dist/core/learning/observation-store.js +30 -15
- package/dist/core/learning/observation-store.js.map +1 -1
- package/dist/core/learning/skill-curator.d.ts +5 -1
- package/dist/core/learning/skill-curator.d.ts.map +1 -1
- package/dist/core/learning/skill-curator.js +21 -19
- package/dist/core/learning/skill-curator.js.map +1 -1
- package/dist/core/local-runtime-controller.d.ts +65 -3
- package/dist/core/local-runtime-controller.d.ts.map +1 -1
- package/dist/core/local-runtime-controller.js +186 -26
- package/dist/core/local-runtime-controller.js.map +1 -1
- package/dist/core/memory/providers/file-store.d.ts +1 -1
- package/dist/core/memory/providers/file-store.d.ts.map +1 -1
- package/dist/core/memory/providers/file-store.js +6 -6
- package/dist/core/memory/providers/file-store.js.map +1 -1
- package/dist/core/model-capability.d.ts +34 -0
- package/dist/core/model-capability.d.ts.map +1 -1
- package/dist/core/model-capability.js +42 -1
- package/dist/core/model-capability.js.map +1 -1
- package/dist/core/model-router/tool-escalation.d.ts +15 -0
- package/dist/core/model-router/tool-escalation.d.ts.map +1 -1
- package/dist/core/model-router/tool-escalation.js +23 -1
- package/dist/core/model-router/tool-escalation.js.map +1 -1
- package/dist/core/model-router-controller.d.ts +34 -7
- package/dist/core/model-router-controller.d.ts.map +1 -1
- package/dist/core/model-router-controller.js +95 -16
- package/dist/core/model-router-controller.js.map +1 -1
- package/dist/core/models/adaptation-store.d.ts +7 -0
- package/dist/core/models/adaptation-store.d.ts.map +1 -1
- package/dist/core/models/adaptation-store.js +47 -11
- package/dist/core/models/adaptation-store.js.map +1 -1
- package/dist/core/models/default-model-suggestions.d.ts.map +1 -1
- package/dist/core/models/default-model-suggestions.js +17 -0
- package/dist/core/models/default-model-suggestions.js.map +1 -1
- package/dist/core/models/fitness-store.d.ts +3 -0
- package/dist/core/models/fitness-store.d.ts.map +1 -1
- package/dist/core/models/fitness-store.js +11 -2
- package/dist/core/models/fitness-store.js.map +1 -1
- package/dist/core/models/llamacpp-runtime.d.ts +180 -0
- package/dist/core/models/llamacpp-runtime.d.ts.map +1 -0
- package/dist/core/models/llamacpp-runtime.js +475 -0
- package/dist/core/models/llamacpp-runtime.js.map +1 -0
- package/dist/core/models/local-registration.d.ts +40 -0
- package/dist/core/models/local-registration.d.ts.map +1 -1
- package/dist/core/models/local-registration.js +94 -5
- package/dist/core/models/local-registration.js.map +1 -1
- package/dist/core/models/local-runtime.d.ts.map +1 -1
- package/dist/core/models/local-runtime.js +9 -7
- package/dist/core/models/local-runtime.js.map +1 -1
- package/dist/core/models/model-ref.d.ts +7 -0
- package/dist/core/models/model-ref.d.ts.map +1 -1
- package/dist/core/models/model-ref.js +26 -0
- package/dist/core/models/model-ref.js.map +1 -1
- package/dist/core/models/needle-runtime.d.ts +257 -0
- package/dist/core/models/needle-runtime.d.ts.map +1 -0
- package/dist/core/models/needle-runtime.js +519 -0
- package/dist/core/models/needle-runtime.js.map +1 -0
- package/dist/core/models/prism-llamacpp-lifecycle.d.ts +89 -0
- package/dist/core/models/prism-llamacpp-lifecycle.d.ts.map +1 -0
- package/dist/core/models/prism-llamacpp-lifecycle.js +121 -0
- package/dist/core/models/prism-llamacpp-lifecycle.js.map +1 -0
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +11 -10
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/process-matrix/codes.d.ts +72 -0
- package/dist/core/process-matrix/codes.d.ts.map +1 -0
- package/dist/core/process-matrix/codes.js +15 -0
- package/dist/core/process-matrix/codes.js.map +1 -0
- package/dist/core/process-matrix/runtime.d.ts +63 -0
- package/dist/core/process-matrix/runtime.d.ts.map +1 -0
- package/dist/core/process-matrix/runtime.js +310 -0
- package/dist/core/process-matrix/runtime.js.map +1 -0
- package/dist/core/process-matrix/store.d.ts +22 -0
- package/dist/core/process-matrix/store.d.ts.map +1 -0
- package/dist/core/process-matrix/store.js +80 -0
- package/dist/core/process-matrix/store.js.map +1 -0
- package/dist/core/process-matrix/supervisor.d.ts +72 -0
- package/dist/core/process-matrix/supervisor.d.ts.map +1 -0
- package/dist/core/process-matrix/supervisor.js +130 -0
- package/dist/core/process-matrix/supervisor.js.map +1 -0
- package/dist/core/profile-registry.d.ts +7 -0
- package/dist/core/profile-registry.d.ts.map +1 -1
- package/dist/core/profile-registry.js +20 -0
- package/dist/core/profile-registry.js.map +1 -1
- package/dist/core/python-runtime.d.ts.map +1 -1
- package/dist/core/python-runtime.js +3 -3
- package/dist/core/python-runtime.js.map +1 -1
- package/dist/core/reflection-controller.d.ts +29 -5
- package/dist/core/reflection-controller.d.ts.map +1 -1
- package/dist/core/reflection-controller.js +215 -126
- package/dist/core/reflection-controller.js.map +1 -1
- package/dist/core/reload-blockers.d.ts +36 -0
- package/dist/core/reload-blockers.d.ts.map +1 -1
- package/dist/core/reload-blockers.js +44 -0
- package/dist/core/reload-blockers.js.map +1 -1
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +8 -7
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/runtime-builder.d.ts +58 -5
- package/dist/core/runtime-builder.d.ts.map +1 -1
- package/dist/core/runtime-builder.js +209 -25
- package/dist/core/runtime-builder.js.map +1 -1
- package/dist/core/scout-controller.d.ts +6 -0
- package/dist/core/scout-controller.d.ts.map +1 -1
- package/dist/core/scout-controller.js +66 -52
- package/dist/core/scout-controller.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +3 -3
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-role.d.ts +31 -0
- package/dist/core/session-role.d.ts.map +1 -0
- package/dist/core/session-role.js +52 -0
- package/dist/core/session-role.js.map +1 -0
- package/dist/core/settings-manager.d.ts +42 -3
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +94 -60
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/system-prompt-builder.d.ts +12 -0
- package/dist/core/system-prompt-builder.d.ts.map +1 -1
- package/dist/core/system-prompt-builder.js +6 -0
- package/dist/core/system-prompt-builder.js.map +1 -1
- package/dist/core/system-prompt.d.ts +14 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +20 -3
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tasks/session-task-state.d.ts +11 -2
- package/dist/core/tasks/session-task-state.d.ts.map +1 -1
- package/dist/core/tasks/session-task-state.js +27 -11
- package/dist/core/tasks/session-task-state.js.map +1 -1
- package/dist/core/tasks/task-contract-monitor.d.ts +45 -0
- package/dist/core/tasks/task-contract-monitor.d.ts.map +1 -0
- package/dist/core/tasks/task-contract-monitor.js +56 -0
- package/dist/core/tasks/task-contract-monitor.js.map +1 -0
- package/dist/core/tasks/task-state.d.ts +8 -0
- package/dist/core/tasks/task-state.d.ts.map +1 -1
- package/dist/core/tasks/task-state.js +28 -1
- package/dist/core/tasks/task-state.js.map +1 -1
- package/dist/core/tool-gate-controller.d.ts.map +1 -1
- package/dist/core/tool-gate-controller.js +5 -0
- package/dist/core/tool-gate-controller.js.map +1 -1
- package/dist/core/tool-recovery-log-records.d.ts +7 -0
- package/dist/core/tool-recovery-log-records.d.ts.map +1 -1
- package/dist/core/tool-recovery-log-records.js +14 -6
- package/dist/core/tool-recovery-log-records.js.map +1 -1
- package/dist/core/tool-selection/promotion.d.ts +54 -0
- package/dist/core/tool-selection/promotion.d.ts.map +1 -0
- package/dist/core/tool-selection/promotion.js +81 -0
- package/dist/core/tool-selection/promotion.js.map +1 -0
- package/dist/core/tool-selection/tool-performance-store.d.ts +37 -0
- package/dist/core/tool-selection/tool-performance-store.d.ts.map +1 -1
- package/dist/core/tool-selection/tool-performance-store.js +87 -3
- package/dist/core/tool-selection/tool-performance-store.js.map +1 -1
- package/dist/core/tool-selection/tool-selection-controller.d.ts +45 -0
- package/dist/core/tool-selection/tool-selection-controller.d.ts.map +1 -1
- package/dist/core/tool-selection/tool-selection-controller.js +96 -0
- package/dist/core/tool-selection/tool-selection-controller.js.map +1 -1
- package/dist/core/tools/bash.d.ts +18 -0
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +97 -18
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/delegate-status.d.ts +14 -0
- package/dist/core/tools/delegate-status.d.ts.map +1 -1
- package/dist/core/tools/delegate-status.js +82 -7
- package/dist/core/tools/delegate-status.js.map +1 -1
- package/dist/core/tools/delegate.d.ts.map +1 -1
- package/dist/core/tools/delegate.js +25 -8
- package/dist/core/tools/delegate.js.map +1 -1
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js +52 -44
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/goal.d.ts +94 -3
- package/dist/core/tools/goal.d.ts.map +1 -1
- package/dist/core/tools/goal.js +165 -15
- package/dist/core/tools/goal.js.map +1 -1
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js +5 -4
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/model-fitness.d.ts +7 -0
- package/dist/core/tools/model-fitness.d.ts.map +1 -1
- package/dist/core/tools/model-fitness.js +2 -2
- package/dist/core/tools/model-fitness.js.map +1 -1
- package/dist/core/tools/render-utils.d.ts.map +1 -1
- package/dist/core/tools/render-utils.js +1 -1
- package/dist/core/tools/render-utils.js.map +1 -1
- package/dist/core/tools/shell-contract-router.d.ts +6 -1
- package/dist/core/tools/shell-contract-router.d.ts.map +1 -1
- package/dist/core/tools/shell-contract-router.js +69 -13
- package/dist/core/tools/shell-contract-router.js.map +1 -1
- package/dist/core/tools/shell-session.d.ts +89 -0
- package/dist/core/tools/shell-session.d.ts.map +1 -0
- package/dist/core/tools/shell-session.js +432 -0
- package/dist/core/tools/shell-session.js.map +1 -0
- package/dist/core/tools/task-steps.d.ts +4 -0
- package/dist/core/tools/task-steps.d.ts.map +1 -1
- package/dist/core/tools/task-steps.js +63 -8
- package/dist/core/tools/task-steps.js.map +1 -1
- package/dist/core/tools/tmux-dispatch.d.ts +86 -0
- package/dist/core/tools/tmux-dispatch.d.ts.map +1 -0
- package/dist/core/tools/tmux-dispatch.js +91 -0
- package/dist/core/tools/tmux-dispatch.js.map +1 -0
- package/dist/core/tools/windows-shell-engine.d.ts +42 -0
- package/dist/core/tools/windows-shell-engine.d.ts.map +1 -0
- package/dist/core/tools/windows-shell-engine.js +153 -0
- package/dist/core/tools/windows-shell-engine.js.map +1 -0
- package/dist/core/tools/windows-shell-state.d.ts +40 -0
- package/dist/core/tools/windows-shell-state.d.ts.map +1 -0
- package/dist/core/tools/windows-shell-state.js +59 -0
- package/dist/core/tools/windows-shell-state.js.map +1 -0
- package/dist/core/tools/worktree-sync.d.ts +24 -0
- package/dist/core/tools/worktree-sync.d.ts.map +1 -0
- package/dist/core/tools/worktree-sync.js +338 -0
- package/dist/core/tools/worktree-sync.js.map +1 -0
- package/dist/core/trust-manager.d.ts +4 -1
- package/dist/core/trust-manager.d.ts.map +1 -1
- package/dist/core/trust-manager.js +20 -2
- package/dist/core/trust-manager.js.map +1 -1
- package/dist/core/util/atomic-file.d.ts +55 -0
- package/dist/core/util/atomic-file.d.ts.map +1 -0
- package/dist/core/util/atomic-file.js +255 -0
- package/dist/core/util/atomic-file.js.map +1 -0
- package/dist/core/util/minimatch-cache.d.ts +33 -0
- package/dist/core/util/minimatch-cache.d.ts.map +1 -0
- package/dist/core/util/minimatch-cache.js +0 -0
- package/dist/core/util/minimatch-cache.js.map +1 -0
- package/dist/core/worktree-sync/codes.d.ts +227 -0
- package/dist/core/worktree-sync/codes.d.ts.map +1 -0
- package/dist/core/worktree-sync/codes.js +14 -0
- package/dist/core/worktree-sync/codes.js.map +1 -0
- package/dist/core/worktree-sync/git-engine.d.ts +156 -0
- package/dist/core/worktree-sync/git-engine.d.ts.map +1 -0
- package/dist/core/worktree-sync/git-engine.js +1191 -0
- package/dist/core/worktree-sync/git-engine.js.map +1 -0
- package/dist/core/worktree-sync/lane-gate.d.ts +75 -0
- package/dist/core/worktree-sync/lane-gate.d.ts.map +1 -0
- package/dist/core/worktree-sync/lane-gate.js +360 -0
- package/dist/core/worktree-sync/lane-gate.js.map +1 -0
- package/dist/core/worktree-sync/runtime.d.ts +47 -0
- package/dist/core/worktree-sync/runtime.d.ts.map +1 -0
- package/dist/core/worktree-sync/runtime.js +96 -0
- package/dist/core/worktree-sync/runtime.js.map +1 -0
- package/dist/core/worktree-sync/store.d.ts +69 -0
- package/dist/core/worktree-sync/store.d.ts.map +1 -0
- package/dist/core/worktree-sync/store.js +247 -0
- package/dist/core/worktree-sync/store.js.map +1 -0
- package/dist/core/worktree-sync/watcher.d.ts +29 -0
- package/dist/core/worktree-sync/watcher.d.ts.map +1 -0
- package/dist/core/worktree-sync/watcher.js +93 -0
- package/dist/core/worktree-sync/watcher.js.map +1 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +94 -0
- package/dist/main.js.map +1 -1
- package/dist/migrations.d.ts +9 -0
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +38 -0
- package/dist/migrations.js.map +1 -1
- package/dist/modes/interactive/auto-learn-controller.d.ts +16 -1
- package/dist/modes/interactive/auto-learn-controller.d.ts.map +1 -1
- package/dist/modes/interactive/auto-learn-controller.js +50 -8
- package/dist/modes/interactive/auto-learn-controller.js.map +1 -1
- package/dist/modes/interactive/components/profile-resource-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/profile-resource-editor.js +23 -1
- package/dist/modes/interactive/components/profile-resource-editor.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +4 -0
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/local-model-commands.d.ts +43 -0
- package/dist/modes/interactive/local-model-commands.d.ts.map +1 -1
- package/dist/modes/interactive/local-model-commands.js +290 -3
- package/dist/modes/interactive/local-model-commands.js.map +1 -1
- package/dist/modes/interactive/session-flow-commands.d.ts.map +1 -1
- package/dist/modes/interactive/session-flow-commands.js +24 -4
- package/dist/modes/interactive/session-flow-commands.js.map +1 -1
- package/dist/utils/fs-watch.d.ts +11 -0
- package/dist/utils/fs-watch.d.ts.map +1 -1
- package/dist/utils/fs-watch.js +20 -2
- package/dist/utils/fs-watch.js.map +1 -1
- package/dist/utils/highlight-js-languages.d.ts +4 -0
- package/dist/utils/highlight-js-languages.d.ts.map +1 -0
- package/dist/utils/highlight-js-languages.js +573 -0
- package/dist/utils/highlight-js-languages.js.map +1 -0
- package/dist/utils/shell.d.ts +7 -1
- package/dist/utils/shell.d.ts.map +1 -1
- package/dist/utils/shell.js +39 -9
- package/dist/utils/shell.js.map +1 -1
- package/dist/utils/syntax-highlight.d.ts.map +1 -1
- package/dist/utils/syntax-highlight.js +53 -5
- package/dist/utils/syntax-highlight.js.map +1 -1
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +112 -1
- package/dist/utils/tools-manager.js.map +1 -1
- package/docs/development.md +2 -0
- package/docs/packages.md +1 -1
- package/docs/process-matrix.md +120 -0
- package/docs/settings.md +5 -2
- package/docs/tmux-agent-manager.md +85 -2
- package/docs/windows.md +52 -3
- package/docs/work-directory.md +29 -0
- package/docs/worktree-sync.md +250 -0
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/npm-shrinkwrap.json +12 -12
- package/package.json +10 -4
- package/docs/integration-sweep-builder-blueprint-2026-07-09.md +0 -365
- package/docs/integration-sweep-resume-2026-07-09.md +0 -407
|
@@ -0,0 +1,519 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { createWriteStream, existsSync, mkdirSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { platform as osPlatform } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { Transform } from "node:stream";
|
|
6
|
+
import { pipeline } from "node:stream/promises";
|
|
7
|
+
import { spawnProcess, spawnProcessSync, waitForChildProcessWithTermination } from "../../utils/child-process.js";
|
|
8
|
+
import { modelsDir as agentModelsDir, runtimesDir as agentRuntimesDir } from "../agent-paths.js";
|
|
9
|
+
/**
|
|
10
|
+
* Managed runtime for needle (https://github.com/cactus-compute/needle), a 26M-parameter single-shot
|
|
11
|
+
* function-calling model. Needle exposes no server — the only integration surface is CLI/library
|
|
12
|
+
* invocation — so this module owns clone+build of a pinned commit into a contained venv, base
|
|
13
|
+
* checkpoint download, and one-shot `needle run` invocation/parsing. Mirrors local-runtime.ts's
|
|
14
|
+
* OllamaRuntime/TransformersRuntime and llamacpp-runtime.ts's PrismLlamaCppRuntime: injectable seams
|
|
15
|
+
* for exists/fetch/run-command, pi-owned directories under agentDir (runtimes/, models/), onProgress
|
|
16
|
+
* as best-effort UI feedback, and honest error taxonomies instead of silent fallbacks.
|
|
17
|
+
*
|
|
18
|
+
* Investigation findings (verified against a real clone + real venv + real CPU inference run, not
|
|
19
|
+
* just the README):
|
|
20
|
+
*
|
|
21
|
+
* - `needle run` (the CLI's single-inference subcommand) takes a REQUIRED `--checkpoint` and does
|
|
22
|
+
* NOT auto-download one — unlike `needle playground`/`finetune`, which resolve a checkpoint
|
|
23
|
+
* internally. The pretrained base checkpoint (`needle.pkl`) lives on `Cactus-Compute/needle` on
|
|
24
|
+
* Hugging Face and is fetched directly by {@link NeedleRuntime.downloadWeights} (never invoked
|
|
25
|
+
* implicitly by installManaged/runFunctionCall — this module never silently starts a large
|
|
26
|
+
* download on the caller's behalf).
|
|
27
|
+
* - SECURITY (owner-flagged, verified during investigation): `Cactus-Compute/needle` on Hugging Face
|
|
28
|
+
* publishes THREE weight artifacts — `needle.pkl` (52,633,098 bytes), `model.safetensors`
|
|
29
|
+
* (reported as a different param count on the HF model card, 30.4M vs the 26,315,421 a real
|
|
30
|
+
* `needle run` printed — likely not even the same checkpoint), and `needle-cq4.zip`. I grepped the
|
|
31
|
+
* ENTIRE needle package (cli.py, model/run.py, model/export.py, model/quantize.py,
|
|
32
|
+
* training/{finetune,pretrain,train}.py, utils/distributed.py, ui/server.py — every `pickle`/`.zip`
|
|
33
|
+
* hit in the repo) and confirmed every checkpoint load/save path in the codebase — including the
|
|
34
|
+
* one `needle run` uses — is `pickle.load`/`pickle.dump` on a `.pkl` file. There is NO safetensors
|
|
35
|
+
* reader anywhere in the package; `model.safetensors` is not consumed by any code this module
|
|
36
|
+
* drives. `needle-cq4.zip` ("Cactus Quantized 4-bit"?) isn't referenced by any code here either —
|
|
37
|
+
* the README states production inference runs on the separate Cactus C++ engine
|
|
38
|
+
* (cactus-compute/cactus), which very plausibly consumes its own format; that repo was not audited
|
|
39
|
+
* here. Bottom line: **this module has no choice but to fetch and deserialize `needle.pkl` via
|
|
40
|
+
* Python's `pickle` module, which executes arbitrary code by construction** — there is no
|
|
41
|
+
* safetensors alternative available in the code path we drive. Given that, {@link
|
|
42
|
+
* NeedleRuntime.downloadWeights} pins the exact sha256 ({@link NEEDLE_WEIGHTS_SHA256}) and byte
|
|
43
|
+
* count ({@link NEEDLE_WEIGHTS_BYTES}) of the file as verified on 2026-07-18 (direct `sha256sum` of
|
|
44
|
+
* a real download, cross-checked against Hugging Face's own `x-linked-etag`/`x-linked-size`
|
|
45
|
+
* headers on the resolve redirect, at repo commit `5f89b4307696d669c3df1d38ae057e6e1728b107`) and
|
|
46
|
+
* VERIFIES every download against that pin, refusing to keep any file that doesn't match — this
|
|
47
|
+
* catches a compromised/tampered/swapped artifact but does NOT make loading a pickle file safe in
|
|
48
|
+
* the abstract; the actual `pickle.load` execution happens entirely inside needle's own Python
|
|
49
|
+
* process, outside this module's control. The owner should decide whether serving this model at
|
|
50
|
+
* all is acceptable given upstream's format choice.
|
|
51
|
+
* - The upstream `setup` script does more than a contained managed install can honor: it can `sudo
|
|
52
|
+
* apt-get install` a newer Python or `python3-venv`, `sudo modprobe` TPU kernel modules, toggle
|
|
53
|
+
* transparent hugepages via a root-owned sysfs write, and interactively prompts for a
|
|
54
|
+
* WANDB_API_KEY. None of that is available under containment (no writes outside the pi-owned dir,
|
|
55
|
+
* no sudo, no interactive prompts, no global installs) and none of it is required for the
|
|
56
|
+
* documented Mac/PC single-inference quickstart path. {@link NeedleRuntime.installManaged} instead
|
|
57
|
+
* replicates only the containable core: `python3 -m venv`, `pip install -e <clonedRepo>` (the
|
|
58
|
+
* script's own `pip install -e . -q`), and the CPU (or CUDA, when an NVIDIA GPU is detected) JAX
|
|
59
|
+
* backend install. TPU support and the sudo-gated OS package/kernel-module steps are intentionally
|
|
60
|
+
* NOT replicated.
|
|
61
|
+
* - `needle run`'s console-script entry point (`<venv>/bin/needle`, installed by
|
|
62
|
+
* `pip install -e .` from the `[project.scripts]` table) is the only way to invoke the CLI:
|
|
63
|
+
* `needle/cli.py` has no `if __name__ == "__main__":` guard, so `python -m needle.cli` silently
|
|
64
|
+
* does nothing — the installed console script is required.
|
|
65
|
+
* - A verified real stdout capture of `needle run --checkpoint <path> --query "..." --tools '[...]'`
|
|
66
|
+
* is multi-line diagnostic prose followed by the tool call:
|
|
67
|
+
* ```
|
|
68
|
+
* Loading checkpoint: <path>
|
|
69
|
+
* Downloading pretrained tokenizer from HuggingFace... (first run only — cached after)
|
|
70
|
+
* Model parameters: 26,315,421
|
|
71
|
+
*
|
|
72
|
+
* Query: <query>
|
|
73
|
+
* Tools: <tools JSON, truncated to 80 chars>...
|
|
74
|
+
*
|
|
75
|
+
* <tool_call>[{"name":"get_weather","arguments":{"location":"SanFrancisco"}}]
|
|
76
|
+
* ```
|
|
77
|
+
* The `<tool_call>` marker is a literal SentencePiece user-defined-symbol token, always emitted as
|
|
78
|
+
* the first generated token per the model's own answer format. {@link NeedleRuntime}'s stdout
|
|
79
|
+
* parser locates that marker and strictly `JSON.parse`s everything after it — never guessing a
|
|
80
|
+
* result from unparseable output.
|
|
81
|
+
* - Known upstream quirk (verified against a live run with a camelCase tool name): `needle run`'s CLI
|
|
82
|
+
* wrapper (`main()` in `needle/model/run.py`) streams tokens straight to stdout and never applies
|
|
83
|
+
* the tool-name restoration its own `generate()` return value receives — a tool name that wasn't
|
|
84
|
+
* already snake_case (e.g. `getWeatherNow`) is reported back snake-cased (`get_weather_now`), not
|
|
85
|
+
* in its original casing. This is a limitation of the upstream CLI, not something patched here.
|
|
86
|
+
* - Pinned commit `ffb1c5144c5a16cb8ec650dbc8a6f6fd3854f8f2` was captured via a real `git clone` of
|
|
87
|
+
* the repo's default branch HEAD on 2026-07-18 (the repo's own history shows no pushes since
|
|
88
|
+
* 2026-07-01, so this is not stale). A plain `git clone --depth 1` only reliably lands on a pinned
|
|
89
|
+
* commit when it still happens to be the remote's current tip; {@link NeedleRuntime.installManaged}
|
|
90
|
+
* instead fetches the exact commit SHA directly (`git fetch --depth 1 origin <sha>`), which GitHub
|
|
91
|
+
* serves for any reachable commit on a public repo, so the pin holds even after upstream moves on.
|
|
92
|
+
*/
|
|
93
|
+
export const NEEDLE_REPO_URL = "https://github.com/cactus-compute/needle.git";
|
|
94
|
+
/**
|
|
95
|
+
* Pinned needle commit — captured via a real `git clone` of the repo's default-branch HEAD on
|
|
96
|
+
* 2026-07-18 (`git ls-remote https://github.com/cactus-compute/needle HEAD` resolves to the same
|
|
97
|
+
* SHA). Bump here (re-verify with the same command) when a newer commit is needed.
|
|
98
|
+
*/
|
|
99
|
+
export const NEEDLE_PINNED_COMMIT = "ffb1c5144c5a16cb8ec650dbc8a6f6fd3854f8f2";
|
|
100
|
+
const NEEDLE_HF_MODEL_REPO = "Cactus-Compute/needle";
|
|
101
|
+
const NEEDLE_CHECKPOINT_FILENAME = "needle.pkl";
|
|
102
|
+
const NEEDLE_TOOL_CALL_MARKER = "<tool_call>";
|
|
103
|
+
/**
|
|
104
|
+
* Pinned integrity for `needle.pkl` — see the module-level SECURITY note: this is a pickle file
|
|
105
|
+
* (arbitrary code execution on load by construction), so every download is verified against this
|
|
106
|
+
* exact sha256+size before being kept. Captured 2026-07-18: `sha256sum` of a real download matched
|
|
107
|
+
* Hugging Face's `x-linked-etag` header on the resolve redirect at repo commit
|
|
108
|
+
* `5f89b4307696d669c3df1d38ae057e6e1728b107`. Bump both together (re-verify the same way) only for a
|
|
109
|
+
* deliberate, reviewed upstream weights update.
|
|
110
|
+
*/
|
|
111
|
+
export const NEEDLE_WEIGHTS_SHA256 = "40a32e91d1d4197bf15ba559b74f6727c342dc8746918742fc7d8e2c1f18df40";
|
|
112
|
+
export const NEEDLE_WEIGHTS_BYTES = 52_633_098;
|
|
113
|
+
const NEEDLE_COMMAND_TIMEOUT_MS = 10 * 60_000;
|
|
114
|
+
const NEEDLE_KILL_GRACE_MS = 2_000;
|
|
115
|
+
const NEEDLE_SMOKE_QUERY = "What's the weather in San Francisco?";
|
|
116
|
+
const NEEDLE_SMOKE_TOOLS = [
|
|
117
|
+
{
|
|
118
|
+
name: "get_weather",
|
|
119
|
+
description: "Get current weather for a city.",
|
|
120
|
+
parameters: { location: { type: "string", description: "City name.", required: true } },
|
|
121
|
+
},
|
|
122
|
+
];
|
|
123
|
+
function tailLines(value, count = 3) {
|
|
124
|
+
return value?.trim().split("\n").slice(-count).join("\n") || "unknown error";
|
|
125
|
+
}
|
|
126
|
+
/** Pass bytes through unchanged while feeding them into a running hash — lets a single `pipeline()`
|
|
127
|
+
* call both write the download to disk and compute its sha256 in one streaming pass. */
|
|
128
|
+
function hashingPassThrough(hash) {
|
|
129
|
+
return new Transform({
|
|
130
|
+
transform(chunk, _encoding, callback) {
|
|
131
|
+
hash.update(chunk);
|
|
132
|
+
callback(null, chunk);
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
async function defaultRunCommand(command, args, options = {}) {
|
|
137
|
+
try {
|
|
138
|
+
const proc = spawnProcess(command, args, {
|
|
139
|
+
detached: process.platform !== "win32",
|
|
140
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
141
|
+
env: options.env ? { ...process.env, ...options.env } : process.env,
|
|
142
|
+
});
|
|
143
|
+
let stdout = "";
|
|
144
|
+
let stderr = "";
|
|
145
|
+
proc.stdout.setEncoding("utf8");
|
|
146
|
+
proc.stderr.setEncoding("utf8");
|
|
147
|
+
proc.stdout.on("data", (chunk) => {
|
|
148
|
+
stdout = `${stdout}${chunk}`.slice(-1024 * 1024);
|
|
149
|
+
options.onOutput?.(chunk);
|
|
150
|
+
});
|
|
151
|
+
proc.stderr.on("data", (chunk) => {
|
|
152
|
+
stderr = `${stderr}${chunk}`.slice(-1024 * 1024);
|
|
153
|
+
options.onOutput?.(chunk);
|
|
154
|
+
});
|
|
155
|
+
const terminal = await waitForChildProcessWithTermination(proc, {
|
|
156
|
+
timeoutMs: NEEDLE_COMMAND_TIMEOUT_MS,
|
|
157
|
+
killGraceMs: NEEDLE_KILL_GRACE_MS,
|
|
158
|
+
});
|
|
159
|
+
const timedOut = terminal.reason === "timeout";
|
|
160
|
+
return {
|
|
161
|
+
ok: terminal.code === 0,
|
|
162
|
+
stdout,
|
|
163
|
+
stderr,
|
|
164
|
+
code: terminal.code,
|
|
165
|
+
...(terminal.code === 0
|
|
166
|
+
? {}
|
|
167
|
+
: {
|
|
168
|
+
error: timedOut
|
|
169
|
+
? `${command} timed out after ${NEEDLE_COMMAND_TIMEOUT_MS}ms`
|
|
170
|
+
: stderr.trim() || stdout.trim() || `exit code ${terminal.code ?? "unknown"}`,
|
|
171
|
+
}),
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
return { ok: false, stdout: "", stderr: "", error: error instanceof Error ? error.message : String(error) };
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
export class NeedleRuntime {
|
|
179
|
+
_agentDir;
|
|
180
|
+
_exists;
|
|
181
|
+
_fetch;
|
|
182
|
+
_runCommand;
|
|
183
|
+
_hasCommand;
|
|
184
|
+
_hasNvidiaGpu;
|
|
185
|
+
_platform;
|
|
186
|
+
_weightsIntegrity;
|
|
187
|
+
constructor(args) {
|
|
188
|
+
this._agentDir = args.agentDir;
|
|
189
|
+
this._exists = args.deps?.existsFn ?? existsSync;
|
|
190
|
+
this._fetch = args.deps?.fetchFn ?? fetch;
|
|
191
|
+
this._runCommand = args.deps?.runCommand ?? defaultRunCommand;
|
|
192
|
+
this._hasCommand =
|
|
193
|
+
args.deps?.hasCommand ??
|
|
194
|
+
((command) => spawnProcessSync(command, ["--version"], { encoding: "utf8", timeout: 5_000 }).error === undefined);
|
|
195
|
+
this._hasNvidiaGpu = args.deps?.hasNvidiaGpu ?? (() => this._hasCommand("nvidia-smi"));
|
|
196
|
+
this._platform = args.deps?.platform ?? osPlatform;
|
|
197
|
+
this._weightsIntegrity = args.deps?.weightsIntegrity ?? {
|
|
198
|
+
sha256: NEEDLE_WEIGHTS_SHA256,
|
|
199
|
+
bytes: NEEDLE_WEIGHTS_BYTES,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
runtimeDir() {
|
|
203
|
+
return agentRuntimesDir("needle", this._agentDir);
|
|
204
|
+
}
|
|
205
|
+
modelsDir() {
|
|
206
|
+
return agentModelsDir("needle", this._agentDir);
|
|
207
|
+
}
|
|
208
|
+
checkpointPath() {
|
|
209
|
+
return join(this.modelsDir(), NEEDLE_CHECKPOINT_FILENAME);
|
|
210
|
+
}
|
|
211
|
+
_srcDir() {
|
|
212
|
+
return join(this.runtimeDir(), "src");
|
|
213
|
+
}
|
|
214
|
+
_venvDir() {
|
|
215
|
+
return join(this.runtimeDir(), "venv");
|
|
216
|
+
}
|
|
217
|
+
_pythonPath() {
|
|
218
|
+
return this._platform() === "win32"
|
|
219
|
+
? join(this._venvDir(), "Scripts", "python.exe")
|
|
220
|
+
: join(this._venvDir(), "bin", "python");
|
|
221
|
+
}
|
|
222
|
+
/** The installed `needle` console script (from `[project.scripts]`, written by `pip install -e`)
|
|
223
|
+
* — the only invokable entry point, since `needle/cli.py` has no `__main__` guard. */
|
|
224
|
+
_entryPath() {
|
|
225
|
+
return this._platform() === "win32"
|
|
226
|
+
? join(this._venvDir(), "Scripts", "needle.exe")
|
|
227
|
+
: join(this._venvDir(), "bin", "needle");
|
|
228
|
+
}
|
|
229
|
+
_manifestPath() {
|
|
230
|
+
return join(this.runtimeDir(), "install.json");
|
|
231
|
+
}
|
|
232
|
+
_readManifest() {
|
|
233
|
+
try {
|
|
234
|
+
const parsed = JSON.parse(readFileSync(this._manifestPath(), "utf8"));
|
|
235
|
+
return typeof parsed.commit === "string" && typeof parsed.entryCommand === "string"
|
|
236
|
+
? { commit: parsed.commit, entryCommand: parsed.entryCommand }
|
|
237
|
+
: undefined;
|
|
238
|
+
}
|
|
239
|
+
catch {
|
|
240
|
+
return undefined;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
_writeManifest(metadata) {
|
|
244
|
+
writeFileSync(this._manifestPath(), JSON.stringify(metadata, null, "\t"));
|
|
245
|
+
}
|
|
246
|
+
async detect() {
|
|
247
|
+
const installed = this._exists(this._entryPath());
|
|
248
|
+
return {
|
|
249
|
+
installed,
|
|
250
|
+
installDir: installed ? this.runtimeDir() : undefined,
|
|
251
|
+
commit: this._readManifest()?.commit,
|
|
252
|
+
pythonAvailable: this._hasCommand("python3"),
|
|
253
|
+
checkpointPresent: this._exists(this.checkpointPath()),
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Clone the pinned needle commit and build a contained, pi-owned runtime (consent-gated by the
|
|
258
|
+
* caller, same contract as OllamaRuntime#installManaged / PrismLlamaCppRuntime#installManaged —
|
|
259
|
+
* this method only does the mechanical clone+build+verify and reports the outcome honestly). See
|
|
260
|
+
* the module docstring for exactly which upstream `setup` steps are and are not replicated under
|
|
261
|
+
* containment.
|
|
262
|
+
*/
|
|
263
|
+
async installManaged(onProgress) {
|
|
264
|
+
if (!this._hasCommand("git")) {
|
|
265
|
+
return {
|
|
266
|
+
ok: false,
|
|
267
|
+
error: `git-missing: git is required to clone needle (${NEEDLE_REPO_URL}). Install git (e.g. \`apt-get install git\`, \`brew install git\`) and try again.`,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
if (!this._hasCommand("python3")) {
|
|
271
|
+
return {
|
|
272
|
+
ok: false,
|
|
273
|
+
error: "python-missing: python3 (>=3.11) is required to run needle. Install Python 3.11+ (e.g. `apt-get install python3 python3-venv`, `brew install python@3.12`) and try again.",
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
mkdirSync(this.runtimeDir(), { recursive: true });
|
|
277
|
+
const srcDir = this._srcDir();
|
|
278
|
+
if (this._exists(srcDir))
|
|
279
|
+
rmSync(srcDir, { recursive: true, force: true });
|
|
280
|
+
onProgress?.(`Cloning ${NEEDLE_REPO_URL}…`);
|
|
281
|
+
const cloned = await this._cloneAtPinnedCommit(srcDir);
|
|
282
|
+
if (!cloned.ok)
|
|
283
|
+
return cloned;
|
|
284
|
+
onProgress?.(`Creating isolated Python venv at ${this._venvDir()}…`);
|
|
285
|
+
const venv = await this._runCommand("python3", ["-m", "venv", this._venvDir()]);
|
|
286
|
+
if (!venv.ok)
|
|
287
|
+
return { ok: false, error: `setup-fail: ${tailLines(venv.error ?? venv.stderr)}` };
|
|
288
|
+
if (!this._exists(this._pythonPath())) {
|
|
289
|
+
return { ok: false, error: `setup-fail: venv created but ${this._pythonPath()} was not found` };
|
|
290
|
+
}
|
|
291
|
+
onProgress?.("Upgrading pip inside the pi-managed venv…");
|
|
292
|
+
const pipUpgrade = await this._runCommand(this._pythonPath(), ["-m", "pip", "install", "--upgrade", "pip"]);
|
|
293
|
+
if (!pipUpgrade.ok) {
|
|
294
|
+
return { ok: false, error: `setup-fail: ${tailLines(pipUpgrade.error ?? pipUpgrade.stderr)}` };
|
|
295
|
+
}
|
|
296
|
+
onProgress?.("Installing needle (editable) into the pi-managed venv…");
|
|
297
|
+
const install = await this._runCommand(this._pythonPath(), ["-m", "pip", "install", "-e", srcDir]);
|
|
298
|
+
if (!install.ok)
|
|
299
|
+
return { ok: false, error: `setup-fail: ${tailLines(install.error ?? install.stderr)}` };
|
|
300
|
+
const cudaAvailable = this._hasNvidiaGpu();
|
|
301
|
+
onProgress?.(cudaAvailable ? "Installing JAX (CUDA 12 backend)…" : "Installing JAX (CPU backend)…");
|
|
302
|
+
const jaxArgs = cudaAvailable ? ["jax[cuda12]"] : ["jax"];
|
|
303
|
+
const jax = await this._runCommand(this._pythonPath(), ["-m", "pip", "install", "-U", ...jaxArgs]);
|
|
304
|
+
if (!jax.ok)
|
|
305
|
+
return { ok: false, error: `setup-fail: ${tailLines(jax.error ?? jax.stderr)}` };
|
|
306
|
+
onProgress?.("Verifying the needle install…");
|
|
307
|
+
const verify = await this._runCommand(this._pythonPath(), ["-c", "import jax, needle; print(needle.__file__)"]);
|
|
308
|
+
if (!verify.ok)
|
|
309
|
+
return { ok: false, error: `verify-fail: ${tailLines(verify.error ?? verify.stderr)}` };
|
|
310
|
+
const entryCommand = this._entryPath();
|
|
311
|
+
if (!this._exists(entryCommand)) {
|
|
312
|
+
return { ok: false, error: `verify-fail: install reported success but ${entryCommand} was not found` };
|
|
313
|
+
}
|
|
314
|
+
this._writeManifest({ commit: NEEDLE_PINNED_COMMIT, entryCommand });
|
|
315
|
+
onProgress?.("needle installed.");
|
|
316
|
+
return { ok: true };
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* A plain `git clone --depth 1` only lands on {@link NEEDLE_PINNED_COMMIT} when it still happens
|
|
320
|
+
* to be the remote's current default-branch tip. Fetching the exact SHA directly instead (verified
|
|
321
|
+
* against the real repo) pins reliably even after upstream moves on: GitHub serves any reachable
|
|
322
|
+
* commit for a public repo via `git fetch <sha>`, not just refs.
|
|
323
|
+
*/
|
|
324
|
+
async _cloneAtPinnedCommit(srcDir) {
|
|
325
|
+
const init = await this._runCommand("git", ["init", "-q", srcDir]);
|
|
326
|
+
if (!init.ok)
|
|
327
|
+
return { ok: false, error: `clone-fail: ${tailLines(init.error ?? init.stderr)}` };
|
|
328
|
+
const remote = await this._runCommand("git", ["-C", srcDir, "remote", "add", "origin", NEEDLE_REPO_URL]);
|
|
329
|
+
if (!remote.ok)
|
|
330
|
+
return { ok: false, error: `clone-fail: ${tailLines(remote.error ?? remote.stderr)}` };
|
|
331
|
+
const fetched = await this._runCommand("git", [
|
|
332
|
+
"-C",
|
|
333
|
+
srcDir,
|
|
334
|
+
"fetch",
|
|
335
|
+
"--depth",
|
|
336
|
+
"1",
|
|
337
|
+
"origin",
|
|
338
|
+
NEEDLE_PINNED_COMMIT,
|
|
339
|
+
]);
|
|
340
|
+
if (!fetched.ok)
|
|
341
|
+
return { ok: false, error: `clone-fail: ${tailLines(fetched.error ?? fetched.stderr)}` };
|
|
342
|
+
const checkout = await this._runCommand("git", ["-C", srcDir, "checkout", "-q", "FETCH_HEAD"]);
|
|
343
|
+
if (!checkout.ok)
|
|
344
|
+
return { ok: false, error: `clone-fail: ${tailLines(checkout.error ?? checkout.stderr)}` };
|
|
345
|
+
return { ok: true };
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Download the pretrained base weights (`needle.pkl`). `needle run` takes an explicit
|
|
349
|
+
* `--checkpoint` and does NOT auto-download one (unlike `needle playground`/`finetune`) — see the
|
|
350
|
+
* module docstring. Never called implicitly by installManaged/runFunctionCall; the caller decides
|
|
351
|
+
* when to pull weights.
|
|
352
|
+
*
|
|
353
|
+
* `needle.pkl` is a pickle file — arbitrary code execution on load by construction (see the
|
|
354
|
+
* module-level SECURITY note) — so every download is verified against the pinned sha256+size
|
|
355
|
+
* ({@link NEEDLE_WEIGHTS_SHA256}/{@link NEEDLE_WEIGHTS_BYTES}, overridable only via the
|
|
356
|
+
* `weightsIntegrity` test seam) computed in the same streaming pass that writes the file, with the
|
|
357
|
+
* partial/mismatched file deleted on ANY failure — this never leaves an unverified or wrong pickle
|
|
358
|
+
* sitting where {@link runFunctionCall} would load it.
|
|
359
|
+
*/
|
|
360
|
+
async downloadWeights(onProgress) {
|
|
361
|
+
const destPath = this.checkpointPath();
|
|
362
|
+
const url = `https://huggingface.co/${NEEDLE_HF_MODEL_REPO}/resolve/main/${NEEDLE_CHECKPOINT_FILENAME}`;
|
|
363
|
+
mkdirSync(this.modelsDir(), { recursive: true });
|
|
364
|
+
if (this._exists(destPath) && this._fileSizeBytes(destPath) === this._weightsIntegrity.bytes) {
|
|
365
|
+
onProgress?.(`${NEEDLE_CHECKPOINT_FILENAME} already downloaded (${this._weightsIntegrity.bytes} bytes) — skipping.`);
|
|
366
|
+
return { ok: true, path: destPath, skipped: true };
|
|
367
|
+
}
|
|
368
|
+
onProgress?.(`Downloading ${NEEDLE_CHECKPOINT_FILENAME} from ${NEEDLE_HF_MODEL_REPO}…`);
|
|
369
|
+
let response;
|
|
370
|
+
try {
|
|
371
|
+
response = await this._fetch(url);
|
|
372
|
+
}
|
|
373
|
+
catch (error) {
|
|
374
|
+
return { ok: false, error: `download-fail: ${error instanceof Error ? error.message : String(error)}` };
|
|
375
|
+
}
|
|
376
|
+
if (!response.ok || !response.body) {
|
|
377
|
+
return { ok: false, error: `download-fail: HTTP ${response.status}` };
|
|
378
|
+
}
|
|
379
|
+
const hash = createHash("sha256");
|
|
380
|
+
try {
|
|
381
|
+
await pipeline(response.body, hashingPassThrough(hash), createWriteStream(destPath));
|
|
382
|
+
}
|
|
383
|
+
catch (error) {
|
|
384
|
+
this._cleanupPartial(destPath);
|
|
385
|
+
return { ok: false, error: `download-fail: ${error instanceof Error ? error.message : String(error)}` };
|
|
386
|
+
}
|
|
387
|
+
const actualBytes = this._fileSizeBytes(destPath);
|
|
388
|
+
const actualSha256 = hash.digest("hex");
|
|
389
|
+
if (actualBytes !== this._weightsIntegrity.bytes || actualSha256 !== this._weightsIntegrity.sha256) {
|
|
390
|
+
this._cleanupPartial(destPath);
|
|
391
|
+
return {
|
|
392
|
+
ok: false,
|
|
393
|
+
error: `integrity-fail: downloaded ${NEEDLE_CHECKPOINT_FILENAME} does not match the pinned checksum — expected sha256 ${this._weightsIntegrity.sha256} (${this._weightsIntegrity.bytes} bytes), got ${actualSha256} (${actualBytes ?? 0} bytes). Refusing to keep a pickle file that failed integrity verification.`,
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
onProgress?.(`${NEEDLE_CHECKPOINT_FILENAME} downloaded and verified (sha256 ${actualSha256}).`);
|
|
397
|
+
return { ok: true, path: destPath };
|
|
398
|
+
}
|
|
399
|
+
_fileSizeBytes(path) {
|
|
400
|
+
try {
|
|
401
|
+
return statSync(path).size;
|
|
402
|
+
}
|
|
403
|
+
catch {
|
|
404
|
+
return undefined;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
_cleanupPartial(path) {
|
|
408
|
+
try {
|
|
409
|
+
rmSync(path, { force: true });
|
|
410
|
+
}
|
|
411
|
+
catch {
|
|
412
|
+
// best-effort cleanup
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Run one single-shot inference through `needle run --checkpoint ... --query ... --tools ...`
|
|
417
|
+
* inside the contained venv's installed console script. Argv is passed as an array to the
|
|
418
|
+
* injected `runCommand` seam — never a shell string — so query/tools text containing quotes,
|
|
419
|
+
* spaces, or other shell-hostile characters is passed through literally (verified against a real
|
|
420
|
+
* invocation; see the module docstring). `tools` is serialized with `JSON.stringify` exactly once.
|
|
421
|
+
*/
|
|
422
|
+
async runFunctionCall(request, options) {
|
|
423
|
+
const entryCommand = this._entryPath();
|
|
424
|
+
if (!this._exists(entryCommand)) {
|
|
425
|
+
return { ok: false, error: "not-installed: run installManaged() first", rawOutput: "" };
|
|
426
|
+
}
|
|
427
|
+
const checkpointPath = options?.checkpointPath ?? this.checkpointPath();
|
|
428
|
+
if (!this._exists(checkpointPath)) {
|
|
429
|
+
return {
|
|
430
|
+
ok: false,
|
|
431
|
+
error: `checkpoint-missing: no checkpoint at ${checkpointPath} — run downloadWeights() first`,
|
|
432
|
+
rawOutput: "",
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
const argv = [
|
|
436
|
+
"run",
|
|
437
|
+
"--checkpoint",
|
|
438
|
+
checkpointPath,
|
|
439
|
+
"--query",
|
|
440
|
+
request.query,
|
|
441
|
+
"--tools",
|
|
442
|
+
JSON.stringify(request.tools),
|
|
443
|
+
];
|
|
444
|
+
const result = await this._runCommand(entryCommand, argv);
|
|
445
|
+
const rawOutput = [result.stdout, result.stderr].filter(Boolean).join("\n");
|
|
446
|
+
if (!result.ok) {
|
|
447
|
+
return { ok: false, error: `run-fail: ${tailLines(result.error ?? result.stderr)}`, rawOutput };
|
|
448
|
+
}
|
|
449
|
+
return this._parseFunctionCall(result.stdout, rawOutput);
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* Strictly parse a real `needle run` stdout capture:
|
|
453
|
+
* ```
|
|
454
|
+
* Loading checkpoint: <path>
|
|
455
|
+
* Model parameters: <n>
|
|
456
|
+
*
|
|
457
|
+
* Query: <query>
|
|
458
|
+
* Tools: <tools, truncated to 80 chars>...
|
|
459
|
+
*
|
|
460
|
+
* <tool_call>[{"name":"...","arguments":{...}}]
|
|
461
|
+
* ```
|
|
462
|
+
* (diagnostic lines above the marker vary — e.g. a one-time "Downloading pretrained tokenizer..."
|
|
463
|
+
* line on a cold cache — so parsing locates the `<tool_call>` marker rather than assuming a fixed
|
|
464
|
+
* line count/position). Unparseable output is always an error carrying the raw text, never a
|
|
465
|
+
* guessed result.
|
|
466
|
+
*/
|
|
467
|
+
_parseFunctionCall(stdout, rawOutput) {
|
|
468
|
+
const markerIndex = stdout.indexOf(NEEDLE_TOOL_CALL_MARKER);
|
|
469
|
+
if (markerIndex < 0) {
|
|
470
|
+
return { ok: false, error: `unparseable-output: no ${NEEDLE_TOOL_CALL_MARKER} marker in stdout`, rawOutput };
|
|
471
|
+
}
|
|
472
|
+
const jsonText = stdout.slice(markerIndex + NEEDLE_TOOL_CALL_MARKER.length).trim();
|
|
473
|
+
let parsed;
|
|
474
|
+
try {
|
|
475
|
+
parsed = JSON.parse(jsonText);
|
|
476
|
+
}
|
|
477
|
+
catch (error) {
|
|
478
|
+
return {
|
|
479
|
+
ok: false,
|
|
480
|
+
error: `unparseable-output: ${error instanceof Error ? error.message : String(error)}`,
|
|
481
|
+
rawOutput,
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
if (!Array.isArray(parsed) || parsed.length === 0) {
|
|
485
|
+
return { ok: false, error: "unparseable-output: expected a non-empty tool-call array", rawOutput };
|
|
486
|
+
}
|
|
487
|
+
const first = parsed[0];
|
|
488
|
+
if (typeof first !== "object" ||
|
|
489
|
+
first === null ||
|
|
490
|
+
typeof first.name !== "string" ||
|
|
491
|
+
typeof first.arguments !== "object" ||
|
|
492
|
+
first.arguments === null) {
|
|
493
|
+
return { ok: false, error: "unparseable-output: tool call missing name/arguments", rawOutput };
|
|
494
|
+
}
|
|
495
|
+
return { ok: true, call: { name: first.name, arguments: first.arguments } };
|
|
496
|
+
}
|
|
497
|
+
/** Canned get_weather query/tool through {@link runFunctionCall}; the wiring task uses this as the
|
|
498
|
+
* post-install verification step. */
|
|
499
|
+
async smokeTest(onProgress) {
|
|
500
|
+
onProgress?.("Running needle smoke test…");
|
|
501
|
+
const startedAt = Date.now();
|
|
502
|
+
const result = await this.runFunctionCall({ query: NEEDLE_SMOKE_QUERY, tools: NEEDLE_SMOKE_TOOLS });
|
|
503
|
+
const latencyMs = Date.now() - startedAt;
|
|
504
|
+
if (!result.ok) {
|
|
505
|
+
onProgress?.(`Smoke test failed: ${result.error}`);
|
|
506
|
+
return { ok: false, latencyMs, error: result.error };
|
|
507
|
+
}
|
|
508
|
+
onProgress?.(`Smoke test ok (${latencyMs}ms): ${result.call.name}`);
|
|
509
|
+
return { ok: true, latencyMs, call: result.call };
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* No-op: every needle invocation here is a single-shot `runCommand` call that spawns, waits for
|
|
513
|
+
* exit, and returns — there is no persistent/detached child process this runtime owns between
|
|
514
|
+
* calls for dispose() to track or kill (contrast OllamaRuntime/PrismLlamaCppRuntime's long-lived
|
|
515
|
+
* `serve()` child).
|
|
516
|
+
*/
|
|
517
|
+
dispose() { }
|
|
518
|
+
}
|
|
519
|
+
//# sourceMappingURL=needle-runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"needle-runtime.js","sourceRoot":"","sources":["../../../src/core/models/needle-runtime.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { createWriteStream, existsSync, mkdirSync, readFileSync, rmSync, statSync, writeFileSync } from \"node:fs\";\nimport { platform as osPlatform } from \"node:os\";\nimport { join } from \"node:path\";\nimport { type Readable, Transform } from \"node:stream\";\nimport { pipeline } from \"node:stream/promises\";\nimport { spawnProcess, spawnProcessSync, waitForChildProcessWithTermination } from \"../../utils/child-process.ts\";\nimport { modelsDir as agentModelsDir, runtimesDir as agentRuntimesDir } from \"../agent-paths.ts\";\nimport type { RuntimeCommandResult, RuntimeCommandRunner } from \"./local-runtime.ts\";\n\n/**\n * Managed runtime for needle (https://github.com/cactus-compute/needle), a 26M-parameter single-shot\n * function-calling model. Needle exposes no server — the only integration surface is CLI/library\n * invocation — so this module owns clone+build of a pinned commit into a contained venv, base\n * checkpoint download, and one-shot `needle run` invocation/parsing. Mirrors local-runtime.ts's\n * OllamaRuntime/TransformersRuntime and llamacpp-runtime.ts's PrismLlamaCppRuntime: injectable seams\n * for exists/fetch/run-command, pi-owned directories under agentDir (runtimes/, models/), onProgress\n * as best-effort UI feedback, and honest error taxonomies instead of silent fallbacks.\n *\n * Investigation findings (verified against a real clone + real venv + real CPU inference run, not\n * just the README):\n *\n * - `needle run` (the CLI's single-inference subcommand) takes a REQUIRED `--checkpoint` and does\n * NOT auto-download one — unlike `needle playground`/`finetune`, which resolve a checkpoint\n * internally. The pretrained base checkpoint (`needle.pkl`) lives on `Cactus-Compute/needle` on\n * Hugging Face and is fetched directly by {@link NeedleRuntime.downloadWeights} (never invoked\n * implicitly by installManaged/runFunctionCall — this module never silently starts a large\n * download on the caller's behalf).\n * - SECURITY (owner-flagged, verified during investigation): `Cactus-Compute/needle` on Hugging Face\n * publishes THREE weight artifacts — `needle.pkl` (52,633,098 bytes), `model.safetensors`\n * (reported as a different param count on the HF model card, 30.4M vs the 26,315,421 a real\n * `needle run` printed — likely not even the same checkpoint), and `needle-cq4.zip`. I grepped the\n * ENTIRE needle package (cli.py, model/run.py, model/export.py, model/quantize.py,\n * training/{finetune,pretrain,train}.py, utils/distributed.py, ui/server.py — every `pickle`/`.zip`\n * hit in the repo) and confirmed every checkpoint load/save path in the codebase — including the\n * one `needle run` uses — is `pickle.load`/`pickle.dump` on a `.pkl` file. There is NO safetensors\n * reader anywhere in the package; `model.safetensors` is not consumed by any code this module\n * drives. `needle-cq4.zip` (\"Cactus Quantized 4-bit\"?) isn't referenced by any code here either —\n * the README states production inference runs on the separate Cactus C++ engine\n * (cactus-compute/cactus), which very plausibly consumes its own format; that repo was not audited\n * here. Bottom line: **this module has no choice but to fetch and deserialize `needle.pkl` via\n * Python's `pickle` module, which executes arbitrary code by construction** — there is no\n * safetensors alternative available in the code path we drive. Given that, {@link\n * NeedleRuntime.downloadWeights} pins the exact sha256 ({@link NEEDLE_WEIGHTS_SHA256}) and byte\n * count ({@link NEEDLE_WEIGHTS_BYTES}) of the file as verified on 2026-07-18 (direct `sha256sum` of\n * a real download, cross-checked against Hugging Face's own `x-linked-etag`/`x-linked-size`\n * headers on the resolve redirect, at repo commit `5f89b4307696d669c3df1d38ae057e6e1728b107`) and\n * VERIFIES every download against that pin, refusing to keep any file that doesn't match — this\n * catches a compromised/tampered/swapped artifact but does NOT make loading a pickle file safe in\n * the abstract; the actual `pickle.load` execution happens entirely inside needle's own Python\n * process, outside this module's control. The owner should decide whether serving this model at\n * all is acceptable given upstream's format choice.\n * - The upstream `setup` script does more than a contained managed install can honor: it can `sudo\n * apt-get install` a newer Python or `python3-venv`, `sudo modprobe` TPU kernel modules, toggle\n * transparent hugepages via a root-owned sysfs write, and interactively prompts for a\n * WANDB_API_KEY. None of that is available under containment (no writes outside the pi-owned dir,\n * no sudo, no interactive prompts, no global installs) and none of it is required for the\n * documented Mac/PC single-inference quickstart path. {@link NeedleRuntime.installManaged} instead\n * replicates only the containable core: `python3 -m venv`, `pip install -e <clonedRepo>` (the\n * script's own `pip install -e . -q`), and the CPU (or CUDA, when an NVIDIA GPU is detected) JAX\n * backend install. TPU support and the sudo-gated OS package/kernel-module steps are intentionally\n * NOT replicated.\n * - `needle run`'s console-script entry point (`<venv>/bin/needle`, installed by\n * `pip install -e .` from the `[project.scripts]` table) is the only way to invoke the CLI:\n * `needle/cli.py` has no `if __name__ == \"__main__\":` guard, so `python -m needle.cli` silently\n * does nothing — the installed console script is required.\n * - A verified real stdout capture of `needle run --checkpoint <path> --query \"...\" --tools '[...]'`\n * is multi-line diagnostic prose followed by the tool call:\n * ```\n * Loading checkpoint: <path>\n * Downloading pretrained tokenizer from HuggingFace... (first run only — cached after)\n * Model parameters: 26,315,421\n *\n * Query: <query>\n * Tools: <tools JSON, truncated to 80 chars>...\n *\n * <tool_call>[{\"name\":\"get_weather\",\"arguments\":{\"location\":\"SanFrancisco\"}}]\n * ```\n * The `<tool_call>` marker is a literal SentencePiece user-defined-symbol token, always emitted as\n * the first generated token per the model's own answer format. {@link NeedleRuntime}'s stdout\n * parser locates that marker and strictly `JSON.parse`s everything after it — never guessing a\n * result from unparseable output.\n * - Known upstream quirk (verified against a live run with a camelCase tool name): `needle run`'s CLI\n * wrapper (`main()` in `needle/model/run.py`) streams tokens straight to stdout and never applies\n * the tool-name restoration its own `generate()` return value receives — a tool name that wasn't\n * already snake_case (e.g. `getWeatherNow`) is reported back snake-cased (`get_weather_now`), not\n * in its original casing. This is a limitation of the upstream CLI, not something patched here.\n * - Pinned commit `ffb1c5144c5a16cb8ec650dbc8a6f6fd3854f8f2` was captured via a real `git clone` of\n * the repo's default branch HEAD on 2026-07-18 (the repo's own history shows no pushes since\n * 2026-07-01, so this is not stale). A plain `git clone --depth 1` only reliably lands on a pinned\n * commit when it still happens to be the remote's current tip; {@link NeedleRuntime.installManaged}\n * instead fetches the exact commit SHA directly (`git fetch --depth 1 origin <sha>`), which GitHub\n * serves for any reachable commit on a public repo, so the pin holds even after upstream moves on.\n */\n\nexport const NEEDLE_REPO_URL = \"https://github.com/cactus-compute/needle.git\";\n\n/**\n * Pinned needle commit — captured via a real `git clone` of the repo's default-branch HEAD on\n * 2026-07-18 (`git ls-remote https://github.com/cactus-compute/needle HEAD` resolves to the same\n * SHA). Bump here (re-verify with the same command) when a newer commit is needed.\n */\nexport const NEEDLE_PINNED_COMMIT = \"ffb1c5144c5a16cb8ec650dbc8a6f6fd3854f8f2\";\n\nconst NEEDLE_HF_MODEL_REPO = \"Cactus-Compute/needle\";\nconst NEEDLE_CHECKPOINT_FILENAME = \"needle.pkl\";\nconst NEEDLE_TOOL_CALL_MARKER = \"<tool_call>\";\n\n/**\n * Pinned integrity for `needle.pkl` — see the module-level SECURITY note: this is a pickle file\n * (arbitrary code execution on load by construction), so every download is verified against this\n * exact sha256+size before being kept. Captured 2026-07-18: `sha256sum` of a real download matched\n * Hugging Face's `x-linked-etag` header on the resolve redirect at repo commit\n * `5f89b4307696d669c3df1d38ae057e6e1728b107`. Bump both together (re-verify the same way) only for a\n * deliberate, reviewed upstream weights update.\n */\nexport const NEEDLE_WEIGHTS_SHA256 = \"40a32e91d1d4197bf15ba559b74f6727c342dc8746918742fc7d8e2c1f18df40\";\nexport const NEEDLE_WEIGHTS_BYTES = 52_633_098;\n\nconst NEEDLE_COMMAND_TIMEOUT_MS = 10 * 60_000;\nconst NEEDLE_KILL_GRACE_MS = 2_000;\n\nconst NEEDLE_SMOKE_QUERY = \"What's the weather in San Francisco?\";\nconst NEEDLE_SMOKE_TOOLS = [\n\t{\n\t\tname: \"get_weather\",\n\t\tdescription: \"Get current weather for a city.\",\n\t\tparameters: { location: { type: \"string\", description: \"City name.\", required: true } },\n\t},\n];\n\nexport interface NeedleDetectResult {\n\tinstalled: boolean;\n\tinstallDir?: string;\n\tcommit?: string;\n\tpythonAvailable: boolean;\n\tcheckpointPresent: boolean;\n}\n\nexport interface NeedleFunctionCallRequest {\n\tquery: string;\n\t/** A JSON-serializable tool schema value (never a pre-stringified JSON string) — serialized\n\t * exactly once by {@link NeedleRuntime.runFunctionCall}, never by the caller. */\n\ttools: unknown;\n}\n\nexport interface NeedleFunctionCall {\n\tname: string;\n\targuments: Record<string, unknown>;\n}\n\nexport type NeedleFunctionCallResult =\n\t| { ok: true; call: NeedleFunctionCall }\n\t| { ok: false; error: string; rawOutput: string };\n\nexport interface NeedleSmokeTestResult {\n\tok: boolean;\n\tlatencyMs: number;\n\tcall?: NeedleFunctionCall;\n\terror?: string;\n}\n\nexport interface NeedleWeightsDownloadResult {\n\tok: boolean;\n\tpath?: string;\n\tskipped?: boolean;\n\terror?: string;\n}\n\ninterface NeedleWeightsIntegrity {\n\tsha256: string;\n\tbytes: number;\n}\n\nexport interface NeedleRuntimeDeps {\n\texistsFn?: (path: string) => boolean;\n\tfetchFn?: typeof fetch;\n\t/** Runs git/pip/python commands. Injectable so installManaged/runFunctionCall's orchestration is\n\t * testable without a real clone/venv/inference. Defaults to a real spawn-and-collect-output\n\t * runner (installs can run long; a single inference call is comparatively quick but still shells\n\t * out to a real interpreter). */\n\trunCommand?: RuntimeCommandRunner;\n\t/** Whether a named command exists on PATH (git, python3, nvidia-smi). */\n\thasCommand?: (command: string) => boolean;\n\thasNvidiaGpu?: () => boolean;\n\tplatform?: () => string;\n\t/** Expected weights sha256+size. Defaults to {@link NEEDLE_WEIGHTS_SHA256}/{@link\n\t * NEEDLE_WEIGHTS_BYTES} — the real pinned values. Overridable ONLY so tests can exercise the full\n\t * download+verify path with small synthetic content instead of the real 52MB pickle; this is a\n\t * constructor-level seam for a trusted caller, the same trust boundary every other injectable dep\n\t * in this class already relies on (fetchFn/runCommand could equally be used to bypass anything).\n\t */\n\tweightsIntegrity?: NeedleWeightsIntegrity;\n}\n\ninterface NeedleInstallMetadata {\n\tcommit: string;\n\tentryCommand: string;\n}\n\nfunction tailLines(value: string | undefined, count = 3): string {\n\treturn value?.trim().split(\"\\n\").slice(-count).join(\"\\n\") || \"unknown error\";\n}\n\n/** Pass bytes through unchanged while feeding them into a running hash — lets a single `pipeline()`\n * call both write the download to disk and compute its sha256 in one streaming pass. */\nfunction hashingPassThrough(hash: ReturnType<typeof createHash>): Transform {\n\treturn new Transform({\n\t\ttransform(chunk: Buffer, _encoding, callback) {\n\t\t\thash.update(chunk);\n\t\t\tcallback(null, chunk);\n\t\t},\n\t});\n}\n\nasync function defaultRunCommand(\n\tcommand: string,\n\targs: string[],\n\toptions: { env?: NodeJS.ProcessEnv; onOutput?: (chunk: string) => void } = {},\n): Promise<RuntimeCommandResult> {\n\ttry {\n\t\tconst proc = spawnProcess(command, args, {\n\t\t\tdetached: process.platform !== \"win32\",\n\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\tenv: options.env ? { ...process.env, ...options.env } : process.env,\n\t\t});\n\t\tlet stdout = \"\";\n\t\tlet stderr = \"\";\n\t\tproc.stdout.setEncoding(\"utf8\");\n\t\tproc.stderr.setEncoding(\"utf8\");\n\t\tproc.stdout.on(\"data\", (chunk: string) => {\n\t\t\tstdout = `${stdout}${chunk}`.slice(-1024 * 1024);\n\t\t\toptions.onOutput?.(chunk);\n\t\t});\n\t\tproc.stderr.on(\"data\", (chunk: string) => {\n\t\t\tstderr = `${stderr}${chunk}`.slice(-1024 * 1024);\n\t\t\toptions.onOutput?.(chunk);\n\t\t});\n\t\tconst terminal = await waitForChildProcessWithTermination(proc, {\n\t\t\ttimeoutMs: NEEDLE_COMMAND_TIMEOUT_MS,\n\t\t\tkillGraceMs: NEEDLE_KILL_GRACE_MS,\n\t\t});\n\t\tconst timedOut = terminal.reason === \"timeout\";\n\t\treturn {\n\t\t\tok: terminal.code === 0,\n\t\t\tstdout,\n\t\t\tstderr,\n\t\t\tcode: terminal.code,\n\t\t\t...(terminal.code === 0\n\t\t\t\t? {}\n\t\t\t\t: {\n\t\t\t\t\t\terror: timedOut\n\t\t\t\t\t\t\t? `${command} timed out after ${NEEDLE_COMMAND_TIMEOUT_MS}ms`\n\t\t\t\t\t\t\t: stderr.trim() || stdout.trim() || `exit code ${terminal.code ?? \"unknown\"}`,\n\t\t\t\t\t}),\n\t\t};\n\t} catch (error) {\n\t\treturn { ok: false, stdout: \"\", stderr: \"\", error: error instanceof Error ? error.message : String(error) };\n\t}\n}\n\nexport class NeedleRuntime {\n\tprivate readonly _agentDir: string;\n\tprivate readonly _exists: (path: string) => boolean;\n\tprivate readonly _fetch: typeof fetch;\n\tprivate readonly _runCommand: RuntimeCommandRunner;\n\tprivate readonly _hasCommand: (command: string) => boolean;\n\tprivate readonly _hasNvidiaGpu: () => boolean;\n\tprivate readonly _platform: () => string;\n\tprivate readonly _weightsIntegrity: NeedleWeightsIntegrity;\n\n\tconstructor(args: { agentDir: string; deps?: NeedleRuntimeDeps }) {\n\t\tthis._agentDir = args.agentDir;\n\t\tthis._exists = args.deps?.existsFn ?? existsSync;\n\t\tthis._fetch = args.deps?.fetchFn ?? fetch;\n\t\tthis._runCommand = args.deps?.runCommand ?? defaultRunCommand;\n\t\tthis._hasCommand =\n\t\t\targs.deps?.hasCommand ??\n\t\t\t((command) =>\n\t\t\t\tspawnProcessSync(command, [\"--version\"], { encoding: \"utf8\", timeout: 5_000 }).error === undefined);\n\t\tthis._hasNvidiaGpu = args.deps?.hasNvidiaGpu ?? (() => this._hasCommand(\"nvidia-smi\"));\n\t\tthis._platform = args.deps?.platform ?? osPlatform;\n\t\tthis._weightsIntegrity = args.deps?.weightsIntegrity ?? {\n\t\t\tsha256: NEEDLE_WEIGHTS_SHA256,\n\t\t\tbytes: NEEDLE_WEIGHTS_BYTES,\n\t\t};\n\t}\n\n\truntimeDir(): string {\n\t\treturn agentRuntimesDir(\"needle\", this._agentDir);\n\t}\n\n\tmodelsDir(): string {\n\t\treturn agentModelsDir(\"needle\", this._agentDir);\n\t}\n\n\tcheckpointPath(): string {\n\t\treturn join(this.modelsDir(), NEEDLE_CHECKPOINT_FILENAME);\n\t}\n\n\tprivate _srcDir(): string {\n\t\treturn join(this.runtimeDir(), \"src\");\n\t}\n\n\tprivate _venvDir(): string {\n\t\treturn join(this.runtimeDir(), \"venv\");\n\t}\n\n\tprivate _pythonPath(): string {\n\t\treturn this._platform() === \"win32\"\n\t\t\t? join(this._venvDir(), \"Scripts\", \"python.exe\")\n\t\t\t: join(this._venvDir(), \"bin\", \"python\");\n\t}\n\n\t/** The installed `needle` console script (from `[project.scripts]`, written by `pip install -e`)\n\t * — the only invokable entry point, since `needle/cli.py` has no `__main__` guard. */\n\tprivate _entryPath(): string {\n\t\treturn this._platform() === \"win32\"\n\t\t\t? join(this._venvDir(), \"Scripts\", \"needle.exe\")\n\t\t\t: join(this._venvDir(), \"bin\", \"needle\");\n\t}\n\n\tprivate _manifestPath(): string {\n\t\treturn join(this.runtimeDir(), \"install.json\");\n\t}\n\n\tprivate _readManifest(): NeedleInstallMetadata | undefined {\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(readFileSync(this._manifestPath(), \"utf8\")) as {\n\t\t\t\tcommit?: unknown;\n\t\t\t\tentryCommand?: unknown;\n\t\t\t};\n\t\t\treturn typeof parsed.commit === \"string\" && typeof parsed.entryCommand === \"string\"\n\t\t\t\t? { commit: parsed.commit, entryCommand: parsed.entryCommand }\n\t\t\t\t: undefined;\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\tprivate _writeManifest(metadata: NeedleInstallMetadata): void {\n\t\twriteFileSync(this._manifestPath(), JSON.stringify(metadata, null, \"\\t\"));\n\t}\n\n\tasync detect(): Promise<NeedleDetectResult> {\n\t\tconst installed = this._exists(this._entryPath());\n\t\treturn {\n\t\t\tinstalled,\n\t\t\tinstallDir: installed ? this.runtimeDir() : undefined,\n\t\t\tcommit: this._readManifest()?.commit,\n\t\t\tpythonAvailable: this._hasCommand(\"python3\"),\n\t\t\tcheckpointPresent: this._exists(this.checkpointPath()),\n\t\t};\n\t}\n\n\t/**\n\t * Clone the pinned needle commit and build a contained, pi-owned runtime (consent-gated by the\n\t * caller, same contract as OllamaRuntime#installManaged / PrismLlamaCppRuntime#installManaged —\n\t * this method only does the mechanical clone+build+verify and reports the outcome honestly). See\n\t * the module docstring for exactly which upstream `setup` steps are and are not replicated under\n\t * containment.\n\t */\n\tasync installManaged(onProgress?: (status: string) => void): Promise<{ ok: boolean; error?: string }> {\n\t\tif (!this._hasCommand(\"git\")) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: `git-missing: git is required to clone needle (${NEEDLE_REPO_URL}). Install git (e.g. \\`apt-get install git\\`, \\`brew install git\\`) and try again.`,\n\t\t\t};\n\t\t}\n\t\tif (!this._hasCommand(\"python3\")) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: \"python-missing: python3 (>=3.11) is required to run needle. Install Python 3.11+ (e.g. `apt-get install python3 python3-venv`, `brew install python@3.12`) and try again.\",\n\t\t\t};\n\t\t}\n\n\t\tmkdirSync(this.runtimeDir(), { recursive: true });\n\t\tconst srcDir = this._srcDir();\n\t\tif (this._exists(srcDir)) rmSync(srcDir, { recursive: true, force: true });\n\n\t\tonProgress?.(`Cloning ${NEEDLE_REPO_URL}…`);\n\t\tconst cloned = await this._cloneAtPinnedCommit(srcDir);\n\t\tif (!cloned.ok) return cloned;\n\n\t\tonProgress?.(`Creating isolated Python venv at ${this._venvDir()}…`);\n\t\tconst venv = await this._runCommand(\"python3\", [\"-m\", \"venv\", this._venvDir()]);\n\t\tif (!venv.ok) return { ok: false, error: `setup-fail: ${tailLines(venv.error ?? venv.stderr)}` };\n\t\tif (!this._exists(this._pythonPath())) {\n\t\t\treturn { ok: false, error: `setup-fail: venv created but ${this._pythonPath()} was not found` };\n\t\t}\n\n\t\tonProgress?.(\"Upgrading pip inside the pi-managed venv…\");\n\t\tconst pipUpgrade = await this._runCommand(this._pythonPath(), [\"-m\", \"pip\", \"install\", \"--upgrade\", \"pip\"]);\n\t\tif (!pipUpgrade.ok) {\n\t\t\treturn { ok: false, error: `setup-fail: ${tailLines(pipUpgrade.error ?? pipUpgrade.stderr)}` };\n\t\t}\n\n\t\tonProgress?.(\"Installing needle (editable) into the pi-managed venv…\");\n\t\tconst install = await this._runCommand(this._pythonPath(), [\"-m\", \"pip\", \"install\", \"-e\", srcDir]);\n\t\tif (!install.ok) return { ok: false, error: `setup-fail: ${tailLines(install.error ?? install.stderr)}` };\n\n\t\tconst cudaAvailable = this._hasNvidiaGpu();\n\t\tonProgress?.(cudaAvailable ? \"Installing JAX (CUDA 12 backend)…\" : \"Installing JAX (CPU backend)…\");\n\t\tconst jaxArgs = cudaAvailable ? [\"jax[cuda12]\"] : [\"jax\"];\n\t\tconst jax = await this._runCommand(this._pythonPath(), [\"-m\", \"pip\", \"install\", \"-U\", ...jaxArgs]);\n\t\tif (!jax.ok) return { ok: false, error: `setup-fail: ${tailLines(jax.error ?? jax.stderr)}` };\n\n\t\tonProgress?.(\"Verifying the needle install…\");\n\t\tconst verify = await this._runCommand(this._pythonPath(), [\"-c\", \"import jax, needle; print(needle.__file__)\"]);\n\t\tif (!verify.ok) return { ok: false, error: `verify-fail: ${tailLines(verify.error ?? verify.stderr)}` };\n\n\t\tconst entryCommand = this._entryPath();\n\t\tif (!this._exists(entryCommand)) {\n\t\t\treturn { ok: false, error: `verify-fail: install reported success but ${entryCommand} was not found` };\n\t\t}\n\n\t\tthis._writeManifest({ commit: NEEDLE_PINNED_COMMIT, entryCommand });\n\t\tonProgress?.(\"needle installed.\");\n\t\treturn { ok: true };\n\t}\n\n\t/**\n\t * A plain `git clone --depth 1` only lands on {@link NEEDLE_PINNED_COMMIT} when it still happens\n\t * to be the remote's current default-branch tip. Fetching the exact SHA directly instead (verified\n\t * against the real repo) pins reliably even after upstream moves on: GitHub serves any reachable\n\t * commit for a public repo via `git fetch <sha>`, not just refs.\n\t */\n\tprivate async _cloneAtPinnedCommit(srcDir: string): Promise<{ ok: boolean; error?: string }> {\n\t\tconst init = await this._runCommand(\"git\", [\"init\", \"-q\", srcDir]);\n\t\tif (!init.ok) return { ok: false, error: `clone-fail: ${tailLines(init.error ?? init.stderr)}` };\n\t\tconst remote = await this._runCommand(\"git\", [\"-C\", srcDir, \"remote\", \"add\", \"origin\", NEEDLE_REPO_URL]);\n\t\tif (!remote.ok) return { ok: false, error: `clone-fail: ${tailLines(remote.error ?? remote.stderr)}` };\n\t\tconst fetched = await this._runCommand(\"git\", [\n\t\t\t\"-C\",\n\t\t\tsrcDir,\n\t\t\t\"fetch\",\n\t\t\t\"--depth\",\n\t\t\t\"1\",\n\t\t\t\"origin\",\n\t\t\tNEEDLE_PINNED_COMMIT,\n\t\t]);\n\t\tif (!fetched.ok) return { ok: false, error: `clone-fail: ${tailLines(fetched.error ?? fetched.stderr)}` };\n\t\tconst checkout = await this._runCommand(\"git\", [\"-C\", srcDir, \"checkout\", \"-q\", \"FETCH_HEAD\"]);\n\t\tif (!checkout.ok) return { ok: false, error: `clone-fail: ${tailLines(checkout.error ?? checkout.stderr)}` };\n\t\treturn { ok: true };\n\t}\n\n\t/**\n\t * Download the pretrained base weights (`needle.pkl`). `needle run` takes an explicit\n\t * `--checkpoint` and does NOT auto-download one (unlike `needle playground`/`finetune`) — see the\n\t * module docstring. Never called implicitly by installManaged/runFunctionCall; the caller decides\n\t * when to pull weights.\n\t *\n\t * `needle.pkl` is a pickle file — arbitrary code execution on load by construction (see the\n\t * module-level SECURITY note) — so every download is verified against the pinned sha256+size\n\t * ({@link NEEDLE_WEIGHTS_SHA256}/{@link NEEDLE_WEIGHTS_BYTES}, overridable only via the\n\t * `weightsIntegrity` test seam) computed in the same streaming pass that writes the file, with the\n\t * partial/mismatched file deleted on ANY failure — this never leaves an unverified or wrong pickle\n\t * sitting where {@link runFunctionCall} would load it.\n\t */\n\tasync downloadWeights(onProgress?: (status: string) => void): Promise<NeedleWeightsDownloadResult> {\n\t\tconst destPath = this.checkpointPath();\n\t\tconst url = `https://huggingface.co/${NEEDLE_HF_MODEL_REPO}/resolve/main/${NEEDLE_CHECKPOINT_FILENAME}`;\n\t\tmkdirSync(this.modelsDir(), { recursive: true });\n\n\t\tif (this._exists(destPath) && this._fileSizeBytes(destPath) === this._weightsIntegrity.bytes) {\n\t\t\tonProgress?.(\n\t\t\t\t`${NEEDLE_CHECKPOINT_FILENAME} already downloaded (${this._weightsIntegrity.bytes} bytes) — skipping.`,\n\t\t\t);\n\t\t\treturn { ok: true, path: destPath, skipped: true };\n\t\t}\n\n\t\tonProgress?.(`Downloading ${NEEDLE_CHECKPOINT_FILENAME} from ${NEEDLE_HF_MODEL_REPO}…`);\n\t\tlet response: Response;\n\t\ttry {\n\t\t\tresponse = await this._fetch(url);\n\t\t} catch (error) {\n\t\t\treturn { ok: false, error: `download-fail: ${error instanceof Error ? error.message : String(error)}` };\n\t\t}\n\t\tif (!response.ok || !response.body) {\n\t\t\treturn { ok: false, error: `download-fail: HTTP ${response.status}` };\n\t\t}\n\n\t\tconst hash = createHash(\"sha256\");\n\t\ttry {\n\t\t\tawait pipeline(response.body as unknown as Readable, hashingPassThrough(hash), createWriteStream(destPath));\n\t\t} catch (error) {\n\t\t\tthis._cleanupPartial(destPath);\n\t\t\treturn { ok: false, error: `download-fail: ${error instanceof Error ? error.message : String(error)}` };\n\t\t}\n\n\t\tconst actualBytes = this._fileSizeBytes(destPath);\n\t\tconst actualSha256 = hash.digest(\"hex\");\n\t\tif (actualBytes !== this._weightsIntegrity.bytes || actualSha256 !== this._weightsIntegrity.sha256) {\n\t\t\tthis._cleanupPartial(destPath);\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: `integrity-fail: downloaded ${NEEDLE_CHECKPOINT_FILENAME} does not match the pinned checksum — expected sha256 ${this._weightsIntegrity.sha256} (${this._weightsIntegrity.bytes} bytes), got ${actualSha256} (${actualBytes ?? 0} bytes). Refusing to keep a pickle file that failed integrity verification.`,\n\t\t\t};\n\t\t}\n\n\t\tonProgress?.(`${NEEDLE_CHECKPOINT_FILENAME} downloaded and verified (sha256 ${actualSha256}).`);\n\t\treturn { ok: true, path: destPath };\n\t}\n\n\tprivate _fileSizeBytes(path: string): number | undefined {\n\t\ttry {\n\t\t\treturn statSync(path).size;\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\tprivate _cleanupPartial(path: string): void {\n\t\ttry {\n\t\t\trmSync(path, { force: true });\n\t\t} catch {\n\t\t\t// best-effort cleanup\n\t\t}\n\t}\n\n\t/**\n\t * Run one single-shot inference through `needle run --checkpoint ... --query ... --tools ...`\n\t * inside the contained venv's installed console script. Argv is passed as an array to the\n\t * injected `runCommand` seam — never a shell string — so query/tools text containing quotes,\n\t * spaces, or other shell-hostile characters is passed through literally (verified against a real\n\t * invocation; see the module docstring). `tools` is serialized with `JSON.stringify` exactly once.\n\t */\n\tasync runFunctionCall(\n\t\trequest: NeedleFunctionCallRequest,\n\t\toptions?: { checkpointPath?: string },\n\t): Promise<NeedleFunctionCallResult> {\n\t\tconst entryCommand = this._entryPath();\n\t\tif (!this._exists(entryCommand)) {\n\t\t\treturn { ok: false, error: \"not-installed: run installManaged() first\", rawOutput: \"\" };\n\t\t}\n\t\tconst checkpointPath = options?.checkpointPath ?? this.checkpointPath();\n\t\tif (!this._exists(checkpointPath)) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: `checkpoint-missing: no checkpoint at ${checkpointPath} — run downloadWeights() first`,\n\t\t\t\trawOutput: \"\",\n\t\t\t};\n\t\t}\n\n\t\tconst argv = [\n\t\t\t\"run\",\n\t\t\t\"--checkpoint\",\n\t\t\tcheckpointPath,\n\t\t\t\"--query\",\n\t\t\trequest.query,\n\t\t\t\"--tools\",\n\t\t\tJSON.stringify(request.tools),\n\t\t];\n\t\tconst result = await this._runCommand(entryCommand, argv);\n\t\tconst rawOutput = [result.stdout, result.stderr].filter(Boolean).join(\"\\n\");\n\t\tif (!result.ok) {\n\t\t\treturn { ok: false, error: `run-fail: ${tailLines(result.error ?? result.stderr)}`, rawOutput };\n\t\t}\n\t\treturn this._parseFunctionCall(result.stdout, rawOutput);\n\t}\n\n\t/**\n\t * Strictly parse a real `needle run` stdout capture:\n\t * ```\n\t * Loading checkpoint: <path>\n\t * Model parameters: <n>\n\t *\n\t * Query: <query>\n\t * Tools: <tools, truncated to 80 chars>...\n\t *\n\t * <tool_call>[{\"name\":\"...\",\"arguments\":{...}}]\n\t * ```\n\t * (diagnostic lines above the marker vary — e.g. a one-time \"Downloading pretrained tokenizer...\"\n\t * line on a cold cache — so parsing locates the `<tool_call>` marker rather than assuming a fixed\n\t * line count/position). Unparseable output is always an error carrying the raw text, never a\n\t * guessed result.\n\t */\n\tprivate _parseFunctionCall(stdout: string, rawOutput: string): NeedleFunctionCallResult {\n\t\tconst markerIndex = stdout.indexOf(NEEDLE_TOOL_CALL_MARKER);\n\t\tif (markerIndex < 0) {\n\t\t\treturn { ok: false, error: `unparseable-output: no ${NEEDLE_TOOL_CALL_MARKER} marker in stdout`, rawOutput };\n\t\t}\n\t\tconst jsonText = stdout.slice(markerIndex + NEEDLE_TOOL_CALL_MARKER.length).trim();\n\t\tlet parsed: unknown;\n\t\ttry {\n\t\t\tparsed = JSON.parse(jsonText);\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: `unparseable-output: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t\trawOutput,\n\t\t\t};\n\t\t}\n\t\tif (!Array.isArray(parsed) || parsed.length === 0) {\n\t\t\treturn { ok: false, error: \"unparseable-output: expected a non-empty tool-call array\", rawOutput };\n\t\t}\n\t\tconst first = parsed[0] as { name?: unknown; arguments?: unknown };\n\t\tif (\n\t\t\ttypeof first !== \"object\" ||\n\t\t\tfirst === null ||\n\t\t\ttypeof first.name !== \"string\" ||\n\t\t\ttypeof first.arguments !== \"object\" ||\n\t\t\tfirst.arguments === null\n\t\t) {\n\t\t\treturn { ok: false, error: \"unparseable-output: tool call missing name/arguments\", rawOutput };\n\t\t}\n\t\treturn { ok: true, call: { name: first.name, arguments: first.arguments as Record<string, unknown> } };\n\t}\n\n\t/** Canned get_weather query/tool through {@link runFunctionCall}; the wiring task uses this as the\n\t * post-install verification step. */\n\tasync smokeTest(onProgress?: (status: string) => void): Promise<NeedleSmokeTestResult> {\n\t\tonProgress?.(\"Running needle smoke test…\");\n\t\tconst startedAt = Date.now();\n\t\tconst result = await this.runFunctionCall({ query: NEEDLE_SMOKE_QUERY, tools: NEEDLE_SMOKE_TOOLS });\n\t\tconst latencyMs = Date.now() - startedAt;\n\t\tif (!result.ok) {\n\t\t\tonProgress?.(`Smoke test failed: ${result.error}`);\n\t\t\treturn { ok: false, latencyMs, error: result.error };\n\t\t}\n\t\tonProgress?.(`Smoke test ok (${latencyMs}ms): ${result.call.name}`);\n\t\treturn { ok: true, latencyMs, call: result.call };\n\t}\n\n\t/**\n\t * No-op: every needle invocation here is a single-shot `runCommand` call that spawns, waits for\n\t * exit, and returns — there is no persistent/detached child process this runtime owns between\n\t * calls for dispose() to track or kill (contrast OllamaRuntime/PrismLlamaCppRuntime's long-lived\n\t * `serve()` child).\n\t */\n\tdispose(): void {}\n}\n"]}
|