@link-assistant/agent 0.13.4 → 0.14.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/package.json +1 -1
- package/src/bun/index.ts +42 -2
- package/src/flag/flag.ts +39 -5
- package/src/index.js +20 -20
- package/src/provider/models.ts +5 -2
- package/src/provider/provider.ts +23 -1
- package/src/provider/retry-fetch.ts +19 -19
- package/src/session/message-v2.ts +19 -0
- package/src/session/processor.ts +17 -0
- package/src/session/retry.ts +6 -6
- package/src/session/summary.ts +11 -0
package/package.json
CHANGED
package/src/bun/index.ts
CHANGED
|
@@ -141,6 +141,14 @@ export namespace BunProc {
|
|
|
141
141
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
+
/**
|
|
145
|
+
* Staleness threshold for 'latest' version packages (24 hours).
|
|
146
|
+
* Packages installed as 'latest' will be refreshed after this period.
|
|
147
|
+
* This ensures users get updated packages with bug fixes and new features.
|
|
148
|
+
* @see https://github.com/link-assistant/agent/issues/177
|
|
149
|
+
*/
|
|
150
|
+
const LATEST_VERSION_STALE_THRESHOLD_MS = 24 * 60 * 60 * 1000;
|
|
151
|
+
|
|
144
152
|
export async function install(pkg: string, version = 'latest') {
|
|
145
153
|
const mod = path.join(Global.Path.cache, 'node_modules', pkg);
|
|
146
154
|
|
|
@@ -150,11 +158,41 @@ export namespace BunProc {
|
|
|
150
158
|
|
|
151
159
|
const pkgjson = Bun.file(path.join(Global.Path.cache, 'package.json'));
|
|
152
160
|
const parsed = await pkgjson.json().catch(async () => {
|
|
153
|
-
const result = { dependencies: {} };
|
|
161
|
+
const result = { dependencies: {}, _installTime: {} };
|
|
154
162
|
await Bun.write(pkgjson.name!, JSON.stringify(result, null, 2));
|
|
155
163
|
return result;
|
|
156
164
|
});
|
|
157
|
-
|
|
165
|
+
|
|
166
|
+
// Initialize _installTime tracking if not present
|
|
167
|
+
if (!parsed._installTime) {
|
|
168
|
+
parsed._installTime = {};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Check if package is already installed with the requested version
|
|
172
|
+
const installedVersion = parsed.dependencies[pkg];
|
|
173
|
+
const installTime = parsed._installTime[pkg] as number | undefined;
|
|
174
|
+
|
|
175
|
+
if (installedVersion === version) {
|
|
176
|
+
// For 'latest' version, check if installation is stale and needs refresh
|
|
177
|
+
// This ensures users get updated packages with important fixes
|
|
178
|
+
// @see https://github.com/link-assistant/agent/issues/177 (specificationVersion v3 support)
|
|
179
|
+
if (version === 'latest' && installTime) {
|
|
180
|
+
const age = Date.now() - installTime;
|
|
181
|
+
if (age < LATEST_VERSION_STALE_THRESHOLD_MS) {
|
|
182
|
+
return mod;
|
|
183
|
+
}
|
|
184
|
+
log.info(() => ({
|
|
185
|
+
message: 'refreshing stale latest package',
|
|
186
|
+
pkg,
|
|
187
|
+
version,
|
|
188
|
+
ageMs: age,
|
|
189
|
+
threshold: LATEST_VERSION_STALE_THRESHOLD_MS,
|
|
190
|
+
}));
|
|
191
|
+
} else if (version !== 'latest') {
|
|
192
|
+
// For explicit versions, don't reinstall
|
|
193
|
+
return mod;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
158
196
|
|
|
159
197
|
// Check for dry-run mode
|
|
160
198
|
if (Flag.OPENCODE_DRY_RUN) {
|
|
@@ -205,6 +243,8 @@ export namespace BunProc {
|
|
|
205
243
|
attempt,
|
|
206
244
|
}));
|
|
207
245
|
parsed.dependencies[pkg] = version;
|
|
246
|
+
// Track installation time for 'latest' version staleness checks
|
|
247
|
+
parsed._installTime[pkg] = Date.now();
|
|
208
248
|
await Bun.write(pkgjson.name!, JSON.stringify(parsed, null, 2));
|
|
209
249
|
return mod;
|
|
210
250
|
} catch (e) {
|
package/src/flag/flag.ts
CHANGED
|
@@ -4,6 +4,11 @@ export namespace Flag {
|
|
|
4
4
|
return process.env[newKey] ?? process.env[oldKey];
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
+
function truthy(key: string) {
|
|
8
|
+
const value = process.env[key]?.toLowerCase();
|
|
9
|
+
return value === 'true' || value === '1';
|
|
10
|
+
}
|
|
11
|
+
|
|
7
12
|
function truthyCompat(newKey: string, oldKey: string): boolean {
|
|
8
13
|
const value = (getEnv(newKey, oldKey) ?? '').toLowerCase();
|
|
9
14
|
return value === 'true' || value === '1';
|
|
@@ -77,6 +82,40 @@ export namespace Flag {
|
|
|
77
82
|
GENERATE_TITLE = value;
|
|
78
83
|
}
|
|
79
84
|
|
|
85
|
+
// Output response model information in step-finish parts
|
|
86
|
+
// Enabled by default - includes model info (providerID, requestedModelID, respondedModelID) in output
|
|
87
|
+
// Can be disabled with AGENT_OUTPUT_RESPONSE_MODEL=false
|
|
88
|
+
// See: https://github.com/link-assistant/agent/issues/179
|
|
89
|
+
export let OUTPUT_RESPONSE_MODEL = (() => {
|
|
90
|
+
const value = (
|
|
91
|
+
getEnv(
|
|
92
|
+
'LINK_ASSISTANT_AGENT_OUTPUT_RESPONSE_MODEL',
|
|
93
|
+
'AGENT_OUTPUT_RESPONSE_MODEL'
|
|
94
|
+
) ?? ''
|
|
95
|
+
).toLowerCase();
|
|
96
|
+
if (value === 'false' || value === '0') return false;
|
|
97
|
+
return true; // Default to true
|
|
98
|
+
})();
|
|
99
|
+
|
|
100
|
+
// Allow setting output-response-model mode programmatically (e.g., from CLI --output-response-model flag)
|
|
101
|
+
export function setOutputResponseModel(value: boolean) {
|
|
102
|
+
OUTPUT_RESPONSE_MODEL = value;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Session summarization configuration
|
|
106
|
+
// When disabled, session summaries will not be generated
|
|
107
|
+
// This saves tokens and prevents rate limit issues with free tier models
|
|
108
|
+
// See: https://github.com/link-assistant/agent/issues/179
|
|
109
|
+
export let SUMMARIZE_SESSION = truthyCompat(
|
|
110
|
+
'LINK_ASSISTANT_AGENT_SUMMARIZE_SESSION',
|
|
111
|
+
'AGENT_SUMMARIZE_SESSION'
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
// Allow setting summarize-session mode programmatically (e.g., from CLI --summarize-session flag)
|
|
115
|
+
export function setSummarizeSession(value: boolean) {
|
|
116
|
+
SUMMARIZE_SESSION = value;
|
|
117
|
+
}
|
|
118
|
+
|
|
80
119
|
// Retry timeout configuration
|
|
81
120
|
// Maximum total time to keep retrying for the same error type (default: 7 days in seconds)
|
|
82
121
|
// For different error types, the timer resets
|
|
@@ -155,9 +194,4 @@ export namespace Flag {
|
|
|
155
194
|
export function setCompactJson(value: boolean) {
|
|
156
195
|
_compactJson = value;
|
|
157
196
|
}
|
|
158
|
-
|
|
159
|
-
function truthy(key: string) {
|
|
160
|
-
const value = process.env[key]?.toLowerCase();
|
|
161
|
-
return value === 'true' || value === '1';
|
|
162
|
-
}
|
|
163
197
|
}
|
package/src/index.js
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
-
|
|
2
|
+
import { Flag } from './flag/flag.ts';
|
|
3
3
|
import { setProcessName } from './cli/process-name.ts';
|
|
4
|
-
|
|
5
4
|
setProcessName('agent');
|
|
6
|
-
|
|
7
5
|
import { Server } from './server/server.ts';
|
|
8
6
|
import { Instance } from './project/instance.ts';
|
|
9
7
|
import { Log } from './util/log.ts';
|
|
@@ -19,7 +17,6 @@ import {
|
|
|
19
17
|
} from './json-standard/index.ts';
|
|
20
18
|
import { McpCommand } from './cli/cmd/mcp.ts';
|
|
21
19
|
import { AuthCommand } from './cli/cmd/auth.ts';
|
|
22
|
-
import { Flag } from './flag/flag.ts';
|
|
23
20
|
import { FormatError } from './cli/error.ts';
|
|
24
21
|
import { UI } from './cli/ui.ts';
|
|
25
22
|
import {
|
|
@@ -745,6 +742,16 @@ async function main() {
|
|
|
745
742
|
type: 'number',
|
|
746
743
|
description:
|
|
747
744
|
'Maximum total retry time in seconds for rate limit errors (default: 604800 = 7 days)',
|
|
745
|
+
})
|
|
746
|
+
.option('output-response-model', {
|
|
747
|
+
type: 'boolean',
|
|
748
|
+
description: 'Include model info in step_finish output',
|
|
749
|
+
default: true,
|
|
750
|
+
})
|
|
751
|
+
.option('summarize-session', {
|
|
752
|
+
type: 'boolean',
|
|
753
|
+
description: 'Generate AI session summaries',
|
|
754
|
+
default: false,
|
|
748
755
|
}),
|
|
749
756
|
handler: async (argv) => {
|
|
750
757
|
// Check both CLI flag and environment variable for compact JSON mode
|
|
@@ -908,37 +915,30 @@ async function main() {
|
|
|
908
915
|
await runAgentMode(argv, request);
|
|
909
916
|
},
|
|
910
917
|
})
|
|
911
|
-
// Initialize logging early for all CLI commands
|
|
912
|
-
// This prevents debug output from appearing in CLI unless --verbose is used
|
|
918
|
+
// Initialize logging and flags early for all CLI commands
|
|
913
919
|
.middleware(async (argv) => {
|
|
914
|
-
// Set global compact JSON setting (CLI flag or environment variable)
|
|
915
920
|
const isCompact = argv['compact-json'] === true || Flag.COMPACT_JSON();
|
|
916
921
|
if (isCompact) {
|
|
917
922
|
setCompactJson(true);
|
|
918
923
|
}
|
|
919
|
-
|
|
920
|
-
// Set verbose flag if requested
|
|
921
924
|
if (argv.verbose) {
|
|
922
925
|
Flag.setVerbose(true);
|
|
923
926
|
}
|
|
924
|
-
|
|
925
|
-
// Set dry-run flag if requested
|
|
926
927
|
if (argv['dry-run']) {
|
|
927
928
|
Flag.setDryRun(true);
|
|
928
929
|
}
|
|
929
|
-
|
|
930
|
-
// Set generate-title flag if explicitly enabled
|
|
931
|
-
// Default is false to save tokens and prevent rate limit issues
|
|
932
|
-
// See: https://github.com/link-assistant/agent/issues/157
|
|
933
930
|
if (argv['generate-title'] === true) {
|
|
934
931
|
Flag.setGenerateTitle(true);
|
|
935
932
|
}
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
933
|
+
// output-response-model is enabled by default, only set if explicitly disabled
|
|
934
|
+
if (argv['output-response-model'] === false) {
|
|
935
|
+
Flag.setOutputResponseModel(false);
|
|
936
|
+
}
|
|
937
|
+
if (argv['summarize-session'] === true) {
|
|
938
|
+
Flag.setSummarizeSession(true);
|
|
939
|
+
}
|
|
940
940
|
await Log.init({
|
|
941
|
-
print: Flag.OPENCODE_VERBOSE,
|
|
941
|
+
print: Flag.OPENCODE_VERBOSE,
|
|
942
942
|
level: Flag.OPENCODE_VERBOSE ? 'DEBUG' : 'INFO',
|
|
943
943
|
compactJson: isCompact,
|
|
944
944
|
});
|
package/src/provider/models.ts
CHANGED
|
@@ -131,8 +131,11 @@ export namespace ModelsDev {
|
|
|
131
131
|
if (result) return result as Record<string, Provider>;
|
|
132
132
|
|
|
133
133
|
// Fallback to bundled data if cache read failed
|
|
134
|
-
|
|
135
|
-
|
|
134
|
+
// This is expected behavior when the cache is unavailable or corrupted
|
|
135
|
+
// Using info level since bundled data is a valid fallback mechanism
|
|
136
|
+
// @see https://github.com/link-assistant/agent/issues/177
|
|
137
|
+
log.info(() => ({
|
|
138
|
+
message: 'cache unavailable, using bundled data',
|
|
136
139
|
path: filepath,
|
|
137
140
|
}));
|
|
138
141
|
const json = await data();
|
package/src/provider/provider.ts
CHANGED
|
@@ -1297,11 +1297,25 @@ export namespace Provider {
|
|
|
1297
1297
|
}
|
|
1298
1298
|
}
|
|
1299
1299
|
|
|
1300
|
+
/**
|
|
1301
|
+
* Get a small/cheap model for auxiliary tasks like title generation and summarization.
|
|
1302
|
+
* This is NOT the primary model for user requests - it's used for background tasks.
|
|
1303
|
+
*
|
|
1304
|
+
* Note: Logs from this function may show a different model than what the user specified.
|
|
1305
|
+
* This is by design - we use cheaper models for auxiliary tasks to save tokens/costs.
|
|
1306
|
+
*
|
|
1307
|
+
* @see https://github.com/link-assistant/agent/issues/179
|
|
1308
|
+
*/
|
|
1300
1309
|
export async function getSmallModel(providerID: string) {
|
|
1301
1310
|
const cfg = await Config.get();
|
|
1302
1311
|
|
|
1303
1312
|
if (cfg.small_model) {
|
|
1304
1313
|
const parsed = parseModel(cfg.small_model);
|
|
1314
|
+
log.info(() => ({
|
|
1315
|
+
message: 'using configured small_model for auxiliary task',
|
|
1316
|
+
modelID: parsed.modelID,
|
|
1317
|
+
providerID: parsed.providerID,
|
|
1318
|
+
}));
|
|
1305
1319
|
return getModel(parsed.providerID, parsed.modelID);
|
|
1306
1320
|
}
|
|
1307
1321
|
|
|
@@ -1339,7 +1353,15 @@ export namespace Provider {
|
|
|
1339
1353
|
}
|
|
1340
1354
|
for (const item of priority) {
|
|
1341
1355
|
for (const model of Object.keys(provider.info.models)) {
|
|
1342
|
-
if (model.includes(item))
|
|
1356
|
+
if (model.includes(item)) {
|
|
1357
|
+
log.info(() => ({
|
|
1358
|
+
message: 'selected small model for auxiliary task',
|
|
1359
|
+
modelID: model,
|
|
1360
|
+
providerID,
|
|
1361
|
+
hint: 'This model is used for title/summary generation, not primary requests',
|
|
1362
|
+
}));
|
|
1363
|
+
return getModel(providerID, model);
|
|
1364
|
+
}
|
|
1343
1365
|
}
|
|
1344
1366
|
}
|
|
1345
1367
|
}
|
|
@@ -130,8 +130,8 @@ export namespace RetryFetch {
|
|
|
130
130
|
log.info(() => ({
|
|
131
131
|
message: 'using retry-after value',
|
|
132
132
|
retryAfterMs,
|
|
133
|
-
delay,
|
|
134
|
-
minInterval,
|
|
133
|
+
delayMs: delay,
|
|
134
|
+
minIntervalMs: minInterval,
|
|
135
135
|
}));
|
|
136
136
|
return addJitter(delay);
|
|
137
137
|
}
|
|
@@ -145,10 +145,10 @@ export namespace RetryFetch {
|
|
|
145
145
|
log.info(() => ({
|
|
146
146
|
message: 'no retry-after header, using exponential backoff',
|
|
147
147
|
attempt,
|
|
148
|
-
backoffDelay,
|
|
149
|
-
delay,
|
|
150
|
-
minInterval,
|
|
151
|
-
maxBackoffDelay,
|
|
148
|
+
backoffDelayMs: backoffDelay,
|
|
149
|
+
delayMs: delay,
|
|
150
|
+
minIntervalMs: minInterval,
|
|
151
|
+
maxBackoffDelayMs: maxBackoffDelay,
|
|
152
152
|
}));
|
|
153
153
|
return addJitter(delay);
|
|
154
154
|
}
|
|
@@ -334,8 +334,8 @@ export namespace RetryFetch {
|
|
|
334
334
|
message:
|
|
335
335
|
'network error retry timeout exceeded, re-throwing error',
|
|
336
336
|
sessionID,
|
|
337
|
-
elapsed,
|
|
338
|
-
maxRetryTimeout,
|
|
337
|
+
elapsedMs: elapsed,
|
|
338
|
+
maxRetryTimeoutMs: maxRetryTimeout,
|
|
339
339
|
error: (error as Error).message,
|
|
340
340
|
}));
|
|
341
341
|
throw error;
|
|
@@ -350,7 +350,7 @@ export namespace RetryFetch {
|
|
|
350
350
|
message: 'network error, retrying',
|
|
351
351
|
sessionID,
|
|
352
352
|
attempt,
|
|
353
|
-
delay,
|
|
353
|
+
delayMs: delay,
|
|
354
354
|
error: (error as Error).message,
|
|
355
355
|
}));
|
|
356
356
|
await sleep(delay, init?.signal ?? undefined);
|
|
@@ -370,8 +370,8 @@ export namespace RetryFetch {
|
|
|
370
370
|
log.warn(() => ({
|
|
371
371
|
message: 'retry timeout exceeded in fetch wrapper, returning 429',
|
|
372
372
|
sessionID,
|
|
373
|
-
elapsed,
|
|
374
|
-
maxRetryTimeout,
|
|
373
|
+
elapsedMs: elapsed,
|
|
374
|
+
maxRetryTimeoutMs: maxRetryTimeout,
|
|
375
375
|
}));
|
|
376
376
|
return response; // Let higher-level handling take over
|
|
377
377
|
}
|
|
@@ -390,8 +390,8 @@ export namespace RetryFetch {
|
|
|
390
390
|
message:
|
|
391
391
|
'retry-after exceeds remaining timeout, returning 429 response',
|
|
392
392
|
sessionID,
|
|
393
|
-
elapsed,
|
|
394
|
-
|
|
393
|
+
elapsedMs: elapsed,
|
|
394
|
+
remainingTimeoutMs: maxRetryTimeout - elapsed,
|
|
395
395
|
}));
|
|
396
396
|
return response;
|
|
397
397
|
}
|
|
@@ -401,9 +401,9 @@ export namespace RetryFetch {
|
|
|
401
401
|
log.warn(() => ({
|
|
402
402
|
message: 'delay would exceed retry timeout, returning 429 response',
|
|
403
403
|
sessionID,
|
|
404
|
-
elapsed,
|
|
405
|
-
delay,
|
|
406
|
-
maxRetryTimeout,
|
|
404
|
+
elapsedMs: elapsed,
|
|
405
|
+
delayMs: delay,
|
|
406
|
+
maxRetryTimeoutMs: maxRetryTimeout,
|
|
407
407
|
}));
|
|
408
408
|
return response;
|
|
409
409
|
}
|
|
@@ -414,11 +414,11 @@ export namespace RetryFetch {
|
|
|
414
414
|
message: 'rate limited, will retry',
|
|
415
415
|
sessionID,
|
|
416
416
|
attempt,
|
|
417
|
-
delay,
|
|
417
|
+
delayMs: delay,
|
|
418
418
|
delayMinutes: (delay / 1000 / 60).toFixed(2),
|
|
419
419
|
delayHours: (delay / 1000 / 3600).toFixed(2),
|
|
420
|
-
elapsed,
|
|
421
|
-
remainingTimeout,
|
|
420
|
+
elapsedMs: elapsed,
|
|
421
|
+
remainingTimeoutMs: remainingTimeout,
|
|
422
422
|
remainingTimeoutHours: (remainingTimeout / 1000 / 3600).toFixed(2),
|
|
423
423
|
isolatedSignal: true, // Indicates we're using isolated signal for this wait
|
|
424
424
|
}));
|
|
@@ -224,6 +224,22 @@ export namespace MessageV2 {
|
|
|
224
224
|
});
|
|
225
225
|
export type StepStartPart = z.infer<typeof StepStartPart>;
|
|
226
226
|
|
|
227
|
+
/**
|
|
228
|
+
* Model information for output parts.
|
|
229
|
+
* Included when --output-response-model flag is enabled.
|
|
230
|
+
* @see https://github.com/link-assistant/agent/issues/179
|
|
231
|
+
*/
|
|
232
|
+
export const ModelInfo = z
|
|
233
|
+
.object({
|
|
234
|
+
providerID: z.string(),
|
|
235
|
+
requestedModelID: z.string(),
|
|
236
|
+
respondedModelID: z.string().optional(),
|
|
237
|
+
})
|
|
238
|
+
.meta({
|
|
239
|
+
ref: 'ModelInfo',
|
|
240
|
+
});
|
|
241
|
+
export type ModelInfo = z.infer<typeof ModelInfo>;
|
|
242
|
+
|
|
227
243
|
export const StepFinishPart = PartBase.extend({
|
|
228
244
|
type: z.literal('step-finish'),
|
|
229
245
|
reason: z.string(),
|
|
@@ -238,6 +254,9 @@ export namespace MessageV2 {
|
|
|
238
254
|
write: z.number(),
|
|
239
255
|
}),
|
|
240
256
|
}),
|
|
257
|
+
// Model info included when --output-response-model is enabled
|
|
258
|
+
// @see https://github.com/link-assistant/agent/issues/179
|
|
259
|
+
model: ModelInfo.optional(),
|
|
241
260
|
}).meta({
|
|
242
261
|
ref: 'StepFinishPart',
|
|
243
262
|
});
|
package/src/session/processor.ts
CHANGED
|
@@ -16,6 +16,7 @@ import { SessionSummary } from './summary';
|
|
|
16
16
|
import { Bus } from '../bus';
|
|
17
17
|
import { SessionRetry } from './retry';
|
|
18
18
|
import { SessionStatus } from './status';
|
|
19
|
+
import { Flag } from '../flag/flag';
|
|
19
20
|
|
|
20
21
|
export namespace SessionProcessor {
|
|
21
22
|
const DOOM_LOOP_THRESHOLD = 3;
|
|
@@ -261,6 +262,21 @@ export namespace SessionProcessor {
|
|
|
261
262
|
input.assistantMessage.finish = finishReason;
|
|
262
263
|
input.assistantMessage.cost += usage.cost;
|
|
263
264
|
input.assistantMessage.tokens = usage.tokens;
|
|
265
|
+
|
|
266
|
+
// Build model info if --output-response-model flag is enabled
|
|
267
|
+
// @see https://github.com/link-assistant/agent/issues/179
|
|
268
|
+
const modelInfo: MessageV2.ModelInfo | undefined =
|
|
269
|
+
Flag.OUTPUT_RESPONSE_MODEL
|
|
270
|
+
? {
|
|
271
|
+
providerID: input.providerID,
|
|
272
|
+
requestedModelID: input.model.id,
|
|
273
|
+
// Get respondedModelID from finish-step response if available
|
|
274
|
+
// AI SDK includes response.modelId when available from provider
|
|
275
|
+
respondedModelID:
|
|
276
|
+
(value as any).response?.modelId ?? undefined,
|
|
277
|
+
}
|
|
278
|
+
: undefined;
|
|
279
|
+
|
|
264
280
|
await Session.updatePart({
|
|
265
281
|
id: Identifier.ascending('part'),
|
|
266
282
|
reason: finishReason,
|
|
@@ -270,6 +286,7 @@ export namespace SessionProcessor {
|
|
|
270
286
|
type: 'step-finish',
|
|
271
287
|
tokens: usage.tokens,
|
|
272
288
|
cost: usage.cost,
|
|
289
|
+
model: modelInfo,
|
|
273
290
|
});
|
|
274
291
|
await Session.updateMessage(input.assistantMessage);
|
|
275
292
|
if (snapshot) {
|
package/src/session/retry.ts
CHANGED
|
@@ -94,8 +94,8 @@ export namespace SessionRetry {
|
|
|
94
94
|
message: 'retry timeout exceeded',
|
|
95
95
|
sessionID,
|
|
96
96
|
errorType,
|
|
97
|
-
elapsedTime,
|
|
98
|
-
maxTime,
|
|
97
|
+
elapsedTimeMs: elapsedTime,
|
|
98
|
+
maxTimeMs: maxTime,
|
|
99
99
|
}));
|
|
100
100
|
return { shouldRetry: false, elapsedTime, maxTime };
|
|
101
101
|
}
|
|
@@ -245,8 +245,8 @@ export namespace SessionRetry {
|
|
|
245
245
|
log.info(() => ({
|
|
246
246
|
message: 'no retry-after header, using exponential backoff',
|
|
247
247
|
attempt,
|
|
248
|
-
backoffDelay,
|
|
249
|
-
maxBackoffDelay,
|
|
248
|
+
backoffDelayMs: backoffDelay,
|
|
249
|
+
maxBackoffDelayMs: maxBackoffDelay,
|
|
250
250
|
}));
|
|
251
251
|
return addJitter(backoffDelay);
|
|
252
252
|
}
|
|
@@ -260,8 +260,8 @@ export namespace SessionRetry {
|
|
|
260
260
|
message:
|
|
261
261
|
'no response headers, using exponential backoff with conservative cap',
|
|
262
262
|
attempt,
|
|
263
|
-
backoffDelay,
|
|
264
|
-
|
|
263
|
+
backoffDelayMs: backoffDelay,
|
|
264
|
+
maxCapMs: RETRY_MAX_DELAY_NO_HEADERS,
|
|
265
265
|
}));
|
|
266
266
|
return addJitter(backoffDelay);
|
|
267
267
|
}
|
package/src/session/summary.ts
CHANGED
|
@@ -13,6 +13,7 @@ import path from 'path';
|
|
|
13
13
|
import { Instance } from '../project/instance';
|
|
14
14
|
import { Storage } from '../storage/storage';
|
|
15
15
|
import { Bus } from '../bus';
|
|
16
|
+
import { Flag } from '../flag/flag';
|
|
16
17
|
|
|
17
18
|
export namespace SessionSummary {
|
|
18
19
|
const log = Log.create({ service: 'session.summary' });
|
|
@@ -79,6 +80,16 @@ export namespace SessionSummary {
|
|
|
79
80
|
};
|
|
80
81
|
await Session.updateMessage(userMsg);
|
|
81
82
|
|
|
83
|
+
// Skip AI-powered summarization if disabled (default)
|
|
84
|
+
// See: https://github.com/link-assistant/agent/issues/179
|
|
85
|
+
if (!Flag.SUMMARIZE_SESSION) {
|
|
86
|
+
log.info(() => ({
|
|
87
|
+
message: 'session summarization disabled',
|
|
88
|
+
hint: 'Enable with --summarize-session flag or AGENT_SUMMARIZE_SESSION=true',
|
|
89
|
+
}));
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
82
93
|
const assistantMsg = messages.find((m) => m.info.role === 'assistant')!
|
|
83
94
|
.info as MessageV2.Assistant;
|
|
84
95
|
const small = await Provider.getSmallModel(assistantMsg.providerID);
|