@hassangameryt/freeflow 0.6.0 → 0.7.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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +2 -2
- package/.codex-plugin/plugin.json +4 -5
- package/README.md +39 -55
- package/capabilities/cognitive-routing/SKILL.md +288 -130
- package/command-surface.json +0 -24
- package/hooks/freeflow-runtime-context.mjs +42 -477
- package/hooks/hooks.json +1 -26
- package/hooks/tests/check-runtime-context-hook.sh +108 -319
- package/package.json +4 -2
- package/pi-extension/dist/cognitive-routing/commands.js +3 -3
- package/pi-extension/dist/cognitive-routing/config.js +16 -20
- package/pi-extension/dist/cognitive-routing/controller.js +478 -48
- package/pi-extension/dist/cognitive-routing/history.js +12 -0
- package/pi-extension/dist/cognitive-routing/host.js +74 -12
- package/pi-extension/dist/cognitive-routing/pi-routing-session.js +414 -0
- package/pi-extension/dist/cognitive-routing/pi-session-control.js +320 -0
- package/pi-extension/dist/cognitive-routing/tool.js +1 -0
- package/pi-extension/dist/cognitive-routing/types.js +1 -1
- package/pi-extension/dist/index.js +236 -219
- package/pi-extension/dist/runtime/runtime-context.js +126 -286
- package/pi-extension/dist/settings/settings-ui.js +52 -336
- package/pi-extension/src/cognitive-routing/commands.ts +15 -5
- package/pi-extension/src/cognitive-routing/config.ts +16 -20
- package/pi-extension/src/cognitive-routing/controller.ts +561 -51
- package/pi-extension/src/cognitive-routing/history.ts +12 -0
- package/pi-extension/src/cognitive-routing/host.ts +110 -16
- package/pi-extension/src/cognitive-routing/pi-routing-session.ts +538 -0
- package/pi-extension/src/cognitive-routing/pi-session-control.ts +444 -0
- package/pi-extension/src/cognitive-routing/tool.ts +1 -0
- package/pi-extension/src/cognitive-routing/types.ts +1 -1
- package/pi-extension/src/index.ts +249 -239
- package/pi-extension/src/runtime/runtime-context.ts +129 -305
- package/pi-extension/src/settings/settings-ui.ts +53 -371
- package/pi-extension/tests/cognitive-routing/commands.test.js +1 -1
- package/pi-extension/tests/cognitive-routing/controller.test.js +69 -40
- package/pi-extension/tests/cognitive-routing/extension-lifecycle.test.js +128 -57
- package/pi-extension/tests/cognitive-routing/pi-normal-lifecycle.test.js +434 -0
- package/pi-extension/tests/cognitive-routing/pi-session-control.test.js +174 -0
- package/pi-extension/tests/cognitive-routing/pi-session-history.test.js +91 -0
- package/pi-extension/tests/cognitive-routing/profile-resolution.test.js +47 -4
- package/pi-extension/tests/integration/mode-free-core.test.js +218 -0
- package/pi-extension/tests/integration/pi-extension.test.js +430 -2478
- package/pi-extension/tests/integration/prompt-architecture.test.js +124 -79
- package/pi-extension/tests/integration/unaffected-regressions.test.js +411 -0
- package/runtime/prompts/cognitive-routing.md +5 -4
- package/runtime/prompts/conversation-history.md +1 -1
- package/runtime/prompts/core.md +77 -6
- package/runtime/prompts/interaction-contract.md +6 -2
- package/skills/action-selection/SKILL.md +93 -44
- package/skills/action-selection/references/trajectory-stalls.md +71 -0
- package/skills/bypass/SKILL.md +109 -39
- package/skills/commit-work/SKILL.md +94 -55
- package/skills/commit-work/references/git-state-edges.md +94 -0
- package/skills/commit-work/references/simple-push.md +92 -0
- package/skills/commit-work/references/staging-decisions.md +79 -46
- package/skills/decision-gate/SKILL.md +80 -22
- package/skills/design-for-depth/SKILL.md +208 -43
- package/skills/design-for-depth/references/design-pressure-signals.md +108 -240
- package/skills/design-for-depth/references/interface-design-loop.md +77 -97
- package/skills/design-for-depth/references/module-and-dependency-design.md +147 -0
- package/skills/design-for-depth/references/software-design-philosophy.md +127 -173
- package/skills/design-for-depth/references/state-and-failure-boundaries.md +136 -0
- package/skills/diagnose-failure/SKILL.md +198 -52
- package/skills/diagnose-failure/references/diagnostic-loop-catalog.md +19 -5
- package/skills/diagnose-failure/references/flaky-and-performance-diagnosis.md +13 -5
- package/skills/discuss/SKILL.md +118 -76
- package/skills/discuss/references/discussion-continuity.md +84 -32
- package/skills/evaluate-skill/SKILL.md +166 -52
- package/skills/evaluate-skill/references/evaluation-definition-schema.md +2 -2
- package/skills/evaluate-skill/references/evaluation-design.md +56 -30
- package/skills/evaluate-skill/references/execution-and-evidence.md +1 -1
- package/skills/evaluate-skill/references/review-and-revision.md +62 -21
- package/skills/execute-work/SKILL.md +98 -59
- package/skills/execute-work/references/code-practices.md +4 -4
- package/skills/execute-work/references/execute-work-edges.md +42 -22
- package/skills/review-artifact/SKILL.md +132 -71
- package/skills/review-artifact/references/adjudicate-artifact-review.md +101 -0
- package/skills/review-artifact/references/independent-artifact-reviewer-contract.md +52 -41
- package/skills/review-work/SKILL.md +115 -72
- package/skills/review-work/references/adjudicate-work-review.md +97 -0
- package/skills/review-work/references/independent-work-reviewer-contract.md +40 -35
- package/skills/review-work/references/security-risk-lens.md +17 -15
- package/skills/setup-freeflow/SKILL.md +14 -17
- package/skills/setup-freeflow/references/activation-contract.md +24 -30
- package/skills/setup-freeflow/references/host-setup.md +6 -7
- package/skills/simplify-code/SKILL.md +97 -39
- package/skills/simplify-code/references/simplification-patterns.md +1 -1
- package/skills/tdd/SKILL.md +108 -41
- package/skills/tdd/references/test-design.md +1 -1
- package/skills/track-work/SKILL.md +133 -300
- package/skills/track-work/references/working-record-format.md +515 -0
- package/skills/track-work/scripts/lib/document.mjs +713 -0
- package/skills/track-work/scripts/lib/format.mjs +166 -0
- package/skills/track-work/scripts/lib/store.mjs +233 -0
- package/skills/track-work/scripts/lib/transitions.mjs +790 -0
- package/skills/track-work/scripts/lib/views.mjs +27 -430
- package/skills/track-work/scripts/working-record.mjs +292 -98
- package/skills/verify-work/SKILL.md +46 -20
- package/skills/verify-work/references/browser-runtime-evidence.md +1 -1
- package/skills/verify-work/references/integration-evidence.md +1 -7
- package/skills/verify-work/references/performance-evidence.md +1 -1
- package/skills/workflow/SKILL.md +110 -80
- package/skills/workflow/references/domain-skill-composition.md +24 -18
- package/skills/write-skill/SKILL.md +218 -62
- package/skills/write-skill/references/activation-boundaries.md +63 -21
- package/skills/write-skill/references/agent-first-instructions.md +61 -27
- package/skills/write-skill/references/progressive-disclosure.md +91 -41
- package/skills/write-skill/references/skill-dependency-graphs.md +190 -0
- package/runtime/prompts/skills.md +0 -65
- package/skills/mode-contract/SKILL.md +0 -104
- package/skills/track-work/scripts/lib/codec.mjs +0 -570
- package/skills/track-work/scripts/lib/commands.mjs +0 -568
- package/skills/track-work/scripts/lib/contract.mjs +0 -303
- package/skills/track-work/scripts/lib/model.mjs +0 -506
- package/skills/track-work/scripts/lib/operations.mjs +0 -965
- package/skills/track-work/scripts/lib/record-store.mjs +0 -264
- package/skills/track-work/scripts/lib/result.mjs +0 -37
- package/skills/track-work/scripts/lib/rewrites.mjs +0 -740
- package/skills/track-work/scripts/lib/schema.mjs +0 -277
- package/skills/track-work/scripts/lib/source-inventory.mjs +0 -162
- package/skills/track-work/scripts/lib/workspace.mjs +0 -134
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
|
-
import { cp, mkdir, mkdtemp, rm, symlink, writeFile } from "node:fs/promises";
|
|
2
|
+
import { cp, mkdir, mkdtemp, readFile, rm, symlink, writeFile } from "node:fs/promises";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { pathToFileURL } from "node:url";
|
|
5
5
|
import { tmpdir } from "node:os";
|
|
@@ -53,7 +53,7 @@ function loadExtension(extension = freeflowExtension, host = PIFLOW_HOST) {
|
|
|
53
53
|
return { handlers, activeToolNames: () => activeToolNames ?? tools.map((tool) => tool.name) };
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
async function configuredRepo(config) {
|
|
56
|
+
async function configuredRepo(config = {}) {
|
|
57
57
|
const cwd = await mkdtemp(join(tmpdir(), "freeflow-prompt-architecture-"));
|
|
58
58
|
await mkdir(join(cwd, ".freeflow"));
|
|
59
59
|
await writeFile(join(cwd, ".freeflow/config.json"), JSON.stringify(config, null, 2), "utf8");
|
|
@@ -64,12 +64,29 @@ function lastRuntimeState(messages) {
|
|
|
64
64
|
return messages.findLast((message) => message.customType === "freeflow-runtime-state");
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
test("
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
67
|
+
test("re-entry recovery is stable and capability-neutral", async () => {
|
|
68
|
+
const [core, cognitiveRouting, conversationHistory] = await Promise.all([
|
|
69
|
+
readFile(join(process.cwd(), "runtime", "prompts", "core.md"), "utf8"),
|
|
70
|
+
readFile(join(process.cwd(), "runtime", "prompts", "cognitive-routing.md"), "utf8"),
|
|
71
|
+
readFile(join(process.cwd(), "runtime", "prompts", "conversation-history.md"), "utf8"),
|
|
72
|
+
]);
|
|
73
|
+
|
|
74
|
+
assert.match(core, /## Recover After Context Loss/);
|
|
75
|
+
assert.match(core, /latest Freeflow Runtime State/);
|
|
76
|
+
assert.doesNotMatch(cognitiveRouting, /When Cognitive Routing is active and its skill is absent/);
|
|
77
|
+
assert.match(cognitiveRouting, /A Runtime State refresh is host context, not a user interruption/);
|
|
78
|
+
assert.match(
|
|
79
|
+
cognitiveRouting,
|
|
80
|
+
/`Control` and `Profile` in the latest Runtime State report current compute state, not Yield, Delegate, `ACT_BOUNDED`, or boundary state/,
|
|
81
|
+
);
|
|
82
|
+
assert.doesNotMatch(
|
|
83
|
+
conversationHistory,
|
|
84
|
+
/Current user direction, live source truth, and present runtime state remain authoritative/,
|
|
85
|
+
);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("composes the mandatory core fragments, optional capabilities, discovery, and runtime state", async () => {
|
|
89
|
+
const cwd = await configuredRepo({ contextVirtualization: true, conversationHistory: true });
|
|
73
90
|
try {
|
|
74
91
|
const { handlers, activeToolNames } = loadExtension();
|
|
75
92
|
const ctx = context(cwd);
|
|
@@ -80,45 +97,40 @@ test("composes conditional prompt fragments, discoverable capabilities, and runt
|
|
|
80
97
|
const prompt = before.systemPrompt;
|
|
81
98
|
const order = [
|
|
82
99
|
"# Freeflow Stable Guidance",
|
|
83
|
-
"# Freeflow Interaction Contract",
|
|
84
100
|
"## Shared Terms",
|
|
101
|
+
"## Recover After Context Loss",
|
|
85
102
|
"## Three Nested Loops",
|
|
86
103
|
"## Workflow Cue",
|
|
87
104
|
"## Action Selection Cue",
|
|
88
105
|
"## Supported Exit",
|
|
106
|
+
"# Freeflow Interaction Contract",
|
|
89
107
|
"## Context Virtualization Cue",
|
|
90
108
|
"## Conversation History Cue",
|
|
91
109
|
].map((marker) => prompt.indexOf(marker));
|
|
92
110
|
assert.ok(order.every((index) => index >= 0));
|
|
93
|
-
assert.match(
|
|
94
|
-
prompt,
|
|
95
|
-
/\*\*Slice:\*\* one coherent outcome that can be executed and checked as a unit.*Track Work gives it durable identity/s,
|
|
96
|
-
);
|
|
97
|
-
assert.match(
|
|
98
|
-
prompt,
|
|
99
|
-
/After context loss.*current user direction, bounded task memory, and live evidence.*read Track Work before any record operation.*`resume` view and current schema before mutation.*Workflow is unread.*read it before consequential work or owner selection/s,
|
|
100
|
-
);
|
|
101
111
|
assert.deepEqual(
|
|
102
112
|
order,
|
|
103
113
|
[...order].sort((a, b) => a - b),
|
|
104
114
|
);
|
|
115
|
+
assert.doesNotMatch(prompt, /## Mode\b|strict-workflow|conversation mode|workflow mode/);
|
|
116
|
+
assert.doesNotMatch(prompt, /Skills prompt/);
|
|
105
117
|
assert.doesNotMatch(prompt, /# Workflow\n/);
|
|
106
118
|
assert.doesNotMatch(prompt, /# Cognitive Routing\n/);
|
|
107
|
-
assert.doesNotMatch(prompt, /# Conversation History\n/);
|
|
108
119
|
|
|
109
120
|
const providerContext = await handlers.get("context")({ messages: [] }, ctx);
|
|
110
121
|
const runtimeState = lastRuntimeState(providerContext.messages);
|
|
111
122
|
assert.ok(runtimeState);
|
|
112
|
-
assert.match(runtimeState.content, /
|
|
123
|
+
assert.match(runtimeState.content, /Freeflow: active/);
|
|
113
124
|
assert.match(runtimeState.content, /Context Virtualization: active/);
|
|
114
125
|
assert.match(runtimeState.content, /Conversation History: active/);
|
|
115
126
|
assert.match(runtimeState.content, /Cognitive Routing: inactive/);
|
|
127
|
+
assert.doesNotMatch(runtimeState.content, /Default mode|Active mode|Interaction Contract|Skills/);
|
|
116
128
|
|
|
117
129
|
const resources = await handlers.get("resources_discover")({ cwd }, ctx);
|
|
118
130
|
assert.ok(resources.skillPaths.some((path) => path.endsWith("/skills/action-selection/SKILL.md")));
|
|
119
131
|
assert.ok(resources.skillPaths.some((path) => path.endsWith("/capabilities/context-virtualization/SKILL.md")));
|
|
120
132
|
assert.ok(resources.skillPaths.some((path) => path.endsWith("/capabilities/conversation-history/SKILL.md")));
|
|
121
|
-
assert.ok(!resources.skillPaths.some((path) => path.endsWith("/
|
|
133
|
+
assert.ok(!resources.skillPaths.some((path) => path.endsWith("/skills/mode-contract/SKILL.md")));
|
|
122
134
|
assert.ok(activeToolNames().includes("freeflow_context"));
|
|
123
135
|
} finally {
|
|
124
136
|
await rm(cwd, { recursive: true, force: true });
|
|
@@ -126,7 +138,7 @@ test("composes conditional prompt fragments, discoverable capabilities, and runt
|
|
|
126
138
|
});
|
|
127
139
|
|
|
128
140
|
test("provider context reuses the before-agent surface until the next provider turn", async () => {
|
|
129
|
-
const cwd = await configuredRepo({
|
|
141
|
+
const cwd = await configuredRepo({ contextVirtualization: true });
|
|
130
142
|
try {
|
|
131
143
|
const { handlers } = loadExtension();
|
|
132
144
|
const ctx = context(cwd);
|
|
@@ -135,59 +147,20 @@ test("provider context reuses the before-agent surface until the next provider t
|
|
|
135
147
|
const before = await handlers.get("before_agent_start")({ systemPrompt: "base prompt" }, ctx);
|
|
136
148
|
assert.match(before.systemPrompt, /## Context Virtualization Cue/);
|
|
137
149
|
|
|
138
|
-
await writeFile(
|
|
139
|
-
join(cwd, ".freeflow/config.json"),
|
|
140
|
-
JSON.stringify({ defaultMode: "workflow", skills: { enabled: false }, contextVirtualization: true }),
|
|
141
|
-
"utf8",
|
|
142
|
-
);
|
|
150
|
+
await writeFile(join(cwd, ".freeflow/config.json"), JSON.stringify({}), "utf8");
|
|
143
151
|
const sameTurn = await handlers.get("context")({ messages: [] }, ctx);
|
|
144
|
-
assert.match(lastRuntimeState(sameTurn.messages).content, /Skills: active/);
|
|
145
152
|
assert.match(lastRuntimeState(sameTurn.messages).content, /Context Virtualization: active/);
|
|
146
153
|
|
|
147
154
|
const next = await handlers.get("before_agent_start")({ systemPrompt: "base prompt" }, ctx);
|
|
148
|
-
assert.doesNotMatch(next.systemPrompt, /## Shared Terms/);
|
|
149
155
|
assert.doesNotMatch(next.systemPrompt, /## Context Virtualization Cue/);
|
|
156
|
+
assert.match(next.systemPrompt, /# Freeflow Interaction Contract/);
|
|
157
|
+
assert.match(next.systemPrompt, /## Shared Terms/);
|
|
150
158
|
} finally {
|
|
151
159
|
await rm(cwd, { recursive: true, force: true });
|
|
152
160
|
}
|
|
153
161
|
});
|
|
154
162
|
|
|
155
|
-
test("
|
|
156
|
-
const cwd = await configuredRepo({
|
|
157
|
-
defaultMode: "workflow",
|
|
158
|
-
skills: { enabled: false },
|
|
159
|
-
contextVirtualization: true,
|
|
160
|
-
conversationHistory: true,
|
|
161
|
-
});
|
|
162
|
-
try {
|
|
163
|
-
const { handlers, activeToolNames } = loadExtension();
|
|
164
|
-
const ctx = context(cwd);
|
|
165
|
-
await handlers.get("session_start")({ type: "session_start" }, ctx);
|
|
166
|
-
|
|
167
|
-
const before = await handlers.get("before_agent_start")({ systemPrompt: "base prompt" }, ctx);
|
|
168
|
-
assert.match(before.systemPrompt, /# Freeflow Stable Guidance/);
|
|
169
|
-
assert.match(before.systemPrompt, /## Mode/);
|
|
170
|
-
assert.match(before.systemPrompt, /# Freeflow Interaction Contract/);
|
|
171
|
-
assert.doesNotMatch(before.systemPrompt, /## Shared Terms/);
|
|
172
|
-
assert.doesNotMatch(before.systemPrompt, /## Three Nested Loops/);
|
|
173
|
-
assert.doesNotMatch(before.systemPrompt, /## Context Virtualization Cue/);
|
|
174
|
-
assert.doesNotMatch(before.systemPrompt, /## Conversation History Cue/);
|
|
175
|
-
|
|
176
|
-
const providerContext = await handlers.get("context")({ messages: [] }, ctx);
|
|
177
|
-
const runtimeState = lastRuntimeState(providerContext.messages);
|
|
178
|
-
assert.match(runtimeState.content, /Skills: inactive/);
|
|
179
|
-
assert.match(runtimeState.content, /Context Virtualization: inactive/);
|
|
180
|
-
assert.match(runtimeState.content, /Conversation History: inactive/);
|
|
181
|
-
|
|
182
|
-
const resources = await handlers.get("resources_discover")({ cwd }, ctx);
|
|
183
|
-
assert.deepEqual(resources.skillPaths, []);
|
|
184
|
-
assert.ok(!activeToolNames().includes("freeflow_context"));
|
|
185
|
-
} finally {
|
|
186
|
-
await rm(cwd, { recursive: true, force: true });
|
|
187
|
-
}
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
test("missing optional prompt fragments preserve runtime context loading", async () => {
|
|
163
|
+
test("missing optional prompt fragments preserve the mandatory core surface", async () => {
|
|
191
164
|
const root = await mkdtemp(join(tmpdir(), "freeflow-prompt-fragment-failure-"));
|
|
192
165
|
try {
|
|
193
166
|
await cp(join(process.cwd(), "pi-extension", "dist"), join(root, "pi-extension", "dist"), { recursive: true });
|
|
@@ -200,8 +173,6 @@ test("missing optional prompt fragments preserve runtime context loading", async
|
|
|
200
173
|
const state = {
|
|
201
174
|
configured: true,
|
|
202
175
|
enabled: true,
|
|
203
|
-
interactionContract: { effective: true },
|
|
204
|
-
skills: { effective: true },
|
|
205
176
|
cognitiveRouting: { effective: true },
|
|
206
177
|
contextVirtualization: { effective: false },
|
|
207
178
|
conversationHistory: { effective: false },
|
|
@@ -213,6 +184,10 @@ test("missing optional prompt fragments preserve runtime context loading", async
|
|
|
213
184
|
runtime.runtimeContext(loaded, { ...state, cognitiveRouting: { effective: false } }),
|
|
214
185
|
/# Freeflow Stable Guidance/,
|
|
215
186
|
);
|
|
187
|
+
assert.match(
|
|
188
|
+
runtime.runtimeContext(loaded, { ...state, cognitiveRouting: { effective: false } }),
|
|
189
|
+
/# Freeflow Interaction Contract/,
|
|
190
|
+
);
|
|
216
191
|
assert.doesNotMatch(
|
|
217
192
|
runtime.runtimeContext(loaded, { ...state, cognitiveRouting: { effective: false } }),
|
|
218
193
|
/## Cognitive Routing Cue/,
|
|
@@ -222,9 +197,9 @@ test("missing optional prompt fragments preserve runtime context loading", async
|
|
|
222
197
|
}
|
|
223
198
|
});
|
|
224
199
|
|
|
225
|
-
test("missing child prompt removes that capability from every model-facing surface", async () => {
|
|
200
|
+
test("missing child prompt removes only that capability from every model-facing surface", async () => {
|
|
226
201
|
const root = await mkdtemp(join(tmpdir(), "freeflow-child-prompt-failure-"));
|
|
227
|
-
const cwd = await configuredRepo({
|
|
202
|
+
const cwd = await configuredRepo({ contextVirtualization: true });
|
|
228
203
|
try {
|
|
229
204
|
await cp(join(process.cwd(), "pi-extension", "dist"), join(root, "pi-extension", "dist"), { recursive: true });
|
|
230
205
|
await cp(join(process.cwd(), "runtime", "prompts"), join(root, "runtime", "prompts"), { recursive: true });
|
|
@@ -240,20 +215,76 @@ test("missing child prompt removes that capability from every model-facing surfa
|
|
|
240
215
|
|
|
241
216
|
const before = await handlers.get("before_agent_start")({ systemPrompt: "base prompt" }, ctx);
|
|
242
217
|
assert.doesNotMatch(before.systemPrompt, /## Context Virtualization Cue/);
|
|
218
|
+
assert.match(before.systemPrompt, /# Freeflow Interaction Contract/);
|
|
243
219
|
const resources = await handlers.get("resources_discover")({ cwd }, ctx);
|
|
244
220
|
assert.ok(!resources.skillPaths.some((path) => path.endsWith("/capabilities/context-virtualization/SKILL.md")));
|
|
245
221
|
assert.ok(!activeToolNames().includes("freeflow_context"));
|
|
222
|
+
|
|
223
|
+
await handlers.get("session_tree")({}, ctx);
|
|
224
|
+
assert.ok(!activeToolNames().includes("freeflow_context"));
|
|
225
|
+
await handlers.get("session_compact")({}, ctx);
|
|
226
|
+
assert.ok(!activeToolNames().includes("freeflow_context"));
|
|
227
|
+
|
|
246
228
|
const providerContext = await handlers.get("context")({ messages: [] }, ctx);
|
|
247
229
|
assert.match(providerContext.messages.at(-1).content, /Context Virtualization: unavailable/);
|
|
230
|
+
|
|
231
|
+
await writeFile(join(root, "runtime", "prompts", "context-virtualization.md"), " \n\t", "utf8");
|
|
232
|
+
const whitespaceBefore = await handlers.get("before_agent_start")({ systemPrompt: "base prompt" }, ctx);
|
|
233
|
+
assert.doesNotMatch(whitespaceBefore.systemPrompt, /## Context Virtualization Cue/);
|
|
234
|
+
const whitespaceResources = await handlers.get("resources_discover")({ cwd }, ctx);
|
|
235
|
+
assert.ok(
|
|
236
|
+
!whitespaceResources.skillPaths.some((path) => path.endsWith("/capabilities/context-virtualization/SKILL.md")),
|
|
237
|
+
);
|
|
238
|
+
assert.ok(!activeToolNames().includes("freeflow_context"));
|
|
239
|
+
const whitespaceContext = await handlers.get("context")({ messages: [] }, ctx);
|
|
240
|
+
assert.match(whitespaceContext.messages.at(-1).content, /Context Virtualization: unavailable/);
|
|
248
241
|
} finally {
|
|
249
242
|
await rm(cwd, { recursive: true, force: true });
|
|
250
243
|
await rm(root, { recursive: true, force: true });
|
|
251
244
|
}
|
|
252
245
|
});
|
|
253
246
|
|
|
254
|
-
test("missing
|
|
247
|
+
test("missing mandatory Interaction Contract preserves the host prompt and hides base skills", async () => {
|
|
248
|
+
const root = await mkdtemp(join(tmpdir(), "freeflow-interaction-contract-failure-"));
|
|
249
|
+
const cwd = await configuredRepo();
|
|
250
|
+
try {
|
|
251
|
+
await cp(join(process.cwd(), "pi-extension", "dist"), join(root, "pi-extension", "dist"), { recursive: true });
|
|
252
|
+
await cp(join(process.cwd(), "runtime", "prompts"), join(root, "runtime", "prompts"), { recursive: true });
|
|
253
|
+
await symlink(join(process.cwd(), "node_modules"), join(root, "node_modules"), "dir");
|
|
254
|
+
await rm(join(root, "runtime", "prompts", "interaction-contract.md"));
|
|
255
|
+
|
|
256
|
+
const extension = (
|
|
257
|
+
await import(
|
|
258
|
+
`${pathToFileURL(join(root, "pi-extension", "dist", "index.js")).href}?missing-contract=${Date.now()}`
|
|
259
|
+
)
|
|
260
|
+
).default;
|
|
261
|
+
const { handlers, activeToolNames } = loadExtension(extension);
|
|
262
|
+
const ctx = context(cwd);
|
|
263
|
+
await handlers.get("session_start")({}, ctx);
|
|
264
|
+
assert.ok(!activeToolNames().includes("freeflow_context"));
|
|
265
|
+
const before = await handlers.get("before_agent_start")({ systemPrompt: "base prompt" }, ctx);
|
|
266
|
+
assert.equal(before.systemPrompt, "base prompt");
|
|
267
|
+
const resources = await handlers.get("resources_discover")({ cwd }, ctx);
|
|
268
|
+
assert.deepEqual(resources.skillPaths, []);
|
|
269
|
+
await handlers.get("session_tree")({}, ctx);
|
|
270
|
+
assert.ok(!activeToolNames().includes("freeflow_context"));
|
|
271
|
+
|
|
272
|
+
await writeFile(join(root, "runtime", "prompts", "interaction-contract.md"), " \n\t", "utf8");
|
|
273
|
+
const whitespaceBefore = await handlers.get("before_agent_start")({ systemPrompt: "base prompt" }, ctx);
|
|
274
|
+
assert.equal(whitespaceBefore.systemPrompt, "base prompt");
|
|
275
|
+
const whitespaceResources = await handlers.get("resources_discover")({ cwd }, ctx);
|
|
276
|
+
assert.deepEqual(whitespaceResources.skillPaths, []);
|
|
277
|
+
const whitespaceContext = await handlers.get("context")({ messages: [] }, ctx);
|
|
278
|
+
assert.match(whitespaceContext.messages.at(-1).content, /Freeflow: unavailable/);
|
|
279
|
+
} finally {
|
|
280
|
+
await rm(cwd, { recursive: true, force: true });
|
|
281
|
+
await rm(root, { recursive: true, force: true });
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
test("missing mandatory core prompt preserves the host prompt and hides base skills", async () => {
|
|
255
286
|
const root = await mkdtemp(join(tmpdir(), "freeflow-core-prompt-failure-"));
|
|
256
|
-
const cwd = await configuredRepo({
|
|
287
|
+
const cwd = await configuredRepo({ contextVirtualization: true });
|
|
257
288
|
try {
|
|
258
289
|
await cp(join(process.cwd(), "pi-extension", "dist"), join(root, "pi-extension", "dist"), { recursive: true });
|
|
259
290
|
await cp(join(process.cwd(), "runtime", "prompts"), join(root, "runtime", "prompts"), { recursive: true });
|
|
@@ -263,7 +294,7 @@ test("missing core prompt preserves the host prompt and marks Freeflow unavailab
|
|
|
263
294
|
const extension = (
|
|
264
295
|
await import(`${pathToFileURL(join(root, "pi-extension", "dist", "index.js")).href}?missing-core=${Date.now()}`)
|
|
265
296
|
).default;
|
|
266
|
-
const { handlers } = loadExtension(extension);
|
|
297
|
+
const { handlers, activeToolNames } = loadExtension(extension);
|
|
267
298
|
const ctx = context(cwd);
|
|
268
299
|
await handlers.get("session_start")({ type: "session_start" }, ctx);
|
|
269
300
|
|
|
@@ -272,17 +303,32 @@ test("missing core prompt preserves the host prompt and marks Freeflow unavailab
|
|
|
272
303
|
const resources = await handlers.get("resources_discover")({ cwd }, ctx);
|
|
273
304
|
assert.deepEqual(resources.skillPaths, []);
|
|
274
305
|
const providerContext = await handlers.get("context")({ messages: [] }, ctx);
|
|
275
|
-
assert.match(providerContext.messages.at(-1).content, /
|
|
276
|
-
assert.
|
|
277
|
-
|
|
306
|
+
assert.match(providerContext.messages.at(-1).content, /Freeflow: unavailable/);
|
|
307
|
+
assert.doesNotMatch(
|
|
308
|
+
providerContext.messages.at(-1).content,
|
|
309
|
+
/Default mode|Active mode|Interaction Contract|Skills/,
|
|
310
|
+
);
|
|
311
|
+
|
|
312
|
+
await handlers.get("session_tree")({}, ctx);
|
|
313
|
+
assert.ok(!activeToolNames().includes("freeflow_context"));
|
|
314
|
+
await handlers.get("session_compact")({}, ctx);
|
|
315
|
+
assert.ok(!activeToolNames().includes("freeflow_context"));
|
|
316
|
+
|
|
317
|
+
await writeFile(join(root, "runtime", "prompts", "core.md"), " \n\t", "utf8");
|
|
318
|
+
const whitespaceBefore = await handlers.get("before_agent_start")({ systemPrompt: "base prompt" }, ctx);
|
|
319
|
+
assert.equal(whitespaceBefore.systemPrompt, "base prompt");
|
|
320
|
+
const whitespaceResources = await handlers.get("resources_discover")({ cwd }, ctx);
|
|
321
|
+
assert.deepEqual(whitespaceResources.skillPaths, []);
|
|
322
|
+
const whitespaceContext = await handlers.get("context")({ messages: [] }, ctx);
|
|
323
|
+
assert.match(whitespaceContext.messages.at(-1).content, /Freeflow: unavailable/);
|
|
278
324
|
} finally {
|
|
279
325
|
await rm(cwd, { recursive: true, force: true });
|
|
280
326
|
await rm(root, { recursive: true, force: true });
|
|
281
327
|
}
|
|
282
328
|
});
|
|
283
329
|
|
|
284
|
-
test("Runtime State remains present while Freeflow is disabled
|
|
285
|
-
const cwd = await configuredRepo({ enabled: false
|
|
330
|
+
test("Runtime State remains present while Freeflow is disabled without optional core labels", async () => {
|
|
331
|
+
const cwd = await configuredRepo({ enabled: false });
|
|
286
332
|
try {
|
|
287
333
|
const { handlers } = loadExtension();
|
|
288
334
|
const ctx = context(cwd);
|
|
@@ -293,9 +339,8 @@ test("Runtime State remains present while Freeflow is disabled and prompt surfac
|
|
|
293
339
|
const providerContext = await handlers.get("context")({ messages: [] }, ctx);
|
|
294
340
|
const runtimeState = lastRuntimeState(providerContext.messages);
|
|
295
341
|
assert.ok(runtimeState);
|
|
296
|
-
assert.match(runtimeState.content, /
|
|
297
|
-
assert.
|
|
298
|
-
assert.match(runtimeState.content, /Interaction Contract: inactive/);
|
|
342
|
+
assert.match(runtimeState.content, /Freeflow: inactive/);
|
|
343
|
+
assert.doesNotMatch(runtimeState.content, /Default mode|Active mode|Interaction Contract|Skills/);
|
|
299
344
|
|
|
300
345
|
const resources = await handlers.get("resources_discover")({ cwd }, ctx);
|
|
301
346
|
assert.deepEqual(resources.skillPaths, []);
|