clay-server 2.32.0-beta.3 → 2.32.0-beta.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/lib/project-debate.js +8 -0
- package/lib/project-mate-interaction.js +1 -0
- package/lib/public/modules/markdown.js +5 -1
- package/lib/sdk-bridge.js +48 -3
- package/lib/sdk-message-processor.js +6 -73
- package/lib/yoke/adapters/claude.js +52 -0
- package/lib/yoke/adapters/codex.js +42 -0
- package/lib/yoke/adapters/gemini.js +41 -0
- package/lib/yoke/interface.js +6 -0
- package/package.json +1 -1
package/lib/project-debate.js
CHANGED
|
@@ -601,7 +601,9 @@ function attachDebate(ctx) {
|
|
|
601
601
|
var digests = ctx.loadMateDigests(mateCtx, debate.moderatorId, debate.topic);
|
|
602
602
|
|
|
603
603
|
var briefText = "";
|
|
604
|
+
var _modMate = matesModule.getMate(mateCtx, debate.moderatorId);
|
|
604
605
|
ctx.sdk.createMentionSession({
|
|
606
|
+
vendor: _modMate ? _modMate.vendor : null,
|
|
605
607
|
claudeMd: claudeMd,
|
|
606
608
|
initialContext: digests,
|
|
607
609
|
initialMessage: quickBriefPrompt,
|
|
@@ -794,7 +796,9 @@ function attachDebate(ctx) {
|
|
|
794
796
|
var digests = ctx.loadMateDigests(mateCtx, debate.moderatorId, debate.topic);
|
|
795
797
|
var moderatorContext = buildModeratorContext(debate) + digests;
|
|
796
798
|
|
|
799
|
+
var _modMate2 = matesModule.getMate(mateCtx, debate.moderatorId);
|
|
797
800
|
ctx.sdk.createMentionSession({
|
|
801
|
+
vendor: _modMate2 ? _modMate2.vendor : null,
|
|
798
802
|
claudeMd: claudeMd,
|
|
799
803
|
initialContext: moderatorContext,
|
|
800
804
|
initialMessage: "Begin the debate on: " + debate.topic,
|
|
@@ -987,7 +991,9 @@ function attachDebate(ctx) {
|
|
|
987
991
|
historyContext += "---";
|
|
988
992
|
}
|
|
989
993
|
|
|
994
|
+
var _panMate = matesModule.getMate(debate.mateCtx, mateId);
|
|
990
995
|
ctx.sdk.createMentionSession({
|
|
996
|
+
vendor: _panMate ? _panMate.vendor : null,
|
|
991
997
|
claudeMd: claudeMd,
|
|
992
998
|
initialContext: panelistContext + historyContext,
|
|
993
999
|
initialMessage: "The moderator addresses you:\n\n" + moderatorText,
|
|
@@ -1521,7 +1527,9 @@ function attachDebate(ctx) {
|
|
|
1521
1527
|
}
|
|
1522
1528
|
moderatorContext += "---\n";
|
|
1523
1529
|
|
|
1530
|
+
var _modMate3 = matesModule.getMate(mateCtx, debate.moderatorId);
|
|
1524
1531
|
ctx.sdk.createMentionSession({
|
|
1532
|
+
vendor: _modMate3 ? _modMate3.vendor : null,
|
|
1525
1533
|
claudeMd: claudeMd,
|
|
1526
1534
|
initialContext: moderatorContext,
|
|
1527
1535
|
initialMessage: resumePrompt,
|
|
@@ -23,7 +23,11 @@ export function updateMermaidTheme(vars) {
|
|
|
23
23
|
var mermaidIdCounter = 0;
|
|
24
24
|
|
|
25
25
|
export function renderMarkdown(text) {
|
|
26
|
-
|
|
26
|
+
// Normalize smart quotes so bold/italic delimiters flanking them parse correctly
|
|
27
|
+
var normalized = text
|
|
28
|
+
.replace(/[\u201C\u201D]/g, '"')
|
|
29
|
+
.replace(/[\u2018\u2019]/g, "'");
|
|
30
|
+
return DOMPurify.sanitize(marked.parse(normalized));
|
|
27
31
|
}
|
|
28
32
|
|
|
29
33
|
/**
|
package/lib/sdk-bridge.js
CHANGED
|
@@ -171,6 +171,45 @@ function createSDKBridge(opts) {
|
|
|
171
171
|
var mergeSkills = skills.mergeSkills;
|
|
172
172
|
|
|
173
173
|
// --- Message processing (extracted to sdk-message-processor.js) ---
|
|
174
|
+
// Auto-generate a session title via YOKE adapter.generateTitle().
|
|
175
|
+
// Triggered by sdk-message-processor after AUTO_TITLE_TURN_THRESHOLD turns.
|
|
176
|
+
function autoGenerateTitle(session) {
|
|
177
|
+
if (typeof adapter.generateTitle !== "function") {
|
|
178
|
+
console.log("[auto-title] adapter.generateTitle not available for vendor=" + adapter.vendor);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
var userMessages = [];
|
|
182
|
+
for (var i = 0; i < session.history.length; i++) {
|
|
183
|
+
var entry = session.history[i];
|
|
184
|
+
if (entry.type === "user_message" && entry.text) {
|
|
185
|
+
userMessages.push(entry.text.substring(0, 200));
|
|
186
|
+
if (userMessages.length >= 5) break;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
if (userMessages.length === 0) {
|
|
190
|
+
console.log("[auto-title] No user messages found in session " + session.localId);
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
console.log("[auto-title] Calling adapter.generateTitle with " + userMessages.length + " messages for session " + session.localId);
|
|
194
|
+
|
|
195
|
+
adapter.generateTitle(userMessages, { cwd: cwd }).then(function(title) {
|
|
196
|
+
if (!title || title.length < 2) return;
|
|
197
|
+
title = title.substring(0, 100);
|
|
198
|
+
if (!session.titleManuallySet) {
|
|
199
|
+
session.title = title;
|
|
200
|
+
session.titleAutoGenerated = true;
|
|
201
|
+
sm.saveSessionFile(session);
|
|
202
|
+
sm.broadcastSessionList();
|
|
203
|
+
if (session.cliSessionId && typeof adapter.renameSession === "function") {
|
|
204
|
+
adapter.renameSession(session.cliSessionId, title, { dir: cwd }).catch(function () {});
|
|
205
|
+
}
|
|
206
|
+
console.log("[auto-title] Generated title for session " + session.localId + ": " + title);
|
|
207
|
+
}
|
|
208
|
+
}).catch(function(e) {
|
|
209
|
+
console.error("[auto-title] Failed:", e.message || e);
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
|
|
174
213
|
var msgProcessor = attachMessageProcessor({
|
|
175
214
|
sm: sm,
|
|
176
215
|
send: send,
|
|
@@ -183,6 +222,7 @@ function createSDKBridge(opts) {
|
|
|
183
222
|
adapter: adapter,
|
|
184
223
|
onProcessingChanged: onProcessingChanged,
|
|
185
224
|
onTurnDone: onTurnDone,
|
|
225
|
+
onAutoTitle: function (session) { autoGenerateTitle(session); },
|
|
186
226
|
opts: opts,
|
|
187
227
|
discoverSkillDirs: discoverSkillDirs,
|
|
188
228
|
mergeSkills: mergeSkills,
|
|
@@ -587,7 +627,9 @@ function createSDKBridge(opts) {
|
|
|
587
627
|
console.log("[sdk-bridge] processQueryStream: starting for-await loop, vendor=" + (session.vendor || adapter.vendor));
|
|
588
628
|
try {
|
|
589
629
|
for await (var msg of myQueryInstance) {
|
|
590
|
-
|
|
630
|
+
if (msg && msg.yokeType !== "text_delta" && msg.yokeType !== "thinking_delta" && msg.yokeType !== "tool_input_delta") {
|
|
631
|
+
console.log("[sdk-bridge] processQueryStream: received event yokeType=" + msg.yokeType);
|
|
632
|
+
}
|
|
591
633
|
// Handle worker meta events (context_usage, model_changed, etc.)
|
|
592
634
|
if (msg && msg.type === "_worker_meta") {
|
|
593
635
|
var metaData = msg.data || {};
|
|
@@ -1318,7 +1360,7 @@ function createSDKBridge(opts) {
|
|
|
1318
1360
|
// Creates a mention session that can be reused across multiple mentions
|
|
1319
1361
|
// within a conversation flow (session continuity).
|
|
1320
1362
|
async function createMentionSession(opts) {
|
|
1321
|
-
// opts: { claudeMd, initialContext, initialMessage, onDelta, onDone, onError, onActivity }
|
|
1363
|
+
// opts: { vendor, claudeMd, initialContext, initialMessage, onDelta, onDone, onError, onActivity }
|
|
1322
1364
|
var abortController = new AbortController();
|
|
1323
1365
|
|
|
1324
1366
|
// Current response callbacks (swapped on each pushMessage)
|
|
@@ -1331,9 +1373,12 @@ function createSDKBridge(opts) {
|
|
|
1331
1373
|
var mentionBlocks = {};
|
|
1332
1374
|
var alive = true;
|
|
1333
1375
|
|
|
1376
|
+
// Use the mate's vendor adapter if specified, otherwise default
|
|
1377
|
+
var mentionAdapter = (opts.vendor && adapters[opts.vendor]) || adapter;
|
|
1378
|
+
|
|
1334
1379
|
var handle;
|
|
1335
1380
|
try {
|
|
1336
|
-
handle = await
|
|
1381
|
+
handle = await mentionAdapter.createQuery({
|
|
1337
1382
|
cwd: cwd,
|
|
1338
1383
|
systemPrompt: opts.claudeMd,
|
|
1339
1384
|
model: opts.model || undefined,
|
|
@@ -13,6 +13,7 @@ function attachMessageProcessor(ctx) {
|
|
|
13
13
|
var cwd = ctx.cwd;
|
|
14
14
|
var onProcessingChanged = ctx.onProcessingChanged;
|
|
15
15
|
var onTurnDone = ctx.onTurnDone;
|
|
16
|
+
var onAutoTitle = ctx.onAutoTitle;
|
|
16
17
|
var opts = ctx.opts;
|
|
17
18
|
var discoverSkillDirs = ctx.discoverSkillDirs;
|
|
18
19
|
var mergeSkills = ctx.mergeSkills;
|
|
@@ -27,75 +28,6 @@ function attachMessageProcessor(ctx) {
|
|
|
27
28
|
sm.sendToSession(session, obj);
|
|
28
29
|
}
|
|
29
30
|
|
|
30
|
-
/**
|
|
31
|
-
* Auto-generate a session title using a lightweight SDK query.
|
|
32
|
-
* Fires after AUTO_TITLE_TURN_THRESHOLD turns and runs async (non-blocking).
|
|
33
|
-
*/
|
|
34
|
-
function autoGenerateTitle(session) {
|
|
35
|
-
// Collect user messages from history for context
|
|
36
|
-
var userMessages = [];
|
|
37
|
-
for (var i = 0; i < session.history.length; i++) {
|
|
38
|
-
var entry = session.history[i];
|
|
39
|
-
if (entry.type === "user_message" && entry.text) {
|
|
40
|
-
userMessages.push(entry.text.substring(0, 200));
|
|
41
|
-
if (userMessages.length >= 5) break;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
if (userMessages.length === 0) return;
|
|
45
|
-
|
|
46
|
-
var prompt = "Below is a conversation between a user and an AI assistant. Generate a short, descriptive title (3-8 words) that captures the main topic. Reply with ONLY the title, nothing else.\n\n";
|
|
47
|
-
for (var j = 0; j < userMessages.length; j++) {
|
|
48
|
-
prompt += "User message " + (j + 1) + ": " + userMessages[j] + "\n";
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
var ac = new AbortController();
|
|
52
|
-
adapter.createQuery({
|
|
53
|
-
cwd: cwd,
|
|
54
|
-
systemPrompt: "You are a title generator. Output only a short title (3-8 words). No quotes, no punctuation at the end, no explanation.",
|
|
55
|
-
model: "claude-haiku-4-5-20250901",
|
|
56
|
-
effort: "low",
|
|
57
|
-
abortController: ac,
|
|
58
|
-
adapterOptions: {
|
|
59
|
-
CLAUDE: {
|
|
60
|
-
permissionMode: "bypassPermissions",
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}).then(function(handle) {
|
|
64
|
-
handle.pushMessage(prompt);
|
|
65
|
-
var title = "";
|
|
66
|
-
(async function() {
|
|
67
|
-
try {
|
|
68
|
-
for await (var msg of handle) {
|
|
69
|
-
if (msg.yokeType === "text_delta" && msg.text) {
|
|
70
|
-
title += msg.text;
|
|
71
|
-
} else if (msg.yokeType === "result") {
|
|
72
|
-
break;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
} finally {
|
|
76
|
-
handle.close();
|
|
77
|
-
}
|
|
78
|
-
title = title.replace(/[\r\n]+/g, " ").replace(/^["'\s]+|["'\s.]+$/g, "").trim();
|
|
79
|
-
if (!title || title.length < 2) return;
|
|
80
|
-
title = title.substring(0, 100);
|
|
81
|
-
|
|
82
|
-
// Only update if user hasn't manually renamed the session
|
|
83
|
-
if (!session.titleManuallySet) {
|
|
84
|
-
session.title = title;
|
|
85
|
-
session.titleAutoGenerated = true;
|
|
86
|
-
sm.saveSessionFile(session);
|
|
87
|
-
sm.broadcastSessionList();
|
|
88
|
-
// Sync to SDK
|
|
89
|
-
if (session.cliSessionId) {
|
|
90
|
-
adapter.renameSession(session.cliSessionId, title, { dir: cwd }).catch(function() {});
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
})();
|
|
94
|
-
}).catch(function(e) {
|
|
95
|
-
console.error("[auto-title] Failed to generate title:", e.message || e);
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
|
|
99
31
|
function toolActivityTextForSubagent(name, input) {
|
|
100
32
|
if (name === "Bash" && input && input.description) return input.description;
|
|
101
33
|
if (name === "Read" && input && input.file_path) return "Reading " + input.file_path.split("/").pop();
|
|
@@ -483,14 +415,15 @@ function attachMessageProcessor(ctx) {
|
|
|
483
415
|
session.streamedText = false;
|
|
484
416
|
sm.broadcastSessionList();
|
|
485
417
|
|
|
486
|
-
// Auto-generate title after N turns (skip if loop
|
|
418
|
+
// Auto-generate title after N turns (skip if loop or already auto-generated)
|
|
487
419
|
if (session.turnCount === AUTO_TITLE_TURN_THRESHOLD
|
|
488
420
|
&& !session.titleAutoGenerated
|
|
489
421
|
&& !session.titleManuallySet
|
|
490
422
|
&& !session.loop
|
|
491
|
-
&&
|
|
492
|
-
|
|
493
|
-
|
|
423
|
+
&& onAutoTitle) {
|
|
424
|
+
try { onAutoTitle(session); } catch (e) {
|
|
425
|
+
console.error("[auto-title] onAutoTitle threw:", e.message || e);
|
|
426
|
+
}
|
|
494
427
|
}
|
|
495
428
|
|
|
496
429
|
if (onTurnDone) {
|
|
@@ -1096,6 +1096,58 @@ function createClaudeAdapter(opts) {
|
|
|
1096
1096
|
return createQueryHandle(rawQuery, mq, ac);
|
|
1097
1097
|
},
|
|
1098
1098
|
|
|
1099
|
+
// --- Title generation ---
|
|
1100
|
+
generateTitle: async function(messages, opts) {
|
|
1101
|
+
console.log("[auto-title/claude] generateTitle called with " + messages.length + " messages");
|
|
1102
|
+
var systemPrompt = "You are a title generator. Output only a short title (3-8 words). No quotes, no punctuation at the end, no explanation.";
|
|
1103
|
+
var prompt = "Below is a conversation between a user and an AI assistant. Generate a short, descriptive title (3-8 words) that captures the main topic. Reply with ONLY the title, nothing else.\n\n";
|
|
1104
|
+
for (var i = 0; i < messages.length; i++) {
|
|
1105
|
+
prompt += "User message " + (i + 1) + ": " + messages[i] + "\n";
|
|
1106
|
+
}
|
|
1107
|
+
var ac = new AbortController();
|
|
1108
|
+
console.log("[auto-title/claude] Creating query with model=haiku...");
|
|
1109
|
+
var handle = await adapter.createQuery({
|
|
1110
|
+
cwd: (opts && opts.cwd) || _cwd,
|
|
1111
|
+
systemPrompt: systemPrompt,
|
|
1112
|
+
model: "haiku",
|
|
1113
|
+
adapterOptions: {
|
|
1114
|
+
CLAUDE: {
|
|
1115
|
+
settingSources: ["user"],
|
|
1116
|
+
permissionMode: "bypassPermissions",
|
|
1117
|
+
}
|
|
1118
|
+
},
|
|
1119
|
+
abortController: ac,
|
|
1120
|
+
});
|
|
1121
|
+
console.log("[auto-title/claude] Query created, pushing message...");
|
|
1122
|
+
handle.pushMessage(prompt);
|
|
1123
|
+
var title = "";
|
|
1124
|
+
var streamed = false;
|
|
1125
|
+
try {
|
|
1126
|
+
for await (var msg of handle) {
|
|
1127
|
+
if (msg.yokeType === "text_delta" && msg.text) {
|
|
1128
|
+
streamed = true;
|
|
1129
|
+
title += msg.text;
|
|
1130
|
+
} else if (msg.yokeType === "message" && msg.messageRole === "assistant" && !streamed && msg.content) {
|
|
1131
|
+
// Fallback: extract text from non-streamed message content
|
|
1132
|
+
var content = msg.content;
|
|
1133
|
+
if (Array.isArray(content)) {
|
|
1134
|
+
for (var ci = 0; ci < content.length; ci++) {
|
|
1135
|
+
if (content[ci].type === "text" && content[ci].text) {
|
|
1136
|
+
title += content[ci].text;
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
} else if (msg.yokeType === "result") {
|
|
1141
|
+
break;
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
} finally {
|
|
1145
|
+
handle.close();
|
|
1146
|
+
}
|
|
1147
|
+
console.log("[auto-title/claude] Generated: " + title.substring(0, 80));
|
|
1148
|
+
return title.replace(/[\r\n]+/g, " ").replace(/^["'\s]+|["'\s.]+$/g, "").trim();
|
|
1149
|
+
},
|
|
1150
|
+
|
|
1099
1151
|
// --- Session management ---
|
|
1100
1152
|
// These delegate to SDK module-level functions.
|
|
1101
1153
|
|
|
@@ -932,6 +932,48 @@ function createCodexAdapter(opts) {
|
|
|
932
932
|
return handle;
|
|
933
933
|
},
|
|
934
934
|
|
|
935
|
+
// --- Title generation ---
|
|
936
|
+
generateTitle: async function(messages, opts) {
|
|
937
|
+
var systemPrompt = "You are a title generator. Output only a short title (3-8 words). No quotes, no punctuation at the end, no explanation.";
|
|
938
|
+
var prompt = "Below is a conversation between a user and an AI assistant. Generate a short, descriptive title (3-8 words) that captures the main topic. Reply with ONLY the title, nothing else.\n\n";
|
|
939
|
+
for (var i = 0; i < messages.length; i++) {
|
|
940
|
+
prompt += "User message " + (i + 1) + ": " + messages[i] + "\n";
|
|
941
|
+
}
|
|
942
|
+
var ac = new AbortController();
|
|
943
|
+
var handle = await adapter.createQuery({
|
|
944
|
+
cwd: (opts && opts.cwd) || _cwd,
|
|
945
|
+
systemPrompt: systemPrompt,
|
|
946
|
+
model: "gpt-5.4-mini",
|
|
947
|
+
abortController: ac,
|
|
948
|
+
canUseTool: function() { return Promise.resolve({ behavior: "deny", message: "No tools." }); },
|
|
949
|
+
});
|
|
950
|
+
handle.pushMessage(prompt);
|
|
951
|
+
var title = "";
|
|
952
|
+
var streamed = false;
|
|
953
|
+
try {
|
|
954
|
+
for await (var msg of handle) {
|
|
955
|
+
if (msg.yokeType === "text_delta" && msg.text) {
|
|
956
|
+
streamed = true;
|
|
957
|
+
title += msg.text;
|
|
958
|
+
} else if (msg.yokeType === "message" && msg.messageRole === "assistant" && !streamed && msg.content) {
|
|
959
|
+
var content = msg.content;
|
|
960
|
+
if (Array.isArray(content)) {
|
|
961
|
+
for (var ci = 0; ci < content.length; ci++) {
|
|
962
|
+
if (content[ci].type === "text" && content[ci].text) {
|
|
963
|
+
title += content[ci].text;
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
} else if (msg.yokeType === "result") {
|
|
968
|
+
break;
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
} finally {
|
|
972
|
+
handle.close();
|
|
973
|
+
}
|
|
974
|
+
return title.replace(/[\r\n]+/g, " ").replace(/^["'\s]+|["'\s.]+$/g, "").trim();
|
|
975
|
+
},
|
|
976
|
+
|
|
935
977
|
// Codex has session persistence via thread IDs
|
|
936
978
|
getSessionInfo: function(sessionId) {
|
|
937
979
|
return Promise.resolve(null);
|
|
@@ -653,6 +653,47 @@ function createGeminiAdapter(opts) {
|
|
|
653
653
|
return handle;
|
|
654
654
|
},
|
|
655
655
|
|
|
656
|
+
// --- Title generation ---
|
|
657
|
+
generateTitle: async function(messages, opts) {
|
|
658
|
+
var systemPrompt = "You are a title generator. Output only a short title (3-8 words). No quotes, no punctuation at the end, no explanation.";
|
|
659
|
+
var prompt = "Below is a conversation between a user and an AI assistant. Generate a short, descriptive title (3-8 words) that captures the main topic. Reply with ONLY the title, nothing else.\n\n";
|
|
660
|
+
for (var i = 0; i < messages.length; i++) {
|
|
661
|
+
prompt += "User message " + (i + 1) + ": " + messages[i] + "\n";
|
|
662
|
+
}
|
|
663
|
+
var ac = new AbortController();
|
|
664
|
+
var handle = await adapter.createQuery({
|
|
665
|
+
cwd: (opts && opts.cwd) || undefined,
|
|
666
|
+
systemPrompt: systemPrompt,
|
|
667
|
+
model: "gemini-2.5-flash",
|
|
668
|
+
abortController: ac,
|
|
669
|
+
});
|
|
670
|
+
handle.pushMessage(prompt);
|
|
671
|
+
var title = "";
|
|
672
|
+
var streamed = false;
|
|
673
|
+
try {
|
|
674
|
+
for await (var msg of handle) {
|
|
675
|
+
if (msg.yokeType === "text_delta" && msg.text) {
|
|
676
|
+
streamed = true;
|
|
677
|
+
title += msg.text;
|
|
678
|
+
} else if (msg.yokeType === "message" && msg.messageRole === "assistant" && !streamed && msg.content) {
|
|
679
|
+
var content = msg.content;
|
|
680
|
+
if (Array.isArray(content)) {
|
|
681
|
+
for (var ci = 0; ci < content.length; ci++) {
|
|
682
|
+
if (content[ci].type === "text" && content[ci].text) {
|
|
683
|
+
title += content[ci].text;
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
} else if (msg.yokeType === "result") {
|
|
688
|
+
break;
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
} finally {
|
|
692
|
+
handle.close();
|
|
693
|
+
}
|
|
694
|
+
return title.replace(/[\r\n]+/g, " ").replace(/^["'\s]+|["'\s.]+$/g, "").trim();
|
|
695
|
+
},
|
|
696
|
+
|
|
656
697
|
// Gemini has no session management (stateless chat)
|
|
657
698
|
getSessionInfo: function() { return Promise.resolve(null); },
|
|
658
699
|
listSessions: function() { return Promise.resolve([]); },
|
package/lib/yoke/interface.js
CHANGED
|
@@ -21,6 +21,12 @@ var TOOL_POLICIES = ["ask", "allow-all"];
|
|
|
21
21
|
* .createToolServer(def): ToolServer (opaque)
|
|
22
22
|
* .createQuery(opts): QueryHandle
|
|
23
23
|
*
|
|
24
|
+
* Lightweight utilities:
|
|
25
|
+
* .generateTitle(messages, opts) : Promise<string> - generate a short session title
|
|
26
|
+
* messages: string[] - user messages to derive the title from
|
|
27
|
+
* opts: { cwd }
|
|
28
|
+
* Returns a short (3-8 word) title string.
|
|
29
|
+
*
|
|
24
30
|
* Additional session management (Claude SDK specific, may vary per adapter):
|
|
25
31
|
* .getSessionInfo(sessionId, opts): Promise<object|null>
|
|
26
32
|
* .listSessions(opts) : Promise<Array>
|