@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
|
@@ -10,11 +10,20 @@ import {
|
|
|
10
10
|
resetSessionOverrides,
|
|
11
11
|
setSessionCoreOverride,
|
|
12
12
|
} from "../config.js";
|
|
13
|
+
import {
|
|
14
|
+
REVIEW_MODES,
|
|
15
|
+
RUNTIME_PROMPTS,
|
|
16
|
+
SWITCHABLE_SKILLS,
|
|
17
|
+
resolveReviewSettings,
|
|
18
|
+
sectionEnabled,
|
|
19
|
+
} from "../config-validation.js";
|
|
13
20
|
import { freeflowStatusText } from "../status.js";
|
|
14
21
|
import { PiSettingsComponent } from "./settings-view.js";
|
|
15
22
|
import { workersForDelegation } from "../../cognitive-routing/types.js";
|
|
16
23
|
import { DEFAULT_TOOL_EXECUTION_CONFIG } from "../../tool-execution/config.js";
|
|
17
24
|
import { DEFAULT_COMPACTION_CARRY, DEFAULT_COMPACTION_ENABLED } from "../../compaction/config.js";
|
|
25
|
+
import { AUTO_APPROVE_GROUPS, DEFAULT_TASK_MEMORY_ENABLED } from "../../task-memory/config.js";
|
|
26
|
+
import { DEFAULT_INBOUND_POLICY, DEFAULT_MESSAGING_ENABLED, INBOUND_POLICIES } from "../../messaging/config.js";
|
|
18
27
|
/*
|
|
19
28
|
* The /freeflow command and the settings model it edits: every Freeflow setting with its repository, personal, and
|
|
20
29
|
* session scopes, the routing profile wizard, config file writes, and reload after saving. settings-view.ts draws the
|
|
@@ -98,8 +107,12 @@ function setConfigValue(config, item, value) {
|
|
|
98
107
|
}
|
|
99
108
|
setPath(config, item.path, value);
|
|
100
109
|
}
|
|
110
|
+
/** What choosing inherit means, for the help line: "Follow the default (on)." */
|
|
111
|
+
function inheritText(value, source) {
|
|
112
|
+
return source === "default" ? `Inherit Freeflow's default (${value}).` : `Inherit the ${source} setting (${value}).`;
|
|
113
|
+
}
|
|
101
114
|
function booleanValue(value) {
|
|
102
|
-
return value === true ? "
|
|
115
|
+
return value === true ? "on" : "off";
|
|
103
116
|
}
|
|
104
117
|
function configValueForChoice(item, value) {
|
|
105
118
|
const key = String(value);
|
|
@@ -157,14 +170,14 @@ function createScopedBooleanItem(options) {
|
|
|
157
170
|
item = {
|
|
158
171
|
id: options.id,
|
|
159
172
|
label: options.label,
|
|
160
|
-
description:
|
|
173
|
+
description: options.description,
|
|
161
174
|
path: options.path,
|
|
162
175
|
kind: "enum",
|
|
163
176
|
value: selectedValue,
|
|
164
177
|
values: [LOCAL_INHERIT, "true", "false"],
|
|
165
|
-
valueLabels: { inherit: "inherit", true: "
|
|
178
|
+
valueLabels: { inherit: "inherit", true: "on", false: "off" },
|
|
166
179
|
valueDescriptions: {
|
|
167
|
-
inherit:
|
|
180
|
+
inherit: inheritText(formatCoreValue(inheritedValue), SOURCE_LABELS[inheritedSource]),
|
|
168
181
|
true: "Enabled for you in this checkout.",
|
|
169
182
|
false: "Disabled for you in this checkout.",
|
|
170
183
|
},
|
|
@@ -193,6 +206,7 @@ function createScopedBooleanItem(options) {
|
|
|
193
206
|
value: inheritedValue,
|
|
194
207
|
defaultValue: options.defaultValue,
|
|
195
208
|
configScope: "repository",
|
|
209
|
+
repositoryUnset: typeof repositoryValue !== "boolean",
|
|
196
210
|
effectiveValue: options.effectiveValue,
|
|
197
211
|
effectiveSource: options.effectiveSource,
|
|
198
212
|
localOverrideValue: typeof localValue === "boolean" ? localValue : undefined,
|
|
@@ -201,6 +215,49 @@ function createScopedBooleanItem(options) {
|
|
|
201
215
|
item.displaySuffix = coreDisplaySuffix(item);
|
|
202
216
|
return item;
|
|
203
217
|
}
|
|
218
|
+
/** A choice among fixed values, in the Personal or Repository scope; Personal can inherit the repository's value. */
|
|
219
|
+
function createScopedChoiceItem(options) {
|
|
220
|
+
const repositoryValue = getPath(options.rawConfig, options.path);
|
|
221
|
+
const inheritedValue = options.values.includes(repositoryValue) ? repositoryValue : options.defaultValue;
|
|
222
|
+
const inheritedSource = repositoryValue === undefined ? "builtin" : "repository";
|
|
223
|
+
const localValue = getPath(options.localConfig, options.path);
|
|
224
|
+
const common = {
|
|
225
|
+
id: options.id,
|
|
226
|
+
label: options.label,
|
|
227
|
+
path: options.path,
|
|
228
|
+
kind: "enum",
|
|
229
|
+
effectiveValue: options.effectiveValue,
|
|
230
|
+
effectiveSource: options.effectiveSource,
|
|
231
|
+
};
|
|
232
|
+
return options.scope === "local"
|
|
233
|
+
? {
|
|
234
|
+
...common,
|
|
235
|
+
description: options.description,
|
|
236
|
+
value: options.values.includes(localValue) ? localValue : LOCAL_INHERIT,
|
|
237
|
+
values: [LOCAL_INHERIT, ...options.values],
|
|
238
|
+
valueLabels: { inherit: "inherit", ...options.valueLabels },
|
|
239
|
+
valueDescriptions: {
|
|
240
|
+
inherit: inheritText(options.valueLabels[inheritedValue] ?? inheritedValue, SOURCE_LABELS[inheritedSource]),
|
|
241
|
+
...options.valueDescriptions,
|
|
242
|
+
},
|
|
243
|
+
configScope: "local",
|
|
244
|
+
configValues: Object.fromEntries([["inherit", undefined], ...options.values.map((value) => [value, value])]),
|
|
245
|
+
inheritedValue,
|
|
246
|
+
inheritedSource,
|
|
247
|
+
}
|
|
248
|
+
: {
|
|
249
|
+
...common,
|
|
250
|
+
description: `${options.description} This edits shared .freeflow/config.json.`,
|
|
251
|
+
value: inheritedValue,
|
|
252
|
+
values: [...options.values],
|
|
253
|
+
valueLabels: options.valueLabels,
|
|
254
|
+
valueDescriptions: options.valueDescriptions,
|
|
255
|
+
defaultValue: options.defaultValue,
|
|
256
|
+
configScope: "repository",
|
|
257
|
+
repositoryUnset: repositoryValue === undefined,
|
|
258
|
+
localOverrideValue: options.values.includes(localValue) ? localValue : undefined,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
204
261
|
function createScopedDelegationItem(options) {
|
|
205
262
|
const path = ["cognitiveRouting", "delegation"];
|
|
206
263
|
const repositoryValue = getPath(options.rawConfig, path);
|
|
@@ -217,8 +274,7 @@ function createScopedDelegationItem(options) {
|
|
|
217
274
|
? {
|
|
218
275
|
id: "freeflow.cognitiveRouting.delegation",
|
|
219
276
|
label: "Delegation",
|
|
220
|
-
description:
|
|
221
|
-
"Choose enabled workers for automatic routing. Inherit uses the repository value; this setting does not rank model presets.",
|
|
277
|
+
description: "Choose enabled workers for automatic routing; this setting does not rank model presets.",
|
|
222
278
|
path,
|
|
223
279
|
kind: "enum",
|
|
224
280
|
value: ["executor", "helper", "both"].includes(localValue) ? localValue : LOCAL_INHERIT,
|
|
@@ -230,7 +286,7 @@ function createScopedDelegationItem(options) {
|
|
|
230
286
|
both: "Helper and Executor",
|
|
231
287
|
},
|
|
232
288
|
valueDescriptions: {
|
|
233
|
-
inherit:
|
|
289
|
+
inherit: inheritText(inheritedValue, SOURCE_LABELS[inheritedSource]),
|
|
234
290
|
...descriptions,
|
|
235
291
|
},
|
|
236
292
|
configScope: "local",
|
|
@@ -256,6 +312,7 @@ function createScopedDelegationItem(options) {
|
|
|
256
312
|
valueDescriptions: descriptions,
|
|
257
313
|
defaultValue: "executor",
|
|
258
314
|
configScope: "repository",
|
|
315
|
+
repositoryUnset: repositoryValue === undefined,
|
|
259
316
|
effectiveValue: options.effectiveValue,
|
|
260
317
|
effectiveSource: options.effectiveSource,
|
|
261
318
|
localOverrideValue: ["executor", "helper", "both"].includes(localValue) ? localValue : undefined,
|
|
@@ -280,14 +337,14 @@ function createSessionBooleanItem(options) {
|
|
|
280
337
|
return {
|
|
281
338
|
id: options.id,
|
|
282
339
|
label: options.label,
|
|
283
|
-
description:
|
|
340
|
+
description: options.description,
|
|
284
341
|
path: options.key.split("."),
|
|
285
342
|
kind: "enum",
|
|
286
343
|
value: typeof override === "boolean" ? String(override) : LOCAL_INHERIT,
|
|
287
344
|
values: [LOCAL_INHERIT, "true", "false"],
|
|
288
|
-
valueLabels: { inherit: "inherit", true: "
|
|
345
|
+
valueLabels: { inherit: "inherit", true: "on", false: "off" },
|
|
289
346
|
valueDescriptions: {
|
|
290
|
-
inherit:
|
|
347
|
+
inherit: inheritText(formatCoreValue(options.inheritedValue), SOURCE_LABELS[options.inheritedSource]),
|
|
291
348
|
true: "Enabled for this Pi session only.",
|
|
292
349
|
false: "Disabled for this Pi session only.",
|
|
293
350
|
},
|
|
@@ -299,14 +356,97 @@ function createSessionBooleanItem(options) {
|
|
|
299
356
|
inheritedSource: options.inheritedSource,
|
|
300
357
|
};
|
|
301
358
|
}
|
|
359
|
+
const AUTO_APPROVE_LABELS = {
|
|
360
|
+
attach: ["Attach", "attaching a record, and making a held record writable"],
|
|
361
|
+
create: ["Create", "creating a task or a child task"],
|
|
362
|
+
env: ["Directories", "changing a task's directories"],
|
|
363
|
+
fanout: ["Fan-out", "a child task with its own Git worktree and branch"],
|
|
364
|
+
projects: ["Projects", "creating or linking a project, and importing a record into one"],
|
|
365
|
+
};
|
|
366
|
+
const INBOUND_LABELS = { accept: "never", project: "outside the project", hold: "every message", refuse: "refuse all" };
|
|
367
|
+
const INBOUND_DESCRIPTIONS = {
|
|
368
|
+
accept: "Deliver every message to the agent.",
|
|
369
|
+
project: "Deliver messages from sessions in this repository's Task Memory project; ask you about the rest.",
|
|
370
|
+
hold: "Ask you before delivering any message.",
|
|
371
|
+
refuse: "Refuse every message.",
|
|
372
|
+
};
|
|
373
|
+
const REVIEW_DESCRIPTIONS = {
|
|
374
|
+
auto: "Review Work's risk check decides.",
|
|
375
|
+
off: "Never on its own; for a risky change the agent proposes a review, with its reason and cost, and waits for you.",
|
|
376
|
+
always: "Review every finished task.",
|
|
377
|
+
};
|
|
378
|
+
/** A choice among fixed values in the Session scope; inherit follows the personal and repository settings. */
|
|
379
|
+
function createSessionChoiceItem(options) {
|
|
380
|
+
const override = options.sessionOverrides[options.key];
|
|
381
|
+
const set = typeof override === "string" && options.values.includes(override);
|
|
382
|
+
return {
|
|
383
|
+
id: options.id,
|
|
384
|
+
label: options.label,
|
|
385
|
+
description: options.description,
|
|
386
|
+
path: options.key.split("."),
|
|
387
|
+
kind: "enum",
|
|
388
|
+
value: set ? override : LOCAL_INHERIT,
|
|
389
|
+
values: [LOCAL_INHERIT, ...options.values],
|
|
390
|
+
valueLabels: { inherit: "inherit", ...options.valueLabels },
|
|
391
|
+
valueDescriptions: {
|
|
392
|
+
inherit: inheritText(
|
|
393
|
+
options.valueLabels[options.inheritedValue] ?? options.inheritedValue,
|
|
394
|
+
SOURCE_LABELS[options.inheritedSource],
|
|
395
|
+
),
|
|
396
|
+
...options.valueDescriptions,
|
|
397
|
+
},
|
|
398
|
+
configScope: "session",
|
|
399
|
+
configValues: Object.fromEntries([["inherit", undefined], ...options.values.map((value) => [value, value])]),
|
|
400
|
+
effectiveValue: options.effectiveValue,
|
|
401
|
+
effectiveSource: set ? "session" : options.inheritedSource,
|
|
402
|
+
inheritedValue: options.inheritedValue,
|
|
403
|
+
inheritedSource: options.inheritedSource,
|
|
404
|
+
};
|
|
405
|
+
}
|
|
302
406
|
/** A setting's configured value and source without session overrides: personal, then repository, then the default. */
|
|
303
407
|
function configuredSetting(rawConfig, localConfig, path, defaultValue) {
|
|
304
408
|
const local = getPath(localConfig, path);
|
|
305
|
-
if (typeof local ===
|
|
409
|
+
if (typeof local === typeof defaultValue) return { value: local, source: "local" };
|
|
306
410
|
const repository = getPath(rawConfig, path);
|
|
307
|
-
if (typeof repository ===
|
|
411
|
+
if (typeof repository === typeof defaultValue) return { value: repository, source: "repository" };
|
|
308
412
|
return { value: defaultValue, source: "builtin" };
|
|
309
413
|
}
|
|
414
|
+
const PROMPT_LABELS = {
|
|
415
|
+
core: ["Core", "How Freeflow works: the cycle, what authorizes work, and where it stops."],
|
|
416
|
+
interactionContract: ["Interaction Contract", "How the agent talks with you: questions, reports and stopping."],
|
|
417
|
+
workingMethod: ["Working Method", "How the agent works: small output, named decisions, diagnosing before fixing."],
|
|
418
|
+
};
|
|
419
|
+
/** A skill's name as the screen shows it: discuss-work as Discuss Work. */
|
|
420
|
+
function skillLabel(name) {
|
|
421
|
+
return name
|
|
422
|
+
.split("-")
|
|
423
|
+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
424
|
+
.join(" ");
|
|
425
|
+
}
|
|
426
|
+
/** One row that opens a list of switches on a screen of its own, as Auto-approve does. */
|
|
427
|
+
function nestedGroup(id, label, description, children) {
|
|
428
|
+
return { id, label, description, kind: "group", nested: true, value: true, children };
|
|
429
|
+
}
|
|
430
|
+
/** Sections from everyday to experimental (Task 005): Freeflow, then the capabilities, then session tools. */
|
|
431
|
+
const SECTION_ORDER = [
|
|
432
|
+
"freeflow.enabled",
|
|
433
|
+
"freeflow.prompts",
|
|
434
|
+
"freeflow.skills",
|
|
435
|
+
"freeflow.compaction",
|
|
436
|
+
"freeflow.toolExecution",
|
|
437
|
+
"freeflow.taskMemory",
|
|
438
|
+
"freeflow.messaging",
|
|
439
|
+
"freeflow.cognitiveRouting",
|
|
440
|
+
"freeflow.review",
|
|
441
|
+
"freeflow.session",
|
|
442
|
+
];
|
|
443
|
+
function inSectionOrder(items) {
|
|
444
|
+
const rank = (item) => {
|
|
445
|
+
const at = SECTION_ORDER.indexOf(item.id);
|
|
446
|
+
return at < 0 ? SECTION_ORDER.length : at;
|
|
447
|
+
};
|
|
448
|
+
return [...items].sort((a, b) => rank(a) - rank(b));
|
|
449
|
+
}
|
|
310
450
|
function sectionItem(id, label, description, status, children) {
|
|
311
451
|
return { id, label, description, kind: "group", value: true, displaySuffix: status, children };
|
|
312
452
|
}
|
|
@@ -363,15 +503,19 @@ function sessionFreeflowItems(state, cognitiveRoutingController, ctx, rawConfig,
|
|
|
363
503
|
false,
|
|
364
504
|
);
|
|
365
505
|
enabledItem.inactive = freeflowInactive || routingUnavailable;
|
|
506
|
+
enabledItem.runtimeInactive = routingUnavailable;
|
|
366
507
|
const projectionItem = sessionSwitch(
|
|
367
508
|
"freeflow.cognitiveRouting.projection",
|
|
368
509
|
"Context projection",
|
|
369
510
|
"Show Coordinator the worker evidence selected for it instead of the whole history.",
|
|
370
511
|
"cognitiveRouting.projection",
|
|
371
512
|
routing.projection,
|
|
372
|
-
|
|
513
|
+
true,
|
|
373
514
|
);
|
|
374
|
-
|
|
515
|
+
// Delegation, projection and the presets can be set while routing is not running: setting a missing preset is how
|
|
516
|
+
// it starts. Only Control needs routing running, since holding a profile switches the model at once.
|
|
517
|
+
projectionItem.inactive = freeflowInactive || routingUnavailable;
|
|
518
|
+
projectionItem.runtimeInactive = routingUnavailable;
|
|
375
519
|
const delegationOverride = cognitiveRoutingController?.sessionDelegationOverride() ?? LOCAL_INHERIT;
|
|
376
520
|
const delegation = cognitiveRoutingState?.delegation ?? routing.delegation;
|
|
377
521
|
routingItems.push(
|
|
@@ -395,7 +539,8 @@ function sessionFreeflowItems(state, cognitiveRoutingController, ctx, rawConfig,
|
|
|
395
539
|
]),
|
|
396
540
|
]),
|
|
397
541
|
inactive: routingInactive,
|
|
398
|
-
|
|
542
|
+
// Holding a profile switches the model at once, so it needs routing running, not only configured.
|
|
543
|
+
runtimeInactive: routingInactive,
|
|
399
544
|
},
|
|
400
545
|
{
|
|
401
546
|
id: "freeflow.cognitiveRouting.delegation",
|
|
@@ -411,7 +556,10 @@ function sessionFreeflowItems(state, cognitiveRoutingController, ctx, rawConfig,
|
|
|
411
556
|
both: "Helper and Executor",
|
|
412
557
|
},
|
|
413
558
|
valueDescriptions: {
|
|
414
|
-
inherit:
|
|
559
|
+
inherit: inheritText(
|
|
560
|
+
routing.delegation,
|
|
561
|
+
SOURCE_LABELS[cognitiveRoutingSettingsSource(routing.delegationSource)],
|
|
562
|
+
),
|
|
415
563
|
executor: "Coordinator delegates assignments to Executor.",
|
|
416
564
|
helper: "Coordinator delegates supporting assignments to Helper and keeps production implementation.",
|
|
417
565
|
both: "Coordinator chooses Helper or Executor for each assignment.",
|
|
@@ -422,7 +570,8 @@ function sessionFreeflowItems(state, cognitiveRoutingController, ctx, rawConfig,
|
|
|
422
570
|
delegationOverride === LOCAL_INHERIT ? cognitiveRoutingSettingsSource(routing.delegationSource) : "session",
|
|
423
571
|
inheritedValue: routing.delegation,
|
|
424
572
|
inheritedSource: cognitiveRoutingSettingsSource(routing.delegationSource),
|
|
425
|
-
inactive:
|
|
573
|
+
inactive: freeflowInactive || routingUnavailable,
|
|
574
|
+
runtimeInactive: routingUnavailable,
|
|
426
575
|
},
|
|
427
576
|
projectionItem,
|
|
428
577
|
);
|
|
@@ -438,7 +587,8 @@ function sessionFreeflowItems(state, cognitiveRoutingController, ctx, rawConfig,
|
|
|
438
587
|
ctx,
|
|
439
588
|
sessionProfiles,
|
|
440
589
|
});
|
|
441
|
-
item.inactive ||=
|
|
590
|
+
item.inactive ||= freeflowInactive || routingUnavailable;
|
|
591
|
+
item.runtimeInactive ||= routingUnavailable;
|
|
442
592
|
routingItems.push(item);
|
|
443
593
|
}
|
|
444
594
|
}
|
|
@@ -448,9 +598,17 @@ function sessionFreeflowItems(state, cognitiveRoutingController, ctx, rawConfig,
|
|
|
448
598
|
"Enabled",
|
|
449
599
|
"Use Tool Execution in this session.",
|
|
450
600
|
"toolExecution.enabled",
|
|
451
|
-
state.toolExecution?.enabled ??
|
|
601
|
+
state.toolExecution?.enabled ?? DEFAULT_TOOL_EXECUTION_CONFIG.enabled,
|
|
452
602
|
DEFAULT_TOOL_EXECUTION_CONFIG.enabled,
|
|
453
603
|
);
|
|
604
|
+
const noticesItem = sessionSwitch(
|
|
605
|
+
"freeflow.toolExecution.guidanceNotices",
|
|
606
|
+
"Guidance notices",
|
|
607
|
+
"Add a skill's key lines to the result of the tool call that needs them, in this session.",
|
|
608
|
+
"toolExecution.guidanceNotices",
|
|
609
|
+
state.toolExecution?.guidanceNotices ?? DEFAULT_TOOL_EXECUTION_CONFIG.guidanceNotices,
|
|
610
|
+
DEFAULT_TOOL_EXECUTION_CONFIG.guidanceNotices,
|
|
611
|
+
);
|
|
454
612
|
const compactionItem = sessionSwitch(
|
|
455
613
|
"freeflow.compaction.enabled",
|
|
456
614
|
"Enabled",
|
|
@@ -467,8 +625,119 @@ function sessionFreeflowItems(state, cognitiveRoutingController, ctx, rawConfig,
|
|
|
467
625
|
state.compaction?.carry ?? DEFAULT_COMPACTION_CARRY,
|
|
468
626
|
DEFAULT_COMPACTION_CARRY,
|
|
469
627
|
);
|
|
628
|
+
const autoApproveItems = Object.keys(AUTO_APPROVE_GROUPS).map((group) =>
|
|
629
|
+
sessionSwitch(
|
|
630
|
+
`freeflow.taskMemory.autoApprove.${group}`,
|
|
631
|
+
AUTO_APPROVE_LABELS[group][0],
|
|
632
|
+
`Let the agent do this without asking you first, in this session: ${AUTO_APPROVE_LABELS[group][1]}.`,
|
|
633
|
+
`taskMemory.autoApprove.${group}`,
|
|
634
|
+
state.taskMemory?.autoApprove?.[group] ?? false,
|
|
635
|
+
false,
|
|
636
|
+
),
|
|
637
|
+
);
|
|
638
|
+
const inboundInherited = configuredSetting(rawConfig, localConfig, ["messaging", "inbound"], DEFAULT_INBOUND_POLICY);
|
|
639
|
+
const inboundItem = createSessionChoiceItem({
|
|
640
|
+
id: "freeflow.messaging.inbound",
|
|
641
|
+
label: "Ask before delivering",
|
|
642
|
+
description: "Whether a message from another session waits for you to accept it, in this session.",
|
|
643
|
+
key: "messaging.inbound",
|
|
644
|
+
values: INBOUND_POLICIES,
|
|
645
|
+
valueLabels: INBOUND_LABELS,
|
|
646
|
+
valueDescriptions: INBOUND_DESCRIPTIONS,
|
|
647
|
+
inheritedValue: inboundInherited.value,
|
|
648
|
+
inheritedSource: inboundInherited.source,
|
|
649
|
+
effectiveValue: state.messaging?.inbound ?? DEFAULT_INBOUND_POLICY,
|
|
650
|
+
sessionOverrides,
|
|
651
|
+
});
|
|
652
|
+
inboundItem.inactive = freeflowInactive;
|
|
653
|
+
const reviewInherited = configuredSetting(rawConfig, localConfig, ["review", "mode"], "auto");
|
|
654
|
+
const reviewModeItem = createSessionChoiceItem({
|
|
655
|
+
id: "freeflow.review.mode",
|
|
656
|
+
label: "Independent review",
|
|
657
|
+
description: "When finished work is reviewed by a separate reviewer, in this session.",
|
|
658
|
+
key: "review.mode",
|
|
659
|
+
values: REVIEW_MODES,
|
|
660
|
+
valueLabels: { auto: "auto", off: "off", always: "always" },
|
|
661
|
+
valueDescriptions: REVIEW_DESCRIPTIONS,
|
|
662
|
+
inheritedValue: reviewInherited.value,
|
|
663
|
+
inheritedSource: reviewInherited.source,
|
|
664
|
+
effectiveValue: state.review?.mode ?? "auto",
|
|
665
|
+
sessionOverrides,
|
|
666
|
+
});
|
|
667
|
+
reviewModeItem.inactive = freeflowInactive;
|
|
668
|
+
reviewModeItem.heading = true;
|
|
669
|
+
const reviewModelItem = reviewerModelItem({
|
|
670
|
+
scope: "session",
|
|
671
|
+
rawConfig,
|
|
672
|
+
localConfig,
|
|
673
|
+
effectiveValue: state.review?.model,
|
|
674
|
+
effectiveSource: typeof sessionOverrides["review.model"] === "string" ? "session" : "builtin",
|
|
675
|
+
ctx,
|
|
676
|
+
sessionValue: typeof sessionOverrides["review.model"] === "string" ? sessionOverrides["review.model"] : undefined,
|
|
677
|
+
});
|
|
678
|
+
reviewModelItem.inactive = freeflowInactive;
|
|
679
|
+
const promptsMaster = sessionSwitch(
|
|
680
|
+
"freeflow.prompts.enabled",
|
|
681
|
+
"Enabled",
|
|
682
|
+
"Add Freeflow's runtime prompts in this session. Off leaves all three out; Freeflow stays on.",
|
|
683
|
+
"prompts.enabled",
|
|
684
|
+
state.promptsEnabled ?? true,
|
|
685
|
+
true,
|
|
686
|
+
);
|
|
687
|
+
const skillsMaster = sessionSwitch(
|
|
688
|
+
"freeflow.skills.enabled",
|
|
689
|
+
"Enabled",
|
|
690
|
+
"Give the agent Freeflow's skills in this session. Off turns every skill but Setup Freeflow off.",
|
|
691
|
+
"skills.enabled",
|
|
692
|
+
state.skills?.enabled ?? true,
|
|
693
|
+
true,
|
|
694
|
+
);
|
|
695
|
+
const promptItems = RUNTIME_PROMPTS.map((prompt) =>
|
|
696
|
+
sessionSwitch(
|
|
697
|
+
`freeflow.prompts.${prompt}`,
|
|
698
|
+
PROMPT_LABELS[prompt][0],
|
|
699
|
+
`${PROMPT_LABELS[prompt][1]} In this session.`,
|
|
700
|
+
`prompts.${prompt}`,
|
|
701
|
+
configuredSetting(rawConfig, localConfig, ["prompts", prompt], true).value,
|
|
702
|
+
true,
|
|
703
|
+
),
|
|
704
|
+
);
|
|
705
|
+
const skillItems = SWITCHABLE_SKILLS.map((skill) =>
|
|
706
|
+
sessionSwitch(
|
|
707
|
+
`freeflow.skills.${skill}`,
|
|
708
|
+
skillLabel(skill),
|
|
709
|
+
`Give the agent ${skill} in this session.`,
|
|
710
|
+
`skills.${skill}`,
|
|
711
|
+
configuredSetting(rawConfig, localConfig, ["skills", skill], true).value,
|
|
712
|
+
true,
|
|
713
|
+
),
|
|
714
|
+
);
|
|
715
|
+
const taskMemoryItem = sessionSwitch(
|
|
716
|
+
"freeflow.taskMemory.enabled",
|
|
717
|
+
"Enabled",
|
|
718
|
+
"Use task memory in this session.",
|
|
719
|
+
"taskMemory.enabled",
|
|
720
|
+
state.taskMemory?.enabled ?? DEFAULT_TASK_MEMORY_ENABLED,
|
|
721
|
+
DEFAULT_TASK_MEMORY_ENABLED,
|
|
722
|
+
);
|
|
723
|
+
const messagingItem = sessionSwitch(
|
|
724
|
+
"freeflow.messaging.enabled",
|
|
725
|
+
"Enabled",
|
|
726
|
+
"Exchange messages with other Pi sessions on this machine.",
|
|
727
|
+
"messaging.enabled",
|
|
728
|
+
state.messaging?.enabled ?? DEFAULT_MESSAGING_ENABLED,
|
|
729
|
+
DEFAULT_MESSAGING_ENABLED,
|
|
730
|
+
);
|
|
470
731
|
const items = [
|
|
471
732
|
freeflowItem,
|
|
733
|
+
sectionItem("freeflow.prompts", "Runtime prompts", "Runtime prompts for this session.", "", [
|
|
734
|
+
promptsMaster,
|
|
735
|
+
nestedGroup("freeflow.prompts.each", "Each prompt", "One switch per prompt, in this session.", promptItems),
|
|
736
|
+
]),
|
|
737
|
+
sectionItem("freeflow.skills", "Skills", "Skills for this session; Setup Freeflow always stays.", "", [
|
|
738
|
+
skillsMaster,
|
|
739
|
+
nestedGroup("freeflow.skills.each", "Each skill", "One switch per skill, in this session.", skillItems),
|
|
740
|
+
]),
|
|
472
741
|
...(routing
|
|
473
742
|
? [
|
|
474
743
|
sectionItem(
|
|
@@ -489,7 +758,7 @@ function sessionFreeflowItems(state, cognitiveRoutingController, ctx, rawConfig,
|
|
|
489
758
|
"Tool Execution",
|
|
490
759
|
"Tool Execution for this session.",
|
|
491
760
|
capabilityStatus(state.toolExecution),
|
|
492
|
-
[toolItem],
|
|
761
|
+
[toolItem, noticesItem],
|
|
493
762
|
),
|
|
494
763
|
sectionItem(
|
|
495
764
|
"freeflow.compaction",
|
|
@@ -498,6 +767,26 @@ function sessionFreeflowItems(state, cognitiveRoutingController, ctx, rawConfig,
|
|
|
498
767
|
capabilityStatus(state.compaction),
|
|
499
768
|
[compactionItem, carryItem],
|
|
500
769
|
),
|
|
770
|
+
sectionItem(
|
|
771
|
+
"freeflow.taskMemory",
|
|
772
|
+
"Task Memory",
|
|
773
|
+
"Task memory for this session.",
|
|
774
|
+
capabilityStatus(state.taskMemory),
|
|
775
|
+
[
|
|
776
|
+
taskMemoryItem,
|
|
777
|
+
nestedGroup(
|
|
778
|
+
"freeflow.taskMemory.autoApprove",
|
|
779
|
+
"Auto-approve",
|
|
780
|
+
"Task memory actions the agent may take without asking you first, in this session. Enter opens the five switches; Esc returns.",
|
|
781
|
+
autoApproveItems,
|
|
782
|
+
),
|
|
783
|
+
],
|
|
784
|
+
),
|
|
785
|
+
sectionItem("freeflow.messaging", "Messaging", "Messaging for this session.", capabilityStatus(state.messaging), [
|
|
786
|
+
messagingItem,
|
|
787
|
+
inboundItem,
|
|
788
|
+
]),
|
|
789
|
+
sectionItem("freeflow.review", "Review", "Review Work for this session.", "", [reviewModeItem, reviewModelItem]),
|
|
501
790
|
sectionItem("freeflow.session", "Session", "Session overrides.", "", [
|
|
502
791
|
{
|
|
503
792
|
id: "freeflow.session.reset",
|
|
@@ -528,7 +817,7 @@ function sessionFreeflowItems(state, cognitiveRoutingController, ctx, rawConfig,
|
|
|
528
817
|
},
|
|
529
818
|
]),
|
|
530
819
|
];
|
|
531
|
-
return items;
|
|
820
|
+
return inSectionOrder(items);
|
|
532
821
|
}
|
|
533
822
|
const COGNITIVE_ROUTING_THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
534
823
|
const COGNITIVE_ROUTING_EFFORT_DESCRIPTIONS = {
|
|
@@ -552,16 +841,17 @@ function isCognitiveRoutingProfile(value) {
|
|
|
552
841
|
COGNITIVE_ROUTING_THINKING_LEVELS.includes(value.thinking)
|
|
553
842
|
);
|
|
554
843
|
}
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
844
|
+
/**
|
|
845
|
+
* The thinking levels a model supports, by Pi's own rule (pi-ai's getSupportedThinkingLevels): a model without
|
|
846
|
+
* reasoning has only off; a level its thinkingLevelMap maps to null is unsupported; xhigh and max need a mapping.
|
|
847
|
+
* Pi's model registry has no such method, so the rule reads the model's data directly.
|
|
848
|
+
*/
|
|
849
|
+
function cognitiveRoutingThinkingLevels(model) {
|
|
850
|
+
if (model?.reasoning !== true) return ["off"];
|
|
559
851
|
return COGNITIVE_ROUTING_THINKING_LEVELS.filter((level) => {
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
return false;
|
|
564
|
-
}
|
|
852
|
+
const mapped = model.thinkingLevelMap?.[level];
|
|
853
|
+
if (mapped === null) return false;
|
|
854
|
+
return level === "xhigh" || level === "max" ? mapped !== undefined : true;
|
|
565
855
|
});
|
|
566
856
|
}
|
|
567
857
|
function cognitiveRoutingModelOptions(ctx) {
|
|
@@ -577,7 +867,7 @@ function cognitiveRoutingModelOptions(ctx) {
|
|
|
577
867
|
return models
|
|
578
868
|
.filter((model) => typeof model?.provider === "string" && typeof model?.id === "string")
|
|
579
869
|
.map((model) => {
|
|
580
|
-
const thinkingLevels = cognitiveRoutingThinkingLevels(model
|
|
870
|
+
const thinkingLevels = cognitiveRoutingThinkingLevels(model);
|
|
581
871
|
const provider = model.provider;
|
|
582
872
|
const modelId = model.id;
|
|
583
873
|
const key = cognitiveRoutingModelKey(provider, modelId);
|
|
@@ -624,6 +914,67 @@ function cognitiveRoutingConfirmStep(summary) {
|
|
|
624
914
|
selectedKey: "__save__",
|
|
625
915
|
};
|
|
626
916
|
}
|
|
917
|
+
/**
|
|
918
|
+
* The reviewer model: Freeflow passes the name on as written (Review Work), so the picker offers this host's
|
|
919
|
+
* authenticated models by `provider/id`, keeps a name set elsewhere, and clears the setting with None or inherit.
|
|
920
|
+
*/
|
|
921
|
+
function reviewerModelItem(options) {
|
|
922
|
+
const path = ["review", "model"];
|
|
923
|
+
const name = (value) => (typeof value === "string" ? value : undefined);
|
|
924
|
+
const repositoryModel = name(getPath(options.rawConfig, path));
|
|
925
|
+
const configuredModel = name(getPath(options.localConfig, path)) ?? repositoryModel;
|
|
926
|
+
const session = options.scope === "session";
|
|
927
|
+
const local = options.scope === "local";
|
|
928
|
+
const value = session ? options.sessionValue : local ? name(getPath(options.localConfig, path)) : repositoryModel;
|
|
929
|
+
const inherited = session ? configuredModel : repositoryModel;
|
|
930
|
+
const clear = session
|
|
931
|
+
? { label: "Inherit configured setting", description: "Remove this session's reviewer model." }
|
|
932
|
+
: local
|
|
933
|
+
? { label: "Inherit repository setting", description: "Remove your personal reviewer model." }
|
|
934
|
+
: { label: "None", description: "Review Work chooses the reviewer's model." };
|
|
935
|
+
const models = cognitiveRoutingModelOptions(options.ctx).map((model) => model.key);
|
|
936
|
+
const names = value && !models.includes(value) ? [value, ...models] : models;
|
|
937
|
+
return {
|
|
938
|
+
id: "freeflow.review.model",
|
|
939
|
+
label: "Reviewer model",
|
|
940
|
+
description: session
|
|
941
|
+
? "The model a reviewer runs on, in this Pi session only."
|
|
942
|
+
: local
|
|
943
|
+
? "The model a reviewer runs on, for you in this checkout."
|
|
944
|
+
: "The model a reviewer runs on, where the host lets the agent choose one. This edits shared .freeflow/config.json.",
|
|
945
|
+
path,
|
|
946
|
+
kind: "string",
|
|
947
|
+
value,
|
|
948
|
+
format: (current) => (typeof current === "string" ? current : "none"),
|
|
949
|
+
configScope: options.scope,
|
|
950
|
+
effectiveValue: options.effectiveValue,
|
|
951
|
+
effectiveSource: options.effectiveSource,
|
|
952
|
+
inheritedValue: session || local ? inherited : undefined,
|
|
953
|
+
inheritedSource:
|
|
954
|
+
session && name(getPath(options.localConfig, path))
|
|
955
|
+
? "local"
|
|
956
|
+
: (session || local) && repositoryModel
|
|
957
|
+
? "repository"
|
|
958
|
+
: "builtin",
|
|
959
|
+
wizard: () => ({
|
|
960
|
+
firstStep: () => ({
|
|
961
|
+
title: "Choose reviewer model",
|
|
962
|
+
choices: [
|
|
963
|
+
{ key: LOCAL_INHERIT, value: LOCAL_INHERIT, label: clear.label, description: clear.description },
|
|
964
|
+
...names.map((key) => ({
|
|
965
|
+
key,
|
|
966
|
+
value: key,
|
|
967
|
+
label: key,
|
|
968
|
+
description: models.includes(key) ? "" : "Set in the file; not an authenticated model here.",
|
|
969
|
+
})),
|
|
970
|
+
],
|
|
971
|
+
selectedKey: value ?? LOCAL_INHERIT,
|
|
972
|
+
}),
|
|
973
|
+
nextStep: () => undefined,
|
|
974
|
+
valueFromSelections: (selected) => (selected[0] === LOCAL_INHERIT ? undefined : selected[0]),
|
|
975
|
+
}),
|
|
976
|
+
};
|
|
977
|
+
}
|
|
627
978
|
function createCognitiveRoutingProfileWizard(
|
|
628
979
|
currentValue,
|
|
629
980
|
models,
|
|
@@ -842,9 +1193,10 @@ function freeflowItems(rawConfig, options = {}) {
|
|
|
842
1193
|
description:
|
|
843
1194
|
"Select worker evidence for Coordinator; disable projection to keep ordinary Pi context in all enabled profiles.",
|
|
844
1195
|
path: ["cognitiveRouting", "projection"],
|
|
845
|
-
|
|
1196
|
+
// On by default with routing, as cognitive-routing/config.ts resolves it.
|
|
1197
|
+
effectiveValue: cognitiveRoutingState?.projection ?? true,
|
|
846
1198
|
effectiveSource: cognitiveRoutingSettingsSource(cognitiveRoutingState?.projectionSource),
|
|
847
|
-
defaultValue:
|
|
1199
|
+
defaultValue: true,
|
|
848
1200
|
});
|
|
849
1201
|
cognitiveRoutingProjectionItem.inactive = freeflowInactive || cognitiveRoutingRuntimeDisabled;
|
|
850
1202
|
cognitiveRoutingProjectionItem.runtimeInactive = cognitiveRoutingRuntimeDisabled;
|
|
@@ -903,11 +1255,24 @@ function freeflowItems(rawConfig, options = {}) {
|
|
|
903
1255
|
label: "Enabled",
|
|
904
1256
|
description: "Enable the Tool Execution capability.",
|
|
905
1257
|
path: ["toolExecution", "enabled"],
|
|
906
|
-
effectiveValue: toolExecutionState?.enabled ??
|
|
1258
|
+
effectiveValue: toolExecutionState?.enabled ?? DEFAULT_TOOL_EXECUTION_CONFIG.enabled,
|
|
907
1259
|
effectiveSource: toolSource(["toolExecution", "enabled"]),
|
|
908
|
-
defaultValue:
|
|
1260
|
+
defaultValue: DEFAULT_TOOL_EXECUTION_CONFIG.enabled,
|
|
1261
|
+
});
|
|
1262
|
+
const guidanceNoticesItem = createScopedBooleanItem({
|
|
1263
|
+
scope,
|
|
1264
|
+
rawConfig,
|
|
1265
|
+
localConfig,
|
|
1266
|
+
id: "freeflow.toolExecution.guidanceNotices",
|
|
1267
|
+
label: "Guidance notices",
|
|
1268
|
+
description:
|
|
1269
|
+
"Add a skill's key lines to the result of the tool call that needs them, such as a commit or a failing test.",
|
|
1270
|
+
path: ["toolExecution", "guidanceNotices"],
|
|
1271
|
+
effectiveValue: toolExecutionState?.guidanceNotices ?? DEFAULT_TOOL_EXECUTION_CONFIG.guidanceNotices,
|
|
1272
|
+
effectiveSource: toolSource(["toolExecution", "guidanceNotices"]),
|
|
1273
|
+
defaultValue: DEFAULT_TOOL_EXECUTION_CONFIG.guidanceNotices,
|
|
909
1274
|
});
|
|
910
|
-
const toolExecutionItems = [toolExecutionEnabledItem];
|
|
1275
|
+
const toolExecutionItems = [toolExecutionEnabledItem, guidanceNoticesItem];
|
|
911
1276
|
walkSettingsItems(toolExecutionItems, (item) => {
|
|
912
1277
|
item.inactive = freeflowInactive;
|
|
913
1278
|
});
|
|
@@ -916,7 +1281,7 @@ function freeflowItems(rawConfig, options = {}) {
|
|
|
916
1281
|
label: "Tool Execution",
|
|
917
1282
|
description: "Configure Freeflow's execution layer around Pi's tools.",
|
|
918
1283
|
kind: "group",
|
|
919
|
-
value: toolExecutionState?.enabled ??
|
|
1284
|
+
value: toolExecutionState?.enabled ?? DEFAULT_TOOL_EXECUTION_CONFIG.enabled,
|
|
920
1285
|
inactive: freeflowInactive,
|
|
921
1286
|
displaySuffix: toolExecutionState?.effective ? "active" : toolExecutionState?.enabled ? "inactive" : "disabled",
|
|
922
1287
|
children: toolExecutionItems,
|
|
@@ -960,15 +1325,256 @@ function freeflowItems(rawConfig, options = {}) {
|
|
|
960
1325
|
displaySuffix: compactionState?.effective ? "active" : compactionState?.enabled ? "inactive" : "disabled",
|
|
961
1326
|
children: compactionItems,
|
|
962
1327
|
};
|
|
963
|
-
|
|
1328
|
+
const taskMemoryState = options.taskMemory;
|
|
1329
|
+
const taskMemoryEnabledItem = createScopedBooleanItem({
|
|
1330
|
+
scope,
|
|
1331
|
+
rawConfig,
|
|
1332
|
+
localConfig,
|
|
1333
|
+
id: "freeflow.taskMemory.enabled",
|
|
1334
|
+
label: "Enabled",
|
|
1335
|
+
description:
|
|
1336
|
+
"A preview: attach Working Records to the session, deliver the focused record after compaction, and refuse record writes to records not attached.",
|
|
1337
|
+
path: ["taskMemory", "enabled"],
|
|
1338
|
+
effectiveValue: taskMemoryState?.enabled ?? DEFAULT_TASK_MEMORY_ENABLED,
|
|
1339
|
+
effectiveSource: toolSource(["taskMemory", "enabled"]),
|
|
1340
|
+
defaultValue: DEFAULT_TASK_MEMORY_ENABLED,
|
|
1341
|
+
});
|
|
1342
|
+
taskMemoryEnabledItem.inactive = freeflowInactive;
|
|
1343
|
+
// Which proposals skip the confirmation dialog (Task 004 D-002).
|
|
1344
|
+
const approveLabels = AUTO_APPROVE_LABELS;
|
|
1345
|
+
const approveItems = Object.keys(AUTO_APPROVE_GROUPS).map((group) => {
|
|
1346
|
+
const item = createScopedBooleanItem({
|
|
1347
|
+
scope,
|
|
1348
|
+
rawConfig,
|
|
1349
|
+
localConfig,
|
|
1350
|
+
id: `freeflow.taskMemory.autoApprove.${group}`,
|
|
1351
|
+
label: approveLabels[group][0],
|
|
1352
|
+
description: `Let the agent do this without asking you first: ${approveLabels[group][1]}. Off asks every time.`,
|
|
1353
|
+
path: ["taskMemory", "autoApprove", group],
|
|
1354
|
+
effectiveValue: taskMemoryState?.autoApprove?.[group] ?? false,
|
|
1355
|
+
effectiveSource: toolSource(["taskMemory", "autoApprove", group]),
|
|
1356
|
+
defaultValue: false,
|
|
1357
|
+
});
|
|
1358
|
+
item.inactive = freeflowInactive;
|
|
1359
|
+
return item;
|
|
1360
|
+
});
|
|
1361
|
+
const taskMemoryGroup = {
|
|
1362
|
+
id: "freeflow.taskMemory",
|
|
1363
|
+
label: "Task Memory",
|
|
1364
|
+
description: "Configure task memory: Working Records the session knows, delivers and guards.",
|
|
1365
|
+
kind: "group",
|
|
1366
|
+
value: taskMemoryState?.enabled ?? DEFAULT_TASK_MEMORY_ENABLED,
|
|
1367
|
+
inactive: freeflowInactive,
|
|
1368
|
+
displaySuffix: taskMemoryState?.effective ? "active" : taskMemoryState?.enabled ? "inactive" : "disabled",
|
|
1369
|
+
children: [
|
|
1370
|
+
taskMemoryEnabledItem,
|
|
1371
|
+
{
|
|
1372
|
+
id: "freeflow.taskMemory.autoApprove",
|
|
1373
|
+
label: "Auto-approve",
|
|
1374
|
+
description:
|
|
1375
|
+
"Task memory actions the agent may take without asking you first. Enter opens the five switches; Esc returns.",
|
|
1376
|
+
kind: "group",
|
|
1377
|
+
nested: true,
|
|
1378
|
+
value: true,
|
|
1379
|
+
children: approveItems,
|
|
1380
|
+
},
|
|
1381
|
+
],
|
|
1382
|
+
};
|
|
1383
|
+
const messagingState = options.messaging;
|
|
1384
|
+
const messagingEnabledItem = createScopedBooleanItem({
|
|
1385
|
+
scope,
|
|
1386
|
+
rawConfig,
|
|
1387
|
+
localConfig,
|
|
1388
|
+
id: "freeflow.messaging.enabled",
|
|
1389
|
+
label: "Enabled",
|
|
1390
|
+
description:
|
|
1391
|
+
"A preview: list this session for other Pi sessions on this machine and let the agent exchange messages with them.",
|
|
1392
|
+
path: ["messaging", "enabled"],
|
|
1393
|
+
effectiveValue: messagingState?.enabled ?? DEFAULT_MESSAGING_ENABLED,
|
|
1394
|
+
effectiveSource: toolSource(["messaging", "enabled"]),
|
|
1395
|
+
defaultValue: DEFAULT_MESSAGING_ENABLED,
|
|
1396
|
+
});
|
|
1397
|
+
messagingEnabledItem.inactive = freeflowInactive;
|
|
1398
|
+
// Messages are delivered unless the user asks to approve them (Task 002 D-008).
|
|
1399
|
+
const messagingInboundItem = createScopedChoiceItem({
|
|
1400
|
+
scope,
|
|
1401
|
+
rawConfig,
|
|
1402
|
+
localConfig,
|
|
1403
|
+
id: "freeflow.messaging.inbound",
|
|
1404
|
+
label: "Ask before delivering",
|
|
1405
|
+
description: "Whether a message from another session waits for you to accept it.",
|
|
1406
|
+
path: ["messaging", "inbound"],
|
|
1407
|
+
values: INBOUND_POLICIES,
|
|
1408
|
+
valueLabels: INBOUND_LABELS,
|
|
1409
|
+
valueDescriptions: INBOUND_DESCRIPTIONS,
|
|
1410
|
+
defaultValue: DEFAULT_INBOUND_POLICY,
|
|
1411
|
+
effectiveValue: messagingState?.inbound ?? DEFAULT_INBOUND_POLICY,
|
|
1412
|
+
effectiveSource: toolSource(["messaging", "inbound"]),
|
|
1413
|
+
});
|
|
1414
|
+
messagingInboundItem.inactive = freeflowInactive;
|
|
1415
|
+
const messagingGroup = {
|
|
1416
|
+
id: "freeflow.messaging",
|
|
1417
|
+
label: "Messaging",
|
|
1418
|
+
description: "Configure messaging between Pi sessions on this machine.",
|
|
1419
|
+
kind: "group",
|
|
1420
|
+
value: messagingState?.enabled ?? DEFAULT_MESSAGING_ENABLED,
|
|
1421
|
+
inactive: freeflowInactive,
|
|
1422
|
+
displaySuffix: messagingState?.effective ? "active" : messagingState?.enabled ? "inactive" : "disabled",
|
|
1423
|
+
children: [messagingEnabledItem, messagingInboundItem],
|
|
1424
|
+
};
|
|
1425
|
+
// The runtime prompts and skills, on every host (config-validation.ts).
|
|
1426
|
+
// Each a section whose row is its master switch, with one row opening a switch per item.
|
|
1427
|
+
const masterItem = (section, label, description) =>
|
|
1428
|
+
createScopedBooleanItem({
|
|
1429
|
+
scope,
|
|
1430
|
+
rawConfig,
|
|
1431
|
+
localConfig,
|
|
1432
|
+
id: `freeflow.${section}.enabled`,
|
|
1433
|
+
label,
|
|
1434
|
+
description,
|
|
1435
|
+
path: [section, "enabled"],
|
|
1436
|
+
effectiveValue: sectionEnabled(rawConfig, localConfig, section),
|
|
1437
|
+
effectiveSource: toolSource([section, "enabled"]),
|
|
1438
|
+
defaultValue: true,
|
|
1439
|
+
});
|
|
1440
|
+
const eachPrompt = nestedGroup(
|
|
1441
|
+
"freeflow.prompts.each",
|
|
1442
|
+
"Each prompt",
|
|
1443
|
+
"One switch per prompt. Enter opens them; Esc returns.",
|
|
1444
|
+
RUNTIME_PROMPTS.map((prompt) =>
|
|
1445
|
+
createScopedBooleanItem({
|
|
1446
|
+
scope,
|
|
1447
|
+
rawConfig,
|
|
1448
|
+
localConfig,
|
|
1449
|
+
id: `freeflow.prompts.${prompt}`,
|
|
1450
|
+
label: PROMPT_LABELS[prompt][0],
|
|
1451
|
+
description: PROMPT_LABELS[prompt][1],
|
|
1452
|
+
path: ["prompts", prompt],
|
|
1453
|
+
effectiveValue: configuredSetting(rawConfig, localConfig, ["prompts", prompt], true).value,
|
|
1454
|
+
effectiveSource: toolSource(["prompts", prompt]),
|
|
1455
|
+
defaultValue: true,
|
|
1456
|
+
}),
|
|
1457
|
+
),
|
|
1458
|
+
);
|
|
1459
|
+
const promptsGroup = {
|
|
1460
|
+
id: "freeflow.prompts",
|
|
1461
|
+
label: "Runtime prompts",
|
|
1462
|
+
description: "The prompts Freeflow adds to every session: Core, the Interaction Contract and the Working Method.",
|
|
1463
|
+
kind: "group",
|
|
1464
|
+
value: true,
|
|
1465
|
+
displaySuffix: "",
|
|
1466
|
+
children: [
|
|
1467
|
+
masterItem("prompts", "Enabled", "Add Freeflow's runtime prompts. Off leaves all three out; Freeflow stays on."),
|
|
1468
|
+
eachPrompt,
|
|
1469
|
+
],
|
|
1470
|
+
};
|
|
1471
|
+
const eachSkill = nestedGroup(
|
|
1472
|
+
"freeflow.skills.each",
|
|
1473
|
+
"Each skill",
|
|
1474
|
+
"One switch per skill. Enter opens them; Esc returns.",
|
|
1475
|
+
SWITCHABLE_SKILLS.map((skill) =>
|
|
1476
|
+
createScopedBooleanItem({
|
|
1477
|
+
scope,
|
|
1478
|
+
rawConfig,
|
|
1479
|
+
localConfig,
|
|
1480
|
+
id: `freeflow.skills.${skill}`,
|
|
1481
|
+
label: skillLabel(skill),
|
|
1482
|
+
description: `Give the agent ${skill}. Off, Pi does not load it and its command refuses; other hosts load it themselves, so there the agent is told not to use it.`,
|
|
1483
|
+
path: ["skills", skill],
|
|
1484
|
+
effectiveValue: configuredSetting(rawConfig, localConfig, ["skills", skill], true).value,
|
|
1485
|
+
effectiveSource: toolSource(["skills", skill]),
|
|
1486
|
+
defaultValue: true,
|
|
1487
|
+
}),
|
|
1488
|
+
),
|
|
1489
|
+
);
|
|
1490
|
+
const skillsGroup = {
|
|
1491
|
+
id: "freeflow.skills",
|
|
1492
|
+
label: "Skills",
|
|
1493
|
+
description: "The skills Freeflow gives the agent; Setup Freeflow always stays.",
|
|
1494
|
+
kind: "group",
|
|
1495
|
+
value: true,
|
|
1496
|
+
displaySuffix: "",
|
|
1497
|
+
children: [
|
|
1498
|
+
masterItem(
|
|
1499
|
+
"skills",
|
|
1500
|
+
"Enabled",
|
|
1501
|
+
"Give the agent Freeflow's skills. Off turns every skill but Setup Freeflow off.",
|
|
1502
|
+
),
|
|
1503
|
+
eachSkill,
|
|
1504
|
+
],
|
|
1505
|
+
};
|
|
1506
|
+
walkSettingsItems([promptsGroup, skillsGroup], (item) => {
|
|
1507
|
+
item.inactive = freeflowInactive;
|
|
1508
|
+
});
|
|
1509
|
+
// Review Work's choice on every host (config-validation.ts).
|
|
1510
|
+
const review = resolveReviewSettings(rawConfig, localConfig);
|
|
1511
|
+
const reviewModeItem = createScopedChoiceItem({
|
|
1512
|
+
scope,
|
|
1513
|
+
rawConfig,
|
|
1514
|
+
localConfig,
|
|
1515
|
+
id: "freeflow.review.mode",
|
|
1516
|
+
label: "Independent review",
|
|
1517
|
+
description: "When finished work is reviewed by a separate reviewer; the agent's own self-review always runs.",
|
|
1518
|
+
path: ["review", "mode"],
|
|
1519
|
+
values: REVIEW_MODES,
|
|
1520
|
+
valueLabels: { auto: "auto", off: "off", always: "always" },
|
|
1521
|
+
valueDescriptions: REVIEW_DESCRIPTIONS,
|
|
1522
|
+
defaultValue: "auto",
|
|
1523
|
+
effectiveValue: review.mode,
|
|
1524
|
+
effectiveSource: toolSource(["review", "mode"]),
|
|
1525
|
+
});
|
|
1526
|
+
reviewModeItem.inactive = freeflowInactive;
|
|
1527
|
+
reviewModeItem.heading = true;
|
|
1528
|
+
const reviewModelItem = reviewerModelItem({
|
|
1529
|
+
scope,
|
|
1530
|
+
rawConfig,
|
|
1531
|
+
localConfig,
|
|
1532
|
+
effectiveValue: review.model,
|
|
1533
|
+
effectiveSource: toolSource(["review", "model"]),
|
|
1534
|
+
ctx: options.ctx,
|
|
1535
|
+
});
|
|
1536
|
+
reviewModelItem.inactive = freeflowInactive;
|
|
1537
|
+
const reviewGroup = {
|
|
1538
|
+
id: "freeflow.review",
|
|
1539
|
+
label: "Review",
|
|
1540
|
+
description: "Configure Review Work's independent review, on every host that reads these files.",
|
|
1541
|
+
kind: "group",
|
|
1542
|
+
value: true,
|
|
1543
|
+
inactive: freeflowInactive,
|
|
1544
|
+
displaySuffix: "",
|
|
1545
|
+
children: [reviewModeItem, reviewModelItem],
|
|
1546
|
+
};
|
|
1547
|
+
return inSectionOrder([
|
|
1548
|
+
freeflowItem,
|
|
1549
|
+
...(cognitiveRoutingGroup ? [cognitiveRoutingGroup] : []),
|
|
1550
|
+
toolExecutionGroup,
|
|
1551
|
+
compactionGroup,
|
|
1552
|
+
taskMemoryGroup,
|
|
1553
|
+
messagingGroup,
|
|
1554
|
+
reviewGroup,
|
|
1555
|
+
promptsGroup,
|
|
1556
|
+
skillsGroup,
|
|
1557
|
+
]);
|
|
964
1558
|
}
|
|
965
1559
|
function pruneKnownDefaults(config) {
|
|
966
1560
|
const defaultPaths = [
|
|
967
1561
|
{ path: ["enabled"], value: DEFAULT_FREEFLOW_ENABLED },
|
|
968
1562
|
{ path: ["cognitiveRouting", "delegation"], value: "executor" },
|
|
969
1563
|
{ path: ["toolExecution", "enabled"], value: DEFAULT_TOOL_EXECUTION_CONFIG.enabled },
|
|
1564
|
+
{ path: ["toolExecution", "guidanceNotices"], value: DEFAULT_TOOL_EXECUTION_CONFIG.guidanceNotices },
|
|
970
1565
|
{ path: ["compaction", "enabled"], value: DEFAULT_COMPACTION_ENABLED },
|
|
971
1566
|
{ path: ["compaction", "carry"], value: DEFAULT_COMPACTION_CARRY },
|
|
1567
|
+
{ path: ["taskMemory", "enabled"], value: DEFAULT_TASK_MEMORY_ENABLED },
|
|
1568
|
+
...Object.keys(AUTO_APPROVE_GROUPS).map((group) => ({
|
|
1569
|
+
path: ["taskMemory", "autoApprove", group],
|
|
1570
|
+
value: false,
|
|
1571
|
+
})),
|
|
1572
|
+
{ path: ["messaging", "enabled"], value: DEFAULT_MESSAGING_ENABLED },
|
|
1573
|
+
{ path: ["messaging", "inbound"], value: DEFAULT_INBOUND_POLICY },
|
|
1574
|
+
{ path: ["prompts", "enabled"], value: true },
|
|
1575
|
+
...RUNTIME_PROMPTS.map((prompt) => ({ path: ["prompts", prompt], value: true })),
|
|
1576
|
+
{ path: ["skills", "enabled"], value: true },
|
|
1577
|
+
...SWITCHABLE_SKILLS.map((skill) => ({ path: ["skills", skill], value: true })),
|
|
972
1578
|
];
|
|
973
1579
|
for (const item of defaultPaths) {
|
|
974
1580
|
if (valuesEqual(getPath(config, item.path), item.value)) {
|
|
@@ -1051,24 +1657,54 @@ function formatSettingValue(item, value) {
|
|
|
1051
1657
|
const key = String(value ?? "");
|
|
1052
1658
|
return item.valueLabels?.[key] && key !== LOCAL_INHERIT ? item.valueLabels[key] : formatCoreValue(value);
|
|
1053
1659
|
}
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1660
|
+
const ORIGINS = {
|
|
1661
|
+
builtin: "Inherited: not set in any file, so Freeflow's default applies.",
|
|
1662
|
+
repository: "Inherited from the repository's .freeflow/config.json.",
|
|
1663
|
+
local: "Inherited from your .freeflow/local.json.",
|
|
1664
|
+
session: "Inherited from this session.",
|
|
1665
|
+
};
|
|
1666
|
+
function displayParts(item) {
|
|
1667
|
+
const tone = (value) => (value === true ? { tone: "on" } : value === false ? { tone: "off" } : {});
|
|
1668
|
+
if (item.kind === "group") return { value: item.displaySuffix ?? "" };
|
|
1669
|
+
if (item.transient && item.format) return { value: item.format(item.value) };
|
|
1057
1670
|
if (item.id === "freeflow.cognitiveRouting.profile")
|
|
1058
|
-
return item.valueLabels?.[String(item.value)] ?? String(item.value);
|
|
1671
|
+
return { value: item.valueLabels?.[String(item.value)] ?? String(item.value) };
|
|
1059
1672
|
if (item.configScope === "local" || item.configScope === "session") {
|
|
1060
1673
|
const inherits = item.value === LOCAL_INHERIT || item.value === undefined;
|
|
1061
1674
|
if (inherits) {
|
|
1062
|
-
if (item.inheritedValue === undefined) return "not configured";
|
|
1063
|
-
return
|
|
1675
|
+
if (item.inheritedValue === undefined) return { value: "not configured" };
|
|
1676
|
+
return {
|
|
1677
|
+
value: formatSettingValue(item, item.inheritedValue),
|
|
1678
|
+
note: "inherited",
|
|
1679
|
+
origin: ORIGINS[item.inheritedSource ?? "builtin"],
|
|
1680
|
+
...tone(item.inheritedValue),
|
|
1681
|
+
};
|
|
1064
1682
|
}
|
|
1065
|
-
|
|
1683
|
+
// The value this tab sets, not the effective one: a session override or a switch above it does not change it.
|
|
1684
|
+
const value =
|
|
1685
|
+
item.configValues && String(item.value) in item.configValues ? item.configValues[String(item.value)] : item.value;
|
|
1686
|
+
return { value: formatSettingValue(item, value), ...tone(value) };
|
|
1066
1687
|
}
|
|
1067
1688
|
const value = item.value === undefined && isCognitiveRoutingProfileItem(item) ? undefined : item.value;
|
|
1068
|
-
const shown = formatSettingValue(item, value);
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1689
|
+
const shown = { value: formatSettingValue(item, value), ...tone(value) };
|
|
1690
|
+
const unset = item.repositoryUnset ?? (item.effectiveSource === "builtin" && item.defaultValue !== undefined);
|
|
1691
|
+
const notes = [
|
|
1692
|
+
...(unset ? ["inherited"] : []),
|
|
1693
|
+
...(item.localOverrideValue !== undefined && !valuesEqual(item.localOverrideValue, item.value)
|
|
1694
|
+
? [`personal: ${formatSettingValue(item, item.localOverrideValue)}`]
|
|
1695
|
+
: []),
|
|
1696
|
+
];
|
|
1697
|
+
return {
|
|
1698
|
+
...shown,
|
|
1699
|
+
...(notes.length ? { note: notes.join(" · ") } : {}),
|
|
1700
|
+
...(unset ? { origin: "Inherited: not set in .freeflow/config.json, so Freeflow's default applies." } : {}),
|
|
1701
|
+
};
|
|
1702
|
+
}
|
|
1703
|
+
function joinParts(parts) {
|
|
1704
|
+
return parts.note ? `${parts.value} (${parts.note})` : parts.value;
|
|
1705
|
+
}
|
|
1706
|
+
function displayValue(item) {
|
|
1707
|
+
return joinParts(displayParts(item));
|
|
1072
1708
|
}
|
|
1073
1709
|
/** Small choices change in place; holding a profile switches models and a reset clears everything, so those ask. */
|
|
1074
1710
|
const PICKED_CHOICES = new Set(["freeflow.cognitiveRouting.profile", "freeflow.session.reset"]);
|
|
@@ -1102,13 +1738,12 @@ function refreshSettingsDerivedState(items) {
|
|
|
1102
1738
|
item ? isCognitiveRoutingProfile(effectiveItemValue(item)) : false,
|
|
1103
1739
|
);
|
|
1104
1740
|
cognitiveRoutingGroup.value = enabled;
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
: "disabled";
|
|
1741
|
+
// A status the runtime reported (active with its control mode, a blocking reason, host unsupported) stays until
|
|
1742
|
+
// the switch goes off; only the configuration statuses are worked out again here.
|
|
1743
|
+
const current = cognitiveRoutingGroup.displaySuffix ?? "";
|
|
1744
|
+
if (!enabled) cognitiveRoutingGroup.displaySuffix = "disabled";
|
|
1745
|
+
else if (["", "disabled", "configured", "not configured"].includes(current))
|
|
1746
|
+
cognitiveRoutingGroup.displaySuffix = profilesConfigured ? "configured" : "not configured";
|
|
1112
1747
|
cognitiveRoutingGroup.inactive = freeflowInactive;
|
|
1113
1748
|
}
|
|
1114
1749
|
const contextGroup = findSettingsItem(items, "freeflow.context");
|
|
@@ -1135,6 +1770,20 @@ function refreshSettingsDerivedState(items) {
|
|
|
1135
1770
|
compactionGroup.value = on;
|
|
1136
1771
|
compactionGroup.displaySuffix = on && !freeflowInactive ? "active" : on ? "inactive" : "disabled";
|
|
1137
1772
|
}
|
|
1773
|
+
const taskMemoryGroup = findSettingsItem(items, "freeflow.taskMemory");
|
|
1774
|
+
const taskMemoryEnabled = findSettingsItem(items, "freeflow.taskMemory.enabled");
|
|
1775
|
+
if (taskMemoryGroup && taskMemoryEnabled) {
|
|
1776
|
+
const on = effectiveItemValue(taskMemoryEnabled) === true;
|
|
1777
|
+
taskMemoryGroup.value = on;
|
|
1778
|
+
taskMemoryGroup.displaySuffix = on && !freeflowInactive ? "active" : on ? "inactive" : "disabled";
|
|
1779
|
+
}
|
|
1780
|
+
const messagingGroup = findSettingsItem(items, "freeflow.messaging");
|
|
1781
|
+
const messagingEnabled = findSettingsItem(items, "freeflow.messaging.enabled");
|
|
1782
|
+
if (messagingGroup && messagingEnabled) {
|
|
1783
|
+
const on = effectiveItemValue(messagingEnabled) === true;
|
|
1784
|
+
messagingGroup.value = on;
|
|
1785
|
+
messagingGroup.displaySuffix = on && !freeflowInactive ? "active" : on ? "inactive" : "disabled";
|
|
1786
|
+
}
|
|
1138
1787
|
walkSettingsItems(items, (candidate) => {
|
|
1139
1788
|
if (candidate.id === "freeflow.session.reset") {
|
|
1140
1789
|
candidate.inactive = false;
|
|
@@ -1151,8 +1800,8 @@ function refreshSettingsDerivedState(items) {
|
|
|
1151
1800
|
function settingsChoices(item) {
|
|
1152
1801
|
if (item.kind === "boolean") {
|
|
1153
1802
|
return [
|
|
1154
|
-
{ key: "true", value: true, label: "
|
|
1155
|
-
{ key: "false", value: false, label: "
|
|
1803
|
+
{ key: "true", value: true, label: "on" },
|
|
1804
|
+
{ key: "false", value: false, label: "off" },
|
|
1156
1805
|
];
|
|
1157
1806
|
}
|
|
1158
1807
|
if (item.kind !== "enum") return undefined;
|
|
@@ -1174,53 +1823,136 @@ function resetSessionItemState(items) {
|
|
|
1174
1823
|
}
|
|
1175
1824
|
});
|
|
1176
1825
|
}
|
|
1177
|
-
function settingsEntries(items, rootItems, onChange) {
|
|
1826
|
+
function settingsEntries(items, rootItems, onChange, depth = 0, lockedBy) {
|
|
1827
|
+
// Freeflow's own switch locks everything else on the screen while it is off.
|
|
1828
|
+
if (depth === 0 && !lockedBy) {
|
|
1829
|
+
const master = rootItems.find((candidate) => candidate.id === "freeflow.enabled");
|
|
1830
|
+
const off = () =>
|
|
1831
|
+
master && effectiveItemValue(master) === false ? "Takes effect when Freeflow is on." : undefined;
|
|
1832
|
+
return items.flatMap((item) =>
|
|
1833
|
+
settingsEntries([item], rootItems, onChange, 0, item === master ? () => undefined : off),
|
|
1834
|
+
);
|
|
1835
|
+
}
|
|
1178
1836
|
return items.flatMap((item) => {
|
|
1837
|
+
if (item.kind === "group" && item.children && item.nested) {
|
|
1838
|
+
// One row; Enter opens its settings on a screen of their own. Its value names the ones that are on.
|
|
1839
|
+
const children = item.children;
|
|
1840
|
+
const parts = () => {
|
|
1841
|
+
const isOn = (child) => displayParts(child).tone === "on";
|
|
1842
|
+
const on = children.filter(isOn).map((child) => child.label.toLowerCase());
|
|
1843
|
+
const off = children.filter((child) => !isOn(child)).map((child) => child.label.toLowerCase());
|
|
1844
|
+
// Name the shorter side: what is on, or what is off when most are on.
|
|
1845
|
+
return on.length === 0
|
|
1846
|
+
? { value: "none", tone: "off" }
|
|
1847
|
+
: off.length === 0
|
|
1848
|
+
? { value: "all", tone: "on" }
|
|
1849
|
+
: off.length < on.length
|
|
1850
|
+
? { value: `all but ${off.join(", ")}` }
|
|
1851
|
+
: { value: on.join(", ") };
|
|
1852
|
+
};
|
|
1853
|
+
return [
|
|
1854
|
+
{
|
|
1855
|
+
id: item.id,
|
|
1856
|
+
label: item.label,
|
|
1857
|
+
description: item.description,
|
|
1858
|
+
depth,
|
|
1859
|
+
parts,
|
|
1860
|
+
currentValue: () => parts().value,
|
|
1861
|
+
inactive: () => false,
|
|
1862
|
+
...(lockedBy ? { dormant: lockedBy } : {}),
|
|
1863
|
+
children: () => settingsEntries(children, rootItems, onChange, 0, lockedBy),
|
|
1864
|
+
},
|
|
1865
|
+
];
|
|
1866
|
+
}
|
|
1179
1867
|
if (item.kind === "group" && item.children) {
|
|
1868
|
+
// A capability's own switch is its section's row; while it is off, the section's other settings are locked.
|
|
1869
|
+
const toggle = item.children.find((child) => child.id === `${item.id}.enabled` || child.heading);
|
|
1870
|
+
if (toggle) {
|
|
1871
|
+
const header = settingEntry(toggle, rootItems, onChange, depth, lockedBy);
|
|
1872
|
+
// The section's status adds what the switch alone does not say: not active, unavailable, the routing control.
|
|
1873
|
+
const parts = () => {
|
|
1874
|
+
const shown = displayParts(toggle);
|
|
1875
|
+
const status = item.displaySuffix ?? "";
|
|
1876
|
+
const extra =
|
|
1877
|
+
status === "inactive"
|
|
1878
|
+
? shown.tone === "on"
|
|
1879
|
+
? "not active"
|
|
1880
|
+
: ""
|
|
1881
|
+
: ["", "active", "disabled", "configured"].includes(status)
|
|
1882
|
+
? ""
|
|
1883
|
+
: status.replace(/^active · /, "");
|
|
1884
|
+
return extra ? { ...shown, note: shown.note ? `${shown.note} · ${extra}` : extra } : shown;
|
|
1885
|
+
};
|
|
1886
|
+
const off = () =>
|
|
1887
|
+
lockedBy?.() ?? (effectiveItemValue(toggle) === false ? `Takes effect when ${item.label} is on.` : undefined);
|
|
1888
|
+
return [
|
|
1889
|
+
{ ...header, label: item.label, heading: true, parts, currentValue: () => joinParts(parts()) },
|
|
1890
|
+
...settingsEntries(
|
|
1891
|
+
item.children.filter((child) => child !== toggle),
|
|
1892
|
+
rootItems,
|
|
1893
|
+
onChange,
|
|
1894
|
+
depth + 1,
|
|
1895
|
+
off,
|
|
1896
|
+
),
|
|
1897
|
+
];
|
|
1898
|
+
}
|
|
1180
1899
|
const section = {
|
|
1181
1900
|
id: item.id,
|
|
1182
1901
|
label: item.label,
|
|
1183
1902
|
description: item.description,
|
|
1184
1903
|
section: true,
|
|
1904
|
+
depth,
|
|
1185
1905
|
currentValue: () => displayValue(item),
|
|
1186
1906
|
inactive: () => item.inactive === true,
|
|
1187
1907
|
};
|
|
1188
|
-
return [section, ...settingsEntries(item.children, rootItems, onChange)];
|
|
1908
|
+
return [section, ...settingsEntries(item.children, rootItems, onChange, depth + 1, lockedBy)];
|
|
1189
1909
|
}
|
|
1190
|
-
|
|
1191
|
-
return [
|
|
1192
|
-
{
|
|
1193
|
-
id: item.id,
|
|
1194
|
-
label: item.label,
|
|
1195
|
-
description: item.description,
|
|
1196
|
-
currentValue: () => displayValue(item),
|
|
1197
|
-
inactive: () => item.inactive === true,
|
|
1198
|
-
currentChoiceKey: () => String(item.value),
|
|
1199
|
-
choices,
|
|
1200
|
-
cycle: Boolean(choices?.length) && !PICKED_CHOICES.has(item.id),
|
|
1201
|
-
wizard: item.wizard ? () => item.wizard() : undefined,
|
|
1202
|
-
edit:
|
|
1203
|
-
!["boolean", "enum", "group"].includes(item.kind) && !item.wizard
|
|
1204
|
-
? {
|
|
1205
|
-
initialValue: () => item.editInitialValue?.() ?? displayValue(item),
|
|
1206
|
-
parse: (text) => (item.parse ? item.parse(text) : text),
|
|
1207
|
-
}
|
|
1208
|
-
: undefined,
|
|
1209
|
-
commit: async (value) => {
|
|
1210
|
-
if (valuesEqual(item.value, value)) return { changed: false, reloadRequired: false };
|
|
1211
|
-
const outcome = await onChange(item, value);
|
|
1212
|
-
if (outcome.changed) {
|
|
1213
|
-
updateScopedItemState(item, value);
|
|
1214
|
-
if (item.id === "freeflow.session.reset") resetSessionItemState(rootItems);
|
|
1215
|
-
else if (!item.transient) item.value = value;
|
|
1216
|
-
refreshSettingsDerivedState(rootItems);
|
|
1217
|
-
}
|
|
1218
|
-
return outcome;
|
|
1219
|
-
},
|
|
1220
|
-
},
|
|
1221
|
-
];
|
|
1910
|
+
return [settingEntry(item, rootItems, onChange, depth, lockedBy)];
|
|
1222
1911
|
});
|
|
1223
1912
|
}
|
|
1913
|
+
function settingEntry(item, rootItems, onChange, depth, lockedBy) {
|
|
1914
|
+
const choices = settingsChoices(item);
|
|
1915
|
+
return {
|
|
1916
|
+
id: item.id,
|
|
1917
|
+
label: item.label,
|
|
1918
|
+
description: item.description,
|
|
1919
|
+
depth,
|
|
1920
|
+
parts: () => displayParts(item),
|
|
1921
|
+
currentValue: () => displayValue(item),
|
|
1922
|
+
// A setting under an off switch stays changeable, so it can be set up before the switch is turned on; only what
|
|
1923
|
+
// the host cannot do (runtimeInactive), or what acts at once, blocks it.
|
|
1924
|
+
inactive: () => item.runtimeInactive === true || (item.inactive === true && lockedBy?.() === undefined),
|
|
1925
|
+
...(lockedBy ? { dormant: lockedBy } : {}),
|
|
1926
|
+
currentChoiceKey: () => String(item.value),
|
|
1927
|
+
choices,
|
|
1928
|
+
cycle: Boolean(choices?.length) && !PICKED_CHOICES.has(item.id),
|
|
1929
|
+
wizard: item.wizard ? () => item.wizard() : undefined,
|
|
1930
|
+
edit:
|
|
1931
|
+
!["boolean", "enum", "group"].includes(item.kind) && !item.wizard
|
|
1932
|
+
? {
|
|
1933
|
+
initialValue: () => item.editInitialValue?.() ?? displayValue(item),
|
|
1934
|
+
parse: (text) => (item.parse ? item.parse(text) : text),
|
|
1935
|
+
}
|
|
1936
|
+
: undefined,
|
|
1937
|
+
commit: async (value) => {
|
|
1938
|
+
if (valuesEqual(item.value, value)) return { changed: false, reloadRequired: false };
|
|
1939
|
+
const outcome = await onChange(item, value);
|
|
1940
|
+
if (outcome.changed) {
|
|
1941
|
+
updateScopedItemState(item, value);
|
|
1942
|
+
// The repository file now sets it, unless the value was its default, which setConfigValue leaves out of the file.
|
|
1943
|
+
if (item.configScope === "repository")
|
|
1944
|
+
item.repositoryUnset =
|
|
1945
|
+
value === undefined ||
|
|
1946
|
+
isEmptyValue(value) ||
|
|
1947
|
+
(item.defaultValue !== undefined && valuesEqual(value, item.defaultValue));
|
|
1948
|
+
if (item.id === "freeflow.session.reset") resetSessionItemState(rootItems);
|
|
1949
|
+
else if (!item.transient) item.value = value;
|
|
1950
|
+
refreshSettingsDerivedState(rootItems);
|
|
1951
|
+
}
|
|
1952
|
+
return outcome;
|
|
1953
|
+
},
|
|
1954
|
+
};
|
|
1955
|
+
}
|
|
1224
1956
|
async function openSettings(options) {
|
|
1225
1957
|
if (typeof options.ctx?.ui?.custom !== "function") {
|
|
1226
1958
|
options.ctx?.ui?.notify?.(
|
|
@@ -1348,11 +2080,23 @@ export async function handleFreeflowCommand(args, ctx, afterChange, pi, cognitiv
|
|
|
1348
2080
|
cognitiveRouting: state.cognitiveRouting,
|
|
1349
2081
|
toolExecution: state.toolExecution,
|
|
1350
2082
|
compaction: state.compaction,
|
|
2083
|
+
taskMemory: state.taskMemory,
|
|
2084
|
+
messaging: state.messaging,
|
|
1351
2085
|
ctx,
|
|
1352
2086
|
runtimeAvailable: isCognitiveRoutingRuntimeAvailable(pi),
|
|
1353
2087
|
}).find((candidate) => candidate.id === "freeflow.enabled");
|
|
1354
2088
|
await updateConfig(ctx.cwd, item, enabled, "repository");
|
|
1355
2089
|
await afterChange(true);
|
|
2090
|
+
// The command writes the repository file; a personal file or a session override still wins over it.
|
|
2091
|
+
const after = await readCapabilityState(ctx.cwd, ctx);
|
|
2092
|
+
const winner = after?.configSources?.enabled;
|
|
2093
|
+
if (after?.enabled !== enabled && (winner === "local" || winner === "session")) {
|
|
2094
|
+
ctx.ui.notify(
|
|
2095
|
+
`The repository now ${enabled ? "enables" : "disables"} Freeflow, but ${winner === "session" ? "this session's override" : "your .freeflow/local.json"} still turns it ${enabled ? "off" : "on"}. Change it in /freeflow settings.`,
|
|
2096
|
+
"warning",
|
|
2097
|
+
);
|
|
2098
|
+
return { changed: true, reloaded: false };
|
|
2099
|
+
}
|
|
1356
2100
|
ctx.ui.notify(`Freeflow ${enabled ? "enabled" : "disabled"}. Reloading Freeflow runtime...`, "info");
|
|
1357
2101
|
if (typeof ctx.reload === "function") {
|
|
1358
2102
|
await ctx.reload();
|
|
@@ -1399,6 +2143,8 @@ export async function handleFreeflowCommand(args, ctx, afterChange, pi, cognitiv
|
|
|
1399
2143
|
cognitiveRouting: stateNow.cognitiveRouting,
|
|
1400
2144
|
toolExecution: stateNow.toolExecution,
|
|
1401
2145
|
compaction: stateNow.compaction,
|
|
2146
|
+
taskMemory: stateNow.taskMemory,
|
|
2147
|
+
messaging: stateNow.messaging,
|
|
1402
2148
|
ctx,
|
|
1403
2149
|
runtimeAvailable: isCognitiveRoutingRuntimeAvailable(pi),
|
|
1404
2150
|
});
|
|
@@ -1535,7 +2281,14 @@ export async function handleFreeflowCommand(args, ctx, afterChange, pi, cognitiv
|
|
|
1535
2281
|
}
|
|
1536
2282
|
if (item.configScope === "session") {
|
|
1537
2283
|
const key = item.path.join(".");
|
|
1538
|
-
const override =
|
|
2284
|
+
const override =
|
|
2285
|
+
value === LOCAL_INHERIT || value === undefined
|
|
2286
|
+
? null
|
|
2287
|
+
: value === "true"
|
|
2288
|
+
? true
|
|
2289
|
+
: value === "false"
|
|
2290
|
+
? false
|
|
2291
|
+
: value;
|
|
1539
2292
|
const result = await setSessionCoreOverride(key, override, ctx, pi);
|
|
1540
2293
|
return { changed: result.changed, reloadRequired: result.reloadRequired === true };
|
|
1541
2294
|
}
|