linkshell-cli 0.5.14 → 0.5.15
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/cli/src/runtime/acp/agent-workspace.js +62 -27
- package/dist/cli/src/runtime/acp/agent-workspace.js.map +1 -1
- package/dist/cli/src/runtime/acp/claude-sdk-client.js +10 -0
- package/dist/cli/src/runtime/acp/claude-sdk-client.js.map +1 -1
- package/dist/cli/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/runtime/acp/agent-workspace.ts +72 -33
- package/src/runtime/acp/claude-sdk-client.ts +9 -0
- package/web/assets/{index-BZodQjC_.js → index-DAgnaJ6t.js} +1 -1
- package/web/index.html +1 -1
|
@@ -68,6 +68,46 @@ function contextWindowForModel(model) {
|
|
|
68
68
|
}
|
|
69
69
|
return undefined;
|
|
70
70
|
}
|
|
71
|
+
// Normalize a raw provider usage payload (Claude message.usage / result.usage,
|
|
72
|
+
// Codex thread/tokenUsage/updated) into our wire field names. Provider field
|
|
73
|
+
// names vary, so probe snake_case + camelCase. Shared by the live-turn path and
|
|
74
|
+
// the on-disk-transcript path so the two never drift. Returns undefined when no
|
|
75
|
+
// token field resolves. Cost + updatedAt are layered on by callers.
|
|
76
|
+
function normalizeUsageFields(raw, model) {
|
|
77
|
+
const rec = asRecord(raw);
|
|
78
|
+
if (!rec)
|
|
79
|
+
return undefined;
|
|
80
|
+
const num = (...keys) => {
|
|
81
|
+
for (const k of keys) {
|
|
82
|
+
const v = rec[k];
|
|
83
|
+
if (typeof v === "number" && Number.isFinite(v))
|
|
84
|
+
return v;
|
|
85
|
+
}
|
|
86
|
+
return undefined;
|
|
87
|
+
};
|
|
88
|
+
const inputTokens = num("input_tokens", "inputTokens", "input");
|
|
89
|
+
const outputTokens = num("output_tokens", "outputTokens", "output");
|
|
90
|
+
const cacheReadTokens = num("cache_read_input_tokens", "cacheReadInputTokens", "cache_read_tokens", "cached_input_tokens", "cacheReadTokens");
|
|
91
|
+
let totalTokens = num("total_tokens", "totalTokens", "total");
|
|
92
|
+
if (totalTokens == null && (inputTokens != null || outputTokens != null)) {
|
|
93
|
+
totalTokens = (inputTokens ?? 0) + (outputTokens ?? 0);
|
|
94
|
+
}
|
|
95
|
+
if (inputTokens == null && outputTokens == null && cacheReadTokens == null && totalTokens == null) {
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
let contextWindow = num("context_window", "contextWindow", "model_context_window", "window") ??
|
|
99
|
+
contextWindowForModel(model);
|
|
100
|
+
// Self-correcting window: tokens-in-context can't exceed the window, so if the
|
|
101
|
+
// measured occupancy (input + cache reads) already overshoots our assumed
|
|
102
|
+
// window, the real window must be larger — escalate to 1M. Fixes Claude 1M
|
|
103
|
+
// sessions, whose transcript records the model as plain "claude-opus-4.8"
|
|
104
|
+
// (no [1m] tag), which would otherwise show a misleading 100%.
|
|
105
|
+
const ctxUsed = (inputTokens ?? 0) + (cacheReadTokens ?? 0);
|
|
106
|
+
if (contextWindow && ctxUsed > contextWindow) {
|
|
107
|
+
contextWindow = ctxUsed > 1_000_000 ? ctxUsed : 1_000_000;
|
|
108
|
+
}
|
|
109
|
+
return { inputTokens, outputTokens, cacheReadTokens, totalTokens, contextWindow };
|
|
110
|
+
}
|
|
71
111
|
function stringArray(value) {
|
|
72
112
|
if (!Array.isArray(value))
|
|
73
113
|
return [];
|
|
@@ -1081,6 +1121,7 @@ function parseRemoteSessions(value) {
|
|
|
1081
1121
|
model: firstString(source, ["model", "modelId"]),
|
|
1082
1122
|
createdAt: parseTimestamp(source.createdAt ?? source.created_at),
|
|
1083
1123
|
lastActivityAt: parseTimestamp(source.lastActivityAt ?? source.updatedAt ?? source.modifiedAt ?? source.lastModified ?? source.updated_at),
|
|
1124
|
+
usage: source.usage,
|
|
1084
1125
|
});
|
|
1085
1126
|
}
|
|
1086
1127
|
return result;
|
|
@@ -1695,13 +1736,21 @@ export class AgentWorkspaceProxy {
|
|
|
1695
1736
|
const conversationId = existingId ?? makeAgentV2RemoteConversationId(provider, agentSessionId);
|
|
1696
1737
|
const existing = this.conversations.get(conversationId);
|
|
1697
1738
|
const cwd = remote.cwd ?? existing?.cwd ?? this.input.cwd;
|
|
1739
|
+
const model = remote.model ?? existing?.model;
|
|
1740
|
+
// Usage read straight from the on-disk transcript (decoupled from any
|
|
1741
|
+
// live LinkShell turn) — so history + externally-started sessions all
|
|
1742
|
+
// carry a context meter. Falls back to any existing live-turn usage.
|
|
1743
|
+
const usageFields = normalizeUsageFields(remote.usage, model);
|
|
1744
|
+
const usage = usageFields
|
|
1745
|
+
? { ...usageFields, updatedAt: remote.lastActivityAt ?? now }
|
|
1746
|
+
: existing?.usage;
|
|
1698
1747
|
const conversation = {
|
|
1699
1748
|
id: conversationId,
|
|
1700
1749
|
agentSessionId,
|
|
1701
1750
|
provider,
|
|
1702
1751
|
cwd,
|
|
1703
1752
|
title: remote.title ?? existing?.title ?? titleFromCwd(cwd),
|
|
1704
|
-
model
|
|
1753
|
+
model,
|
|
1705
1754
|
reasoningEffort: existing?.reasoningEffort,
|
|
1706
1755
|
permissionMode: existing?.permissionMode,
|
|
1707
1756
|
collaborationMode: existing?.collaborationMode,
|
|
@@ -1710,6 +1759,7 @@ export class AgentWorkspaceProxy {
|
|
|
1710
1759
|
lastMessagePreview: existing?.lastMessagePreview,
|
|
1711
1760
|
lastActivityAt: remote.lastActivityAt ?? existing?.lastActivityAt ?? now,
|
|
1712
1761
|
createdAt: remote.createdAt ?? existing?.createdAt ?? now,
|
|
1762
|
+
usage,
|
|
1713
1763
|
};
|
|
1714
1764
|
this.conversations.set(conversation.id, conversation);
|
|
1715
1765
|
this.conversationByAgentSessionId.set(agentSessionId, conversation.id);
|
|
@@ -3405,38 +3455,23 @@ export class AgentWorkspaceProxy {
|
|
|
3405
3455
|
}
|
|
3406
3456
|
// Normalize a provider usage payload (Claude result.usage, Codex
|
|
3407
3457
|
// thread/tokenUsage/updated) into our wire shape, merge onto the conversation,
|
|
3408
|
-
// and emit.
|
|
3409
|
-
//
|
|
3410
|
-
//
|
|
3458
|
+
// and emit. Missing fields preserve the previous value (cumulative view).
|
|
3459
|
+
// No-ops when nothing meaningful resolves, so a stray empty payload won't
|
|
3460
|
+
// blank the meter.
|
|
3411
3461
|
applyConversationUsage(conversationId, raw, costUsd) {
|
|
3412
3462
|
const conversation = this.conversations.get(conversationId);
|
|
3413
3463
|
if (!conversation)
|
|
3414
3464
|
return;
|
|
3415
|
-
const
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
const v = rec?.[k];
|
|
3419
|
-
if (typeof v === "number" && Number.isFinite(v))
|
|
3420
|
-
return v;
|
|
3421
|
-
}
|
|
3422
|
-
return undefined;
|
|
3423
|
-
};
|
|
3424
|
-
const inputTokens = num("input_tokens", "inputTokens", "input");
|
|
3425
|
-
const outputTokens = num("output_tokens", "outputTokens", "output");
|
|
3426
|
-
const cacheReadTokens = num("cache_read_input_tokens", "cacheReadInputTokens", "cache_read_tokens", "cached_input_tokens", "cacheReadTokens");
|
|
3427
|
-
let totalTokens = num("total_tokens", "totalTokens", "total");
|
|
3428
|
-
if (totalTokens == null && (inputTokens != null || outputTokens != null)) {
|
|
3429
|
-
totalTokens = (inputTokens ?? 0) + (outputTokens ?? 0);
|
|
3430
|
-
}
|
|
3431
|
-
const contextWindow = num("context_window", "contextWindow", "model_context_window", "window") ??
|
|
3432
|
-
contextWindowForModel(conversation.model);
|
|
3465
|
+
const fields = normalizeUsageFields(raw, conversation.model);
|
|
3466
|
+
if (!fields && costUsd == null)
|
|
3467
|
+
return;
|
|
3433
3468
|
const prev = conversation.usage ?? {};
|
|
3434
3469
|
const next = {
|
|
3435
|
-
inputTokens: inputTokens ?? prev.inputTokens,
|
|
3436
|
-
outputTokens: outputTokens ?? prev.outputTokens,
|
|
3437
|
-
cacheReadTokens: cacheReadTokens ?? prev.cacheReadTokens,
|
|
3438
|
-
totalTokens: totalTokens ?? prev.totalTokens,
|
|
3439
|
-
contextWindow: contextWindow ?? prev.contextWindow,
|
|
3470
|
+
inputTokens: fields?.inputTokens ?? prev.inputTokens,
|
|
3471
|
+
outputTokens: fields?.outputTokens ?? prev.outputTokens,
|
|
3472
|
+
cacheReadTokens: fields?.cacheReadTokens ?? prev.cacheReadTokens,
|
|
3473
|
+
totalTokens: fields?.totalTokens ?? prev.totalTokens,
|
|
3474
|
+
contextWindow: fields?.contextWindow ?? prev.contextWindow,
|
|
3440
3475
|
totalCostUsd: costUsd ?? prev.totalCostUsd,
|
|
3441
3476
|
updatedAt: Date.now(),
|
|
3442
3477
|
};
|