noumen 0.5.0 → 0.6.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/a2a/index.d.ts +4 -4
- package/dist/acp/index.d.ts +4 -4
- package/dist/{agent-C3eDRsxs.d.ts → agent-DWE4_P5X.d.ts} +179 -6
- package/dist/{cache-DsRqxx6v.d.ts → cache-BlBwXXPS.d.ts} +1 -1
- package/dist/{chunk-WPCYGZOE.js → chunk-6MMYCGJQ.js} +325 -16
- package/dist/chunk-6MMYCGJQ.js.map +1 -0
- package/dist/{chunk-WTLK2ZAR.js → chunk-7IQCQI2G.js} +1 -1
- package/dist/{chunk-L3L3FG5T.js → chunk-CCM2AXZG.js} +1 -1
- package/dist/{chunk-L3L3FG5T.js.map → chunk-CCM2AXZG.js.map} +1 -1
- package/dist/{chunk-CS6WNDCF.js → chunk-I3JTUFPK.js} +2 -2
- package/dist/chunk-I3JTUFPK.js.map +1 -0
- package/dist/{chunk-EKOGVTBT.js → chunk-ZXSDKBYB.js} +4 -2
- package/dist/chunk-ZXSDKBYB.js.map +1 -0
- package/dist/cli/index.js +6 -6
- package/dist/client/index.d.ts +1 -1
- package/dist/index.d.ts +16 -9
- package/dist/index.js +17 -3
- package/dist/lsp/index.d.ts +3 -3
- package/dist/mcp/index.d.ts +4 -4
- package/dist/{provider-factory-KI7OZUY3.js → provider-factory-TUHU3DIG.js} +2 -2
- package/dist/providers/anthropic.d.ts +3 -3
- package/dist/providers/anthropic.js +4 -3
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/bedrock.d.ts +3 -3
- package/dist/providers/bedrock.js +2 -2
- package/dist/providers/bedrock.js.map +1 -1
- package/dist/providers/gemini.d.ts +2 -2
- package/dist/providers/gemini.js +1 -1
- package/dist/providers/gemini.js.map +1 -1
- package/dist/providers/ollama.d.ts +1 -1
- package/dist/providers/ollama.js +2 -2
- package/dist/providers/openai.d.ts +2 -2
- package/dist/providers/openai.js +2 -2
- package/dist/providers/openrouter.d.ts +1 -1
- package/dist/providers/openrouter.js +2 -2
- package/dist/providers/vertex.d.ts +3 -3
- package/dist/providers/vertex.js +4 -3
- package/dist/providers/vertex.js.map +1 -1
- package/dist/{resolve-GDSHNMG6.js → resolve-6KUZNEYW.js} +2 -2
- package/dist/server/index.d.ts +4 -4
- package/dist/{server-Cu9gv1dk.d.ts → server-BzNGKTP6.d.ts} +1 -1
- package/dist/{types-BA87bHPV.d.ts → types-DhXwOQwD.d.ts} +1 -1
- package/dist/{types-LrU4LRmX.d.ts → types-kiGBF35b.d.ts} +40 -2
- package/package.json +1 -1
- package/dist/chunk-CS6WNDCF.js.map +0 -1
- package/dist/chunk-EKOGVTBT.js.map +0 -1
- package/dist/chunk-WPCYGZOE.js.map +0 -1
- /package/dist/{chunk-WTLK2ZAR.js.map → chunk-7IQCQI2G.js.map} +0 -0
- /package/dist/{provider-factory-KI7OZUY3.js.map → provider-factory-TUHU3DIG.js.map} +0 -0
- /package/dist/{resolve-GDSHNMG6.js.map → resolve-6KUZNEYW.js.map} +0 -0
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
} from "./chunk-HEQQQGK5.js";
|
|
16
16
|
import {
|
|
17
17
|
ChatStreamError
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-CCM2AXZG.js";
|
|
19
19
|
import {
|
|
20
20
|
generateUUID
|
|
21
21
|
} from "./chunk-3HEYCV26.js";
|
|
@@ -316,6 +316,127 @@ function LocalSandbox(opts) {
|
|
|
316
316
|
};
|
|
317
317
|
}
|
|
318
318
|
|
|
319
|
+
// src/session/auto-title.ts
|
|
320
|
+
var DEFAULT_AUTO_TITLE_MAX_INPUT_CHARS = 2e3;
|
|
321
|
+
var DEFAULT_AUTO_TITLE_SYSTEM_PROMPT = `Generate a concise, sentence-case title (3-7 words) that captures the main topic or goal of this coding session. The title should be clear enough that the user recognizes the session in a list. Use sentence case: capitalize only the first word and proper nouns.
|
|
322
|
+
|
|
323
|
+
Return JSON with a single "title" field.
|
|
324
|
+
|
|
325
|
+
Good examples:
|
|
326
|
+
{"title": "Fix login button on mobile"}
|
|
327
|
+
{"title": "Add OAuth authentication"}
|
|
328
|
+
{"title": "Debug failing CI tests"}
|
|
329
|
+
{"title": "Refactor API client error handling"}
|
|
330
|
+
|
|
331
|
+
Bad (too vague): {"title": "Code changes"}
|
|
332
|
+
Bad (too long): {"title": "Investigate and fix the issue where the login button does not respond on mobile devices"}
|
|
333
|
+
Bad (wrong case): {"title": "Fix Login Button On Mobile"}`;
|
|
334
|
+
function extractTitleSeedText(messages, maxChars = DEFAULT_AUTO_TITLE_MAX_INPUT_CHARS) {
|
|
335
|
+
const parts = [];
|
|
336
|
+
for (const msg of messages) {
|
|
337
|
+
if (msg.role !== "user" && msg.role !== "assistant") continue;
|
|
338
|
+
const content = msg.content;
|
|
339
|
+
if (content === null || content === void 0) continue;
|
|
340
|
+
if (typeof content === "string") {
|
|
341
|
+
if (content.trim()) parts.push(content);
|
|
342
|
+
continue;
|
|
343
|
+
}
|
|
344
|
+
if (Array.isArray(content)) {
|
|
345
|
+
for (const block of content) {
|
|
346
|
+
if (block.type === "text" && typeof block.text === "string") {
|
|
347
|
+
if (block.text.trim()) parts.push(block.text);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
const text = parts.join("\n").trim();
|
|
353
|
+
if (text.length <= maxChars) return text;
|
|
354
|
+
return text.slice(-maxChars);
|
|
355
|
+
}
|
|
356
|
+
function extractTitleFromResponse(raw) {
|
|
357
|
+
if (!raw) return null;
|
|
358
|
+
const trimmed = raw.trim();
|
|
359
|
+
const parseTitle = (s) => {
|
|
360
|
+
let obj;
|
|
361
|
+
try {
|
|
362
|
+
obj = JSON.parse(s);
|
|
363
|
+
} catch {
|
|
364
|
+
return { kind: "none" };
|
|
365
|
+
}
|
|
366
|
+
if (obj && typeof obj === "object" && "title" in obj) {
|
|
367
|
+
const v = obj.title;
|
|
368
|
+
if (typeof v === "string") {
|
|
369
|
+
const t = v.trim();
|
|
370
|
+
if (t) return { kind: "ok", title: t };
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
return { kind: "empty" };
|
|
374
|
+
};
|
|
375
|
+
const whole = parseTitle(trimmed);
|
|
376
|
+
if (whole.kind === "ok") return whole.title;
|
|
377
|
+
if (whole.kind === "empty") return null;
|
|
378
|
+
const start = trimmed.indexOf("{");
|
|
379
|
+
const end = trimmed.lastIndexOf("}");
|
|
380
|
+
if (start >= 0 && end > start) {
|
|
381
|
+
const sliced = parseTitle(trimmed.slice(start, end + 1));
|
|
382
|
+
if (sliced.kind === "ok") return sliced.title;
|
|
383
|
+
if (sliced.kind === "empty") return null;
|
|
384
|
+
}
|
|
385
|
+
const quoteMatch = trimmed.match(/"([^"\\]{2,120})"/);
|
|
386
|
+
if (quoteMatch?.[1]) return quoteMatch[1].trim();
|
|
387
|
+
return null;
|
|
388
|
+
}
|
|
389
|
+
async function generateAutoTitle(messages, opts) {
|
|
390
|
+
const seed = extractTitleSeedText(messages, opts.maxInputChars);
|
|
391
|
+
if (!seed) return null;
|
|
392
|
+
const model = opts.model ?? opts.provider.defaultModel;
|
|
393
|
+
if (!model) return null;
|
|
394
|
+
const system = opts.systemPrompt ?? DEFAULT_AUTO_TITLE_SYSTEM_PROMPT;
|
|
395
|
+
const params = {
|
|
396
|
+
model,
|
|
397
|
+
system,
|
|
398
|
+
messages: [{ role: "user", content: seed }],
|
|
399
|
+
max_tokens: 60,
|
|
400
|
+
outputFormat: {
|
|
401
|
+
type: "json_schema",
|
|
402
|
+
schema: {
|
|
403
|
+
type: "object",
|
|
404
|
+
properties: { title: { type: "string" } },
|
|
405
|
+
required: ["title"],
|
|
406
|
+
additionalProperties: false
|
|
407
|
+
},
|
|
408
|
+
name: "session_title",
|
|
409
|
+
strict: true
|
|
410
|
+
},
|
|
411
|
+
signal: opts.signal
|
|
412
|
+
};
|
|
413
|
+
let text = "";
|
|
414
|
+
try {
|
|
415
|
+
for await (const chunk of opts.provider.chat(params)) {
|
|
416
|
+
for (const choice of chunk.choices) {
|
|
417
|
+
const delta = choice.delta.content;
|
|
418
|
+
if (typeof delta === "string") text += delta;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
} catch {
|
|
422
|
+
return null;
|
|
423
|
+
}
|
|
424
|
+
if (!text) return null;
|
|
425
|
+
const extracted = extractTitleFromResponse(text) ?? text.trim();
|
|
426
|
+
return normalizeTitle(extracted);
|
|
427
|
+
}
|
|
428
|
+
function normalizeTitle(raw) {
|
|
429
|
+
if (!raw) return null;
|
|
430
|
+
let t = raw.replace(/\s+/g, " ").trim();
|
|
431
|
+
if (t.length >= 2 && (t.startsWith('"') && t.endsWith('"') || t.startsWith("'") && t.endsWith("'"))) {
|
|
432
|
+
t = t.slice(1, -1).trim();
|
|
433
|
+
}
|
|
434
|
+
t = t.replace(/\.+$/, "").trim();
|
|
435
|
+
if (!t) return null;
|
|
436
|
+
if (t.length > 120) t = t.slice(0, 120).trim();
|
|
437
|
+
return t;
|
|
438
|
+
}
|
|
439
|
+
|
|
319
440
|
// src/checkpoint/manager.ts
|
|
320
441
|
import { createHash } from "crypto";
|
|
321
442
|
|
|
@@ -1805,31 +1926,83 @@ var SessionStorage = class {
|
|
|
1805
1926
|
await this.appendEntry(sessionId, entry);
|
|
1806
1927
|
}
|
|
1807
1928
|
/**
|
|
1808
|
-
*
|
|
1809
|
-
*
|
|
1929
|
+
* Append a user-set session title. Wins over any `ai-title` when read.
|
|
1930
|
+
* Idempotent-ish: callers may append as many as they like; the last one
|
|
1931
|
+
* wins according to file order.
|
|
1932
|
+
*/
|
|
1933
|
+
async appendCustomTitle(sessionId, title) {
|
|
1934
|
+
const entry = {
|
|
1935
|
+
type: "custom-title",
|
|
1936
|
+
sessionId,
|
|
1937
|
+
title,
|
|
1938
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1939
|
+
};
|
|
1940
|
+
await this.appendEntry(sessionId, entry);
|
|
1941
|
+
}
|
|
1942
|
+
/**
|
|
1943
|
+
* Append an AI-generated session title. A `custom-title` (if present)
|
|
1944
|
+
* always takes precedence on read.
|
|
1945
|
+
*/
|
|
1946
|
+
async appendAiTitle(sessionId, title) {
|
|
1947
|
+
const entry = {
|
|
1948
|
+
type: "ai-title",
|
|
1949
|
+
sessionId,
|
|
1950
|
+
title,
|
|
1951
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1952
|
+
};
|
|
1953
|
+
await this.appendEntry(sessionId, entry);
|
|
1954
|
+
}
|
|
1955
|
+
/**
|
|
1956
|
+
* Re-append custom-title, ai-title, and key metadata entries after a
|
|
1957
|
+
* compact boundary so they remain discoverable in the active-entries
|
|
1958
|
+
* window. Written as a single batched append so a crash mid-write can't
|
|
1959
|
+
* leave the transcript with only a subset of the re-emitted entries.
|
|
1810
1960
|
*/
|
|
1811
1961
|
async reAppendMetadataAfterCompact(sessionId) {
|
|
1812
1962
|
const entries = await this.loadAllEntries(sessionId);
|
|
1813
1963
|
let customTitle;
|
|
1964
|
+
let aiTitle;
|
|
1814
1965
|
const metadataByKey = /* @__PURE__ */ new Map();
|
|
1815
1966
|
for (const entry of entries) {
|
|
1816
1967
|
if (entry.type === "custom-title") {
|
|
1817
1968
|
customTitle = entry.title;
|
|
1818
1969
|
}
|
|
1970
|
+
if (entry.type === "ai-title") {
|
|
1971
|
+
aiTitle = entry.title;
|
|
1972
|
+
}
|
|
1819
1973
|
if (entry.type === "metadata") {
|
|
1820
1974
|
metadataByKey.set(entry.key, entry.value);
|
|
1821
1975
|
}
|
|
1822
1976
|
}
|
|
1977
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
1978
|
+
const batch = [];
|
|
1823
1979
|
if (customTitle) {
|
|
1824
|
-
|
|
1980
|
+
batch.push({
|
|
1825
1981
|
type: "custom-title",
|
|
1826
1982
|
sessionId,
|
|
1827
1983
|
title: customTitle,
|
|
1828
|
-
timestamp
|
|
1984
|
+
timestamp
|
|
1985
|
+
});
|
|
1986
|
+
}
|
|
1987
|
+
if (aiTitle) {
|
|
1988
|
+
batch.push({
|
|
1989
|
+
type: "ai-title",
|
|
1990
|
+
sessionId,
|
|
1991
|
+
title: aiTitle,
|
|
1992
|
+
timestamp
|
|
1829
1993
|
});
|
|
1830
1994
|
}
|
|
1831
1995
|
for (const [key, value] of metadataByKey) {
|
|
1832
|
-
|
|
1996
|
+
batch.push({
|
|
1997
|
+
type: "metadata",
|
|
1998
|
+
sessionId,
|
|
1999
|
+
timestamp,
|
|
2000
|
+
key,
|
|
2001
|
+
value
|
|
2002
|
+
});
|
|
2003
|
+
}
|
|
2004
|
+
if (batch.length > 0) {
|
|
2005
|
+
await this.appendEntriesBatch(sessionId, batch);
|
|
1833
2006
|
}
|
|
1834
2007
|
}
|
|
1835
2008
|
async loadMessages(sessionId) {
|
|
@@ -1880,6 +2053,21 @@ var SessionStorage = class {
|
|
|
1880
2053
|
async sessionExists(sessionId) {
|
|
1881
2054
|
return this.fs.exists(this.getTranscriptPath(sessionId));
|
|
1882
2055
|
}
|
|
2056
|
+
/**
|
|
2057
|
+
* Return the currently persisted titles for a session. `title` reflects
|
|
2058
|
+
* the display preference (custom > ai). Returns all-undefined if the
|
|
2059
|
+
* session file doesn't exist.
|
|
2060
|
+
*/
|
|
2061
|
+
async getSessionTitles(sessionId) {
|
|
2062
|
+
const entries = await this.loadAllEntries(sessionId);
|
|
2063
|
+
let customTitle;
|
|
2064
|
+
let aiTitle;
|
|
2065
|
+
for (const entry of entries) {
|
|
2066
|
+
if (entry.type === "custom-title") customTitle = entry.title;
|
|
2067
|
+
if (entry.type === "ai-title") aiTitle = entry.title;
|
|
2068
|
+
}
|
|
2069
|
+
return { title: customTitle ?? aiTitle, customTitle, aiTitle };
|
|
2070
|
+
}
|
|
1883
2071
|
async deleteSession(sessionId) {
|
|
1884
2072
|
const filePath = this.getTranscriptPath(sessionId);
|
|
1885
2073
|
const exists = await this.fs.exists(filePath);
|
|
@@ -1918,8 +2106,8 @@ var SessionStorage = class {
|
|
|
1918
2106
|
tailSlice = content;
|
|
1919
2107
|
}
|
|
1920
2108
|
const headEntries = parseJSONL(headSlice);
|
|
1921
|
-
|
|
1922
|
-
let
|
|
2109
|
+
let customTitle;
|
|
2110
|
+
let aiTitle;
|
|
1923
2111
|
let firstTimestamp;
|
|
1924
2112
|
let lastTimestamp;
|
|
1925
2113
|
let messageCount = 0;
|
|
@@ -1929,22 +2117,27 @@ var SessionStorage = class {
|
|
|
1929
2117
|
if (!firstTimestamp) firstTimestamp = e.timestamp;
|
|
1930
2118
|
lastTimestamp = e.timestamp;
|
|
1931
2119
|
}
|
|
1932
|
-
if (e.type === "custom-title")
|
|
2120
|
+
if (e.type === "custom-title") customTitle = e.title;
|
|
2121
|
+
if (e.type === "ai-title") aiTitle = e.title;
|
|
1933
2122
|
}
|
|
1934
2123
|
if (isSplit) {
|
|
2124
|
+
const tailEntries = parseJSONL(tailSlice);
|
|
1935
2125
|
for (const e of tailEntries) {
|
|
1936
2126
|
if (e.type === "message" || e.type === "summary") {
|
|
1937
2127
|
messageCount++;
|
|
1938
2128
|
if (e.timestamp) lastTimestamp = e.timestamp;
|
|
1939
2129
|
}
|
|
1940
|
-
if (e.type === "custom-title")
|
|
2130
|
+
if (e.type === "custom-title") customTitle = e.title;
|
|
2131
|
+
if (e.type === "ai-title") aiTitle = e.title;
|
|
1941
2132
|
}
|
|
1942
2133
|
}
|
|
1943
2134
|
sessions.push({
|
|
1944
2135
|
sessionId,
|
|
1945
2136
|
createdAt: firstTimestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
1946
2137
|
lastMessageAt: lastTimestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
1947
|
-
title,
|
|
2138
|
+
title: customTitle ?? aiTitle,
|
|
2139
|
+
customTitle,
|
|
2140
|
+
aiTitle,
|
|
1948
2141
|
messageCount
|
|
1949
2142
|
});
|
|
1950
2143
|
} catch {
|
|
@@ -7052,7 +7245,13 @@ var Thread = class {
|
|
|
7052
7245
|
this.config = config;
|
|
7053
7246
|
this.sessionId = opts?.sessionId ?? generateUUID();
|
|
7054
7247
|
this.cwd = opts?.cwd ?? "/";
|
|
7055
|
-
|
|
7248
|
+
const resolvedModel = opts?.model ?? config.model ?? config.provider.defaultModel;
|
|
7249
|
+
if (!resolvedModel) {
|
|
7250
|
+
throw new Error(
|
|
7251
|
+
"Thread: no model resolved. Pass `model` to Thread / Agent / preset options, set `config.model`, or provide a provider with a `defaultModel` (built-in providers expose one automatically)."
|
|
7252
|
+
);
|
|
7253
|
+
}
|
|
7254
|
+
this.model = resolvedModel;
|
|
7056
7255
|
this.storage = new SessionStorage(config.fs, config.sessionDir);
|
|
7057
7256
|
if (config.permissions) {
|
|
7058
7257
|
this.permissionContext = {
|
|
@@ -7977,6 +8176,14 @@ var Agent = class {
|
|
|
7977
8176
|
historySnipConfig;
|
|
7978
8177
|
outputFormat;
|
|
7979
8178
|
structuredOutputMode;
|
|
8179
|
+
autoTitleConfig;
|
|
8180
|
+
/**
|
|
8181
|
+
* Keyed by `${sessionId}:${force ? "force" : "normal"}`. Parallel calls
|
|
8182
|
+
* that pass the same `force` mode coalesce to one in-flight request; a
|
|
8183
|
+
* `force: true` call arriving during a normal in-flight request runs its
|
|
8184
|
+
* own pass so the caller's intent isn't silently dropped.
|
|
8185
|
+
*/
|
|
8186
|
+
autoTitleInFlight = /* @__PURE__ */ new Map();
|
|
7980
8187
|
providerPromise = null;
|
|
7981
8188
|
initPromise = null;
|
|
7982
8189
|
constructor(opts) {
|
|
@@ -8047,6 +8254,12 @@ var Agent = class {
|
|
|
8047
8254
|
this.historySnipConfig = opts.options?.historySnip;
|
|
8048
8255
|
this.outputFormat = opts.options?.outputFormat;
|
|
8049
8256
|
this.structuredOutputMode = opts.options?.structuredOutputMode;
|
|
8257
|
+
const autoTitleOpt = opts.options?.autoTitle;
|
|
8258
|
+
if (autoTitleOpt === true) {
|
|
8259
|
+
this.autoTitleConfig = { enabled: true };
|
|
8260
|
+
} else if (autoTitleOpt && typeof autoTitleOpt === "object") {
|
|
8261
|
+
this.autoTitleConfig = { enabled: true, ...autoTitleOpt };
|
|
8262
|
+
}
|
|
8050
8263
|
if (opts.options?.checkpoint?.enabled) {
|
|
8051
8264
|
this.checkpointManager = new FileCheckpointManager(
|
|
8052
8265
|
this.fs,
|
|
@@ -8058,7 +8271,7 @@ var Agent = class {
|
|
|
8058
8271
|
if (this.resolvedProvider) return this.resolvedProvider;
|
|
8059
8272
|
if (!this.providerPromise) {
|
|
8060
8273
|
this.providerPromise = (async () => {
|
|
8061
|
-
const { resolveProvider: resolveProvider2 } = await import("./resolve-
|
|
8274
|
+
const { resolveProvider: resolveProvider2 } = await import("./resolve-6KUZNEYW.js");
|
|
8062
8275
|
return resolveProvider2(this.providerInput, { model: this.model });
|
|
8063
8276
|
})();
|
|
8064
8277
|
}
|
|
@@ -8216,6 +8429,92 @@ var Agent = class {
|
|
|
8216
8429
|
async listSessions() {
|
|
8217
8430
|
return this.storage.listSessions();
|
|
8218
8431
|
}
|
|
8432
|
+
/**
|
|
8433
|
+
* Load the message history for a stored session, respecting compact
|
|
8434
|
+
* boundaries and history snips. Returns `[]` when the session does
|
|
8435
|
+
* not exist. Use this to rehydrate a UI — resuming a Thread for
|
|
8436
|
+
* further execution should go through `resumeThread()` instead.
|
|
8437
|
+
*/
|
|
8438
|
+
async getMessages(sessionId) {
|
|
8439
|
+
return this.storage.loadMessages(sessionId);
|
|
8440
|
+
}
|
|
8441
|
+
/**
|
|
8442
|
+
* Persist a user-set title for a session. Takes precedence over any
|
|
8443
|
+
* AI-generated title on read. No-op on empty / whitespace-only input.
|
|
8444
|
+
*/
|
|
8445
|
+
async setCustomTitle(sessionId, title) {
|
|
8446
|
+
const trimmed = title.trim();
|
|
8447
|
+
if (!trimmed) return;
|
|
8448
|
+
await this.storage.appendCustomTitle(sessionId, trimmed);
|
|
8449
|
+
}
|
|
8450
|
+
/**
|
|
8451
|
+
* Persist an AI-generated title for a session. A user-set title
|
|
8452
|
+
* (see `setCustomTitle`) always wins on read, so writing this after a
|
|
8453
|
+
* user has renamed the session is harmless.
|
|
8454
|
+
*/
|
|
8455
|
+
async setAiTitle(sessionId, title) {
|
|
8456
|
+
const trimmed = title.trim();
|
|
8457
|
+
if (!trimmed) return;
|
|
8458
|
+
await this.storage.appendAiTitle(sessionId, trimmed);
|
|
8459
|
+
}
|
|
8460
|
+
/**
|
|
8461
|
+
* Delete a session's persisted transcript. Does not affect any
|
|
8462
|
+
* currently-running `Thread` reading from the same session id.
|
|
8463
|
+
*/
|
|
8464
|
+
async deleteSession(sessionId) {
|
|
8465
|
+
await this.storage.deleteSession(sessionId);
|
|
8466
|
+
}
|
|
8467
|
+
/**
|
|
8468
|
+
* Return the currently persisted titles for a session.
|
|
8469
|
+
* `title` reflects the display preference (custom > ai).
|
|
8470
|
+
*/
|
|
8471
|
+
async getSessionTitles(sessionId) {
|
|
8472
|
+
return this.storage.getSessionTitles(sessionId);
|
|
8473
|
+
}
|
|
8474
|
+
/**
|
|
8475
|
+
* When `autoTitle` is enabled and the session has no title yet,
|
|
8476
|
+
* generate one via the configured provider + model and persist it
|
|
8477
|
+
* as an `ai-title` entry. Returns the new title on success, or `null`
|
|
8478
|
+
* when skipped (feature disabled, title already present, empty seed,
|
|
8479
|
+
* or provider error).
|
|
8480
|
+
*
|
|
8481
|
+
* Concurrency-safe: parallel calls for the same session coalesce to
|
|
8482
|
+
* a single in-flight request.
|
|
8483
|
+
*/
|
|
8484
|
+
async autoTitleIfMissing(sessionId, opts) {
|
|
8485
|
+
const cfg = this.autoTitleConfig;
|
|
8486
|
+
if (!cfg?.enabled) return null;
|
|
8487
|
+
const force = opts?.force === true;
|
|
8488
|
+
const key = `${sessionId}:${force ? "force" : "normal"}`;
|
|
8489
|
+
const existing = this.autoTitleInFlight.get(key);
|
|
8490
|
+
if (existing) return existing;
|
|
8491
|
+
const task = (async () => {
|
|
8492
|
+
try {
|
|
8493
|
+
if (!force) {
|
|
8494
|
+
const titles = await this.storage.getSessionTitles(sessionId);
|
|
8495
|
+
if (titles.customTitle || titles.aiTitle) return null;
|
|
8496
|
+
}
|
|
8497
|
+
await this.ensureProvider();
|
|
8498
|
+
const provider = cfg.provider ?? this.getProvider();
|
|
8499
|
+
const messages = await this.storage.loadMessages(sessionId);
|
|
8500
|
+
if (messages.length === 0) return null;
|
|
8501
|
+
const title = await generateAutoTitle(messages, {
|
|
8502
|
+
provider,
|
|
8503
|
+
model: cfg.model ?? provider.defaultModel ?? this.model,
|
|
8504
|
+
systemPrompt: cfg.systemPrompt ?? DEFAULT_AUTO_TITLE_SYSTEM_PROMPT,
|
|
8505
|
+
maxInputChars: cfg.maxInputChars ?? DEFAULT_AUTO_TITLE_MAX_INPUT_CHARS,
|
|
8506
|
+
signal: opts?.signal
|
|
8507
|
+
});
|
|
8508
|
+
if (!title) return null;
|
|
8509
|
+
await this.storage.appendAiTitle(sessionId, title);
|
|
8510
|
+
return title;
|
|
8511
|
+
} finally {
|
|
8512
|
+
this.autoTitleInFlight.delete(key);
|
|
8513
|
+
}
|
|
8514
|
+
})();
|
|
8515
|
+
this.autoTitleInFlight.set(key, task);
|
|
8516
|
+
return task;
|
|
8517
|
+
}
|
|
8219
8518
|
getCostSummary() {
|
|
8220
8519
|
return this.costTracker?.getSummary() ?? null;
|
|
8221
8520
|
}
|
|
@@ -8461,7 +8760,8 @@ function codingAgent(opts) {
|
|
|
8461
8760
|
costTracking: { enabled: true },
|
|
8462
8761
|
retry: true,
|
|
8463
8762
|
hooks: opts.hooks,
|
|
8464
|
-
mcpServers: opts.mcpServers
|
|
8763
|
+
mcpServers: opts.mcpServers,
|
|
8764
|
+
autoTitle: opts.autoTitle
|
|
8465
8765
|
}
|
|
8466
8766
|
});
|
|
8467
8767
|
}
|
|
@@ -8483,7 +8783,8 @@ function planningAgent(opts) {
|
|
|
8483
8783
|
costTracking: { enabled: true },
|
|
8484
8784
|
retry: true,
|
|
8485
8785
|
hooks: opts.hooks,
|
|
8486
|
-
mcpServers: opts.mcpServers
|
|
8786
|
+
mcpServers: opts.mcpServers,
|
|
8787
|
+
autoTitle: opts.autoTitle
|
|
8487
8788
|
}
|
|
8488
8789
|
});
|
|
8489
8790
|
}
|
|
@@ -8506,6 +8807,7 @@ function reviewAgent(opts) {
|
|
|
8506
8807
|
retry: true,
|
|
8507
8808
|
hooks: opts.hooks,
|
|
8508
8809
|
mcpServers: opts.mcpServers,
|
|
8810
|
+
autoTitle: opts.autoTitle,
|
|
8509
8811
|
webSearch: {
|
|
8510
8812
|
search: async (query) => {
|
|
8511
8813
|
try {
|
|
@@ -9081,6 +9383,12 @@ export {
|
|
|
9081
9383
|
SandboxedLocalComputer,
|
|
9082
9384
|
UnsandboxedLocal,
|
|
9083
9385
|
LocalSandbox,
|
|
9386
|
+
DEFAULT_AUTO_TITLE_MAX_INPUT_CHARS,
|
|
9387
|
+
DEFAULT_AUTO_TITLE_SYSTEM_PROMPT,
|
|
9388
|
+
extractTitleSeedText,
|
|
9389
|
+
extractTitleFromResponse,
|
|
9390
|
+
generateAutoTitle,
|
|
9391
|
+
normalizeTitle,
|
|
9084
9392
|
createCheckpointState,
|
|
9085
9393
|
FileCheckpointManager,
|
|
9086
9394
|
runPreToolUseHooks,
|
|
@@ -9108,6 +9416,7 @@ export {
|
|
|
9108
9416
|
findModelPricing,
|
|
9109
9417
|
calculateCost,
|
|
9110
9418
|
CostTracker,
|
|
9419
|
+
SessionStorage,
|
|
9111
9420
|
TaskStore,
|
|
9112
9421
|
buildProjectContextSection,
|
|
9113
9422
|
parseFrontmatter,
|
|
@@ -9192,4 +9501,4 @@ export {
|
|
|
9192
9501
|
truncateIndex,
|
|
9193
9502
|
FileMemoryProvider
|
|
9194
9503
|
};
|
|
9195
|
-
//# sourceMappingURL=chunk-
|
|
9504
|
+
//# sourceMappingURL=chunk-6MMYCGJQ.js.map
|