@hassangameryt/freeflow 0.7.3 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/plugins/marketplace.json +2 -2
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +2 -2
- package/.codex-plugin/plugin.json +3 -3
- package/.cursor-plugin/plugin.json +1 -1
- package/NOTICE +14 -0
- package/README.md +101 -159
- package/capabilities/cognitive-routing/SKILL.md +47 -210
- package/capabilities/cognitive-routing/references/coordinator.md +163 -0
- package/capabilities/cognitive-routing/references/worker.md +38 -0
- package/capabilities/compaction/SKILL.md +49 -0
- package/capabilities/compaction/references/summary-format.md +250 -0
- package/capabilities/messaging/SKILL.md +49 -0
- package/capabilities/task-memory/SKILL.md +87 -0
- package/command-surface.json +8 -52
- package/gemini-extension.json +1 -1
- package/hooks/claude/hooks.json +13 -3
- package/hooks/codex/hooks.json +2 -1
- package/hooks/shared/runtime-context.mjs +73 -83
- package/licenses/Apache-2.0.txt +55 -0
- package/package.json +42 -12
- package/pi-extension/README.md +41 -0
- package/pi-extension/dist/ask-user/ask.js +108 -0
- package/pi-extension/dist/ask-user/form.js +250 -0
- package/pi-extension/dist/ask-user/questions.js +66 -0
- package/pi-extension/dist/ask-user/tool.js +124 -0
- package/pi-extension/dist/cognitive-routing/assembler.js +417 -0
- package/pi-extension/dist/cognitive-routing/bootstrap.js +90 -0
- package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/config.js +8 -6
- package/pi-extension/dist/cognitive-routing/economics.js +45 -0
- package/pi-extension/dist/cognitive-routing/event-schema.js +388 -0
- package/pi-extension/dist/{session-sources/events.js → cognitive-routing/event-store.js} +107 -41
- package/pi-extension/dist/cognitive-routing/gate.js +256 -0
- package/pi-extension/dist/cognitive-routing/handlers.js +901 -0
- package/pi-extension/dist/cognitive-routing/handoffs.js +350 -0
- package/pi-extension/dist/cognitive-routing/model-control.js +476 -0
- package/pi-extension/dist/cognitive-routing/projection.js +520 -0
- package/pi-extension/dist/cognitive-routing/provenance.js +83 -0
- package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/render.js +15 -15
- package/pi-extension/dist/cognitive-routing/runtime.js +398 -0
- package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/schemas.js +47 -11
- package/pi-extension/dist/cognitive-routing/session.js +223 -0
- package/pi-extension/dist/{session-sources → cognitive-routing}/sources.js +126 -87
- package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/state.js +55 -386
- package/pi-extension/dist/cognitive-routing/status.js +81 -0
- package/pi-extension/dist/cognitive-routing/tools.js +74 -0
- package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/types.js +14 -11
- package/pi-extension/dist/compaction/carry.js +205 -0
- package/pi-extension/dist/compaction/config.js +24 -0
- package/pi-extension/dist/compaction/controller.js +494 -0
- package/pi-extension/dist/compaction/harness.js +93 -0
- package/pi-extension/dist/compaction/results.js +159 -0
- package/pi-extension/dist/compaction/thresholds.js +35 -0
- package/pi-extension/dist/compaction/tool.js +57 -0
- package/pi-extension/dist/host/branch.js +13 -0
- package/pi-extension/dist/host/canonical.js +12 -0
- package/pi-extension/dist/host/catalog.js +62 -0
- package/pi-extension/dist/host/config-validation.js +233 -0
- package/pi-extension/dist/host/config.js +361 -0
- package/pi-extension/dist/host/inbound.js +142 -0
- package/pi-extension/dist/host/projection-tags.js +60 -0
- package/pi-extension/dist/host/prompts.js +93 -0
- package/pi-extension/dist/{session-sources → host}/read-only-session.js +117 -1
- package/pi-extension/dist/{runtime → host}/request-history.js +125 -39
- package/pi-extension/dist/host/rows.js +25 -0
- package/pi-extension/dist/host/runtime-state.js +204 -0
- package/pi-extension/dist/host/section-keeper.js +85 -0
- package/pi-extension/dist/host/settings/freeflow-command.js +2323 -0
- package/pi-extension/dist/{settings/settings-tui.js → host/settings/settings-view.js} +205 -73
- package/pi-extension/dist/host/staging.js +28 -0
- package/pi-extension/dist/host/status.js +115 -0
- package/pi-extension/dist/index.js +1004 -188
- package/pi-extension/dist/messaging/config.js +27 -0
- package/pi-extension/dist/messaging/messaging.js +398 -0
- package/pi-extension/dist/messaging/registry.js +57 -0
- package/pi-extension/dist/messaging/render.js +90 -0
- package/pi-extension/dist/messaging/tools.js +109 -0
- package/pi-extension/dist/messaging/transport.js +101 -0
- package/pi-extension/dist/provider-support/cache/anchor.js +21 -0
- package/pi-extension/dist/provider-support/cache/anthropic.js +102 -0
- package/pi-extension/dist/provider-support/cache/health.js +73 -0
- package/pi-extension/dist/provider-support/cache/index.js +58 -0
- package/pi-extension/dist/provider-support/cache/keepalive.js +222 -0
- package/pi-extension/dist/provider-support/cache/monitor.js +17 -0
- package/pi-extension/dist/provider-support/cache/openai-responses.js +48 -0
- package/pi-extension/dist/provider-support/effort.js +13 -0
- package/pi-extension/dist/provider-support/index.js +44 -3
- package/pi-extension/dist/provider-support/{astra → openai}/adapter.js +46 -29
- package/pi-extension/dist/provider-support/{astra → openai}/history.js +10 -8
- package/pi-extension/dist/provider-support/{astra → openai}/index.js +8 -4
- package/pi-extension/dist/provider-support/{astra → openai}/session-state.js +29 -38
- package/pi-extension/dist/provider-support/routes.js +17 -0
- package/pi-extension/dist/task-memory/config.js +49 -0
- package/pi-extension/dist/task-memory/registry.js +119 -0
- package/pi-extension/dist/task-memory/render.js +107 -0
- package/pi-extension/dist/task-memory/runtime.js +994 -0
- package/pi-extension/dist/task-memory/tool.js +233 -0
- package/pi-extension/dist/tool-execution/apply-patch/grammar.js +23 -0
- package/pi-extension/dist/tool-execution/apply-patch/match.js +38 -0
- package/pi-extension/dist/tool-execution/apply-patch/parser.js +177 -0
- package/pi-extension/dist/tool-execution/apply-patch/plan.js +201 -0
- package/pi-extension/dist/tool-execution/apply-patch/tool.js +84 -0
- package/pi-extension/dist/tool-execution/background.js +430 -0
- package/pi-extension/dist/tool-execution/bash-guard.js +23 -0
- package/pi-extension/dist/tool-execution/config.js +33 -0
- package/pi-extension/dist/tool-execution/edit-messages.js +120 -0
- package/pi-extension/dist/tool-execution/file-state.js +138 -0
- package/pi-extension/dist/tool-execution/file-tracking.js +163 -0
- package/pi-extension/dist/tool-execution/guidance-notices.js +124 -0
- package/pi-extension/dist/tool-execution/prompt.js +54 -0
- package/pi-extension/dist/tool-execution/skill-reads.js +143 -0
- package/pi-extension/dist/tool-execution/tools.js +16 -0
- package/pi-extension/freeflow/README.md +9 -0
- package/runtime/prompts/cognitive-routing.md +4 -4
- package/runtime/prompts/core.md +29 -72
- package/runtime/prompts/interaction-contract.md +18 -8
- package/runtime/prompts/tool-execution.md +25 -0
- package/runtime/prompts/working-method.md +25 -0
- package/skills/deliver-work/SKILL.md +76 -0
- package/skills/deliver-work/references/branch-closeout.md +134 -0
- package/skills/deliver-work/references/commit.md +83 -0
- package/skills/deliver-work/references/launch.md +94 -0
- package/skills/deliver-work/references/release.md +85 -0
- package/skills/deliver-work/scripts/commit-state.mjs +139 -0
- package/skills/deliver-work/scripts/delivery-state.mjs +107 -0
- package/skills/deliver-work/scripts/lib/git-state.mjs +178 -0
- package/skills/deliver-work/scripts/push-route.mjs +122 -0
- package/skills/deliver-work/scripts/release-preflight.mjs +177 -0
- package/skills/discuss-work/SKILL.md +148 -0
- package/skills/{discuss → discuss-work}/references/discussion-continuity.md +13 -50
- package/skills/discuss-work/references/learning-action.md +36 -0
- package/skills/discuss-work/scripts/choices.mjs +101 -0
- package/skills/discuss-work/scripts/lib/choices.mjs +159 -0
- package/skills/evaluate-behavior/SKILL.md +107 -0
- package/skills/evaluate-behavior/references/authoring.md +381 -0
- package/skills/evaluate-behavior/references/coding-tasks.md +145 -0
- package/skills/evaluate-behavior/references/conversations-pipelines-judges.md +177 -0
- package/skills/evaluate-behavior/references/design-and-measurement.md +273 -0
- package/skills/evaluate-behavior/references/running-and-evidence.md +245 -0
- package/skills/evaluate-behavior/scripts/claude-guard.mjs +76 -0
- package/skills/evaluate-behavior/scripts/guard-rules.mjs +309 -0
- package/skills/evaluate-behavior/scripts/lib/agreement.mjs +90 -0
- package/skills/evaluate-behavior/scripts/lib/arms.mjs +55 -0
- package/skills/evaluate-behavior/scripts/lib/budget.mjs +87 -0
- package/skills/evaluate-behavior/scripts/lib/build-source.mjs +287 -0
- package/skills/evaluate-behavior/scripts/lib/clear.mjs +74 -0
- package/skills/evaluate-behavior/scripts/lib/container.mjs +395 -0
- package/skills/evaluate-behavior/scripts/lib/decision.mjs +418 -0
- package/skills/evaluate-behavior/scripts/lib/definitions.mjs +1072 -0
- package/skills/evaluate-behavior/scripts/lib/freeflow.mjs +103 -0
- package/skills/evaluate-behavior/scripts/lib/grade-command.mjs +591 -0
- package/skills/evaluate-behavior/scripts/lib/grade-comparison.mjs +90 -0
- package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/grade-interaction.mjs +19 -18
- package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/grade-support.mjs +32 -5
- package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/grade-workspace.mjs +28 -12
- package/skills/evaluate-behavior/scripts/lib/grade.mjs +101 -0
- package/skills/evaluate-behavior/scripts/lib/hooks.mjs +98 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/confinement.mjs +196 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/constraints.mjs +111 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/index.mjs +48 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/isolation.mjs +74 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/launch.mjs +123 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/model-call.mjs +73 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/recorder.mjs +178 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/session.mjs +494 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/skills.mjs +81 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/index.mjs +31 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/names.mjs +3 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/pi/confinement.mjs +173 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/pi/host.mjs +148 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/pi/index.mjs +70 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/pi/isolation.mjs +81 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/pi/model-call.mjs +79 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/pi/process.mjs +241 -0
- package/skills/{evaluate-skill/scripts/lib → evaluate-behavior/scripts/lib/hosts/pi}/rpc.mjs +144 -26
- package/skills/evaluate-behavior/scripts/lib/identity.mjs +82 -0
- package/skills/evaluate-behavior/scripts/lib/judge.mjs +688 -0
- package/skills/evaluate-behavior/scripts/lib/metrics.mjs +332 -0
- package/skills/evaluate-behavior/scripts/lib/model-call.mjs +17 -0
- package/skills/evaluate-behavior/scripts/lib/participants.mjs +616 -0
- package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/process.mjs +22 -0
- package/skills/evaluate-behavior/scripts/lib/queue.mjs +54 -0
- package/skills/evaluate-behavior/scripts/lib/reference.mjs +343 -0
- package/skills/evaluate-behavior/scripts/lib/reuse.mjs +277 -0
- package/skills/evaluate-behavior/scripts/lib/review.mjs +933 -0
- package/skills/evaluate-behavior/scripts/lib/runner.mjs +732 -0
- package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/sandbox.mjs +121 -14
- package/skills/evaluate-behavior/scripts/lib/scan-shell.mjs +457 -0
- package/skills/evaluate-behavior/scripts/lib/scan.mjs +479 -0
- package/skills/evaluate-behavior/scripts/lib/seal.mjs +91 -0
- package/skills/evaluate-behavior/scripts/lib/shell.mjs +560 -0
- package/skills/evaluate-behavior/scripts/lib/source-override.mjs +64 -0
- package/skills/evaluate-behavior/scripts/lib/subject-file.mjs +88 -0
- package/skills/{evaluate-skill/scripts/lib/pi.mjs → evaluate-behavior/scripts/lib/subject.mjs} +239 -262
- package/skills/evaluate-behavior/scripts/lib/task-copy.mjs +124 -0
- package/skills/evaluate-behavior/scripts/lib/trials.mjs +524 -0
- package/skills/evaluate-behavior/scripts/lib/validity.mjs +68 -0
- package/skills/evaluate-behavior/scripts/lib/view-html.mjs +454 -0
- package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/view.mjs +218 -11
- package/skills/evaluate-behavior/scripts/pi-command-tool.mjs +159 -0
- package/skills/evaluate-behavior/scripts/pi-container-route.mjs +23 -0
- package/skills/evaluate-behavior/scripts/pi-guard.mjs +77 -0
- package/skills/evaluate-behavior/scripts/pi-login-refresh.mjs +31 -0
- package/skills/evaluate-behavior/scripts/skill-eval.mjs +586 -0
- package/skills/execute-work/SKILL.md +96 -72
- package/skills/execute-work/references/code-practices.md +27 -49
- package/skills/execute-work/references/design.md +271 -0
- package/skills/execute-work/references/diagnose.md +117 -0
- package/skills/execute-work/references/migration.md +108 -0
- package/skills/execute-work/references/simplify.md +65 -0
- package/skills/execute-work/scripts/self-review.mjs +422 -0
- package/skills/execute-work/scripts/touch-map.mjs +290 -0
- package/skills/review-work/SKILL.md +94 -64
- package/skills/review-work/references/adjudication.md +94 -0
- package/skills/review-work/references/artifact.md +198 -0
- package/skills/review-work/references/evals-and-judges.md +66 -0
- package/skills/review-work/references/work.md +177 -0
- package/skills/review-work/scripts/check-brief.mjs +128 -0
- package/skills/review-work/scripts/review-packet.mjs +233 -0
- package/skills/setup-freeflow/SKILL.md +69 -16
- package/skills/track-work/SKILL.md +135 -151
- package/skills/track-work/references/handoff.md +190 -0
- package/skills/track-work/references/projects.md +51 -0
- package/skills/track-work/references/working-record-format.md +201 -344
- package/skills/track-work/scripts/lib/document.mjs +79 -19
- package/skills/track-work/scripts/lib/format.mjs +18 -1
- package/skills/track-work/scripts/lib/recover.mjs +254 -0
- package/skills/track-work/scripts/lib/registry.mjs +656 -0
- package/skills/track-work/scripts/lib/search.mjs +40 -0
- package/skills/track-work/scripts/lib/store.mjs +53 -21
- package/skills/track-work/scripts/lib/transitions.mjs +79 -4
- package/skills/track-work/scripts/lib/views.mjs +50 -1
- package/skills/track-work/scripts/working-record.mjs +263 -22
- package/skills/verify-work/SKILL.md +64 -47
- package/skills/verify-work/references/feature-map.md +120 -0
- package/skills/verify-work/references/runtime-evidence.md +203 -0
- package/skills/verify-work/references/test-design.md +27 -6
- package/skills/verify-work/scripts/test-changes.mjs +246 -0
- package/skills/write-docs/SKILL.md +171 -0
- package/skills/write-docs/references/documentation-shapes.md +184 -0
- package/skills/write-docs/references/spec-and-plan-shapes.md +308 -0
- package/skills/write-docs/scripts/artifact-check.mjs +148 -0
- package/skills/write-docs/scripts/docs-check.mjs +229 -0
- package/skills/write-docs/scripts/plan-lint.mjs +123 -0
- package/skills/write-skill/SKILL.md +165 -165
- package/skills/write-skill/references/activation-and-wording.md +143 -0
- package/skills/write-skill/references/placement-and-dependencies.md +154 -0
- package/skills/write-skill/scripts/lib/compare.mjs +701 -0
- package/skills/write-skill/scripts/lib/similarity.mjs +56 -0
- package/skills/write-skill/scripts/lib/skill-author.mjs +197 -3
- package/skills/write-skill/scripts/skill-author.mjs +67 -8
- package/capabilities/context-virtualization/SKILL.md +0 -103
- package/capabilities/conversation-history/SKILL.md +0 -53
- package/hooks/tests/check-host-adapters.test.mjs +0 -82
- package/hooks/tests/check-runtime-context-hook.sh +0 -212
- package/pi-extension/dist/cognitive-routing-v2/projection.js +0 -330
- package/pi-extension/dist/cognitive-routing-v2/runtime.js +0 -2181
- package/pi-extension/dist/cognitive-routing-v2/tools.js +0 -60
- package/pi-extension/dist/context-virtualization/commands.js +0 -89
- package/pi-extension/dist/context-virtualization/projector.js +0 -42
- package/pi-extension/dist/context-virtualization/runtime.js +0 -378
- package/pi-extension/dist/context-virtualization/state.js +0 -57
- package/pi-extension/dist/context-virtualization/types.js +0 -10
- package/pi-extension/dist/conversation-history/passages.js +0 -66
- package/pi-extension/dist/conversation-history/ranking.js +0 -105
- package/pi-extension/dist/conversation-history/runtime.js +0 -226
- package/pi-extension/dist/conversation-history/search.js +0 -56
- package/pi-extension/dist/conversation-history/types.js +0 -1
- package/pi-extension/dist/freeflow-context/resolver.js +0 -120
- package/pi-extension/dist/freeflow-context/runtime.js +0 -80
- package/pi-extension/dist/freeflow-context/source-projector.js +0 -95
- package/pi-extension/dist/freeflow-context/tool.js +0 -314
- package/pi-extension/dist/freeflow-context/types.js +0 -12
- package/pi-extension/dist/freeflow-context/visibility.js +0 -28
- package/pi-extension/dist/integrations/mcp-config.js +0 -29
- package/pi-extension/dist/runtime/runtime-context.js +0 -754
- package/pi-extension/dist/runtime/runtime-identity.js +0 -5
- package/pi-extension/dist/session-sources/provenance.js +0 -32
- package/pi-extension/dist/settings/settings-ui.js +0 -1360
- package/pi-extension/dist/ui/text-component.js +0 -57
- package/pi-extension/src/cognitive-routing-v2/budget.ts +0 -42
- package/pi-extension/src/cognitive-routing-v2/config.ts +0 -173
- package/pi-extension/src/cognitive-routing-v2/projection.ts +0 -375
- package/pi-extension/src/cognitive-routing-v2/render.ts +0 -277
- package/pi-extension/src/cognitive-routing-v2/runtime.ts +0 -2241
- package/pi-extension/src/cognitive-routing-v2/schemas.ts +0 -180
- package/pi-extension/src/cognitive-routing-v2/state.ts +0 -839
- package/pi-extension/src/cognitive-routing-v2/tools.ts +0 -62
- package/pi-extension/src/cognitive-routing-v2/types.ts +0 -254
- package/pi-extension/src/context-virtualization/commands.ts +0 -111
- package/pi-extension/src/context-virtualization/projector.ts +0 -62
- package/pi-extension/src/context-virtualization/runtime.ts +0 -428
- package/pi-extension/src/context-virtualization/state.ts +0 -79
- package/pi-extension/src/context-virtualization/types.ts +0 -92
- package/pi-extension/src/conversation-history/passages.ts +0 -81
- package/pi-extension/src/conversation-history/ranking.ts +0 -155
- package/pi-extension/src/conversation-history/runtime.ts +0 -250
- package/pi-extension/src/conversation-history/search.ts +0 -79
- package/pi-extension/src/conversation-history/types.ts +0 -31
- package/pi-extension/src/freeflow-context/resolver.ts +0 -139
- package/pi-extension/src/freeflow-context/runtime.ts +0 -100
- package/pi-extension/src/freeflow-context/source-projector.ts +0 -106
- package/pi-extension/src/freeflow-context/tool.ts +0 -366
- package/pi-extension/src/freeflow-context/types.ts +0 -70
- package/pi-extension/src/freeflow-context/visibility.ts +0 -53
- package/pi-extension/src/index.ts +0 -390
- package/pi-extension/src/integrations/mcp-config.ts +0 -32
- package/pi-extension/src/provider-support/astra/adapter.ts +0 -108
- package/pi-extension/src/provider-support/astra/history.ts +0 -145
- package/pi-extension/src/provider-support/astra/index.ts +0 -12
- package/pi-extension/src/provider-support/astra/session-state.ts +0 -113
- package/pi-extension/src/provider-support/index.ts +0 -5
- package/pi-extension/src/runtime/request-history.ts +0 -243
- package/pi-extension/src/runtime/runtime-context.ts +0 -860
- package/pi-extension/src/runtime/runtime-identity.ts +0 -8
- package/pi-extension/src/session-sources/events.ts +0 -230
- package/pi-extension/src/session-sources/provenance.ts +0 -39
- package/pi-extension/src/session-sources/read-only-session.ts +0 -170
- package/pi-extension/src/session-sources/sources.ts +0 -334
- package/pi-extension/src/settings/settings-tui.ts +0 -501
- package/pi-extension/src/settings/settings-ui.ts +0 -1652
- package/pi-extension/src/ui/text-component.ts +0 -66
- package/pi-extension/tests/cache-reuse/history.test.js +0 -117
- package/pi-extension/tests/cache-reuse/native.test.js +0 -91
- package/pi-extension/tests/cache-reuse/settings.test.js +0 -211
- package/pi-extension/tests/cognitive-routing-v2/communication.test.js +0 -577
- package/pi-extension/tests/cognitive-routing-v2/compaction-input.test.js +0 -183
- package/pi-extension/tests/cognitive-routing-v2/delegation.test.js +0 -557
- package/pi-extension/tests/cognitive-routing-v2/native.test.js +0 -474
- package/pi-extension/tests/cognitive-routing-v2/performance.test.js +0 -87
- package/pi-extension/tests/cognitive-routing-v2/persistence.test.js +0 -300
- package/pi-extension/tests/cognitive-routing-v2/projection.test.js +0 -241
- package/pi-extension/tests/cognitive-routing-v2/recovery.test.js +0 -1034
- package/pi-extension/tests/cognitive-routing-v2/render.test.js +0 -244
- package/pi-extension/tests/cognitive-routing-v2/runtime.test.js +0 -423
- package/pi-extension/tests/cognitive-routing-v2/stabilization.test.js +0 -521
- package/pi-extension/tests/cognitive-routing-v2/state.test.js +0 -586
- package/pi-extension/tests/context-virtualization/extension.test.js +0 -775
- package/pi-extension/tests/context-virtualization/projector.test.js +0 -110
- package/pi-extension/tests/context-virtualization/runtime.test.js +0 -230
- package/pi-extension/tests/conversation-history/runtime.test.js +0 -98
- package/pi-extension/tests/conversation-history/search.test.js +0 -97
- package/pi-extension/tests/fixtures/pi-host.js +0 -4
- package/pi-extension/tests/fixtures/routing-native.js +0 -191
- package/pi-extension/tests/integration/mode-free-core.test.js +0 -220
- package/pi-extension/tests/integration/pi-extension.test.js +0 -1447
- package/pi-extension/tests/integration/prompt-architecture.test.js +0 -447
- package/pi-extension/tests/integration/unaffected-regressions.test.js +0 -478
- package/pi-extension/tests/provider-support/astra-boundaries.test.js +0 -75
- package/pi-extension/tests/provider-support/astra.test.js +0 -306
- package/pi-extension/tests/provider-support/cli.test.js +0 -92
- package/pi-extension/tests/provider-support/native.test.js +0 -183
- package/pi-extension/tests/settings/settings-tui.test.js +0 -243
- package/pi-extension/tsconfig.json +0 -16
- package/plugin.json +0 -33
- package/runtime/prompts/context-virtualization.md +0 -3
- package/runtime/prompts/conversation-history.md +0 -3
- package/skills/action-selection/SKILL.md +0 -100
- package/skills/action-selection/references/trajectory-stalls.md +0 -71
- package/skills/bypass/SKILL.md +0 -153
- package/skills/commit-work/SKILL.md +0 -145
- package/skills/commit-work/references/git-state-edges.md +0 -94
- package/skills/commit-work/references/simple-push.md +0 -92
- package/skills/commit-work/references/staging-decisions.md +0 -106
- package/skills/decision-gate/SKILL.md +0 -106
- package/skills/design-for-depth/SKILL.md +0 -117
- package/skills/design-for-depth/references/design-pressure-signals.md +0 -182
- package/skills/design-for-depth/references/implementation-decisions.md +0 -76
- package/skills/design-for-depth/references/interface-design-loop.md +0 -136
- package/skills/design-for-depth/references/module-and-dependency-design.md +0 -147
- package/skills/design-for-depth/references/software-design-philosophy.md +0 -202
- package/skills/design-for-depth/references/state-and-failure-boundaries.md +0 -136
- package/skills/diagnose-failure/SKILL.md +0 -125
- package/skills/diagnose-failure/references/diagnostic-loop-catalog.md +0 -48
- package/skills/diagnose-failure/references/flaky-and-performance-diagnosis.md +0 -51
- package/skills/discuss/SKILL.md +0 -116
- package/skills/evaluate-skill/SKILL.md +0 -219
- package/skills/evaluate-skill/references/evaluation-definition-schema.md +0 -385
- package/skills/evaluate-skill/references/evaluation-design.md +0 -112
- package/skills/evaluate-skill/references/execution-and-evidence.md +0 -90
- package/skills/evaluate-skill/references/review-and-revision.md +0 -92
- package/skills/evaluate-skill/scripts/lib/definitions.mjs +0 -407
- package/skills/evaluate-skill/scripts/lib/grade-comparison.mjs +0 -60
- package/skills/evaluate-skill/scripts/lib/grade.mjs +0 -64
- package/skills/evaluate-skill/scripts/lib/runner.mjs +0 -368
- package/skills/evaluate-skill/scripts/pi-guard.mjs +0 -164
- package/skills/evaluate-skill/scripts/skill-eval.mjs +0 -102
- package/skills/execute-work/references/execute-work-edges.md +0 -68
- package/skills/finish-branch/SKILL.md +0 -117
- package/skills/finish-branch/references/integration-options.md +0 -80
- package/skills/handoff/SKILL.md +0 -82
- package/skills/handoff/references/handoff-templates.md +0 -150
- package/skills/launch-work/SKILL.md +0 -116
- package/skills/launch-work/references/launch-readiness.md +0 -85
- package/skills/migration-work/SKILL.md +0 -115
- package/skills/migration-work/references/migration-lifecycle.md +0 -82
- package/skills/release-work/SKILL.md +0 -118
- package/skills/release-work/references/release-evidence.md +0 -78
- package/skills/review-artifact/SKILL.md +0 -142
- package/skills/review-artifact/references/adjudicate-artifact-review.md +0 -101
- package/skills/review-artifact/references/independent-artifact-reviewer-contract.md +0 -159
- package/skills/review-work/references/adjudicate-work-review.md +0 -97
- package/skills/review-work/references/independent-work-reviewer-contract.md +0 -139
- package/skills/review-work/references/security-risk-lens.md +0 -69
- package/skills/setup-freeflow/references/activation-contract.md +0 -109
- package/skills/setup-freeflow/references/host-setup.md +0 -67
- package/skills/simplify-code/SKILL.md +0 -85
- package/skills/simplify-code/references/simplification-patterns.md +0 -70
- package/skills/verify-work/references/browser-runtime-evidence.md +0 -55
- package/skills/verify-work/references/integration-evidence.md +0 -47
- package/skills/verify-work/references/performance-evidence.md +0 -57
- package/skills/workflow/SKILL.md +0 -127
- package/skills/workflow/references/domain-skill-composition.md +0 -43
- package/skills/write-plan/SKILL.md +0 -92
- package/skills/write-plan/references/plan-shapes.md +0 -156
- package/skills/write-skill/references/activation-boundaries.md +0 -104
- package/skills/write-skill/references/agent-first-instructions.md +0 -121
- package/skills/write-skill/references/progressive-disclosure.md +0 -127
- package/skills/write-skill/references/skill-dependency-graphs.md +0 -190
- package/skills/write-spec/SKILL.md +0 -92
- package/skills/write-spec/references/artifact-standards.md +0 -86
- package/skills/write-spec/references/decision-records.md +0 -78
- package/skills/write-spec/references/spec-shapes.md +0 -143
- /package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/budget.js +0 -0
- /package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/evidence.mjs +0 -0
- /package/skills/{evaluate-skill → evaluate-behavior}/scripts/pi-observer.mjs +0 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { APPLY_PATCH_LARK_GRAMMAR } from "./grammar.js";
|
|
2
|
+
import { applyPatch } from "./plan.js";
|
|
3
|
+
export const APPLY_PATCH = "apply_patch";
|
|
4
|
+
const DESCRIPTION =
|
|
5
|
+
"Use apply_patch to change several files in one call, or to add, delete or move files together with other changes; to change one existing file, use edit. The input is a patch: *** Begin Patch, then *** Add File:, *** Update File: (optionally followed by *** Move to:) or *** Delete File: sections, then *** End Patch. In update sections, @@ lines anchor a hunk and every other line starts with space (context), - (remove) or + (add). Copy context lines from your latest view of the file, enough for each hunk to match one place.";
|
|
6
|
+
const CODES = { add: "A", update: "M", move: "M", delete: "D" };
|
|
7
|
+
/** Codex's summary on success, so GPT models read the result they were trained on. */
|
|
8
|
+
export function resultText(result) {
|
|
9
|
+
const notes = result.notes;
|
|
10
|
+
if (result.status === "applied")
|
|
11
|
+
return [
|
|
12
|
+
"Success. Updated the following files:",
|
|
13
|
+
...result.files.map((file) => `${CODES[file.operation]} ${file.to ?? file.path}`),
|
|
14
|
+
...notes,
|
|
15
|
+
].join("\n");
|
|
16
|
+
if (result.status === "not_applied")
|
|
17
|
+
return [`Patch not applied; no files changed. ${result.error ?? ""}`.trim(), ...notes].join("\n");
|
|
18
|
+
const named = (outcome) =>
|
|
19
|
+
result.files.filter((file) => file.outcome === outcome).map((file) => file.to ?? file.path);
|
|
20
|
+
const failed = result.files.find((file) => file.outcome === "failed");
|
|
21
|
+
return [
|
|
22
|
+
`Patch partly applied. Written: ${named("written").join(", ") || "none"}. Not written: ${named("not_written").join(", ") || "none"}. Failed: ${failed?.to ?? failed?.path}: ${failed?.error}.`,
|
|
23
|
+
"Read the files named above before changing them again.",
|
|
24
|
+
...notes,
|
|
25
|
+
].join("\n");
|
|
26
|
+
}
|
|
27
|
+
const OUTPUT_SCHEMA = {
|
|
28
|
+
type: "object",
|
|
29
|
+
properties: {
|
|
30
|
+
status: { type: "string", enum: ["applied", "not_applied", "partial"] },
|
|
31
|
+
files: {
|
|
32
|
+
type: "array",
|
|
33
|
+
items: {
|
|
34
|
+
type: "object",
|
|
35
|
+
properties: {
|
|
36
|
+
path: { type: "string" },
|
|
37
|
+
operation: { type: "string", enum: ["add", "update", "delete", "move"] },
|
|
38
|
+
to: { type: "string" },
|
|
39
|
+
outcome: { type: "string", enum: ["written", "not_written", "failed"] },
|
|
40
|
+
error: { type: "string" },
|
|
41
|
+
},
|
|
42
|
+
required: ["path", "operation", "outcome"],
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
required: ["status", "files"],
|
|
47
|
+
};
|
|
48
|
+
/** Registered once, inactive until Tool Execution is effective (see tool-execution/tools.ts). */
|
|
49
|
+
export function registerApplyPatch(pi, host) {
|
|
50
|
+
pi.registerTool({
|
|
51
|
+
name: APPLY_PATCH,
|
|
52
|
+
label: APPLY_PATCH,
|
|
53
|
+
description: DESCRIPTION,
|
|
54
|
+
parameters: {
|
|
55
|
+
type: "object",
|
|
56
|
+
additionalProperties: false,
|
|
57
|
+
properties: {
|
|
58
|
+
input: { type: "string", description: "The patch, from *** Begin Patch to *** End Patch." },
|
|
59
|
+
},
|
|
60
|
+
required: ["input"],
|
|
61
|
+
},
|
|
62
|
+
// GPT-5+ models on OpenAI endpoints receive Codex's freeform grammar tool; other models a one-string function.
|
|
63
|
+
constrainedSampling: { type: "grammar", variants: { openai_lark: APPLY_PATCH_LARK_GRAMMAR } },
|
|
64
|
+
outputSchema: OUTPUT_SCHEMA,
|
|
65
|
+
exposure: "direct",
|
|
66
|
+
defaultActive: false,
|
|
67
|
+
executionMode: "sequential",
|
|
68
|
+
async execute(_id, params, _signal, _update, ctx) {
|
|
69
|
+
if (!host.effective()) throw new Error("apply_patch is available only while Freeflow Tool Execution is on.");
|
|
70
|
+
const result = await applyPatch(String(params?.input ?? ""), ctx.cwd, await host.files(ctx));
|
|
71
|
+
const written = result.files.filter((file) => file.outcome === "written");
|
|
72
|
+
await host.written(
|
|
73
|
+
written.flatMap((file) => [file.path, ...(file.to ? [file.to] : [])]),
|
|
74
|
+
ctx,
|
|
75
|
+
);
|
|
76
|
+
return {
|
|
77
|
+
content: [{ type: "text", text: resultText(result) }],
|
|
78
|
+
details: result,
|
|
79
|
+
structuredContent: { status: result.status, files: result.files },
|
|
80
|
+
...(result.status === "applied" ? {} : { isError: true }),
|
|
81
|
+
};
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
}
|
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { createWriteStream } from "node:fs";
|
|
3
|
+
import { mkdir } from "node:fs/promises";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { StringDecoder } from "node:string_decoder";
|
|
7
|
+
import { createLocalBashOperations } from "@earendil-works/pi-coding-agent";
|
|
8
|
+
export const BASH_BACKGROUND = "bash_background";
|
|
9
|
+
export const MONITOR = "monitor";
|
|
10
|
+
export const STOP_BACKGROUND = "stop_background";
|
|
11
|
+
export const NOTICE_TYPE = "freeflow-background";
|
|
12
|
+
export const MONITOR_TYPE = "freeflow-monitor";
|
|
13
|
+
/** Background commands and monitors together. */
|
|
14
|
+
export const MAX_RUNNING = 16;
|
|
15
|
+
export const MONITOR_DEFAULT_MS = 5 * 60_000;
|
|
16
|
+
export const MONITOR_MAX_MS = 30 * 60_000;
|
|
17
|
+
/** Event notices a monitor may send per minute before it is stopped; each one can start a model turn. */
|
|
18
|
+
export const MONITOR_PER_MINUTE = 20;
|
|
19
|
+
/** Lines one event notice shows; the rest stay in the output file. */
|
|
20
|
+
export const MONITOR_MAX_LINES = 40;
|
|
21
|
+
const MAX_LINE_CHARS = 1_000;
|
|
22
|
+
/** Marks harness messages, which arrive on their own schedule, as not written by the user. */
|
|
23
|
+
import { freeflowNotice } from "../host/inbound.js";
|
|
24
|
+
export { freeflowNotice };
|
|
25
|
+
const newId = (prefix) => `${prefix}${randomBytes(6).readUIntBE(0, 6).toString(36).slice(-6).padStart(6, "0")}`;
|
|
26
|
+
const shellQuote = (text) => `'${text.replace(/'/g, `'\\''`)}'`;
|
|
27
|
+
function labelFor(command, description) {
|
|
28
|
+
const text = description?.trim() || command.trim().split("\n")[0];
|
|
29
|
+
return text.length > 80 ? `${text.slice(0, 77)}...` : text;
|
|
30
|
+
}
|
|
31
|
+
function close(stream, trailer) {
|
|
32
|
+
return new Promise((resolve) => {
|
|
33
|
+
stream.once("error", () => resolve());
|
|
34
|
+
stream.end(trailer, () => resolve());
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Claude Code's background commands on Pi's shell backend: start and return at once, stream output to one file,
|
|
39
|
+
* notify the model when the command exits, stop on request or when the session ends.
|
|
40
|
+
*/
|
|
41
|
+
export class BackgroundJobs {
|
|
42
|
+
host;
|
|
43
|
+
jobs = new Map();
|
|
44
|
+
/** Launches between their check of MAX_RUNNING and their job's registration. */
|
|
45
|
+
starting = 0;
|
|
46
|
+
/** Changes when the session ends, so a launch that was still starting then does not run. */
|
|
47
|
+
generation = 0;
|
|
48
|
+
constructor(host) {
|
|
49
|
+
this.host = host;
|
|
50
|
+
}
|
|
51
|
+
running() {
|
|
52
|
+
return [...this.jobs.values()].filter((job) => job.status === "running");
|
|
53
|
+
}
|
|
54
|
+
async start(command, description, cwd, sessionId) {
|
|
55
|
+
return this.launch("command", command, description, cwd, sessionId);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Claude Code's Monitor: each line the command prints to stdout is an event for the model, batched by the host;
|
|
59
|
+
* stderr goes to the output file only. One notice when the command exits, expires, or is stopped for sending too
|
|
60
|
+
* much.
|
|
61
|
+
*/
|
|
62
|
+
async monitor(command, description, timeoutMs, cwd, sessionId) {
|
|
63
|
+
const ms = Math.min(Math.max(Math.round(timeoutMs) || MONITOR_DEFAULT_MS, 1_000), MONITOR_MAX_MS);
|
|
64
|
+
return this.launch("monitor", command, description, cwd, sessionId, ms);
|
|
65
|
+
}
|
|
66
|
+
async launch(kind, command, description, cwd, sessionId, timeoutMs) {
|
|
67
|
+
if (this.running().length + this.starting >= MAX_RUNNING)
|
|
68
|
+
throw new Error(`${MAX_RUNNING} background commands are running. Stop one with stop_background, then retry.`);
|
|
69
|
+
const prefix = kind === "monitor" ? "mon" : "bg";
|
|
70
|
+
let id = newId(prefix);
|
|
71
|
+
while (this.jobs.has(id)) id = newId(prefix);
|
|
72
|
+
const generation = this.generation;
|
|
73
|
+
const folder = join(tmpdir(), "freeflow", sessionId.replace(/[^A-Za-z0-9._-]/g, "_"), "background");
|
|
74
|
+
const outputPath = join(folder, `${id}.output`);
|
|
75
|
+
// A monitor's stderr is appended to the same file by the shell, so both writers append.
|
|
76
|
+
let stream;
|
|
77
|
+
this.starting++;
|
|
78
|
+
try {
|
|
79
|
+
await mkdir(folder, { recursive: true, mode: 0o700 });
|
|
80
|
+
stream = createWriteStream(outputPath, { flags: kind === "monitor" ? "a" : "w", mode: 0o600 });
|
|
81
|
+
await new Promise((resolve, reject) => {
|
|
82
|
+
stream.once("open", () => resolve());
|
|
83
|
+
stream.once("error", reject);
|
|
84
|
+
});
|
|
85
|
+
} finally {
|
|
86
|
+
this.starting--;
|
|
87
|
+
}
|
|
88
|
+
if (generation !== this.generation) {
|
|
89
|
+
await close(stream, "[not started: the session ended]\n");
|
|
90
|
+
throw new Error("The session ended before the command started.");
|
|
91
|
+
}
|
|
92
|
+
const controller = new AbortController();
|
|
93
|
+
let endsWithNewline = true;
|
|
94
|
+
const job = {
|
|
95
|
+
id,
|
|
96
|
+
kind,
|
|
97
|
+
label: labelFor(command, description),
|
|
98
|
+
outputPath,
|
|
99
|
+
controller,
|
|
100
|
+
status: "running",
|
|
101
|
+
events: 0,
|
|
102
|
+
done: Promise.resolve(),
|
|
103
|
+
};
|
|
104
|
+
let partial = "";
|
|
105
|
+
// One decoder per job, so a character split across two reads is decoded whole.
|
|
106
|
+
const decoder = new StringDecoder("utf8");
|
|
107
|
+
const event = (line) => {
|
|
108
|
+
job.events++;
|
|
109
|
+
const text = line.length > MAX_LINE_CHARS ? `${line.slice(0, MAX_LINE_CHARS)}... (line cut)` : line;
|
|
110
|
+
this.host.event(
|
|
111
|
+
id,
|
|
112
|
+
text,
|
|
113
|
+
(lines) => this.eventText(job, lines),
|
|
114
|
+
() => this.halt(job, "limit"),
|
|
115
|
+
);
|
|
116
|
+
};
|
|
117
|
+
const script = kind === "monitor" ? `{\n${command}\n} 2>>${shellQuote(outputPath)}` : command;
|
|
118
|
+
const run = createLocalBashOperations({ shellPath: this.host.shellPath() }).exec(script, cwd, {
|
|
119
|
+
onData: (data) => {
|
|
120
|
+
if (data.length === 0) return;
|
|
121
|
+
endsWithNewline = data[data.length - 1] === 10;
|
|
122
|
+
stream.write(data);
|
|
123
|
+
if (kind !== "monitor" || job.stopping) return;
|
|
124
|
+
const lines = (partial + decoder.write(data)).split("\n");
|
|
125
|
+
// An unfinished line keeps only what a terminal would show (the text after its last carriage return), so a
|
|
126
|
+
// progress bar redrawn with \r does not grow it without bound; the output file has everything.
|
|
127
|
+
partial = (lines.pop() ?? "")
|
|
128
|
+
.split("\r")
|
|
129
|
+
.at(-1)
|
|
130
|
+
.slice(-4 * MAX_LINE_CHARS);
|
|
131
|
+
for (const line of lines) if (line.trim() !== "") event(line.replace(/\r$/, ""));
|
|
132
|
+
},
|
|
133
|
+
signal: controller.signal,
|
|
134
|
+
});
|
|
135
|
+
const timer =
|
|
136
|
+
timeoutMs === undefined ? undefined : setTimeout(() => this.halt(job, "expired"), timeoutMs).unref?.();
|
|
137
|
+
const finish = () => {
|
|
138
|
+
if (timer) clearTimeout(timer);
|
|
139
|
+
job.ended = true;
|
|
140
|
+
if (kind !== "monitor") return;
|
|
141
|
+
partial += decoder.end();
|
|
142
|
+
if (partial.trim() !== "" && !job.stopping) event(partial);
|
|
143
|
+
partial = "";
|
|
144
|
+
if (job.stopping === "shutdown" && this.host.discardEvents) this.host.discardEvents(id);
|
|
145
|
+
else this.host.endEvents(id);
|
|
146
|
+
};
|
|
147
|
+
job.done = run.then(
|
|
148
|
+
async ({ exitCode }) => {
|
|
149
|
+
finish();
|
|
150
|
+
const code = exitCode ?? 1;
|
|
151
|
+
job.exitCode = code;
|
|
152
|
+
job.status = code === 0 ? "completed" : "failed";
|
|
153
|
+
await close(stream, `${endsWithNewline ? "" : "\n"}[exited with code ${code}]\n`);
|
|
154
|
+
this.notify(job);
|
|
155
|
+
},
|
|
156
|
+
async (error) => {
|
|
157
|
+
finish();
|
|
158
|
+
const lead = endsWithNewline ? "" : "\n";
|
|
159
|
+
if (job.stopping) {
|
|
160
|
+
job.status = "stopped";
|
|
161
|
+
const trailer =
|
|
162
|
+
job.stopping === "tool"
|
|
163
|
+
? "[stopped]"
|
|
164
|
+
: job.stopping === "shutdown"
|
|
165
|
+
? "[stopped at session end]"
|
|
166
|
+
: job.stopping === "expired"
|
|
167
|
+
? "[stopped: timeout]"
|
|
168
|
+
: "[stopped: too many events]";
|
|
169
|
+
await close(stream, `${lead}${trailer}\n`);
|
|
170
|
+
if (job.stopping === "expired" || job.stopping === "limit") this.notify(job);
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
// The command could not run (for example, the working directory is gone).
|
|
174
|
+
job.status = "failed";
|
|
175
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
176
|
+
await close(stream, `${lead}[failed to run: ${message}]\n`);
|
|
177
|
+
this.notify(job, message);
|
|
178
|
+
},
|
|
179
|
+
);
|
|
180
|
+
this.jobs.set(id, job);
|
|
181
|
+
return { id, outputPath, ...(timeoutMs === undefined ? {} : { timeoutMs }) };
|
|
182
|
+
}
|
|
183
|
+
/** Stop a monitor on its own account: its timeout passed, or it sent more than its rate limit allows. */
|
|
184
|
+
halt(job, reason) {
|
|
185
|
+
if (job.status !== "running" || job.stopping || job.ended) return;
|
|
186
|
+
job.stopping = reason;
|
|
187
|
+
job.controller.abort();
|
|
188
|
+
}
|
|
189
|
+
eventText(job, lines) {
|
|
190
|
+
const shown = lines.slice(0, MONITOR_MAX_LINES);
|
|
191
|
+
const more = lines.length - shown.length;
|
|
192
|
+
return freeflowNotice(
|
|
193
|
+
"monitor",
|
|
194
|
+
[
|
|
195
|
+
`Monitor ${job.id} (${job.label}):`,
|
|
196
|
+
...shown,
|
|
197
|
+
...(more > 0 ? [`... ${more} more lines in ${job.outputPath}`] : []),
|
|
198
|
+
].join("\n"),
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
async stop(id) {
|
|
202
|
+
const job = this.jobs.get(id);
|
|
203
|
+
if (!job) {
|
|
204
|
+
const running = this.running().map((each) => each.id);
|
|
205
|
+
throw new Error(
|
|
206
|
+
`No background command ${id} in this session. Running: ${running.length > 0 ? running.join(", ") : "none"}.`,
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
if (job.status === "running" && !job.stopping) {
|
|
210
|
+
job.stopping = "tool";
|
|
211
|
+
job.controller.abort();
|
|
212
|
+
}
|
|
213
|
+
await job.done;
|
|
214
|
+
if (job.stopping === "tool" && job.status === "stopped")
|
|
215
|
+
return { text: `Stopped ${id}.`, structured: { id, status: "stopped" } };
|
|
216
|
+
const exited =
|
|
217
|
+
job.exitCode === undefined
|
|
218
|
+
? `${id} already ${job.status === "stopped" ? "stopped" : "failed to run"}. Output: ${job.outputPath}.`
|
|
219
|
+
: `${id} already exited with code ${job.exitCode}. Output: ${job.outputPath}.`;
|
|
220
|
+
return {
|
|
221
|
+
text: exited,
|
|
222
|
+
structured: { id, status: job.status, ...(job.exitCode === undefined ? {} : { exitCode: job.exitCode }) },
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
/** Session end: stop every running job, wait for the processes to exit, forget them. No notice is sent. */
|
|
226
|
+
async stopAll() {
|
|
227
|
+
this.generation++;
|
|
228
|
+
const running = this.running();
|
|
229
|
+
for (const job of running) {
|
|
230
|
+
job.stopping = "shutdown";
|
|
231
|
+
job.controller.abort();
|
|
232
|
+
}
|
|
233
|
+
await Promise.all(running.map((job) => job.done));
|
|
234
|
+
this.jobs = new Map();
|
|
235
|
+
}
|
|
236
|
+
/** After compaction the model may no longer see which commands it started; restate the running ones once. */
|
|
237
|
+
restate() {
|
|
238
|
+
const running = this.running();
|
|
239
|
+
if (running.length === 0) return;
|
|
240
|
+
const list = running
|
|
241
|
+
.map((job) => `${job.kind === "monitor" ? "monitor " : ""}${job.id} (${job.label}), output ${job.outputPath}`)
|
|
242
|
+
.join("; ");
|
|
243
|
+
this.host.send(
|
|
244
|
+
freeflowNotice(
|
|
245
|
+
"background",
|
|
246
|
+
`Still running: ${list}. Do not start them again; stop one with stop_background before restarting it.`,
|
|
247
|
+
),
|
|
248
|
+
{ running: running.map((job) => ({ id: job.id, outputPath: job.outputPath })) },
|
|
249
|
+
false,
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
notify(job, runError) {
|
|
253
|
+
if (job.stopping === "tool" || job.stopping === "shutdown") return;
|
|
254
|
+
const details = {
|
|
255
|
+
id: job.id,
|
|
256
|
+
...(job.kind === "monitor" ? { kind: job.kind } : {}),
|
|
257
|
+
status: job.status,
|
|
258
|
+
...(job.exitCode === undefined ? {} : { exitCode: job.exitCode }),
|
|
259
|
+
...(job.kind === "monitor" ? { events: job.events } : {}),
|
|
260
|
+
outputPath: job.outputPath,
|
|
261
|
+
};
|
|
262
|
+
if (job.kind === "monitor") {
|
|
263
|
+
const events = `${job.events} ${job.events === 1 ? "event" : "events"}`;
|
|
264
|
+
const outcome =
|
|
265
|
+
job.stopping === "expired"
|
|
266
|
+
? `reached its timeout after ${events} and was stopped. Start it again to keep watching.`
|
|
267
|
+
: job.stopping === "limit"
|
|
268
|
+
? `was stopped after ${events}: it sent more than ${MONITOR_PER_MINUTE} notices in a minute. Filter its output to the lines you would act on, then start it again.`
|
|
269
|
+
: runError !== undefined
|
|
270
|
+
? `failed to run: ${runError}.`
|
|
271
|
+
: `exited with code ${job.exitCode} after ${events}.`;
|
|
272
|
+
this.host.send(
|
|
273
|
+
freeflowNotice("monitor", `Monitor ${job.id} (${job.label}) ${outcome} Output: ${job.outputPath}.`),
|
|
274
|
+
details,
|
|
275
|
+
true,
|
|
276
|
+
);
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
const outcome =
|
|
280
|
+
runError !== undefined
|
|
281
|
+
? `failed to run: ${runError}`
|
|
282
|
+
: job.status === "completed"
|
|
283
|
+
? "completed"
|
|
284
|
+
: `failed with exit code ${job.exitCode}`;
|
|
285
|
+
this.host.send(
|
|
286
|
+
freeflowNotice(
|
|
287
|
+
"background",
|
|
288
|
+
`Background command ${job.id} (${job.label}) ${outcome}. Output: ${job.outputPath}.`,
|
|
289
|
+
),
|
|
290
|
+
details,
|
|
291
|
+
true,
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
const START_DESCRIPTION = [
|
|
296
|
+
"Run a shell command in the background; it returns at once with an ID and an output file. Use it for servers, watchers, and builds, test runs or installs expected to take over two minutes; run shorter commands with bash.",
|
|
297
|
+
"You are notified once, when the command exits; if you are idle then, the notice starts a new turn. Meanwhile do other work, and read the output file only for output so far; do not poll it.",
|
|
298
|
+
"To be notified when something becomes ready, run a command that exits when it is: until grep -qE 'Listening|Error|Traceback' server.log; do sleep 1; done. Match the failure lines too: a check that waits only for success stays silent when the process crashes.",
|
|
299
|
+
"Commands stop when the session ends; their output files stay readable.",
|
|
300
|
+
].join("\n\n");
|
|
301
|
+
const MONITOR_DESCRIPTION = [
|
|
302
|
+
"Use monitor to act on a running command's lines as they appear: errors in a log while you test, failures in a long test run. For one signal (a server is ready, a build finished), use bash_background with a command that exits when the condition holds.",
|
|
303
|
+
"Each line the command prints to stdout reaches you as a notice (lines within 200 ms join one notice); stderr goes only to the output file. You also get one notice when the command exits or when timeout_ms passes (default 5 minutes, at most 30); start it again to keep watching. A notice that arrives while you are idle starts a model turn, so make the command print only lines you would act on; one that prints too much is stopped.",
|
|
304
|
+
"Every stage of a pipe must flush each line (grep --line-buffered, awk with fflush()), or lines wait in a buffer; head cannot.",
|
|
305
|
+
"Silence is not success: match every outcome you would act on, failures as well as success, for example tail -f app.log | grep -E --line-buffered 'ready|ERROR|Traceback|FAILED'.",
|
|
306
|
+
].join("\n\n");
|
|
307
|
+
const STOP_DESCRIPTION =
|
|
308
|
+
"Stop a background command or monitor once neither you nor the user needs it, such as a server after the checks that used it. Returns after it has exited.";
|
|
309
|
+
/** Registered once, inactive until Tool Execution is effective (see tool-execution/tools.ts). */
|
|
310
|
+
export function registerBackgroundTools(pi, jobs, host) {
|
|
311
|
+
const guard = () => {
|
|
312
|
+
if (!host.effective())
|
|
313
|
+
throw new Error("Background commands are available only while Freeflow Tool Execution is on.");
|
|
314
|
+
};
|
|
315
|
+
pi.registerTool({
|
|
316
|
+
name: BASH_BACKGROUND,
|
|
317
|
+
label: BASH_BACKGROUND,
|
|
318
|
+
description: START_DESCRIPTION,
|
|
319
|
+
parameters: {
|
|
320
|
+
type: "object",
|
|
321
|
+
additionalProperties: false,
|
|
322
|
+
properties: {
|
|
323
|
+
command: { type: "string", description: "Shell command to run" },
|
|
324
|
+
description: { type: "string", description: "A few words saying what the command does" },
|
|
325
|
+
},
|
|
326
|
+
required: ["command"],
|
|
327
|
+
},
|
|
328
|
+
outputSchema: {
|
|
329
|
+
type: "object",
|
|
330
|
+
properties: { id: { type: "string" }, outputPath: { type: "string" } },
|
|
331
|
+
required: ["id", "outputPath"],
|
|
332
|
+
},
|
|
333
|
+
exposure: "direct",
|
|
334
|
+
defaultActive: false,
|
|
335
|
+
async execute(_id, params, _signal, _u, ctx) {
|
|
336
|
+
guard();
|
|
337
|
+
const { id, outputPath } = await jobs.start(
|
|
338
|
+
String(params?.command ?? ""),
|
|
339
|
+
params?.description,
|
|
340
|
+
ctx.cwd,
|
|
341
|
+
ctx.sessionManager?.getSessionId?.() ?? "session",
|
|
342
|
+
);
|
|
343
|
+
return {
|
|
344
|
+
content: [
|
|
345
|
+
{
|
|
346
|
+
type: "text",
|
|
347
|
+
text: `Command running in background with ID ${id}. Output: ${outputPath}. You will be notified when it exits. Read that file for output so far.`,
|
|
348
|
+
},
|
|
349
|
+
],
|
|
350
|
+
details: { id, outputPath },
|
|
351
|
+
structuredContent: { id, outputPath },
|
|
352
|
+
};
|
|
353
|
+
},
|
|
354
|
+
});
|
|
355
|
+
pi.registerTool({
|
|
356
|
+
name: MONITOR,
|
|
357
|
+
label: MONITOR,
|
|
358
|
+
description: MONITOR_DESCRIPTION,
|
|
359
|
+
parameters: {
|
|
360
|
+
type: "object",
|
|
361
|
+
additionalProperties: false,
|
|
362
|
+
properties: {
|
|
363
|
+
command: { type: "string", description: "Shell command whose stdout lines are the events" },
|
|
364
|
+
description: { type: "string", description: "A few words saying what is watched; shown with every event" },
|
|
365
|
+
timeout_ms: {
|
|
366
|
+
type: "number",
|
|
367
|
+
minimum: 1_000,
|
|
368
|
+
maximum: MONITOR_MAX_MS,
|
|
369
|
+
description: `Stop watching after this long (default ${MONITOR_DEFAULT_MS}, at most ${MONITOR_MAX_MS})`,
|
|
370
|
+
},
|
|
371
|
+
},
|
|
372
|
+
required: ["command", "description"],
|
|
373
|
+
},
|
|
374
|
+
outputSchema: {
|
|
375
|
+
type: "object",
|
|
376
|
+
properties: { id: { type: "string" }, outputPath: { type: "string" }, timeoutMs: { type: "number" } },
|
|
377
|
+
required: ["id", "outputPath", "timeoutMs"],
|
|
378
|
+
},
|
|
379
|
+
exposure: "direct",
|
|
380
|
+
defaultActive: false,
|
|
381
|
+
async execute(_id, params, _signal, _u, ctx) {
|
|
382
|
+
guard();
|
|
383
|
+
const started = await jobs.monitor(
|
|
384
|
+
String(params?.command ?? ""),
|
|
385
|
+
String(params?.description ?? ""),
|
|
386
|
+
Number(params?.timeout_ms ?? MONITOR_DEFAULT_MS),
|
|
387
|
+
ctx.cwd,
|
|
388
|
+
ctx.sessionManager?.getSessionId?.() ?? "session",
|
|
389
|
+
);
|
|
390
|
+
const minutes = Math.round(((started.timeoutMs ?? MONITOR_DEFAULT_MS) / 60_000) * 10) / 10;
|
|
391
|
+
return {
|
|
392
|
+
content: [
|
|
393
|
+
{
|
|
394
|
+
type: "text",
|
|
395
|
+
text: `Monitor ${started.id} started. Each line it prints reaches you as a notice; it stops when the command exits, after ${minutes} min, or with stop_background. When you tell the user you are watching, say it stops after ${minutes} min unless restarted. Output: ${started.outputPath}.`,
|
|
396
|
+
},
|
|
397
|
+
],
|
|
398
|
+
details: started,
|
|
399
|
+
structuredContent: started,
|
|
400
|
+
};
|
|
401
|
+
},
|
|
402
|
+
});
|
|
403
|
+
pi.registerTool({
|
|
404
|
+
name: STOP_BACKGROUND,
|
|
405
|
+
label: STOP_BACKGROUND,
|
|
406
|
+
description: STOP_DESCRIPTION,
|
|
407
|
+
parameters: {
|
|
408
|
+
type: "object",
|
|
409
|
+
additionalProperties: false,
|
|
410
|
+
properties: { id: { type: "string", description: "The ID bash_background or monitor returned" } },
|
|
411
|
+
required: ["id"],
|
|
412
|
+
},
|
|
413
|
+
outputSchema: {
|
|
414
|
+
type: "object",
|
|
415
|
+
properties: {
|
|
416
|
+
id: { type: "string" },
|
|
417
|
+
status: { type: "string", enum: ["stopped", "completed", "failed"] },
|
|
418
|
+
exitCode: { type: "number" },
|
|
419
|
+
},
|
|
420
|
+
required: ["id", "status"],
|
|
421
|
+
},
|
|
422
|
+
exposure: "direct",
|
|
423
|
+
defaultActive: false,
|
|
424
|
+
async execute(_id, params) {
|
|
425
|
+
guard();
|
|
426
|
+
const { text, structured } = await jobs.stop(String(params?.id ?? ""));
|
|
427
|
+
return { content: [{ type: "text", text }], details: structured, structuredContent: structured };
|
|
428
|
+
},
|
|
429
|
+
});
|
|
430
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Two shell habits that lose work once background commands exist: a trailing `&` leaves a process running after the
|
|
2
|
+
// call with no notice and no saved output, and a long foreground sleep holds the session while waiting on something.
|
|
3
|
+
/** Seconds at or above which a foreground sleep is refused. */
|
|
4
|
+
export const LONGEST_FOREGROUND_SLEEP_S = 10;
|
|
5
|
+
const UNIT_SECONDS = { "": 1, s: 1, m: 60, h: 3600, d: 86400 };
|
|
6
|
+
function sleepSeconds(command) {
|
|
7
|
+
let longest = 0;
|
|
8
|
+
// A sleep that starts a command: at the start, or after ; & | && || ( { then do.
|
|
9
|
+
for (const match of command.matchAll(/(?:^|[;&|({]|\bthen\b|\bdo\b)\s*sleep\s+(\d+(?:\.\d+)?)([smhd]?)\b/g))
|
|
10
|
+
longest = Math.max(longest, Number(match[1]) * UNIT_SECONDS[match[2]]);
|
|
11
|
+
return longest;
|
|
12
|
+
}
|
|
13
|
+
/** Why a bash command should run through bash_background instead, as one next action; undefined when it need not. */
|
|
14
|
+
export function backgroundRefusal(command) {
|
|
15
|
+
if (typeof command !== "string") return undefined;
|
|
16
|
+
const trimmed = command.replace(/\s+$/, "");
|
|
17
|
+
if (/(^|[^&])&$/.test(trimmed))
|
|
18
|
+
return "A command ending in & keeps running after this call, with no exit notice and no saved output. Run it with bash_background instead, without the &.";
|
|
19
|
+
const seconds = sleepSeconds(command);
|
|
20
|
+
if (seconds >= LONGEST_FOREGROUND_SLEEP_S)
|
|
21
|
+
return `A foreground sleep of ${seconds} s holds the session. To wait for a condition, run a loop that exits when it holds, such as until <check>; do sleep 1; done, with bash_background; you will be notified when it exits.`;
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export const DEFAULT_TOOL_EXECUTION_CONFIG = Object.freeze({
|
|
2
|
+
enabled: true,
|
|
3
|
+
guidanceNotices: true,
|
|
4
|
+
});
|
|
5
|
+
/** The keys `toolExecution` acts on; the settings test checks each has a /freeflow settings row. */
|
|
6
|
+
export const TOOL_EXECUTION_KEYS = ["enabled", "guidanceNotices"];
|
|
7
|
+
// Settings of the retired v2 runtime (.deprecated/tool-execution-v2). Existing configuration files keep loading;
|
|
8
|
+
// the values no longer do anything.
|
|
9
|
+
const RETIRED_KEYS = ["capture", "programs", "workspace", "discovery", "adapters", "accounting"];
|
|
10
|
+
function record(value) {
|
|
11
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
12
|
+
}
|
|
13
|
+
export function validateToolExecutionConfig(value) {
|
|
14
|
+
if (!record(value)) return "toolExecution must be an object";
|
|
15
|
+
const unknown = Object.keys(value).find((key) => !TOOL_EXECUTION_KEYS.includes(key) && !RETIRED_KEYS.includes(key));
|
|
16
|
+
if (unknown) return `toolExecution has unsupported key: ${unknown}`;
|
|
17
|
+
if (value.enabled !== undefined && typeof value.enabled !== "boolean")
|
|
18
|
+
return "toolExecution.enabled must be a boolean";
|
|
19
|
+
if (value.guidanceNotices !== undefined && typeof value.guidanceNotices !== "boolean")
|
|
20
|
+
return "toolExecution.guidanceNotices must be a boolean";
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
export function resolveToolExecutionConfig(repository, local, freeflowEnabled) {
|
|
24
|
+
const repositoryTool = record(repository.toolExecution) ? repository.toolExecution : {};
|
|
25
|
+
const localTool = record(local.toolExecution) ? local.toolExecution : {};
|
|
26
|
+
const setting = Object.hasOwn(localTool, "enabled") ? localTool.enabled : repositoryTool.enabled;
|
|
27
|
+
const enabled = setting === undefined ? DEFAULT_TOOL_EXECUTION_CONFIG.enabled : setting === true;
|
|
28
|
+
const notices = Object.hasOwn(localTool, "guidanceNotices")
|
|
29
|
+
? localTool.guidanceNotices
|
|
30
|
+
: repositoryTool.guidanceNotices;
|
|
31
|
+
const guidanceNotices = notices === undefined ? DEFAULT_TOOL_EXECUTION_CONFIG.guidanceNotices : notices === true;
|
|
32
|
+
return { enabled, guidanceNotices, effective: freeflowEnabled && enabled };
|
|
33
|
+
}
|