@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,10 +2,12 @@ import { existsSync, realpathSync } from "node:fs";
|
|
|
2
2
|
import { relative, resolve as resolvePath } from "node:path";
|
|
3
3
|
import { ROUTING_RECOVERY_HINT } from "./session.js";
|
|
4
4
|
export { ROUTING_RECOVERY_HINT };
|
|
5
|
+
import { requiredReads } from "./bootstrap.js";
|
|
5
6
|
import { bodyHash } from "./sources.js";
|
|
6
7
|
import { initialState } from "./state.js";
|
|
7
8
|
import { RoutingError, isWorkerProfile, requireCondition as check, ROUTING_TOOLS } from "./types.js";
|
|
8
9
|
import { cachedBranch } from "../host/branch.js";
|
|
10
|
+
const BOOTSTRAP_GATED = new Set(["bash", "bash_background", "monitor", "edit", "write", "apply_patch"]);
|
|
9
11
|
/**
|
|
10
12
|
* What routing allows while a profile runs, and what it tells the tool runtime: which ordinary tools a worker may
|
|
11
13
|
* still call, which recovery reads an assessment permits, handoff batching rules, and the operation, admission, and
|
|
@@ -115,9 +117,9 @@ export class ToolGate {
|
|
|
115
117
|
}
|
|
116
118
|
}
|
|
117
119
|
handoffOperation(name, input) {
|
|
118
|
-
if (name === "
|
|
119
|
-
if (name === "
|
|
120
|
-
return name === "
|
|
120
|
+
if (name === "delegate_work") return true;
|
|
121
|
+
if (name === "return_work") return ["submit", "supplement", "retry"].includes(input?.operation);
|
|
122
|
+
return name === "routing_unit" && input?.operation === "recover";
|
|
121
123
|
}
|
|
122
124
|
stableRecoveryPath(path) {
|
|
123
125
|
return (
|
|
@@ -192,7 +194,7 @@ export class ToolGate {
|
|
|
192
194
|
!handoffs.length ||
|
|
193
195
|
(handoffs.length === 1 &&
|
|
194
196
|
this.handoffOperation(calls.at(-1)?.name, calls.at(-1)?.arguments) &&
|
|
195
|
-
calls.every((b) => this.handoffOperation(b.name, b.arguments) || b.name === "
|
|
197
|
+
calls.every((b) => this.handoffOperation(b.name, b.arguments) || b.name === "select_evidence")),
|
|
196
198
|
"invalid_handoff_batch",
|
|
197
199
|
"A handoff must be last and cannot accompany ordinary task tools.",
|
|
198
200
|
);
|
|
@@ -211,12 +213,25 @@ export class ToolGate {
|
|
|
211
213
|
try {
|
|
212
214
|
const state = this.session.stateData();
|
|
213
215
|
if (state.control === "manual")
|
|
214
|
-
return isRouting && name !== "
|
|
216
|
+
return isRouting && name !== "routing_unit" ? { block: true, reason: "Manual control is active." } : undefined;
|
|
215
217
|
if (state.control !== "automatic")
|
|
216
218
|
return isRouting ? { block: true, reason: "Automatic routing is inactive." } : undefined;
|
|
217
219
|
this.batch(event.toolCallId, name, nested);
|
|
218
220
|
check(!this.session.error && !this.session.store?.blocked, "routing_blocked");
|
|
219
221
|
this.session.openTurn();
|
|
222
|
+
const skill = this.session.routingSkill;
|
|
223
|
+
// Commands and edits wait for the bootstrap read of the skill and the profile's role reference; reads and
|
|
224
|
+
// controls do not (`bootstrap.ts`). A codemode script's calls reach this gate under their own names.
|
|
225
|
+
if (skill && BOOTSTRAP_GATED.has(name) && !state.recoveryId && this.session.turn?.skillInView === false) {
|
|
226
|
+
const [path, reference] = requiredReads(skill, this.session.turn?.profile);
|
|
227
|
+
check(
|
|
228
|
+
false,
|
|
229
|
+
"routing_skill_unread",
|
|
230
|
+
reference
|
|
231
|
+
? `Read the complete cognitive-routing skill and the reference for your profile first: ${path} and ${reference}. Under automatic routing each profile needs both in view before commands or edits, and a compaction takes them out of view. Read them in a response of their own.`
|
|
232
|
+
: `Read the complete cognitive-routing skill first: ${path}. Under automatic routing each profile needs it in view before commands or edits, and a compaction takes it out of view. Read it in a response of their own.`,
|
|
233
|
+
);
|
|
234
|
+
}
|
|
220
235
|
if (isWorkerProfile(this.session.turn?.profile) && !isRouting) {
|
|
221
236
|
const a = state.assignmentId ? state.assignments.get(state.assignmentId) : undefined;
|
|
222
237
|
const worker = a ? this.session.assignedWorker(state, a.id) : undefined;
|
|
@@ -4,7 +4,7 @@ import { bodyHash, isTaskEvidence } from "./sources.js";
|
|
|
4
4
|
import { RoutingError, canonical, emptySelection, idFor, isWorkerProfile, requireCondition as check } from "./types.js";
|
|
5
5
|
import { cachedBranch } from "../host/branch.js";
|
|
6
6
|
/**
|
|
7
|
-
* Runs the routing tools (
|
|
7
|
+
* Runs the routing tools (delegate_work, return_work, select_evidence, routing_unit) one at a time on the
|
|
8
8
|
* session's queue. Each accepted call is recorded once under its operation id, so a repeated call returns the saved
|
|
9
9
|
* result instead of acting again; a refused call returns a blocked result that names what to reconcile.
|
|
10
10
|
*/
|
|
@@ -42,13 +42,13 @@ export class ToolHandlers {
|
|
|
42
42
|
this.session.ctx = ctx;
|
|
43
43
|
try {
|
|
44
44
|
check(!signal?.aborted, "cancelled");
|
|
45
|
-
if (name === "
|
|
45
|
+
if (name === "routing_unit" && input.operation === "inspect") {
|
|
46
46
|
check(this.session.supported(), "routing_unavailable");
|
|
47
47
|
return this.result(this.inspectUnit(input));
|
|
48
48
|
}
|
|
49
49
|
const state = this.session.stateData();
|
|
50
50
|
const expected =
|
|
51
|
-
name === "
|
|
51
|
+
name === "delegate_work" || name === "routing_unit" ? "coordinator" : this.session.assignedWorker(state);
|
|
52
52
|
this.session.assertAvailable(expected);
|
|
53
53
|
const op = this.callOperation(callId, name);
|
|
54
54
|
const cached = this.session.receipts.get(op);
|
|
@@ -57,13 +57,13 @@ export class ToolHandlers {
|
|
|
57
57
|
return this.result(structuredClone(cached.value));
|
|
58
58
|
}
|
|
59
59
|
const value =
|
|
60
|
-
name === "
|
|
60
|
+
name === "delegate_work"
|
|
61
61
|
? this.delegate(input, callId, op)
|
|
62
|
-
: name === "
|
|
62
|
+
: name === "return_work"
|
|
63
63
|
? this.returnReport(input, callId, op)
|
|
64
|
-
: name === "
|
|
64
|
+
: name === "select_evidence"
|
|
65
65
|
? this.project(input, op)
|
|
66
|
-
: name === "
|
|
66
|
+
: name === "routing_unit"
|
|
67
67
|
? await this.unit(input, callId, op)
|
|
68
68
|
: undefined;
|
|
69
69
|
if (value !== undefined) {
|
|
@@ -79,9 +79,9 @@ export class ToolHandlers {
|
|
|
79
79
|
problems: error instanceof RoutingError ? error.problems : [],
|
|
80
80
|
observed: this.session.observed(),
|
|
81
81
|
expectedProfile:
|
|
82
|
-
name === "
|
|
82
|
+
name === "routing_unit" && input.operation === "inspect"
|
|
83
83
|
? undefined
|
|
84
|
-
: name === "
|
|
84
|
+
: name === "delegate_work" || name === "routing_unit"
|
|
85
85
|
? "coordinator"
|
|
86
86
|
: (() => {
|
|
87
87
|
try {
|
|
@@ -101,7 +101,7 @@ export class ToolHandlers {
|
|
|
101
101
|
"invalid_inspection",
|
|
102
102
|
].includes(error.code)
|
|
103
103
|
? "Inspect again without a cursor and use a returned work ref. Lookup recovery does not require a new assignment."
|
|
104
|
-
: "Preserve saved work. Use
|
|
104
|
+
: "Preserve saved work. Use routing_unit inspect; reconcile current input/control before retrying the named operation.",
|
|
105
105
|
});
|
|
106
106
|
}
|
|
107
107
|
});
|
|
@@ -341,8 +341,10 @@ export class ToolHandlers {
|
|
|
341
341
|
warnings: prepared.warnings,
|
|
342
342
|
stage: "completed-input preparation",
|
|
343
343
|
finalExchangePending: true,
|
|
344
|
-
|
|
345
|
-
|
|
344
|
+
// The Coordinator reads this receipt too, after delivery; it must not read as "the evidence was not delivered".
|
|
345
|
+
provisional: prepared.ready
|
|
346
|
+
? "Saved. When this turn ends, Freeflow checks the handoff and delivers the report to Coordinator; selected evidence stays in place in the worker's run, where Coordinator sees it."
|
|
347
|
+
: "Saved, but blocked: Coordinator receives nothing until the problems below are fixed. Fix the selection with select_evidence, then call return_work with operation retry.",
|
|
346
348
|
// The text is already in the call arguments; the hash identifies it without a second copy.
|
|
347
349
|
...(isSupplement ? { supplementSha256: bodyHash(h.text) } : { reportSha256: bodyHash(h.text) }),
|
|
348
350
|
evidence: this.assembler.evidenceFacts(state),
|
|
@@ -547,7 +549,7 @@ export class ToolHandlers {
|
|
|
547
549
|
check(
|
|
548
550
|
!idFor(resultId),
|
|
549
551
|
"result_unavailable",
|
|
550
|
-
`${resultId} is a context ref, not a captured result id: name it in the request instead, and the worker selects it as evidence with
|
|
552
|
+
`${resultId} is a context ref, not a captured result id: name it in the request instead, and the worker selects it as evidence with select_evidence.`,
|
|
551
553
|
);
|
|
552
554
|
const grant = await this.resultGrants?.resolve(resultId, this.session.ctx);
|
|
553
555
|
check(grant?.id === resultId, "result_unavailable", `Captured result is unavailable: ${resultId}`);
|
|
@@ -33,30 +33,38 @@ export class ModelControl {
|
|
|
33
33
|
}
|
|
34
34
|
return observed && !samePair(observed, expected) ? { profile: state.profile, expected, observed } : undefined;
|
|
35
35
|
}
|
|
36
|
-
/** Set
|
|
36
|
+
/** Set when a run is prepared before it starts (in beforeRun, or by routing itself); consumed by the run it starts. */
|
|
37
37
|
promptRun = false;
|
|
38
|
-
/** A run that
|
|
38
|
+
/** A run that nothing prepared has started and its first message has not been seen yet. */
|
|
39
39
|
unpreparedRun = false;
|
|
40
40
|
/** The next run is already prepared by routing itself (explicit resume sends the message that starts it). */
|
|
41
41
|
preparedRun() {
|
|
42
42
|
this.promptRun = true;
|
|
43
43
|
}
|
|
44
|
-
/** Pi starts a run: from a user prompt (prepared in beforeRun),
|
|
44
|
+
/** Pi starts a run: from a user prompt or an extension's message (prepared in beforeRun), or a retry. */
|
|
45
45
|
runStarted() {
|
|
46
46
|
this.unpreparedRun = !this.promptRun;
|
|
47
47
|
this.promptRun = false;
|
|
48
48
|
}
|
|
49
49
|
/**
|
|
50
|
-
* Whether this message starts a run an extension's message triggered
|
|
51
|
-
* so routing prepares them here, before their first request
|
|
50
|
+
* Whether this message starts a run an extension's message triggered that beforeRun did not prepare. Pi before 1.2.0
|
|
51
|
+
* skips before_agent_start for those runs, so routing prepares them here, before their first request; from 1.2.0 Pi
|
|
52
|
+
* fires it for them and this returns false. A retry's run starts with no message and is left as is.
|
|
52
53
|
*/
|
|
53
54
|
startedByMessage(message) {
|
|
54
55
|
if (!this.unpreparedRun) return false;
|
|
55
56
|
this.unpreparedRun = false;
|
|
56
57
|
return message?.role === "custom";
|
|
57
58
|
}
|
|
58
|
-
|
|
59
|
-
|
|
59
|
+
/**
|
|
60
|
+
* Prepare a run before its first request. `prompt` and `message` come from before_agent_start, which Pi 1.2.0 and
|
|
61
|
+
* later also fire for a run an extension's message starts; a run routing prepared itself (explicit resume) is not
|
|
62
|
+
* prepared again, so the resumed worker keeps its model. `late` comes from the first message of a run Pi started
|
|
63
|
+
* without before_agent_start.
|
|
64
|
+
*/
|
|
65
|
+
async beforeRun(ctx, trigger = "prompt") {
|
|
66
|
+
if (trigger === "message" && this.promptRun) return;
|
|
67
|
+
if (trigger !== "late") this.promptRun = true;
|
|
60
68
|
this.session.ctx = ctx;
|
|
61
69
|
if (this.session.store && this.session.supported() && !this.session.store.blocked && !this.session.error) {
|
|
62
70
|
if (this.session.stateData().control === "automatic" && isWorkerProfile(this.session.stateData().profile)) {
|
|
@@ -123,7 +131,8 @@ export class ModelControl {
|
|
|
123
131
|
this.session.enabledWorkers(state),
|
|
124
132
|
(provider, modelId) => this.session.ctx?.modelRegistry?.find?.(provider, modelId),
|
|
125
133
|
(model) => keepsCacheAcrossEffort(model, this.session.ctx),
|
|
126
|
-
//
|
|
134
|
+
// The host's default retention, which Freeflow reads and never sets; unset means the short tier. (Pi also takes a
|
|
135
|
+
// per-request cacheRetention option, which Freeflow does not pass.)
|
|
127
136
|
process.env.PI_CACHE_RETENTION === "long" ? "long" : "short",
|
|
128
137
|
);
|
|
129
138
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { annotateSources } from "./provenance.js";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
|
-
import { bodyHash, deliveredSelection } from "./sources.js";
|
|
3
|
+
import { bodyHash, deliveredSelection, isTaskEvidence } from "./sources.js";
|
|
4
4
|
import { canonical, emptySelection, isWorkerProfile, PROFILES, requireCondition as check } from "./types.js";
|
|
5
5
|
import { estimateRequest } from "./budget.js";
|
|
6
6
|
import { occurrenceIdentity, tagDerived } from "../host/projection-tags.js";
|
|
@@ -217,9 +217,18 @@ export function prepareView(options) {
|
|
|
217
217
|
// A structural worker envelope keeps only its calls for the selected results; worker narration is never evidence.
|
|
218
218
|
const structuralWorker =
|
|
219
219
|
role === "assistant" && selective && isWorkerProfile(source.producer) && !full.has(source.ref);
|
|
220
|
-
|
|
220
|
+
// Why a result is not shown, said plainly: "omitted" read as lost evidence, and grok-4.7 Coordinators recovered
|
|
221
|
+
// results that were shown in full beside it (Task 004 S-005).
|
|
222
|
+
const omitted =
|
|
223
|
+
role === "toolResult" && !full.has(source.ref)
|
|
224
|
+
? required.has(source.ref)
|
|
225
|
+
? "[Selected, but not shown: see the evidence problems in the Cognitive Routing Runtime State]"
|
|
226
|
+
: isTaskEvidence(source)
|
|
227
|
+
? "[Not shown: the worker did not select this result as evidence]"
|
|
228
|
+
: "[Routing receipt, not shown]"
|
|
229
|
+
: undefined;
|
|
221
230
|
const renamed = role === "toolResult" ? unpaired.get(source.message.toolCallId) : undefined;
|
|
222
|
-
const variant = `${withheld ? 1 : 0}${structuralWorker ? 1 : 0}${omitted
|
|
231
|
+
const variant = `${withheld ? 1 : 0}${structuralWorker ? 1 : 0}${omitted ?? 0}${renamed === undefined ? "" : `:${renamed}`}`;
|
|
223
232
|
let variants = cache.renderings.get(source.entry);
|
|
224
233
|
if (!variants) cache.renderings.set(source.entry, (variants = new Map()));
|
|
225
234
|
let rendering = variants.get(variant);
|
|
@@ -231,7 +240,7 @@ export function prepareView(options) {
|
|
|
231
240
|
if (omitted)
|
|
232
241
|
message = {
|
|
233
242
|
...message,
|
|
234
|
-
content: [{ type: "text", text:
|
|
243
|
+
content: [{ type: "text", text: omitted }],
|
|
235
244
|
details: undefined,
|
|
236
245
|
};
|
|
237
246
|
if (renamed !== undefined) message = { ...message, toolCallId: renamed };
|
|
@@ -254,6 +263,13 @@ export function prepareView(options) {
|
|
|
254
263
|
const shared = sources.nonWorkerBefore;
|
|
255
264
|
const activeRefs = new Set(associated.flatMap((i) => (i.source ? [i.source.ref] : [])));
|
|
256
265
|
const deferredAt = new Map();
|
|
266
|
+
// Deferred evidence is delivered between call groups: providers such as Anthropic reject anything between a call
|
|
267
|
+
// and its results. A selection is recorded before Pi stores the select_evidence result, so its admission point is
|
|
268
|
+
// that result.
|
|
269
|
+
const afterResults = (at) => {
|
|
270
|
+
while (sources.entries[at]?.message?.role === "toolResult") at++;
|
|
271
|
+
return at;
|
|
272
|
+
};
|
|
257
273
|
if (selective && (options.admissions || options.resumedAt !== undefined))
|
|
258
274
|
for (const source of full.values()) {
|
|
259
275
|
// An attention view omitted required evidence that is no longer in the active context, so after a
|
|
@@ -263,7 +279,8 @@ export function prepareView(options) {
|
|
|
263
279
|
? options.resumedAt
|
|
264
280
|
: undefined;
|
|
265
281
|
const admitted = options.admissions?.get(source.ref);
|
|
266
|
-
const
|
|
282
|
+
const latest = admitted === undefined ? resumed : Math.max(admitted, resumed ?? 0),
|
|
283
|
+
at = latest === undefined ? undefined : afterResults(latest),
|
|
267
284
|
own = rank.get(source.entry.id) ?? 0;
|
|
268
285
|
if (at === undefined || at <= own || !isWorkerProfile(source.producer) || shared[at] <= shared[own + 1]) continue;
|
|
269
286
|
const group = sources.exchange(source).sources;
|
|
@@ -408,7 +425,7 @@ export function prepareView(options) {
|
|
|
408
425
|
a.id,
|
|
409
426
|
options.compactedInAssignment === undefined
|
|
410
427
|
? a.contract
|
|
411
|
-
: `${a.contract}\n\nCompaction: you compacted during this assignment, and cycle ${options.compactedInAssignment} began then. A compaction this contract asks for is done: continue with the rest of the contract, or return it with
|
|
428
|
+
: `${a.contract}\n\nCompaction: you compacted during this assignment, and cycle ${options.compactedInAssignment} began then. A compaction this contract asks for is done: continue with the rest of the contract, or return it with return_work if compacting was all it asked.`,
|
|
412
429
|
);
|
|
413
430
|
}
|
|
414
431
|
if (baseReport) {
|
|
@@ -56,8 +56,10 @@ export function annotateSources(
|
|
|
56
56
|
isWorkerProfile(s.producer) && isTaskEvidence(s) && full.has(s.ref)
|
|
57
57
|
? "task evidence; selection checks apply"
|
|
58
58
|
: "not offered for new evidence selection";
|
|
59
|
+
// The call's path or command lets a reader match this ref to the result above it.
|
|
60
|
+
const label = s.message.toolName ? sources.callLabel(s.ref) : undefined;
|
|
59
61
|
return [
|
|
60
|
-
`${s.ref} | producer: ${s.producer} | ${s.message.role}${s.message.toolName ? ` | ${s.message.toolName}` : ""}${s.assignmentId ? ` | assignment: ${s.assignmentId}` : ""} | ${representation} | ${selection}`,
|
|
62
|
+
`${s.ref} | producer: ${s.producer} | ${s.message.role}${s.message.toolName ? ` | ${s.message.toolName}${label ? `: ${label}` : ""}` : ""}${s.assignmentId ? ` | assignment: ${s.assignmentId}` : ""} | ${representation} | ${selection}`,
|
|
61
63
|
];
|
|
62
64
|
});
|
|
63
65
|
const keys = new Set(attributed.map((s) => `${s.producer}\u0000${s.assignmentId ?? ""}`));
|
|
@@ -9,14 +9,14 @@ const locatorText = (locator) => {
|
|
|
9
9
|
return locator.label ? `${locator.label}${locator.truncated ? " [truncated]" : ""} · ${identity}` : identity;
|
|
10
10
|
};
|
|
11
11
|
function title(name, operation) {
|
|
12
|
-
if (name === "
|
|
13
|
-
if (name === "
|
|
12
|
+
if (name === "delegate_work") return operation === "replace" ? "Replace assignment" : "Delegate assignment";
|
|
13
|
+
if (name === "return_work")
|
|
14
14
|
return operation === "retry"
|
|
15
15
|
? "Retry return"
|
|
16
16
|
: operation === "supplement"
|
|
17
17
|
? "Return recovery supplement"
|
|
18
18
|
: "Return to Coordinator";
|
|
19
|
-
if (name === "
|
|
19
|
+
if (name === "select_evidence")
|
|
20
20
|
return (
|
|
21
21
|
{
|
|
22
22
|
add: "Select evidence",
|
|
@@ -25,7 +25,7 @@ function title(name, operation) {
|
|
|
25
25
|
list: "List evidence",
|
|
26
26
|
}[operation ?? ""] ?? "Evidence"
|
|
27
27
|
);
|
|
28
|
-
if (name === "
|
|
28
|
+
if (name === "routing_unit")
|
|
29
29
|
return (
|
|
30
30
|
{
|
|
31
31
|
inspect: "Inspect routing",
|
|
@@ -41,14 +41,14 @@ function title(name, operation) {
|
|
|
41
41
|
}
|
|
42
42
|
function draft(name, args, expanded) {
|
|
43
43
|
const sections = [];
|
|
44
|
-
if (name === "
|
|
45
|
-
if (name === "
|
|
46
|
-
if (name === "
|
|
47
|
-
if (name === "
|
|
44
|
+
if (name === "delegate_work") sections.push(text(args.contract));
|
|
45
|
+
if (name === "return_work") sections.push(text(args.report));
|
|
46
|
+
if (name === "routing_unit" && args.operation === "close") sections.push(text(args.assessment));
|
|
47
|
+
if (name === "routing_unit" && args.operation === "recover") sections.push(text(args.request));
|
|
48
48
|
// Metadata can arrive before the main text and otherwise occupy the entire
|
|
49
49
|
// trailing preview forever. Keep the live prose moving; expansion keeps all fields.
|
|
50
50
|
if (!expanded && sections.length) return sections.filter(Boolean).join("\n\n");
|
|
51
|
-
if (name === "
|
|
51
|
+
if (name === "select_evidence" && Array.isArray(args.refs))
|
|
52
52
|
sections.push(args.refs.filter((r) => typeof r === "string").join("\n"));
|
|
53
53
|
for (const key of ["scope", "view", "ref", "cursor"])
|
|
54
54
|
if (typeof args[key] === "string") sections.push(`${capital(key)}: ${args[key]}`);
|
|
@@ -63,13 +63,13 @@ export function renderRoutingCall(name, args = {}, context = {}) {
|
|
|
63
63
|
if (context.isPartial === false) return new Text(heading, 0, 0);
|
|
64
64
|
const writing = context.argsComplete !== true;
|
|
65
65
|
const noun =
|
|
66
|
-
name === "
|
|
66
|
+
name === "delegate_work"
|
|
67
67
|
? "assignment"
|
|
68
|
-
: name === "
|
|
68
|
+
: name === "return_work"
|
|
69
69
|
? args.operation === "supplement"
|
|
70
70
|
? "supplement"
|
|
71
71
|
: "report"
|
|
72
|
-
: name === "
|
|
72
|
+
: name === "routing_unit" && args.operation === "close"
|
|
73
73
|
? "assessment"
|
|
74
74
|
: undefined;
|
|
75
75
|
const body = draft(name, args, context.expanded === true);
|
|
@@ -108,8 +108,8 @@ function summary(receipt, name, args) {
|
|
|
108
108
|
return reasons[receipt.code] || text(receipt.message).split("\n")[0] || "Operation needs attention";
|
|
109
109
|
}
|
|
110
110
|
if (receipt.status === "suspended") return "Couldn’t restore evidence · Assessment remains paused";
|
|
111
|
-
if (name === "
|
|
112
|
-
if (name === "
|
|
111
|
+
if (name === "delegate_work") return `${operation === "replace" ? "Replacement" : "Assignment"} saved`;
|
|
112
|
+
if (name === "select_evidence") {
|
|
113
113
|
const selected = (receipt.selected ?? receipt.evidence?.selected ?? []).length;
|
|
114
114
|
if (operation === "list")
|
|
115
115
|
return `${count(receipt.count ?? receipt.items?.length ?? 0, "source")}${Number.isInteger(receipt.selectableCount) ? ` · ${receipt.selectableCount} selectable` : ""}`;
|
|
@@ -132,7 +132,7 @@ function summary(receipt, name, args) {
|
|
|
132
132
|
receipt.items.every((i) => ["already_selected", "unchanged"].includes(i.status));
|
|
133
133
|
return `${unchanged ? "No changes · " : ""}${count(selected, "source")} selected${receipt.ready === false ? ` · ${issue}` : operation === "inspect" ? " · No unresolved references" : ""}`;
|
|
134
134
|
}
|
|
135
|
-
if (name === "
|
|
135
|
+
if (name === "routing_unit") {
|
|
136
136
|
if (operation === "close") return capital(receipt.outcome ?? "closed");
|
|
137
137
|
if (operation === "recover") return "Recovery request saved";
|
|
138
138
|
if (operation === "cancel-recovery") return "Evidence recovery cancelled";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { findRoutingSkill } from "./bootstrap.js";
|
|
2
3
|
import { ContextAssembler } from "./assembler.js";
|
|
3
4
|
import { EventStore } from "./event-store.js";
|
|
4
5
|
import { ToolGate } from "./gate.js";
|
|
@@ -29,6 +30,7 @@ export class RoutingRuntime {
|
|
|
29
30
|
constructor(pi, packageRoots = []) {
|
|
30
31
|
this.pi = pi;
|
|
31
32
|
this.packageRoots = packageRoots;
|
|
33
|
+
this.session.routingSkill = findRoutingSkill(this.packageRoots);
|
|
32
34
|
this.models = new ModelControl(this.session, this.pi);
|
|
33
35
|
this.status = new RoutingStatus(this.session, this.models);
|
|
34
36
|
this.gate = new ToolGate(this.session, this.packageRoots);
|
|
@@ -280,8 +282,8 @@ export class RoutingRuntime {
|
|
|
280
282
|
}
|
|
281
283
|
return true;
|
|
282
284
|
}
|
|
283
|
-
beforeRun(ctx,
|
|
284
|
-
return this.models.beforeRun(ctx,
|
|
285
|
+
beforeRun(ctx, trigger) {
|
|
286
|
+
return this.models.beforeRun(ctx, trigger);
|
|
285
287
|
}
|
|
286
288
|
runStarted() {
|
|
287
289
|
this.models.runStarted();
|
|
@@ -40,7 +40,7 @@ const report = (name) =>
|
|
|
40
40
|
["operation", "report", "outcome"],
|
|
41
41
|
);
|
|
42
42
|
export const ROUTING_SCHEMAS = {
|
|
43
|
-
|
|
43
|
+
delegate_work: {
|
|
44
44
|
oneOf: [
|
|
45
45
|
object({ operation: operation("assign"), contract: string(), worker }, ["operation", "contract"]),
|
|
46
46
|
object({ operation: operation("replace"), contract: string(), reason: string(2048), worker }, [
|
|
@@ -50,8 +50,8 @@ export const ROUTING_SCHEMAS = {
|
|
|
50
50
|
]),
|
|
51
51
|
],
|
|
52
52
|
},
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
return_work: { oneOf: [report("submit"), report("supplement"), object({ operation: operation("retry") })] },
|
|
54
|
+
routing_unit: {
|
|
55
55
|
oneOf: [
|
|
56
56
|
object(
|
|
57
57
|
{
|
|
@@ -81,7 +81,7 @@ export const ROUTING_SCHEMAS = {
|
|
|
81
81
|
}),
|
|
82
82
|
],
|
|
83
83
|
},
|
|
84
|
-
|
|
84
|
+
select_evidence: {
|
|
85
85
|
oneOf: [
|
|
86
86
|
object(
|
|
87
87
|
{
|
|
@@ -18,6 +18,8 @@ export class RoutingSession {
|
|
|
18
18
|
capability;
|
|
19
19
|
token = randomUUID();
|
|
20
20
|
turn;
|
|
21
|
+
/** The packaged routing skill and role references the bootstrap read must cover; undefined when the skill cannot be read. */
|
|
22
|
+
routingSkill;
|
|
21
23
|
messages = [];
|
|
22
24
|
error;
|
|
23
25
|
projectionError;
|
|
@@ -216,6 +218,6 @@ export class RoutingSession {
|
|
|
216
218
|
workerBasis(state, assignmentId) {
|
|
217
219
|
const recovery = state.recoveryId ? state.recoveries.get(state.recoveryId) : undefined;
|
|
218
220
|
if (recovery?.state === "reading") return state.handoffs.get(recovery.requestHandoffId)?.basisUserEntryId ?? null;
|
|
219
|
-
return this.taskBasis(state, assignmentId);
|
|
221
|
+
return this.taskBasis(state, assignmentId) ?? null;
|
|
220
222
|
}
|
|
221
223
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { sessionEntryToContextMessages } from "@earendil-works/pi-coding-agent";
|
|
3
|
-
import { canonical, idFor, isWorkerProfile, refFor } from "./types.js";
|
|
3
|
+
import { canonical, idFor, isWorkerProfile, refFor, ROUTING_TOOLS, routingToolName } from "./types.js";
|
|
4
4
|
import { isHostEdited, isProjected, projectedEntryId } from "../host/projection-tags.js";
|
|
5
5
|
export const bodyHash = (value) => createHash("sha256").update(canonical(value)).digest("hex");
|
|
6
6
|
/** Define a memoized hash so history is hashed only when a consumer needs it. */
|
|
@@ -11,11 +11,11 @@ function lazyHash(target, compute) {
|
|
|
11
11
|
get: () => (value ??= compute()),
|
|
12
12
|
});
|
|
13
13
|
}
|
|
14
|
-
const routingTools = new Set(
|
|
14
|
+
const routingTools = new Set(ROUTING_TOOLS);
|
|
15
15
|
// Discovery, new selections, and delivery share this policy: evidence is what tools produced. What a worker
|
|
16
16
|
// says belongs in its report, so its assistant messages are never evidence.
|
|
17
17
|
export function isTaskEvidence(source) {
|
|
18
|
-
return source.message.role === "toolResult" && !routingTools.has(source.message.toolName);
|
|
18
|
+
return source.message.role === "toolResult" && !routingTools.has(routingToolName(source.message.toolName));
|
|
19
19
|
}
|
|
20
20
|
/** Saved selections keep their delivery, except assistant messages (whole or `#text`), which are never evidence. */
|
|
21
21
|
export function deliveredSelection(sources, ref) {
|
|
@@ -83,7 +83,7 @@ export class Sources {
|
|
|
83
83
|
const source = lazyHash({ ref: refFor(entry.id), entry, message, producer: "common", active: false }, () =>
|
|
84
84
|
bodyHash(message),
|
|
85
85
|
);
|
|
86
|
-
if (message.role === "toolResult" && message.toolName === "
|
|
86
|
+
if (message.role === "toolResult" && routingToolName(message.toolName) === "return_work") {
|
|
87
87
|
for (const block of message.content ?? [])
|
|
88
88
|
if (block.type === "text") {
|
|
89
89
|
try {
|
|
@@ -281,6 +281,31 @@ export class Sources {
|
|
|
281
281
|
);
|
|
282
282
|
return matches.length === 1 ? locatorForCall(group.assistant, matches[0], this.locatorProvider) : undefined;
|
|
283
283
|
}
|
|
284
|
+
/**
|
|
285
|
+
* What a tool result's call did, in a few words: its path, command or first short string argument. Coordinator
|
|
286
|
+
* matches a selected ref to the result in its view by this label (Task 004 S-005); undefined when unknown.
|
|
287
|
+
*/
|
|
288
|
+
callLabel(ref) {
|
|
289
|
+
const source = this.byRef.get(ref);
|
|
290
|
+
if (!source || source.message.role !== "toolResult") return undefined;
|
|
291
|
+
const call = (this.owners.get(source.ref)?.assistant.message.content ?? []).find(
|
|
292
|
+
(block) =>
|
|
293
|
+
block.type === "toolCall" && block.id === source.message.toolCallId && block.name === source.message.toolName,
|
|
294
|
+
);
|
|
295
|
+
const args = call?.arguments;
|
|
296
|
+
if (!args || typeof args !== "object") return undefined;
|
|
297
|
+
// A path keeps its end, where the file name is; anything else keeps its start.
|
|
298
|
+
const path = typeof args.path === "string" ? args.path.trim() : "";
|
|
299
|
+
if (path) return path.length > 100 ? `...${path.slice(-97)}` : path;
|
|
300
|
+
const chosen =
|
|
301
|
+
["command", "pattern", "query", "url"]
|
|
302
|
+
.map((key) => args[key])
|
|
303
|
+
.find((value) => typeof value === "string" && value.trim()) ??
|
|
304
|
+
Object.values(args).find((value) => typeof value === "string" && value.trim() && value.length <= 200);
|
|
305
|
+
const label = normalizeLabel(chosen)?.label;
|
|
306
|
+
if (!label) return undefined;
|
|
307
|
+
return label.length > 100 ? `${label.slice(0, 97)}...` : label;
|
|
308
|
+
}
|
|
284
309
|
ordered(sources) {
|
|
285
310
|
const ids = new Set([...sources].map((s) => s.ref));
|
|
286
311
|
return this.entries.flatMap((entry) => {
|
|
@@ -115,7 +115,7 @@ function applyEvent(state, event, owned = false) {
|
|
|
115
115
|
for (const profile of PROFILES) {
|
|
116
116
|
if (!Object.hasOwn(d.overrides, profile)) continue;
|
|
117
117
|
const override = d.overrides[profile];
|
|
118
|
-
if (override === null) s.profileOverrides.delete(profile);
|
|
118
|
+
if (override === null || override === undefined) s.profileOverrides.delete(profile);
|
|
119
119
|
else s.profileOverrides.set(profile, override);
|
|
120
120
|
}
|
|
121
121
|
break;
|
|
@@ -242,8 +242,8 @@ function applyEvent(state, event, owned = false) {
|
|
|
242
242
|
d.recovery.supplementRevision === 0 &&
|
|
243
243
|
d.recovery.supplementHandoffId === undefined &&
|
|
244
244
|
d.recovery.cancellationReason === undefined &&
|
|
245
|
-
s.assessment?.handoffId === base?.id &&
|
|
246
245
|
base?.kind === "return" &&
|
|
246
|
+
s.assessment?.handoffId === base.id &&
|
|
247
247
|
a.returnHandoffId === base.id &&
|
|
248
248
|
base.state === "configured" &&
|
|
249
249
|
base.reportRevision === d.recovery.baseReportRevision &&
|
|
@@ -267,9 +267,13 @@ function applyEvent(state, event, owned = false) {
|
|
|
267
267
|
s.handoffs.set(h.id, h);
|
|
268
268
|
s.pendingId = h.id;
|
|
269
269
|
s.assessment.view = "suspended";
|
|
270
|
-
s
|
|
271
|
-
|
|
272
|
-
s.assessment.
|
|
270
|
+
// A recovery answers the Coordinator's evidence question, not new user input: an assessment already waiting
|
|
271
|
+
// for that attention keeps waiting for it after the recovery, instead of resuming when the supplement arrives.
|
|
272
|
+
if (s.assessment.suspensionReason !== "user-attention") {
|
|
273
|
+
s.assessment.suspensionReason = "recovery";
|
|
274
|
+
s.assessment.basisUserEntryId = h.basisUserEntryId;
|
|
275
|
+
s.assessment.problems = [];
|
|
276
|
+
}
|
|
273
277
|
break;
|
|
274
278
|
}
|
|
275
279
|
case "recovery-supplement-accepted": {
|
|
@@ -281,7 +285,8 @@ function applyEvent(state, event, owned = false) {
|
|
|
281
285
|
s.control === "automatic" &&
|
|
282
286
|
s.profile === assignmentWorker() &&
|
|
283
287
|
a?.state === "returned" &&
|
|
284
|
-
r
|
|
288
|
+
r !== undefined &&
|
|
289
|
+
r.id === s.recoveryId &&
|
|
285
290
|
r.assignmentId === a.id &&
|
|
286
291
|
s.assessment?.handoffId === r.assessmentHandoffId &&
|
|
287
292
|
s.handoffs.get(r.assessmentHandoffId)?.reportRevision === r.baseReportRevision &&
|
|
@@ -308,7 +313,8 @@ function applyEvent(state, event, owned = false) {
|
|
|
308
313
|
check(
|
|
309
314
|
s.control === "automatic" &&
|
|
310
315
|
s.profile === "coordinator" &&
|
|
311
|
-
r
|
|
316
|
+
r !== undefined &&
|
|
317
|
+
r.id === s.recoveryId &&
|
|
312
318
|
["requested", "reading", "returning"].includes(r.state),
|
|
313
319
|
"recovery_not_cancellable",
|
|
314
320
|
);
|
|
@@ -421,7 +427,11 @@ function applyEvent(state, event, owned = false) {
|
|
|
421
427
|
const u = s.units.get(d.unitId),
|
|
422
428
|
a = assignment();
|
|
423
429
|
check(
|
|
424
|
-
s.control === "automatic" &&
|
|
430
|
+
s.control === "automatic" &&
|
|
431
|
+
s.profile === "coordinator" &&
|
|
432
|
+
u !== undefined &&
|
|
433
|
+
u.id === s.unitId &&
|
|
434
|
+
u.state === "open",
|
|
425
435
|
"unit_not_open",
|
|
426
436
|
);
|
|
427
437
|
if (d.outcome === "accepted")
|
|
@@ -19,7 +19,9 @@ export class RoutingStatus {
|
|
|
19
19
|
} catch {
|
|
20
20
|
state = initialState();
|
|
21
21
|
}
|
|
22
|
-
|
|
22
|
+
// A model or effort named on Pi's command line holds routing at startup (`startup_selection`). Its store is not
|
|
23
|
+
// reconciled then, so the store's own reason would hide why (Task 008 S-005).
|
|
24
|
+
const blocked = this.session.error ?? (this.session.suppressed ? "startup_selection" : this.session.store?.blocked);
|
|
23
25
|
return {
|
|
24
26
|
effective: this.session.supported() && state.control !== "inactive" && !blocked,
|
|
25
27
|
activeProfile: state.profile,
|
|
@@ -30,8 +32,7 @@ export class RoutingStatus {
|
|
|
30
32
|
: this.session.supported() && state.control !== "inactive"
|
|
31
33
|
? "active"
|
|
32
34
|
: "inactive",
|
|
33
|
-
runtimeReason:
|
|
34
|
-
blocked ?? (this.session.suppressed ? "startup_selection" : this.session.capability?.blockingReason.message),
|
|
35
|
+
runtimeReason: blocked ?? this.session.capability?.blockingReason.message,
|
|
35
36
|
projectionFailure: this.session.projectionError,
|
|
36
37
|
...((pending) => (pending ? { pendingPair: `${pending.modelId}/${pending.thinking}` } : {}))(
|
|
37
38
|
this.models.pending(),
|