tide-commander 0.69.1 → 0.69.2
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/assets/main-BOFXGJQm.css +1 -0
- package/dist/assets/{main-CkwIbXCi.js → main-DkIjeEeR.js} +2 -2
- package/dist/index.html +2 -2
- package/dist/src/packages/server/services/runtime-events.js +46 -73
- package/dist/src/packages/server/websocket/listeners/runtime-listeners.js +2 -1
- package/package.json +1 -1
- package/dist/assets/main-CzElx71o.css +0 -1
package/dist/index.html
CHANGED
|
@@ -22,11 +22,11 @@
|
|
|
22
22
|
<link rel="icon" type="image/png" sizes="16x16" href="/assets/icons/favicon-16x16.png" />
|
|
23
23
|
<link rel="apple-touch-icon" sizes="180x180" href="/assets/icons/apple-touch-icon.png" />
|
|
24
24
|
<title>Tide Commander</title>
|
|
25
|
-
<script type="module" crossorigin src="/assets/main-
|
|
25
|
+
<script type="module" crossorigin src="/assets/main-DkIjeEeR.js"></script>
|
|
26
26
|
<link rel="modulepreload" crossorigin href="/assets/modulepreload-polyfill-B5Qt9EMX.js">
|
|
27
27
|
<link rel="modulepreload" crossorigin href="/assets/vendor-react-uS-d4TUT.js">
|
|
28
28
|
<link rel="modulepreload" crossorigin href="/assets/vendor-three-DJ4p3FLF.js">
|
|
29
|
-
<link rel="stylesheet" crossorigin href="/assets/main-
|
|
29
|
+
<link rel="stylesheet" crossorigin href="/assets/main-BOFXGJQm.css">
|
|
30
30
|
</head>
|
|
31
31
|
<body>
|
|
32
32
|
<div id="app"></div>
|
|
@@ -113,6 +113,11 @@ export function createRuntimeEventHandlers(deps) {
|
|
|
113
113
|
agentService.updateAgent(agentId, { currentTool: undefined });
|
|
114
114
|
break;
|
|
115
115
|
case 'usage_snapshot': {
|
|
116
|
+
// Skip subagent events — their token counts reflect the subagent's context,
|
|
117
|
+
// not the parent's. Updating the parent's contextUsed here would corrupt it.
|
|
118
|
+
if (event.parentToolUseId) {
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
116
121
|
// Real-time context tracking from streaming usage data.
|
|
117
122
|
// Context window usage = input tokens only (output tokens don't count toward the limit).
|
|
118
123
|
// total_input = cache_read + cache_creation + input_tokens (the full prompt size).
|
|
@@ -151,6 +156,19 @@ export function createRuntimeEventHandlers(deps) {
|
|
|
151
156
|
break;
|
|
152
157
|
}
|
|
153
158
|
case 'step_complete': {
|
|
159
|
+
// For subagent events, only accumulate cost (tokensUsed) but don't touch
|
|
160
|
+
// contextUsed/contextLimit/status — those belong to the subagent's context
|
|
161
|
+
// window, not the parent's.
|
|
162
|
+
if (event.parentToolUseId) {
|
|
163
|
+
const subTokensUsed = (event.tokens?.input || 0) + (event.tokens?.output || 0);
|
|
164
|
+
if (subTokensUsed > 0) {
|
|
165
|
+
agentService.updateAgent(agentId, {
|
|
166
|
+
tokensUsed: (agent.tokensUsed || 0) + subTokensUsed,
|
|
167
|
+
}, false);
|
|
168
|
+
}
|
|
169
|
+
log.log(`[step_complete] Subagent event for ${agentId} (parentToolUseId=${event.parentToolUseId}); skipping context update, added ${subTokensUsed} to tokensUsed`);
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
154
172
|
markStepCompleteReceived(agentId);
|
|
155
173
|
const isClaudeProvider = (agent.provider ?? 'claude') === 'claude';
|
|
156
174
|
const isCodexProvider = (agent.provider ?? 'claude') === 'codex';
|
|
@@ -162,64 +180,40 @@ export function createRuntimeEventHandlers(deps) {
|
|
|
162
180
|
// We must check that it has actual data before using it, otherwise we'd
|
|
163
181
|
// zero out contextUsed (since all fields would be undefined → 0).
|
|
164
182
|
const hasModelUsageData = event.modelUsage && Object.keys(event.modelUsage).length > 0;
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
183
|
+
// For CLAUDE agents: the usage_snapshot handler (from the streaming assistant
|
|
184
|
+
// event) already set the authoritative per-turn contextUsed value. The
|
|
185
|
+
// step_complete's modelUsage/tokens may contain CUMULATIVE session-wide totals
|
|
186
|
+
// which would inflate the tracked context. Only extract contextLimit (window
|
|
187
|
+
// size) from modelUsage — never override contextUsed for Claude agents.
|
|
188
|
+
//
|
|
189
|
+
// For CODEX agents: there's no usage_snapshot, so step_complete is the only
|
|
190
|
+
// source of context estimation.
|
|
191
|
+
if (isClaudeProvider) {
|
|
192
|
+
// Extract contextLimit from modelUsage if available
|
|
193
|
+
if (hasModelUsageData && event.modelUsage?.contextWindow) {
|
|
194
|
+
contextLimit = event.modelUsage.contextWindow;
|
|
195
|
+
}
|
|
196
|
+
// Preserve contextUsed from usage_snapshot (set during streaming)
|
|
197
|
+
contextUsed = agent.contextUsed || 0;
|
|
198
|
+
log.log(`[step_complete] Claude agent ${agentId}: preserving usage_snapshot contextUsed=${contextUsed}, contextLimit=${contextLimit}`);
|
|
199
|
+
}
|
|
200
|
+
else if (hasModelUsageData && event.modelUsage) {
|
|
168
201
|
const inputTokens = event.modelUsage.inputTokens || 0;
|
|
169
202
|
const outputTokens = event.modelUsage.outputTokens || 0;
|
|
170
|
-
// Only update contextLimit if the event actually carries contextWindow.
|
|
171
|
-
// If undefined, keep the existing agent.contextLimit (which may have been
|
|
172
|
-
// bumped by usage_snapshot or a previous step_complete).
|
|
173
203
|
if (event.modelUsage.contextWindow) {
|
|
174
204
|
contextLimit = event.modelUsage.contextWindow;
|
|
175
205
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
}
|
|
185
|
-
else {
|
|
186
|
-
// Context window = input side only (output tokens don't count toward the limit).
|
|
187
|
-
// IMPORTANT: modelUsage token sums in result events are CUMULATIVE session-wide
|
|
188
|
-
// totals, not per-request context fill. If the sum exceeds contextLimit, it's
|
|
189
|
-
// clearly cumulative — preserve the usage_snapshot value which IS per-request.
|
|
190
|
-
const modelUsageSum = cacheRead + cacheCreation + inputTokens;
|
|
191
|
-
if (modelUsageSum > contextLimit) {
|
|
192
|
-
// Cumulative session total detected. Prefer existing usage_snapshot value,
|
|
193
|
-
// but if that's ALSO stale (exceeds limit), fall back to 0 rather than
|
|
194
|
-
// propagating a bad value.
|
|
195
|
-
const existing = agent.contextUsed || 0;
|
|
196
|
-
contextUsed = existing <= contextLimit ? existing : 0;
|
|
197
|
-
log.log(`[step_complete] modelUsage sum ${modelUsageSum} exceeds contextLimit ${contextLimit} for ${agentId} (cumulative); using contextUsed=${contextUsed}`);
|
|
198
|
-
}
|
|
199
|
-
else {
|
|
200
|
-
contextUsed = modelUsageSum;
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
log.log(`[step_complete] modelUsage data for ${agentId}: cacheRead=${cacheRead}, cacheCreation=${cacheCreation}, input=${inputTokens}, contextWindow=${event.modelUsage.contextWindow || 'none'}`);
|
|
206
|
+
const turnGrowthEstimate = estimateTokensFromText(agent.lastAssignedTask) + outputTokens;
|
|
207
|
+
const rollingEstimate = updateCodexRollingContextEstimate(agentId, turnGrowthEstimate);
|
|
208
|
+
const plausibleSnapshotLimit = contextLimit * CODEX_PLAUSIBLE_USAGE_MULTIPLIER;
|
|
209
|
+
const hasPlausibleSnapshot = inputTokens > 0 && inputTokens <= plausibleSnapshotLimit;
|
|
210
|
+
contextUsed = hasPlausibleSnapshot
|
|
211
|
+
? Math.max(rollingEstimate, inputTokens + outputTokens)
|
|
212
|
+
: rollingEstimate;
|
|
213
|
+
log.log(`[step_complete] Codex modelUsage for ${agentId}: input=${inputTokens}, contextWindow=${event.modelUsage.contextWindow || 'none'}`);
|
|
204
214
|
}
|
|
205
215
|
else if (event.tokens) {
|
|
206
|
-
if (
|
|
207
|
-
const cacheRead = event.tokens.cacheRead || 0;
|
|
208
|
-
const cacheCreation = event.tokens.cacheCreation || 0;
|
|
209
|
-
const inputTokens = event.tokens.input || 0;
|
|
210
|
-
// Context window = input side only.
|
|
211
|
-
// Same cumulative guard as modelUsage path above.
|
|
212
|
-
const tokenSum = cacheRead + cacheCreation + inputTokens;
|
|
213
|
-
if (tokenSum > contextLimit) {
|
|
214
|
-
const existing = agent.contextUsed || 0;
|
|
215
|
-
contextUsed = existing <= contextLimit ? existing : 0;
|
|
216
|
-
log.log(`[step_complete] tokens sum ${tokenSum} exceeds contextLimit ${contextLimit} for ${agentId} (cumulative); using contextUsed=${contextUsed}`);
|
|
217
|
-
}
|
|
218
|
-
else {
|
|
219
|
-
contextUsed = tokenSum;
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
else {
|
|
216
|
+
if (isCodexProvider) {
|
|
223
217
|
const inputTokens = event.tokens.input || 0;
|
|
224
218
|
const outputTokens = event.tokens.output || 0;
|
|
225
219
|
const turnGrowthEstimate = estimateTokensFromText(agent.lastAssignedTask) + outputTokens;
|
|
@@ -231,14 +225,8 @@ export function createRuntimeEventHandlers(deps) {
|
|
|
231
225
|
: rollingEstimate;
|
|
232
226
|
contextLimit = agent.contextLimit || DEFAULT_CODEX_CONTEXT_WINDOW;
|
|
233
227
|
}
|
|
228
|
+
// For Claude with tokens but no modelUsage — usage_snapshot already handled it
|
|
234
229
|
}
|
|
235
|
-
const hasZeroTokenUsage = !!event.tokens
|
|
236
|
-
&& (event.tokens.input || 0) === 0
|
|
237
|
-
&& (event.tokens.output || 0) === 0
|
|
238
|
-
&& (event.tokens.cacheRead || 0) === 0
|
|
239
|
-
&& (event.tokens.cacheCreation || 0) === 0;
|
|
240
|
-
// Treat empty objects {} as "no model usage" (they have no meaningful data)
|
|
241
|
-
const hasNoModelUsage = !hasModelUsageData;
|
|
242
230
|
// For /context, /cost, /compact: the context_stats event already set the
|
|
243
231
|
// authoritative contextUsed/contextLimit values. Don't overwrite them with
|
|
244
232
|
// zero-token step_complete data from the local command.
|
|
@@ -255,21 +243,6 @@ export function createRuntimeEventHandlers(deps) {
|
|
|
255
243
|
log.log(`[step_complete] Context command for ${agentId}; preserving context values from tracked fields`);
|
|
256
244
|
}
|
|
257
245
|
}
|
|
258
|
-
else if (isClaudeProvider && hasZeroTokenUsage && hasNoModelUsage) {
|
|
259
|
-
contextUsed = agent.contextUsed || 0;
|
|
260
|
-
contextLimit = agent.contextLimit || 200000;
|
|
261
|
-
log.log(`[step_complete] Claude empty usage detected for ${agentId}; preserving previous context`);
|
|
262
|
-
}
|
|
263
|
-
else if (isClaudeProvider && !hasModelUsageData && event.tokens) {
|
|
264
|
-
// modelUsage was {} (empty) but we DO have event.tokens.
|
|
265
|
-
// The usage_snapshot handler already set the correct contextUsed value
|
|
266
|
-
// from the assistant event's usage data. Don't overwrite it.
|
|
267
|
-
// The event.tokens here come from the result event, which for Claude
|
|
268
|
-
// in --print mode may have different semantics. Prefer the last
|
|
269
|
-
// usage_snapshot value which is the most accurate real-time reading.
|
|
270
|
-
log.log(`[step_complete] Claude empty modelUsage but has tokens for ${agentId}; preserving usage_snapshot contextUsed=${agent.contextUsed}`);
|
|
271
|
-
contextUsed = agent.contextUsed || 0;
|
|
272
|
-
}
|
|
273
246
|
// Don't clamp contextUsed to contextLimit - models can have up to 1M context.
|
|
274
247
|
// The contextLimit comes from modelUsage.contextWindow which is authoritative.
|
|
275
248
|
contextUsed = Math.max(0, contextUsed);
|
|
@@ -144,7 +144,8 @@ export function setupRuntimeListeners(ctx) {
|
|
|
144
144
|
}
|
|
145
145
|
}
|
|
146
146
|
// Real-time context tracking: broadcast lightweight context_update on usage_snapshot and step_complete
|
|
147
|
-
|
|
147
|
+
// Skip subagent events — their token counts reflect the subagent's own context, not the parent's.
|
|
148
|
+
if (!event.parentToolUseId && ((event.type === 'usage_snapshot' && event.tokens) || event.type === 'step_complete')) {
|
|
148
149
|
const agent = agentService.getAgent(agentId);
|
|
149
150
|
if (agent) {
|
|
150
151
|
ctx.broadcast({
|