@comchatx/cx-topics 0.1.3 → 0.1.4

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/README.md CHANGED
@@ -1,4 +1,4 @@
1
- Names your conversations and retrieves the messages you quote. cx-topics is one pi extension: it titles the seller's work itself, on the platform's default model and on Jev's verdict on each seller message, and gives cx one tool, `quoted`, that reads a message the seller quoted.
1
+ Names your conversations and retrieves the messages you quote. cx-topics is one pi extension: it titles the seller's work itself, on the model the seller is chatting on and on Jev's verdict on each seller message, and gives cx one tool, `quoted`, that reads a message the seller quoted.
2
2
 
3
3
  ## The titles
4
4
 
@@ -19,7 +19,7 @@ A title is a pi label on a seller message: a new topic's on its first message, a
19
19
 
20
20
  **What Jev is shown.** The topic in progress, from pi's branch: its title and the seller's own words in it, `<quote>` markers taken out, and the message judged; none of cx's words, tool calls, thinking or images. Past 31 seller messages, the newest 31 (`ROUTING_LIMITS.sellerMessages`). Each message over 512 code points and the latest over 2,048 keep their head and tail around `\n[… N characters omitted …]\n`. The topic's length sets how sure Jev must be that a message continues it: in a topic of two seller messages or fewer an even answer continues it, from eight on the confident 0.8, and between the two the bar rises evenly (`ROUTING_LIMITS`). The largest request Jev is sent is 109,260 bytes.
21
21
 
22
- **The models.** Jev by pi's catalogue, `typesafe` `jev-latest` (`JEV`); the title model `balance` `minimax/minimax-m3` (`TITLE_MODEL`), named by the package rather than read from pi's settings, which hold the seller's own chat model. The title call goes through pi's nested call (`modelRegistry.complete`) with no reasoning, no tools, at most 128 output tokens and no cache retention, within 20,000 ms. The package holds no address or key: where pi has no Jev configured nothing is asked.
22
+ **The models.** Jev by pi's catalogue, `typesafe` `jev-latest` (`JEV`). The title model is the one the seller is chatting on, read from pi (`ctx.model`) at each title call, so a model the seller switches to writes the next title. A title is the seller's, written by the model they chose (Jack, 2026-10-28), and the package names no model of its own and offers no setting for one. On a physical model the title call (`modelRegistry.complete`) asks at most 128 output tokens with no thinking, no tools and no cache retention, within 20,000 ms. On a virtual model the seller selected, pi routes it (`modelRegistry.streamSimple`, with the same options) and the thinking level the seller's router returns applies, pi adding its budget to the 128: that is the seller's own configuration, billed as their chat is, since pi 1.4.0 offers an extension no public way to route a request and then bound it. The package holds no address or key: where pi has no Jev configured nothing is asked, and where the session has no model, or pi holds no key or sign-in for it, no title is written and the pass stops at that message as on a refused call.
23
23
 
24
24
  **Failures write nothing.** Only a result pi ends with `stop` that answers every question is a verdict, and only an answer pi ends with `stop` is a title. A call that was answered, or a request refused as it is (`400`, `413`, `422`, or a `403` without one of the gate's codes), moves on to the next message; a call that failed otherwise stops the pass at that message, to ask it again at the next seller message or the next load, never in a loop. No call is retried.
25
25
 
@@ -44,17 +44,17 @@ one marker per quoted message or selection, before the seller's own words. Quote
44
44
  ## How it is built
45
45
 
46
46
  - `src/core/` holds the pure parts — the protocol of Jev's questions, the routing bars and the title's cut — and imports nothing but itself.
47
- - `src/extensions/topics.ts` is the one extension; `judge.ts` asks and writes, `pass.ts` walks the branch. `npm run build` bundles `topics.ts` into `dist/extensions/topics.js`, pi's own packages left for pi to supply; the package ships that file alone. The built module also exports `INSTALLS_FILE`, `installKey`, `JEV` and `TITLE_MODEL`, for a host to hold the package to.
47
+ - `src/extensions/topics.ts` is the one extension; `judge.ts` asks and writes, `pass.ts` walks the branch. `npm run build` bundles `topics.ts` into `dist/extensions/topics.js`, pi's own packages left for pi to supply; the package ships that file alone. The built module also exports `INSTALLS_FILE`, `installKey` and `JEV`, for a host to hold the package to.
48
48
  - `package.json` declares no `type`. pi's loader then reads the bundle at the path pi lists the `quoted` tool's source by; declared a module, it is loaded natively and knows itself by its real path, which behind a symlink is not the path pi lists, so the package would find no key of its own and miss an install.
49
49
 
50
50
  ## Tests
51
51
 
52
52
  - `test/core/` — the protocol, the routing bars against a fixed comparison (`test/support/routing-comparison.json`), the title's cut, and that the core imports nothing else and the bundle carries the live files alone.
53
53
  - `test/extensions/` — the title and the quote reading on pi's own session shapes, Jev's verdict and the titles, and the pass: an install's clears and its judging from the first, carrying on, a branch left, one pass at a time, a reload's cut call asked again, and the key read from pi's tool list.
54
- - `test/support/replay.test.ts` and `replay-main.test.ts` — the calibration replay on a stand-in through pi's own registry, with no key.
54
+ - `test/support/replay.test.ts` and `replay-main.test.ts` — the calibration replay on a stand-in through pi's own registry, with no key: the title request names the session's model, a model switched between two messages titles the second, a virtual model is routed to its physical model, the output tokens and thinking a physical and a routed request carry, and a model pi holds no key for is asked nothing.
55
55
  - `test/package.test.ts` — the pinned pi loads the built extension with its tool and handlers; the built module exports what a host holds it to; the package ships the built extension alone.
56
56
 
57
- **The calibration set.** `test/support/calibration-cases.ts`, replayed by hand by `test/support/replay-main.ts`, bundled with esbuild as `replay-main.test.ts` does (`--live --keys <env file>`): each seller message judged by the package's own judge on pi's registry, against TypeSafe and OpenRouter on development keys read from an env file by name, and held to the outcome wanted — a new topic, a continuation with its title standing or a rewrite asked. With no key it runs on a stand-in.
57
+ **The calibration set.** `test/support/calibration-cases.ts`, replayed by hand by `test/support/replay-main.ts`, bundled with esbuild as `replay-main.test.ts` does (`--live --keys <env file>`): each seller message judged by the package's own judge on pi's registry, against TypeSafe and OpenRouter on development keys read from an env file by name, and held to the outcome wanted — a new topic, a continuation with its title standing or a rewrite asked. With no key it runs on a stand-in. The set was measured with the session on `balance` `minimax/minimax-m3`, the platform's default (`CALIBRATED`); Jev's verdicts do not depend on the session's model, while a title's wording now varies with the model the seller chats on.
58
58
 
59
59
  ## Release
60
60
 
@@ -214,7 +214,7 @@ function titlePrompt(request) {
214
214
 
215
215
  // src/extensions/judge.ts
216
216
  var JEV = Object.freeze({ provider: "typesafe", model: "jev-latest" });
217
- var TITLE_MODEL = Object.freeze({ provider: "balance", model: "minimax/minimax-m3" });
217
+ var VIRTUAL = "pi-virtual";
218
218
  var CALL_MS = 2e4;
219
219
  var isSellers = (entry) => entry.type === "message" && entry.message.role === "user";
220
220
  var QUOTE = /<quote\b[^>]*>[\s\S]*?<\/quote>/g;
@@ -260,13 +260,12 @@ var asker = (ctx) => async (state, questions, signal) => {
260
260
  return ctx.modelRegistry.classify(jev, { state, questions }, { maxRetries: 0, timeoutMs: CALL_MS, signal });
261
261
  };
262
262
  var titler = (ctx) => async (request) => {
263
- const model = ctx.modelRegistry.find(TITLE_MODEL.provider, TITLE_MODEL.model);
263
+ const model = ctx.model;
264
264
  if (model === void 0) return { failure: "no-model" };
265
265
  try {
266
- const answer = await ctx.modelRegistry.complete(model, {
267
- systemPrompt: TITLE_SYSTEM,
268
- messages: [{ role: "user", content: [{ type: "text", text: titlePrompt(request) }], timestamp: Date.now() }]
269
- }, { maxTokens: ROUTING_LIMITS.titleTokens, signal: AbortSignal.timeout(ROUTING_LIMITS.titleMs), cacheRetention: "none" });
266
+ const context = { systemPrompt: TITLE_SYSTEM, messages: [{ role: "user", content: [{ type: "text", text: titlePrompt(request) }], timestamp: Date.now() }] };
267
+ const options = { maxTokens: ROUTING_LIMITS.titleTokens, signal: AbortSignal.timeout(ROUTING_LIMITS.titleMs), cacheRetention: "none" };
268
+ const answer = model.api === VIRTUAL ? await ctx.modelRegistry.streamSimple(model, context, options).result() : await ctx.modelRegistry.complete(model, context, options);
270
269
  if (answer.stopReason !== "stop") return { failure: failureOf(answer) };
271
270
  return { text: answer.content.flatMap((block) => block.type === "text" ? [block.text] : []).join(" ") };
272
271
  } catch {
@@ -542,7 +541,6 @@ export {
542
541
  JEV,
543
542
  QUOTED_CHARS,
544
543
  TITLE_CHARS,
545
- TITLE_MODEL,
546
544
  TITLE_WORDS,
547
545
  topics as default,
548
546
  installKey,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@comchatx/cx-topics",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Names your conversations and retrieves the messages you quote.",
5
5
  "license": "MIT",
6
6
  "author": "ComChatX",
@@ -33,7 +33,10 @@
33
33
  "line": "hash"
34
34
  },
35
35
  "section": "extensions",
36
- "order": 100
36
+ "order": 100,
37
+ "builds": [
38
+ "jev"
39
+ ]
37
40
  },
38
41
  "peerDependencies": {
39
42
  "@earendil-works/pi-coding-agent": "*"