@hassangameryt/freeflow 0.7.3 → 0.8.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 +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.cursor-plugin/plugin.json +1 -1
- package/NOTICE +14 -0
- package/README.md +26 -31
- package/capabilities/cognitive-routing/SKILL.md +106 -161
- package/capabilities/cognitive-routing/references/both-mode.md +36 -0
- package/capabilities/cognitive-routing/references/executor-mode.md +48 -0
- package/capabilities/cognitive-routing/references/helper-mode.md +47 -0
- package/capabilities/compaction/SKILL.md +49 -0
- package/capabilities/compaction/references/summary-format.md +239 -0
- package/command-surface.json +4 -0
- package/gemini-extension.json +1 -1
- package/hooks/shared/runtime-context.mjs +28 -30
- package/licenses/Apache-2.0.txt +55 -0
- package/package.json +22 -9
- package/pi-extension/README.md +41 -0
- package/pi-extension/dist/cognitive-routing/assembler.js +393 -0
- package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/config.js +5 -2
- package/pi-extension/dist/cognitive-routing/economics.js +45 -0
- package/pi-extension/dist/cognitive-routing/event-schema.js +388 -0
- package/pi-extension/dist/{session-sources/events.js → cognitive-routing/event-store.js} +103 -40
- package/pi-extension/dist/cognitive-routing/gate.js +241 -0
- package/pi-extension/dist/cognitive-routing/handlers.js +899 -0
- package/pi-extension/dist/cognitive-routing/handoffs.js +350 -0
- package/pi-extension/dist/cognitive-routing/model-control.js +467 -0
- package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/projection.js +229 -56
- package/pi-extension/dist/cognitive-routing/provenance.js +81 -0
- package/pi-extension/dist/cognitive-routing/runtime.js +396 -0
- package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/schemas.js +43 -7
- package/pi-extension/dist/cognitive-routing/session.js +221 -0
- package/pi-extension/dist/{session-sources → cognitive-routing}/sources.js +99 -85
- package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/state.js +37 -378
- package/pi-extension/dist/cognitive-routing/status.js +80 -0
- package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/tools.js +12 -12
- package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/types.js +5 -11
- package/pi-extension/dist/compaction/carry.js +191 -0
- package/pi-extension/dist/compaction/config.js +22 -0
- package/pi-extension/dist/compaction/controller.js +471 -0
- package/pi-extension/dist/compaction/harness.js +77 -0
- package/pi-extension/dist/compaction/results.js +158 -0
- package/pi-extension/dist/compaction/thresholds.js +35 -0
- package/pi-extension/dist/compaction/tool.js +57 -0
- package/pi-extension/dist/host/branch.js +13 -0
- package/pi-extension/dist/host/canonical.js +12 -0
- package/pi-extension/dist/host/catalog.js +77 -0
- package/pi-extension/dist/host/config.js +374 -0
- package/pi-extension/dist/host/projection-tags.js +60 -0
- package/pi-extension/dist/host/prompts.js +82 -0
- package/pi-extension/dist/{session-sources → host}/read-only-session.js +112 -1
- package/pi-extension/dist/{runtime → host}/request-history.js +121 -39
- package/pi-extension/dist/host/runtime-state.js +189 -0
- package/pi-extension/dist/{settings/settings-ui.js → host/settings/freeflow-command.js} +620 -410
- package/pi-extension/dist/{settings/settings-tui.js → host/settings/settings-view.js} +186 -73
- package/pi-extension/dist/host/staging.js +28 -0
- package/pi-extension/dist/host/status.js +101 -0
- package/pi-extension/dist/index.js +423 -173
- package/pi-extension/dist/provider-support/cache/anchor.js +21 -0
- package/pi-extension/dist/provider-support/cache/anthropic.js +102 -0
- package/pi-extension/dist/provider-support/cache/health.js +73 -0
- package/pi-extension/dist/provider-support/cache/index.js +58 -0
- package/pi-extension/dist/provider-support/cache/keepalive.js +210 -0
- package/pi-extension/dist/provider-support/cache/monitor.js +17 -0
- package/pi-extension/dist/provider-support/cache/openai-responses.js +48 -0
- package/pi-extension/dist/provider-support/effort.js +13 -0
- package/pi-extension/dist/provider-support/index.js +44 -3
- package/pi-extension/dist/provider-support/{astra → openai}/adapter.js +46 -29
- package/pi-extension/dist/provider-support/{astra → openai}/history.js +10 -8
- package/pi-extension/dist/provider-support/{astra → openai}/index.js +8 -4
- package/pi-extension/dist/provider-support/{astra → openai}/session-state.js +29 -38
- package/pi-extension/dist/provider-support/routes.js +17 -0
- package/pi-extension/dist/tool-execution/apply-patch/grammar.js +23 -0
- package/pi-extension/dist/tool-execution/apply-patch/match.js +38 -0
- package/pi-extension/dist/tool-execution/apply-patch/parser.js +177 -0
- package/pi-extension/dist/tool-execution/apply-patch/plan.js +201 -0
- package/pi-extension/dist/tool-execution/apply-patch/tool.js +84 -0
- package/pi-extension/dist/tool-execution/background.js +238 -0
- package/pi-extension/dist/tool-execution/bash-guard.js +23 -0
- package/pi-extension/dist/tool-execution/config.js +22 -0
- package/pi-extension/dist/tool-execution/edit-messages.js +120 -0
- package/pi-extension/dist/tool-execution/file-state.js +138 -0
- package/pi-extension/dist/tool-execution/file-tracking.js +143 -0
- package/pi-extension/dist/tool-execution/prompt.js +54 -0
- package/pi-extension/dist/tool-execution/tools.js +16 -0
- package/pi-extension/freeflow/README.md +9 -0
- package/plugin.json +1 -1
- package/runtime/prompts/cognitive-routing.md +4 -4
- package/runtime/prompts/core.md +22 -75
- package/runtime/prompts/interaction-contract.md +15 -8
- package/runtime/prompts/tool-execution.md +38 -0
- package/runtime/prompts/working-method.md +108 -0
- package/skills/action-selection/SKILL.md +39 -47
- package/skills/action-selection/references/trajectory-stalls.md +2 -0
- package/skills/bypass/SKILL.md +2 -2
- package/skills/commit-work/SKILL.md +31 -83
- package/skills/commit-work/references/git-state-edges.md +1 -1
- package/skills/commit-work/references/simple-push.md +2 -0
- package/skills/commit-work/references/staging-decisions.md +1 -1
- package/skills/decision-gate/SKILL.md +31 -50
- package/skills/design-for-depth/SKILL.md +44 -53
- package/skills/design-for-depth/references/design-pressure-signals.md +46 -144
- package/skills/design-for-depth/references/implementation-decisions.md +1 -1
- package/skills/design-for-depth/references/interface-design-loop.md +5 -36
- package/skills/design-for-depth/references/module-and-dependency-design.md +2 -32
- package/skills/design-for-depth/references/state-and-failure-boundaries.md +2 -13
- package/skills/diagnose-failure/SKILL.md +39 -24
- package/skills/diagnose-failure/references/diagnostic-loop-catalog.md +2 -0
- package/skills/diagnose-failure/references/flaky-and-performance-diagnosis.md +2 -2
- package/skills/discuss/SKILL.md +35 -11
- package/skills/discuss/references/discussion-continuity.md +7 -27
- package/skills/evaluate-skill/SKILL.md +49 -80
- package/skills/evaluate-skill/references/evaluation-definition-schema.md +35 -5
- package/skills/evaluate-skill/references/evaluation-design.md +14 -14
- package/skills/evaluate-skill/references/execution-and-evidence.md +26 -5
- package/skills/evaluate-skill/references/review-and-revision.md +3 -1
- package/skills/evaluate-skill/scripts/lib/definitions.mjs +49 -8
- package/skills/evaluate-skill/scripts/lib/pi.mjs +14 -7
- package/skills/evaluate-skill/scripts/lib/review.mjs +196 -0
- package/skills/evaluate-skill/scripts/lib/runner.mjs +45 -8
- package/skills/evaluate-skill/scripts/lib/sandbox.mjs +2 -0
- package/skills/evaluate-skill/scripts/lib/trials.mjs +103 -0
- package/skills/evaluate-skill/scripts/lib/view.mjs +67 -1
- package/skills/evaluate-skill/scripts/pi-command-tool.mjs +144 -0
- package/skills/evaluate-skill/scripts/pi-guard.mjs +30 -13
- package/skills/evaluate-skill/scripts/skill-eval.mjs +114 -34
- package/skills/execute-work/SKILL.md +41 -9
- package/skills/execute-work/references/code-practices.md +2 -0
- package/skills/execute-work/references/execute-work-edges.md +11 -1
- package/skills/handoff/SKILL.md +1 -1
- package/skills/handoff/references/handoff-templates.md +1 -1
- package/skills/review-artifact/SKILL.md +42 -41
- package/skills/review-artifact/references/adjudicate-artifact-review.md +1 -1
- package/skills/review-artifact/references/independent-artifact-reviewer-contract.md +1 -1
- package/skills/review-work/SKILL.md +38 -32
- package/skills/review-work/references/adjudicate-work-review.md +1 -1
- package/skills/review-work/references/independent-work-reviewer-contract.md +1 -1
- package/skills/review-work/references/security-risk-lens.md +2 -4
- package/skills/setup-freeflow/SKILL.md +3 -1
- package/skills/setup-freeflow/references/activation-contract.md +7 -13
- package/skills/setup-freeflow/references/host-setup.md +3 -3
- package/skills/track-work/SKILL.md +113 -161
- package/skills/track-work/references/working-record-format.md +189 -350
- package/skills/track-work/scripts/lib/document.mjs +62 -6
- package/skills/track-work/scripts/lib/format.mjs +16 -1
- package/skills/track-work/scripts/lib/store.mjs +9 -8
- package/skills/track-work/scripts/lib/transitions.mjs +79 -4
- package/skills/track-work/scripts/working-record.mjs +34 -12
- package/skills/verify-work/SKILL.md +29 -20
- package/skills/verify-work/references/browser-runtime-evidence.md +1 -5
- package/skills/verify-work/references/integration-evidence.md +2 -0
- package/skills/verify-work/references/performance-evidence.md +3 -1
- package/skills/verify-work/references/test-design.md +3 -1
- package/skills/workflow/SKILL.md +65 -21
- package/skills/workflow/references/domain-skill-composition.md +5 -3
- package/skills/write-docs/SKILL.md +83 -0
- package/skills/write-docs/references/documentation-shapes.md +119 -0
- package/skills/write-docs/scripts/docs-check.mjs +139 -0
- package/skills/write-plan/SKILL.md +40 -40
- package/skills/write-plan/references/plan-shapes.md +1 -1
- package/skills/write-skill/SKILL.md +107 -147
- package/skills/write-skill/references/activation-boundaries.md +2 -0
- package/skills/write-skill/references/agent-first-instructions.md +22 -10
- package/skills/write-skill/references/progressive-disclosure.md +4 -2
- package/skills/write-skill/references/skill-dependency-graphs.md +2 -0
- package/skills/write-skill/scripts/lib/similarity.mjs +56 -0
- package/skills/write-skill/scripts/skill-author.mjs +18 -2
- package/skills/write-spec/SKILL.md +34 -41
- package/skills/write-spec/references/artifact-standards.md +1 -1
- package/skills/write-spec/references/decision-records.md +2 -0
- package/skills/write-spec/references/spec-shapes.md +1 -1
- package/capabilities/context-virtualization/SKILL.md +0 -103
- package/capabilities/conversation-history/SKILL.md +0 -53
- package/hooks/tests/check-host-adapters.test.mjs +0 -82
- package/hooks/tests/check-runtime-context-hook.sh +0 -212
- package/pi-extension/dist/cognitive-routing-v2/runtime.js +0 -2181
- package/pi-extension/dist/context-virtualization/commands.js +0 -89
- package/pi-extension/dist/context-virtualization/projector.js +0 -42
- package/pi-extension/dist/context-virtualization/runtime.js +0 -378
- package/pi-extension/dist/context-virtualization/state.js +0 -57
- package/pi-extension/dist/context-virtualization/types.js +0 -10
- package/pi-extension/dist/conversation-history/passages.js +0 -66
- package/pi-extension/dist/conversation-history/ranking.js +0 -105
- package/pi-extension/dist/conversation-history/runtime.js +0 -226
- package/pi-extension/dist/conversation-history/search.js +0 -56
- package/pi-extension/dist/conversation-history/types.js +0 -1
- package/pi-extension/dist/freeflow-context/resolver.js +0 -120
- package/pi-extension/dist/freeflow-context/runtime.js +0 -80
- package/pi-extension/dist/freeflow-context/source-projector.js +0 -95
- package/pi-extension/dist/freeflow-context/tool.js +0 -314
- package/pi-extension/dist/freeflow-context/types.js +0 -12
- package/pi-extension/dist/freeflow-context/visibility.js +0 -28
- package/pi-extension/dist/integrations/mcp-config.js +0 -29
- package/pi-extension/dist/runtime/runtime-context.js +0 -754
- package/pi-extension/dist/runtime/runtime-identity.js +0 -5
- package/pi-extension/dist/session-sources/provenance.js +0 -32
- package/pi-extension/dist/ui/text-component.js +0 -57
- package/pi-extension/src/cognitive-routing-v2/budget.ts +0 -42
- package/pi-extension/src/cognitive-routing-v2/config.ts +0 -173
- package/pi-extension/src/cognitive-routing-v2/projection.ts +0 -375
- package/pi-extension/src/cognitive-routing-v2/render.ts +0 -277
- package/pi-extension/src/cognitive-routing-v2/runtime.ts +0 -2241
- package/pi-extension/src/cognitive-routing-v2/schemas.ts +0 -180
- package/pi-extension/src/cognitive-routing-v2/state.ts +0 -839
- package/pi-extension/src/cognitive-routing-v2/tools.ts +0 -62
- package/pi-extension/src/cognitive-routing-v2/types.ts +0 -254
- package/pi-extension/src/context-virtualization/commands.ts +0 -111
- package/pi-extension/src/context-virtualization/projector.ts +0 -62
- package/pi-extension/src/context-virtualization/runtime.ts +0 -428
- package/pi-extension/src/context-virtualization/state.ts +0 -79
- package/pi-extension/src/context-virtualization/types.ts +0 -92
- package/pi-extension/src/conversation-history/passages.ts +0 -81
- package/pi-extension/src/conversation-history/ranking.ts +0 -155
- package/pi-extension/src/conversation-history/runtime.ts +0 -250
- package/pi-extension/src/conversation-history/search.ts +0 -79
- package/pi-extension/src/conversation-history/types.ts +0 -31
- package/pi-extension/src/freeflow-context/resolver.ts +0 -139
- package/pi-extension/src/freeflow-context/runtime.ts +0 -100
- package/pi-extension/src/freeflow-context/source-projector.ts +0 -106
- package/pi-extension/src/freeflow-context/tool.ts +0 -366
- package/pi-extension/src/freeflow-context/types.ts +0 -70
- package/pi-extension/src/freeflow-context/visibility.ts +0 -53
- package/pi-extension/src/index.ts +0 -390
- package/pi-extension/src/integrations/mcp-config.ts +0 -32
- package/pi-extension/src/provider-support/astra/adapter.ts +0 -108
- package/pi-extension/src/provider-support/astra/history.ts +0 -145
- package/pi-extension/src/provider-support/astra/index.ts +0 -12
- package/pi-extension/src/provider-support/astra/session-state.ts +0 -113
- package/pi-extension/src/provider-support/index.ts +0 -5
- package/pi-extension/src/runtime/request-history.ts +0 -243
- package/pi-extension/src/runtime/runtime-context.ts +0 -860
- package/pi-extension/src/runtime/runtime-identity.ts +0 -8
- package/pi-extension/src/session-sources/events.ts +0 -230
- package/pi-extension/src/session-sources/provenance.ts +0 -39
- package/pi-extension/src/session-sources/read-only-session.ts +0 -170
- package/pi-extension/src/session-sources/sources.ts +0 -334
- package/pi-extension/src/settings/settings-tui.ts +0 -501
- package/pi-extension/src/settings/settings-ui.ts +0 -1652
- package/pi-extension/src/ui/text-component.ts +0 -66
- package/pi-extension/tests/cache-reuse/history.test.js +0 -117
- package/pi-extension/tests/cache-reuse/native.test.js +0 -91
- package/pi-extension/tests/cache-reuse/settings.test.js +0 -211
- package/pi-extension/tests/cognitive-routing-v2/communication.test.js +0 -577
- package/pi-extension/tests/cognitive-routing-v2/compaction-input.test.js +0 -183
- package/pi-extension/tests/cognitive-routing-v2/delegation.test.js +0 -557
- package/pi-extension/tests/cognitive-routing-v2/native.test.js +0 -474
- package/pi-extension/tests/cognitive-routing-v2/performance.test.js +0 -87
- package/pi-extension/tests/cognitive-routing-v2/persistence.test.js +0 -300
- package/pi-extension/tests/cognitive-routing-v2/projection.test.js +0 -241
- package/pi-extension/tests/cognitive-routing-v2/recovery.test.js +0 -1034
- package/pi-extension/tests/cognitive-routing-v2/render.test.js +0 -244
- package/pi-extension/tests/cognitive-routing-v2/runtime.test.js +0 -423
- package/pi-extension/tests/cognitive-routing-v2/stabilization.test.js +0 -521
- package/pi-extension/tests/cognitive-routing-v2/state.test.js +0 -586
- package/pi-extension/tests/context-virtualization/extension.test.js +0 -775
- package/pi-extension/tests/context-virtualization/projector.test.js +0 -110
- package/pi-extension/tests/context-virtualization/runtime.test.js +0 -230
- package/pi-extension/tests/conversation-history/runtime.test.js +0 -98
- package/pi-extension/tests/conversation-history/search.test.js +0 -97
- package/pi-extension/tests/fixtures/pi-host.js +0 -4
- package/pi-extension/tests/fixtures/routing-native.js +0 -191
- package/pi-extension/tests/integration/mode-free-core.test.js +0 -220
- package/pi-extension/tests/integration/pi-extension.test.js +0 -1447
- package/pi-extension/tests/integration/prompt-architecture.test.js +0 -447
- package/pi-extension/tests/integration/unaffected-regressions.test.js +0 -478
- package/pi-extension/tests/provider-support/astra-boundaries.test.js +0 -75
- package/pi-extension/tests/provider-support/astra.test.js +0 -306
- package/pi-extension/tests/provider-support/cli.test.js +0 -92
- package/pi-extension/tests/provider-support/native.test.js +0 -183
- package/pi-extension/tests/settings/settings-tui.test.js +0 -243
- package/pi-extension/tsconfig.json +0 -16
- package/runtime/prompts/context-virtualization.md +0 -3
- package/runtime/prompts/conversation-history.md +0 -3
- package/skills/design-for-depth/references/software-design-philosophy.md +0 -202
- /package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/budget.js +0 -0
- /package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/render.js +0 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { resolveToolPath } from "../tool-execution/file-state.js";
|
|
3
|
+
/**
|
|
4
|
+
* Carried context: what a Freeflow compaction delivers again after the summary. The latest user messages are always
|
|
5
|
+
* carried, word for word; files the agent selects are read from disk at compaction time, never copied from an older
|
|
6
|
+
* read. Everything is copied into one message, so a reopened session needs no Freeflow lookup to rebuild it.
|
|
7
|
+
*/
|
|
8
|
+
/** A planning estimate, as Pi's own compaction uses: about four characters per token. */
|
|
9
|
+
export const estimateTokens = (text) => Math.ceil(text.length / 4);
|
|
10
|
+
export const SUMMARY_LIMIT_TOKENS = 8_000;
|
|
11
|
+
/** The latest user messages are carried outside the budget, up to this many and this size. */
|
|
12
|
+
export const USER_MESSAGE_LIMIT = { count: 3, tokens: 8_000 };
|
|
13
|
+
/**
|
|
14
|
+
* Carry budget: 15% of the context window, never above 40k tokens, and never above a quarter of the warning point.
|
|
15
|
+
* The next cycle starts from the carried context plus the summary and the methods the agent reloads; when Pi's reserve
|
|
16
|
+
* is large, a budget from the window alone refills the cycle to the warning before any work.
|
|
17
|
+
*/
|
|
18
|
+
export function carryBudget(contextWindow, warning) {
|
|
19
|
+
const fromWindow = typeof contextWindow === "number" && contextWindow > 0 ? Math.floor(contextWindow * 0.15) : 40_000;
|
|
20
|
+
const fromWarning = typeof warning === "number" && warning > 0 ? Math.floor(warning / 4) : Infinity;
|
|
21
|
+
return Math.min(fromWindow, 40_000, fromWarning);
|
|
22
|
+
}
|
|
23
|
+
export const isCarryResult = (item) => typeof item?.result === "string";
|
|
24
|
+
/** Read one selected file as it is now. */
|
|
25
|
+
export async function readCarriedFile(item, cwd) {
|
|
26
|
+
let raw;
|
|
27
|
+
try {
|
|
28
|
+
raw = await readFile(resolveToolPath(item.file, cwd), "utf8");
|
|
29
|
+
} catch (error) {
|
|
30
|
+
return { path: item.file, error: error?.code === "ENOENT" ? "file not found" : "file could not be read" };
|
|
31
|
+
}
|
|
32
|
+
if (raw.includes("\u0000")) return { path: item.file, error: "binary file, not carried" };
|
|
33
|
+
if (!item.lines) return { path: item.file, text: raw };
|
|
34
|
+
const [first, last] = item.lines;
|
|
35
|
+
return {
|
|
36
|
+
path: item.file,
|
|
37
|
+
lines: item.lines,
|
|
38
|
+
text: raw
|
|
39
|
+
.split("\n")
|
|
40
|
+
.slice(first - 1, last)
|
|
41
|
+
.join("\n"),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/** The reason a carry selection is unusable, or undefined. */
|
|
45
|
+
export function carryProblem(item) {
|
|
46
|
+
const value = item;
|
|
47
|
+
if (typeof value === "object" && value !== null && typeof value.result === "string") {
|
|
48
|
+
if (value.file !== undefined || value.lines !== undefined)
|
|
49
|
+
return `${value.result}: give a result id or a file, not both`;
|
|
50
|
+
return value.result.trim() ? undefined : "a result id must not be empty";
|
|
51
|
+
}
|
|
52
|
+
if (typeof value !== "object" || value === null || typeof value.file !== "string" || value.file.length === 0)
|
|
53
|
+
return "each carry item needs a file path or a result id";
|
|
54
|
+
if (value.lines === undefined) return undefined;
|
|
55
|
+
const lines = value.lines;
|
|
56
|
+
if (
|
|
57
|
+
!Array.isArray(lines) ||
|
|
58
|
+
lines.length !== 2 ||
|
|
59
|
+
!lines.every((n) => Number.isSafeInteger(n) && n >= 1) ||
|
|
60
|
+
lines[0] > lines[1]
|
|
61
|
+
)
|
|
62
|
+
return `${value.file}: lines must be [first, last], 1-based, first <= last`;
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
const text = (content) =>
|
|
66
|
+
typeof content === "string"
|
|
67
|
+
? content
|
|
68
|
+
: Array.isArray(content)
|
|
69
|
+
? content.map((part) => (part?.type === "text" ? part.text : "")).join("")
|
|
70
|
+
: "";
|
|
71
|
+
/**
|
|
72
|
+
* The latest user messages on the branch, oldest first, within the carried limit. Messages `skip` matches are left
|
|
73
|
+
* out: Freeflow's own /freeflow compact request is sent as the user's message, and the compaction completes it.
|
|
74
|
+
*/
|
|
75
|
+
export function latestUserMessages(branch, skip = () => false) {
|
|
76
|
+
const messages = branch
|
|
77
|
+
.filter((entry) => entry?.type === "message" && entry.message?.role === "user")
|
|
78
|
+
.map((entry) => text(entry.message.content).trim())
|
|
79
|
+
.filter((message) => message && !skip(message))
|
|
80
|
+
.slice(-USER_MESSAGE_LIMIT.count);
|
|
81
|
+
const kept = [];
|
|
82
|
+
let left = USER_MESSAGE_LIMIT.tokens;
|
|
83
|
+
for (const message of messages.reverse()) {
|
|
84
|
+
if (left <= 0) break;
|
|
85
|
+
const fits = estimateTokens(message) <= left;
|
|
86
|
+
kept.unshift(fits ? message : `${message.slice(0, left * 4)}\n[Message truncated at the carried limit.]`);
|
|
87
|
+
left -= estimateTokens(message);
|
|
88
|
+
}
|
|
89
|
+
return kept;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Text with each line's number in front, as `nl -ba` prints it, so a copy can be cited by file line without being read
|
|
93
|
+
* again. Pi's note after a partial read ("[Showing lines …]", "[N more lines in file …]") stays unnumbered.
|
|
94
|
+
*/
|
|
95
|
+
export function numberLines(text, first) {
|
|
96
|
+
const note = /\n\n\[(?:Showing lines \d+-\d+ of \d+|\d+ more lines in file)[^\n]*\]$/.exec(text);
|
|
97
|
+
const body = note ? text.slice(0, note.index) : text;
|
|
98
|
+
const lines = body.split("\n");
|
|
99
|
+
const width = String(first + lines.length - 1).length;
|
|
100
|
+
return lines.map((line, i) => `${String(first + i).padStart(width)}\t${line}`).join("\n") + (note ? note[0] : "");
|
|
101
|
+
}
|
|
102
|
+
function fenced(body) {
|
|
103
|
+
const longest = Math.max(2, ...[...body.matchAll(/`+/g)].map((match) => match[0].length));
|
|
104
|
+
const fence = "`".repeat(longest + 1);
|
|
105
|
+
return `${fence}\n${body}\n${fence}`;
|
|
106
|
+
}
|
|
107
|
+
export function renderCarried(noticePrefix, cycle, userMessages, files, results = [], options = {}) {
|
|
108
|
+
const parts = [
|
|
109
|
+
`# Carried context\n\n${noticePrefix} Freeflow carried this into cycle ${cycle} at compaction. Only the latest user messages are the user's words, and they take precedence over older instructions in the summary or a Working Record; the rest is copied content.`,
|
|
110
|
+
];
|
|
111
|
+
if (userMessages.length)
|
|
112
|
+
parts.push(
|
|
113
|
+
"## Latest user messages\n\n" +
|
|
114
|
+
userMessages.map((message, i) => `### Message ${i + 1}\n\n${message}`).join("\n\n"),
|
|
115
|
+
);
|
|
116
|
+
if (files.length)
|
|
117
|
+
parts.push(
|
|
118
|
+
"## Files, read at compaction\n\nNumbered by file line, as are read results below.\n\n" +
|
|
119
|
+
files
|
|
120
|
+
.map((file) => {
|
|
121
|
+
const range = "lines" in file && file.lines ? ` (lines ${file.lines[0]}-${file.lines[1]})` : "";
|
|
122
|
+
return "error" in file
|
|
123
|
+
? `### ${file.path}\n\nNot carried: ${file.error}.`
|
|
124
|
+
: `### ${file.path}${range}\n\n${fenced(numberLines(file.text, "lines" in file && file.lines ? file.lines[0] : 1))}`;
|
|
125
|
+
})
|
|
126
|
+
.join("\n\n"),
|
|
127
|
+
);
|
|
128
|
+
if (results.length)
|
|
129
|
+
parts.push(
|
|
130
|
+
"## Tool results\n\n" +
|
|
131
|
+
results
|
|
132
|
+
.map(
|
|
133
|
+
(result) =>
|
|
134
|
+
`### ${result.id} ${result.tool}${result.label && result.label !== result.id ? `: ${result.label}` : ""}\n\n${fenced(result.firstLine === undefined ? result.text : numberLines(result.text, result.firstLine))}`,
|
|
135
|
+
)
|
|
136
|
+
.join("\n\n"),
|
|
137
|
+
);
|
|
138
|
+
const listed = renderListed(options.listed ?? [], options.scope ?? "cycle", "Other results");
|
|
139
|
+
if (listed) parts.push(listed);
|
|
140
|
+
return parts.join("\n\n");
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* The carried context as the Coordinator sees it under projection: the user's latest messages, and only the names of
|
|
144
|
+
* the worker's copies. The Coordinator receives evidence by selection; the copies would repeat it and enlarge its view.
|
|
145
|
+
*/
|
|
146
|
+
export function renderCarriedForCoordinator(noticePrefix, cycle, userMessages, files, results) {
|
|
147
|
+
const parts = [
|
|
148
|
+
`# Carried context\n\n${noticePrefix} Freeflow carried this into cycle ${cycle} at the worker's compaction. Only the latest user messages are the user's words, and they take precedence over older instructions in the summary or a Working Record.`,
|
|
149
|
+
];
|
|
150
|
+
if (userMessages.length)
|
|
151
|
+
parts.push(
|
|
152
|
+
"## Latest user messages\n\n" +
|
|
153
|
+
userMessages.map((message, i) => `### Message ${i + 1}\n\n${message}`).join("\n\n"),
|
|
154
|
+
);
|
|
155
|
+
const names = [
|
|
156
|
+
...files
|
|
157
|
+
.filter((file) => "text" in file)
|
|
158
|
+
.map(
|
|
159
|
+
(file) => `- ${file.path}${"lines" in file && file.lines ? ` (lines ${file.lines[0]}-${file.lines[1]})` : ""}`,
|
|
160
|
+
),
|
|
161
|
+
...results.map(
|
|
162
|
+
(result) =>
|
|
163
|
+
`- ${result.id} ${result.tool}${result.label && result.label !== result.id ? `: ${result.label}` : ""}`,
|
|
164
|
+
),
|
|
165
|
+
];
|
|
166
|
+
if (names.length)
|
|
167
|
+
parts.push(
|
|
168
|
+
"## Carried for the worker\n\nThe worker's copies are left out of this view; evidence it selects reaches you as usual.\n\n" +
|
|
169
|
+
names.join("\n"),
|
|
170
|
+
);
|
|
171
|
+
return parts.join("\n\n");
|
|
172
|
+
}
|
|
173
|
+
/** Results of the work in progress named by ref, so the agent knows they exist without their content in context. */
|
|
174
|
+
export function renderListed(listed, scope, heading, intro) {
|
|
175
|
+
if (!listed.length) return "";
|
|
176
|
+
const assignment = scope === "assignment";
|
|
177
|
+
return (
|
|
178
|
+
`## ${heading} of this ${assignment ? "assignment" : "cycle"}\n\n` +
|
|
179
|
+
(intro ??
|
|
180
|
+
(assignment
|
|
181
|
+
? "Not carried. They are still stored: select one as evidence by its ref, carry it at a later compaction, or read it again if you need its content."
|
|
182
|
+
: "Not carried. Carry one by its id at a later compaction, or read it again if you need its content.")) +
|
|
183
|
+
"\n\n" +
|
|
184
|
+
listed
|
|
185
|
+
.map(
|
|
186
|
+
(result) =>
|
|
187
|
+
`- ${result.id} ${result.tool} ${result.label} ~${result.tokens} tokens${result.selected ? " (selected as evidence)" : ""}`,
|
|
188
|
+
)
|
|
189
|
+
.join("\n")
|
|
190
|
+
);
|
|
191
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** Freeflow compaction is on whenever Freeflow is, unless a config turns it off. */
|
|
2
|
+
export const DEFAULT_COMPACTION_ENABLED = true;
|
|
3
|
+
export const DEFAULT_COMPACTION_CARRY = true;
|
|
4
|
+
function record(value) {
|
|
5
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6
|
+
}
|
|
7
|
+
export function validateCompactionConfig(value) {
|
|
8
|
+
if (!record(value)) return "compaction must be an object";
|
|
9
|
+
const unknown = Object.keys(value).find((key) => key !== "enabled" && key !== "carry");
|
|
10
|
+
if (unknown) return `compaction has unsupported key: ${unknown}`;
|
|
11
|
+
for (const key of ["enabled", "carry"])
|
|
12
|
+
if (value[key] !== undefined && typeof value[key] !== "boolean") return `compaction.${key} must be a boolean`;
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
export function resolveCompactionConfig(repository, local, freeflowEnabled) {
|
|
16
|
+
const repositoryConfig = record(repository.compaction) ? repository.compaction : {};
|
|
17
|
+
const localConfig = record(local.compaction) ? local.compaction : {};
|
|
18
|
+
const layered = (key) => (Object.hasOwn(localConfig, key) ? localConfig[key] : repositoryConfig[key]);
|
|
19
|
+
const enabled = layered("enabled") === undefined ? DEFAULT_COMPACTION_ENABLED : layered("enabled") === true;
|
|
20
|
+
const carry = layered("carry") === undefined ? DEFAULT_COMPACTION_CARRY : layered("carry") === true;
|
|
21
|
+
return { enabled, effective: freeflowEnabled && enabled, carry };
|
|
22
|
+
}
|