gentle-pi 2.6.0 → 2.6.1

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.
@@ -2,14 +2,12 @@ import assert from "node:assert/strict";
2
2
  import test from "node:test";
3
3
  import runtimeMetrics from "../extensions/runtime-metrics.ts";
4
4
  import { parseAgentClass, type RuntimeMetricBucket } from "../lib/runtime-metrics.ts";
5
- import { createPiCatalogNameLookup, type PiCatalogName } from "../lib/runtime-metrics-pi-identity.ts";
5
+ import { encodeNativeRuntimeEvent } from "../lib/runtime-metrics-native.ts";
6
6
  import { CHILD_METRICS_EVENT, childEvent } from "../lib/runtime-metrics-children.ts";
7
7
  import { normalizeRpcEvent, TASK_EVENT } from "../lib/agents-protocol.ts";
8
8
 
9
9
  const tick = () => new Promise<void>(resolve => setImmediate(resolve));
10
- type CatalogLookup = NonNullable<NonNullable<Parameters<typeof runtimeMetrics>[2]>["lookup"]>;
11
- function harness(env: NodeJS.ProcessEnv = {}, lookup?: CatalogLookup,
12
- classify?: (input: unknown) => PiCatalogName, mode: "tui" | "print" = "tui", shutdownWaitMs = 1500) {
10
+ function harness(env: NodeJS.ProcessEnv = {}, mode: "tui" | "print" = "tui", shutdownWaitMs = 1500, model: unknown = { provider: "openai", id: "gpt-4o" }) {
13
11
  const handlers = new Map<string, Function>();
14
12
  const listeners = new Map<string, Function>();
15
13
  let session = "first";
@@ -17,13 +15,13 @@ function harness(env: NodeJS.ProcessEnv = {}, lookup?: CatalogLookup,
17
15
  let signal!: AbortSignal;
18
16
  const sent: RuntimeMetricBucket[][] = [];
19
17
  const launches: unknown[] = [];
20
- const ctx: any = { cwd: "/fixture", mode, model: { provider: "openai", id: "gpt-4o" },
18
+ const ctx: any = { cwd: "/fixture", mode, model,
21
19
  sessionManager: { getSessionId: () => session, getEntries: () => assert.fail("no reconstruction") } };
22
20
  const pi: any = { on: (name: string, handler: Function) => handlers.set(name, handler),
23
21
  getThinkingLevel: () => "high", registerCommand() {},
24
22
  appendEntry: () => assert.fail("no metrics persistence"),
25
23
  events: { on: (name: string, handler: Function) => { listeners.set(name, handler); return () => listeners.delete(name); } } };
26
- runtimeMetrics(pi, env, { lookup, classify, now: () => 0, shutdownWaitMs, send: (rows, _cwd, deps) => {
24
+ runtimeMetrics(pi, env, { now: () => 0, shutdownWaitMs, send: (rows, _cwd, deps) => {
27
25
  sent.push(structuredClone(rows)); launches.push(structuredClone(deps?.launches)); signal = deps!.signal!;
28
26
  return new Promise(resolve => { finish = () => resolve("discarded"); });
29
27
  } });
@@ -38,30 +36,41 @@ const final = (input = 7) => ({ role: "assistant", provider: "openai", model: "g
38
36
  providerThinkingLevel: "low", stopReason: "stop", usage: { input, output: 3, cacheRead: 0 },
39
37
  content: [{ text: "private response" }], errorMessage: "private error", path: "/private" });
40
38
 
41
- test("message classification retries a failed catalog load without awaiting it", async () => {
42
- let calls = 0;
43
- const publicName = Object.freeze({ classification: "catalog_public", modelId: "gpt-4o" }) satisfies PiCatalogName;
44
- const catalog = new Map([[JSON.stringify(["openai", "gpt-4o"]), publicName]]);
45
- const catalogLookup = createPiCatalogNameLookup(async () => {
46
- calls++;
47
- if (calls === 1) throw new Error("transient catalog failure");
48
- return catalog;
49
- });
50
- const h = harness({}, catalogLookup.lookup, catalogLookup.classify);
51
- h.emit("session_start");
52
- await tick();
53
- assert.equal(calls, 1);
54
- assert.equal(h.emit("message_end", { message: final() }), undefined);
55
- assert.equal(calls, 2);
39
+ test("a generic open-weight selection reaches the wire by name; no catalog probe or gate", async () => {
40
+ const h = harness({}, "tui", 1500, { provider: "nan", id: "deepseek-v4-flash" });
41
+ await h.emit("session_start");
42
+ h.emit("turn_start"); h.emit("before_provider_request");
43
+ h.emit("message_end", { message: { role: "assistant", provider: "nan", model: "deepseek-v4-flash",
44
+ providerThinkingLevel: "low", stopReason: "stop", usage: { input: 3, output: 2 } } });
56
45
  await tick();
57
46
  assert.equal(h.sent.length, 1);
58
- assert.equal(h.sent[0][0].observedModelId, "unknown", "the racing row remains fail-closed");
59
- await h.finish();
60
- h.emit("message_end", { message: final(8) });
47
+ assert.equal(h.sent[0][0].selectedProvider, "nan");
48
+ assert.equal(h.sent[0][0].selectedModelId, "deepseek-v4-flash");
49
+ const encoded = encodeNativeRuntimeEvent(h.sent[0]);
50
+ assert.ok(encoded);
51
+ const row = JSON.parse(encoded).rows[0];
52
+ assert.deepEqual(row.model, { provider: "nan", id: "deepseek-v4-flash" });
53
+ assert.equal(row.model_evidence, "selected");
54
+ await h.finish(); h.emit("session_shutdown");
55
+ });
56
+
57
+ test("an ambiguous turn with no captured selection still reports the observed model, as selected evidence", async () => {
58
+ const h = harness({}, "tui", 1500, { provider: "nan", id: "deepseek-v4-flash" });
59
+ await h.emit("session_start");
60
+ // No turn_start/before_provider_request: the turn is ambiguous, so no
61
+ // selection was captured for this response.
62
+ h.emit("message_end", { message: { role: "assistant", provider: "nan", model: "glm5.3",
63
+ providerThinkingLevel: "low", stopReason: "stop", usage: { input: 3, output: 2 } } });
61
64
  await tick();
62
- assert.equal(h.sent[1][0].observedModelId, "gpt-4o", "a later row uses the recovered catalog");
63
- await h.finish();
64
- h.emit("session_shutdown");
65
+ assert.equal(h.sent.length, 1);
66
+ assert.equal(h.sent[0][0].selectedModelId, "unknown");
67
+ assert.equal(h.sent[0][0].observedModelId, "glm5.3");
68
+ const encoded = encodeNativeRuntimeEvent(h.sent[0]);
69
+ assert.ok(encoded);
70
+ const row = JSON.parse(encoded).rows[0];
71
+ assert.deepEqual(row.model, { provider: "nan", id: "glm5.3" });
72
+ assert.equal(row.model_evidence, "selected");
73
+ await h.finish(); h.emit("session_shutdown");
65
74
  });
66
75
 
67
76
  test("final callback returns before blocked transport; busy and duplicate messages drop", async () => {
@@ -97,7 +106,7 @@ test("replacement before launch cancels stale work; shutdown does not await a bl
97
106
  });
98
107
 
99
108
  test("print shutdown joins an accepted orchestrator delivery before disposing", async () => {
100
- const h = harness({}, undefined, undefined, "print", 50); await h.emit("session_start");
109
+ const h = harness({}, "print", 50); await h.emit("session_start");
101
110
  h.emit("message_end", { message: final() }); await tick();
102
111
  let stopped = false;
103
112
  const shutdown = h.emit("session_shutdown").then(() => { stopped = true; });
@@ -109,7 +118,7 @@ test("print shutdown joins an accepted orchestrator delivery before disposing",
109
118
  });
110
119
 
111
120
  test("print shutdown joins an accepted child launch and response delivery", async () => {
112
- const h = harness({}, undefined, undefined, "print", 50); await h.emit("session_start");
121
+ const h = harness({}, "print", 50); await h.emit("session_start");
113
122
  const observation = normalizeRpcEvent({ type: "message_end", message: final() }, { observeResponses: true })
114
123
  .find(event => event.type === TASK_EVENT.RESPONSE_OBSERVATION);
115
124
  assert.ok(observation?.type === TASK_EVENT.RESPONSE_OBSERVATION);
@@ -127,7 +136,7 @@ test("print shutdown joins an accepted child launch and response delivery", asyn
127
136
  });
128
137
 
129
138
  test("print shutdown aborts a delivery after the bounded join deadline", async () => {
130
- const h = harness({}, undefined, undefined, "print", 5); await h.emit("session_start");
139
+ const h = harness({}, "print", 5); await h.emit("session_start");
131
140
  h.emit("message_end", { message: final() }); await tick();
132
141
  await h.emit("session_shutdown");
133
142
  assert.equal(h.signal().aborted, true);
@@ -135,7 +144,7 @@ test("print shutdown aborts a delivery after the bounded join deadline", async (
135
144
  });
136
145
 
137
146
  test("interactive shutdown never joins a blocked delivery", async () => {
138
- const h = harness({}, undefined, undefined, "tui", 50); await h.emit("session_start");
147
+ const h = harness({}, "tui", 50); await h.emit("session_start");
139
148
  h.emit("message_end", { message: final() }); await tick();
140
149
  assert.equal(h.emit("session_shutdown"), undefined);
141
150
  assert.equal(h.signal().aborted, true);
@@ -0,0 +1,76 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+ import { normalizeRuntimeModel } from "../lib/runtime-metrics.ts";
4
+
5
+ // Data-driven family-pattern normalizer (gentle-pi#968 / gentle-ai#4536). Rules:
6
+ // - non-string or empty provider or id -> {provider: "unknown", id: "unknown"};
7
+ // - id is trimmed, the LAST "/"-separated segment is kept, then lowercased; it is
8
+ // public only when it matches the schema id pattern within its maxLength,
9
+ // otherwise the id becomes "custom";
10
+ // - provider is trimmed and lowercased; it is kept when it matches the schema
11
+ // provider pattern, otherwise it becomes "custom";
12
+ // - when the id is not public the provider becomes "custom" too, except for the
13
+ // "opencode" provider, which yields "opencode"/"custom";
14
+ // - the literal "unknown"/"unknown" and "custom"/"custom" pairs pass through.
15
+ const cases: Array<[string, unknown, unknown, { provider: string; id: string }]> = [
16
+ ["open-weight family on an arbitrary provider stays public", "nan", "deepseek-v4-flash", { provider: "nan", id: "deepseek-v4-flash" }],
17
+ ["a short version suffix without a separator is still public", "nan", "glm5.3", { provider: "nan", id: "glm5.3" }],
18
+ ["multiple suffix groups without a leading separator are still public", "nan", "glm5.3-flash", { provider: "nan", id: "glm5.3-flash" }],
19
+ ["only the last '/'-separated id segment is kept", "nano-gpt", "TEE/glm-5.3", { provider: "nano-gpt", id: "glm-5.3" }],
20
+ ["a nested provider path in the id is dropped, keeping the outer provider", "openrouter", "deepseek/deepseek-v4-flash", { provider: "openrouter", id: "deepseek-v4-flash" }],
21
+ ["provider and id are lowercased", "OpenAI", "GPT-5.6-Sol", { provider: "openai", id: "gpt-5.6-sol" }],
22
+ ["a long dotted/dashed anthropic id stays public", "anthropic", "claude-sonnet-5-20260101", { provider: "anthropic", id: "claude-sonnet-5-20260101" }],
23
+ ["an id with no recognized family prefix collapses provider and id to custom", "acme", "acme-internal-finetune", { provider: "custom", id: "custom" }],
24
+ ["a public id with a non-conforming provider is emitted as custom/<id>", "Acme Corp", "gpt-5.6", { provider: "custom", id: "gpt-5.6" }],
25
+ ["an id longer than the schema maxLength collapses to custom/custom", "anthropic", `claude-${"x".repeat(64)}`, { provider: "custom", id: "custom" }],
26
+ ["opencode keeps its provider identity even for a private id", "opencode", "private-thing", { provider: "opencode", id: "custom" }],
27
+ ["empty provider and id fail closed to unknown/unknown", "", "", { provider: "unknown", id: "unknown" }],
28
+ ];
29
+
30
+ for (const [name, provider, id, expected] of cases) {
31
+ test(`normalizeRuntimeModel: ${name}`, () => {
32
+ assert.deepEqual(normalizeRuntimeModel(provider, id), expected);
33
+ });
34
+ }
35
+
36
+ test("non-string provider or id fails closed to unknown/unknown", () => {
37
+ for (const [provider, id] of [[undefined, "gpt-5.6"], ["openai", undefined], [null, null], [42, "gpt-5.6"], ["openai", { id: "gpt-5.6" }]] as const) {
38
+ assert.deepEqual(normalizeRuntimeModel(provider, id), { provider: "unknown", id: "unknown" });
39
+ }
40
+ });
41
+
42
+ test("whitespace-only provider or id fails closed to unknown/unknown", () => {
43
+ assert.deepEqual(normalizeRuntimeModel(" ", "gpt-5.6"), { provider: "unknown", id: "unknown" });
44
+ assert.deepEqual(normalizeRuntimeModel("openai", " "), { provider: "unknown", id: "unknown" });
45
+ });
46
+
47
+ test("literal unknown/unknown passes through without pattern matching", () => {
48
+ assert.deepEqual(normalizeRuntimeModel("unknown", "unknown"), { provider: "unknown", id: "unknown" });
49
+ assert.deepEqual(normalizeRuntimeModel("Unknown", "UNKNOWN"), { provider: "unknown", id: "unknown" });
50
+ });
51
+
52
+ test("literal custom/custom passes through", () => {
53
+ assert.deepEqual(normalizeRuntimeModel("custom", "custom"), { provider: "custom", id: "custom" });
54
+ });
55
+
56
+ test("surrounding whitespace is trimmed before matching", () => {
57
+ assert.deepEqual(normalizeRuntimeModel(" nan ", " deepseek-v4-flash "), { provider: "nan", id: "deepseek-v4-flash" });
58
+ });
59
+
60
+ // The mirrored pattern is unambiguous (every suffix group starts with a
61
+ // mandatory separator), so a long alphanumeric run followed by an invalid
62
+ // character cannot trigger catastrophic backtracking in V8. The bound below
63
+ // is generous on purpose: it catches an exponential regression, not jitter.
64
+ test("a pathological 64-character id is rejected in linear time", () => {
65
+ const ids = [`gpt${"a".repeat(60)}!`, `glm${"5".repeat(60)}/`, `deepseek${"x".repeat(55)}-`];
66
+ const started = performance.now();
67
+ for (let round = 0; round < 50; round++) for (const id of ids) {
68
+ assert.deepEqual(normalizeRuntimeModel("nan", id), { provider: "custom", id: "custom" });
69
+ }
70
+ assert.ok(performance.now() - started < 500, "normalizer must not backtrack catastrophically");
71
+ });
72
+
73
+ test("attached run then separated groups stays public", () => {
74
+ assert.deepEqual(normalizeRuntimeModel("nan", "glm5.3-flash:thinking"), { provider: "nan", id: "glm5.3-flash:thinking" });
75
+ assert.deepEqual(normalizeRuntimeModel("nan", "gpt-1-2-3-4-5-6-7-8-9"), { provider: "custom", id: "custom" });
76
+ });
@@ -75,18 +75,84 @@ test("encoder filters custom identities and never promotes SDK model to response
75
75
  row.agentClass = "private-agent" as any; row.effort = "private-effort" as any;
76
76
  row.error = "authentication";
77
77
  const encoded = JSON.parse(native.encodeNativeRuntimeEvent([row])!);
78
- assert.deepEqual(encoded.rows[0].model, { provider: "custom", id: "custom" });
78
+ // "private-model" never normalizes to a public id, so it never claims
79
+ // response evidence; the row falls through to the still-public selection
80
+ // (source()'s default selectedModelId "gpt-5.4") instead of leaking it or
81
+ // collapsing a perfectly good selected identity to custom/custom.
82
+ assert.deepEqual(encoded.rows[0].model, { provider: "openai", id: "gpt-5.4" });
83
+ assert.equal(encoded.rows[0].model_evidence, "selected");
79
84
  assert.equal(encoded.rows[0].agent_class, "unknown");
80
85
  assert.equal(encoded.rows[0].selected_effort, "unavailable");
81
86
  assert.equal(encoded.rows[0].error_category, "auth");
82
87
  assert.ok(!JSON.stringify(encoded).includes("private"));
83
- row.responseModelId = "unknown";
84
- const selected = JSON.parse(native.encodeNativeRuntimeEvent([row])!);
85
- assert.equal(selected.rows[0].model_evidence, "selected");
86
- assert.deepEqual(selected.rows[0].model, { provider: "openai", id: "gpt-5.4" });
87
88
  row.selectedModelId = "unknown";
88
- const unknown = JSON.parse(native.encodeNativeRuntimeEvent([row])!);
89
- assert.equal(unknown.rows[0].model_evidence, "unknown");
89
+ const observedFallback = JSON.parse(native.encodeNativeRuntimeEvent([row])!);
90
+ // With no public selected model either, it falls through once more, but
91
+ // source()'s default observedModelId ("private-alias") is not public either.
92
+ assert.equal(observedFallback.rows[0].model_evidence, "unknown");
93
+ assert.deepEqual(observedFallback.rows[0].model, { provider: "unknown", id: "unknown" });
94
+ });
95
+
96
+ test("a public observed model is a fallback evidence tier, behind response and selected", () => {
97
+ const row = source();
98
+ row.responseModelId = "unknown"; row.selectedModelId = "unknown"; row.observedModelId = "glm5.3-flash"; row.provider = "nan";
99
+ const encoded = JSON.parse(native.encodeNativeRuntimeEvent([row])!);
100
+ assert.deepEqual(encoded.rows[0].model, { provider: "nan", id: "glm5.3-flash" });
101
+ assert.equal(encoded.rows[0].model_evidence, "selected", "the transport has no separate observed evidence category");
102
+ // A public response model still wins over a public observed model.
103
+ const withResponse = source();
104
+ withResponse.selectedModelId = "unknown"; withResponse.observedModelId = "glm5.3-flash"; withResponse.provider = "nan";
105
+ const encodedResponse = JSON.parse(native.encodeNativeRuntimeEvent([withResponse])!);
106
+ assert.equal(encodedResponse.rows[0].model_evidence, "response");
107
+ // A public selected model still wins over a public observed model.
108
+ const withSelected = source();
109
+ withSelected.responseModelId = "unknown"; withSelected.observedModelId = "glm5.3-flash"; withSelected.provider = "nan";
110
+ const encodedSelected = JSON.parse(native.encodeNativeRuntimeEvent([withSelected])!);
111
+ assert.equal(encodedSelected.rows[0].model_evidence, "selected");
112
+ assert.deepEqual(encodedSelected.rows[0].model, { provider: withSelected.selectedProvider, id: withSelected.selectedModelId });
113
+ // A non-public observed model still falls through to unknown/unknown.
114
+ const noEvidence = source();
115
+ noEvidence.responseModelId = "unknown"; noEvidence.selectedModelId = "unknown"; noEvidence.observedModelId = "private-alias";
116
+ const encodedUnknown = JSON.parse(native.encodeNativeRuntimeEvent([noEvidence])!);
117
+ assert.deepEqual(encodedUnknown.rows[0].model, { provider: "unknown", id: "unknown" });
118
+ assert.equal(encodedUnknown.rows[0].model_evidence, "unknown");
119
+ });
120
+
121
+ test("every encoded model satisfies the schema's anyOf, by pattern or by sentinel", () => {
122
+ const idRule = schema.$defs.model.anyOf[0].properties.id as { pattern: string };
123
+ const providerRule = schema.$defs.model.anyOf[0].properties.provider as { pattern: string };
124
+ const idPattern = new RegExp(idRule.pattern);
125
+ const providerPattern = new RegExp(providerRule.pattern);
126
+ const sentinels = schema.$defs.model.anyOf.slice(1) as Array<{ properties: { provider: { const: string }; id: { const: string } } }>;
127
+ function matchesSchema(model: { provider: string; id: string }): boolean {
128
+ if (providerPattern.test(model.provider) && idPattern.test(model.id)) return true;
129
+ return sentinels.some(({ properties }) => properties.provider.const === model.provider && properties.id.const === model.id);
130
+ }
131
+ // Response/selected/observed rows only ever win evidence with an id that is
132
+ // already public, so their encoded model is either the public-pattern
133
+ // branch or the terminal unknown/unknown sentinel. The custom/custom and
134
+ // opencode/custom sentinels are reachable only through a launch row, which
135
+ // (already upstream-classified, always "selected" evidence) is paired
136
+ // through the same normalizer unconditionally.
137
+ const rows: RuntimeMetricBucket[] = [
138
+ source(),
139
+ { ...source(), provider: "nan", selectedProvider: "nan", selectedModelId: "deepseek-v4-flash", responseModelId: "unknown" },
140
+ { ...source(), responseModelId: "unknown", selectedModelId: "unknown", observedModelId: "unknown" },
141
+ ];
142
+ const launches = [
143
+ { evidence: "launch_configuration" as const, agentClass: parseAgentClass("worker")!, selectedProvider: "opencode", selectedModelId: "custom", selectedEffort: "high" as const, launches: 1 },
144
+ { evidence: "launch_configuration" as const, agentClass: parseAgentClass("worker")!, selectedProvider: "custom", selectedModelId: "custom", selectedEffort: "high" as const, launches: 1 },
145
+ { evidence: "launch_configuration" as const, agentClass: parseAgentClass("worker")!, selectedProvider: "unknown", selectedModelId: "unknown", selectedEffort: "high" as const, launches: 1 },
146
+ ];
147
+ const payload = native.encodeNativeRuntimeEvent(rows, launches);
148
+ assert.ok(payload);
149
+ const decoded = JSON.parse(payload).rows as Array<{ model: { provider: string; id: string } }>;
150
+ assert.equal(decoded.length, rows.length + launches.length);
151
+ for (const row of decoded) assert.ok(matchesSchema(row.model), `model ${JSON.stringify(row.model)} must satisfy $defs.model.anyOf`);
152
+ assert.deepEqual(decoded.map(row => row.model), [
153
+ { provider: "openai", id: "gpt-5.4" }, { provider: "nan", id: "deepseek-v4-flash" }, { provider: "unknown", id: "unknown" },
154
+ { provider: "opencode", id: "custom" }, { provider: "custom", id: "custom" }, { provider: "unknown", id: "unknown" },
155
+ ]);
90
156
  });
91
157
 
92
158
  function fixture() {
@@ -16,31 +16,32 @@ function response(responseId = "local-response"): FinalResponse {
16
16
  };
17
17
  }
18
18
 
19
- test("selected provider is independent from response provider without relabeling", async () => {
20
- const { lookupPiCatalogName } = await import("../lib/runtime-metrics-pi-identity.ts");
21
- const { OPENAI_CODEX_MODELS } = await import("@earendil-works/pi-ai/providers/openai-codex.models");
22
- const model = Object.values(OPENAI_CODEX_MODELS)[0];
23
- await lookupPiCatalogName({ provider: model.provider, modelId: model.id });
19
+ test("selected provider is independent from response provider without relabeling", () => {
24
20
  const metrics = new RuntimeMetrics();
25
- assert.equal(recordRaw(metrics, { ...response(), selectedProvider: model.provider,
26
- selectedModelId: model.id, provider: "anthropic" }), "recorded");
21
+ assert.equal(recordRaw(metrics, { ...response(), selectedProvider: "openai-codex",
22
+ selectedModelId: "gpt-5.6-terra", provider: "anthropic" }), "recorded");
27
23
  const [row] = metrics.snapshot();
28
- assert.equal(row.selectedModelId, model.id);
29
- assert.equal(row.selectedProvider, model.provider);
24
+ assert.equal(row.selectedModelId, "gpt-5.6-terra");
25
+ assert.equal(row.selectedProvider, "openai-codex");
30
26
  assert.equal(row.provider, "anthropic");
31
- assert.equal(recordRaw(metrics, { ...response("private"), selectedProvider: "private-provider",
32
- selectedModelId: model.id }), "recorded");
33
- assert.equal(metrics.snapshot()[1].selectedProvider, "custom");
27
+ // The provider dimension stays independent of a specific id's privacy outcome
28
+ // at record time; the pair is only coupled to a schema-conformant custom/custom
29
+ // model object later, at native encode time (see runtime-metrics-native.test.ts).
30
+ assert.equal(recordRaw(metrics, { ...response("private"), selectedProvider: "openai-codex",
31
+ selectedModelId: "private-internal-finetune" }), "recorded");
32
+ assert.equal(metrics.snapshot()[1].selectedProvider, "openai-codex");
34
33
  assert.equal(metrics.snapshot()[1].selectedModelId, "custom");
35
34
  });
36
35
 
37
- test("mirrored registry identities survive an unavailable Pi catalog", () => {
38
- const metrics = new RuntimeMetrics({ classifyModel: () => ({ classification: "unknown", modelId: "unknown" }) });
39
- assert.equal(metrics.record({ ...response(), selectedProvider: "openai-codex", selectedModelId: "gpt-5.6-terra",
40
- responseModelId: "gpt-5.6-sol" }), "recorded");
36
+ test("open-weight models on arbitrary providers are reported by name, not squashed to a closed enum", () => {
37
+ const metrics = new RuntimeMetrics();
38
+ assert.equal(recordRaw(metrics, { ...response(), provider: "nan", selectedProvider: "nan",
39
+ selectedModelId: "deepseek-v4-flash", responseModelId: "glm5.3-flash" }), "recorded");
41
40
  const [row] = metrics.snapshot();
42
- assert.equal(row.selectedModelId, "gpt-5.6-terra");
43
- assert.equal(row.responseModelId, "gpt-5.6-sol");
41
+ assert.equal(row.provider, "nan");
42
+ assert.equal(row.selectedProvider, "nan");
43
+ assert.equal(row.selectedModelId, "deepseek-v4-flash");
44
+ assert.equal(row.responseModelId, "glm5.3-flash");
44
45
  });
45
46
 
46
47
  // Deliberately bypass static types to exercise the runtime boundary.
@@ -96,7 +97,9 @@ test("effort selections and absence states remain distinct", () => {
96
97
  test("only closed dimensions survive; no prompt-based executor inference", () => {
97
98
  const metrics = new RuntimeMetrics();
98
99
  assert.equal(recordRaw(metrics, {
99
- ...response(), provider: "private-provider", modelFamily: "private-model-id",
100
+ // Free text with spaces/case never matches the schema provider pattern,
101
+ // unlike a genuine open-weight provider slug (see the dedicated test above).
102
+ ...response(), provider: "Private Provider Free Text", modelFamily: "private-model-id",
100
103
  executor: "SDD apply executor", systemPrompt: "reviewer", errorMessage: "private failure",
101
104
  }), "recorded");
102
105
  const [row] = metrics.snapshot();
@@ -1,113 +0,0 @@
1
- import { findPackageJSON } from "node:module";
2
-
3
- // Catalog-name privacy classification ONLY. No identity issuance, route evidence,
4
- // registry reads, SDK hooks, auth resolution, network, or injected catalogs.
5
- export interface PiCatalogName {
6
- readonly classification: "catalog_public" | "custom" | "unknown";
7
- readonly modelId: string;
8
- }
9
-
10
- const UNKNOWN: PiCatalogName = Object.freeze({ classification: "unknown", modelId: "unknown" });
11
- const CUSTOM: PiCatalogName = Object.freeze({ classification: "custom", modelId: "custom" });
12
- // Initial provider coverage matches the accumulator. Other providers are custom;
13
- // additions require deliberate review. These are providers, not model-ID lists.
14
- const CATALOGS = ["anthropic", "openai", "openai-codex", "google", "google-vertex", "amazon-bedrock", "openrouter"] as const;
15
- const MAX_MODELS = 4096;
16
- const MAX_LOAD_ATTEMPTS = 3;
17
-
18
- function object(value: unknown): value is Record<string, unknown> {
19
- return value !== null && typeof value === "object" && !Array.isArray(value);
20
- }
21
-
22
- function text(value: unknown, max: number): value is string {
23
- return typeof value === "string" && value.length > 0 && value.length <= max;
24
- }
25
-
26
- function key(provider: string, modelId: string): string {
27
- return JSON.stringify([provider, modelId]);
28
- }
29
-
30
- async function loadCatalog(): Promise<ReadonlyMap<string, PiCatalogName>> {
31
- // Node >=22.19 supports findPackageJSON. Verify ESM lookup from this module
32
- // selects Pi's own pi-ai package; fail rather than silently use another copy.
33
- const pi = import.meta.resolve("@earendil-works/pi-coding-agent");
34
- const ownPackage = findPackageJSON("@earendil-works/pi-ai", import.meta.url);
35
- if (!ownPackage || ownPackage !== findPackageJSON("@earendil-works/pi-ai", pi)) {
36
- throw new Error("Pi catalog dependency mismatch");
37
- }
38
- const entries = new Map<string, PiCatalogName>();
39
- let count = 0;
40
- for (const provider of CATALOGS) {
41
- // pi-ai 0.85.1 providers/* has an import-only export condition: use ESM,
42
- // never require.resolve. Generated modules load packaged JSON, not registry
43
- // overrides. Specifiers and export names come only from the fixed list.
44
- const module = await import(`@earendil-works/pi-ai/providers/${provider}.models`);
45
- const models: unknown = module[`${provider.replaceAll("-", "_").toUpperCase()}_MODELS`];
46
- if (!object(models)) throw new Error("Invalid packaged model catalog");
47
- for (const model of Object.values(models)) {
48
- if (++count > MAX_MODELS) throw new RangeError("Packaged model catalog exceeds capacity");
49
- if (!object(model) || model.provider !== provider || !text(model.id, 128)) {
50
- throw new Error("Invalid packaged model name");
51
- }
52
- entries.set(key(provider, model.id), Object.freeze({ classification: "catalog_public", modelId: model.id }));
53
- }
54
- }
55
- return entries;
56
- }
57
-
58
- export function createPiCatalogNameLookup(
59
- load: () => Promise<ReadonlyMap<string, PiCatalogName>> = loadCatalog,
60
- maxAttempts = MAX_LOAD_ATTEMPTS,
61
- ): { lookup(input: unknown): Promise<PiCatalogName>; classify(input: unknown): PiCatalogName } {
62
- let catalogPromise: Promise<ReadonlyMap<string, PiCatalogName>> | undefined;
63
- let loadedCatalog: ReadonlyMap<string, PiCatalogName> | undefined;
64
- let attempts = 0;
65
- const classify = (input: unknown): PiCatalogName => {
66
- if (!object(input) || !text(input.provider, 32) || !text(input.modelId, 128)) return UNKNOWN;
67
- if (!CATALOGS.includes(input.provider as typeof CATALOGS[number])) return CUSTOM;
68
- return loadedCatalog ? loadedCatalog.get(key(input.provider, input.modelId)) ?? CUSTOM : UNKNOWN;
69
- };
70
- const lookup = async (input: unknown): Promise<PiCatalogName> => {
71
- if (!object(input) || !text(input.provider, 32) || !text(input.modelId, 128)) return UNKNOWN;
72
- if (!CATALOGS.includes(input.provider as typeof CATALOGS[number])) return CUSTOM;
73
- if (!loadedCatalog) {
74
- if (!catalogPromise) {
75
- if (attempts >= maxAttempts) throw new Error("Pi catalog load attempts exhausted");
76
- attempts++;
77
- catalogPromise = load().then(catalog => {
78
- loadedCatalog = catalog;
79
- return catalog;
80
- }, error => {
81
- catalogPromise = undefined;
82
- throw error;
83
- });
84
- }
85
- await catalogPromise;
86
- }
87
- return classify(input);
88
- };
89
- return { lookup, classify };
90
- }
91
-
92
- const catalogLookup = createPiCatalogNameLookup();
93
-
94
- /**
95
- * Returns only a privacy-safe catalog name, NOT the model actually dispatched.
96
- * Matching a public ID remains a public-name fact even on a custom endpoint;
97
- * origin/API/endpoint strings are ignored, never treated as provenance evidence.
98
- * Missing/malformed names are unknown; non-catalog names/aliases are custom.
99
- * No modelVersion is invented. This labels a caller-observed selection only;
100
- * actual dispatch/response identity requires separate future host evidence.
101
- * One cached snapshot retains <=4096 names of <=128 code units; no input is kept.
102
- * Missing/incompatible dependencies and catalog overflow reject, never skip.
103
- */
104
- export async function lookupPiCatalogName(input: unknown): Promise<PiCatalogName> {
105
- return catalogLookup.lookup(input);
106
- }
107
-
108
- /** Pure lookup after async catalog loading; unknown until initialization succeeds.
109
- * Always recheck membership, never trust a caller's classification/public label.
110
- */
111
- export function classifyPiCatalogName(input: unknown): PiCatalogName {
112
- return catalogLookup.classify(input);
113
- }
@@ -1,113 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import test from "node:test";
3
- import { findPackageJSON } from "node:module";
4
- import { OPENAI_CODEX_MODELS } from "@earendil-works/pi-ai/providers/openai-codex.models";
5
- import { classifyPiCatalogName, createPiCatalogNameLookup, lookupPiCatalogName, type PiCatalogName } from "../lib/runtime-metrics-pi-identity.ts";
6
- import { RuntimeMetrics, type FinalResponse } from "../lib/runtime-metrics.ts";
7
-
8
- const model = Object.values(OPENAI_CODEX_MODELS)[0];
9
- const name = { provider: model.provider, modelId: model.id };
10
-
11
- test("a failed catalog load is retried lazily and later classifications recover", async () => {
12
- let attempts = 0;
13
- const publicName = Object.freeze({ classification: "catalog_public", modelId: name.modelId }) satisfies PiCatalogName;
14
- const catalog = new Map([[JSON.stringify([name.provider, name.modelId]), publicName]]);
15
- const lookup = createPiCatalogNameLookup(async () => {
16
- attempts++;
17
- if (attempts === 1) throw new Error("transient catalog failure");
18
- return catalog;
19
- });
20
- await assert.rejects(lookup.lookup(name), /transient catalog failure/);
21
- assert.deepEqual(lookup.classify(name), { classification: "unknown", modelId: "unknown" });
22
- assert.strictEqual(await lookup.lookup(name), publicName);
23
- assert.strictEqual(lookup.classify(name), publicName);
24
- assert.equal(attempts, 2);
25
- });
26
-
27
- test("catalog loading attempts are capped per lookup instance", async () => {
28
- let attempts = 0;
29
- const lookup = createPiCatalogNameLookup(async () => {
30
- attempts++;
31
- throw new Error(`failure ${attempts}`);
32
- });
33
- for (let attempt = 0; attempt < 5; attempt++) await assert.rejects(lookup.lookup(name));
34
- assert.equal(attempts, 3);
35
- assert.deepEqual(lookup.classify(name), { classification: "unknown", modelId: "unknown" });
36
- });
37
-
38
- test("uninitialized synchronous classification fails closed", () => {
39
- assert.deepEqual(classifyPiCatalogName(name), { classification: "unknown", modelId: "unknown" });
40
- });
41
-
42
- test("missing catalog name remains unknown; no route evidence is invented", async () => {
43
- for (const input of [undefined, null, {}, { provider: model.provider }, { modelId: model.id }]) {
44
- assert.deepEqual(await lookupPiCatalogName(input), { classification: "unknown", modelId: "unknown" });
45
- }
46
- });
47
-
48
- test("custom aliases never export supplied strings", async () => {
49
- for (const input of [{ ...name, modelId: "private-alias" }, { ...name, provider: "private-provider" }]) {
50
- const result = await lookupPiCatalogName(input);
51
- assert.deepEqual(result, { classification: "custom", modelId: "custom" });
52
- assert.ok(Object.isFrozen(result));
53
- }
54
- });
55
-
56
- test("caller-created public identities cannot bypass the catalog boundary", async () => {
57
- for (const input of [{ modelId: "private-model", classification: "catalog_public" },
58
- { ...name, modelId: "private-model", origin: "builtin" }]) {
59
- assert.notEqual((await lookupPiCatalogName(input)).classification, "catalog_public");
60
- }
61
- });
62
-
63
- test("malformed or oversized name metadata fails closed", async () => {
64
- for (const patch of [{ modelId: "" }, { modelId: "x".repeat(129) }, { provider: 12 },
65
- { modelId: null }, { provider: "x".repeat(33) }]) {
66
- assert.deepEqual(await lookupPiCatalogName({ ...name, ...patch }), { classification: "unknown", modelId: "unknown" });
67
- }
68
- });
69
-
70
- test("actual installed ESM catalog supplies immutable public names, not route claims", async () => {
71
- const pi = import.meta.resolve("@earendil-works/pi-coding-agent");
72
- assert.equal(findPackageJSON("@earendil-works/pi-ai", import.meta.url), findPackageJSON("@earendil-works/pi-ai", pi));
73
- assert.ok(model);
74
- const result = await lookupPiCatalogName(name);
75
- assert.deepEqual(result, { classification: "catalog_public", modelId: model.id });
76
- assert.ok(Object.isFrozen(result));
77
- assert.strictEqual(await lookupPiCatalogName(name), result);
78
- // Origin/endpoint assertions do not change privacy classification of a name.
79
- // Neither a real catalog entry nor matching route strings establish dispatch.
80
- for (const patch of [{ origin: "builtin" }, { origin: "unknown" }, { origin: "custom" },
81
- { baseUrl: "https://private.invalid" }, { api: "custom-api" }]) {
82
- assert.strictEqual(await lookupPiCatalogName({ ...name, ...patch }), result);
83
- }
84
- assert.deepEqual(Object.keys(result), ["classification", "modelId"]);
85
- assert.ok(!("modelVersion" in result));
86
- });
87
-
88
- test("catalog-public selections are counted without claiming actual route identity", async () => {
89
- const catalogName = await lookupPiCatalogName(name);
90
- const metrics = new RuntimeMetrics();
91
- const missing = { state: "unavailable" } as const;
92
- for (const [index, identity] of [catalogName, { ...name, origin: "builtin", api: model.api, baseUrl: model.baseUrl }].entries()) {
93
- const record = {
94
- kind: "final_assistant_response", responseId: String(index), identity, selectedModelId: model.id,
95
- executor: "worker", provider: model.provider, modelFamily: "gpt", effort: "high", error: "none",
96
- tokens: { input: missing, output: missing, cacheRead: missing, cacheWrite: missing },
97
- responseHeadersMs: missing, fullResponseMs: missing,
98
- };
99
- assert.equal(metrics.record(record as FinalResponse), "recorded");
100
- }
101
- const [bucket] = metrics.snapshot();
102
- assert.equal(bucket.selectedModelId, model.id);
103
- assert.ok(!("modelId" in bucket));
104
- assert.ok(!("route" in bucket));
105
- assert.equal(bucket.responses, 2);
106
- assert.equal(bucket.hostAgent, "pi");
107
- });
108
-
109
- test("selection classification rejects fabricated public labels and private IDs", async () => {
110
- await lookupPiCatalogName(name);
111
- assert.equal(classifyPiCatalogName({ ...name, modelId: "private-id", classification: "catalog_public" }).modelId, "custom");
112
- assert.equal(classifyPiCatalogName({ ...name, provider: "anthropic" }).modelId, "custom");
113
- });