@geminixiang/mikan 1.0.0-beta.67 → 1.0.0-beta.69

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.
Files changed (49) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/adapters/slack/auto-reply-context.d.ts +7 -5
  3. package/dist/adapters/slack/auto-reply-context.d.ts.map +1 -1
  4. package/dist/adapters/slack/auto-reply-context.js +11 -53
  5. package/dist/adapters/slack/auto-reply-context.js.map +1 -1
  6. package/dist/adapters/slack/bot.d.ts +8 -0
  7. package/dist/adapters/slack/bot.d.ts.map +1 -1
  8. package/dist/adapters/slack/bot.js +65 -11
  9. package/dist/adapters/slack/bot.js.map +1 -1
  10. package/dist/adapters/slack/jev-context.d.ts +30 -0
  11. package/dist/adapters/slack/jev-context.d.ts.map +1 -0
  12. package/dist/adapters/slack/jev-context.js +67 -0
  13. package/dist/adapters/slack/jev-context.js.map +1 -0
  14. package/dist/adapters/slack/task-intent.d.ts +32 -0
  15. package/dist/adapters/slack/task-intent.d.ts.map +1 -0
  16. package/dist/adapters/slack/task-intent.js +72 -0
  17. package/dist/adapters/slack/task-intent.js.map +1 -0
  18. package/dist/adapters/slack/task-status.d.ts.map +1 -1
  19. package/dist/adapters/slack/task-status.js +6 -0
  20. package/dist/adapters/slack/task-status.js.map +1 -1
  21. package/dist/env-manifest.js +1 -1
  22. package/dist/env-manifest.js.map +1 -1
  23. package/dist/harness/index.d.ts +1 -1
  24. package/dist/harness/index.d.ts.map +1 -1
  25. package/dist/harness/index.js.map +1 -1
  26. package/dist/harness/jev.d.ts +32 -11
  27. package/dist/harness/jev.d.ts.map +1 -1
  28. package/dist/harness/jev.js +103 -45
  29. package/dist/harness/jev.js.map +1 -1
  30. package/dist/harness/presenter.d.ts.map +1 -1
  31. package/dist/harness/presenter.js +4 -3
  32. package/dist/harness/presenter.js.map +1 -1
  33. package/dist/harness/prompt.d.ts.map +1 -1
  34. package/dist/harness/prompt.js +1 -0
  35. package/dist/harness/prompt.js.map +1 -1
  36. package/dist/harness/subagent-profiles.js +5 -5
  37. package/dist/harness/subagent-profiles.js.map +1 -1
  38. package/dist/harness/tools/index.d.ts.map +1 -1
  39. package/dist/harness/tools/index.js +3 -0
  40. package/dist/harness/tools/index.js.map +1 -1
  41. package/dist/harness/tools/jev.d.ts +21 -0
  42. package/dist/harness/tools/jev.d.ts.map +1 -0
  43. package/dist/harness/tools/jev.js +109 -0
  44. package/dist/harness/tools/jev.js.map +1 -0
  45. package/dist/sessions/session-store.d.ts +2 -0
  46. package/dist/sessions/session-store.d.ts.map +1 -1
  47. package/dist/sessions/session-store.js +1 -0
  48. package/dist/sessions/session-store.js.map +1 -1
  49. package/package.json +2 -1
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Jev (typesafe/jev) client, reached through OpenRouter's decisions API.
2
+ * Jev (typesafe/jev) client, backed by `@geminixiang/jev`.
3
3
  *
4
4
  * Jev is a "System One" evaluation model: it scores a shared `state`
5
5
  * against typed questions (a yes/no probability, a multiple choice, or a
@@ -14,16 +14,19 @@
14
14
  * classification, routing, guardrails), and interpret the returned
15
15
  * probabilities themselves.
16
16
  *
17
- * Requires `OPENROUTER_API_KEY` (see env-manifest.ts) — the same key
18
- * pi-ai's `openrouter` chat provider reads. `evaluateWithJev` calls
19
- * OpenRouter's `/api/alpha/decisions` REST endpoint directly; this is a
20
- * plain typed-decision API, not chat/completion, so it does not go
21
- * through pi-ai's provider machinery.
17
+ * `@geminixiang/jev` owns the wire protocol, provider catalog, and auth
18
+ * resolution for four backends (TypeSafe, OpenRouter, Vercel AI Gateway,
19
+ * Cloudflare Workers AI); this module picks OpenRouter (the same key
20
+ * pi-ai's `openrouter` chat provider reads) and adapts its typed
21
+ * request/answer shapes to the caller-facing contract mikan's call sites
22
+ * already depend on, so this file is the only one that needs to know a
23
+ * dependency swap happened.
22
24
  */
25
+ import { createBuiltinJevModels, JevAPIError, JevAuthError, JevConfigError, JevConnectionError, JevResponseError, JevTimeoutError, } from "@geminixiang/jev";
23
26
  import { readEnv } from "../env-manifest.js";
24
27
  /** The current public Jev model id on OpenRouter. */
25
28
  export const JEV_MODEL_ID = "~typesafe/jev-latest";
26
- const OPENROUTER_DECISIONS_URL = "https://openrouter.ai/api/alpha/decisions";
29
+ /** Thrown when no Jev backend has a usable credential. Callers fail closed on this. */
27
30
  export class JevNotConfiguredError extends Error {
28
31
  constructor() {
29
32
  super("Jev requires OPENROUTER_API_KEY (an OpenRouter API key). Set it in the environment before calling evaluateWithJev().");
@@ -37,73 +40,128 @@ export class JevRequestError extends Error {
37
40
  this.name = "JevRequestError";
38
41
  }
39
42
  }
40
- function toOpenRouterQuestion(question) {
43
+ // ── @geminixiang/jev wiring ──────────────────────────────────────────────
44
+ /**
45
+ * Routes env reads through `readEnv` so `MIKAN_OPENROUTER_API_KEY` resolves
46
+ * the same as every other mikan-aliased credential, instead of the
47
+ * package's default `process.env`-only context.
48
+ */
49
+ const mikanAuthContext = {
50
+ async env(name) {
51
+ return readEnv(name);
52
+ },
53
+ async fileExists() {
54
+ return false;
55
+ },
56
+ };
57
+ let cachedModels;
58
+ function models() {
59
+ cachedModels ??= createBuiltinJevModels({ authContext: mikanAuthContext });
60
+ return cachedModels;
61
+ }
62
+ function toGeminixiangQuestion(question) {
63
+ const instructions = question.instructions;
41
64
  if (question.type === "boolean") {
65
+ // `@geminixiang/jev` leaves noul criteria optional; mikan has always sent
66
+ // an explicit Yes/No default so the wire payload (and anything tuned
67
+ // against it, e.g. the auto-reply gate) does not change under this swap.
42
68
  return {
43
69
  type: "noul",
44
- instructions: question.instructions,
45
- criteria: { true: "Yes", false: "No" },
70
+ instructions,
71
+ criteria: {
72
+ true: question.criteria?.true ?? "Yes",
73
+ false: question.criteria?.false ?? "No",
74
+ },
46
75
  };
47
76
  }
48
- return question;
77
+ if (question.type === "choice") {
78
+ return {
79
+ type: "choice",
80
+ instructions,
81
+ criteria: question.criteria,
82
+ };
83
+ }
84
+ const [first, second, ...rest] = question.criteria;
85
+ if (first === undefined || second === undefined) {
86
+ throw new Error("score questions need at least two levels");
87
+ }
88
+ return { type: "score", instructions, criteria: [first, second, ...rest] };
49
89
  }
50
- function fromOpenRouterAnswer(answer) {
90
+ function fromGeminixiangAnswer(answer) {
51
91
  if (answer.type === "noul") {
52
- return { type: "boolean", probability: answer.noul ?? 0 };
92
+ return { type: "boolean", probability: answer.noul };
53
93
  }
54
94
  if (answer.type === "choice") {
55
- return { type: "choice", choice: answer.choice ?? "", probabilities: answer.probabilities };
95
+ return {
96
+ type: "choice",
97
+ choice: answer.choice,
98
+ probabilities: answer.probabilities,
99
+ confidence: answer.confidence,
100
+ };
56
101
  }
57
- return { type: "score", score: answer.score ?? 0, probabilities: answer.probabilities };
102
+ return {
103
+ type: "score",
104
+ score: answer.score,
105
+ probabilities: answer.probabilities,
106
+ legend: answer.legend,
107
+ confidence: answer.confidence,
108
+ };
58
109
  }
59
110
  /**
60
111
  * Evaluate one or more typed questions against a shared `state` using Jev,
61
- * reached through OpenRouter's decisions API. Every question is scored
62
- * independently against the same state in a single request; batching
63
- * questions here is cheap (they share the input) and is preferred over
64
- * separate calls per question.
112
+ * reached through OpenRouter's decisions API by way of `@geminixiang/jev`.
113
+ * Every question is scored independently against the same state in a
114
+ * single request; batching questions here is cheap (they share the input)
115
+ * and is preferred over separate calls per question.
65
116
  */
66
117
  export async function evaluateWithJev(state, questions, options = {}) {
67
- const apiKey = readEnv("OPENROUTER_API_KEY");
68
- if (!apiKey)
118
+ const catalogModel = models().getModel("openrouter", "jev-latest");
119
+ if (!catalogModel)
69
120
  throw new JevNotConfiguredError();
121
+ // `options.model` is the wire model id callers already pass (e.g. from
122
+ // JEV_MODEL_ID); override the catalog slug directly rather than adding a
123
+ // second catalog entry per possible override.
124
+ const model = options.model ? { ...catalogModel, slug: options.model } : catalogModel;
70
125
  const wireQuestions = {};
71
126
  for (const [id, question] of Object.entries(questions)) {
72
- wireQuestions[id] = toOpenRouterQuestion(question);
127
+ wireQuestions[id] = toGeminixiangQuestion(question);
73
128
  }
74
- const response = await fetch(OPENROUTER_DECISIONS_URL, {
75
- method: "POST",
76
- headers: {
77
- Authorization: `Bearer ${apiKey}`,
78
- "Content-Type": "application/json",
79
- ...options.headers,
80
- },
81
- body: JSON.stringify({
82
- model: options.model ?? JEV_MODEL_ID,
83
- state,
84
- questions: wireQuestions,
85
- }),
86
- signal: options.abortSignal,
87
- });
88
- const body = (await response.json());
89
- if (!response.ok || body.error) {
90
- throw new JevRequestError(body.error?.message ?? `Jev request failed with status ${response.status}`, body.error?.code ?? response.status);
129
+ let result;
130
+ try {
131
+ result = await models().evaluate(model, { state: state, questions: wireQuestions }, {
132
+ ...(options.headers ? { headers: options.headers } : {}),
133
+ ...(options.abortSignal ? { signal: options.abortSignal } : {}),
134
+ });
135
+ }
136
+ catch (error) {
137
+ if (error instanceof JevAuthError || error instanceof JevConfigError) {
138
+ throw new JevNotConfiguredError();
139
+ }
140
+ if (error instanceof JevAPIError) {
141
+ throw new JevRequestError(error.message, error.status);
142
+ }
143
+ if (error instanceof JevResponseError ||
144
+ error instanceof JevConnectionError ||
145
+ error instanceof JevTimeoutError) {
146
+ throw new JevRequestError(error.message, 502);
147
+ }
148
+ throw error;
91
149
  }
92
150
  const answers = {};
93
151
  for (const id of Object.keys(questions)) {
94
- const answer = body.answers[id];
152
+ const answer = result.answers[id];
95
153
  if (!answer)
96
154
  throw new JevRequestError(`Jev response missing answer for "${id}"`, 502);
97
- answers[id] = fromOpenRouterAnswer(answer);
155
+ answers[id] = fromGeminixiangAnswer(answer);
98
156
  }
99
157
  return {
100
158
  answers,
101
159
  usage: {
102
- inputTokens: body.usage?.input_tokens,
103
- outputTokens: body.usage?.output_tokens,
104
- cost: body.usage?.cost,
160
+ inputTokens: result.usage.input,
161
+ outputTokens: result.usage.output,
162
+ cost: result.usage.cost.total,
105
163
  },
106
- model: body.model,
164
+ model: result.model,
107
165
  };
108
166
  }
109
167
  //# sourceMappingURL=jev.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"jev.js","sourceRoot":"","sources":["../../src/harness/jev.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Jev (typesafe/jev) client, reached through OpenRouter's decisions API.\n *\n * Jev is a \"System One\" evaluation model: it scores a shared `state`\n * against typed questions (a yes/no probability, a multiple choice, or a\n * position on an ordered scale) and returns calibrated probabilities. It\n * does not generate text or tool calls, so it cannot be driven through\n * pi-ai's `Provider`/`Model`/`stream()` contract the way every model\n * `models.ts` catalogs is — that contract is chat/completion shaped\n * (system prompt + messages in, streamed text/tool-call events out), and\n * Jev's typed-decision shape does not fit it. Jev is therefore not a\n * selectable chat provider: callers import `evaluateWithJev` directly at\n * whatever call site wants a fast, cheap, type-safe decision (e.g.\n * classification, routing, guardrails), and interpret the returned\n * probabilities themselves.\n *\n * Requires `OPENROUTER_API_KEY` (see env-manifest.ts) — the same key\n * pi-ai's `openrouter` chat provider reads. `evaluateWithJev` calls\n * OpenRouter's `/api/alpha/decisions` REST endpoint directly; this is a\n * plain typed-decision API, not chat/completion, so it does not go\n * through pi-ai's provider machinery.\n */\nimport { readEnv } from \"../env-manifest.js\";\n\n/** The current public Jev model id on OpenRouter. */\nexport const JEV_MODEL_ID = \"~typesafe/jev-latest\";\n\nconst OPENROUTER_DECISIONS_URL = \"https://openrouter.ai/api/alpha/decisions\";\n\n/** A shared caller-facing question shape; translated to OpenRouter's wire format below. */\nexport type JevQuestion =\n | { type: \"boolean\"; instructions: string }\n | { type: \"choice\"; instructions: string; criteria: Record<string, string> }\n | { type: \"score\"; instructions: string; criteria: readonly string[] };\n\nexport type JevQuestions = Record<string, JevQuestion>;\n\nexport type JevAnswer<QUESTION extends JevQuestion> = QUESTION extends { type: \"choice\" }\n ? {\n type: \"choice\";\n choice: string;\n probabilities?: Record<string, number>;\n }\n : QUESTION extends { type: \"score\" }\n ? { type: \"score\"; score: number; probabilities?: Record<string, number> }\n : { type: \"boolean\"; probability: number };\n\nexport interface JevResult<QUESTIONS extends JevQuestions> {\n readonly answers: { [ID in keyof QUESTIONS]: JevAnswer<QUESTIONS[ID]> };\n readonly usage: {\n inputTokens: number | undefined;\n outputTokens: number | undefined;\n cost: number | undefined;\n };\n readonly model: string;\n}\n\nexport class JevNotConfiguredError extends Error {\n constructor() {\n super(\n \"Jev requires OPENROUTER_API_KEY (an OpenRouter API key). Set it in the environment before calling evaluateWithJev().\",\n );\n this.name = \"JevNotConfiguredError\";\n }\n}\n\nexport class JevRequestError extends Error {\n constructor(\n message: string,\n public readonly status: number,\n ) {\n super(message);\n this.name = \"JevRequestError\";\n }\n}\n\nexport interface EvaluateWithJevOptions {\n /** Evaluation model id; defaults to the current public Jev model. */\n model?: string;\n abortSignal?: AbortSignal;\n headers?: Record<string, string>;\n}\n\n// ── wire format (OpenRouter decisions API) ──────────────────────────────────\n\ntype OpenRouterQuestion =\n | { type: \"noul\"; instructions: string; criteria: { true: string; false: string } }\n | { type: \"choice\"; instructions: string; criteria: Record<string, string> }\n | { type: \"score\"; instructions: string; criteria: readonly string[] };\n\ninterface OpenRouterAnswer {\n type: \"noul\" | \"choice\" | \"score\";\n noul?: number;\n choice?: string;\n score?: number;\n probabilities?: Record<string, number>;\n}\n\ninterface OpenRouterDecisionsResponse {\n model: string;\n answers: Record<string, OpenRouterAnswer>;\n usage?: { input_tokens?: number; output_tokens?: number; cost?: number };\n error?: { message: string; code?: number };\n}\n\nfunction toOpenRouterQuestion(question: JevQuestion): OpenRouterQuestion {\n if (question.type === \"boolean\") {\n return {\n type: \"noul\",\n instructions: question.instructions,\n criteria: { true: \"Yes\", false: \"No\" },\n };\n }\n return question;\n}\n\nfunction fromOpenRouterAnswer(answer: OpenRouterAnswer): JevAnswer<JevQuestion> {\n if (answer.type === \"noul\") {\n return { type: \"boolean\", probability: answer.noul ?? 0 };\n }\n if (answer.type === \"choice\") {\n return { type: \"choice\", choice: answer.choice ?? \"\", probabilities: answer.probabilities };\n }\n return { type: \"score\", score: answer.score ?? 0, probabilities: answer.probabilities };\n}\n\n/**\n * Evaluate one or more typed questions against a shared `state` using Jev,\n * reached through OpenRouter's decisions API. Every question is scored\n * independently against the same state in a single request; batching\n * questions here is cheap (they share the input) and is preferred over\n * separate calls per question.\n */\nexport async function evaluateWithJev<const QUESTIONS extends JevQuestions>(\n state: string,\n questions: QUESTIONS,\n options: EvaluateWithJevOptions = {},\n): Promise<JevResult<QUESTIONS>> {\n const apiKey = readEnv(\"OPENROUTER_API_KEY\");\n if (!apiKey) throw new JevNotConfiguredError();\n\n const wireQuestions: Record<string, OpenRouterQuestion> = {};\n for (const [id, question] of Object.entries(questions)) {\n wireQuestions[id] = toOpenRouterQuestion(question);\n }\n\n const response = await fetch(OPENROUTER_DECISIONS_URL, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${apiKey}`,\n \"Content-Type\": \"application/json\",\n ...options.headers,\n },\n body: JSON.stringify({\n model: options.model ?? JEV_MODEL_ID,\n state,\n questions: wireQuestions,\n }),\n signal: options.abortSignal,\n });\n\n const body = (await response.json()) as OpenRouterDecisionsResponse;\n if (!response.ok || body.error) {\n throw new JevRequestError(\n body.error?.message ?? `Jev request failed with status ${response.status}`,\n body.error?.code ?? response.status,\n );\n }\n\n const answers = {} as { [ID in keyof QUESTIONS]: JevAnswer<QUESTIONS[ID]> };\n for (const id of Object.keys(questions)) {\n const answer = body.answers[id];\n if (!answer) throw new JevRequestError(`Jev response missing answer for \"${id}\"`, 502);\n (answers as Record<string, JevAnswer<JevQuestion>>)[id] = fromOpenRouterAnswer(answer);\n }\n\n return {\n answers,\n usage: {\n inputTokens: body.usage?.input_tokens,\n outputTokens: body.usage?.output_tokens,\n cost: body.usage?.cost,\n },\n model: body.model,\n };\n}\n"]}
1
+ {"version":3,"file":"jev.js","sourceRoot":"","sources":["../../src/harness/jev.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Jev (typesafe/jev) client, backed by `@geminixiang/jev`.\n *\n * Jev is a \"System One\" evaluation model: it scores a shared `state`\n * against typed questions (a yes/no probability, a multiple choice, or a\n * position on an ordered scale) and returns calibrated probabilities. It\n * does not generate text or tool calls, so it cannot be driven through\n * pi-ai's `Provider`/`Model`/`stream()` contract the way every model\n * `models.ts` catalogs is — that contract is chat/completion shaped\n * (system prompt + messages in, streamed text/tool-call events out), and\n * Jev's typed-decision shape does not fit it. Jev is therefore not a\n * selectable chat provider: callers import `evaluateWithJev` directly at\n * whatever call site wants a fast, cheap, type-safe decision (e.g.\n * classification, routing, guardrails), and interpret the returned\n * probabilities themselves.\n *\n * `@geminixiang/jev` owns the wire protocol, provider catalog, and auth\n * resolution for four backends (TypeSafe, OpenRouter, Vercel AI Gateway,\n * Cloudflare Workers AI); this module picks OpenRouter (the same key\n * pi-ai's `openrouter` chat provider reads) and adapts its typed\n * request/answer shapes to the caller-facing contract mikan's call sites\n * already depend on, so this file is the only one that needs to know a\n * dependency swap happened.\n */\nimport {\n createBuiltinJevModels,\n JevAPIError,\n JevAuthError,\n JevConfigError,\n JevConnectionError,\n JevResponseError,\n JevTimeoutError,\n type AnswerFor as GeminixiangAnswerFor,\n type Entry as GeminixiangEntry,\n type Question as GeminixiangQuestion,\n} from \"@geminixiang/jev\";\nimport type { AuthContext } from \"@earendil-works/pi-ai\";\nimport { readEnv } from \"../env-manifest.js\";\n\n/** The current public Jev model id on OpenRouter. */\nexport const JEV_MODEL_ID = \"~typesafe/jev-latest\";\n\n/**\n * Any value Jev accepts as text-bearing structure: a string, a JSON object,\n * an array, or `null`. State, instructions, and criteria descriptions all\n * take this shape; Jev is trained to read the structure, so objects with\n * labelled keys are preferred over string templates.\n */\nexport type JevEntry = string | number | boolean | null | JevEntry[] | { [key: string]: JevEntry };\n\n/** A shared caller-facing question shape; translated to `@geminixiang/jev`'s shape below. */\nexport type JevQuestion =\n | {\n type: \"boolean\";\n instructions: JevEntry;\n /** Optional descriptions of the yes / no outcomes. */\n criteria?: { true?: JevEntry; false?: JevEntry };\n }\n | { type: \"choice\"; instructions: JevEntry; criteria: Record<string, JevEntry> }\n | { type: \"score\"; instructions: JevEntry; criteria: readonly JevEntry[] };\n\nexport type JevQuestions = Record<string, JevQuestion>;\n\nexport type JevAnswer<QUESTION extends JevQuestion> = QUESTION extends { type: \"choice\" }\n ? {\n type: \"choice\";\n choice: string;\n probabilities?: Record<string, number>;\n /** 0-1 statistic over `probabilities`; low means no option clearly fits. */\n confidence?: number;\n }\n : QUESTION extends { type: \"score\" }\n ? {\n type: \"score\";\n /** Expected level index; fractional between levels. */\n score: number;\n probabilities?: Record<string, number>;\n /** Level index -> criterion text, as echoed by Jev. */\n legend?: Record<string, string>;\n confidence?: number;\n }\n : { type: \"boolean\"; probability: number };\n\nexport interface JevResult<QUESTIONS extends JevQuestions> {\n readonly answers: { [ID in keyof QUESTIONS]: JevAnswer<QUESTIONS[ID]> };\n readonly usage: {\n inputTokens: number | undefined;\n outputTokens: number | undefined;\n cost: number | undefined;\n };\n readonly model: string;\n}\n\n/** Thrown when no Jev backend has a usable credential. Callers fail closed on this. */\nexport class JevNotConfiguredError extends Error {\n constructor() {\n super(\n \"Jev requires OPENROUTER_API_KEY (an OpenRouter API key). Set it in the environment before calling evaluateWithJev().\",\n );\n this.name = \"JevNotConfiguredError\";\n }\n}\n\nexport class JevRequestError extends Error {\n constructor(\n message: string,\n public readonly status: number,\n ) {\n super(message);\n this.name = \"JevRequestError\";\n }\n}\n\nexport interface EvaluateWithJevOptions {\n /** Evaluation model id; defaults to the current public Jev model. */\n model?: string;\n abortSignal?: AbortSignal;\n headers?: Record<string, string>;\n}\n\n// ── @geminixiang/jev wiring ──────────────────────────────────────────────\n\n/**\n * Routes env reads through `readEnv` so `MIKAN_OPENROUTER_API_KEY` resolves\n * the same as every other mikan-aliased credential, instead of the\n * package's default `process.env`-only context.\n */\nconst mikanAuthContext: AuthContext = {\n async env(name) {\n return readEnv(name);\n },\n async fileExists() {\n return false;\n },\n};\n\nlet cachedModels: ReturnType<typeof createBuiltinJevModels> | undefined;\n\nfunction models() {\n cachedModels ??= createBuiltinJevModels({ authContext: mikanAuthContext });\n return cachedModels;\n}\n\nfunction toGeminixiangQuestion(question: JevQuestion): GeminixiangQuestion {\n const instructions = question.instructions as GeminixiangEntry;\n if (question.type === \"boolean\") {\n // `@geminixiang/jev` leaves noul criteria optional; mikan has always sent\n // an explicit Yes/No default so the wire payload (and anything tuned\n // against it, e.g. the auto-reply gate) does not change under this swap.\n return {\n type: \"noul\",\n instructions,\n criteria: {\n true: (question.criteria?.true as GeminixiangEntry) ?? \"Yes\",\n false: (question.criteria?.false as GeminixiangEntry) ?? \"No\",\n },\n };\n }\n if (question.type === \"choice\") {\n return {\n type: \"choice\",\n instructions,\n criteria: question.criteria as Record<string, GeminixiangEntry>,\n };\n }\n const [first, second, ...rest] = question.criteria as GeminixiangEntry[];\n if (first === undefined || second === undefined) {\n throw new Error(\"score questions need at least two levels\");\n }\n return { type: \"score\", instructions, criteria: [first, second, ...rest] };\n}\n\nfunction fromGeminixiangAnswer(\n answer: GeminixiangAnswerFor<GeminixiangQuestion>,\n): JevAnswer<JevQuestion> {\n if (answer.type === \"noul\") {\n return { type: \"boolean\", probability: answer.noul };\n }\n if (answer.type === \"choice\") {\n return {\n type: \"choice\",\n choice: answer.choice,\n probabilities: answer.probabilities as Record<string, number>,\n confidence: answer.confidence,\n };\n }\n return {\n type: \"score\",\n score: answer.score,\n probabilities: answer.probabilities,\n legend: answer.legend as Record<string, string> | undefined,\n confidence: answer.confidence,\n };\n}\n\n/**\n * Evaluate one or more typed questions against a shared `state` using Jev,\n * reached through OpenRouter's decisions API by way of `@geminixiang/jev`.\n * Every question is scored independently against the same state in a\n * single request; batching questions here is cheap (they share the input)\n * and is preferred over separate calls per question.\n */\nexport async function evaluateWithJev<const QUESTIONS extends JevQuestions>(\n state: JevEntry,\n questions: QUESTIONS,\n options: EvaluateWithJevOptions = {},\n): Promise<JevResult<QUESTIONS>> {\n const catalogModel = models().getModel(\"openrouter\", \"jev-latest\");\n if (!catalogModel) throw new JevNotConfiguredError();\n // `options.model` is the wire model id callers already pass (e.g. from\n // JEV_MODEL_ID); override the catalog slug directly rather than adding a\n // second catalog entry per possible override.\n const model = options.model ? { ...catalogModel, slug: options.model } : catalogModel;\n\n const wireQuestions: Record<string, GeminixiangQuestion> = {};\n for (const [id, question] of Object.entries(questions)) {\n wireQuestions[id] = toGeminixiangQuestion(question);\n }\n\n let result;\n try {\n result = await models().evaluate(\n model,\n { state: state as GeminixiangEntry, questions: wireQuestions },\n {\n ...(options.headers ? { headers: options.headers } : {}),\n ...(options.abortSignal ? { signal: options.abortSignal } : {}),\n },\n );\n } catch (error) {\n if (error instanceof JevAuthError || error instanceof JevConfigError) {\n throw new JevNotConfiguredError();\n }\n if (error instanceof JevAPIError) {\n throw new JevRequestError(error.message, error.status);\n }\n if (\n error instanceof JevResponseError ||\n error instanceof JevConnectionError ||\n error instanceof JevTimeoutError\n ) {\n throw new JevRequestError(error.message, 502);\n }\n throw error;\n }\n\n const answers = {} as { [ID in keyof QUESTIONS]: JevAnswer<QUESTIONS[ID]> };\n for (const id of Object.keys(questions)) {\n const answer = result.answers[id];\n if (!answer) throw new JevRequestError(`Jev response missing answer for \"${id}\"`, 502);\n (answers as Record<string, JevAnswer<JevQuestion>>)[id] = fromGeminixiangAnswer(answer);\n }\n\n return {\n answers,\n usage: {\n inputTokens: result.usage.input,\n outputTokens: result.usage.output,\n cost: result.usage.cost.total,\n },\n model: result.model,\n };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"presenter.d.ts","sourceRoot":"","sources":["../../src/harness/presenter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,GAAG,EAAE,KAAK,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAC1E,OAAO,KAAK,EAEV,eAAe,EACf,kBAAkB,EAClB,kBAAkB,EACnB,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAOtD,OAAO,KAAK,EAAE,qBAAqB,EAA4B,MAAM,aAAa,CAAC;AACnF,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,sBAAsB,CAAC;AA+DxE,wBAAgB,cAAc,IAAI,kBAAkB,CAkCnD;AAED,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,EAAE;IACP,SAAS,EAAE,qBAAqB,CAAC;IACjC,mBAAmB,EAAE,MAAM,CAAC;IAC5B,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,kBAAkB,EAAE,MAAM,GAAG,SAAS,CAAC;CACxC,GACA,eAAe,CAsEjB;AAOD,wBAAgB,yBAAyB,CAAC,kBAAkB,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAEzF;AAmKD,wBAAsB,mBAAmB,CACvC,SAAS,EAAE,qBAAqB,EAChC,OAAO,EAAE,iBAAiB,EAC1B,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,CAAC,EAAE;IACR,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,kBAAkB,CAAC,EAAE,MAAM,MAAM,CAAC;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IACnB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,GACA,OAAO,CAAC,IAAI,CAAC,CAsBf;AAsFD,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAiG/E;AAucD,wBAAgB,0BAA0B,CAAC,MAAM,EAAE;IACjD,OAAO,EAAE,iBAAiB,CAAC;IAC3B,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IAClB,WAAW,EAAE,UAAU,CAAC,OAAO,2BAA2B,CAAC,CAAC;CAC7D,GAAG,IAAI,CAiBP"}
1
+ {"version":3,"file":"presenter.d.ts","sourceRoot":"","sources":["../../src/harness/presenter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,GAAG,EAAE,KAAK,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAC1E,OAAO,KAAK,EAEV,eAAe,EACf,kBAAkB,EAClB,kBAAkB,EACnB,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAOtD,OAAO,KAAK,EAAE,qBAAqB,EAA4B,MAAM,aAAa,CAAC;AACnF,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,sBAAsB,CAAC;AA+DxE,wBAAgB,cAAc,IAAI,kBAAkB,CAkCnD;AAED,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,EAAE;IACP,SAAS,EAAE,qBAAqB,CAAC;IACjC,mBAAmB,EAAE,MAAM,CAAC;IAC5B,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,kBAAkB,EAAE,MAAM,GAAG,SAAS,CAAC;CACxC,GACA,eAAe,CAsEjB;AAOD,wBAAgB,yBAAyB,CAAC,kBAAkB,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAEzF;AAqKD,wBAAsB,mBAAmB,CACvC,SAAS,EAAE,qBAAqB,EAChC,OAAO,EAAE,iBAAiB,EAC1B,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,CAAC,EAAE;IACR,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,kBAAkB,CAAC,EAAE,MAAM,MAAM,CAAC;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IACnB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,GACA,OAAO,CAAC,IAAI,CAAC,CAsBf;AAsFD,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAiG/E;AAucD,wBAAgB,0BAA0B,CAAC,MAAM,EAAE;IACjD,OAAO,EAAE,iBAAiB,CAAC;IAC3B,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IAClB,WAAW,EAAE,UAAU,CAAC,OAAO,2BAA2B,CAAC,CAAC;CAC7D,GAAG,IAAI,CAiBP"}
@@ -156,9 +156,10 @@ export function isEventTriggerAttribution(triggerAttribution) {
156
156
  }
157
157
  function extractToolLabel(toolName, args) {
158
158
  const label = args?.label;
159
- if (typeof label !== "string")
160
- return toolName;
161
- return label.trim() || toolName;
159
+ const text = typeof label === "string" ? label.trim() || toolName : toolName;
160
+ // Jev judgments are calibrated probabilities, not the model's own guess;
161
+ // name the tool so the user can tell which steps came from it.
162
+ return toolName === "jev" ? `jev · ${text}` : text;
162
163
  }
163
164
  /** Non-subagent tool activity lines; subagent calls render as the dashboard. */
164
165
  function formatToolProgress(runState) {