@hassangameryt/freeflow 0.8.0 → 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/README.md +96 -149
- package/capabilities/cognitive-routing/SKILL.md +21 -129
- package/capabilities/cognitive-routing/references/coordinator.md +163 -0
- package/capabilities/cognitive-routing/references/worker.md +38 -0
- package/capabilities/compaction/SKILL.md +3 -3
- package/capabilities/compaction/references/summary-format.md +26 -15
- package/capabilities/messaging/SKILL.md +49 -0
- package/capabilities/task-memory/SKILL.md +87 -0
- package/command-surface.json +6 -54
- 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 +69 -77
- package/package.json +25 -8
- 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 +35 -11
- package/pi-extension/dist/cognitive-routing/bootstrap.js +90 -0
- package/pi-extension/dist/cognitive-routing/config.js +3 -4
- package/pi-extension/dist/cognitive-routing/event-store.js +4 -1
- package/pi-extension/dist/cognitive-routing/gate.js +20 -5
- package/pi-extension/dist/cognitive-routing/handlers.js +15 -13
- package/pi-extension/dist/cognitive-routing/model-control.js +17 -8
- package/pi-extension/dist/cognitive-routing/projection.js +23 -6
- package/pi-extension/dist/cognitive-routing/provenance.js +3 -1
- package/pi-extension/dist/cognitive-routing/render.js +15 -15
- package/pi-extension/dist/cognitive-routing/runtime.js +4 -2
- package/pi-extension/dist/cognitive-routing/schemas.js +4 -4
- package/pi-extension/dist/cognitive-routing/session.js +3 -1
- package/pi-extension/dist/cognitive-routing/sources.js +29 -4
- package/pi-extension/dist/cognitive-routing/state.js +18 -8
- package/pi-extension/dist/cognitive-routing/status.js +4 -3
- package/pi-extension/dist/cognitive-routing/tools.js +29 -15
- package/pi-extension/dist/cognitive-routing/types.js +10 -1
- package/pi-extension/dist/compaction/carry.js +36 -22
- package/pi-extension/dist/compaction/config.js +4 -2
- package/pi-extension/dist/compaction/controller.js +52 -29
- package/pi-extension/dist/compaction/harness.js +19 -3
- package/pi-extension/dist/compaction/results.js +14 -13
- package/pi-extension/dist/compaction/tool.js +6 -6
- package/pi-extension/dist/host/catalog.js +20 -35
- package/pi-extension/dist/host/config-validation.js +233 -0
- package/pi-extension/dist/host/config.js +78 -91
- package/pi-extension/dist/host/inbound.js +142 -0
- package/pi-extension/dist/host/projection-tags.js +1 -1
- package/pi-extension/dist/host/prompts.js +24 -13
- package/pi-extension/dist/host/read-only-session.js +6 -1
- package/pi-extension/dist/host/request-history.js +7 -3
- package/pi-extension/dist/host/rows.js +25 -0
- package/pi-extension/dist/host/runtime-state.js +17 -2
- package/pi-extension/dist/host/section-keeper.js +85 -0
- package/pi-extension/dist/host/settings/freeflow-command.js +846 -93
- package/pi-extension/dist/host/settings/settings-view.js +27 -8
- package/pi-extension/dist/host/status.js +19 -5
- package/pi-extension/dist/index.js +638 -72
- 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/keepalive.js +16 -4
- 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/tool.js +1 -1
- package/pi-extension/dist/tool-execution/background.js +220 -28
- package/pi-extension/dist/tool-execution/config.js +14 -3
- package/pi-extension/dist/tool-execution/file-tracking.js +24 -4
- package/pi-extension/dist/tool-execution/guidance-notices.js +124 -0
- package/pi-extension/dist/tool-execution/skill-reads.js +143 -0
- package/pi-extension/dist/tool-execution/tools.js +2 -2
- package/runtime/prompts/cognitive-routing.md +4 -4
- package/runtime/prompts/core.md +24 -14
- package/runtime/prompts/interaction-contract.md +7 -4
- package/runtime/prompts/tool-execution.md +3 -16
- package/runtime/prompts/working-method.md +16 -99
- 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 +10 -27
- 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 +119 -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} +235 -265
- 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 +159 -18
- package/skills/{evaluate-skill → evaluate-behavior}/scripts/pi-command-tool.mjs +21 -6
- 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 -104
- package/skills/execute-work/references/code-practices.md +27 -51
- 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 +89 -65
- 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 +68 -17
- package/skills/track-work/SKILL.md +52 -20
- 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 +36 -18
- package/skills/track-work/scripts/lib/document.mjs +17 -13
- package/skills/track-work/scripts/lib/format.mjs +2 -0
- 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 +46 -15
- package/skills/track-work/scripts/lib/views.mjs +50 -1
- package/skills/track-work/scripts/working-record.mjs +232 -13
- package/skills/verify-work/SKILL.md +45 -37
- 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 +25 -6
- package/skills/verify-work/scripts/test-changes.mjs +246 -0
- package/skills/write-docs/SKILL.md +113 -25
- package/skills/write-docs/references/documentation-shapes.md +68 -3
- 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 +96 -6
- package/skills/write-docs/scripts/plan-lint.mjs +123 -0
- package/skills/write-skill/SKILL.md +92 -52
- 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/skill-author.mjs +197 -3
- package/skills/write-skill/scripts/skill-author.mjs +51 -8
- package/capabilities/cognitive-routing/references/both-mode.md +0 -36
- package/capabilities/cognitive-routing/references/executor-mode.md +0 -48
- package/capabilities/cognitive-routing/references/helper-mode.md +0 -47
- package/plugin.json +0 -33
- package/skills/action-selection/SKILL.md +0 -92
- package/skills/action-selection/references/trajectory-stalls.md +0 -73
- package/skills/bypass/SKILL.md +0 -153
- package/skills/commit-work/SKILL.md +0 -93
- package/skills/commit-work/references/git-state-edges.md +0 -94
- package/skills/commit-work/references/simple-push.md +0 -94
- package/skills/commit-work/references/staging-decisions.md +0 -106
- package/skills/decision-gate/SKILL.md +0 -87
- package/skills/design-for-depth/SKILL.md +0 -108
- package/skills/design-for-depth/references/design-pressure-signals.md +0 -84
- package/skills/design-for-depth/references/implementation-decisions.md +0 -76
- package/skills/design-for-depth/references/interface-design-loop.md +0 -105
- package/skills/design-for-depth/references/module-and-dependency-design.md +0 -117
- package/skills/design-for-depth/references/state-and-failure-boundaries.md +0 -125
- package/skills/diagnose-failure/SKILL.md +0 -140
- package/skills/diagnose-failure/references/diagnostic-loop-catalog.md +0 -50
- package/skills/diagnose-failure/references/flaky-and-performance-diagnosis.md +0 -51
- package/skills/discuss/SKILL.md +0 -140
- package/skills/evaluate-skill/SKILL.md +0 -188
- package/skills/evaluate-skill/references/evaluation-definition-schema.md +0 -415
- package/skills/evaluate-skill/references/evaluation-design.md +0 -112
- package/skills/evaluate-skill/references/execution-and-evidence.md +0 -111
- package/skills/evaluate-skill/references/review-and-revision.md +0 -94
- package/skills/evaluate-skill/scripts/lib/definitions.mjs +0 -448
- 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/review.mjs +0 -196
- package/skills/evaluate-skill/scripts/lib/runner.mjs +0 -405
- package/skills/evaluate-skill/scripts/lib/trials.mjs +0 -103
- package/skills/evaluate-skill/scripts/pi-guard.mjs +0 -181
- package/skills/evaluate-skill/scripts/skill-eval.mjs +0 -182
- package/skills/execute-work/references/execute-work-edges.md +0 -78
- 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 -143
- 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 -67
- package/skills/setup-freeflow/references/activation-contract.md +0 -103
- 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 -51
- package/skills/verify-work/references/integration-evidence.md +0 -49
- package/skills/verify-work/references/performance-evidence.md +0 -59
- package/skills/workflow/SKILL.md +0 -171
- package/skills/workflow/references/domain-skill-composition.md +0 -45
- 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 -106
- package/skills/write-skill/references/agent-first-instructions.md +0 -133
- package/skills/write-skill/references/progressive-disclosure.md +0 -129
- package/skills/write-skill/references/skill-dependency-graphs.md +0 -192
- package/skills/write-spec/SKILL.md +0 -85
- package/skills/write-spec/references/artifact-standards.md +0 -86
- package/skills/write-spec/references/decision-records.md +0 -80
- package/skills/write-spec/references/spec-shapes.md +0 -143
- /package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/evidence.mjs +0 -0
- /package/skills/{evaluate-skill → evaluate-behavior}/scripts/pi-observer.mjs +0 -0
|
@@ -2,27 +2,34 @@ import { ROUTING_TOOLS } from "./types.js";
|
|
|
2
2
|
import { ROUTING_SCHEMAS, explainRoutingArguments, matches } from "./schemas.js";
|
|
3
3
|
export { ROUTING_SCHEMAS } from "./schemas.js";
|
|
4
4
|
import { renderRoutingCall, renderRoutingResult } from "./render.js";
|
|
5
|
+
/** What Pi shows for each tool, as the other Freeflow tools name theirs. */
|
|
6
|
+
const ROUTING_LABELS = {
|
|
7
|
+
delegate_work: "Delegate",
|
|
8
|
+
return_work: "Return",
|
|
9
|
+
routing_unit: "Routing unit",
|
|
10
|
+
select_evidence: "Evidence",
|
|
11
|
+
};
|
|
5
12
|
const descriptions = {
|
|
6
|
-
|
|
13
|
+
delegate_work:
|
|
7
14
|
"Save a supported assignment contract and request enabled worker execution. Explicit replace supersedes a quiescent outstanding assignment in the same unit.",
|
|
8
|
-
|
|
15
|
+
return_work:
|
|
9
16
|
"Save or revise the actual assignment report, save an attached recovery supplement, or retry the current saved return without resubmitting text. Returning does not accept or close the unit.",
|
|
10
|
-
|
|
17
|
+
routing_unit:
|
|
11
18
|
"Inspect current responsibility, paginated work history, or exact saved communication. Coordinator can restore an assessment, request or cancel attached evidence recovery, or close a unit without clearing history.",
|
|
12
|
-
|
|
19
|
+
select_evidence:
|
|
13
20
|
"Inspect current selection and scoped candidates when needed, then add or remove exposed worker task-evidence refs. Strict shapes: inspect uses operation plus optional scope/cursor; add uses operation plus eligible refs only; remove uses operation plus currently selected or unresolved refs plus required reason. Reason is remove-only. Only tool results are evidence; put anything you would say in the report.",
|
|
14
21
|
};
|
|
15
22
|
const guidance = {
|
|
16
|
-
|
|
17
|
-
"
|
|
23
|
+
delegate_work: [
|
|
24
|
+
"delegate_work is Coordinator-only under Automatic control. Put the contract in the input. Choose worker when both Helper and Executor are enabled; the sole worker is inferred otherwise. Use replace only for explicit same-unit replacement; do not supply IDs.",
|
|
18
25
|
],
|
|
19
|
-
|
|
20
|
-
"
|
|
26
|
+
return_work: [
|
|
27
|
+
"return_work submit saves/revises the assignment report; supplement saves separate recovery communication; retry completes the current unchanged saved return without text. Stop ordinary task or recovery-read work after its communication is saved. Keep the handoff last; do not batch it with new task tools.",
|
|
21
28
|
],
|
|
22
|
-
|
|
23
|
-
"
|
|
29
|
+
routing_unit: [
|
|
30
|
+
"routing_unit inspect defaults to current state; view history returns work refs and detail reads saved communication. Coordinator recover requests bounded evidence work against the current returned assessment; cancel-recovery ends only that recovery; assess restores a suspended evidence obligation; close changes work status without clearing communication or completing a Working Record task.",
|
|
24
31
|
],
|
|
25
|
-
|
|
32
|
+
select_evidence: [
|
|
26
33
|
"Use exactly one shape: inspect {operation, scope?, cursor?}; add {operation, refs} with no reason; remove {operation, refs, reason}. Reason is required only for remove and invalid for add/inspect. Add exact eligible visible refs directly when identity and eligibility are clear; inspect when identity, eligibility, representation or selection state is unclear. Never add refs marked not offered for new evidence selection. For remove, use currently selected or unresolved refs; a non-selectable ref may be named only to clear its unresolved request and requires a reason. Only tool results are evidence: select the result bodies behind your claims, not calls or your own messages, and say everything else in the report. Correct or explicitly withdraw unresolved items with a limitation. Known previously exposed bodies resolve automatically; target-representation gaps are not successful delivery.",
|
|
27
34
|
],
|
|
28
35
|
};
|
|
@@ -30,11 +37,14 @@ export function registerRoutingTools(pi, runtime) {
|
|
|
30
37
|
for (const name of ROUTING_TOOLS) {
|
|
31
38
|
const definition = {
|
|
32
39
|
name,
|
|
33
|
-
label: name
|
|
40
|
+
label: ROUTING_LABELS[name],
|
|
34
41
|
description: descriptions[name],
|
|
35
42
|
parameters: ROUTING_SCHEMAS[name],
|
|
36
43
|
// Handoffs and evidence selection are assistant-issued boundaries: codemode scripts must not call them.
|
|
37
44
|
exposure: "model-only",
|
|
45
|
+
// Declared only while routing is effective: hidden until applyRoutingToolVisibility adds them, like Freeflow's
|
|
46
|
+
// other tools, so no loadout before Freeflow's first update carries them (Pi 1.4.0 activates tools by default).
|
|
47
|
+
defaultActive: false,
|
|
38
48
|
executionMode: "sequential",
|
|
39
49
|
promptGuidelines: guidance[name],
|
|
40
50
|
renderCall: (args, _theme, context) => renderRoutingCall(name, args, context),
|
|
@@ -47,14 +57,18 @@ export function registerRoutingTools(pi, runtime) {
|
|
|
47
57
|
pi.registerTool(definition);
|
|
48
58
|
}
|
|
49
59
|
}
|
|
50
|
-
export function applyRoutingToolVisibility(pi, _runtime,
|
|
60
|
+
export function applyRoutingToolVisibility(pi, _runtime, available = true) {
|
|
51
61
|
if (!pi.getActiveTools || !pi.setActiveTools) return;
|
|
52
62
|
const current = pi.getActiveTools();
|
|
63
|
+
// Declared only while routing is effective (Task 003 D-002); turning it on or off costs one cache write (D-001).
|
|
53
64
|
// Missing routing tools are appended and present ones keep their place, so tools another extension adds later
|
|
54
65
|
// (tool_search, for one) are never reordered around them.
|
|
55
66
|
const kept = current.filter(
|
|
56
|
-
(name) =>
|
|
67
|
+
(name) =>
|
|
68
|
+
name !== "freeflow_switch_profile" &&
|
|
69
|
+
name !== "freeflow_cognitive_routing_history" &&
|
|
70
|
+
(available || !ROUTING_TOOLS.includes(name)),
|
|
57
71
|
);
|
|
58
|
-
const next = [...kept, ...ROUTING_TOOLS.filter((name) => !kept.includes(name))];
|
|
72
|
+
const next = available ? [...kept, ...ROUTING_TOOLS.filter((name) => !kept.includes(name))] : kept;
|
|
59
73
|
if (JSON.stringify(next) !== JSON.stringify(current)) pi.setActiveTools(next);
|
|
60
74
|
}
|
|
@@ -39,4 +39,13 @@ export function idFor(ref) {
|
|
|
39
39
|
return /^ctx:([^\s:#]+)$/.exec(ref)?.[1];
|
|
40
40
|
}
|
|
41
41
|
export { canonical };
|
|
42
|
-
export const ROUTING_TOOLS = ["
|
|
42
|
+
export const ROUTING_TOOLS = ["delegate_work", "return_work", "routing_unit", "select_evidence"];
|
|
43
|
+
/** The routing tools' names before Task 003's rename; sessions saved earlier still hold them in their history. */
|
|
44
|
+
export const LEGACY_ROUTING_TOOLS = {
|
|
45
|
+
freeflow_delegate: "delegate_work",
|
|
46
|
+
freeflow_return: "return_work",
|
|
47
|
+
freeflow_unit: "routing_unit",
|
|
48
|
+
freeflow_project: "select_evidence",
|
|
49
|
+
};
|
|
50
|
+
/** A routing tool's current name, for a name read from saved history. */
|
|
51
|
+
export const routingToolName = (name) => LEGACY_ROUTING_TOOLS[name] ?? name;
|
|
@@ -69,14 +69,14 @@ const text = (content) =>
|
|
|
69
69
|
? content.map((part) => (part?.type === "text" ? part.text : "")).join("")
|
|
70
70
|
: "";
|
|
71
71
|
/**
|
|
72
|
-
* The latest user messages on the branch, oldest first, within the carried limit.
|
|
73
|
-
*
|
|
72
|
+
* The latest user messages on the branch, oldest first, within the carried limit. Freeflow's own /freeflow compact
|
|
73
|
+
* request is a custom message, not a user message, so it is never among them.
|
|
74
74
|
*/
|
|
75
|
-
export function latestUserMessages(branch
|
|
75
|
+
export function latestUserMessages(branch) {
|
|
76
76
|
const messages = branch
|
|
77
77
|
.filter((entry) => entry?.type === "message" && entry.message?.role === "user")
|
|
78
78
|
.map((entry) => text(entry.message.content).trim())
|
|
79
|
-
.filter(
|
|
79
|
+
.filter(Boolean)
|
|
80
80
|
.slice(-USER_MESSAGE_LIMIT.count);
|
|
81
81
|
const kept = [];
|
|
82
82
|
let left = USER_MESSAGE_LIMIT.tokens;
|
|
@@ -90,28 +90,43 @@ export function latestUserMessages(branch, skip = () => false) {
|
|
|
90
90
|
}
|
|
91
91
|
/**
|
|
92
92
|
* Text with each line's number in front, as `nl -ba` prints it, so a copy can be cited by file line without being read
|
|
93
|
-
* again. Pi's note after a partial read ("[Showing
|
|
93
|
+
* again. A final newline ends the last line rather than starting another. Pi's note after a partial read ("[Showing
|
|
94
|
+
* lines …]", "[N more lines in file …]") stays unnumbered; a whole file has no such note (`readNote: false`).
|
|
94
95
|
*/
|
|
95
|
-
export function numberLines(text, first) {
|
|
96
|
-
const note = /\n\n\[(?:Showing lines \d+-\d+ of \d+|\d+ more lines in file)[^\n]*\]$/.exec(text);
|
|
97
|
-
const
|
|
98
|
-
const
|
|
96
|
+
export function numberLines(text, first, { readNote = true } = {}) {
|
|
97
|
+
const note = readNote ? /\n\n\[(?:Showing lines \d+-\d+ of \d+|\d+ more lines in file)[^\n]*\]$/.exec(text) : null;
|
|
98
|
+
const whole = note ? text.slice(0, note.index) : text;
|
|
99
|
+
const ending = whole.endsWith("\n") ? "\n" : "";
|
|
100
|
+
const lines = (ending ? whole.slice(0, -1) : whole).split("\n");
|
|
99
101
|
const width = String(first + lines.length - 1).length;
|
|
100
|
-
|
|
102
|
+
const numbered = lines.map((line, i) => `${String(first + i).padStart(width)}\t${line}`).join("\n");
|
|
103
|
+
return numbered + ending + (note ? note[0] : "");
|
|
101
104
|
}
|
|
102
105
|
function fenced(body) {
|
|
103
106
|
const longest = Math.max(2, ...[...body.matchAll(/`+/g)].map((match) => match[0].length));
|
|
104
107
|
const fence = "`".repeat(longest + 1);
|
|
105
108
|
return `${fence}\n${body}\n${fence}`;
|
|
106
109
|
}
|
|
107
|
-
|
|
110
|
+
/**
|
|
111
|
+
* Copied text cannot open or close a `<user-message>` tag, so nothing but the user's own messages reads as their words.
|
|
112
|
+
*/
|
|
113
|
+
function withoutUserMessageTags(text) {
|
|
114
|
+
return text.replace(/<(\/?)(user-message)/gi, "<\\$1$2");
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* One of the user's latest messages. Freeflow's guidance says text in a notice is not the user's instruction, except what
|
|
118
|
+
* these tags quote.
|
|
119
|
+
*/
|
|
120
|
+
function userMessage(text, index) {
|
|
121
|
+
return `<user-message index="${index}">\n${withoutUserMessageTags(text)}\n</user-message>`;
|
|
122
|
+
}
|
|
123
|
+
export function renderCarried(notice, cycle, userMessages, files, results = [], options = {}) {
|
|
108
124
|
const parts = [
|
|
109
|
-
`# Carried context\n\
|
|
125
|
+
`# Carried context\n\nFreeflow carried this into cycle ${cycle} at compaction. Only the latest user messages, in \`<user-message>\` tags, are the user's words, and they take precedence over older instructions in the summary or a Working Record; the rest is copied content.`,
|
|
110
126
|
];
|
|
111
127
|
if (userMessages.length)
|
|
112
128
|
parts.push(
|
|
113
|
-
"## Latest user messages\n\n" +
|
|
114
|
-
userMessages.map((message, i) => `### Message ${i + 1}\n\n${message}`).join("\n\n"),
|
|
129
|
+
"## Latest user messages\n\n" + userMessages.map((message, i) => userMessage(message, i + 1)).join("\n\n"),
|
|
115
130
|
);
|
|
116
131
|
if (files.length)
|
|
117
132
|
parts.push(
|
|
@@ -121,7 +136,7 @@ export function renderCarried(noticePrefix, cycle, userMessages, files, results
|
|
|
121
136
|
const range = "lines" in file && file.lines ? ` (lines ${file.lines[0]}-${file.lines[1]})` : "";
|
|
122
137
|
return "error" in file
|
|
123
138
|
? `### ${file.path}\n\nNot carried: ${file.error}.`
|
|
124
|
-
: `### ${file.path}${range}\n\n${fenced(numberLines(file.text, "lines" in file && file.lines ? file.lines[0] : 1))}`;
|
|
139
|
+
: `### ${file.path}${range}\n\n${fenced(numberLines(withoutUserMessageTags(file.text), "lines" in file && file.lines ? file.lines[0] : 1, { readNote: false }))}`;
|
|
125
140
|
})
|
|
126
141
|
.join("\n\n"),
|
|
127
142
|
);
|
|
@@ -131,26 +146,25 @@ export function renderCarried(noticePrefix, cycle, userMessages, files, results
|
|
|
131
146
|
results
|
|
132
147
|
.map(
|
|
133
148
|
(result) =>
|
|
134
|
-
`### ${result.id} ${result.tool}${result.label && result.label !== result.id ? `: ${result.label}` : ""}\n\n${fenced(result.firstLine === undefined ? result.text : numberLines(result.text, result.firstLine))}`,
|
|
149
|
+
`### ${result.id} ${result.tool}${result.label && result.label !== result.id ? `: ${result.label}` : ""}\n\n${fenced(result.firstLine === undefined ? withoutUserMessageTags(result.text) : numberLines(withoutUserMessageTags(result.text), result.firstLine))}`,
|
|
135
150
|
)
|
|
136
151
|
.join("\n\n"),
|
|
137
152
|
);
|
|
138
153
|
const listed = renderListed(options.listed ?? [], options.scope ?? "cycle", "Other results");
|
|
139
154
|
if (listed) parts.push(listed);
|
|
140
|
-
return parts.join("\n\n");
|
|
155
|
+
return notice(parts.join("\n\n"));
|
|
141
156
|
}
|
|
142
157
|
/**
|
|
143
158
|
* The carried context as the Coordinator sees it under projection: the user's latest messages, and only the names of
|
|
144
159
|
* the worker's copies. The Coordinator receives evidence by selection; the copies would repeat it and enlarge its view.
|
|
145
160
|
*/
|
|
146
|
-
export function renderCarriedForCoordinator(
|
|
161
|
+
export function renderCarriedForCoordinator(notice, cycle, userMessages, files, results) {
|
|
147
162
|
const parts = [
|
|
148
|
-
`# Carried context\n\
|
|
163
|
+
`# Carried context\n\nFreeflow carried this into cycle ${cycle} at the worker's compaction. Only the latest user messages, in \`<user-message>\` tags, are the user's words, and they take precedence over older instructions in the summary or a Working Record.`,
|
|
149
164
|
];
|
|
150
165
|
if (userMessages.length)
|
|
151
166
|
parts.push(
|
|
152
|
-
"## Latest user messages\n\n" +
|
|
153
|
-
userMessages.map((message, i) => `### Message ${i + 1}\n\n${message}`).join("\n\n"),
|
|
167
|
+
"## Latest user messages\n\n" + userMessages.map((message, i) => userMessage(message, i + 1)).join("\n\n"),
|
|
154
168
|
);
|
|
155
169
|
const names = [
|
|
156
170
|
...files
|
|
@@ -168,7 +182,7 @@ export function renderCarriedForCoordinator(noticePrefix, cycle, userMessages, f
|
|
|
168
182
|
"## Carried for the worker\n\nThe worker's copies are left out of this view; evidence it selects reaches you as usual.\n\n" +
|
|
169
183
|
names.join("\n"),
|
|
170
184
|
);
|
|
171
|
-
return parts.join("\n\n");
|
|
185
|
+
return notice(parts.join("\n\n"));
|
|
172
186
|
}
|
|
173
187
|
/** Results of the work in progress named by ref, so the agent knows they exist without their content in context. */
|
|
174
188
|
export function renderListed(listed, scope, heading, intro) {
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
/** Freeflow compaction is on whenever Freeflow is, unless a config turns it off. */
|
|
2
2
|
export const DEFAULT_COMPACTION_ENABLED = true;
|
|
3
3
|
export const DEFAULT_COMPACTION_CARRY = true;
|
|
4
|
+
/** The keys `compaction` accepts; the settings test checks each has a /freeflow settings row. */
|
|
5
|
+
export const COMPACTION_KEYS = ["enabled", "carry"];
|
|
4
6
|
function record(value) {
|
|
5
7
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6
8
|
}
|
|
7
9
|
export function validateCompactionConfig(value) {
|
|
8
10
|
if (!record(value)) return "compaction must be an object";
|
|
9
|
-
const unknown = Object.keys(value).find((key) => key
|
|
11
|
+
const unknown = Object.keys(value).find((key) => !COMPACTION_KEYS.includes(key));
|
|
10
12
|
if (unknown) return `compaction has unsupported key: ${unknown}`;
|
|
11
|
-
for (const key of
|
|
13
|
+
for (const key of COMPACTION_KEYS)
|
|
12
14
|
if (value[key] !== undefined && typeof value[key] !== "boolean") return `compaction.${key} must be a boolean`;
|
|
13
15
|
return null;
|
|
14
16
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { resolveToolPath } from "../tool-execution/file-state.js";
|
|
3
|
+
import { skillsRead } from "../tool-execution/skill-reads.js";
|
|
3
4
|
import {
|
|
4
5
|
carryBudget,
|
|
5
6
|
carryProblem,
|
|
@@ -12,7 +13,7 @@ import {
|
|
|
12
13
|
renderListed,
|
|
13
14
|
SUMMARY_LIMIT_TOKENS,
|
|
14
15
|
} from "./carry.js";
|
|
15
|
-
import { cycleFiles, harnessPart, nextCycle, previousFileLists, sessionFileLists } from "./harness.js";
|
|
16
|
+
import { currentCycle, cycleFiles, harnessPart, nextCycle, previousFileLists, sessionFileLists } from "./harness.js";
|
|
16
17
|
import {
|
|
17
18
|
INDEX_TOKEN_BOUND,
|
|
18
19
|
renderIndex,
|
|
@@ -25,16 +26,15 @@ import { cachedBranch } from "../host/branch.js";
|
|
|
25
26
|
/**
|
|
26
27
|
* Freeflow compaction's agent path. After each turn Freeflow measures the context and, once per cycle each, says
|
|
27
28
|
* compaction is due (the warning, with the result index) and then to compact now. The agent asks to compact with
|
|
28
|
-
*
|
|
29
|
+
* compact_context once compaction is due. At the end of that turn Freeflow writes Pi's compaction entry, keeping
|
|
29
30
|
* nothing raw, followed by the carried context and a recovery message, and asks Pi to continue the same run. Pi fires
|
|
30
31
|
* no compaction events for this, so the caller runs Freeflow's post-compaction resets before the next request
|
|
31
32
|
* (takeCompacted).
|
|
32
33
|
*/
|
|
33
34
|
export const CARRIED_TYPE = "freeflow-carried-context";
|
|
34
35
|
export const RECOVERY_TYPE = "freeflow-compaction-recovery";
|
|
35
|
-
/** What /freeflow compact
|
|
36
|
-
export const USER_REQUEST =
|
|
37
|
-
"Compact now: read the compaction skill, prepare the next cycle, and call freeflow_compact.";
|
|
36
|
+
/** What /freeflow compact asks for, in a Freeflow notice; a list of results may follow it. */
|
|
37
|
+
export const USER_REQUEST = "Compact now: read the compaction skill, prepare the next cycle, and call compact_context.";
|
|
38
38
|
const ABNORMAL_STOPS = new Set(["error", "aborted", "length"]);
|
|
39
39
|
/** The most results the carried context lists by ref; the newest are listed. */
|
|
40
40
|
const MAX_LISTED = 60;
|
|
@@ -145,27 +145,30 @@ export class CompactionController {
|
|
|
145
145
|
if (level === "compactNow") this.sent.compactNowAt = tokens;
|
|
146
146
|
return { level, text: this.noticeText(level, tokens, thresholds, ctx, withList) };
|
|
147
147
|
}
|
|
148
|
+
/** A compaction notice, tagged as Freeflow's (the host's notice()). */
|
|
148
149
|
noticeText(level, tokens, thresholds, ctx, withList = true) {
|
|
149
|
-
|
|
150
|
+
return this.host.notice(this.noticeTextBody(level, tokens, thresholds, ctx, withList));
|
|
151
|
+
}
|
|
152
|
+
noticeTextBody(level, tokens, thresholds, ctx, withList = true) {
|
|
150
153
|
const { window, trigger } = thresholds;
|
|
151
154
|
if (this.host.coordinatorUnderProjection())
|
|
152
|
-
return `${
|
|
155
|
+
return `${level === "compactNow" ? "Compact now" : "Compaction is due soon"}: the full history is about ${tokens} of ${window} tokens, and Pi compacts on its own past about ${trigger}${level === "compactNow" ? ", or when a request overflows the model's window" : ""}. Your view leaves out what workers did, so do not compact yourself: ${level === "compactNow" ? "now" : "at a safe point (after the current assessment, or before the next assignment)"}, delegate an assignment asking the worker to read the compaction skill, compact with compact_context, and then return with return_work; then continue.`;
|
|
153
156
|
const index = withList ? this.indexText(ctx) : "";
|
|
154
157
|
const list = index ? `\n\n${index}` : "";
|
|
155
158
|
// A worker's return hands the same history to the Coordinator and the next assignment, so it ends no growth.
|
|
156
159
|
const worker = this.host.routingAssignment() !== undefined;
|
|
157
160
|
if (level === "compactNow")
|
|
158
|
-
return
|
|
161
|
+
return `Compact now: context is at about ${tokens} of ${window} tokens. Pi compacts on its own past about ${trigger}, or when a request overflows the model's window. The end of your current step is the safe point: write down any partial state, then prepare and call compact_context; ${worker ? "compact before you return, since the Coordinator and the next assignment continue this history; " : ""}if Pi compacts first, its own summary replaces yours.${list}`;
|
|
159
162
|
// Before compact now there is room for the Coordinator's next turn, so a worker may finish a return first.
|
|
160
163
|
const end = `If you are at one now, compact now; if your work ends within a step or two with ${worker ? "a return" : "a final answer"}, finish it instead.`;
|
|
161
|
-
return
|
|
164
|
+
return `Compaction is due soon: context is at about ${tokens} of ${window} tokens, and Pi compacts on its own past about ${trigger}. Keep working until a safe point: a finished step whose result you know (a check read, an edit set complete, a report written), not mid-edit or with a command running. ${end} At the safe point, read the compaction skill, update the Working Record, ${this.carryText(ctx)}, write the summary, and call compact_context. A "compact now" notice follows near Pi's limit.${list}`;
|
|
162
165
|
}
|
|
163
|
-
/** Check a
|
|
166
|
+
/** Check a compact_context call and schedule it for the end of this turn; throws with the reason it is refused. */
|
|
164
167
|
async request(params, ctx) {
|
|
165
168
|
if (!this.host.effective()) throw new Error("Freeflow compaction is turned off.");
|
|
166
169
|
if (this.host.coordinatorUnderProjection())
|
|
167
170
|
throw new Error(
|
|
168
|
-
"Your view leaves out what workers did, so do not compact yourself: delegate an assignment asking the worker to read the compaction skill, compact with
|
|
171
|
+
"Your view leaves out what workers did, so do not compact yourself: delegate an assignment asking the worker to read the compaction skill, compact with compact_context, and then return with return_work.",
|
|
169
172
|
);
|
|
170
173
|
if (!this.isDue(ctx)) throw new Error(notDueText(branchOf(ctx)));
|
|
171
174
|
const summary = typeof params?.summary === "string" ? params.summary.trim() : "";
|
|
@@ -173,13 +176,13 @@ export class CompactionController {
|
|
|
173
176
|
const summaryTokens = estimateTokens(summary);
|
|
174
177
|
if (summaryTokens > SUMMARY_LIMIT_TOKENS)
|
|
175
178
|
throw new Error(
|
|
176
|
-
`The summary is about ${summaryTokens} tokens; the limit is ${SUMMARY_LIMIT_TOKENS}. Shorten it by about ${summaryTokens - SUMMARY_LIMIT_TOKENS} tokens and call
|
|
179
|
+
`The summary is about ${summaryTokens} tokens; the limit is ${SUMMARY_LIMIT_TOKENS}. Shorten it by about ${summaryTokens - SUMMARY_LIMIT_TOKENS} tokens and call compact_context again.`,
|
|
177
180
|
);
|
|
178
181
|
const carry = params?.carry === undefined ? [] : params.carry;
|
|
179
182
|
if (!Array.isArray(carry)) throw new Error("carry must be a list of { file, lines? } or { result } items.");
|
|
180
183
|
if (carry.length && !this.host.carryEnabled())
|
|
181
184
|
throw new Error(
|
|
182
|
-
"Context reuse is off: call
|
|
185
|
+
"Context reuse is off: call compact_context without carry. The user's latest messages are carried for you.",
|
|
183
186
|
);
|
|
184
187
|
const problems = carry.map(carryProblem).filter(Boolean);
|
|
185
188
|
if (problems.length) throw new Error(`Fix the carry list: ${problems.join("; ")}.`);
|
|
@@ -299,15 +302,25 @@ export class CompactionController {
|
|
|
299
302
|
requestedBy,
|
|
300
303
|
routingProfile: this.host.routingProfile(),
|
|
301
304
|
background: this.host.background(),
|
|
305
|
+
skills: skillsRead(currentCycle(branch), ctx.cwd),
|
|
302
306
|
files: sessionFileLists(branch),
|
|
303
307
|
})}`;
|
|
304
|
-
const userMessages = latestUserMessages(branch
|
|
308
|
+
const userMessages = latestUserMessages(branch);
|
|
305
309
|
const { automatic, listed } = this.fill(branch, ctx, files, results);
|
|
306
|
-
const carriedText = renderCarried(
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
310
|
+
const carriedText = renderCarried(
|
|
311
|
+
(text) => this.host.notice(text),
|
|
312
|
+
cycle,
|
|
313
|
+
userMessages,
|
|
314
|
+
files,
|
|
315
|
+
[...results, ...automatic],
|
|
316
|
+
{
|
|
317
|
+
listed,
|
|
318
|
+
scope: this.host.nativeRefs() && this.host.assignmentResults() ? "assignment" : "cycle",
|
|
319
|
+
},
|
|
320
|
+
);
|
|
321
|
+
const recovery = this.host.notice(
|
|
322
|
+
`${recoveryText(cycle, "The files and results copied into the carried context above were read at compaction: do not read those again unless something has changed them. Skills, and anything listed there only by name, were not carried: read them when you need them. ")} ${AFTER_REQUESTED}`,
|
|
323
|
+
);
|
|
311
324
|
this.requested = undefined;
|
|
312
325
|
this.compacted = { carriedFiles: files.filter((file) => "text" in file).map((file) => file.path) };
|
|
313
326
|
return {
|
|
@@ -343,10 +356,13 @@ export class CompactionController {
|
|
|
343
356
|
...(this.host.nativeRefs()
|
|
344
357
|
? {
|
|
345
358
|
details: {
|
|
346
|
-
coordinatorContent: renderCarriedForCoordinator(
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
359
|
+
coordinatorContent: renderCarriedForCoordinator(
|
|
360
|
+
(text) => this.host.notice(text),
|
|
361
|
+
cycle,
|
|
362
|
+
userMessages,
|
|
363
|
+
files,
|
|
364
|
+
[...results, ...automatic],
|
|
365
|
+
),
|
|
350
366
|
},
|
|
351
367
|
}
|
|
352
368
|
: {}),
|
|
@@ -382,6 +398,7 @@ export class CompactionController {
|
|
|
382
398
|
cycle,
|
|
383
399
|
routingProfile: this.host.routingProfile(),
|
|
384
400
|
background: this.host.background(),
|
|
401
|
+
skills: skillsRead(currentCycle(branch), ctx.cwd),
|
|
385
402
|
files: { readFiles: [], modifiedFiles: [] },
|
|
386
403
|
includeFiles: false,
|
|
387
404
|
listed: this.assignmentList(branch),
|
|
@@ -410,8 +427,14 @@ export class CompactionController {
|
|
|
410
427
|
return compacted;
|
|
411
428
|
}
|
|
412
429
|
}
|
|
413
|
-
/**
|
|
430
|
+
/**
|
|
431
|
+
* Freeflow's additions to Pi's own summarizer, written for a reader that summarizes someone else's conversation. From
|
|
432
|
+
* Pi 1.4.0 the summarizer's output is no longer capped at 80% of Pi's reserve but at the model's output limit (reduced
|
|
433
|
+
* to what the window leaves), so the length is asked for: the next cycle then starts within the room an agent's
|
|
434
|
+
* compaction leaves, whose summary Freeflow caps at the same limit.
|
|
435
|
+
*/
|
|
414
436
|
const FALLBACK_INSTRUCTIONS = [
|
|
437
|
+
`Keep the summary under ${SUMMARY_LIMIT_TOKENS.toLocaleString("en-US")} tokens; the next part of the conversation starts from it.`,
|
|
415
438
|
"Write state, not a story: what is true now, each item marked as settled, tentative, superseded, or unverified.",
|
|
416
439
|
"Keep exact paths, identifiers, commands, error text, numbers, and the user's words where wording matters; never paraphrase a decision or a constraint.",
|
|
417
440
|
"Never record a requested, reported, or expected result as observed. Name partial or uncommitted changes, unverified results, running commands, and obligations with what triggers each.",
|
|
@@ -423,18 +446,18 @@ const FALLBACK_INSTRUCTIONS = [
|
|
|
423
446
|
* pointing at the wrong one sends recovery into another task. The summary names the record when there is one.
|
|
424
447
|
*/
|
|
425
448
|
function recoveryText(cycle, carried = "") {
|
|
426
|
-
return `Compaction finished; cycle ${cycle} starts here. Recover before you continue: if the summary names a Working Record for this work, recover it as Track Work says; otherwise the summary is your record for this cycle, so re-read its Recovery sources. Reconcile with the live state your next step depends on, and verify work the summary calls done instead of redoing it. ${carried}This compaction is done, so do not re-read the compaction skill or its summary format. Then continue the work the compaction interrupted.`;
|
|
449
|
+
return `Compaction finished; cycle ${cycle} starts here. Recover before you continue: if the summary names a Working Record for this work, recover it as Track Work says; otherwise the summary is your record for this cycle, so re-read its Recovery sources. Reconcile with the live state your next step depends on, and verify work the summary calls done instead of redoing it. ${carried}Skills you read before this point are out of view, even where the summary lists their files: read each one again before you apply its method, the cognitive-routing skill first when routing is on. This compaction is done, so do not re-read the compaction skill or its summary format. Then continue the work the compaction interrupted.`;
|
|
427
450
|
}
|
|
428
451
|
/** The agent path: the compaction was the request, and it is done. */
|
|
429
452
|
const AFTER_REQUESTED =
|
|
430
|
-
"This compaction completes the request for it: do not call
|
|
453
|
+
"This compaction completes the request for it: do not call compact_context again in this cycle. If compacting was all you were asked to do, report that it is done (a worker returns its assignment with return_work).";
|
|
431
454
|
/** Pi compacted on its own, so a Freeflow compaction being prepared is no longer needed. */
|
|
432
|
-
const AFTER_PI = "Compaction is no longer due: do not call
|
|
433
|
-
/** Why
|
|
455
|
+
const AFTER_PI = "Compaction is no longer due: do not call compact_context.";
|
|
456
|
+
/** Why compact_context is refused when compaction is not due, and where to go next if a compaction began this cycle. */
|
|
434
457
|
function notDueText(branch) {
|
|
435
458
|
const latest = [...branch].reverse().find((entry) => entry?.type === "compaction");
|
|
436
459
|
const next =
|
|
437
|
-
"Continue the work; if this is a routing assignment that only asked for compaction, return it with
|
|
460
|
+
"Continue the work; if this is a routing assignment that only asked for compaction, return it with return_work.";
|
|
438
461
|
if (!latest)
|
|
439
462
|
return "Compaction is not due. Freeflow says when it is; the user can also ask for it with /freeflow compact.";
|
|
440
463
|
if (latest.details?.freeflow && !latest.details.freeflow.fallback)
|
|
@@ -7,11 +7,26 @@ export function currentCycle(branch) {
|
|
|
7
7
|
});
|
|
8
8
|
return branch.slice(start);
|
|
9
9
|
}
|
|
10
|
+
/**
|
|
11
|
+
* The entries whose file touches no compaction has listed yet: the current cycle, plus the latest compaction's kept
|
|
12
|
+
* tail. Pi keeps raw the entries from its `firstKeptEntryId`, which sit before its compaction entry on the branch, and
|
|
13
|
+
* lists only what it summarized; Freeflow's compactions keep nothing raw.
|
|
14
|
+
*/
|
|
15
|
+
function unlistedEntries(branch) {
|
|
16
|
+
let latest = branch.length - 1;
|
|
17
|
+
while (latest >= 0 && branch[latest]?.type !== "compaction") latest--;
|
|
18
|
+
if (latest < 0) return [...branch];
|
|
19
|
+
const keptFrom = branch[latest].firstKeptEntryId
|
|
20
|
+
? branch.findIndex((entry, i) => i < latest && entry?.id === branch[latest].firstKeptEntryId)
|
|
21
|
+
: -1;
|
|
22
|
+
const tail = keptFrom < 0 ? [] : branch.slice(keptFrom, latest).filter((entry) => entry?.type !== "compaction");
|
|
23
|
+
return [...tail, ...branch.slice(latest + 1)];
|
|
24
|
+
}
|
|
10
25
|
/** The cycle number the next compaction starts. Cycle 1 runs from session start to the first compaction. */
|
|
11
26
|
export const nextCycle = (branch) => branch.filter((entry) => entry?.type === "compaction").length + 2;
|
|
12
27
|
/**
|
|
13
|
-
* Files successful calls
|
|
14
|
-
* too: Pi records them on the script's result as nested calls.
|
|
28
|
+
* Files successful calls read or changed since the latest compaction listed them (this cycle, and Pi's kept tail), in
|
|
29
|
+
* first-touched order. Calls a codemode script made count too: Pi records them on the script's result as nested calls.
|
|
15
30
|
*/
|
|
16
31
|
export function cycleFiles(branch) {
|
|
17
32
|
const calls = new Map();
|
|
@@ -20,7 +35,7 @@ export function cycleFiles(branch) {
|
|
|
20
35
|
const touch = (name, input) => {
|
|
21
36
|
for (const path of touchedPaths(name, input)) (name === "read" ? read : changed).add(path);
|
|
22
37
|
};
|
|
23
|
-
for (const entry of
|
|
38
|
+
for (const entry of unlistedEntries(branch)) {
|
|
24
39
|
const message = entry?.type === "message" ? entry.message : undefined;
|
|
25
40
|
if (message?.role === "assistant")
|
|
26
41
|
for (const block of message.content ?? [])
|
|
@@ -69,6 +84,7 @@ export function harnessPart(facts) {
|
|
|
69
84
|
lines.push(
|
|
70
85
|
`Background commands still running: ${facts.background.map((job) => `${job.id} (${job.label}), output ${job.outputPath}`).join("; ")}.`,
|
|
71
86
|
);
|
|
87
|
+
if (facts.skills?.length) lines.push(`Freeflow skills read in cycle ${facts.cycle - 1}: ${facts.skills.join(", ")}.`);
|
|
72
88
|
const files = facts.includeFiles === false ? "" : formatFileLists(facts.files);
|
|
73
89
|
if (files) lines.push("", "Files read and modified in this session:", "", files);
|
|
74
90
|
if (facts.listed) lines.push("", facts.listed.replace(/^## /, "### "));
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { resolve, sep } from "node:path";
|
|
2
|
-
import { fileURLToPath } from "node:url";
|
|
3
1
|
import { estimateTokens } from "./carry.js";
|
|
4
2
|
import { currentCycle } from "./harness.js";
|
|
3
|
+
import { withoutGuidanceNotices } from "../tool-execution/guidance-notices.js";
|
|
4
|
+
import { isFreeflowInstruction } from "../tool-execution/skill-reads.js";
|
|
5
5
|
/**
|
|
6
6
|
* The result index: this cycle's larger tool results, each with an id the agent can carry by. Ids number tool results
|
|
7
7
|
* in branch order (r1, r2, ...), so they stay stable while the branch grows, and a result from an earlier cycle still
|
|
@@ -16,16 +16,25 @@ const MAX_ROWS = 40;
|
|
|
16
16
|
export const INDEX_TOKEN_BOUND = 2_000;
|
|
17
17
|
/** Freeflow's own control calls: no evidence to carry, as routing's evidence selection also excludes them. */
|
|
18
18
|
const CONTROL_TOOLS = new Set([
|
|
19
|
+
"delegate_work",
|
|
20
|
+
"return_work",
|
|
21
|
+
"select_evidence",
|
|
22
|
+
"routing_unit",
|
|
23
|
+
"compact_context",
|
|
24
|
+
// The same tools' names before Task 003's rename, still in sessions saved earlier.
|
|
19
25
|
"freeflow_delegate",
|
|
20
26
|
"freeflow_return",
|
|
21
27
|
"freeflow_project",
|
|
22
28
|
"freeflow_unit",
|
|
23
29
|
"freeflow_compact",
|
|
24
30
|
]);
|
|
31
|
+
/** A result's text without the guidance notices that joined it. */
|
|
25
32
|
const resultText = (message) =>
|
|
26
|
-
(
|
|
27
|
-
|
|
28
|
-
|
|
33
|
+
withoutGuidanceNotices(
|
|
34
|
+
(message?.content ?? [])
|
|
35
|
+
.map((part) => (part?.type === "text" ? part.text : part?.type === "image" ? "[image]" : ""))
|
|
36
|
+
.join(""),
|
|
37
|
+
);
|
|
29
38
|
function label(name, input) {
|
|
30
39
|
const firstLine = (text) =>
|
|
31
40
|
text
|
|
@@ -49,14 +58,6 @@ function label(name, input) {
|
|
|
49
58
|
const line = firstLine(String(value));
|
|
50
59
|
return line.length > 80 ? `${line.slice(0, 77)}...` : line;
|
|
51
60
|
}
|
|
52
|
-
/** The Freeflow package root: reads of its skills, references and prompts are instructions, not work to carry. */
|
|
53
|
-
const FREEFLOW_ROOT = fileURLToPath(new URL("../../../", import.meta.url));
|
|
54
|
-
const INSTRUCTION_DIRS = ["capabilities", "skills", "runtime"].map((dir) => resolve(FREEFLOW_ROOT, dir) + sep);
|
|
55
|
-
function isFreeflowInstruction(tool, input) {
|
|
56
|
-
if (tool !== "read" || typeof input?.path !== "string") return false;
|
|
57
|
-
const path = resolve(input.path);
|
|
58
|
-
return INSTRUCTION_DIRS.some((dir) => path.startsWith(dir));
|
|
59
|
-
}
|
|
60
61
|
/** Every tool result on the branch with its id, oldest first. */
|
|
61
62
|
function allResults(branch) {
|
|
62
63
|
const calls = new Map();
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
export const
|
|
1
|
+
export const COMPACT_CONTEXT = "compact_context";
|
|
2
2
|
const DESCRIPTION =
|
|
3
3
|
"Compact the conversation into a new cycle. Use only when Freeflow says compaction is due or the user asks, and read the compaction skill first. Pass your summary and what to carry; compaction happens at the end of this turn, and the run continues from the summary, the carried context and a recovery message.";
|
|
4
4
|
/** Registered once, declared only while Freeflow compaction is effective (see applyCompactionTools). */
|
|
5
5
|
export function registerCompactionTool(pi, controller) {
|
|
6
6
|
pi.registerTool({
|
|
7
|
-
name:
|
|
7
|
+
name: COMPACT_CONTEXT,
|
|
8
8
|
label: "Compact",
|
|
9
9
|
description: DESCRIPTION,
|
|
10
10
|
parameters: {
|
|
@@ -47,11 +47,11 @@ export function registerCompactionTool(pi, controller) {
|
|
|
47
47
|
},
|
|
48
48
|
});
|
|
49
49
|
}
|
|
50
|
-
/** Declare or withdraw
|
|
50
|
+
/** Declare or withdraw compact_context when compaction's effective state changes, keeping other tools in place. */
|
|
51
51
|
export function applyCompactionTools(pi, effective) {
|
|
52
52
|
if (typeof pi.getActiveTools !== "function" || typeof pi.setActiveTools !== "function") return;
|
|
53
53
|
const current = pi.getActiveTools();
|
|
54
|
-
const present = current.includes(
|
|
55
|
-
if (effective && !present) pi.setActiveTools([...current,
|
|
56
|
-
if (!effective && present) pi.setActiveTools(current.filter((name) => name !==
|
|
54
|
+
const present = current.includes(COMPACT_CONTEXT);
|
|
55
|
+
if (effective && !present) pi.setActiveTools([...current, COMPACT_CONTEXT]);
|
|
56
|
+
if (!effective && present) pi.setActiveTools(current.filter((name) => name !== COMPACT_CONTEXT));
|
|
57
57
|
}
|