@link-assistant/agent 0.8.18 → 0.8.20
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 +2 -2
- package/src/cli/process-name.ts +41 -0
- package/src/index.js +3 -4
- package/src/session/index.ts +21 -0
- package/src/session/processor.ts +37 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@link-assistant/agent",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.20",
|
|
4
4
|
"description": "A minimal, public domain AI CLI agent compatible with OpenCode's JSON interface. Bun-only runtime.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
"@standard-community/standard-json": "^0.3.5",
|
|
72
72
|
"@standard-community/standard-openapi": "^0.2.9",
|
|
73
73
|
"@zip.js/zip.js": "^2.8.10",
|
|
74
|
-
"ai": "6.0.
|
|
74
|
+
"ai": "^6.0.1",
|
|
75
75
|
"chokidar": "^4.0.3",
|
|
76
76
|
"clipboardy": "^5.0.0",
|
|
77
77
|
"command-stream": "^0.7.1",
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { platform } from 'os';
|
|
2
|
+
import { dlopen, FFIType, ptr } from 'bun:ffi';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Set the process name visible in system monitoring tools (top, ps, htop, etc.).
|
|
6
|
+
*
|
|
7
|
+
* Bun does not implement the process.title setter (unlike Node.js), so we use
|
|
8
|
+
* platform-specific system calls via Bun's FFI:
|
|
9
|
+
* - Linux: prctl(PR_SET_NAME, name) sets /proc/<pid>/comm
|
|
10
|
+
* - macOS: relies on the binary/symlink name (set by `bun install -g`)
|
|
11
|
+
* - Windows: no-op (Task Manager shows the executable name)
|
|
12
|
+
*/
|
|
13
|
+
export function setProcessName(name: string): void {
|
|
14
|
+
// Set in-process values for any JS code that checks them
|
|
15
|
+
process.title = name;
|
|
16
|
+
process.argv0 = name;
|
|
17
|
+
|
|
18
|
+
const os = platform();
|
|
19
|
+
|
|
20
|
+
if (os === 'linux') {
|
|
21
|
+
try {
|
|
22
|
+
const PR_SET_NAME = 15;
|
|
23
|
+
const libc = dlopen('libc.so.6', {
|
|
24
|
+
prctl: {
|
|
25
|
+
args: [FFIType.i32, FFIType.ptr],
|
|
26
|
+
returns: FFIType.i32,
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
// PR_SET_NAME accepts up to 16 bytes including the null terminator
|
|
30
|
+
const buf = Buffer.from(name.slice(0, 15) + '\0');
|
|
31
|
+
libc.symbols.prctl(PR_SET_NAME, ptr(buf));
|
|
32
|
+
libc.close();
|
|
33
|
+
} catch (_e) {
|
|
34
|
+
// Silently ignore - process name is cosmetic
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
// macOS: no userspace API changes the process comm shown in ps/top.
|
|
38
|
+
// When installed via `bun install -g`, the symlink is named 'agent',
|
|
39
|
+
// so macOS will already show 'agent' in ps/top.
|
|
40
|
+
// Windows: Task Manager always shows the executable name.
|
|
41
|
+
}
|
package/src/index.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
process.argv0 = 'agent';
|
|
3
|
+
import { setProcessName } from './cli/process-name.ts';
|
|
4
|
+
|
|
5
|
+
setProcessName('agent');
|
|
7
6
|
|
|
8
7
|
import { Server } from './server/server.ts';
|
|
9
8
|
import { Instance } from './project/instance.ts';
|
package/src/session/index.ts
CHANGED
|
@@ -580,6 +580,27 @@ export namespace Session {
|
|
|
580
580
|
metadata: z.custom<ProviderMetadata>().optional(),
|
|
581
581
|
}),
|
|
582
582
|
(input) => {
|
|
583
|
+
// Handle undefined or null usage gracefully
|
|
584
|
+
// Some providers (e.g., OpenCode with Kimi K2.5) may return incomplete usage data
|
|
585
|
+
// See: https://github.com/link-assistant/agent/issues/152
|
|
586
|
+
if (!input.usage) {
|
|
587
|
+
log.warn(() => ({
|
|
588
|
+
message: 'getUsage received undefined usage, returning zero values',
|
|
589
|
+
providerMetadata: input.metadata
|
|
590
|
+
? JSON.stringify(input.metadata)
|
|
591
|
+
: 'none',
|
|
592
|
+
}));
|
|
593
|
+
return {
|
|
594
|
+
cost: 0,
|
|
595
|
+
tokens: {
|
|
596
|
+
input: 0,
|
|
597
|
+
output: 0,
|
|
598
|
+
reasoning: 0,
|
|
599
|
+
cache: { read: 0, write: 0 },
|
|
600
|
+
},
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
|
|
583
604
|
// Log raw usage data in verbose mode for debugging
|
|
584
605
|
if (Flag.OPENCODE_VERBOSE) {
|
|
585
606
|
log.debug(() => ({
|
package/src/session/processor.ts
CHANGED
|
@@ -219,9 +219,17 @@ export namespace SessionProcessor {
|
|
|
219
219
|
break;
|
|
220
220
|
|
|
221
221
|
case 'finish-step':
|
|
222
|
+
// Safely handle missing or undefined usage data
|
|
223
|
+
// Some providers (e.g., OpenCode with Kimi K2.5) may return incomplete usage
|
|
224
|
+
// See: https://github.com/link-assistant/agent/issues/152
|
|
225
|
+
const safeUsage = value.usage ?? {
|
|
226
|
+
inputTokens: 0,
|
|
227
|
+
outputTokens: 0,
|
|
228
|
+
totalTokens: 0,
|
|
229
|
+
};
|
|
222
230
|
const usage = Session.getUsage({
|
|
223
231
|
model: input.model,
|
|
224
|
-
usage:
|
|
232
|
+
usage: safeUsage,
|
|
225
233
|
metadata: value.providerMetadata,
|
|
226
234
|
});
|
|
227
235
|
// Use toFinishReason to safely convert object/string finishReason to string
|
|
@@ -322,6 +330,34 @@ export namespace SessionProcessor {
|
|
|
322
330
|
}
|
|
323
331
|
} catch (e) {
|
|
324
332
|
log.error(() => ({ message: 'process', error: e }));
|
|
333
|
+
|
|
334
|
+
// Check for AI SDK usage-related TypeError (input_tokens undefined)
|
|
335
|
+
// This happens when providers return incomplete usage data
|
|
336
|
+
// See: https://github.com/link-assistant/agent/issues/152
|
|
337
|
+
if (
|
|
338
|
+
e instanceof TypeError &&
|
|
339
|
+
(e.message.includes('input_tokens') ||
|
|
340
|
+
e.message.includes('output_tokens') ||
|
|
341
|
+
e.message.includes("reading 'input_tokens'") ||
|
|
342
|
+
e.message.includes("reading 'output_tokens'"))
|
|
343
|
+
) {
|
|
344
|
+
log.warn(() => ({
|
|
345
|
+
message:
|
|
346
|
+
'Provider returned invalid usage data, continuing with zero usage',
|
|
347
|
+
errorMessage: e.message,
|
|
348
|
+
providerID: input.providerID,
|
|
349
|
+
}));
|
|
350
|
+
// Set default token values to prevent crash
|
|
351
|
+
input.assistantMessage.tokens = {
|
|
352
|
+
input: 0,
|
|
353
|
+
output: 0,
|
|
354
|
+
reasoning: 0,
|
|
355
|
+
cache: { read: 0, write: 0 },
|
|
356
|
+
};
|
|
357
|
+
// Continue processing instead of failing
|
|
358
|
+
continue;
|
|
359
|
+
}
|
|
360
|
+
|
|
325
361
|
const error = MessageV2.fromError(e, {
|
|
326
362
|
providerID: input.providerID,
|
|
327
363
|
});
|