toolnexus 0.25.0 → 0.26.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/dist/client.d.ts CHANGED
@@ -208,6 +208,12 @@ export type MetricEvent = {
208
208
  promptTokens: number;
209
209
  completionTokens: number;
210
210
  error?: string;
211
+ /** L1: how many questions were asked. */
212
+ questions: number;
213
+ /** L1: sha256 hex of `canonicalRequest(model, questions)` ("" when unencodable). */
214
+ questionsHash: string;
215
+ /** True when the decision came from the classifier cache and no request was sent. */
216
+ cached: boolean;
211
217
  } | {
212
218
  event: "classifier.warning";
213
219
  question: string;
package/dist/judge.d.ts CHANGED
@@ -7,12 +7,12 @@
7
7
  *
8
8
  * A gate never authorises; it only declines to decide.
9
9
  */
10
- import { Classifier, Decision, type DecisionAnswer, type NoulCriteria, type Question, type RecordedDecision } from "./classifier.js";
10
+ import { Classifier, Decision, type DecisionAnswer, type NoulCriteria, type AnyQuestion, type RecordedDecision } from "./classifier.js";
11
11
  import type { Request } from "./types.js";
12
12
  /** One named question of an ordered question list. */
13
13
  export interface NamedQuestion {
14
14
  name: string;
15
- question: Question;
15
+ question: AnyQuestion;
16
16
  }
17
17
  /** `noul` question with a name. Name the state field it judges in `instructions`. */
18
18
  export declare function noul(name: string, instructions: string, criteria?: NoulCriteria): NamedQuestion;
@@ -20,8 +20,11 @@ export declare function noul(name: string, instructions: string, criteria?: Noul
20
20
  export declare function choice(name: string, instructions: string, options: Record<string, string>): NamedQuestion;
21
21
  /** `score` question with a name; `levels` is the ordered rubric. */
22
22
  export declare function score(name: string, instructions: string, levels: string[]): NamedQuestion;
23
+ /** A raw question with a name: a System One type the library does not model (e.g. `box`), sent
24
+ * as written. Its answer decodes to a raw answer `{type, value}`. */
25
+ export declare function raw(name: string, type: string, instructions: string, criteria?: unknown): NamedQuestion;
23
26
  /** Ordered list -> the §8B questions map. A duplicate name is an error naming it. */
24
- export declare function questionMap(questions: readonly NamedQuestion[]): Record<string, Question>;
27
+ export declare function questionMap(questions: readonly NamedQuestion[]): Record<string, AnyQuestion>;
25
28
  /** `role` next to the data's fields; a non-object value goes under `data`. */
26
29
  export declare function State(role: string, data: unknown): Record<string, unknown>;
27
30
  /** Sugar: `{context, message, ...extra}`. */
package/dist/judge.js CHANGED
@@ -7,7 +7,7 @@
7
7
  *
8
8
  * A gate never authorises; it only declines to decide.
9
9
  */
10
- import { Classifier, noul as noulQ, choice as choiceQ, score as scoreQ, } from "./classifier.js";
10
+ import { Classifier, isRawAnswer, raw as rawQ, noul as noulQ, choice as choiceQ, score as scoreQ, } from "./classifier.js";
11
11
  /** `noul` question with a name. Name the state field it judges in `instructions`. */
12
12
  export function noul(name, instructions, criteria) {
13
13
  return { name, question: noulQ(instructions, criteria) };
@@ -20,6 +20,11 @@ export function choice(name, instructions, options) {
20
20
  export function score(name, instructions, levels) {
21
21
  return { name, question: scoreQ(instructions, levels) };
22
22
  }
23
+ /** A raw question with a name: a System One type the library does not model (e.g. `box`), sent
24
+ * as written. Its answer decodes to a raw answer `{type, value}`. */
25
+ export function raw(name, type, instructions, criteria) {
26
+ return { name, question: rawQ(type, instructions, criteria) };
27
+ }
23
28
  /** Ordered list -> the §8B questions map. A duplicate name is an error naming it. */
24
29
  export function questionMap(questions) {
25
30
  const out = {};
@@ -47,6 +52,13 @@ function noulBand(p, b) {
47
52
  return p < b.low ? "no" : p > b.high ? "yes" : "uncertain";
48
53
  }
49
54
  function wrap(a, b) {
55
+ if (isRawAnswer(a)) {
56
+ // An unmodelled answer type is never sure: there is no number to band.
57
+ const out = { ...a, sure: false };
58
+ Object.defineProperty(out, "value", { value: () => 0, enumerable: false });
59
+ Object.defineProperty(out, "pick", { value: () => "", enumerable: false });
60
+ return out;
61
+ }
50
62
  const extra = a.type === "noul"
51
63
  ? { band: noulBand(a.noul, b) }
52
64
  : { sure: a.confidence > b.high && !(a.type === "choice" && a.nearUniform) };
package/dist/serve.js CHANGED
@@ -153,6 +153,13 @@ export async function startA2AServer(opts) {
153
153
  // per request (stateless — sessionIdGenerator undefined). Checked first so
154
154
  // `/mcp` is never shadowed by the A2A POST handler.
155
155
  if (mcp && req.url && (req.url === "/mcp" || req.url.startsWith("/mcp?"))) {
156
+ // Stateless profile: no server-initiated SSE stream exists, so only POST is
157
+ // served. Refuse anything else at once (§7C) instead of holding a GET open.
158
+ if (req.method !== "POST") {
159
+ res.writeHead(405, { Allow: "POST" });
160
+ res.end();
161
+ return;
162
+ }
156
163
  void (async () => {
157
164
  const body = req.method === "POST" ? await readBody(req) : "";
158
165
  const parsed = body ? safeJson(body) : undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toolnexus",
3
- "version": "0.25.0",
3
+ "version": "0.26.0",
4
4
  "description": "Dynamic MCP servers + agent skills as uniform tools for any LLM (opencode-style).",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -17,7 +17,7 @@
17
17
  ],
18
18
  "scripts": {
19
19
  "build": "tsc -p tsconfig.json",
20
- "test": "npm run build && node --experimental-strip-types --test test/unit.test.ts test/agents.test.ts test/agent-hooks.test.ts test/agent-inprocess.test.ts test/home.test.ts test/compaction.test.ts test/loop.test.ts test/in-process.test.ts test/translate.test.ts test/multimodal.test.ts test/classifier.test.ts test/acp.test.ts test/acp-client.test.ts test/issues.test.ts test/host-boundary.test.ts test/judge.test.ts test/batteries.test.ts test/host-engine.test.ts test/host-turn-durability.test.ts test/host-engine-asks.test.ts test/tool-concurrency.test.ts test/media.test.ts test/examples.test.ts test/mcphandler.test.ts test/mcphandler-conformance.test.ts test/mcp2026.test.ts",
20
+ "test": "npm run build && node --experimental-strip-types --test test/unit.test.ts test/agents.test.ts test/agent-hooks.test.ts test/agent-inprocess.test.ts test/home.test.ts test/compaction.test.ts test/loop.test.ts test/in-process.test.ts test/translate.test.ts test/multimodal.test.ts test/classifier.test.ts test/classifier-backends.test.ts test/acp.test.ts test/acp-client.test.ts test/issues.test.ts test/host-boundary.test.ts test/judge.test.ts test/batteries.test.ts test/host-engine.test.ts test/host-turn-durability.test.ts test/host-engine-asks.test.ts test/tool-concurrency.test.ts test/media.test.ts test/examples.test.ts test/mcphandler.test.ts test/mcphandler-conformance.test.ts test/mcp2026.test.ts test/serve-mcp-conformance.test.ts",
21
21
  "example": "node --experimental-strip-types examples/basic.ts",
22
22
  "example:judge": "node --experimental-strip-types examples/judge.ts",
23
23
  "example:multimodal": "node --experimental-strip-types examples/multimodal.ts",