@hassangameryt/freeflow 0.7.2 → 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 +151 -136
- package/capabilities/cognitive-routing/SKILL.md +110 -96
- 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 +40 -21
- 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/cognitive-routing/event-store.js +280 -0
- 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/projection.js +503 -0
- package/pi-extension/dist/cognitive-routing/provenance.js +81 -0
- package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/render.js +31 -10
- package/pi-extension/dist/cognitive-routing/runtime.js +396 -0
- package/pi-extension/dist/cognitive-routing/schemas.js +209 -0
- package/pi-extension/dist/cognitive-routing/session.js +221 -0
- package/pi-extension/dist/cognitive-routing/sources.js +291 -0
- package/pi-extension/dist/cognitive-routing/state.js +487 -0
- package/pi-extension/dist/cognitive-routing/status.js +80 -0
- package/pi-extension/dist/cognitive-routing/tools.js +60 -0
- package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/types.js +10 -12
- 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/host/request-history.js +298 -0
- package/pi-extension/dist/host/runtime-state.js +189 -0
- package/pi-extension/dist/host/settings/freeflow-command.js +1570 -0
- 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 +441 -170
- 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 +45 -0
- package/pi-extension/dist/provider-support/openai/adapter.js +127 -0
- package/pi-extension/dist/provider-support/openai/history.js +127 -0
- package/pi-extension/dist/provider-support/openai/index.js +15 -0
- package/pi-extension/dist/provider-support/openai/session-state.js +105 -0
- 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 +4 -4
- 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 +37 -13
- 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/projection.js +0 -321
- package/pi-extension/dist/cognitive-routing-v2/runtime.js +0 -1585
- package/pi-extension/dist/cognitive-routing-v2/schemas.js +0 -107
- package/pi-extension/dist/cognitive-routing-v2/state.js +0 -612
- package/pi-extension/dist/cognitive-routing-v2/tools.js +0 -91
- 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 -356
- 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 -313
- 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 -735
- package/pi-extension/dist/runtime/runtime-identity.js +0 -5
- package/pi-extension/dist/session-sources/events.js +0 -195
- package/pi-extension/dist/session-sources/sources.js +0 -233
- package/pi-extension/dist/settings/settings-ui.js +0 -1160
- 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 -143
- package/pi-extension/src/cognitive-routing-v2/projection.ts +0 -365
- package/pi-extension/src/cognitive-routing-v2/render.ts +0 -256
- package/pi-extension/src/cognitive-routing-v2/runtime.ts +0 -1634
- package/pi-extension/src/cognitive-routing-v2/schemas.ts +0 -109
- package/pi-extension/src/cognitive-routing-v2/state.ts +0 -622
- package/pi-extension/src/cognitive-routing-v2/tools.ts +0 -93
- package/pi-extension/src/cognitive-routing-v2/types.ts +0 -222
- 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 -406
- 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 -365
- 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 -368
- package/pi-extension/src/integrations/mcp-config.ts +0 -32
- package/pi-extension/src/runtime/runtime-context.ts +0 -838
- package/pi-extension/src/runtime/runtime-identity.ts +0 -8
- package/pi-extension/src/session-sources/events.ts +0 -208
- package/pi-extension/src/session-sources/read-only-session.ts +0 -170
- package/pi-extension/src/session-sources/sources.ts +0 -263
- package/pi-extension/src/settings/settings-tui.ts +0 -501
- package/pi-extension/src/settings/settings-ui.ts +0 -1422
- package/pi-extension/src/ui/text-component.ts +0 -66
- package/pi-extension/tests/cognitive-routing-v2/communication.test.js +0 -484
- package/pi-extension/tests/cognitive-routing-v2/compaction-input.test.js +0 -183
- package/pi-extension/tests/cognitive-routing-v2/native.test.js +0 -441
- package/pi-extension/tests/cognitive-routing-v2/performance.test.js +0 -87
- package/pi-extension/tests/cognitive-routing-v2/persistence.test.js +0 -245
- package/pi-extension/tests/cognitive-routing-v2/projection.test.js +0 -241
- package/pi-extension/tests/cognitive-routing-v2/recovery.test.js +0 -236
- package/pi-extension/tests/cognitive-routing-v2/render.test.js +0 -230
- package/pi-extension/tests/cognitive-routing-v2/runtime.test.js +0 -153
- package/pi-extension/tests/cognitive-routing-v2/stabilization.test.js +0 -521
- package/pi-extension/tests/cognitive-routing-v2/state.test.js +0 -275
- package/pi-extension/tests/context-virtualization/extension.test.js +0 -765
- package/pi-extension/tests/context-virtualization/projector.test.js +0 -110
- package/pi-extension/tests/context-virtualization/runtime.test.js +0 -216
- 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 -188
- package/pi-extension/tests/integration/mode-free-core.test.js +0 -219
- package/pi-extension/tests/integration/pi-extension.test.js +0 -968
- package/pi-extension/tests/integration/prompt-architecture.test.js +0 -431
- package/pi-extension/tests/integration/unaffected-regressions.test.js +0 -478
- 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
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { isObject } from "./types.js";
|
|
2
|
+
const string = (maxLength = 32768, description) => ({
|
|
3
|
+
type: "string",
|
|
4
|
+
minLength: 1,
|
|
5
|
+
maxLength,
|
|
6
|
+
...(description ? { description } : {}),
|
|
7
|
+
});
|
|
8
|
+
const object = (properties, required = Object.keys(properties), description, title) => ({
|
|
9
|
+
type: "object",
|
|
10
|
+
additionalProperties: false,
|
|
11
|
+
properties,
|
|
12
|
+
required,
|
|
13
|
+
...(description ? { description } : {}),
|
|
14
|
+
...(title ? { title } : {}),
|
|
15
|
+
});
|
|
16
|
+
const operation = (value, description) => ({
|
|
17
|
+
type: "string",
|
|
18
|
+
enum: [value],
|
|
19
|
+
...(description ? { description } : {}),
|
|
20
|
+
});
|
|
21
|
+
const evidenceRefs = (description) => ({
|
|
22
|
+
type: "array",
|
|
23
|
+
maxItems: 64,
|
|
24
|
+
items: string(512),
|
|
25
|
+
description,
|
|
26
|
+
});
|
|
27
|
+
const worker = {
|
|
28
|
+
type: "string",
|
|
29
|
+
enum: ["helper", "executor"],
|
|
30
|
+
description: "Worker for this assignment. Required when delegation mode enables both workers.",
|
|
31
|
+
};
|
|
32
|
+
const report = (name) =>
|
|
33
|
+
object(
|
|
34
|
+
{
|
|
35
|
+
operation: operation(name),
|
|
36
|
+
report: string(),
|
|
37
|
+
outcome: { type: "string", enum: ["completed", "partial", "blocked"] },
|
|
38
|
+
limitations: { type: "array", maxItems: 32, items: string(2048) },
|
|
39
|
+
},
|
|
40
|
+
["operation", "report", "outcome"],
|
|
41
|
+
);
|
|
42
|
+
export const ROUTING_SCHEMAS = {
|
|
43
|
+
freeflow_delegate: {
|
|
44
|
+
oneOf: [
|
|
45
|
+
object({ operation: operation("assign"), contract: string(), worker }, ["operation", "contract"]),
|
|
46
|
+
object({ operation: operation("replace"), contract: string(), reason: string(2048), worker }, [
|
|
47
|
+
"operation",
|
|
48
|
+
"contract",
|
|
49
|
+
"reason",
|
|
50
|
+
]),
|
|
51
|
+
],
|
|
52
|
+
},
|
|
53
|
+
freeflow_return: { oneOf: [report("submit"), report("supplement"), object({ operation: operation("retry") })] },
|
|
54
|
+
freeflow_unit: {
|
|
55
|
+
oneOf: [
|
|
56
|
+
object(
|
|
57
|
+
{
|
|
58
|
+
operation: operation("inspect"),
|
|
59
|
+
view: { type: "string", enum: ["current", "history", "detail"] },
|
|
60
|
+
ref: string(128),
|
|
61
|
+
cursor: string(128),
|
|
62
|
+
limit: { type: "integer", minimum: 1, maximum: 100 },
|
|
63
|
+
},
|
|
64
|
+
["operation"],
|
|
65
|
+
),
|
|
66
|
+
object({ operation: operation("assess") }),
|
|
67
|
+
object(
|
|
68
|
+
{
|
|
69
|
+
operation: operation("recover"),
|
|
70
|
+
request: string(),
|
|
71
|
+
paths: { type: "array", maxItems: 32, items: string(4096) },
|
|
72
|
+
results: { type: "array", maxItems: 32, uniqueItems: true, items: string(256) },
|
|
73
|
+
},
|
|
74
|
+
["operation", "request"],
|
|
75
|
+
),
|
|
76
|
+
object({ operation: operation("cancel-recovery"), reason: string(2048) }),
|
|
77
|
+
object({
|
|
78
|
+
operation: operation("close"),
|
|
79
|
+
outcome: { type: "string", enum: ["accepted", "cancelled", "deferred"] },
|
|
80
|
+
assessment: string(),
|
|
81
|
+
}),
|
|
82
|
+
],
|
|
83
|
+
},
|
|
84
|
+
freeflow_project: {
|
|
85
|
+
oneOf: [
|
|
86
|
+
object(
|
|
87
|
+
{
|
|
88
|
+
operation: operation("inspect", "Read current selection and offered evidence candidates."),
|
|
89
|
+
scope: {
|
|
90
|
+
type: "string",
|
|
91
|
+
enum: ["selected", "assignment", "active", "history"],
|
|
92
|
+
description: "Optional candidate scope; omit to inspect the current assignment.",
|
|
93
|
+
},
|
|
94
|
+
cursor: string(128, "Use the nextCursor returned by a previous inspection."),
|
|
95
|
+
},
|
|
96
|
+
["operation"],
|
|
97
|
+
"Inspect selection and candidates without changing evidence state.",
|
|
98
|
+
"Inspect evidence selection",
|
|
99
|
+
),
|
|
100
|
+
object(
|
|
101
|
+
{
|
|
102
|
+
operation: operation("add", "Select eligible worker task-evidence refs; do not include reason."),
|
|
103
|
+
refs: evidenceRefs(
|
|
104
|
+
"Exact eligible visible refs for worker task evidence. Add known eligible refs directly; inspect when identity, eligibility, representation, or selection state is unclear. Never add a ref marked not offered for new evidence selection.",
|
|
105
|
+
),
|
|
106
|
+
},
|
|
107
|
+
["operation", "refs"],
|
|
108
|
+
"Add evidence refs. This shape has no reason field.",
|
|
109
|
+
"Add evidence",
|
|
110
|
+
),
|
|
111
|
+
object(
|
|
112
|
+
{
|
|
113
|
+
operation: operation("remove", "Withdraw selected or unresolved refs; reason is required."),
|
|
114
|
+
refs: evidenceRefs(
|
|
115
|
+
"Currently selected or unresolved refs to withdraw. A non-selectable ref may be removed with a reason to clear its unresolved request.",
|
|
116
|
+
),
|
|
117
|
+
reason: string(2048, "Required only for remove; explain why the selection is being withdrawn."),
|
|
118
|
+
},
|
|
119
|
+
["operation", "refs", "reason"],
|
|
120
|
+
"Remove evidence refs. This is the only operation that accepts reason.",
|
|
121
|
+
"Remove evidence",
|
|
122
|
+
),
|
|
123
|
+
],
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
// Provider function schemas require an object root. Keep operation-specific branches
|
|
127
|
+
// for runtime validation without leaving the provider-facing root untyped.
|
|
128
|
+
for (const schema of Object.values(ROUTING_SCHEMAS)) {
|
|
129
|
+
schema.type = "object";
|
|
130
|
+
schema.additionalProperties = false;
|
|
131
|
+
schema.required = ["operation"];
|
|
132
|
+
schema.properties = Object.assign({}, ...schema.oneOf.map((branch) => branch.properties));
|
|
133
|
+
schema.properties.operation = {
|
|
134
|
+
type: "string",
|
|
135
|
+
enum: schema.oneOf.flatMap((branch) => branch.properties.operation.enum),
|
|
136
|
+
description: "Choose exactly one operation; each operation has a strict shape and rejects unknown fields.",
|
|
137
|
+
};
|
|
138
|
+
if (schema.properties.refs)
|
|
139
|
+
schema.properties.refs = {
|
|
140
|
+
...schema.properties.refs,
|
|
141
|
+
description:
|
|
142
|
+
"For add, use exact eligible visible refs directly when their identity and eligibility are clear; inspect when identity, eligibility, representation, or selection state is unclear. Never add refs marked not offered for new evidence selection. For remove, use currently selected or unresolved refs; a non-selectable ref may be named to clear its unresolved request and requires reason.",
|
|
143
|
+
};
|
|
144
|
+
// The provider sees one flattened root, so each field names the operations whose strict shape accepts it.
|
|
145
|
+
const operations = schema.oneOf.map((branch) => branch.properties.operation.enum[0]);
|
|
146
|
+
for (const [field, property] of Object.entries(schema.properties)) {
|
|
147
|
+
if (field === "operation") continue;
|
|
148
|
+
const accepting = operations.filter((_op, i) => Object.hasOwn(schema.oneOf[i].properties, field));
|
|
149
|
+
if (accepting.length === operations.length) continue;
|
|
150
|
+
const scope = `Only for operation: ${accepting.join(", ")}.`;
|
|
151
|
+
schema.properties[field] = {
|
|
152
|
+
...property,
|
|
153
|
+
description: property.description ? `${property.description} ${scope}` : scope,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
const expected = (schema) =>
|
|
158
|
+
schema.enum
|
|
159
|
+
? `one of ${schema.enum.join(", ")}`
|
|
160
|
+
: schema.type === "string"
|
|
161
|
+
? `a non-empty string of at most ${schema.maxLength} characters`
|
|
162
|
+
: schema.type === "array"
|
|
163
|
+
? `an array of at most ${schema.maxItems} items`
|
|
164
|
+
: schema.type === "integer"
|
|
165
|
+
? `an integer from ${schema.minimum} to ${schema.maximum}`
|
|
166
|
+
: `type ${schema.type}`;
|
|
167
|
+
/** Explain why routing arguments match no strict operation shape, naming what to change. */
|
|
168
|
+
export function explainRoutingArguments(name, input) {
|
|
169
|
+
const schema = ROUTING_SCHEMAS[name];
|
|
170
|
+
const operations = schema.oneOf.map((branch) => branch.properties.operation.enum[0]);
|
|
171
|
+
const operation = isObject(input) ? input.operation : undefined;
|
|
172
|
+
const branch = schema.oneOf.find((b) => b.properties.operation.enum[0] === operation);
|
|
173
|
+
if (!isObject(input) || !branch)
|
|
174
|
+
return `Invalid ${name} arguments: operation must be one of ${operations.join(", ")}. No operation was accepted.`;
|
|
175
|
+
const allowed = Object.keys(branch.properties);
|
|
176
|
+
const problems = [];
|
|
177
|
+
const unknown = Object.keys(input).filter((field) => !allowed.includes(field));
|
|
178
|
+
if (unknown.length) problems.push(`remove field${unknown.length > 1 ? "s" : ""} ${unknown.join(", ")}`);
|
|
179
|
+
const missing = branch.required.filter((field) => !Object.hasOwn(input, field));
|
|
180
|
+
if (missing.length) problems.push(`missing required field${missing.length > 1 ? "s" : ""} ${missing.join(", ")}`);
|
|
181
|
+
for (const field of allowed)
|
|
182
|
+
if (Object.hasOwn(input, field) && !matches(input[field], branch.properties[field]))
|
|
183
|
+
problems.push(`${field} must be ${expected(branch.properties[field])}`);
|
|
184
|
+
if (!problems.length) problems.push("arguments do not match this operation's shape");
|
|
185
|
+
return `Invalid ${name} arguments for operation ${operation}: ${problems.join("; ")}. Allowed fields: ${allowed.join(", ")}. No operation was accepted.`;
|
|
186
|
+
}
|
|
187
|
+
export function matches(value, schema) {
|
|
188
|
+
if (schema.oneOf) return schema.oneOf.filter((branch) => matches(value, branch)).length === 1;
|
|
189
|
+
if (schema.enum && !schema.enum.includes(value)) return false;
|
|
190
|
+
if (schema.type === "object")
|
|
191
|
+
return (
|
|
192
|
+
isObject(value) &&
|
|
193
|
+
Object.keys(value).every((k) => schema.properties[k]) &&
|
|
194
|
+
schema.required.every((k) => Object.hasOwn(value, k)) &&
|
|
195
|
+
Object.entries(value).every(([k, v]) => matches(v, schema.properties[k]))
|
|
196
|
+
);
|
|
197
|
+
if (schema.type === "string")
|
|
198
|
+
return (
|
|
199
|
+
typeof value === "string" &&
|
|
200
|
+
value.length >= (schema.minLength ?? 0) &&
|
|
201
|
+
value.length <= (schema.maxLength ?? Infinity) &&
|
|
202
|
+
(!schema.pattern || new RegExp(schema.pattern).test(value))
|
|
203
|
+
);
|
|
204
|
+
if (schema.type === "array")
|
|
205
|
+
return Array.isArray(value) && value.length <= schema.maxItems && value.every((v) => matches(v, schema.items));
|
|
206
|
+
if (schema.type === "integer")
|
|
207
|
+
return Number.isSafeInteger(value) && value >= schema.minimum && value <= schema.maximum;
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { Sources } from "./sources.js";
|
|
3
|
+
import { ViewCache } from "./projection.js";
|
|
4
|
+
import { pairFromProfile } from "./config.js";
|
|
5
|
+
import { initialState } from "./state.js";
|
|
6
|
+
import { RoutingError, isWorkerProfile, requireCondition as check, samePair, workersForDelegation } from "./types.js";
|
|
7
|
+
import { cachedBranch } from "../host/branch.js";
|
|
8
|
+
const STAGED_CONTROL = new Set(["control", "profile-overrides", "delegation-override"]);
|
|
9
|
+
export const ROUTING_RECOVERY_HINT =
|
|
10
|
+
"Run /freeflow profile auto to reconcile routing, or /freeflow resume to continue saved work.";
|
|
11
|
+
/**
|
|
12
|
+
* State and identity shared by every part of routing for one bound session: the event journal, the host
|
|
13
|
+
* context, the current turn, blocking errors, control mode, and the queue that orders control changes.
|
|
14
|
+
*/
|
|
15
|
+
export class RoutingSession {
|
|
16
|
+
store;
|
|
17
|
+
ctx;
|
|
18
|
+
capability;
|
|
19
|
+
token = randomUUID();
|
|
20
|
+
turn;
|
|
21
|
+
messages = [];
|
|
22
|
+
error;
|
|
23
|
+
projectionError;
|
|
24
|
+
suppressed = false;
|
|
25
|
+
operations = Promise.resolve();
|
|
26
|
+
revision = 0;
|
|
27
|
+
manualHold;
|
|
28
|
+
automaticControl = false;
|
|
29
|
+
sourceCache;
|
|
30
|
+
/** Rendered view messages reused across requests (see ViewCache); rebuilt after a rebind or ancestry change. */
|
|
31
|
+
viewCache = new ViewCache();
|
|
32
|
+
/** Replayed tool results for the current turn, keyed by operation id. */
|
|
33
|
+
receipts = new Map();
|
|
34
|
+
/** Recently served inspection pages, keyed by request; at most eight are kept. */
|
|
35
|
+
pages = new Map();
|
|
36
|
+
get projectionEnabled() {
|
|
37
|
+
return this.supported() && this.capability?.projection === true && this.stateData().control === "automatic";
|
|
38
|
+
}
|
|
39
|
+
/** Forgets everything derived for the current turn: its context view, sources, replay receipts, and pages. */
|
|
40
|
+
resetTurn() {
|
|
41
|
+
this.turn = undefined;
|
|
42
|
+
this.messages = [];
|
|
43
|
+
this.sourceCache = undefined;
|
|
44
|
+
this.viewCache = new ViewCache();
|
|
45
|
+
this.receipts.clear();
|
|
46
|
+
this.pages.clear();
|
|
47
|
+
}
|
|
48
|
+
subject() {
|
|
49
|
+
const branch = cachedBranch(this.store?.reader) ?? [];
|
|
50
|
+
return {
|
|
51
|
+
token: this.token,
|
|
52
|
+
revision: this.revision,
|
|
53
|
+
store: this.store,
|
|
54
|
+
leafId: branch.at(-1)?.id ?? null,
|
|
55
|
+
userId: branch.filter((e) => e.message?.role === "user").at(-1)?.id ?? null,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
current(s) {
|
|
59
|
+
if (s.token !== this.token || s.revision !== this.revision || s.store !== this.store) return false;
|
|
60
|
+
const branch = cachedBranch(this.store?.reader) ?? [];
|
|
61
|
+
return (
|
|
62
|
+
(!s.leafId || branch.some((e) => e.id === s.leafId)) &&
|
|
63
|
+
(branch.filter((e) => e.message?.role === "user").at(-1)?.id ?? null) === s.userId
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
guard(s) {
|
|
67
|
+
check(
|
|
68
|
+
this.current(s),
|
|
69
|
+
"stale_operation",
|
|
70
|
+
"Routing state changed while the operation was waiting; no further effects applied.",
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
taskBasis(state, assignmentId) {
|
|
74
|
+
return state.resumeBasis.has(assignmentId)
|
|
75
|
+
? state.resumeBasis.get(assignmentId)
|
|
76
|
+
: state.assignments.get(assignmentId)?.basisUserEntryId;
|
|
77
|
+
}
|
|
78
|
+
delegation(state = this.stateData()) {
|
|
79
|
+
return state.delegationOverride ?? this.capability?.delegation ?? "executor";
|
|
80
|
+
}
|
|
81
|
+
enabledWorkers(state = this.stateData()) {
|
|
82
|
+
return workersForDelegation(this.delegation(state));
|
|
83
|
+
}
|
|
84
|
+
assignedWorker(state, assignmentId = state.assignmentId) {
|
|
85
|
+
const assignment = assignmentId ? state.assignments.get(assignmentId) : undefined;
|
|
86
|
+
const handoff = assignment ? state.handoffs.get(assignment.delegateHandoffId) : undefined;
|
|
87
|
+
check(
|
|
88
|
+
handoff?.kind === "delegate" && handoff.assignmentId === assignment?.id && isWorkerProfile(handoff.to),
|
|
89
|
+
"assignment_worker_missing",
|
|
90
|
+
"The current assignment has no valid recorded worker.",
|
|
91
|
+
);
|
|
92
|
+
return handoff.to;
|
|
93
|
+
}
|
|
94
|
+
requiredProfiles(state = this.stateData(), delegation = this.delegation(state)) {
|
|
95
|
+
const profiles = new Set(["coordinator", ...workersForDelegation(delegation)]);
|
|
96
|
+
if (state.profile) profiles.add(state.profile);
|
|
97
|
+
if (this.manualHold) profiles.add(this.manualHold);
|
|
98
|
+
const assignment = state.assignmentId ? state.assignments.get(state.assignmentId) : undefined;
|
|
99
|
+
if (assignment?.state === "outstanding" || state.recoveryId) profiles.add(this.assignedWorker(state));
|
|
100
|
+
const pending = state.pendingId ? state.handoffs.get(state.pendingId) : undefined;
|
|
101
|
+
if (pending && isWorkerProfile(pending.to)) profiles.add(pending.to);
|
|
102
|
+
return [...profiles];
|
|
103
|
+
}
|
|
104
|
+
retireUnbound(reason) {
|
|
105
|
+
for (const execution of this.stateData().executions.values()) {
|
|
106
|
+
if (!execution.assistantEntryId && !execution.interrupted)
|
|
107
|
+
this.append({ type: "execution-interrupted", executionId: execution.id, reason });
|
|
108
|
+
}
|
|
109
|
+
this.turn = undefined;
|
|
110
|
+
}
|
|
111
|
+
openTurn() {
|
|
112
|
+
if (!this.turn || this.turn.opened) return;
|
|
113
|
+
const t = this.turn;
|
|
114
|
+
this.append({
|
|
115
|
+
type: "execution-opened",
|
|
116
|
+
execution: {
|
|
117
|
+
id: t.id,
|
|
118
|
+
profile: t.profile,
|
|
119
|
+
assignmentId: t.assignmentId,
|
|
120
|
+
recoveryId: t.recoveryId,
|
|
121
|
+
basisUserEntryId: t.basisUserEntryId,
|
|
122
|
+
pair: t.pair,
|
|
123
|
+
resultEntryIds: [],
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
t.opened = true;
|
|
127
|
+
}
|
|
128
|
+
stateData() {
|
|
129
|
+
return this.store?.state() ?? initialState();
|
|
130
|
+
}
|
|
131
|
+
sources(state = this.stateData()) {
|
|
132
|
+
const branch = cachedBranch(this.ctx.sessionManager);
|
|
133
|
+
const cache = this.sourceCache;
|
|
134
|
+
// The cached branch is the same array until the leaf moves, so an unchanged branch needs no filtering.
|
|
135
|
+
if (cache && cache.branch === branch && cache.authors === state.authors.size) return cache.source;
|
|
136
|
+
const entries = branch.filter((e) =>
|
|
137
|
+
["message", "custom_message", "compaction", "branch_summary"].includes(e.type),
|
|
138
|
+
);
|
|
139
|
+
if (
|
|
140
|
+
cache &&
|
|
141
|
+
cache.authors === state.authors.size &&
|
|
142
|
+
entries.length === cache.entries.length &&
|
|
143
|
+
entries.every((e, i) => e === cache.entries[i])
|
|
144
|
+
) {
|
|
145
|
+
cache.branch = branch;
|
|
146
|
+
return cache.source;
|
|
147
|
+
}
|
|
148
|
+
const active = this.ctx.sessionManager.buildContextEntries?.();
|
|
149
|
+
const activeIds = active ? new Set(active.map((e) => e.id)) : undefined;
|
|
150
|
+
const source = cache?.source ?? new Sources([], state);
|
|
151
|
+
source.refresh(entries, state, activeIds);
|
|
152
|
+
this.sourceCache = { branch, entries, authors: state.authors.size, source };
|
|
153
|
+
return source;
|
|
154
|
+
}
|
|
155
|
+
observed(ctx = this.ctx) {
|
|
156
|
+
return ctx?.model?.provider && ctx?.model?.id && ctx?.thinkingLevel
|
|
157
|
+
? { provider: ctx.model.provider, modelId: ctx.model.id, thinking: ctx.thinkingLevel }
|
|
158
|
+
: undefined;
|
|
159
|
+
}
|
|
160
|
+
supported() {
|
|
161
|
+
return !!this.capability?.effective && !this.suppressed;
|
|
162
|
+
}
|
|
163
|
+
append(data, op, step) {
|
|
164
|
+
check(this.store, "routing_unavailable");
|
|
165
|
+
// Control changes made while no run is active affect no request until the next prompt; stage them there.
|
|
166
|
+
const stage = STAGED_CONTROL.has(data.type) && this.ctx?.isIdle?.() !== false;
|
|
167
|
+
return this.store.append(this.store.make(data, op, step), stage);
|
|
168
|
+
}
|
|
169
|
+
enqueue(action) {
|
|
170
|
+
const token = this.token;
|
|
171
|
+
const result = this.operations.then(() => {
|
|
172
|
+
check(token === this.token, "stale_instance");
|
|
173
|
+
return action();
|
|
174
|
+
});
|
|
175
|
+
this.operations = result.catch(() => {});
|
|
176
|
+
return result;
|
|
177
|
+
}
|
|
178
|
+
mark(error) {
|
|
179
|
+
if (this.error && error instanceof RoutingError && error.code === "routing_blocked") return;
|
|
180
|
+
this.error =
|
|
181
|
+
error instanceof RoutingError
|
|
182
|
+
? `${error.code}: ${error.message}`
|
|
183
|
+
: error instanceof Error
|
|
184
|
+
? error.message
|
|
185
|
+
: String(error);
|
|
186
|
+
}
|
|
187
|
+
announced;
|
|
188
|
+
// Tell the user once per distinct block; routing stays blocked until an explicit control clears it.
|
|
189
|
+
announceBlock(ctx) {
|
|
190
|
+
if (!this.error || this.announced === this.error) return;
|
|
191
|
+
this.announced = this.error;
|
|
192
|
+
ctx?.ui?.notify?.(`Cognitive Routing blocked: ${this.error}. ${ROUTING_RECOVERY_HINT}`, "warning");
|
|
193
|
+
}
|
|
194
|
+
assertAvailable(profile) {
|
|
195
|
+
check(
|
|
196
|
+
this.supported() && this.store && !this.error && !this.store.blocked,
|
|
197
|
+
"routing_unavailable",
|
|
198
|
+
this.error ?? this.store?.blocked ?? "Routing is inactive.",
|
|
199
|
+
);
|
|
200
|
+
const state = this.stateData();
|
|
201
|
+
check(state.control === "automatic", "manual_control");
|
|
202
|
+
if (profile) check(state.profile === profile && this.turn?.profile === profile, "wrong_profile");
|
|
203
|
+
if (state.profile) check(samePair(this.observed(), this.profilePair(state.profile)), "configuration_mismatch");
|
|
204
|
+
}
|
|
205
|
+
configuredProfilePair(profile) {
|
|
206
|
+
const configured = this.capability?.profiles[profile];
|
|
207
|
+
check(configured, "profile_missing");
|
|
208
|
+
return pairFromProfile(configured);
|
|
209
|
+
}
|
|
210
|
+
profilePair(profile) {
|
|
211
|
+
return this.stateData().profileOverrides.get(profile) ?? this.configuredProfilePair(profile);
|
|
212
|
+
}
|
|
213
|
+
profilePairs(profiles = this.requiredProfiles()) {
|
|
214
|
+
return Object.fromEntries(profiles.map((profile) => [profile, this.profilePair(profile)]));
|
|
215
|
+
}
|
|
216
|
+
workerBasis(state, assignmentId) {
|
|
217
|
+
const recovery = state.recoveryId ? state.recoveries.get(state.recoveryId) : undefined;
|
|
218
|
+
if (recovery?.state === "reading") return state.handoffs.get(recovery.requestHandoffId)?.basisUserEntryId ?? null;
|
|
219
|
+
return this.taskBasis(state, assignmentId);
|
|
220
|
+
}
|
|
221
|
+
}
|