@vincemakes/kiso-runtime 0.15.3 → 0.15.5

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/store.d.ts CHANGED
@@ -66,6 +66,20 @@ export interface SessionMeta {
66
66
  readonly createdAt: number;
67
67
  readonly updatedAt: number;
68
68
  }
69
+ /**
70
+ * The session's title — its first substantive prompt.
71
+ *
72
+ * Exported on the INTERNAL entry (never the public root: this is a
73
+ * projection helper, not an API the runtime wants to support forever —
74
+ * and adding it to the root surface is a ritual the api-surface gate
75
+ * rightly refused). It has two consumers and may only have one
76
+ * definition: `list()` below, which is what `kiso sessions` prints, and
77
+ * the CLI's session-card projection, which is what the resume PICKER
78
+ * shows. They were allowed to differ once — the listing got the title
79
+ * and the picker kept showing bare ids, so the fix reached the surface
80
+ * nobody uses and missed the one everybody does.
81
+ */
82
+ export declare function sessionTitle(records: readonly StoreRecord[]): string;
69
83
  export declare class SessionStore {
70
84
  #private;
71
85
  readonly root: string;
package/dist/store.js CHANGED
@@ -62,6 +62,54 @@ export class StaleWriterError extends Error {
62
62
  }
63
63
  /** Session ids become file names — keep them host-safe. */
64
64
  const ID_PATTERN = /^[A-Za-z0-9._-]+$/;
65
+ /** REL-0152-D6 — a session is named by its first REQUEST, not its first
66
+ * word.
67
+ *
68
+ * This used to take `records[0]` unconditionally, so a session was
69
+ * titled by whatever the user opened with. Three of the owner's five
70
+ * sessions opened with a greeting, and the picker showed three
71
+ * identical rows separated only by a timestamp they had to decode.
72
+ * RD1B-F9 made ids unique; unique is not meaningful.
73
+ *
74
+ * A greeting is skipped only when something follows it — a session that
75
+ * is genuinely nothing but "hello" is still titled "hello", because the
76
+ * alternative is naming it "(no prompt)" when a prompt plainly exists.
77
+ * No model call: this is a scan of the turns already on disk. */
78
+ /** A greeting is recognised by SHAPE, not by a word list: it is a
79
+ * handful of characters with nothing asked in them. Counting code
80
+ * points (never UTF-16 units) makes the rule the same in every
81
+ * language — a two-character greeting is two characters whatever
82
+ * script it is written in — and it keeps a word list out of a tree
83
+ * that is required to be English. The ASCII pattern catches the few
84
+ * openers long enough to pass the length test but still empty of a
85
+ * request. */
86
+ const MIN_TITLE_CHARS = 4;
87
+ const ASCII_OPENER = /^\s*(hi|hey|hello|yo|hiya|howdy|ping|test|ok|okay|thanks|thx)[\s!.,?~]*$/i;
88
+ const isOpener = (t) => [...t].length < MIN_TITLE_CHARS || ASCII_OPENER.test(t);
89
+ /**
90
+ * The session's title — its first substantive prompt.
91
+ *
92
+ * Exported on the INTERNAL entry (never the public root: this is a
93
+ * projection helper, not an API the runtime wants to support forever —
94
+ * and adding it to the root surface is a ritual the api-surface gate
95
+ * rightly refused). It has two consumers and may only have one
96
+ * definition: `list()` below, which is what `kiso sessions` prints, and
97
+ * the CLI's session-card projection, which is what the resume PICKER
98
+ * shows. They were allowed to differ once — the listing got the title
99
+ * and the picker kept showing bare ids, so the fix reached the surface
100
+ * nobody uses and missed the one everybody does.
101
+ */
102
+ export function sessionTitle(records) {
103
+ const asked = records
104
+ .map((r) => r.event)
105
+ .filter((e) => e.type === "user_input" && typeof e.content === "string")
106
+ .map((e) => e.content.trim())
107
+ .filter((t) => t !== "");
108
+ if (asked.length === 0)
109
+ return "(no prompt)";
110
+ const substantive = asked.find((t) => !isOpener(t));
111
+ return (substantive ?? asked[0]).slice(0, 60);
112
+ }
65
113
  export class SessionStore {
66
114
  root;
67
115
  #fds = new Map();
@@ -293,12 +341,9 @@ export class SessionStore {
293
341
  const records = this.load(id);
294
342
  if (records.length === 0)
295
343
  continue;
296
- const first = records[0].event;
297
344
  metas.push({
298
345
  id,
299
- title: first.type === "user_input" && typeof first.content === "string"
300
- ? first.content.slice(0, 60)
301
- : "(no prompt)",
346
+ title: sessionTitle(records),
302
347
  events: records.length,
303
348
  runs: new Set(records.map((r) => r.runId)).size,
304
349
  createdAt: records[0]?.ts ?? 0,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vincemakes/kiso-runtime",
3
- "version": "0.15.3",
3
+ "version": "0.15.5",
4
4
  "description": "kiso runtime \u2014 durable multi-turn agent sessions: AgentDefinition, AgentRuntime, AgentSession, Run, append-only JSONL store.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -25,11 +25,11 @@
25
25
  "test": "vitest run"
26
26
  },
27
27
  "dependencies": {
28
- "@vincemakes/kiso-core": "0.15.3"
28
+ "@vincemakes/kiso-core": "0.15.5"
29
29
  },
30
30
  "peerDependencies": {
31
- "@vincemakes/kiso-provider-anthropic": "0.15.3",
32
- "@vincemakes/kiso-provider-openai": "0.15.3"
31
+ "@vincemakes/kiso-provider-anthropic": "0.15.5",
32
+ "@vincemakes/kiso-provider-openai": "0.15.5"
33
33
  },
34
34
  "peerDependenciesMeta": {
35
35
  "@vincemakes/kiso-provider-anthropic": {
@@ -40,7 +40,7 @@
40
40
  }
41
41
  },
42
42
  "devDependencies": {
43
- "@vincemakes/kiso-evals": "0.15.3",
43
+ "@vincemakes/kiso-evals": "0.15.5",
44
44
  "@types/node": "^26.1.2",
45
45
  "typescript": "^5.7.2",
46
46
  "vitest": "^3.0.0"