pi-crew 0.9.53 → 0.9.55
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 +45 -0
- package/package.json +6 -3
- package/scripts/clean-strip-types.mjs +60 -0
- package/src/adapters/claude-adapter.ts +23 -0
- package/src/adapters/codex-adapter.ts +21 -0
- package/src/adapters/cursor-adapter.ts +17 -0
- package/src/adapters/export-util.ts +143 -0
- package/src/adapters/index.ts +15 -0
- package/src/adapters/registry.ts +18 -0
- package/src/adapters/types.ts +23 -0
- package/src/agents/agent-config.ts +194 -0
- package/src/agents/agent-search.ts +98 -0
- package/src/agents/agent-serializer.ts +38 -0
- package/src/agents/discover-agents.ts +623 -0
- package/src/benchmark/benchmark-runner.ts +313 -0
- package/src/benchmark/feedback-loop.ts +73 -0
- package/src/config/config.ts +1270 -0
- package/src/config/defaults.ts +193 -0
- package/src/config/drift-detector.ts +290 -0
- package/src/config/markers.ts +330 -0
- package/src/config/resilient-parser.ts +117 -0
- package/src/config/role-tools.ts +118 -0
- package/src/config/suggestions.ts +75 -0
- package/src/config/types.ts +280 -0
- package/src/errors.ts +194 -0
- package/src/extension/action-suggestions.ts +117 -0
- package/src/extension/async-notifier.ts +179 -0
- package/src/extension/autonomous-policy.ts +209 -0
- package/src/extension/command-completions.ts +120 -0
- package/src/extension/context-status-injection.ts +185 -0
- package/src/extension/crew-autocomplete.ts +131 -0
- package/src/extension/crew-cleanup.ts +168 -0
- package/src/extension/crew-input-router.ts +93 -0
- package/src/extension/crew-shortcuts.ts +87 -0
- package/src/extension/crew-vibes/cat-frames.ts +18 -0
- package/src/extension/crew-vibes/config.ts +195 -0
- package/src/extension/crew-vibes/figures.ts +101 -0
- package/src/extension/crew-vibes/font-detect.ts +71 -0
- package/src/extension/crew-vibes/footer.ts +292 -0
- package/src/extension/crew-vibes/index.ts +432 -0
- package/src/extension/crew-vibes/provider-usage.ts +396 -0
- package/src/extension/crew-vibes/render.ts +206 -0
- package/src/extension/crew-vibes/speed.ts +286 -0
- package/src/extension/cross-extension-rpc.ts +350 -0
- package/src/extension/help.ts +62 -0
- package/src/extension/import-index.ts +74 -0
- package/src/extension/knowledge-injection.ts +402 -0
- package/src/extension/management.ts +571 -0
- package/src/extension/message-renderers.ts +108 -0
- package/src/extension/notification-router.ts +152 -0
- package/src/extension/notification-sink.ts +54 -0
- package/src/extension/pi-api.ts +56 -0
- package/src/extension/plan-orchestrate.ts +302 -0
- package/src/extension/project-init.ts +170 -0
- package/src/extension/register.ts +113 -0
- package/src/extension/registration/artifact-cleanup.ts +20 -0
- package/src/extension/registration/command-registration.ts +58 -0
- package/src/extension/registration/command-utils.ts +58 -0
- package/src/extension/registration/commands.ts +1228 -0
- package/src/extension/registration/compaction-guard.ts +333 -0
- package/src/extension/registration/context-builder.ts +135 -0
- package/src/extension/registration/crash-recovery-cache.ts +54 -0
- package/src/extension/registration/foreground-run-controller.ts +238 -0
- package/src/extension/registration/hook-registration.ts +119 -0
- package/src/extension/registration/lazy-configurers.ts +124 -0
- package/src/extension/registration/lifecycle-handlers.ts +923 -0
- package/src/extension/registration/lifecycle.ts +259 -0
- package/src/extension/registration/observability.ts +322 -0
- package/src/extension/registration/registration-types.ts +158 -0
- package/src/extension/registration/runtime-cleanup.ts +230 -0
- package/src/extension/registration/subagent-helpers.ts +125 -0
- package/src/extension/registration/subagent-manager-setup.ts +330 -0
- package/src/extension/registration/subagent-tools.ts +456 -0
- package/src/extension/registration/team-tool.ts +225 -0
- package/src/extension/registration/tool-registration.ts +50 -0
- package/src/extension/registration/ui.ts +172 -0
- package/src/extension/registration/viewers.ts +121 -0
- package/src/extension/registration/wire-cross-extension.ts +37 -0
- package/src/extension/result-watcher.ts +139 -0
- package/src/extension/rpc-hmac.ts +256 -0
- package/src/extension/run-bundle-schema.ts +104 -0
- package/src/extension/run-export.ts +97 -0
- package/src/extension/run-import.ts +168 -0
- package/src/extension/run-index.ts +131 -0
- package/src/extension/run-maintenance.ts +190 -0
- package/src/extension/session-summary.ts +18 -0
- package/src/extension/team-manager-command.ts +153 -0
- package/src/extension/team-onboard.ts +180 -0
- package/src/extension/team-recommendation.ts +288 -0
- package/src/extension/team-tool/anchor.ts +172 -0
- package/src/extension/team-tool/api.ts +1244 -0
- package/src/extension/team-tool/auto-summarize.ts +142 -0
- package/src/extension/team-tool/cache-control.ts +19 -0
- package/src/extension/team-tool/cancel.ts +337 -0
- package/src/extension/team-tool/chain-dispatch.ts +95 -0
- package/src/extension/team-tool/chain-executor.ts +379 -0
- package/src/extension/team-tool/config-patch.ts +50 -0
- package/src/extension/team-tool/context.ts +149 -0
- package/src/extension/team-tool/destructive-gate.ts +52 -0
- package/src/extension/team-tool/dispatch/automate.ts +80 -0
- package/src/extension/team-tool/dispatch/control.ts +42 -0
- package/src/extension/team-tool/dispatch/index.ts +95 -0
- package/src/extension/team-tool/dispatch/manage.ts +161 -0
- package/src/extension/team-tool/dispatch/run.ts +53 -0
- package/src/extension/team-tool/dispatch/status.ts +187 -0
- package/src/extension/team-tool/doctor.ts +431 -0
- package/src/extension/team-tool/explain.ts +282 -0
- package/src/extension/team-tool/failure-patterns.ts +118 -0
- package/src/extension/team-tool/goal-wrap.ts +330 -0
- package/src/extension/team-tool/goal.ts +556 -0
- package/src/extension/team-tool/handle-schedule.ts +332 -0
- package/src/extension/team-tool/handle-settings.ts +520 -0
- package/src/extension/team-tool/health-monitor.ts +481 -0
- package/src/extension/team-tool/inspect.ts +88 -0
- package/src/extension/team-tool/intent-policy.ts +45 -0
- package/src/extension/team-tool/lifecycle-actions.ts +615 -0
- package/src/extension/team-tool/orchestrate.ts +98 -0
- package/src/extension/team-tool/parallel-dispatch.ts +183 -0
- package/src/extension/team-tool/plan.ts +50 -0
- package/src/extension/team-tool/respond.ts +150 -0
- package/src/extension/team-tool/run-deadline.ts +66 -0
- package/src/extension/team-tool/run-not-found.ts +49 -0
- package/src/extension/team-tool/run.ts +1051 -0
- package/src/extension/team-tool/status.ts +260 -0
- package/src/extension/team-tool/workflow-manage.ts +261 -0
- package/src/extension/team-tool-types.ts +26 -0
- package/src/extension/team-tool.ts +750 -0
- package/src/extension/tool-result.ts +16 -0
- package/src/extension/validate-resources.ts +108 -0
- package/src/hooks/registry.ts +200 -0
- package/src/hooks/types.ts +69 -0
- package/src/i18n.ts +212 -0
- package/src/observability/correlation.ts +50 -0
- package/src/observability/event-bus.ts +86 -0
- package/src/observability/event-to-metric.ts +170 -0
- package/src/observability/exporters/adapter.ts +27 -0
- package/src/observability/exporters/otlp-exporter.ts +356 -0
- package/src/observability/exporters/prometheus-exporter.ts +54 -0
- package/src/observability/metric-registry.ts +100 -0
- package/src/observability/metric-retention.ts +64 -0
- package/src/observability/metric-sink.ts +99 -0
- package/src/observability/metrics-primitives.ts +219 -0
- package/src/plugins/plugin-define.ts +6 -0
- package/src/plugins/plugin-registry.ts +32 -0
- package/src/plugins/plugins/index.ts +3 -0
- package/src/plugins/plugins/nextjs.ts +19 -0
- package/src/plugins/plugins/vite.ts +10 -0
- package/src/plugins/plugins/vitest.ts +9 -0
- package/src/prompt/prompt-runtime.ts +380 -0
- package/src/runtime/adaptive-plan.ts +563 -0
- package/src/runtime/agent-control.ts +215 -0
- package/src/runtime/agent-memory.ts +81 -0
- package/src/runtime/agent-observability.ts +122 -0
- package/src/runtime/anchor-manager.ts +475 -0
- package/src/runtime/async-marker.ts +32 -0
- package/src/runtime/async-runner.ts +381 -0
- package/src/runtime/attention-events.ts +28 -0
- package/src/runtime/auto-summarize.ts +344 -0
- package/src/runtime/background-runner.ts +840 -0
- package/src/runtime/batch-barrier.ts +147 -0
- package/src/runtime/broker-issuer.ts +39 -0
- package/src/runtime/cancellation-token.ts +99 -0
- package/src/runtime/cancellation.ts +99 -0
- package/src/runtime/capability-inventory.ts +137 -0
- package/src/runtime/chain-parser.ts +237 -0
- package/src/runtime/chain-runner.ts +606 -0
- package/src/runtime/checkpoint.ts +291 -0
- package/src/runtime/child-pi-constants.ts +42 -0
- package/src/runtime/child-pi-kill.ts +180 -0
- package/src/runtime/child-pi-pool.ts +68 -0
- package/src/runtime/child-pi-spawn.ts +287 -0
- package/src/runtime/child-pi-steering.ts +128 -0
- package/src/runtime/child-pi-streams.ts +296 -0
- package/src/runtime/child-pi-transcript.ts +169 -0
- package/src/runtime/child-pi.ts +1105 -0
- package/src/runtime/coalesce-tasks.ts +268 -0
- package/src/runtime/code-summary.ts +292 -0
- package/src/runtime/command-trace.ts +105 -0
- package/src/runtime/compact-pipeline.ts +56 -0
- package/src/runtime/compact-stages/ansi-strip-stage.ts +25 -0
- package/src/runtime/compact-stages/blank-collapse-stage.ts +31 -0
- package/src/runtime/compact-stages/deduplicate-stage.ts +34 -0
- package/src/runtime/compact-stages/head-snap-stage.ts +57 -0
- package/src/runtime/compact-stages/index.ts +23 -0
- package/src/runtime/compact-stages/tail-capture-stage.ts +77 -0
- package/src/runtime/compact-stages/truncation-stage.ts +74 -0
- package/src/runtime/compaction-summary.ts +278 -0
- package/src/runtime/completion-guard.ts +207 -0
- package/src/runtime/concurrency.ts +58 -0
- package/src/runtime/crash-classification.ts +229 -0
- package/src/runtime/crash-recovery.ts +594 -0
- package/src/runtime/crew-agent-records.ts +595 -0
- package/src/runtime/crew-agent-runtime.ts +62 -0
- package/src/runtime/crew-broker-child.ts +88 -0
- package/src/runtime/crew-broker-client.ts +673 -0
- package/src/runtime/crew-broker-tokens.ts +159 -0
- package/src/runtime/crew-broker.ts +1304 -0
- package/src/runtime/crew-hooks.ts +227 -0
- package/src/runtime/cross-extension-rpc.ts +143 -0
- package/src/runtime/custom-tools/irc-tool.ts +282 -0
- package/src/runtime/custom-tools/submit-result-tool.ts +102 -0
- package/src/runtime/deadletter.ts +47 -0
- package/src/runtime/delivery-coordinator.ts +211 -0
- package/src/runtime/delta-conflict.ts +348 -0
- package/src/runtime/deterministic-ast.ts +161 -0
- package/src/runtime/diagnostic-export.ts +171 -0
- package/src/runtime/direct-run.ts +45 -0
- package/src/runtime/dwf-state-store.ts +102 -0
- package/src/runtime/dynamic-workflow-context.ts +1013 -0
- package/src/runtime/dynamic-workflow-runner.ts +325 -0
- package/src/runtime/effectiveness.ts +90 -0
- package/src/runtime/errors/crew-errors.ts +162 -0
- package/src/runtime/event-stream-bridge.ts +98 -0
- package/src/runtime/foreground-control.ts +193 -0
- package/src/runtime/foreground-watchdog.ts +132 -0
- package/src/runtime/global-worker-cap.ts +96 -0
- package/src/runtime/goal-achievement.ts +148 -0
- package/src/runtime/goal-evaluator.ts +365 -0
- package/src/runtime/goal-loop-runner.ts +825 -0
- package/src/runtime/goal-state-store.ts +219 -0
- package/src/runtime/green-contract.ts +55 -0
- package/src/runtime/group-join.ts +267 -0
- package/src/runtime/handoff-manager.ts +598 -0
- package/src/runtime/heartbeat-gradient.ts +36 -0
- package/src/runtime/heartbeat-watcher.ts +239 -0
- package/src/runtime/hidden-handoff.ts +407 -0
- package/src/runtime/important-line-classifier.ts +140 -0
- package/src/runtime/intercom-bridge.ts +187 -0
- package/src/runtime/iteration-hooks.ts +305 -0
- package/src/runtime/live-agent-control.ts +136 -0
- package/src/runtime/live-agent-manager.ts +704 -0
- package/src/runtime/live-control-realtime.ts +56 -0
- package/src/runtime/live-extension-bridge.ts +148 -0
- package/src/runtime/live-irc.ts +97 -0
- package/src/runtime/live-session-health.ts +108 -0
- package/src/runtime/live-session-runtime.ts +1146 -0
- package/src/runtime/loop-gates.ts +128 -0
- package/src/runtime/manifest-cache.ts +375 -0
- package/src/runtime/mcp-proxy.ts +105 -0
- package/src/runtime/metric-parser.ts +36 -0
- package/src/runtime/model-fallback.ts +409 -0
- package/src/runtime/model-resolver.ts +126 -0
- package/src/runtime/model-scope.ts +158 -0
- package/src/runtime/orphan-worker-registry.ts +465 -0
- package/src/runtime/output-validator.ts +202 -0
- package/src/runtime/overflow-recovery.ts +206 -0
- package/src/runtime/parallel-research.ts +68 -0
- package/src/runtime/parallel-utils.ts +160 -0
- package/src/runtime/parent-guard.ts +134 -0
- package/src/runtime/path-overlap.ts +150 -0
- package/src/runtime/peer-dep.ts +292 -0
- package/src/runtime/per-write-validator.ts +181 -0
- package/src/runtime/phase-progress.ts +217 -0
- package/src/runtime/phase-tracker.ts +385 -0
- package/src/runtime/pi-args.ts +655 -0
- package/src/runtime/pi-json-output.ts +170 -0
- package/src/runtime/pi-spawn.ts +273 -0
- package/src/runtime/pipeline-runner.ts +523 -0
- package/src/runtime/plan-templates.ts +201 -0
- package/src/runtime/policy-engine.ts +115 -0
- package/src/runtime/post-checks.ts +142 -0
- package/src/runtime/post-exit-stdio-guard.ts +109 -0
- package/src/runtime/process-lifecycle.ts +491 -0
- package/src/runtime/process-status.ts +154 -0
- package/src/runtime/progress-event-coalescer.ts +44 -0
- package/src/runtime/progress-tracker.ts +124 -0
- package/src/runtime/prose-compressor.ts +162 -0
- package/src/runtime/recovery-recipes.ts +203 -0
- package/src/runtime/replace.ts +570 -0
- package/src/runtime/resilient-edit.ts +153 -0
- package/src/runtime/result-extractor.ts +217 -0
- package/src/runtime/retry-executor.ts +109 -0
- package/src/runtime/retry-runner.ts +336 -0
- package/src/runtime/role-permission.ts +59 -0
- package/src/runtime/run-coalesced-task-group.ts +308 -0
- package/src/runtime/run-drift.ts +219 -0
- package/src/runtime/run-tracker.ts +116 -0
- package/src/runtime/run-worker.ts +78 -0
- package/src/runtime/runtime-policy.ts +29 -0
- package/src/runtime/runtime-resolver.ts +172 -0
- package/src/runtime/runtime-warmup.ts +160 -0
- package/src/runtime/scheduler.ts +353 -0
- package/src/runtime/semaphore.ts +141 -0
- package/src/runtime/sensitive-paths.ts +93 -0
- package/src/runtime/session-resources.ts +25 -0
- package/src/runtime/session-snapshot.ts +59 -0
- package/src/runtime/session-usage.ts +79 -0
- package/src/runtime/settings-store.ts +155 -0
- package/src/runtime/sidechain-output.ts +35 -0
- package/src/runtime/single-agent-compose.ts +84 -0
- package/src/runtime/skill-effectiveness.ts +524 -0
- package/src/runtime/skill-instructions.ts +394 -0
- package/src/runtime/stale-reconciler.ts +680 -0
- package/src/runtime/stream-preview.ts +184 -0
- package/src/runtime/streaming-output.ts +50 -0
- package/src/runtime/subagent-manager.ts +561 -0
- package/src/runtime/subprocess-tool-registry.ts +70 -0
- package/src/runtime/supervisor-contact.ts +64 -0
- package/src/runtime/task-display.ts +52 -0
- package/src/runtime/task-graph-scheduler.ts +227 -0
- package/src/runtime/task-graph.ts +290 -0
- package/src/runtime/task-health.ts +83 -0
- package/src/runtime/task-id.ts +161 -0
- package/src/runtime/task-output-context.ts +602 -0
- package/src/runtime/task-packet.ts +268 -0
- package/src/runtime/task-quality.ts +199 -0
- package/src/runtime/task-runner/capabilities.ts +78 -0
- package/src/runtime/task-runner/child-executor.ts +790 -0
- package/src/runtime/task-runner/context-retrieval.ts +163 -0
- package/src/runtime/task-runner/live-executor.ts +227 -0
- package/src/runtime/task-runner/output-splitter.ts +152 -0
- package/src/runtime/task-runner/post-execution.ts +480 -0
- package/src/runtime/task-runner/pre-execution.ts +365 -0
- package/src/runtime/task-runner/progress.ts +157 -0
- package/src/runtime/task-runner/prompt-builder.ts +278 -0
- package/src/runtime/task-runner/prompt-pipeline.ts +88 -0
- package/src/runtime/task-runner/result-utils.ts +16 -0
- package/src/runtime/task-runner/retrieval-orchestrator.ts +310 -0
- package/src/runtime/task-runner/run-projection.ts +125 -0
- package/src/runtime/task-runner/scaffold-executor.ts +25 -0
- package/src/runtime/task-runner/state-helpers.ts +176 -0
- package/src/runtime/task-runner/tail-read.ts +34 -0
- package/src/runtime/task-runner.ts +216 -0
- package/src/runtime/team-runner-artifacts.ts +13 -0
- package/src/runtime/team-runner.ts +2396 -0
- package/src/runtime/tool-output-pruner.ts +333 -0
- package/src/runtime/tool-progress.ts +278 -0
- package/src/runtime/usage-tracker.ts +73 -0
- package/src/runtime/verification-gates.ts +579 -0
- package/src/runtime/verification-integrity.ts +108 -0
- package/src/runtime/verification-worktree.ts +186 -0
- package/src/runtime/worker-heartbeat.ts +25 -0
- package/src/runtime/worker-startup.ts +83 -0
- package/src/runtime/workflow-state.ts +195 -0
- package/src/runtime/workspace-lock.ts +443 -0
- package/src/runtime/workspace-tree.ts +312 -0
- package/src/runtime/yield-handler.ts +224 -0
- package/src/runtime/zombie-scanner.ts +298 -0
- package/src/schema/config-schema.ts +327 -0
- package/src/schema/team-tool-schema.ts +492 -0
- package/src/schema/validation-types.ts +159 -0
- package/src/skills/discover-skills.ts +203 -0
- package/src/skills/skill-templates.ts +456 -0
- package/src/skills/validate.ts +285 -0
- package/src/state/active-run-registry.ts +439 -0
- package/src/state/artifact-store.ts +176 -0
- package/src/state/atomic-write.ts +970 -0
- package/src/state/blob-store.ts +308 -0
- package/src/state/contracts.ts +160 -0
- package/src/state/crew-init.ts +269 -0
- package/src/state/decision-ledger.ts +372 -0
- package/src/state/event-log-rotation.ts +399 -0
- package/src/state/event-log.ts +1465 -0
- package/src/state/event-reconstructor.ts +229 -0
- package/src/state/gitignore-manager.ts +46 -0
- package/src/state/health-store.ts +79 -0
- package/src/state/hook-instinct-bridge.ts +94 -0
- package/src/state/hook-integrations.ts +51 -0
- package/src/state/instinct-store.ts +275 -0
- package/src/state/jsonl-writer.ts +107 -0
- package/src/state/locks.ts +562 -0
- package/src/state/mailbox.ts +916 -0
- package/src/state/observation-store.ts +176 -0
- package/src/state/run-cache.ts +193 -0
- package/src/state/run-graph.ts +183 -0
- package/src/state/run-metrics.ts +165 -0
- package/src/state/schedule.ts +166 -0
- package/src/state/session-state-map.ts +51 -0
- package/src/state/state-store.ts +973 -0
- package/src/state/task-claims.ts +61 -0
- package/src/state/tiered-eval.ts +480 -0
- package/src/state/types-eval.ts +58 -0
- package/src/state/types.ts +447 -0
- package/src/state/usage.ts +138 -0
- package/src/state/worker-atomic-writer.ts +198 -0
- package/src/subagents/async-entry.ts +1 -0
- package/src/subagents/index.ts +3 -0
- package/src/subagents/live/control.ts +1 -0
- package/src/subagents/live/manager.ts +1 -0
- package/src/subagents/live/realtime.ts +1 -0
- package/src/subagents/live/session-runtime.ts +1 -0
- package/src/subagents/manager.ts +1 -0
- package/src/subagents/spawn.ts +1 -0
- package/src/teams/discover-teams.ts +187 -0
- package/src/teams/team-config.ts +27 -0
- package/src/teams/team-serializer.ts +38 -0
- package/src/tools/safe-bash-extension.ts +54 -0
- package/src/tools/safe-bash.ts +505 -0
- package/src/types/diff.d.ts +18 -0
- package/src/types/new-api-types.ts +35 -0
- package/src/ui/agent-management-overlay.ts +160 -0
- package/src/ui/card-colors.ts +139 -0
- package/src/ui/crew-footer.ts +102 -0
- package/src/ui/crew-select-list.ts +112 -0
- package/src/ui/dashboard-panes/agents-pane.ts +164 -0
- package/src/ui/dashboard-panes/cancellation-pane.ts +66 -0
- package/src/ui/dashboard-panes/capability-pane.ts +77 -0
- package/src/ui/dashboard-panes/health-pane.ts +31 -0
- package/src/ui/dashboard-panes/mailbox-pane.ts +35 -0
- package/src/ui/dashboard-panes/metrics-pane.ts +37 -0
- package/src/ui/dashboard-panes/progress-pane.ts +38 -0
- package/src/ui/dashboard-panes/transcript-pane.ts +10 -0
- package/src/ui/deploy-bundled-themes.ts +71 -0
- package/src/ui/dwf-phase-display.ts +151 -0
- package/src/ui/dynamic-border.ts +35 -0
- package/src/ui/format-helpers.ts +31 -0
- package/src/ui/heartbeat-aggregator.ts +74 -0
- package/src/ui/key-utils.ts +42 -0
- package/src/ui/keybinding-map.ts +248 -0
- package/src/ui/layout-primitives.ts +106 -0
- package/src/ui/live-conversation-overlay.ts +183 -0
- package/src/ui/live-duration.ts +55 -0
- package/src/ui/live-run-sidebar.ts +293 -0
- package/src/ui/loaders.ts +6 -0
- package/src/ui/mascot.ts +449 -0
- package/src/ui/overlays/agent-picker-overlay.ts +66 -0
- package/src/ui/overlays/confirm-overlay.ts +60 -0
- package/src/ui/overlays/help-overlay.ts +177 -0
- package/src/ui/overlays/mailbox-compose-overlay.ts +181 -0
- package/src/ui/overlays/mailbox-compose-preview.ts +78 -0
- package/src/ui/overlays/mailbox-detail-overlay.ts +155 -0
- package/src/ui/pi-ui-compat.ts +68 -0
- package/src/ui/powerbar-publisher.ts +459 -0
- package/src/ui/render-coalescer.ts +101 -0
- package/src/ui/render-diff.ts +160 -0
- package/src/ui/render-scheduler.ts +278 -0
- package/src/ui/run-action-dispatcher.ts +182 -0
- package/src/ui/run-dashboard.ts +910 -0
- package/src/ui/run-event-bus.ts +381 -0
- package/src/ui/run-snapshot-cache.ts +1057 -0
- package/src/ui/settings-overlay.ts +1067 -0
- package/src/ui/shared-overlay-scheduler.ts +96 -0
- package/src/ui/snapshot-types.ts +90 -0
- package/src/ui/spinner.ts +17 -0
- package/src/ui/status-colors.ts +143 -0
- package/src/ui/syntax-highlight.ts +136 -0
- package/src/ui/terminal-status.ts +269 -0
- package/src/ui/theme-adapter.ts +271 -0
- package/src/ui/theme-discovery.ts +199 -0
- package/src/ui/tool-progress-formatter.ts +93 -0
- package/src/ui/tool-renderers/brief-mode.ts +225 -0
- package/src/ui/tool-renderers/index.ts +726 -0
- package/src/ui/transcript-cache.ts +128 -0
- package/src/ui/transcript-entries.ts +256 -0
- package/src/ui/transcript-viewer.ts +424 -0
- package/src/ui/widget/index.ts +418 -0
- package/src/ui/widget/widget-formatters.ts +182 -0
- package/src/ui/widget/widget-model.ts +110 -0
- package/src/ui/widget/widget-renderer.ts +182 -0
- package/src/ui/widget/widget-types.ts +36 -0
- package/src/utils/bm25-search.ts +235 -0
- package/src/utils/completion-dedupe.ts +63 -0
- package/src/utils/conflict-detect.ts +668 -0
- package/src/utils/env-allowlist.ts +30 -0
- package/src/utils/env-filter.ts +155 -0
- package/src/utils/file-coalescer.ts +98 -0
- package/src/utils/fingerprint.ts +180 -0
- package/src/utils/frontmatter.ts +70 -0
- package/src/utils/fs-watch.ts +37 -0
- package/src/utils/gh-protocol.ts +556 -0
- package/src/utils/git.ts +260 -0
- package/src/utils/guards.ts +107 -0
- package/src/utils/ids.ts +24 -0
- package/src/utils/incremental-reader.ts +220 -0
- package/src/utils/internal-error.ts +10 -0
- package/src/utils/names.ts +36 -0
- package/src/utils/ndjson.ts +115 -0
- package/src/utils/paths.ts +227 -0
- package/src/utils/project-detector.ts +160 -0
- package/src/utils/redaction.ts +370 -0
- package/src/utils/resolve-shell.ts +36 -0
- package/src/utils/run-watcher-registry.ts +152 -0
- package/src/utils/safe-paths.ts +393 -0
- package/src/utils/scan-cache.ts +144 -0
- package/src/utils/session-utils.ts +110 -0
- package/src/utils/sleep.ts +54 -0
- package/src/utils/socket-path.ts +164 -0
- package/src/utils/sse-parser.ts +131 -0
- package/src/utils/task-name-generator.ts +337 -0
- package/src/utils/timings.ts +33 -0
- package/src/utils/token-counter.ts +200 -0
- package/src/utils/visual.ts +207 -0
- package/src/workflows/cost-estimator.ts +34 -0
- package/src/workflows/discover-workflows.ts +308 -0
- package/src/workflows/intermediate-store.ts +166 -0
- package/src/workflows/preflight-validator.ts +168 -0
- package/src/workflows/topology-analyzer.ts +203 -0
- package/src/workflows/validate-workflow.ts +50 -0
- package/src/workflows/workflow-config.ts +75 -0
- package/src/workflows/workflow-serializer.ts +32 -0
- package/src/worktree/branch-freshness.ts +112 -0
- package/src/worktree/cleanup.ts +341 -0
- package/src/worktree/worktree-manager.ts +1204 -0
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import type { SpeedConfig } from "./config.ts";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Token-speed engine ported from pi-speeed (MIT, attribution in NOTICE.md).
|
|
5
|
+
* Tracks output tok/s with a sliding window and suppresses unreliable
|
|
6
|
+
* burst-only readings. No stats/font handling — just the live + session avg.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export type TokenEvent = { time: number; tokens: number };
|
|
10
|
+
|
|
11
|
+
const COMPACTION_THRESHOLD = 5000;
|
|
12
|
+
|
|
13
|
+
export type EngineConfig = Pick<SpeedConfig, "slidingWindowMs" | "minReliableDurationMs" | "maxDisplayTokS">;
|
|
14
|
+
|
|
15
|
+
export function estimateTokensFromDelta(text: string): number {
|
|
16
|
+
if (!text) return 0;
|
|
17
|
+
const matches = text.match(/\w+|[^\s\w]/g);
|
|
18
|
+
return matches ? matches.length : 0;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export class TokenSpeedEngine {
|
|
22
|
+
private _isStreaming = false;
|
|
23
|
+
private _tokenCount = 0;
|
|
24
|
+
private _startTime = 0;
|
|
25
|
+
private _endTime = 0;
|
|
26
|
+
private _events: TokenEvent[] = [];
|
|
27
|
+
private _windowStartIndex = 0;
|
|
28
|
+
private _lastStableTokS = 0;
|
|
29
|
+
private _lastUsageOutput = 0;
|
|
30
|
+
private _config: EngineConfig;
|
|
31
|
+
|
|
32
|
+
constructor(config: EngineConfig) {
|
|
33
|
+
this._config = config;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
updateConfig(config: EngineConfig): void {
|
|
37
|
+
this._config = config;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
get isStreaming(): boolean {
|
|
41
|
+
return this._isStreaming;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
get tokenCount(): number {
|
|
45
|
+
return this._tokenCount;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
get elapsedMs(): number {
|
|
49
|
+
if (this._startTime === 0) return 0;
|
|
50
|
+
return this._isStreaming ? Date.now() - this._startTime : this._endTime - this._startTime;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
get avgTokS(): number {
|
|
54
|
+
const elapsedSec = this.elapsedMs / 1000;
|
|
55
|
+
if (elapsedSec <= 0) return 0;
|
|
56
|
+
return this._tokenCount / elapsedSec;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
sanitizeTokS(value: number | null, durationMs = this.elapsedMs): number | null {
|
|
60
|
+
if (value === null || !Number.isFinite(value) || value <= 0) return null;
|
|
61
|
+
if (durationMs < this._config.minReliableDurationMs) return null;
|
|
62
|
+
if (value > this._config.maxDisplayTokS) return null;
|
|
63
|
+
return value;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
get tokS(): number {
|
|
67
|
+
const candidate = this.rawTokS;
|
|
68
|
+
const stable = this.sanitizeTokS(candidate);
|
|
69
|
+
if (stable !== null) this._lastStableTokS = stable;
|
|
70
|
+
return this._lastStableTokS;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
get rawTokS(): number {
|
|
74
|
+
if (this.elapsedMs < this._config.slidingWindowMs) return this.avgTokS;
|
|
75
|
+
if (!this._isStreaming) return this.avgTokS;
|
|
76
|
+
|
|
77
|
+
const now = Date.now();
|
|
78
|
+
const windowStart = now - this._config.slidingWindowMs;
|
|
79
|
+
|
|
80
|
+
while (this._windowStartIndex < this._events.length && this._events[this._windowStartIndex].time < windowStart) {
|
|
81
|
+
this._windowStartIndex++;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (this._windowStartIndex >= this._events.length) return this.avgTokS;
|
|
85
|
+
|
|
86
|
+
let windowTokenCount = 0;
|
|
87
|
+
for (let i = this._windowStartIndex; i < this._events.length; i++) {
|
|
88
|
+
windowTokenCount += this._events[i].tokens;
|
|
89
|
+
}
|
|
90
|
+
if (windowTokenCount === 0) return this.avgTokS;
|
|
91
|
+
|
|
92
|
+
const windowDuration = (now - this._events[this._windowStartIndex].time) / 1000;
|
|
93
|
+
if (windowDuration <= 0) return 0;
|
|
94
|
+
|
|
95
|
+
return windowTokenCount / windowDuration;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
start(): void {
|
|
99
|
+
this._tokenCount = 0;
|
|
100
|
+
this._isStreaming = true;
|
|
101
|
+
this._startTime = Date.now();
|
|
102
|
+
this._endTime = this._startTime;
|
|
103
|
+
this._events = [];
|
|
104
|
+
this._windowStartIndex = 0;
|
|
105
|
+
this._lastStableTokS = 0;
|
|
106
|
+
this._lastUsageOutput = 0;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
stop(): void {
|
|
110
|
+
this._isStreaming = false;
|
|
111
|
+
this._endTime = Date.now();
|
|
112
|
+
this._events = [];
|
|
113
|
+
this._windowStartIndex = 0;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
recordDelta(delta: string, usageOutput?: number): void {
|
|
117
|
+
if (!this._isStreaming) return;
|
|
118
|
+
if (usageOutput !== undefined && usageOutput > 0) {
|
|
119
|
+
// usageOutput may be cumulative across message_update events. Track the
|
|
120
|
+
// delta to avoid inflating _tokenCount by N× the true total.
|
|
121
|
+
const increment = usageOutput - this._lastUsageOutput;
|
|
122
|
+
this._lastUsageOutput = usageOutput;
|
|
123
|
+
this.recordTokens(Math.max(0, increment));
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
this.recordTokens(estimateTokensFromDelta(delta));
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
reconcileTotal(tokens: number): void {
|
|
130
|
+
if (tokens > 0) this._tokenCount = tokens;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
recordTokens(tokens: number): void {
|
|
134
|
+
if (!this._isStreaming || tokens <= 0) return;
|
|
135
|
+
this._tokenCount += tokens;
|
|
136
|
+
this._events.push({ time: Date.now(), tokens });
|
|
137
|
+
if (this._windowStartIndex >= COMPACTION_THRESHOLD) this.compact();
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
private compact(): void {
|
|
141
|
+
if (this._windowStartIndex === 0) return;
|
|
142
|
+
this._events = this._events.slice(this._windowStartIndex);
|
|
143
|
+
this._windowStartIndex = 0;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export type CompletedMessageSpeed = {
|
|
148
|
+
outputTokens: number;
|
|
149
|
+
durationMs: number;
|
|
150
|
+
tokS: number | null;
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
function isSuccessfulStop(stopReason: string | undefined): boolean {
|
|
154
|
+
return stopReason !== "error" && stopReason !== "aborted";
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export class SpeedTracker {
|
|
158
|
+
private readonly engine: TokenSpeedEngine;
|
|
159
|
+
private lastStableTokS: number | null = null;
|
|
160
|
+
private sessionOutputTokens = 0;
|
|
161
|
+
private sessionDurationMs = 0;
|
|
162
|
+
|
|
163
|
+
constructor(config: SpeedConfig) {
|
|
164
|
+
this.engine = new TokenSpeedEngine(config);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
updateConfig(config: SpeedConfig): void {
|
|
168
|
+
this.engine.updateConfig(config);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
get isStreaming(): boolean {
|
|
172
|
+
return this.engine.isStreaming;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
get lastTokS(): number | null {
|
|
176
|
+
return this.lastStableTokS;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
resetSession(): void {
|
|
180
|
+
this.sessionOutputTokens = 0;
|
|
181
|
+
this.sessionDurationMs = 0;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
startMessage(): void {
|
|
185
|
+
this.engine.start();
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
recordDelta(delta: string, usageOutput?: number): void {
|
|
189
|
+
this.engine.recordDelta(delta, usageOutput);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
stopMessage(): void {
|
|
193
|
+
if (this.engine.isStreaming) this.engine.stop();
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
liveTokS(): number | null {
|
|
197
|
+
const speed = this.engine.tokS;
|
|
198
|
+
return speed > 0 ? speed : this.lastStableTokS;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
sessionAvgTokS(): number | null {
|
|
202
|
+
return this.sessionDurationMs > 0 ? this.sessionOutputTokens / (this.sessionDurationMs / 1000) : null;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
finishMessage(outputTokens: number, stopReason: string | undefined): CompletedMessageSpeed | null {
|
|
206
|
+
if (!this.engine.isStreaming) return null;
|
|
207
|
+
|
|
208
|
+
this.engine.reconcileTotal(outputTokens);
|
|
209
|
+
const durationMs = this.engine.elapsedMs;
|
|
210
|
+
const tokens = this.engine.tokenCount;
|
|
211
|
+
const rawAvgTokS = durationMs > 0 ? tokens / (durationMs / 1000) : null;
|
|
212
|
+
const tokS = this.engine.sanitizeTokS(rawAvgTokS, durationMs);
|
|
213
|
+
this.lastStableTokS = tokS;
|
|
214
|
+
this.engine.stop();
|
|
215
|
+
|
|
216
|
+
if (tokS !== null && isSuccessfulStop(stopReason)) {
|
|
217
|
+
this.sessionOutputTokens += tokens;
|
|
218
|
+
this.sessionDurationMs += durationMs;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return { outputTokens: tokens, durationMs, tokS };
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export class SpeedAnimator {
|
|
226
|
+
private from: number | null = null;
|
|
227
|
+
private target: number | null = null;
|
|
228
|
+
private startedAt = 0;
|
|
229
|
+
private durationMs: number;
|
|
230
|
+
|
|
231
|
+
constructor(durationMs: number) {
|
|
232
|
+
this.durationMs = durationMs;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
updateDuration(durationMs: number): void {
|
|
236
|
+
this.durationMs = durationMs;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
reset(value: number | null = null, now = Date.now()): void {
|
|
240
|
+
this.from = value;
|
|
241
|
+
this.target = value;
|
|
242
|
+
this.startedAt = now;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
setTarget(target: number | null, now = Date.now()): number | null {
|
|
246
|
+
if (target === null) {
|
|
247
|
+
this.reset(null, now);
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const current = this.value(now);
|
|
252
|
+
if (current === null) {
|
|
253
|
+
this.reset(target, now);
|
|
254
|
+
return target;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (this.target !== null && Math.abs(target - this.target) < 0.05) return current;
|
|
258
|
+
|
|
259
|
+
this.from = current;
|
|
260
|
+
this.target = target;
|
|
261
|
+
this.startedAt = now;
|
|
262
|
+
return current;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
value(now = Date.now()): number | null {
|
|
266
|
+
if (this.target === null) return null;
|
|
267
|
+
if (this.from === null || this.durationMs <= 0) return this.target;
|
|
268
|
+
|
|
269
|
+
const progress = Math.max(0, Math.min(1, (now - this.startedAt) / this.durationMs));
|
|
270
|
+
if (progress >= 1) {
|
|
271
|
+
this.from = this.target;
|
|
272
|
+
return this.target;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return this.from + (this.target - this.from) * progress;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
isAnimating(now = Date.now()): boolean {
|
|
279
|
+
return (
|
|
280
|
+
this.target !== null &&
|
|
281
|
+
this.from !== null &&
|
|
282
|
+
Math.abs(this.target - this.from) >= 0.05 &&
|
|
283
|
+
now - this.startedAt < this.durationMs
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import type { TeamToolParamsValue } from "../schema/team-tool-schema.ts";
|
|
3
|
+
import { resolveRealContainedPath } from "../utils/safe-paths.ts";
|
|
4
|
+
import { withHmacVerification } from "./rpc-hmac.ts";
|
|
5
|
+
// Lazy-loaded to avoid pulling team-tool.ts (and its entire runtime chain) into module load.
|
|
6
|
+
import type { handleTeamTool as HandleTeamToolFn } from "./team-tool.ts";
|
|
7
|
+
|
|
8
|
+
let _cachedHandleTeamTool: typeof HandleTeamToolFn | undefined;
|
|
9
|
+
async function handleTeamTool(
|
|
10
|
+
params: Parameters<typeof HandleTeamToolFn>[0],
|
|
11
|
+
ctx: Parameters<typeof HandleTeamToolFn>[1],
|
|
12
|
+
): Promise<Awaited<ReturnType<typeof HandleTeamToolFn>>> {
|
|
13
|
+
if (!_cachedHandleTeamTool) {
|
|
14
|
+
// LAZY: avoid pulling team-tool.ts (and its entire runtime chain) into module load.
|
|
15
|
+
const mod = await import("./team-tool.ts");
|
|
16
|
+
_cachedHandleTeamTool = mod.handleTeamTool;
|
|
17
|
+
}
|
|
18
|
+
return _cachedHandleTeamTool(params, ctx);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
import { parseLiveControlRealtimeMessage, publishLiveControlRealtime } from "../runtime/live-control-realtime.ts";
|
|
22
|
+
|
|
23
|
+
export interface EventBusLike {
|
|
24
|
+
on(event: string, handler: (data: unknown) => void): (() => void) | void;
|
|
25
|
+
emit(event: string, data: unknown): void;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export type RpcReply<T = unknown> = { success: true; data?: T } | { success: false; error: string };
|
|
29
|
+
export const PI_CREW_RPC_VERSION = 1;
|
|
30
|
+
|
|
31
|
+
export interface PiCrewRpcHandle {
|
|
32
|
+
unsubscribe(): void;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function requestId(raw: unknown): string | undefined {
|
|
36
|
+
return raw && typeof raw === "object" && !Array.isArray(raw) && typeof (raw as { requestId?: unknown }).requestId === "string"
|
|
37
|
+
? (raw as { requestId: string }).requestId
|
|
38
|
+
: undefined;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function reply(events: EventBusLike, channel: string, id: string | undefined, payload: RpcReply): void {
|
|
42
|
+
if (!id) return;
|
|
43
|
+
// SECURITY: Validate requestId format to prevent channel injection.
|
|
44
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(id)) return;
|
|
45
|
+
events.emit(`${channel}:reply:${id}`, payload);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function textOf(result: Awaited<ReturnType<typeof handleTeamTool>>): string {
|
|
49
|
+
return result.content?.map((item) => (item.type === "text" ? item.text : "")).join("\n") ?? "";
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// SECURITY: Strictly enumerate allowed operations per RPC channel.
|
|
53
|
+
// Only read-only operations are permitted via RPC to prevent malicious extensions
|
|
54
|
+
// from mutating run state without user consent.
|
|
55
|
+
const RPC_ALLOWED_OPERATIONS = new Set([
|
|
56
|
+
// Read-only manifest/plan ops
|
|
57
|
+
"metrics-snapshot",
|
|
58
|
+
"inventory",
|
|
59
|
+
"read-manifest",
|
|
60
|
+
"list-tasks",
|
|
61
|
+
"read-task",
|
|
62
|
+
"read-events",
|
|
63
|
+
// Read-only agent ops
|
|
64
|
+
"list-agents",
|
|
65
|
+
"read-agent-status",
|
|
66
|
+
"read-agent-events",
|
|
67
|
+
"read-agent-transcript",
|
|
68
|
+
"read-agent-output",
|
|
69
|
+
"agent-dashboard",
|
|
70
|
+
// Mailbox read ops
|
|
71
|
+
"read-mailbox",
|
|
72
|
+
"read-delivery",
|
|
73
|
+
"read-heartbeat",
|
|
74
|
+
// No mutating ops (approve-plan, cancel-plan, steer-agent, stop-agent, etc.)
|
|
75
|
+
// — these require explicit intent confirmation and are NOT allowed via RPC.
|
|
76
|
+
]);
|
|
77
|
+
|
|
78
|
+
function isAllowedRpcOperation(operation: string): boolean {
|
|
79
|
+
return RPC_ALLOWED_OPERATIONS.has(operation);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// SECURITY (HIGH #4 fix): In-memory rate limiter for RPC run requests.
|
|
83
|
+
// Prevents any extension from spawning unlimited child processes.
|
|
84
|
+
const RPC_RATE_LIMIT_MAX = 5; // Max 5 RPC run requests...
|
|
85
|
+
const RPC_RATE_LIMIT_WINDOW_MS = 60_000; // ...per 60 seconds
|
|
86
|
+
const rpcRunTimestamps: number[] = [];
|
|
87
|
+
|
|
88
|
+
function checkRpcRateLimit(): { allowed: boolean; retryAfterMs?: number } {
|
|
89
|
+
const now = Date.now();
|
|
90
|
+
// Evict entries older than the window
|
|
91
|
+
const cutoff = now - RPC_RATE_LIMIT_WINDOW_MS;
|
|
92
|
+
while (rpcRunTimestamps.length > 0 && rpcRunTimestamps[0] < cutoff) {
|
|
93
|
+
rpcRunTimestamps.shift();
|
|
94
|
+
}
|
|
95
|
+
if (rpcRunTimestamps.length >= RPC_RATE_LIMIT_MAX) {
|
|
96
|
+
const oldestInWindow = rpcRunTimestamps[0];
|
|
97
|
+
const retryAfterMs = oldestInWindow + RPC_RATE_LIMIT_WINDOW_MS - now;
|
|
98
|
+
return { allowed: false, retryAfterMs: Math.max(retryAfterMs, 1000) };
|
|
99
|
+
}
|
|
100
|
+
return { allowed: true };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function recordRpcRun(): void {
|
|
104
|
+
rpcRunTimestamps.push(Date.now());
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Reset the RPC rate limiter. Used primarily for testing. */
|
|
108
|
+
export function resetRpcRateLimit(): void {
|
|
109
|
+
rpcRunTimestamps.length = 0;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function isAllowedRpcRunParams(
|
|
113
|
+
params: TeamToolParamsValue,
|
|
114
|
+
ctx: ExtensionContext,
|
|
115
|
+
): {
|
|
116
|
+
ok: boolean;
|
|
117
|
+
error?: string;
|
|
118
|
+
} {
|
|
119
|
+
// SECURITY: Require explicit intent for any RPC-initiated run creation.
|
|
120
|
+
// This prevents malicious extensions from spawning child Pi processes silently.
|
|
121
|
+
const cfg = params.config as Record<string, unknown> | undefined;
|
|
122
|
+
const intent = cfg?.intent as string | undefined;
|
|
123
|
+
if (!intent || typeof intent !== "string" || intent.trim().length === 0) {
|
|
124
|
+
return {
|
|
125
|
+
ok: false,
|
|
126
|
+
error: "RPC run requires config.intent (a non-empty intent string)",
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
// SECURITY: Validate cwd, if provided, resolves WITHIN the project directory (ctx.cwd).
|
|
130
|
+
// Arg order is (baseDir=ctx.cwd, targetPath=params.cwd): the project dir is the container,
|
|
131
|
+
// the caller-supplied cwd is the contained target. (Previously the args were swapped —
|
|
132
|
+
// resolveContainedPath(params.cwd, ".") — which validates that "." is inside params.cwd,
|
|
133
|
+
// trivially true for ANY path, so the check never rejected anything.)
|
|
134
|
+
// NOTE: defense-in-depth — handleTeamTool currently uses ctx.cwd and ignores params.cwd,
|
|
135
|
+
// but a correct guard here prevents a future regression from silently enabling cwd escape.
|
|
136
|
+
if (params.cwd && typeof params.cwd === "string") {
|
|
137
|
+
try {
|
|
138
|
+
resolveRealContainedPath(ctx.cwd, params.cwd);
|
|
139
|
+
} catch {
|
|
140
|
+
return {
|
|
141
|
+
ok: false,
|
|
142
|
+
error: "RPC run config.cwd must be within the project directory",
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return { ok: true };
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function on(events: EventBusLike, channel: string, handler: (raw: any) => unknown): () => void {
|
|
150
|
+
const unsub = events.on(channel, handler);
|
|
151
|
+
return typeof unsub === "function" ? unsub : () => {};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// SECURITY TRUST BOUNDARY: RPC channels (pi-crew:rpc:run, pi-crew:rpc:status,
|
|
155
|
+
// pi-crew:rpc:live-control) are accessible to any extension on the shared event
|
|
156
|
+
// bus. Mitigations: rate limiting, explicit intent, operation allowlist, cwd
|
|
157
|
+
// containment validation, and HMAC origin signing (see rpc-hmac.ts).
|
|
158
|
+
|
|
159
|
+
export function registerPiCrewRpc(
|
|
160
|
+
events: EventBusLike | undefined,
|
|
161
|
+
getCtx: () => ExtensionContext | undefined,
|
|
162
|
+
): PiCrewRpcHandle | undefined {
|
|
163
|
+
if (!events) return undefined;
|
|
164
|
+
// Note: the prior loud startup warning has been replaced by a soft, opt-out
|
|
165
|
+
// info note emitted lazily by withHmacVerification only when an actual RPC
|
|
166
|
+
// request is processed (see rpc-hmac.ts). This avoids noisy output during
|
|
167
|
+
// every pi load for users who never wire cross-extension RPC.
|
|
168
|
+
const unsubs = [
|
|
169
|
+
on(
|
|
170
|
+
events,
|
|
171
|
+
"pi-crew:rpc:ping",
|
|
172
|
+
withHmacVerification(
|
|
173
|
+
(raw) =>
|
|
174
|
+
reply(events, "pi-crew:rpc:ping", requestId(raw), {
|
|
175
|
+
success: true,
|
|
176
|
+
data: { version: PI_CREW_RPC_VERSION },
|
|
177
|
+
}),
|
|
178
|
+
"pi-crew:rpc:ping",
|
|
179
|
+
),
|
|
180
|
+
),
|
|
181
|
+
on(
|
|
182
|
+
events,
|
|
183
|
+
"pi-crew:rpc:run",
|
|
184
|
+
withHmacVerification(async (raw) => {
|
|
185
|
+
const id = requestId(raw);
|
|
186
|
+
try {
|
|
187
|
+
// SECURITY (HIGH #4 fix): Rate limit RPC run requests
|
|
188
|
+
const rateLimit = checkRpcRateLimit();
|
|
189
|
+
if (!rateLimit.allowed) {
|
|
190
|
+
reply(events, "pi-crew:rpc:run", id, {
|
|
191
|
+
success: false,
|
|
192
|
+
error: `RPC run rate limit exceeded. Max ${RPC_RATE_LIMIT_MAX} requests per ${RPC_RATE_LIMIT_WINDOW_MS / 1000}s. Retry after ${Math.ceil((rateLimit.retryAfterMs ?? 60000) / 1000)}s.`,
|
|
193
|
+
});
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
recordRpcRun();
|
|
197
|
+
const ctx = getCtx();
|
|
198
|
+
if (!ctx) throw new Error("No active pi-crew session context.");
|
|
199
|
+
// Validate payload: only allow known fields from TeamToolParamsValue
|
|
200
|
+
const ALLOWED_RPC_RUN_KEYS = new Set([
|
|
201
|
+
"goal",
|
|
202
|
+
"team",
|
|
203
|
+
"workflow",
|
|
204
|
+
"async",
|
|
205
|
+
"cwd",
|
|
206
|
+
"config",
|
|
207
|
+
"skill",
|
|
208
|
+
"model",
|
|
209
|
+
"budgetTotal",
|
|
210
|
+
"budgetWarning",
|
|
211
|
+
"budgetAbort",
|
|
212
|
+
"budgetUnlimited",
|
|
213
|
+
]);
|
|
214
|
+
let params: TeamToolParamsValue;
|
|
215
|
+
if (raw && typeof raw === "object" && !Array.isArray(raw)) {
|
|
216
|
+
const filtered: Record<string, unknown> = {
|
|
217
|
+
...(raw as object),
|
|
218
|
+
};
|
|
219
|
+
// Strip any keys not in the allowlist to prevent injection of unexpected fields
|
|
220
|
+
for (const key of Object.keys(filtered)) {
|
|
221
|
+
if (!ALLOWED_RPC_RUN_KEYS.has(key)) delete filtered[key];
|
|
222
|
+
}
|
|
223
|
+
params = {
|
|
224
|
+
...filtered,
|
|
225
|
+
action: "run",
|
|
226
|
+
} as TeamToolParamsValue;
|
|
227
|
+
} else {
|
|
228
|
+
params = { action: "run" };
|
|
229
|
+
}
|
|
230
|
+
const permission = isAllowedRpcRunParams(params, ctx);
|
|
231
|
+
if (!permission.ok) {
|
|
232
|
+
reply(events, "pi-crew:rpc:run", id, {
|
|
233
|
+
success: false,
|
|
234
|
+
error: permission.error ?? "permission denied",
|
|
235
|
+
});
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
const result = await handleTeamTool(params, ctx);
|
|
239
|
+
reply(
|
|
240
|
+
events,
|
|
241
|
+
"pi-crew:rpc:run",
|
|
242
|
+
id,
|
|
243
|
+
result.isError ? { success: false, error: textOf(result) } : { success: true, data: result.details },
|
|
244
|
+
);
|
|
245
|
+
} catch (error) {
|
|
246
|
+
reply(events, "pi-crew:rpc:run", id, {
|
|
247
|
+
success: false,
|
|
248
|
+
error: error instanceof Error ? error.message : String(error),
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
}, "pi-crew:rpc:run"),
|
|
252
|
+
),
|
|
253
|
+
on(
|
|
254
|
+
events,
|
|
255
|
+
"pi-crew:rpc:status",
|
|
256
|
+
withHmacVerification(async (raw) => {
|
|
257
|
+
const id = requestId(raw);
|
|
258
|
+
try {
|
|
259
|
+
const ctx = getCtx();
|
|
260
|
+
if (!ctx) throw new Error("No active pi-crew session context.");
|
|
261
|
+
const runId = raw && typeof raw === "object" && !Array.isArray(raw) ? (raw as { runId?: string }).runId : undefined;
|
|
262
|
+
const result = await handleTeamTool({ action: "status", runId }, ctx);
|
|
263
|
+
reply(
|
|
264
|
+
events,
|
|
265
|
+
"pi-crew:rpc:status",
|
|
266
|
+
id,
|
|
267
|
+
result.isError
|
|
268
|
+
? { success: false, error: textOf(result) }
|
|
269
|
+
: {
|
|
270
|
+
success: true,
|
|
271
|
+
data: {
|
|
272
|
+
text: textOf(result),
|
|
273
|
+
details: result.details,
|
|
274
|
+
},
|
|
275
|
+
},
|
|
276
|
+
);
|
|
277
|
+
} catch (error) {
|
|
278
|
+
reply(events, "pi-crew:rpc:status", id, {
|
|
279
|
+
success: false,
|
|
280
|
+
error: error instanceof Error ? error.message : String(error),
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
}, "pi-crew:rpc:status"),
|
|
284
|
+
),
|
|
285
|
+
on(
|
|
286
|
+
events,
|
|
287
|
+
"pi-crew:live-control",
|
|
288
|
+
withHmacVerification((raw) => {
|
|
289
|
+
const request = parseLiveControlRealtimeMessage(raw);
|
|
290
|
+
if (request) publishLiveControlRealtime(request);
|
|
291
|
+
}, "pi-crew:live-control"),
|
|
292
|
+
),
|
|
293
|
+
on(
|
|
294
|
+
events,
|
|
295
|
+
"pi-crew:rpc:live-control",
|
|
296
|
+
withHmacVerification(async (raw) => {
|
|
297
|
+
const id = requestId(raw);
|
|
298
|
+
try {
|
|
299
|
+
const ctx = getCtx();
|
|
300
|
+
if (!ctx) throw new Error("No active pi-crew session context.");
|
|
301
|
+
const obj = raw && typeof raw === "object" && !Array.isArray(raw) ? (raw as Record<string, unknown>) : {};
|
|
302
|
+
const rawOp = typeof obj.operation === "string" ? obj.operation : "steer-agent";
|
|
303
|
+
// SECURITY: Reject any operation not in the explicit allowlist.
|
|
304
|
+
// Mutating ops (approve-plan, cancel-plan, steer-agent, stop-agent, etc.)
|
|
305
|
+
// require user consent and are blocked here.
|
|
306
|
+
if (!isAllowedRpcOperation(rawOp)) {
|
|
307
|
+
reply(events, "pi-crew:rpc:live-control", id, {
|
|
308
|
+
success: false,
|
|
309
|
+
error: `RPC operation '${rawOp}' is not allowed. Allowed: ${[...RPC_ALLOWED_OPERATIONS].join(", ")}`,
|
|
310
|
+
});
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
const result = await handleTeamTool(
|
|
314
|
+
{
|
|
315
|
+
action: "api",
|
|
316
|
+
runId: typeof obj.runId === "string" ? obj.runId : undefined,
|
|
317
|
+
config: {
|
|
318
|
+
operation: rawOp,
|
|
319
|
+
agentId: obj.agentId,
|
|
320
|
+
message: obj.message,
|
|
321
|
+
prompt: obj.prompt,
|
|
322
|
+
},
|
|
323
|
+
},
|
|
324
|
+
ctx,
|
|
325
|
+
);
|
|
326
|
+
reply(
|
|
327
|
+
events,
|
|
328
|
+
"pi-crew:rpc:live-control",
|
|
329
|
+
id,
|
|
330
|
+
result.isError
|
|
331
|
+
? { success: false, error: textOf(result) }
|
|
332
|
+
: {
|
|
333
|
+
success: true,
|
|
334
|
+
data: {
|
|
335
|
+
text: textOf(result),
|
|
336
|
+
details: result.details,
|
|
337
|
+
},
|
|
338
|
+
},
|
|
339
|
+
);
|
|
340
|
+
} catch (error) {
|
|
341
|
+
reply(events, "pi-crew:rpc:live-control", id, {
|
|
342
|
+
success: false,
|
|
343
|
+
error: error instanceof Error ? error.message : String(error),
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
}, "pi-crew:rpc:live-control"),
|
|
347
|
+
),
|
|
348
|
+
];
|
|
349
|
+
return { unsubscribe: () => unsubs.forEach((unsub) => unsub()) };
|
|
350
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export function piTeamsHelp(): string {
|
|
2
|
+
return [
|
|
3
|
+
"pi-crew commands:",
|
|
4
|
+
"",
|
|
5
|
+
"Core:",
|
|
6
|
+
"- Agent can use the `team` tool autonomously; slash commands are manual controls.",
|
|
7
|
+
"- Tool action `recommend` suggests the best team/workflow for a goal.",
|
|
8
|
+
"- /teams — list teams, workflows, agents, recent runs",
|
|
9
|
+
"- /team-run [--team=name] [--workflow=name] [--async] [--worktree] <goal>",
|
|
10
|
+
"- /team-status <runId>",
|
|
11
|
+
"- /team-summary <runId>",
|
|
12
|
+
"- /team-resume <runId>",
|
|
13
|
+
"- /team-cancel <runId>",
|
|
14
|
+
"- /team-retry <runId> [taskId]",
|
|
15
|
+
"",
|
|
16
|
+
"Inspection:",
|
|
17
|
+
"- /team-events <runId>",
|
|
18
|
+
"- /team-artifacts <runId>",
|
|
19
|
+
"- /team-worktrees <runId>",
|
|
20
|
+
"- /team-api <runId> <operation> [taskId=<taskId>] [body=<message>]",
|
|
21
|
+
"- /team-dashboard",
|
|
22
|
+
"- /team-mascot",
|
|
23
|
+
"- /team-transcript <runId> [taskId]",
|
|
24
|
+
"- /team-result <runId> [taskId]",
|
|
25
|
+
"- /team-manager — interactive menu (alias: /team-cleanup-menu)",
|
|
26
|
+
"",
|
|
27
|
+
"Maintenance:",
|
|
28
|
+
"- /team-cleanup <runId> [--force] (or scope=project/user for uninstall cleanup)",
|
|
29
|
+
"- /team-forget <runId> --confirm [--force]",
|
|
30
|
+
"- /team-prune --keep=20 --confirm",
|
|
31
|
+
"",
|
|
32
|
+
"Portability:",
|
|
33
|
+
"- /team-export <runId>",
|
|
34
|
+
"- /team-import <path-to-run-export.json> [--user]",
|
|
35
|
+
"- /team-imports",
|
|
36
|
+
"",
|
|
37
|
+
"Diagnostics:",
|
|
38
|
+
"- /team-health",
|
|
39
|
+
"- /team-doctor",
|
|
40
|
+
"- /team-init [--copy-builtins] [--overwrite]",
|
|
41
|
+
"- /team-config [key=value] [--unset=key.path] [--project]",
|
|
42
|
+
"- /team-autonomy [status|on|off|manual|suggested|assisted|aggressive] [--prefer-async] [--no-worktree-suggest]",
|
|
43
|
+
"- /team-validate",
|
|
44
|
+
"- /team-help",
|
|
45
|
+
"",
|
|
46
|
+
"Goal loops (P0/P1 — autonomous goal loop):",
|
|
47
|
+
"- team action='goal' config.subAction='start' config.objective='...' config.evaluatorModel='...' [config.maxTurns=20] [budgetTotal=N]",
|
|
48
|
+
"- team action='goal' config.subAction='status' [config.goalId=<id>]",
|
|
49
|
+
"- team action='goal' config.subAction='pause|resume|stop|step|clear' config.goalId=<id>",
|
|
50
|
+
"",
|
|
51
|
+
"Dynamic workflows (P2/P3 — JS-script orchestration):",
|
|
52
|
+
"- Place a .dwf.ts under .crew/workflows/ then: team action='run' workflow='<name>' goal='...'",
|
|
53
|
+
"- team action='workflow-list'",
|
|
54
|
+
"- team action='workflow-get' config.name='<name>'",
|
|
55
|
+
"- team action='workflow-create' confirm=true config.name='<name>' config.script='<.dwf.ts source>' (USER-initiated only; ACE-gated)",
|
|
56
|
+
"- team action='workflow-save' config.name='<name>' config.script='<source>'",
|
|
57
|
+
"- team action='workflow-delete' confirm=true config.name='<name>'",
|
|
58
|
+
"- /workflows — list dynamic workflows",
|
|
59
|
+
"",
|
|
60
|
+
"Real child workers are enabled by default. Use runtime.mode=scaffold or executeWorkers=false only for dry runs.",
|
|
61
|
+
].join("\n");
|
|
62
|
+
}
|