@hassangameryt/freeflow 0.7.2 → 0.7.3
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/README.md +147 -127
- package/capabilities/cognitive-routing/SKILL.md +162 -93
- package/gemini-extension.json +1 -1
- package/package.json +1 -1
- package/pi-extension/dist/cognitive-routing-v2/config.js +35 -19
- package/pi-extension/dist/cognitive-routing-v2/projection.js +53 -44
- package/pi-extension/dist/cognitive-routing-v2/render.js +31 -10
- package/pi-extension/dist/cognitive-routing-v2/runtime.js +701 -105
- package/pi-extension/dist/cognitive-routing-v2/schemas.js +90 -24
- package/pi-extension/dist/cognitive-routing-v2/state.js +243 -27
- package/pi-extension/dist/cognitive-routing-v2/tools.js +19 -50
- package/pi-extension/dist/cognitive-routing-v2/types.js +5 -1
- package/pi-extension/dist/context-virtualization/runtime.js +29 -7
- package/pi-extension/dist/freeflow-context/tool.js +7 -6
- package/pi-extension/dist/index.js +36 -15
- package/pi-extension/dist/provider-support/astra/adapter.js +110 -0
- package/pi-extension/dist/provider-support/astra/history.js +125 -0
- package/pi-extension/dist/provider-support/astra/index.js +11 -0
- package/pi-extension/dist/provider-support/astra/session-state.js +114 -0
- package/pi-extension/dist/provider-support/index.js +4 -0
- package/pi-extension/dist/runtime/request-history.js +216 -0
- package/pi-extension/dist/runtime/runtime-context.js +29 -10
- package/pi-extension/dist/session-sources/events.js +22 -0
- package/pi-extension/dist/session-sources/provenance.js +32 -0
- package/pi-extension/dist/session-sources/sources.js +51 -7
- package/pi-extension/dist/settings/settings-ui.js +247 -47
- package/pi-extension/src/cognitive-routing-v2/config.ts +49 -19
- package/pi-extension/src/cognitive-routing-v2/projection.ts +53 -43
- package/pi-extension/src/cognitive-routing-v2/render.ts +31 -10
- package/pi-extension/src/cognitive-routing-v2/runtime.ts +712 -105
- package/pi-extension/src/cognitive-routing-v2/schemas.ts +95 -24
- package/pi-extension/src/cognitive-routing-v2/state.ts +244 -27
- package/pi-extension/src/cognitive-routing-v2/tools.ts +19 -50
- package/pi-extension/src/cognitive-routing-v2/types.ts +35 -3
- package/pi-extension/src/context-virtualization/runtime.ts +30 -8
- package/pi-extension/src/freeflow-context/tool.ts +7 -6
- package/pi-extension/src/index.ts +36 -14
- package/pi-extension/src/provider-support/astra/adapter.ts +108 -0
- package/pi-extension/src/provider-support/astra/history.ts +145 -0
- package/pi-extension/src/provider-support/astra/index.ts +12 -0
- package/pi-extension/src/provider-support/astra/session-state.ts +113 -0
- package/pi-extension/src/provider-support/index.ts +5 -0
- package/pi-extension/src/runtime/request-history.ts +243 -0
- package/pi-extension/src/runtime/runtime-context.ts +32 -10
- package/pi-extension/src/session-sources/events.ts +22 -0
- package/pi-extension/src/session-sources/provenance.ts +39 -0
- package/pi-extension/src/session-sources/sources.ts +77 -6
- package/pi-extension/src/settings/settings-ui.ts +276 -46
- package/pi-extension/tests/cache-reuse/history.test.js +117 -0
- package/pi-extension/tests/cache-reuse/native.test.js +91 -0
- package/pi-extension/tests/cache-reuse/settings.test.js +211 -0
- package/pi-extension/tests/cognitive-routing-v2/communication.test.js +94 -1
- package/pi-extension/tests/cognitive-routing-v2/delegation.test.js +557 -0
- package/pi-extension/tests/cognitive-routing-v2/native.test.js +34 -1
- package/pi-extension/tests/cognitive-routing-v2/persistence.test.js +55 -0
- package/pi-extension/tests/cognitive-routing-v2/recovery.test.js +800 -2
- package/pi-extension/tests/cognitive-routing-v2/render.test.js +15 -1
- package/pi-extension/tests/cognitive-routing-v2/runtime.test.js +273 -3
- package/pi-extension/tests/cognitive-routing-v2/state.test.js +312 -1
- package/pi-extension/tests/context-virtualization/extension.test.js +20 -10
- package/pi-extension/tests/context-virtualization/runtime.test.js +17 -3
- package/pi-extension/tests/fixtures/routing-native.js +4 -1
- package/pi-extension/tests/integration/mode-free-core.test.js +4 -3
- package/pi-extension/tests/integration/pi-extension.test.js +508 -29
- package/pi-extension/tests/integration/prompt-architecture.test.js +56 -40
- package/pi-extension/tests/integration/unaffected-regressions.test.js +3 -3
- package/pi-extension/tests/provider-support/astra-boundaries.test.js +75 -0
- package/pi-extension/tests/provider-support/astra.test.js +306 -0
- package/pi-extension/tests/provider-support/cli.test.js +92 -0
- package/pi-extension/tests/provider-support/native.test.js +183 -0
- package/plugin.json +1 -1
- package/runtime/prompts/cognitive-routing.md +4 -4
- package/skills/design-for-depth/SKILL.md +1 -1
- package/skills/design-for-depth/references/implementation-decisions.md +3 -3
- package/skills/discuss/SKILL.md +2 -2
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { isWorkerProfile } from "../cognitive-routing-v2/types.js";
|
|
2
|
+
import { isTaskEvidence, textRef, type Sources, type Source } from "./sources.js";
|
|
3
|
+
|
|
4
|
+
export function annotateSources(
|
|
5
|
+
messages: any[],
|
|
6
|
+
renderedSources: Map<any, Source>,
|
|
7
|
+
full: Set<string>,
|
|
8
|
+
sources: Sources,
|
|
9
|
+
instance: string,
|
|
10
|
+
): any[] {
|
|
11
|
+
const annotated: any[] = [];
|
|
12
|
+
for (let i = 0; i < messages.length;) {
|
|
13
|
+
const group = [messages[i++]];
|
|
14
|
+
while (i < messages.length && messages[i].role === "toolResult") group.push(messages[i++]);
|
|
15
|
+
const rows = group.flatMap((message) => {
|
|
16
|
+
const s = renderedSources.get(message);
|
|
17
|
+
if (!s) return [];
|
|
18
|
+
const representation = full.has(s.ref) ? "full" : "structural only; omitted result bodies are not evidence";
|
|
19
|
+
const selection =
|
|
20
|
+
isWorkerProfile(s.producer) && isTaskEvidence(s) && full.has(s.ref)
|
|
21
|
+
? "task evidence; selection checks apply"
|
|
22
|
+
: "not offered for new evidence selection";
|
|
23
|
+
return [
|
|
24
|
+
`${s.ref} | producer: ${s.producer === "common" ? "unknown/common (no observed routing profile)" : s.producer} | ${s.original ? "assistant-text" : s.message.role}${s.message.toolName ? ` | ${s.message.toolName}` : ""}${s.assignmentId ? ` | assignment: ${s.assignmentId}` : ""} | ${representation} | ${selection}${!s.original && full.has(s.ref) && sources.byRef.has(textRef(s.ref)) ? ` | visible text: ${textRef(s.ref)}` : ""}`,
|
|
25
|
+
];
|
|
26
|
+
});
|
|
27
|
+
annotated.push(...group);
|
|
28
|
+
if (rows.length)
|
|
29
|
+
annotated.push({
|
|
30
|
+
role: "custom",
|
|
31
|
+
customType: "freeflow-routing-v2-refs",
|
|
32
|
+
display: false,
|
|
33
|
+
content: `Source provenance for the preceding message/exchange:\n${rows.join("\n")}`,
|
|
34
|
+
details: { routingInstance: instance },
|
|
35
|
+
timestamp: 0,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
return annotated;
|
|
39
|
+
}
|
|
@@ -3,6 +3,7 @@ import { sessionEntryToContextMessages } from "@earendil-works/pi-coding-agent";
|
|
|
3
3
|
import {
|
|
4
4
|
canonical,
|
|
5
5
|
idFor,
|
|
6
|
+
isWorkerProfile,
|
|
6
7
|
refFor,
|
|
7
8
|
type NativeEntry,
|
|
8
9
|
type State,
|
|
@@ -10,6 +11,12 @@ import {
|
|
|
10
11
|
type Problem,
|
|
11
12
|
} from "../cognitive-routing-v2/types.js";
|
|
12
13
|
|
|
14
|
+
export interface EvidenceLocator {
|
|
15
|
+
callRef: string;
|
|
16
|
+
toolCallId: string;
|
|
17
|
+
label?: string;
|
|
18
|
+
truncated?: boolean;
|
|
19
|
+
}
|
|
13
20
|
export interface Source {
|
|
14
21
|
ref: string;
|
|
15
22
|
entry: NativeEntry;
|
|
@@ -22,6 +29,15 @@ export interface Source {
|
|
|
22
29
|
original?: Source;
|
|
23
30
|
reportHandoff?: string;
|
|
24
31
|
}
|
|
32
|
+
export interface NativeToolCall {
|
|
33
|
+
id: string;
|
|
34
|
+
name: string;
|
|
35
|
+
arguments?: unknown;
|
|
36
|
+
}
|
|
37
|
+
export type EvidenceLocatorProvider = (
|
|
38
|
+
assistant: Source,
|
|
39
|
+
call: NativeToolCall,
|
|
40
|
+
) => Pick<EvidenceLocator, "label" | "truncated"> | undefined;
|
|
25
41
|
export interface Associated {
|
|
26
42
|
message: any;
|
|
27
43
|
source?: Source;
|
|
@@ -45,6 +61,31 @@ interface Exchange {
|
|
|
45
61
|
results: Map<string, Source[]>;
|
|
46
62
|
}
|
|
47
63
|
const callKey = (id: string, name: string) => JSON.stringify([id, name]);
|
|
64
|
+
const locatorLimit = 160;
|
|
65
|
+
|
|
66
|
+
function normalizeLabel(value: unknown): Pick<EvidenceLocator, "label" | "truncated"> | undefined {
|
|
67
|
+
if (typeof value !== "string") return undefined;
|
|
68
|
+
const clean = value
|
|
69
|
+
.replace(/[\u0000-\u001f\u007f]/g, " ")
|
|
70
|
+
.replace(/\s+/g, " ")
|
|
71
|
+
.trim();
|
|
72
|
+
if (!clean) return undefined;
|
|
73
|
+
if (clean.length <= locatorLimit) return { label: clean, truncated: false };
|
|
74
|
+
return { label: `${clean.slice(0, locatorLimit - 3)}...`, truncated: true };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function locatorForCall(
|
|
78
|
+
assistant: Source,
|
|
79
|
+
call: NativeToolCall,
|
|
80
|
+
locatorProvider?: EvidenceLocatorProvider,
|
|
81
|
+
): EvidenceLocator | undefined {
|
|
82
|
+
if (typeof assistant.ref !== "string" || typeof call.id !== "string" || typeof call.name !== "string")
|
|
83
|
+
return undefined;
|
|
84
|
+
const locator: EvidenceLocator = { callRef: assistant.ref, toolCallId: call.id };
|
|
85
|
+
const details = locatorProvider?.(assistant, call);
|
|
86
|
+
const label = normalizeLabel(details?.label);
|
|
87
|
+
return label ? { ...locator, ...label, truncated: details?.truncated === true || label.truncated } : locator;
|
|
88
|
+
}
|
|
48
89
|
|
|
49
90
|
export class Sources {
|
|
50
91
|
readonly byRef = new Map<string, Source>();
|
|
@@ -56,7 +97,14 @@ export class Sources {
|
|
|
56
97
|
private associated?: { messages: readonly any[]; items: Associated[] };
|
|
57
98
|
entries: readonly NativeEntry[] = [];
|
|
58
99
|
private activeIds?: ReadonlySet<string>;
|
|
59
|
-
|
|
100
|
+
private readonly locatorProvider?: EvidenceLocatorProvider;
|
|
101
|
+
constructor(
|
|
102
|
+
entries: readonly NativeEntry[],
|
|
103
|
+
state: State,
|
|
104
|
+
activeIds?: ReadonlySet<string>,
|
|
105
|
+
locatorProvider?: EvidenceLocatorProvider,
|
|
106
|
+
) {
|
|
107
|
+
this.locatorProvider = locatorProvider;
|
|
60
108
|
this.refresh(entries, state, activeIds);
|
|
61
109
|
}
|
|
62
110
|
refresh(entries: readonly NativeEntry[], state: State, activeIds?: ReadonlySet<string>): void {
|
|
@@ -89,7 +137,11 @@ export class Sources {
|
|
|
89
137
|
if (block.type === "text") {
|
|
90
138
|
try {
|
|
91
139
|
const receipt = JSON.parse(block.text);
|
|
92
|
-
if (
|
|
140
|
+
if (
|
|
141
|
+
((receipt.reportSaved && typeof receipt.report === "string") ||
|
|
142
|
+
(receipt.supplementSaved && typeof receipt.supplement === "string")) &&
|
|
143
|
+
typeof receipt.handoff === "string"
|
|
144
|
+
)
|
|
93
145
|
source.reportHandoff = receipt.handoff;
|
|
94
146
|
} catch {}
|
|
95
147
|
}
|
|
@@ -153,7 +205,15 @@ export class Sources {
|
|
|
153
205
|
this.ambiguous.clear();
|
|
154
206
|
for (const source of this.byRef.values()) source.active = false;
|
|
155
207
|
const used = new Set<string>(),
|
|
156
|
-
hashes = messages.map(
|
|
208
|
+
hashes = messages.map((message) => {
|
|
209
|
+
const direct = bodyHash(message);
|
|
210
|
+
if (this.byBody.has(direct) || message?.role !== "toolResult" || !Array.isArray(message.content)) return direct;
|
|
211
|
+
const last = message.content.at(-1);
|
|
212
|
+
if (last?.type !== "text") return direct;
|
|
213
|
+
const original = bodyHash({ ...message, content: message.content.slice(0, -1) });
|
|
214
|
+
// Freeflow's stable reference decoration does not change the native evidence body.
|
|
215
|
+
return this.byBody.get(original)?.some((s) => last.text === `[context-ref: ${s.ref}]`) ? original : direct;
|
|
216
|
+
}),
|
|
157
217
|
remaining = new Map<string, number>();
|
|
158
218
|
for (const hash of hashes) remaining.set(hash, (remaining.get(hash) ?? 0) + 1);
|
|
159
219
|
const items = messages.map((message, index) => {
|
|
@@ -192,13 +252,13 @@ export class Sources {
|
|
|
192
252
|
code: "source_changed",
|
|
193
253
|
detail: "The captured source changed; reconcile evidence before delivery.",
|
|
194
254
|
};
|
|
195
|
-
if (source.producer
|
|
196
|
-
return { ref, code: "source_origin", detail: "Selection requires observed
|
|
255
|
+
if (!isWorkerProfile(source.producer))
|
|
256
|
+
return { ref, code: "source_origin", detail: "Selection requires observed worker attribution." };
|
|
197
257
|
if (state.exposure.get(original.ref) !== original.hash && state.exposure.get(source.ref) !== source.hash)
|
|
198
258
|
return {
|
|
199
259
|
ref,
|
|
200
260
|
code: "source_unexposed",
|
|
201
|
-
detail: "The captured body has not been fully exposed to
|
|
261
|
+
detail: "The captured body has not been fully exposed to a worker on this ancestry.",
|
|
202
262
|
};
|
|
203
263
|
return undefined;
|
|
204
264
|
}
|
|
@@ -252,6 +312,17 @@ export class Sources {
|
|
|
252
312
|
}
|
|
253
313
|
return { sources, problems };
|
|
254
314
|
}
|
|
315
|
+
locator(ref: string): EvidenceLocator | undefined {
|
|
316
|
+
const source = this.byRef.get(ref);
|
|
317
|
+
if (!source || source.original || source.message.role !== "toolResult") return undefined;
|
|
318
|
+
const group = this.owners.get(source.ref);
|
|
319
|
+
if (!group) return undefined;
|
|
320
|
+
const calls = group.assistant.message.content?.filter((b: any) => b.type === "toolCall") ?? [];
|
|
321
|
+
const matches = calls.filter(
|
|
322
|
+
(call: any) => call.id === source.message.toolCallId && call.name === source.message.toolName,
|
|
323
|
+
);
|
|
324
|
+
return matches.length === 1 ? locatorForCall(group.assistant, matches[0], this.locatorProvider) : undefined;
|
|
325
|
+
}
|
|
255
326
|
ordered(sources: Iterable<Source>): Source[] {
|
|
256
327
|
const ids = new Set([...sources].map((s) => s.ref));
|
|
257
328
|
return this.entries.flatMap((entry) =>
|