@agentconnect/host 0.2.5 → 0.2.6
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/host.js +71 -8
- package/dist/providers/claude.js +224 -5
- package/dist/providers/codex.js +75 -3
- package/dist/providers/cursor.js +76 -3
- package/dist/summary.d.ts +2 -1
- package/dist/summary.js +22 -0
- package/dist/types.d.ts +24 -5
- package/package.json +1 -1
package/dist/host.js
CHANGED
|
@@ -9,7 +9,7 @@ import { listModels, listRecentModels, providers, resolveProviderForModel, } fro
|
|
|
9
9
|
import { debugLog, setSpawnLogging } from './providers/utils.js';
|
|
10
10
|
import { createObservedTracker } from './observed.js';
|
|
11
11
|
import { createStorage } from './storage.js';
|
|
12
|
-
import { buildSummaryPrompt, getSummaryModel, runSummaryPrompt, } from './summary.js';
|
|
12
|
+
import { buildSummaryPrompt, buildSummaryPromptWithOverride, getSummaryModel, runSummaryPrompt, } from './summary.js';
|
|
13
13
|
function send(socket, payload) {
|
|
14
14
|
socket.send(JSON.stringify(payload));
|
|
15
15
|
}
|
|
@@ -126,6 +126,29 @@ function createHostRuntime(options) {
|
|
|
126
126
|
return null;
|
|
127
127
|
}
|
|
128
128
|
}
|
|
129
|
+
function normalizeSummaryMode(value) {
|
|
130
|
+
const raw = typeof value === 'string' ? value.trim().toLowerCase() : '';
|
|
131
|
+
if (raw === 'auto' || raw === 'off' || raw === 'force')
|
|
132
|
+
return raw;
|
|
133
|
+
return undefined;
|
|
134
|
+
}
|
|
135
|
+
function normalizeSummaryPrompt(value) {
|
|
136
|
+
if (typeof value !== 'string')
|
|
137
|
+
return undefined;
|
|
138
|
+
const trimmed = value.trim();
|
|
139
|
+
return trimmed ? trimmed : undefined;
|
|
140
|
+
}
|
|
141
|
+
function resolveSummaryMode(session, requested) {
|
|
142
|
+
if (requested)
|
|
143
|
+
return requested;
|
|
144
|
+
return session.summaryMode ?? 'auto';
|
|
145
|
+
}
|
|
146
|
+
function resolveEffectiveSummaryMode(session, requested) {
|
|
147
|
+
const mode = resolveSummaryMode(session, requested);
|
|
148
|
+
if (mode === 'auto' && session.summaryAutoUsed)
|
|
149
|
+
return 'off';
|
|
150
|
+
return mode;
|
|
151
|
+
}
|
|
129
152
|
function recordCapability(capability) {
|
|
130
153
|
observedTracker.record(capability);
|
|
131
154
|
}
|
|
@@ -183,17 +206,22 @@ function createHostRuntime(options) {
|
|
|
183
206
|
function clearSummarySeed(session) {
|
|
184
207
|
session.summarySeed = undefined;
|
|
185
208
|
session.summaryReasoning = undefined;
|
|
209
|
+
session.summaryNextMode = undefined;
|
|
210
|
+
session.summaryNextPrompt = undefined;
|
|
186
211
|
}
|
|
187
212
|
async function startPromptSummary(options) {
|
|
188
|
-
const { sessionId, session, message, reasoning, emit } = options;
|
|
213
|
+
const { sessionId, session, message, reasoning, summaryPrompt, mode, emit } = options;
|
|
189
214
|
if (session.summaryRequested)
|
|
190
215
|
return;
|
|
191
216
|
session.summaryRequested = true;
|
|
217
|
+
let completed = false;
|
|
192
218
|
try {
|
|
193
219
|
const provider = providers[session.providerId];
|
|
194
220
|
if (!provider)
|
|
195
221
|
return;
|
|
196
|
-
const prompt =
|
|
222
|
+
const prompt = summaryPrompt
|
|
223
|
+
? buildSummaryPromptWithOverride(summaryPrompt, message, reasoning)
|
|
224
|
+
: buildSummaryPrompt(message, reasoning);
|
|
197
225
|
const summaryModel = getSummaryModel(session.providerId);
|
|
198
226
|
const cwd = session.cwd || basePath;
|
|
199
227
|
const repoRoot = session.repoRoot || basePath;
|
|
@@ -213,9 +241,13 @@ function createHostRuntime(options) {
|
|
|
213
241
|
model: result.model ?? null,
|
|
214
242
|
createdAt: new Date().toISOString(),
|
|
215
243
|
}, session);
|
|
244
|
+
completed = true;
|
|
216
245
|
}
|
|
217
246
|
finally {
|
|
218
247
|
session.summaryRequested = false;
|
|
248
|
+
if (!completed && mode === 'auto') {
|
|
249
|
+
session.summaryAutoUsed = true;
|
|
250
|
+
}
|
|
219
251
|
if (session.summarySeed) {
|
|
220
252
|
maybeStartPromptSummary({
|
|
221
253
|
sessionId,
|
|
@@ -342,6 +374,9 @@ function createHostRuntime(options) {
|
|
|
342
374
|
}
|
|
343
375
|
function maybeStartPromptSummary(options) {
|
|
344
376
|
const { sessionId, session, emit, trigger } = options;
|
|
377
|
+
const mode = resolveEffectiveSummaryMode(session, session.summaryNextMode);
|
|
378
|
+
if (mode === 'off')
|
|
379
|
+
return;
|
|
345
380
|
if (session.summaryRequested)
|
|
346
381
|
return;
|
|
347
382
|
if (!session.summarySeed)
|
|
@@ -350,8 +385,17 @@ function createHostRuntime(options) {
|
|
|
350
385
|
return;
|
|
351
386
|
const message = session.summarySeed;
|
|
352
387
|
const reasoning = session.summaryReasoning;
|
|
388
|
+
const summaryPrompt = session.summaryNextPrompt ?? session.summaryPrompt;
|
|
353
389
|
clearSummarySeed(session);
|
|
354
|
-
void startPromptSummary({
|
|
390
|
+
void startPromptSummary({
|
|
391
|
+
sessionId,
|
|
392
|
+
session,
|
|
393
|
+
message,
|
|
394
|
+
reasoning,
|
|
395
|
+
summaryPrompt,
|
|
396
|
+
mode,
|
|
397
|
+
emit,
|
|
398
|
+
});
|
|
355
399
|
}
|
|
356
400
|
function sessionSummaryKey(sessionId) {
|
|
357
401
|
return `session:${sessionId}:summary`;
|
|
@@ -360,9 +404,6 @@ function createHostRuntime(options) {
|
|
|
360
404
|
if (!payload.summary)
|
|
361
405
|
return;
|
|
362
406
|
if (session) {
|
|
363
|
-
if (session.summarySource === 'claude-log' && payload.source === 'prompt') {
|
|
364
|
-
return;
|
|
365
|
-
}
|
|
366
407
|
if (session.summary === payload.summary && session.summarySource === payload.source) {
|
|
367
408
|
return;
|
|
368
409
|
}
|
|
@@ -370,6 +411,7 @@ function createHostRuntime(options) {
|
|
|
370
411
|
session.summarySource = payload.source;
|
|
371
412
|
session.summaryModel = payload.model ?? null;
|
|
372
413
|
session.summaryCreatedAt = payload.createdAt;
|
|
414
|
+
session.summaryAutoUsed = true;
|
|
373
415
|
}
|
|
374
416
|
emitSessionEvent(emit, sessionId, 'summary', payload);
|
|
375
417
|
storage.set(sessionSummaryKey(sessionId), payload);
|
|
@@ -602,6 +644,8 @@ function createHostRuntime(options) {
|
|
|
602
644
|
else {
|
|
603
645
|
recordProviderCapability(providerId);
|
|
604
646
|
}
|
|
647
|
+
const summaryMode = normalizeSummaryMode(params.summary?.mode);
|
|
648
|
+
const summaryPrompt = normalizeSummaryPrompt(params.summary?.prompt);
|
|
605
649
|
sessions.set(sessionId, {
|
|
606
650
|
id: sessionId,
|
|
607
651
|
providerId,
|
|
@@ -614,6 +658,9 @@ function createHostRuntime(options) {
|
|
|
614
658
|
providerDetailLevel: providerDetailLevel === 'raw' || providerDetailLevel === 'minimal'
|
|
615
659
|
? providerDetailLevel
|
|
616
660
|
: undefined,
|
|
661
|
+
summaryMode: summaryMode === 'force' ? 'auto' : summaryMode ?? 'auto',
|
|
662
|
+
summaryPrompt,
|
|
663
|
+
summaryAutoUsed: false,
|
|
617
664
|
});
|
|
618
665
|
responder.reply(id, { sessionId });
|
|
619
666
|
return;
|
|
@@ -643,6 +690,16 @@ function createHostRuntime(options) {
|
|
|
643
690
|
existing.providerDetailLevel = level;
|
|
644
691
|
}
|
|
645
692
|
}
|
|
693
|
+
if ('summary' in params) {
|
|
694
|
+
const summaryMode = normalizeSummaryMode(params.summary?.mode);
|
|
695
|
+
const summaryPrompt = normalizeSummaryPrompt(params.summary?.prompt);
|
|
696
|
+
if (summaryMode) {
|
|
697
|
+
existing.summaryMode = summaryMode === 'force' ? 'auto' : summaryMode;
|
|
698
|
+
}
|
|
699
|
+
if (summaryPrompt !== undefined) {
|
|
700
|
+
existing.summaryPrompt = summaryPrompt;
|
|
701
|
+
}
|
|
702
|
+
}
|
|
646
703
|
if (!existing.model && typeof params.model === 'string' && params.model.trim()) {
|
|
647
704
|
const candidate = params.model.trim();
|
|
648
705
|
const matches = await isModelForProvider(candidate, existing.providerId);
|
|
@@ -698,7 +755,13 @@ function createHostRuntime(options) {
|
|
|
698
755
|
return;
|
|
699
756
|
}
|
|
700
757
|
}
|
|
701
|
-
|
|
758
|
+
const summaryMode = resolveEffectiveSummaryMode(session, normalizeSummaryMode(params.summary?.mode));
|
|
759
|
+
const summaryPrompt = normalizeSummaryPrompt(params.summary?.prompt);
|
|
760
|
+
session.summaryNextMode = summaryMode;
|
|
761
|
+
if (summaryPrompt !== undefined) {
|
|
762
|
+
session.summaryNextPrompt = summaryPrompt;
|
|
763
|
+
}
|
|
764
|
+
if (summaryMode !== 'off' && message.trim()) {
|
|
702
765
|
session.summarySeed = message;
|
|
703
766
|
session.summaryReasoning = '';
|
|
704
767
|
}
|
package/dist/providers/claude.js
CHANGED
|
@@ -903,6 +903,180 @@ function mapClaudeModel(model) {
|
|
|
903
903
|
return value.replace('claude-', '');
|
|
904
904
|
return model;
|
|
905
905
|
}
|
|
906
|
+
function extractUsageFromValue(value) {
|
|
907
|
+
if (!value || typeof value !== 'object')
|
|
908
|
+
return null;
|
|
909
|
+
const usage = value;
|
|
910
|
+
const toNumber = (v) => {
|
|
911
|
+
if (typeof v === 'number' && Number.isFinite(v))
|
|
912
|
+
return v;
|
|
913
|
+
if (typeof v === 'string' && v.trim()) {
|
|
914
|
+
const parsed = Number(v);
|
|
915
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
916
|
+
}
|
|
917
|
+
return undefined;
|
|
918
|
+
};
|
|
919
|
+
const input = toNumber(usage.input_tokens ?? usage.prompt_tokens ?? usage.inputTokens ?? usage.promptTokens);
|
|
920
|
+
const output = toNumber(usage.output_tokens ?? usage.completion_tokens ?? usage.outputTokens ?? usage.completionTokens);
|
|
921
|
+
const total = toNumber(usage.total_tokens ?? usage.totalTokens);
|
|
922
|
+
let cached = toNumber(usage.cached_input_tokens ?? usage.cachedInputTokens);
|
|
923
|
+
const cacheCreation = toNumber(usage.cache_creation_input_tokens ?? usage.cacheCreationInputTokens);
|
|
924
|
+
const cacheRead = toNumber(usage.cache_read_input_tokens ?? usage.cacheReadInputTokens);
|
|
925
|
+
if (cached === undefined) {
|
|
926
|
+
const sum = (cacheCreation ?? 0) + (cacheRead ?? 0);
|
|
927
|
+
if (sum > 0)
|
|
928
|
+
cached = sum;
|
|
929
|
+
}
|
|
930
|
+
const reasoning = toNumber(usage.reasoning_tokens ?? usage.reasoningTokens);
|
|
931
|
+
const out = {};
|
|
932
|
+
if (input !== undefined)
|
|
933
|
+
out.input_tokens = input;
|
|
934
|
+
if (output !== undefined)
|
|
935
|
+
out.output_tokens = output;
|
|
936
|
+
if (total !== undefined)
|
|
937
|
+
out.total_tokens = total;
|
|
938
|
+
if (cached !== undefined)
|
|
939
|
+
out.cached_input_tokens = cached;
|
|
940
|
+
if (reasoning !== undefined)
|
|
941
|
+
out.reasoning_tokens = reasoning;
|
|
942
|
+
return Object.keys(out).length ? out : null;
|
|
943
|
+
}
|
|
944
|
+
function pickModelUsage(value) {
|
|
945
|
+
if (!value || typeof value !== 'object')
|
|
946
|
+
return null;
|
|
947
|
+
const entries = Object.values(value);
|
|
948
|
+
for (const entry of entries) {
|
|
949
|
+
if (entry && typeof entry === 'object')
|
|
950
|
+
return entry;
|
|
951
|
+
}
|
|
952
|
+
return null;
|
|
953
|
+
}
|
|
954
|
+
function extractClaudeUsage(msg) {
|
|
955
|
+
const usage = msg.usage ??
|
|
956
|
+
msg.message?.usage ??
|
|
957
|
+
msg.event?.usage ??
|
|
958
|
+
msg.delta?.usage ??
|
|
959
|
+
msg.token_usage ??
|
|
960
|
+
msg.tokenUsage;
|
|
961
|
+
if (usage)
|
|
962
|
+
return extractUsageFromValue(usage);
|
|
963
|
+
const resultRecord = msg.result && typeof msg.result === 'object' ? msg.result : null;
|
|
964
|
+
const nestedUsage = resultRecord?.usage ??
|
|
965
|
+
resultRecord?.token_usage ??
|
|
966
|
+
resultRecord?.tokenUsage ??
|
|
967
|
+
resultRecord?.token_usage;
|
|
968
|
+
if (nestedUsage)
|
|
969
|
+
return extractUsageFromValue(nestedUsage);
|
|
970
|
+
const modelUsage = pickModelUsage(msg.modelUsage ?? msg.message?.modelUsage ?? resultRecord?.modelUsage);
|
|
971
|
+
return extractUsageFromValue(modelUsage);
|
|
972
|
+
}
|
|
973
|
+
function extractClaudeContextUsage(msg, usage) {
|
|
974
|
+
const context = msg.context_usage ??
|
|
975
|
+
msg.contextUsage ??
|
|
976
|
+
msg.message?.context_usage ??
|
|
977
|
+
msg.message?.contextUsage ??
|
|
978
|
+
msg.event?.context_usage ??
|
|
979
|
+
msg.event?.contextUsage;
|
|
980
|
+
const resultRecord = msg.result && typeof msg.result === 'object' ? msg.result : null;
|
|
981
|
+
const modelUsage = pickModelUsage(msg.modelUsage ?? msg.message?.modelUsage ?? resultRecord?.modelUsage);
|
|
982
|
+
const toNumber = (v) => {
|
|
983
|
+
if (typeof v === 'number' && Number.isFinite(v))
|
|
984
|
+
return v;
|
|
985
|
+
if (typeof v === 'string' && v.trim()) {
|
|
986
|
+
const parsed = Number(v);
|
|
987
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
988
|
+
}
|
|
989
|
+
return undefined;
|
|
990
|
+
};
|
|
991
|
+
const toBoolean = (v) => typeof v === 'boolean' ? v : undefined;
|
|
992
|
+
const contextWindow = toNumber(context?.context_window ??
|
|
993
|
+
context?.contextWindow ??
|
|
994
|
+
modelUsage?.contextWindow ??
|
|
995
|
+
modelUsage?.context_window ??
|
|
996
|
+
msg.context_window ??
|
|
997
|
+
msg.contextWindow);
|
|
998
|
+
let contextTokens = toNumber(context?.context_tokens ??
|
|
999
|
+
context?.contextTokens ??
|
|
1000
|
+
msg.context_tokens ??
|
|
1001
|
+
msg.contextTokens);
|
|
1002
|
+
let contextCachedTokens = toNumber(context?.context_cached_tokens ??
|
|
1003
|
+
context?.contextCachedTokens ??
|
|
1004
|
+
msg.context_cached_tokens ??
|
|
1005
|
+
msg.contextCachedTokens);
|
|
1006
|
+
let contextRemainingTokens = toNumber(context?.context_remaining_tokens ??
|
|
1007
|
+
context?.contextRemainingTokens ??
|
|
1008
|
+
msg.context_remaining_tokens ??
|
|
1009
|
+
msg.contextRemainingTokens);
|
|
1010
|
+
const contextTruncated = toBoolean(context?.context_truncated ??
|
|
1011
|
+
context?.contextTruncated ??
|
|
1012
|
+
msg.context_truncated ??
|
|
1013
|
+
msg.contextTruncated);
|
|
1014
|
+
if (contextCachedTokens === undefined) {
|
|
1015
|
+
const cached = toNumber(context?.cache_creation_input_tokens) ??
|
|
1016
|
+
toNumber(context?.cacheCreationInputTokens) ??
|
|
1017
|
+
toNumber(context?.cache_read_input_tokens) ??
|
|
1018
|
+
toNumber(context?.cacheReadInputTokens);
|
|
1019
|
+
if (cached !== undefined)
|
|
1020
|
+
contextCachedTokens = cached;
|
|
1021
|
+
}
|
|
1022
|
+
if (contextTokens === undefined) {
|
|
1023
|
+
if (usage?.input_tokens !== undefined &&
|
|
1024
|
+
usage?.cached_input_tokens !== undefined) {
|
|
1025
|
+
contextTokens = usage.input_tokens + usage.cached_input_tokens;
|
|
1026
|
+
}
|
|
1027
|
+
else if (usage?.input_tokens !== undefined) {
|
|
1028
|
+
contextTokens = usage.input_tokens;
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
if (contextCachedTokens === undefined && usage?.cached_input_tokens !== undefined) {
|
|
1032
|
+
contextCachedTokens = usage.cached_input_tokens;
|
|
1033
|
+
}
|
|
1034
|
+
if (contextRemainingTokens === undefined &&
|
|
1035
|
+
contextWindow !== undefined &&
|
|
1036
|
+
contextTokens !== undefined) {
|
|
1037
|
+
contextRemainingTokens = Math.max(0, contextWindow - contextTokens);
|
|
1038
|
+
}
|
|
1039
|
+
const out = {};
|
|
1040
|
+
if (contextWindow !== undefined)
|
|
1041
|
+
out.context_window = contextWindow;
|
|
1042
|
+
if (contextTokens !== undefined)
|
|
1043
|
+
out.context_tokens = contextTokens;
|
|
1044
|
+
if (contextCachedTokens !== undefined)
|
|
1045
|
+
out.context_cached_tokens = contextCachedTokens;
|
|
1046
|
+
if (contextRemainingTokens !== undefined)
|
|
1047
|
+
out.context_remaining_tokens = contextRemainingTokens;
|
|
1048
|
+
if (contextTruncated !== undefined)
|
|
1049
|
+
out.context_truncated = contextTruncated;
|
|
1050
|
+
return Object.keys(out).length ? out : null;
|
|
1051
|
+
}
|
|
1052
|
+
function mergeUsage(current, next) {
|
|
1053
|
+
const out = { ...(current ?? {}) };
|
|
1054
|
+
if (next.input_tokens !== undefined)
|
|
1055
|
+
out.input_tokens = next.input_tokens;
|
|
1056
|
+
if (next.output_tokens !== undefined)
|
|
1057
|
+
out.output_tokens = next.output_tokens;
|
|
1058
|
+
if (next.total_tokens !== undefined)
|
|
1059
|
+
out.total_tokens = next.total_tokens;
|
|
1060
|
+
if (next.cached_input_tokens !== undefined)
|
|
1061
|
+
out.cached_input_tokens = next.cached_input_tokens;
|
|
1062
|
+
if (next.reasoning_tokens !== undefined)
|
|
1063
|
+
out.reasoning_tokens = next.reasoning_tokens;
|
|
1064
|
+
return out;
|
|
1065
|
+
}
|
|
1066
|
+
function mergeContextUsage(current, next) {
|
|
1067
|
+
const out = { ...(current ?? {}) };
|
|
1068
|
+
if (next.context_window !== undefined)
|
|
1069
|
+
out.context_window = next.context_window;
|
|
1070
|
+
if (next.context_tokens !== undefined)
|
|
1071
|
+
out.context_tokens = next.context_tokens;
|
|
1072
|
+
if (next.context_cached_tokens !== undefined)
|
|
1073
|
+
out.context_cached_tokens = next.context_cached_tokens;
|
|
1074
|
+
if (next.context_remaining_tokens !== undefined)
|
|
1075
|
+
out.context_remaining_tokens = next.context_remaining_tokens;
|
|
1076
|
+
if (next.context_truncated !== undefined)
|
|
1077
|
+
out.context_truncated = next.context_truncated;
|
|
1078
|
+
return out;
|
|
1079
|
+
}
|
|
906
1080
|
function extractSessionId(msg) {
|
|
907
1081
|
const direct = msg.session_id ?? msg.sessionId;
|
|
908
1082
|
if (typeof direct === 'string' && direct)
|
|
@@ -1004,6 +1178,11 @@ export function runClaudePrompt({ prompt, system, resumeSessionId, model, cwd, p
|
|
|
1004
1178
|
let finalSessionId = null;
|
|
1005
1179
|
let didFinalize = false;
|
|
1006
1180
|
let sawError = false;
|
|
1181
|
+
let usageEmitted = false;
|
|
1182
|
+
let latestUsage = null;
|
|
1183
|
+
let latestContextUsage = null;
|
|
1184
|
+
let latestUsageDetail;
|
|
1185
|
+
let latestContextUsageDetail;
|
|
1007
1186
|
const toolBlocks = new Map();
|
|
1008
1187
|
const thinkingBlocks = new Set();
|
|
1009
1188
|
const includeRaw = providerDetailLevel === 'raw';
|
|
@@ -1027,11 +1206,46 @@ export function runClaudePrompt({ prompt, system, resumeSessionId, model, cwd, p
|
|
|
1027
1206
|
if (sawError)
|
|
1028
1207
|
return;
|
|
1029
1208
|
sawError = true;
|
|
1209
|
+
emitUsageIfAvailable();
|
|
1030
1210
|
emit({ type: 'error', message });
|
|
1031
1211
|
};
|
|
1032
1212
|
const emitFinal = (text, providerDetail) => {
|
|
1213
|
+
emitUsageIfAvailable();
|
|
1033
1214
|
emit({ type: 'final', text, providerDetail });
|
|
1034
1215
|
};
|
|
1216
|
+
const captureUsage = (msg, providerDetail) => {
|
|
1217
|
+
const usage = extractClaudeUsage(msg);
|
|
1218
|
+
if (usage) {
|
|
1219
|
+
latestUsage = mergeUsage(latestUsage, usage);
|
|
1220
|
+
latestUsageDetail = providerDetail;
|
|
1221
|
+
}
|
|
1222
|
+
const contextUsage = extractClaudeContextUsage(msg, latestUsage);
|
|
1223
|
+
if (contextUsage) {
|
|
1224
|
+
latestContextUsage = mergeContextUsage(latestContextUsage, contextUsage);
|
|
1225
|
+
latestContextUsageDetail = providerDetail;
|
|
1226
|
+
}
|
|
1227
|
+
};
|
|
1228
|
+
const emitUsageIfAvailable = () => {
|
|
1229
|
+
if (usageEmitted)
|
|
1230
|
+
return;
|
|
1231
|
+
if (!latestUsage && !latestContextUsage)
|
|
1232
|
+
return;
|
|
1233
|
+
if (latestUsage) {
|
|
1234
|
+
emit({
|
|
1235
|
+
type: 'usage',
|
|
1236
|
+
usage: latestUsage,
|
|
1237
|
+
providerDetail: latestUsageDetail,
|
|
1238
|
+
});
|
|
1239
|
+
}
|
|
1240
|
+
if (latestContextUsage) {
|
|
1241
|
+
emit({
|
|
1242
|
+
type: 'context_usage',
|
|
1243
|
+
contextUsage: latestContextUsage,
|
|
1244
|
+
providerDetail: latestContextUsageDetail ?? latestUsageDetail,
|
|
1245
|
+
});
|
|
1246
|
+
}
|
|
1247
|
+
usageEmitted = true;
|
|
1248
|
+
};
|
|
1035
1249
|
const handleLine = (line) => {
|
|
1036
1250
|
const parsed = safeJsonParse(line);
|
|
1037
1251
|
if (!parsed || typeof parsed !== 'object') {
|
|
@@ -1046,6 +1260,8 @@ export function runClaudePrompt({ prompt, system, resumeSessionId, model, cwd, p
|
|
|
1046
1260
|
finalSessionId = sid;
|
|
1047
1261
|
const msgType = String(msg.type ?? '');
|
|
1048
1262
|
let handled = false;
|
|
1263
|
+
const detail = buildProviderDetail(msgType || 'unknown', {}, msg);
|
|
1264
|
+
captureUsage(msg, detail);
|
|
1049
1265
|
if (msgType === 'assistant' || msgType === 'user' || msgType === 'system') {
|
|
1050
1266
|
const role = msg.message?.role === 'assistant' ||
|
|
1051
1267
|
msg.message?.role === 'user' ||
|
|
@@ -1060,7 +1276,7 @@ export function runClaudePrompt({ prompt, system, resumeSessionId, model, cwd, p
|
|
|
1060
1276
|
role,
|
|
1061
1277
|
content,
|
|
1062
1278
|
contentParts: rawContent ?? null,
|
|
1063
|
-
providerDetail:
|
|
1279
|
+
providerDetail: detail,
|
|
1064
1280
|
});
|
|
1065
1281
|
handled = true;
|
|
1066
1282
|
}
|
|
@@ -1152,7 +1368,7 @@ export function runClaudePrompt({ prompt, system, resumeSessionId, model, cwd, p
|
|
|
1152
1368
|
emit({
|
|
1153
1369
|
type: 'delta',
|
|
1154
1370
|
text: delta,
|
|
1155
|
-
providerDetail:
|
|
1371
|
+
providerDetail: detail,
|
|
1156
1372
|
});
|
|
1157
1373
|
return;
|
|
1158
1374
|
}
|
|
@@ -1162,21 +1378,21 @@ export function runClaudePrompt({ prompt, system, resumeSessionId, model, cwd, p
|
|
|
1162
1378
|
emit({
|
|
1163
1379
|
type: 'delta',
|
|
1164
1380
|
text: assistant,
|
|
1165
|
-
providerDetail:
|
|
1381
|
+
providerDetail: detail,
|
|
1166
1382
|
});
|
|
1167
1383
|
return;
|
|
1168
1384
|
}
|
|
1169
1385
|
const result = extractResultText(msg);
|
|
1170
1386
|
if (result && !didFinalize && !sawError) {
|
|
1171
1387
|
didFinalize = true;
|
|
1172
|
-
emitFinal(aggregated || result,
|
|
1388
|
+
emitFinal(aggregated || result, detail);
|
|
1173
1389
|
handled = true;
|
|
1174
1390
|
}
|
|
1175
1391
|
if (!handled) {
|
|
1176
1392
|
emit({
|
|
1177
1393
|
type: 'detail',
|
|
1178
1394
|
provider: 'claude',
|
|
1179
|
-
providerDetail:
|
|
1395
|
+
providerDetail: detail,
|
|
1180
1396
|
});
|
|
1181
1397
|
}
|
|
1182
1398
|
};
|
|
@@ -1193,6 +1409,9 @@ export function runClaudePrompt({ prompt, system, resumeSessionId, model, cwd, p
|
|
|
1193
1409
|
emitFinal(aggregated);
|
|
1194
1410
|
}
|
|
1195
1411
|
}
|
|
1412
|
+
if (!usageEmitted) {
|
|
1413
|
+
emitUsageIfAvailable();
|
|
1414
|
+
}
|
|
1196
1415
|
resolve({ sessionId: finalSessionId });
|
|
1197
1416
|
});
|
|
1198
1417
|
child.on('error', (err) => {
|
package/dist/providers/codex.js
CHANGED
|
@@ -430,11 +430,20 @@ function extractUsage(ev) {
|
|
|
430
430
|
const usage = ev.usage ?? ev.token_usage ?? ev.tokens ?? ev.tokenUsage;
|
|
431
431
|
if (!usage || typeof usage !== 'object')
|
|
432
432
|
return null;
|
|
433
|
-
const toNumber = (v) =>
|
|
433
|
+
const toNumber = (v) => {
|
|
434
|
+
if (typeof v === 'number' && Number.isFinite(v))
|
|
435
|
+
return v;
|
|
436
|
+
if (typeof v === 'string' && v.trim()) {
|
|
437
|
+
const parsed = Number(v);
|
|
438
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
439
|
+
}
|
|
440
|
+
return undefined;
|
|
441
|
+
};
|
|
434
442
|
const input = toNumber(usage.input_tokens ?? usage.prompt_tokens ?? usage.inputTokens ?? usage.promptTokens);
|
|
435
443
|
const output = toNumber(usage.output_tokens ?? usage.completion_tokens ?? usage.outputTokens ?? usage.completionTokens);
|
|
436
444
|
const total = toNumber(usage.total_tokens ?? usage.totalTokens);
|
|
437
445
|
const cached = toNumber(usage.cached_input_tokens ?? usage.cachedInputTokens);
|
|
446
|
+
const reasoning = toNumber(usage.reasoning_tokens ?? usage.reasoningTokens);
|
|
438
447
|
const out = {};
|
|
439
448
|
if (input !== undefined)
|
|
440
449
|
out.input_tokens = input;
|
|
@@ -444,6 +453,61 @@ function extractUsage(ev) {
|
|
|
444
453
|
out.total_tokens = total;
|
|
445
454
|
if (cached !== undefined)
|
|
446
455
|
out.cached_input_tokens = cached;
|
|
456
|
+
if (reasoning !== undefined)
|
|
457
|
+
out.reasoning_tokens = reasoning;
|
|
458
|
+
return Object.keys(out).length ? out : null;
|
|
459
|
+
}
|
|
460
|
+
function extractContextUsage(ev, usage) {
|
|
461
|
+
const context = ev.context_usage ?? ev.contextUsage;
|
|
462
|
+
const toNumber = (v) => {
|
|
463
|
+
if (typeof v === 'number' && Number.isFinite(v))
|
|
464
|
+
return v;
|
|
465
|
+
if (typeof v === 'string' && v.trim()) {
|
|
466
|
+
const parsed = Number(v);
|
|
467
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
468
|
+
}
|
|
469
|
+
return undefined;
|
|
470
|
+
};
|
|
471
|
+
const toBoolean = (v) => typeof v === 'boolean' ? v : undefined;
|
|
472
|
+
const contextWindow = toNumber(context?.context_window ?? context?.contextWindow ?? ev.context_window ?? ev.contextWindow);
|
|
473
|
+
let contextTokens = toNumber(context?.context_tokens ?? context?.contextTokens ?? ev.context_tokens ?? ev.contextTokens);
|
|
474
|
+
let contextCachedTokens = toNumber(context?.context_cached_tokens ??
|
|
475
|
+
context?.contextCachedTokens ??
|
|
476
|
+
ev.context_cached_tokens ??
|
|
477
|
+
ev.contextCachedTokens);
|
|
478
|
+
let contextRemainingTokens = toNumber(context?.context_remaining_tokens ??
|
|
479
|
+
context?.contextRemainingTokens ??
|
|
480
|
+
ev.context_remaining_tokens ??
|
|
481
|
+
ev.contextRemainingTokens);
|
|
482
|
+
const contextTruncated = toBoolean(context?.context_truncated ?? context?.contextTruncated ?? ev.context_truncated ?? ev.contextTruncated);
|
|
483
|
+
if (contextTokens === undefined) {
|
|
484
|
+
if (usage?.input_tokens !== undefined &&
|
|
485
|
+
usage?.cached_input_tokens !== undefined) {
|
|
486
|
+
contextTokens = usage.input_tokens + usage.cached_input_tokens;
|
|
487
|
+
}
|
|
488
|
+
else if (usage?.input_tokens !== undefined) {
|
|
489
|
+
contextTokens = usage.input_tokens;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
if (contextCachedTokens === undefined && usage?.cached_input_tokens !== undefined) {
|
|
493
|
+
contextCachedTokens = usage.cached_input_tokens;
|
|
494
|
+
}
|
|
495
|
+
if (contextRemainingTokens === undefined &&
|
|
496
|
+
contextWindow !== undefined &&
|
|
497
|
+
contextTokens !== undefined) {
|
|
498
|
+
contextRemainingTokens = Math.max(0, contextWindow - contextTokens);
|
|
499
|
+
}
|
|
500
|
+
const out = {};
|
|
501
|
+
if (contextWindow !== undefined)
|
|
502
|
+
out.context_window = contextWindow;
|
|
503
|
+
if (contextTokens !== undefined)
|
|
504
|
+
out.context_tokens = contextTokens;
|
|
505
|
+
if (contextCachedTokens !== undefined)
|
|
506
|
+
out.context_cached_tokens = contextCachedTokens;
|
|
507
|
+
if (contextRemainingTokens !== undefined)
|
|
508
|
+
out.context_remaining_tokens = contextRemainingTokens;
|
|
509
|
+
if (contextTruncated !== undefined)
|
|
510
|
+
out.context_truncated = contextTruncated;
|
|
447
511
|
return Object.keys(out).length ? out : null;
|
|
448
512
|
}
|
|
449
513
|
function normalizeItem(raw) {
|
|
@@ -840,8 +904,16 @@ export function runCodexPrompt({ prompt, system, resumeSessionId, model, reasoni
|
|
|
840
904
|
if (usage) {
|
|
841
905
|
emit({
|
|
842
906
|
type: 'usage',
|
|
843
|
-
|
|
844
|
-
|
|
907
|
+
usage,
|
|
908
|
+
providerDetail,
|
|
909
|
+
});
|
|
910
|
+
handled = true;
|
|
911
|
+
}
|
|
912
|
+
const contextUsage = extractContextUsage(ev, usage);
|
|
913
|
+
if (contextUsage) {
|
|
914
|
+
emit({
|
|
915
|
+
type: 'context_usage',
|
|
916
|
+
contextUsage,
|
|
845
917
|
providerDetail,
|
|
846
918
|
});
|
|
847
919
|
handled = true;
|
package/dist/providers/cursor.js
CHANGED
|
@@ -468,10 +468,20 @@ function extractUsage(ev) {
|
|
|
468
468
|
const usage = ev.usage ?? ev.token_usage ?? ev.tokenUsage ?? ev.tokens;
|
|
469
469
|
if (!usage || typeof usage !== 'object')
|
|
470
470
|
return null;
|
|
471
|
-
const toNumber = (v) =>
|
|
471
|
+
const toNumber = (v) => {
|
|
472
|
+
if (typeof v === 'number' && Number.isFinite(v))
|
|
473
|
+
return v;
|
|
474
|
+
if (typeof v === 'string' && v.trim()) {
|
|
475
|
+
const parsed = Number(v);
|
|
476
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
477
|
+
}
|
|
478
|
+
return undefined;
|
|
479
|
+
};
|
|
472
480
|
const input = toNumber(usage.input_tokens ?? usage.prompt_tokens ?? usage.inputTokens ?? usage.promptTokens);
|
|
473
481
|
const output = toNumber(usage.output_tokens ?? usage.completion_tokens ?? usage.outputTokens ?? usage.completionTokens);
|
|
474
482
|
const total = toNumber(usage.total_tokens ?? usage.totalTokens);
|
|
483
|
+
const cached = toNumber(usage.cached_input_tokens ?? usage.cachedInputTokens);
|
|
484
|
+
const reasoning = toNumber(usage.reasoning_tokens ?? usage.reasoningTokens);
|
|
475
485
|
const out = {};
|
|
476
486
|
if (input !== undefined)
|
|
477
487
|
out.input_tokens = input;
|
|
@@ -479,6 +489,63 @@ function extractUsage(ev) {
|
|
|
479
489
|
out.output_tokens = output;
|
|
480
490
|
if (total !== undefined)
|
|
481
491
|
out.total_tokens = total;
|
|
492
|
+
if (cached !== undefined)
|
|
493
|
+
out.cached_input_tokens = cached;
|
|
494
|
+
if (reasoning !== undefined)
|
|
495
|
+
out.reasoning_tokens = reasoning;
|
|
496
|
+
return Object.keys(out).length ? out : null;
|
|
497
|
+
}
|
|
498
|
+
function extractContextUsage(ev, usage) {
|
|
499
|
+
const context = ev.context_usage ?? ev.contextUsage;
|
|
500
|
+
const toNumber = (v) => {
|
|
501
|
+
if (typeof v === 'number' && Number.isFinite(v))
|
|
502
|
+
return v;
|
|
503
|
+
if (typeof v === 'string' && v.trim()) {
|
|
504
|
+
const parsed = Number(v);
|
|
505
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
506
|
+
}
|
|
507
|
+
return undefined;
|
|
508
|
+
};
|
|
509
|
+
const toBoolean = (v) => typeof v === 'boolean' ? v : undefined;
|
|
510
|
+
const contextWindow = toNumber(context?.context_window ?? context?.contextWindow ?? ev.context_window ?? ev.contextWindow);
|
|
511
|
+
let contextTokens = toNumber(context?.context_tokens ?? context?.contextTokens ?? ev.context_tokens ?? ev.contextTokens);
|
|
512
|
+
let contextCachedTokens = toNumber(context?.context_cached_tokens ??
|
|
513
|
+
context?.contextCachedTokens ??
|
|
514
|
+
ev.context_cached_tokens ??
|
|
515
|
+
ev.contextCachedTokens);
|
|
516
|
+
let contextRemainingTokens = toNumber(context?.context_remaining_tokens ??
|
|
517
|
+
context?.contextRemainingTokens ??
|
|
518
|
+
ev.context_remaining_tokens ??
|
|
519
|
+
ev.contextRemainingTokens);
|
|
520
|
+
const contextTruncated = toBoolean(context?.context_truncated ?? context?.contextTruncated ?? ev.context_truncated ?? ev.contextTruncated);
|
|
521
|
+
if (contextTokens === undefined) {
|
|
522
|
+
if (usage?.input_tokens !== undefined &&
|
|
523
|
+
usage?.cached_input_tokens !== undefined) {
|
|
524
|
+
contextTokens = usage.input_tokens + usage.cached_input_tokens;
|
|
525
|
+
}
|
|
526
|
+
else if (usage?.input_tokens !== undefined) {
|
|
527
|
+
contextTokens = usage.input_tokens;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
if (contextCachedTokens === undefined && usage?.cached_input_tokens !== undefined) {
|
|
531
|
+
contextCachedTokens = usage.cached_input_tokens;
|
|
532
|
+
}
|
|
533
|
+
if (contextRemainingTokens === undefined &&
|
|
534
|
+
contextWindow !== undefined &&
|
|
535
|
+
contextTokens !== undefined) {
|
|
536
|
+
contextRemainingTokens = Math.max(0, contextWindow - contextTokens);
|
|
537
|
+
}
|
|
538
|
+
const out = {};
|
|
539
|
+
if (contextWindow !== undefined)
|
|
540
|
+
out.context_window = contextWindow;
|
|
541
|
+
if (contextTokens !== undefined)
|
|
542
|
+
out.context_tokens = contextTokens;
|
|
543
|
+
if (contextCachedTokens !== undefined)
|
|
544
|
+
out.context_cached_tokens = contextCachedTokens;
|
|
545
|
+
if (contextRemainingTokens !== undefined)
|
|
546
|
+
out.context_remaining_tokens = contextRemainingTokens;
|
|
547
|
+
if (contextTruncated !== undefined)
|
|
548
|
+
out.context_truncated = contextTruncated;
|
|
482
549
|
return Object.keys(out).length ? out : null;
|
|
483
550
|
}
|
|
484
551
|
function extractTextFromContent(content) {
|
|
@@ -787,8 +854,14 @@ export function runCursorPrompt({ prompt, system, resumeSessionId, model, repoRo
|
|
|
787
854
|
if (usage) {
|
|
788
855
|
emit({
|
|
789
856
|
type: 'usage',
|
|
790
|
-
|
|
791
|
-
|
|
857
|
+
usage,
|
|
858
|
+
});
|
|
859
|
+
}
|
|
860
|
+
const contextUsage = extractContextUsage(ev, usage);
|
|
861
|
+
if (contextUsage) {
|
|
862
|
+
emit({
|
|
863
|
+
type: 'context_usage',
|
|
864
|
+
contextUsage,
|
|
792
865
|
});
|
|
793
866
|
}
|
|
794
867
|
if (isErrorEvent(ev)) {
|
package/dist/summary.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Provider, ProviderId } from './types.js';
|
|
2
|
-
export type SummarySource = 'prompt'
|
|
2
|
+
export type SummarySource = 'prompt';
|
|
3
3
|
export type SummaryPayload = {
|
|
4
4
|
summary: string;
|
|
5
5
|
source: SummarySource;
|
|
@@ -9,6 +9,7 @@ export type SummaryPayload = {
|
|
|
9
9
|
};
|
|
10
10
|
export declare function getSummaryModel(providerId: ProviderId): string | null;
|
|
11
11
|
export declare function buildSummaryPrompt(userPrompt: string, reasoning?: string): string;
|
|
12
|
+
export declare function buildSummaryPromptWithOverride(template: string, userPrompt: string, reasoning?: string): string;
|
|
12
13
|
export declare function sanitizeSummary(raw: string): string;
|
|
13
14
|
export declare function runSummaryPrompt(options: {
|
|
14
15
|
provider: Provider;
|
package/dist/summary.js
CHANGED
|
@@ -26,6 +26,7 @@ export function buildSummaryPrompt(userPrompt, reasoning) {
|
|
|
26
26
|
const clippedReasoning = reasoning?.trim() ? clipText(reasoning, REASONING_MAX_CHARS) : '';
|
|
27
27
|
const lines = [
|
|
28
28
|
'You write ultra-short task summaries for a chat list.',
|
|
29
|
+
'Do not run commands or edit files; only write the summary.',
|
|
29
30
|
`Summarize the task in ${Math.max(6, SUMMARY_MAX_WORDS - 4)}-${SUMMARY_MAX_WORDS} words.`,
|
|
30
31
|
'Capture the task and outcome; include key file/component/tech if present.',
|
|
31
32
|
'Use a specific action verb; avoid vague verbs like "help" or "work on".',
|
|
@@ -42,6 +43,27 @@ export function buildSummaryPrompt(userPrompt, reasoning) {
|
|
|
42
43
|
}
|
|
43
44
|
return lines.join('\n');
|
|
44
45
|
}
|
|
46
|
+
export function buildSummaryPromptWithOverride(template, userPrompt, reasoning) {
|
|
47
|
+
const trimmedTemplate = template.trim();
|
|
48
|
+
if (!trimmedTemplate)
|
|
49
|
+
return buildSummaryPrompt(userPrompt, reasoning);
|
|
50
|
+
const clipped = clipText(userPrompt.trim(), 1200);
|
|
51
|
+
const clippedReasoning = reasoning?.trim() ? clipText(reasoning, REASONING_MAX_CHARS) : '';
|
|
52
|
+
const hasMessage = trimmedTemplate.includes('{{message}}');
|
|
53
|
+
const hasReasoning = trimmedTemplate.includes('{{reasoning}}');
|
|
54
|
+
let prompt = trimmedTemplate;
|
|
55
|
+
if (hasMessage)
|
|
56
|
+
prompt = prompt.replaceAll('{{message}}', clipped);
|
|
57
|
+
if (hasReasoning)
|
|
58
|
+
prompt = prompt.replaceAll('{{reasoning}}', clippedReasoning);
|
|
59
|
+
if (!hasMessage) {
|
|
60
|
+
prompt = `${prompt}\n\nUser request:\n${clipped}`;
|
|
61
|
+
}
|
|
62
|
+
if (clippedReasoning && !hasReasoning) {
|
|
63
|
+
prompt = `${prompt}\n\nInitial reasoning (first lines):\n${clippedReasoning}`;
|
|
64
|
+
}
|
|
65
|
+
return prompt;
|
|
66
|
+
}
|
|
45
67
|
export function sanitizeSummary(raw) {
|
|
46
68
|
const normalized = raw
|
|
47
69
|
.replace(/[\r\n]+/g, ' ')
|
package/dist/types.d.ts
CHANGED
|
@@ -116,6 +116,20 @@ export interface ModelInfo {
|
|
|
116
116
|
reasoningEfforts?: ReasoningEffort[];
|
|
117
117
|
defaultReasoningEffort?: string;
|
|
118
118
|
}
|
|
119
|
+
export type TokenUsage = {
|
|
120
|
+
input_tokens?: number;
|
|
121
|
+
output_tokens?: number;
|
|
122
|
+
total_tokens?: number;
|
|
123
|
+
cached_input_tokens?: number;
|
|
124
|
+
reasoning_tokens?: number;
|
|
125
|
+
};
|
|
126
|
+
export type ContextUsage = {
|
|
127
|
+
context_window?: number;
|
|
128
|
+
context_tokens?: number;
|
|
129
|
+
context_cached_tokens?: number;
|
|
130
|
+
context_remaining_tokens?: number;
|
|
131
|
+
context_truncated?: boolean;
|
|
132
|
+
};
|
|
119
133
|
export interface ProviderLoginOptions {
|
|
120
134
|
baseUrl?: string;
|
|
121
135
|
apiKey?: string;
|
|
@@ -125,15 +139,15 @@ export interface ProviderLoginOptions {
|
|
|
125
139
|
loginExperience?: 'embedded' | 'terminal';
|
|
126
140
|
}
|
|
127
141
|
export interface SessionEvent {
|
|
128
|
-
type: 'delta' | 'final' | 'usage' | 'status' | 'error' | 'raw_line' | 'message' | 'thinking' | 'tool_call' | 'detail' | 'summary';
|
|
142
|
+
type: 'delta' | 'final' | 'usage' | 'context_usage' | 'status' | 'error' | 'raw_line' | 'message' | 'thinking' | 'tool_call' | 'detail' | 'summary';
|
|
129
143
|
text?: string;
|
|
130
144
|
message?: string;
|
|
131
145
|
line?: string;
|
|
132
146
|
provider?: ProviderId;
|
|
133
147
|
providerDetail?: ProviderDetail;
|
|
134
148
|
providerSessionId?: string | null;
|
|
135
|
-
|
|
136
|
-
|
|
149
|
+
usage?: TokenUsage;
|
|
150
|
+
contextUsage?: ContextUsage;
|
|
137
151
|
role?: 'system' | 'user' | 'assistant';
|
|
138
152
|
content?: string;
|
|
139
153
|
contentParts?: unknown;
|
|
@@ -146,7 +160,7 @@ export interface SessionEvent {
|
|
|
146
160
|
timestampMs?: number;
|
|
147
161
|
cancelled?: boolean;
|
|
148
162
|
summary?: string;
|
|
149
|
-
source?: 'prompt'
|
|
163
|
+
source?: 'prompt';
|
|
150
164
|
model?: string | null;
|
|
151
165
|
createdAt?: string;
|
|
152
166
|
}
|
|
@@ -195,11 +209,16 @@ export interface SessionState {
|
|
|
195
209
|
repoRoot?: string;
|
|
196
210
|
providerDetailLevel?: ProviderDetailLevel;
|
|
197
211
|
systemPrompt?: string;
|
|
212
|
+
summaryMode?: 'auto' | 'off';
|
|
213
|
+
summaryPrompt?: string;
|
|
214
|
+
summaryNextMode?: 'auto' | 'off' | 'force';
|
|
215
|
+
summaryNextPrompt?: string;
|
|
216
|
+
summaryAutoUsed?: boolean;
|
|
198
217
|
summaryRequested?: boolean;
|
|
199
218
|
summarySeed?: string;
|
|
200
219
|
summaryReasoning?: string;
|
|
201
220
|
summary?: string | null;
|
|
202
|
-
summarySource?: 'prompt'
|
|
221
|
+
summarySource?: 'prompt';
|
|
203
222
|
summaryModel?: string | null;
|
|
204
223
|
summaryCreatedAt?: string;
|
|
205
224
|
}
|