@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
|
@@ -4,59 +4,38 @@ import { fileURLToPath } from "node:url";
|
|
|
4
4
|
* their files live.
|
|
5
5
|
*/
|
|
6
6
|
export const WORKFLOW_COMMANDS = [
|
|
7
|
-
{ command: "discuss", skill: "discuss" },
|
|
8
|
-
{ command: "action-selection", skill: "action-selection" },
|
|
7
|
+
{ command: "discuss-work", skill: "discuss-work" },
|
|
9
8
|
{ command: "track-work", skill: "track-work" },
|
|
10
|
-
{ command: "write-spec", skill: "write-spec" },
|
|
11
|
-
{ command: "review-artifact", skill: "review-artifact" },
|
|
12
|
-
{ command: "write-plan", skill: "write-plan" },
|
|
13
9
|
{ command: "write-docs", skill: "write-docs" },
|
|
14
10
|
{ command: "execute-work", skill: "execute-work" },
|
|
15
|
-
{ command: "simplify-code", skill: "simplify-code" },
|
|
16
|
-
{ command: "migration-work", skill: "migration-work" },
|
|
17
|
-
{ command: "diagnose-failure", skill: "diagnose-failure" },
|
|
18
11
|
{ command: "verify-work", skill: "verify-work" },
|
|
19
12
|
{ command: "review-work", skill: "review-work" },
|
|
20
|
-
{ command: "
|
|
21
|
-
{ command: "handoff", skill: "handoff" },
|
|
22
|
-
{ command: "finish-branch", skill: "finish-branch" },
|
|
23
|
-
{ command: "release-work", skill: "release-work" },
|
|
24
|
-
{ command: "launch-work", skill: "launch-work" },
|
|
25
|
-
{ command: "bypass", skill: "bypass" },
|
|
13
|
+
{ command: "deliver-work", skill: "deliver-work" },
|
|
26
14
|
];
|
|
27
|
-
export const CONTRIBUTOR_COMMANDS = ["setup-freeflow", "write-skill", "evaluate-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"discuss",
|
|
37
|
-
"evaluate-skill",
|
|
15
|
+
export const CONTRIBUTOR_COMMANDS = ["setup-freeflow", "write-skill", "evaluate-behavior"];
|
|
16
|
+
/**
|
|
17
|
+
* Every portable skill Freeflow ships, by directory name. The canonical list: validation scripts and tests compare the
|
|
18
|
+
* skills directory, the package and the coverage matrix against it instead of a fixed count.
|
|
19
|
+
*/
|
|
20
|
+
export const FREEFLOW_MODEL_SKILL_NAMES = [
|
|
21
|
+
"deliver-work",
|
|
22
|
+
"discuss-work",
|
|
23
|
+
"evaluate-behavior",
|
|
38
24
|
"execute-work",
|
|
39
|
-
"finish-branch",
|
|
40
|
-
"handoff",
|
|
41
|
-
"release-work",
|
|
42
|
-
"review-artifact",
|
|
43
25
|
"review-work",
|
|
44
26
|
"setup-freeflow",
|
|
45
|
-
"launch-work",
|
|
46
|
-
"simplify-code",
|
|
47
27
|
"track-work",
|
|
48
28
|
"verify-work",
|
|
49
|
-
"workflow",
|
|
50
29
|
"write-docs",
|
|
51
|
-
"write-plan",
|
|
52
30
|
"write-skill",
|
|
53
|
-
"write-spec",
|
|
54
31
|
];
|
|
55
32
|
export const STABLE_FREEFLOW_SURFACE = Object.freeze({
|
|
56
33
|
enabled: true,
|
|
57
34
|
cognitiveRouting: { effective: true },
|
|
58
35
|
toolExecution: { effective: true },
|
|
59
36
|
compaction: { effective: true },
|
|
37
|
+
taskMemory: { effective: true },
|
|
38
|
+
messaging: { effective: true },
|
|
60
39
|
});
|
|
61
40
|
export function freeflowSkillPath(skillName) {
|
|
62
41
|
return fileURLToPath(new URL(`../../../skills/${skillName}/SKILL.md`, import.meta.url));
|
|
@@ -65,10 +44,16 @@ export function freeflowCapabilitySkillPath(skillName) {
|
|
|
65
44
|
return fileURLToPath(new URL(`../../../capabilities/${skillName}/SKILL.md`, import.meta.url));
|
|
66
45
|
}
|
|
67
46
|
export function freeflowModelSkillPaths(capabilityState = undefined) {
|
|
68
|
-
|
|
47
|
+
// A skill the user turned off is not loaded (config-validation.ts SWITCHABLE_SKILLS).
|
|
48
|
+
const off = capabilityState?.skills?.off ?? [];
|
|
49
|
+
const paths = FREEFLOW_MODEL_SKILL_NAMES.filter((skillName) => !off.includes(skillName)).map((skillName) =>
|
|
50
|
+
freeflowSkillPath(skillName),
|
|
51
|
+
);
|
|
69
52
|
if (capabilityState?.cognitiveRouting?.effective === true)
|
|
70
53
|
paths.push(freeflowCapabilitySkillPath("cognitive-routing"));
|
|
71
54
|
if (capabilityState?.compaction?.effective === true) paths.push(freeflowCapabilitySkillPath("compaction"));
|
|
55
|
+
if (capabilityState?.taskMemory?.effective === true) paths.push(freeflowCapabilitySkillPath("task-memory"));
|
|
56
|
+
if (capabilityState?.messaging?.effective === true) paths.push(freeflowCapabilitySkillPath("messaging"));
|
|
72
57
|
return paths;
|
|
73
58
|
}
|
|
74
59
|
export function skillPrompt(skill, args) {
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { validateCompactionConfig } from "../compaction/config.js";
|
|
2
|
+
import { validateTaskMemoryConfig } from "../task-memory/config.js";
|
|
3
|
+
import { validateMessagingConfig } from "../messaging/config.js";
|
|
4
|
+
import { validateToolExecutionConfig } from "../tool-execution/config.js";
|
|
5
|
+
import { FREEFLOW_MODEL_SKILL_NAMES } from "./catalog.js";
|
|
6
|
+
/**
|
|
7
|
+
* Whether a Freeflow config file is valid: one answer for Pi and for the session-start hooks of the other hosts, which
|
|
8
|
+
* import the built file. Also how the settings every host reads, `prompts`, `review` and `skills`, layer. Keep this module and its
|
|
9
|
+
* imports free of Pi packages; the hooks run in plain Node.
|
|
10
|
+
*/
|
|
11
|
+
function isRecord(value) {
|
|
12
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
13
|
+
}
|
|
14
|
+
/** The runtime prompts Freeflow delivers on every host, each on unless a config turns it off. */
|
|
15
|
+
export const RUNTIME_PROMPTS = ["core", "interactionContract", "workingMethod"];
|
|
16
|
+
/** The keys `prompts` accepts: a master switch for all three, and one switch per prompt. */
|
|
17
|
+
export const PROMPTS_KEYS = ["enabled", ...RUNTIME_PROMPTS];
|
|
18
|
+
/** A section's master switch (`prompts.enabled`, `skills.enabled`) for two valid layers; on unless a config turns it off. */
|
|
19
|
+
export function sectionEnabled(repository, local, section) {
|
|
20
|
+
const layer = (config) => (isRecord(config?.[section]) ? config[section] : {});
|
|
21
|
+
return (layer(local).enabled ?? layer(repository).enabled ?? true) === true;
|
|
22
|
+
}
|
|
23
|
+
function validatePromptsConfig(value) {
|
|
24
|
+
if (!isRecord(value)) return "prompts must be an object";
|
|
25
|
+
const unknown = Object.keys(value).find((key) => !PROMPTS_KEYS.includes(key));
|
|
26
|
+
if (unknown) return `prompts has unsupported key: ${unknown}`;
|
|
27
|
+
for (const key of PROMPTS_KEYS)
|
|
28
|
+
if (value[key] !== undefined && typeof value[key] !== "boolean") return `prompts.${key} must be a boolean`;
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* The prompts Freeflow delivers for two valid config layers: the personal layer overrides the repository's, and the
|
|
33
|
+
* master switch off turns all three off.
|
|
34
|
+
*/
|
|
35
|
+
export function selectedRuntimePrompts(repository, local) {
|
|
36
|
+
const layer = (config) => (isRecord(config?.prompts) ? config.prompts : {});
|
|
37
|
+
const [shared, personal] = [layer(repository), layer(local)];
|
|
38
|
+
const master = sectionEnabled(repository, local, "prompts");
|
|
39
|
+
const selection = {};
|
|
40
|
+
for (const key of RUNTIME_PROMPTS) selection[key] = master && (personal[key] ?? shared[key] ?? true) === true;
|
|
41
|
+
return selection;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The skills a user can turn off. Setup Freeflow always stays, so a user can repair a configuration; the capability
|
|
45
|
+
* skills follow their capability's switch. On Pi an off skill is not loaded; other hosts load plugin skills themselves,
|
|
46
|
+
* so there the Runtime State only tells the agent not to use it.
|
|
47
|
+
*/
|
|
48
|
+
export const SWITCHABLE_SKILLS = FREEFLOW_MODEL_SKILL_NAMES.filter((name) => name !== "setup-freeflow").sort();
|
|
49
|
+
/** The keys `skills` accepts: a master switch for every switchable skill, and one switch per skill. */
|
|
50
|
+
export const SKILLS_KEYS = ["enabled", ...SWITCHABLE_SKILLS];
|
|
51
|
+
function validateSkillsConfig(value) {
|
|
52
|
+
if (!isRecord(value)) return "skills must be an object";
|
|
53
|
+
const unknown = Object.keys(value).find((key) => !SKILLS_KEYS.includes(key));
|
|
54
|
+
if (unknown) return `skills has unsupported key: ${unknown}; use ${SKILLS_KEYS.join(", ")}`;
|
|
55
|
+
for (const [key, on] of Object.entries(value)) if (typeof on !== "boolean") return `skills.${key} must be a boolean`;
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* The skills turned off for two valid config layers: the personal layer overrides the repository's, skill by skill,
|
|
60
|
+
* and the master switch off turns every switchable skill off.
|
|
61
|
+
*/
|
|
62
|
+
export function skillsOff(repository, local) {
|
|
63
|
+
const layer = (config) => (isRecord(config?.skills) ? config.skills : {});
|
|
64
|
+
const [shared, personal] = [layer(repository), layer(local)];
|
|
65
|
+
if (!sectionEnabled(repository, local, "skills")) return [...SWITCHABLE_SKILLS];
|
|
66
|
+
return SWITCHABLE_SKILLS.filter((name) => (personal[name] ?? shared[name] ?? true) === false);
|
|
67
|
+
}
|
|
68
|
+
/** The Runtime State line for skills the user turned off; none while every skill is on. */
|
|
69
|
+
export function skillsStateLine(off) {
|
|
70
|
+
return off?.length ? `Skills off: ${off.map((name) => `\`${name}\``).join(", ")}; do not open or follow them.` : null;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* How Review Work reviews finished work. Without the key it runs `auto`, its own risk gate; `off` and `always` are the
|
|
74
|
+
* user's choice for every task, and `model` names the model a reviewer runs on. `auto` is accepted so a personal file
|
|
75
|
+
* can return a shared `off` or `always` to the default.
|
|
76
|
+
*/
|
|
77
|
+
export const REVIEW_MODES = ["auto", "off", "always"];
|
|
78
|
+
export const REVIEW_KEYS = ["mode", "model"];
|
|
79
|
+
function validateReviewConfig(value) {
|
|
80
|
+
if (!isRecord(value)) return "review must be an object";
|
|
81
|
+
const unknown = Object.keys(value).find((key) => !REVIEW_KEYS.includes(key));
|
|
82
|
+
if (unknown) return `review has unsupported key: ${unknown}`;
|
|
83
|
+
if (value.mode !== undefined && !REVIEW_MODES.includes(value.mode))
|
|
84
|
+
return `review.mode must be one of ${REVIEW_MODES.join(", ")}`;
|
|
85
|
+
// The model is shown to the agent in the Runtime State, so it must fit on one line in a code span.
|
|
86
|
+
if (
|
|
87
|
+
value.model !== undefined &&
|
|
88
|
+
(typeof value.model !== "string" || !value.model.trim() || /[\p{Cc}`]/u.test(value.model))
|
|
89
|
+
)
|
|
90
|
+
return "review.model must be a non-empty model name on one line";
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
/** Review settings for two valid config layers: the personal layer overrides the repository's, field by field. */
|
|
94
|
+
export function resolveReviewSettings(repository, local) {
|
|
95
|
+
const layer = (config) => (isRecord(config?.review) ? config.review : {});
|
|
96
|
+
const [shared, personal] = [layer(repository), layer(local)];
|
|
97
|
+
const mode = personal.mode ?? shared.mode ?? "auto";
|
|
98
|
+
const model = personal.model ?? shared.model;
|
|
99
|
+
return model === undefined ? { mode } : { mode, model };
|
|
100
|
+
}
|
|
101
|
+
/** The Runtime State line for the user's review choice; none for the default, `auto` with no model. */
|
|
102
|
+
export function reviewStateLine(review) {
|
|
103
|
+
if (!review || (review.mode === "auto" && review.model === undefined)) return null;
|
|
104
|
+
return `Review: \`${review.mode}\`${review.model === undefined ? "" : `, reviewer model \`${review.model}\``}`;
|
|
105
|
+
}
|
|
106
|
+
function removedContextConfigError(value, filePath) {
|
|
107
|
+
const removedKeys = ["contextVirtualization", "conversationHistory"].filter((key) =>
|
|
108
|
+
Object.prototype.hasOwnProperty.call(value, key),
|
|
109
|
+
);
|
|
110
|
+
return removedKeys.length > 0
|
|
111
|
+
? `Removed Freeflow context setting(s) in ${filePath}: ${removedKeys.join(", ")}. Delete those keys before using Freeflow.`
|
|
112
|
+
: null;
|
|
113
|
+
}
|
|
114
|
+
function validateCoreConfigFields(value) {
|
|
115
|
+
if (value.enabled !== undefined && typeof value.enabled !== "boolean") {
|
|
116
|
+
return "enabled must be a boolean";
|
|
117
|
+
}
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
/** The top-level keys Freeflow acts on; the settings test checks that every key under them has a settings row. */
|
|
121
|
+
export const CONFIG_SECTIONS = [
|
|
122
|
+
"enabled",
|
|
123
|
+
"cognitiveRouting",
|
|
124
|
+
"toolExecution",
|
|
125
|
+
"compaction",
|
|
126
|
+
"taskMemory",
|
|
127
|
+
"messaging",
|
|
128
|
+
"prompts",
|
|
129
|
+
"review",
|
|
130
|
+
"skills",
|
|
131
|
+
];
|
|
132
|
+
// outputRouter, observedRouting, and scriptTransform belong to retired features; they are accepted and ignored so
|
|
133
|
+
// existing configs keep loading.
|
|
134
|
+
const RETIRED_SECTIONS = ["outputRouter", "observedRouting", "scriptTransform"];
|
|
135
|
+
export function validateRepositoryConfig(value) {
|
|
136
|
+
if (!isRecord(value)) {
|
|
137
|
+
return "config must be a JSON object";
|
|
138
|
+
}
|
|
139
|
+
const removedContextError = removedContextConfigError(value, ".freeflow/config.json");
|
|
140
|
+
if (removedContextError) return removedContextError;
|
|
141
|
+
const allowedKeys = new Set([...CONFIG_SECTIONS, ...RETIRED_SECTIONS]);
|
|
142
|
+
for (const key of Object.keys(value)) {
|
|
143
|
+
if (!allowedKeys.has(key)) {
|
|
144
|
+
return `unsupported top-level config key: ${key}`;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
const coreError = validateCoreConfigFields(value);
|
|
148
|
+
if (coreError) return coreError;
|
|
149
|
+
for (const key of ["outputRouter", "observedRouting", "scriptTransform"]) {
|
|
150
|
+
if (value[key] !== undefined && !isRecord(value[key])) {
|
|
151
|
+
return `${key} must be an object`;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (value.toolExecution !== undefined) {
|
|
155
|
+
const error = validateToolExecutionConfig(value.toolExecution);
|
|
156
|
+
if (error) return error;
|
|
157
|
+
}
|
|
158
|
+
if (value.prompts !== undefined) {
|
|
159
|
+
const error = validatePromptsConfig(value.prompts);
|
|
160
|
+
if (error) return error;
|
|
161
|
+
}
|
|
162
|
+
if (value.compaction !== undefined) {
|
|
163
|
+
const error = validateCompactionConfig(value.compaction);
|
|
164
|
+
if (error) return error;
|
|
165
|
+
}
|
|
166
|
+
if (value.taskMemory !== undefined) {
|
|
167
|
+
const error = validateTaskMemoryConfig(value.taskMemory);
|
|
168
|
+
if (error) return error;
|
|
169
|
+
}
|
|
170
|
+
if (value.messaging !== undefined) {
|
|
171
|
+
const error = validateMessagingConfig(value.messaging);
|
|
172
|
+
if (error) return error;
|
|
173
|
+
}
|
|
174
|
+
if (value.skills !== undefined) {
|
|
175
|
+
const error = validateSkillsConfig(value.skills);
|
|
176
|
+
if (error) return error;
|
|
177
|
+
}
|
|
178
|
+
if (value.review !== undefined) return validateReviewConfig(value.review);
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
export function validateLocalConfig(value) {
|
|
182
|
+
if (!isRecord(value)) {
|
|
183
|
+
return "local config must be a JSON object";
|
|
184
|
+
}
|
|
185
|
+
const removedContextError = removedContextConfigError(value, ".freeflow/local.json");
|
|
186
|
+
if (removedContextError) return removedContextError;
|
|
187
|
+
// processing belongs to a retired feature; it is accepted and ignored so existing local configs keep loading.
|
|
188
|
+
const allowedKeys = new Set([
|
|
189
|
+
"enabled",
|
|
190
|
+
"processing",
|
|
191
|
+
"cognitiveRouting",
|
|
192
|
+
"toolExecution",
|
|
193
|
+
"compaction",
|
|
194
|
+
"taskMemory",
|
|
195
|
+
"messaging",
|
|
196
|
+
"prompts",
|
|
197
|
+
"review",
|
|
198
|
+
"skills",
|
|
199
|
+
]);
|
|
200
|
+
for (const key of Object.keys(value)) {
|
|
201
|
+
if (!allowedKeys.has(key)) {
|
|
202
|
+
return `unsupported top-level local config key: ${key}`;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
const coreError = validateCoreConfigFields(value);
|
|
206
|
+
if (coreError) return coreError;
|
|
207
|
+
if (value.toolExecution !== undefined) {
|
|
208
|
+
const error = validateToolExecutionConfig(value.toolExecution);
|
|
209
|
+
if (error) return error;
|
|
210
|
+
}
|
|
211
|
+
if (value.prompts !== undefined) {
|
|
212
|
+
const error = validatePromptsConfig(value.prompts);
|
|
213
|
+
if (error) return error;
|
|
214
|
+
}
|
|
215
|
+
if (value.compaction !== undefined) {
|
|
216
|
+
const error = validateCompactionConfig(value.compaction);
|
|
217
|
+
if (error) return error;
|
|
218
|
+
}
|
|
219
|
+
if (value.taskMemory !== undefined) {
|
|
220
|
+
const error = validateTaskMemoryConfig(value.taskMemory);
|
|
221
|
+
if (error) return error;
|
|
222
|
+
}
|
|
223
|
+
if (value.messaging !== undefined) {
|
|
224
|
+
const error = validateMessagingConfig(value.messaging);
|
|
225
|
+
if (error) return error;
|
|
226
|
+
}
|
|
227
|
+
if (value.skills !== undefined) {
|
|
228
|
+
const error = validateSkillsConfig(value.skills);
|
|
229
|
+
if (error) return error;
|
|
230
|
+
}
|
|
231
|
+
if (value.review !== undefined) return validateReviewConfig(value.review);
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
@@ -2,25 +2,62 @@ import { readFile } from "node:fs/promises";
|
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { resolveCognitiveRoutingState, supportsCognitiveRoutingModelRegistry } from "../cognitive-routing/config.js";
|
|
4
4
|
import { sessionStage, stagedFor } from "./staging.js";
|
|
5
|
-
import { resolveToolExecutionConfig
|
|
6
|
-
import { resolveCompactionConfig
|
|
5
|
+
import { resolveToolExecutionConfig } from "../tool-execution/config.js";
|
|
6
|
+
import { resolveCompactionConfig } from "../compaction/config.js";
|
|
7
|
+
import { AUTO_APPROVE_GROUPS, resolveTaskMemoryConfig } from "../task-memory/config.js";
|
|
8
|
+
import { INBOUND_POLICIES, resolveMessagingConfig } from "../messaging/config.js";
|
|
9
|
+
import {
|
|
10
|
+
REVIEW_MODES,
|
|
11
|
+
RUNTIME_PROMPTS,
|
|
12
|
+
SWITCHABLE_SKILLS,
|
|
13
|
+
resolveReviewSettings,
|
|
14
|
+
sectionEnabled,
|
|
15
|
+
selectedRuntimePrompts,
|
|
16
|
+
skillsOff,
|
|
17
|
+
validateLocalConfig,
|
|
18
|
+
validateRepositoryConfig,
|
|
19
|
+
} from "./config-validation.js";
|
|
7
20
|
import { cachedBranch } from "./branch.js";
|
|
8
21
|
/**
|
|
9
22
|
* Freeflow configuration: the repository and personal config files, their layering, per-session core overrides, and
|
|
10
23
|
* the resulting capability state, with optional capabilities disabled inside subagents.
|
|
11
24
|
*/
|
|
12
25
|
/**
|
|
13
|
-
* Settings a session may override, as dotted config paths
|
|
14
|
-
*
|
|
26
|
+
* Settings a session may override, as dotted config paths: every setting the settings screen shows, so the Session
|
|
27
|
+
* scope has what the file scopes have. "enabled" is Freeflow's master switch; the others are applied as a layer above
|
|
28
|
+
* the personal config. Most are switches; SESSION_CHOICES lists the ones that hold a value.
|
|
15
29
|
*/
|
|
16
30
|
export const SESSION_OVERRIDE_KEYS = [
|
|
17
31
|
"enabled",
|
|
18
32
|
"cognitiveRouting.enabled",
|
|
19
33
|
"cognitiveRouting.projection",
|
|
20
34
|
"toolExecution.enabled",
|
|
35
|
+
"toolExecution.guidanceNotices",
|
|
21
36
|
"compaction.enabled",
|
|
22
37
|
"compaction.carry",
|
|
38
|
+
"taskMemory.enabled",
|
|
39
|
+
...Object.keys(AUTO_APPROVE_GROUPS).map((group) => `taskMemory.autoApprove.${group}`),
|
|
40
|
+
"messaging.enabled",
|
|
41
|
+
"messaging.inbound",
|
|
42
|
+
"review.mode",
|
|
43
|
+
"review.model",
|
|
44
|
+
"prompts.enabled",
|
|
45
|
+
...RUNTIME_PROMPTS.map((prompt) => `prompts.${prompt}`),
|
|
46
|
+
"skills.enabled",
|
|
47
|
+
...SWITCHABLE_SKILLS.map((skill) => `skills.${skill}`),
|
|
23
48
|
];
|
|
49
|
+
/** The overrides that hold a value rather than on or off, and the values each accepts (a model is any one-line name). */
|
|
50
|
+
const SESSION_CHOICES = {
|
|
51
|
+
"messaging.inbound": INBOUND_POLICIES,
|
|
52
|
+
"review.mode": REVIEW_MODES,
|
|
53
|
+
"review.model": "name",
|
|
54
|
+
};
|
|
55
|
+
function validSessionValue(key, value) {
|
|
56
|
+
const choice = SESSION_CHOICES[key];
|
|
57
|
+
if (!choice) return typeof value === "boolean";
|
|
58
|
+
if (choice === "name") return typeof value === "string" && value.trim() !== "" && !/[\p{Cc}`]/u.test(value);
|
|
59
|
+
return choice.includes(value);
|
|
60
|
+
}
|
|
24
61
|
const SESSION_OVERRIDES_ENTRY = "freeflow-session-overrides";
|
|
25
62
|
const SESSION_CORE_KEYS = new Set(SESSION_OVERRIDE_KEYS);
|
|
26
63
|
let currentSessionOverrides = {};
|
|
@@ -51,81 +88,6 @@ function disableSubagentCapability(capability) {
|
|
|
51
88
|
function isRecord(value) {
|
|
52
89
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
53
90
|
}
|
|
54
|
-
function removedContextConfigError(value, filePath) {
|
|
55
|
-
const removedKeys = ["contextVirtualization", "conversationHistory"].filter((key) =>
|
|
56
|
-
Object.prototype.hasOwnProperty.call(value, key),
|
|
57
|
-
);
|
|
58
|
-
return removedKeys.length > 0
|
|
59
|
-
? `Removed Freeflow context setting(s) in ${filePath}: ${removedKeys.join(", ")}. Delete those keys before using Freeflow.`
|
|
60
|
-
: null;
|
|
61
|
-
}
|
|
62
|
-
function validateCoreConfigFields(value) {
|
|
63
|
-
if (value.enabled !== undefined && typeof value.enabled !== "boolean") {
|
|
64
|
-
return "enabled must be a boolean";
|
|
65
|
-
}
|
|
66
|
-
return null;
|
|
67
|
-
}
|
|
68
|
-
function validateFreeflowConfigShape(value) {
|
|
69
|
-
if (!isRecord(value)) {
|
|
70
|
-
return "config must be a JSON object";
|
|
71
|
-
}
|
|
72
|
-
const removedContextError = removedContextConfigError(value, ".freeflow/config.json");
|
|
73
|
-
if (removedContextError) return removedContextError;
|
|
74
|
-
// outputRouter, observedRouting, and scriptTransform belong to retired features; they are accepted and ignored so
|
|
75
|
-
// existing configs keep loading.
|
|
76
|
-
const allowedKeys = new Set([
|
|
77
|
-
"enabled",
|
|
78
|
-
"outputRouter",
|
|
79
|
-
"observedRouting",
|
|
80
|
-
"scriptTransform",
|
|
81
|
-
"cognitiveRouting",
|
|
82
|
-
"toolExecution",
|
|
83
|
-
"compaction",
|
|
84
|
-
]);
|
|
85
|
-
for (const key of Object.keys(value)) {
|
|
86
|
-
if (!allowedKeys.has(key)) {
|
|
87
|
-
return `unsupported top-level config key: ${key}`;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
const coreError = validateCoreConfigFields(value);
|
|
91
|
-
if (coreError) return coreError;
|
|
92
|
-
for (const key of ["outputRouter", "observedRouting", "scriptTransform"]) {
|
|
93
|
-
if (value[key] !== undefined && !isRecord(value[key])) {
|
|
94
|
-
return `${key} must be an object`;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
if (value.toolExecution !== undefined) {
|
|
98
|
-
const error = validateToolExecutionConfig(value.toolExecution);
|
|
99
|
-
if (error) return error;
|
|
100
|
-
}
|
|
101
|
-
if (value.compaction !== undefined) {
|
|
102
|
-
const error = validateCompactionConfig(value.compaction);
|
|
103
|
-
if (error) return error;
|
|
104
|
-
}
|
|
105
|
-
return null;
|
|
106
|
-
}
|
|
107
|
-
function validateFreeflowLocalConfigShape(value) {
|
|
108
|
-
if (!isRecord(value)) {
|
|
109
|
-
return "local config must be a JSON object";
|
|
110
|
-
}
|
|
111
|
-
const removedContextError = removedContextConfigError(value, ".freeflow/local.json");
|
|
112
|
-
if (removedContextError) return removedContextError;
|
|
113
|
-
// processing belongs to a retired feature; it is accepted and ignored so existing local configs keep loading.
|
|
114
|
-
const allowedKeys = new Set(["enabled", "processing", "cognitiveRouting", "toolExecution", "compaction"]);
|
|
115
|
-
for (const key of Object.keys(value)) {
|
|
116
|
-
if (!allowedKeys.has(key)) {
|
|
117
|
-
return `unsupported top-level local config key: ${key}`;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
const coreError = validateCoreConfigFields(value);
|
|
121
|
-
if (coreError) return coreError;
|
|
122
|
-
if (value.toolExecution !== undefined) {
|
|
123
|
-
const error = validateToolExecutionConfig(value.toolExecution);
|
|
124
|
-
if (error) return error;
|
|
125
|
-
}
|
|
126
|
-
if (value.compaction !== undefined) return validateCompactionConfig(value.compaction);
|
|
127
|
-
return null;
|
|
128
|
-
}
|
|
129
91
|
async function readConfigFileState(path, validate) {
|
|
130
92
|
try {
|
|
131
93
|
const raw = await readFile(path, "utf8");
|
|
@@ -142,7 +104,7 @@ async function readConfigFileState(path, validate) {
|
|
|
142
104
|
}
|
|
143
105
|
return { path, exists: true, valid: true, parsed, parseError: null };
|
|
144
106
|
} catch (error) {
|
|
145
|
-
const code = error && typeof error === "object" ? error.code : undefined;
|
|
107
|
+
const code = error && typeof error === "object" && "code" in error ? error.code : undefined;
|
|
146
108
|
if (code === "ENOENT") {
|
|
147
109
|
return {
|
|
148
110
|
path,
|
|
@@ -163,10 +125,10 @@ async function readConfigFileState(path, validate) {
|
|
|
163
125
|
}
|
|
164
126
|
}
|
|
165
127
|
function readFreeflowConfigState(cwd) {
|
|
166
|
-
return readConfigFileState(join(cwd, ".freeflow/config.json"),
|
|
128
|
+
return readConfigFileState(join(cwd, ".freeflow/config.json"), validateRepositoryConfig);
|
|
167
129
|
}
|
|
168
130
|
function readFreeflowLocalConfigState(cwd) {
|
|
169
|
-
return readConfigFileState(join(cwd, ".freeflow/local.json"),
|
|
131
|
+
return readConfigFileState(join(cwd, ".freeflow/local.json"), validateLocalConfig);
|
|
170
132
|
}
|
|
171
133
|
export async function readFreeflowConfig(cwd) {
|
|
172
134
|
const state = await readFreeflowConfigState(cwd);
|
|
@@ -196,26 +158,29 @@ function normalizeSessionOverrides(value) {
|
|
|
196
158
|
if (!isRecord(value)) return {};
|
|
197
159
|
const overrides = {};
|
|
198
160
|
for (const key of SESSION_CORE_KEYS) {
|
|
199
|
-
if (
|
|
161
|
+
if (validSessionValue(key, value[key])) {
|
|
200
162
|
overrides[key] = value[key];
|
|
201
163
|
}
|
|
202
164
|
}
|
|
203
165
|
return overrides;
|
|
204
166
|
}
|
|
205
|
-
/** The
|
|
167
|
+
/** The setting overrides of this session as a config layer, e.g. { compaction: { carry: false } }. */
|
|
206
168
|
function sessionConfigLayer(overrides) {
|
|
207
169
|
const layer = {};
|
|
208
170
|
for (const [key, value] of Object.entries(overrides)) {
|
|
209
|
-
const
|
|
210
|
-
if (
|
|
211
|
-
|
|
171
|
+
const path = key.split(".");
|
|
172
|
+
if (path.length < 2 || value === undefined) continue;
|
|
173
|
+
let at = layer;
|
|
174
|
+
for (const part of path.slice(0, -1)) at = at[part] = isRecord(at[part]) ? at[part] : {};
|
|
175
|
+
at[path.at(-1)] = value;
|
|
212
176
|
}
|
|
213
177
|
return layer;
|
|
214
178
|
}
|
|
179
|
+
/** The personal config with the session layer over it, merged object by object. */
|
|
215
180
|
function withSessionLayer(local, session) {
|
|
216
181
|
const merged = { ...local };
|
|
217
|
-
for (const [
|
|
218
|
-
merged[
|
|
182
|
+
for (const [key, value] of Object.entries(session))
|
|
183
|
+
merged[key] = isRecord(value) && isRecord(local[key]) ? withSessionLayer(local[key], value) : value;
|
|
219
184
|
return merged;
|
|
220
185
|
}
|
|
221
186
|
function resolveSessionCoreConfig(layers) {
|
|
@@ -251,6 +216,7 @@ export async function readFreeflowConfigLayers(cwd) {
|
|
|
251
216
|
parseError: blockingState?.parseError ?? null,
|
|
252
217
|
};
|
|
253
218
|
}
|
|
219
|
+
// host is Pi's extension context (or none); Pi's runtime objects are wider than its exported types.
|
|
254
220
|
export async function readCapabilityState(cwd, host = undefined) {
|
|
255
221
|
const layers = await readFreeflowConfigLayers(cwd);
|
|
256
222
|
const effectiveCore = resolveSessionCoreConfig(layers);
|
|
@@ -278,6 +244,16 @@ export async function readCapabilityState(cwd, host = undefined) {
|
|
|
278
244
|
const sessionLocalConfig = withSessionLayer(localConfig, sessionLayer);
|
|
279
245
|
const toolExecution = resolveToolExecutionConfig(repositoryConfig, sessionLocalConfig, enabled);
|
|
280
246
|
const compaction = resolveCompactionConfig(repositoryConfig, sessionLocalConfig, enabled);
|
|
247
|
+
const taskMemory = resolveTaskMemoryConfig(repositoryConfig, sessionLocalConfig, enabled);
|
|
248
|
+
const messaging = resolveMessagingConfig(repositoryConfig, sessionLocalConfig, enabled);
|
|
249
|
+
// Not capabilities: the user's choices for Review Work, the runtime prompts and the skills, on every host.
|
|
250
|
+
const review = resolveReviewSettings(repositoryConfig, sessionLocalConfig);
|
|
251
|
+
const prompts = selectedRuntimePrompts(repositoryConfig, sessionLocalConfig);
|
|
252
|
+
const skills = {
|
|
253
|
+
enabled: sectionEnabled(repositoryConfig, sessionLocalConfig, "skills"),
|
|
254
|
+
off: skillsOff(repositoryConfig, sessionLocalConfig),
|
|
255
|
+
};
|
|
256
|
+
const promptsEnabled = sectionEnabled(repositoryConfig, sessionLocalConfig, "prompts");
|
|
281
257
|
const capabilityState = {
|
|
282
258
|
configured: layers.configured,
|
|
283
259
|
repositoryConfigured: layers.repositoryConfigured,
|
|
@@ -298,6 +274,14 @@ export async function readCapabilityState(cwd, host = undefined) {
|
|
|
298
274
|
cognitiveRouting,
|
|
299
275
|
toolExecution,
|
|
300
276
|
compaction,
|
|
277
|
+
taskMemory,
|
|
278
|
+
messaging,
|
|
279
|
+
review,
|
|
280
|
+
prompts,
|
|
281
|
+
promptsEnabled,
|
|
282
|
+
skills,
|
|
283
|
+
// ask_user: core, on whenever Freeflow is enabled (Task 002 D-001); off in subagents, which have no user.
|
|
284
|
+
askUser: { enabled, effective: enabled },
|
|
301
285
|
};
|
|
302
286
|
if (!subagentContext) return capabilityState;
|
|
303
287
|
return {
|
|
@@ -305,6 +289,9 @@ export async function readCapabilityState(cwd, host = undefined) {
|
|
|
305
289
|
cognitiveRouting: disableSubagentCapability(capabilityState.cognitiveRouting),
|
|
306
290
|
toolExecution: disableSubagentCapability(capabilityState.toolExecution),
|
|
307
291
|
compaction: disableSubagentCapability(capabilityState.compaction),
|
|
292
|
+
taskMemory: disableSubagentCapability(capabilityState.taskMemory),
|
|
293
|
+
messaging: disableSubagentCapability(capabilityState.messaging),
|
|
294
|
+
askUser: disableSubagentCapability(capabilityState.askUser),
|
|
308
295
|
};
|
|
309
296
|
}
|
|
310
297
|
function recordedSessionOverrides(sessionManager) {
|
|
@@ -335,7 +322,7 @@ export async function setSessionCoreOverride(key, value, ctx, pi) {
|
|
|
335
322
|
if (!SESSION_CORE_KEYS.has(key)) {
|
|
336
323
|
throw new Error(`Invalid Freeflow session override: ${String(key)}`);
|
|
337
324
|
}
|
|
338
|
-
if (value !== null &&
|
|
325
|
+
if (value !== null && !validSessionValue(key, value)) {
|
|
339
326
|
throw new Error(`Invalid Freeflow session override value for ${key}: ${String(value)}`);
|
|
340
327
|
}
|
|
341
328
|
const hasOverride = Object.hasOwn(currentSessionOverrides, key);
|
|
@@ -352,7 +339,7 @@ export async function setSessionCoreOverride(key, value, ctx, pi) {
|
|
|
352
339
|
recordSessionOverrides(ctx, pi);
|
|
353
340
|
return {
|
|
354
341
|
changed: true,
|
|
355
|
-
//
|
|
342
|
+
// A session override can switch Freeflow, a capability, a skill or a prompt, which changes tools and guidance: reload.
|
|
356
343
|
reloadRequired: true,
|
|
357
344
|
sessionOverrides: { ...currentSessionOverrides },
|
|
358
345
|
capabilityState: await readCapabilityState(ctx.cwd, ctx),
|