@nuvin/session 0.3.0-rc.2 → 0.3.0-rc.4
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/{chunk-COSZ5DKO.js → chunk-6QNR3TBF.js} +1 -1
- package/dist/{chunk-B6S3CWQX.js → chunk-UKDIV6I5.js} +296 -89
- package/dist/client/index.js +21 -11
- package/dist/client/session-client.d.ts +3 -3
- package/dist/client/session-client.d.ts.map +1 -1
- package/dist/controller/agent-channel.d.ts +11 -15
- package/dist/controller/agent-channel.d.ts.map +1 -1
- package/dist/controller/index.js +156 -56
- package/dist/controller/session-controller.d.ts +9 -5
- package/dist/controller/session-controller.d.ts.map +1 -1
- package/dist/protocol/index.js +1 -1
- package/dist/protocol/types.d.ts +31 -10
- package/dist/protocol/types.d.ts.map +1 -1
- package/dist/protocol/version.d.ts +1 -1
- package/dist/state/approvals.d.ts.map +1 -1
- package/dist/state/display-text.d.ts +8 -0
- package/dist/state/display-text.d.ts.map +1 -0
- package/dist/state/index.d.ts +1 -0
- package/dist/state/index.d.ts.map +1 -1
- package/dist/state/index.js +9 -3
- package/dist/state/messages.d.ts +14 -20
- package/dist/state/messages.d.ts.map +1 -1
- package/dist/state/questions.d.ts +1 -1
- package/dist/state/questions.d.ts.map +1 -1
- package/dist/state/session.d.ts.map +1 -1
- package/dist/state/tool-key.d.ts +1 -1
- package/dist/state/tool-key.d.ts.map +1 -1
- package/dist/state/tool-preview.d.ts.map +1 -1
- package/dist/state/tool-preview.js +6 -6
- package/dist/test-utils/index.js +1 -1
- package/package.json +2 -2
|
@@ -14,7 +14,9 @@ var BACKGROUNDLESS_TOOLS = /* @__PURE__ */ new Set([
|
|
|
14
14
|
"UpdateTopic",
|
|
15
15
|
"Memory",
|
|
16
16
|
"MemoryRead",
|
|
17
|
-
"InvokeCommand"
|
|
17
|
+
"InvokeCommand",
|
|
18
|
+
"SendMessage",
|
|
19
|
+
"StopAgent"
|
|
18
20
|
]);
|
|
19
21
|
function isToolAutoApprovedForMode(mode, permissionClass) {
|
|
20
22
|
if (mode === "sudo") return true;
|
|
@@ -42,14 +44,114 @@ function removeApproval(state, matches) {
|
|
|
42
44
|
return pending.length === state.pending.length ? state : { ...state, pending };
|
|
43
45
|
}
|
|
44
46
|
|
|
47
|
+
// src/state/display-text.ts
|
|
48
|
+
var DISPLAY_TEXT_MAX_BYTES = 512;
|
|
49
|
+
var textEncoder = new TextEncoder();
|
|
50
|
+
function isControlCodePoint(codePoint) {
|
|
51
|
+
return codePoint <= 31 || codePoint >= 127 && codePoint <= 159;
|
|
52
|
+
}
|
|
53
|
+
function sanitizeBoundedDisplayText(text) {
|
|
54
|
+
let output = "";
|
|
55
|
+
let outputBytes = 0;
|
|
56
|
+
let terminalState = "normal";
|
|
57
|
+
for (const codePointText of text) {
|
|
58
|
+
const codePoint = codePointText.codePointAt(0);
|
|
59
|
+
if (codePoint === void 0) continue;
|
|
60
|
+
if (terminalState === "osc") {
|
|
61
|
+
if (codePoint === 7 || codePoint === 156) {
|
|
62
|
+
terminalState = "normal";
|
|
63
|
+
} else if (codePoint === 27) {
|
|
64
|
+
terminalState = "osc-escape";
|
|
65
|
+
}
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (terminalState === "osc-escape") {
|
|
69
|
+
if (codePoint === 92 || codePoint === 7 || codePoint === 156) {
|
|
70
|
+
terminalState = "normal";
|
|
71
|
+
} else if (codePoint !== 27) {
|
|
72
|
+
terminalState = "osc";
|
|
73
|
+
}
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
if (terminalState === "st-string") {
|
|
77
|
+
if (codePoint === 156) {
|
|
78
|
+
terminalState = "normal";
|
|
79
|
+
} else if (codePoint === 27) {
|
|
80
|
+
terminalState = "st-string-escape";
|
|
81
|
+
}
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (terminalState === "st-string-escape") {
|
|
85
|
+
if (codePoint === 92 || codePoint === 156) {
|
|
86
|
+
terminalState = "normal";
|
|
87
|
+
} else if (codePoint !== 27) {
|
|
88
|
+
terminalState = "st-string";
|
|
89
|
+
}
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (codePoint === 27) {
|
|
93
|
+
terminalState = "escape";
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (codePoint === 155) {
|
|
97
|
+
terminalState = "csi";
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (codePoint === 157) {
|
|
101
|
+
terminalState = "osc";
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
if (codePoint === 144 || codePoint === 152 || codePoint === 158 || codePoint === 159) {
|
|
105
|
+
terminalState = "st-string";
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
if (codePoint === 156) {
|
|
109
|
+
terminalState = "normal";
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
if (terminalState === "csi") {
|
|
113
|
+
if (codePoint >= 64 && codePoint <= 126) {
|
|
114
|
+
terminalState = "normal";
|
|
115
|
+
}
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
if (terminalState === "escape-intermediate") {
|
|
119
|
+
if (codePoint >= 48 && codePoint <= 126) {
|
|
120
|
+
terminalState = "normal";
|
|
121
|
+
}
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
if (terminalState === "escape") {
|
|
125
|
+
if (codePoint === 91) {
|
|
126
|
+
terminalState = "csi";
|
|
127
|
+
} else if (codePoint === 93) {
|
|
128
|
+
terminalState = "osc";
|
|
129
|
+
} else if (codePoint === 80 || codePoint === 88 || codePoint === 94 || codePoint === 95) {
|
|
130
|
+
terminalState = "st-string";
|
|
131
|
+
} else if (codePoint >= 32 && codePoint <= 47) {
|
|
132
|
+
terminalState = "escape-intermediate";
|
|
133
|
+
} else {
|
|
134
|
+
terminalState = "normal";
|
|
135
|
+
}
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
if (isControlCodePoint(codePoint)) continue;
|
|
139
|
+
const codePointBytes = textEncoder.encode(codePointText).byteLength;
|
|
140
|
+
if (outputBytes + codePointBytes > DISPLAY_TEXT_MAX_BYTES) break;
|
|
141
|
+
output += codePointText;
|
|
142
|
+
outputBytes += codePointBytes;
|
|
143
|
+
}
|
|
144
|
+
return output;
|
|
145
|
+
}
|
|
146
|
+
|
|
45
147
|
// src/state/json.ts
|
|
46
148
|
function asJsonObject(value) {
|
|
47
149
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
48
150
|
}
|
|
49
151
|
|
|
50
152
|
// src/state/tool-key.ts
|
|
51
|
-
function scopedToolKey(
|
|
52
|
-
return JSON.stringify([
|
|
153
|
+
function scopedToolKey(toolCallPath, toolCallId) {
|
|
154
|
+
return JSON.stringify([...toolCallPath ?? [], toolCallId]);
|
|
53
155
|
}
|
|
54
156
|
|
|
55
157
|
// src/state/messages.ts
|
|
@@ -73,8 +175,11 @@ function addMessage(state, message) {
|
|
|
73
175
|
nextId: state.nextId + 1
|
|
74
176
|
};
|
|
75
177
|
}
|
|
76
|
-
function
|
|
77
|
-
return
|
|
178
|
+
function cloneToolCallPath(toolCallPath) {
|
|
179
|
+
return toolCallPath && toolCallPath.length > 0 ? [...toolCallPath] : void 0;
|
|
180
|
+
}
|
|
181
|
+
function scopedRegistryKey(toolCallPath, id) {
|
|
182
|
+
return scopedToolKey(toolCallPath, id);
|
|
78
183
|
}
|
|
79
184
|
function compactError(error) {
|
|
80
185
|
const normalized = error.trim().replace(/\s+/g, " ");
|
|
@@ -91,9 +196,9 @@ function retryReasonText(event) {
|
|
|
91
196
|
function formatProviderRetryText(event) {
|
|
92
197
|
return `${retryReasonText(event)}: ${compactError(event.error)} \xB7 ${retryDelayText(event.delayMs)} \xB7 attempt ${event.attempt + 1}/${event.maxAttempts}`;
|
|
93
198
|
}
|
|
94
|
-
function removeProviderStreamMessages(state, assistantMessageId, reasoningMessageId,
|
|
95
|
-
const assistantKey = scopedRegistryKey(
|
|
96
|
-
const reasoningKey = scopedRegistryKey(
|
|
199
|
+
function removeProviderStreamMessages(state, assistantMessageId, reasoningMessageId, toolCallPath) {
|
|
200
|
+
const assistantKey = scopedRegistryKey(toolCallPath, assistantMessageId);
|
|
201
|
+
const reasoningKey = scopedRegistryKey(toolCallPath, reasoningMessageId);
|
|
97
202
|
const assistantRowId = state.assistantMessageIds[assistantKey];
|
|
98
203
|
const reasoningRowId = state.reasoningMessageIds[reasoningKey];
|
|
99
204
|
if (!assistantRowId && !reasoningRowId) {
|
|
@@ -121,8 +226,8 @@ function removeProviderStreamMessages(state, assistantMessageId, reasoningMessag
|
|
|
121
226
|
messages: state.messages.filter((message) => !removedIds.has(message.id))
|
|
122
227
|
};
|
|
123
228
|
}
|
|
124
|
-
function upsertProviderRetryMessage(state, event,
|
|
125
|
-
const trackingKey = scopedRegistryKey(
|
|
229
|
+
function upsertProviderRetryMessage(state, event, toolCallPath, now) {
|
|
230
|
+
const trackingKey = scopedRegistryKey(toolCallPath, event.assistantMessageId);
|
|
126
231
|
const existingId = state.providerRetryMessageIds[trackingKey];
|
|
127
232
|
const attempt = {
|
|
128
233
|
failedAttempt: event.attempt,
|
|
@@ -150,11 +255,12 @@ function upsertProviderRetryMessage(state, event, parentToolCallId, now) {
|
|
|
150
255
|
});
|
|
151
256
|
}
|
|
152
257
|
const id = createMessageId(state);
|
|
258
|
+
const storedToolCallPath = cloneToolCallPath(toolCallPath);
|
|
153
259
|
const nextState = addMessage(state, {
|
|
154
260
|
id,
|
|
155
261
|
role: "warning",
|
|
156
262
|
text,
|
|
157
|
-
...
|
|
263
|
+
...storedToolCallPath ? { toolCallPath: storedToolCallPath } : {},
|
|
158
264
|
providerRetry: {
|
|
159
265
|
assistantMessageId: event.assistantMessageId,
|
|
160
266
|
attempts: [attempt]
|
|
@@ -168,8 +274,8 @@ function upsertProviderRetryMessage(state, event, parentToolCallId, now) {
|
|
|
168
274
|
}
|
|
169
275
|
};
|
|
170
276
|
}
|
|
171
|
-
function upsertTrackedTextMessage(state, registryKey, messageId, role, text, mode, live,
|
|
172
|
-
const trackingKey = scopedRegistryKey(
|
|
277
|
+
function upsertTrackedTextMessage(state, registryKey, messageId, role, text, mode, live, toolCallPath, timing) {
|
|
278
|
+
const trackingKey = scopedRegistryKey(toolCallPath, messageId);
|
|
173
279
|
const existingId = state[registryKey][trackingKey];
|
|
174
280
|
if (existingId) {
|
|
175
281
|
return withUpdatedMessage(state, existingId, (message) => {
|
|
@@ -188,12 +294,13 @@ function upsertTrackedTextMessage(state, registryKey, messageId, role, text, mod
|
|
|
188
294
|
});
|
|
189
295
|
}
|
|
190
296
|
const id = createMessageId(state);
|
|
297
|
+
const storedToolCallPath = cloneToolCallPath(toolCallPath);
|
|
191
298
|
const nextState = addMessage(state, {
|
|
192
299
|
id,
|
|
193
300
|
role,
|
|
194
301
|
text,
|
|
195
302
|
live,
|
|
196
|
-
...
|
|
303
|
+
...storedToolCallPath ? { toolCallPath: storedToolCallPath } : {},
|
|
197
304
|
...timing !== void 0 ? { startedAt: timing } : {},
|
|
198
305
|
// A reasoning message that arrives already-settled stamps completedAt too.
|
|
199
306
|
...timing !== void 0 && !live ? { completedAt: timing } : {}
|
|
@@ -206,10 +313,11 @@ function upsertTrackedTextMessage(state, registryKey, messageId, role, text, mod
|
|
|
206
313
|
}
|
|
207
314
|
};
|
|
208
315
|
}
|
|
209
|
-
function settleLiveReasoning(state,
|
|
316
|
+
function settleLiveReasoning(state, toolCallPath, now) {
|
|
317
|
+
const scopeKey = scopedRegistryKey(toolCallPath, "");
|
|
210
318
|
let changed = false;
|
|
211
319
|
const messages = state.messages.map((message) => {
|
|
212
|
-
if (message.role === "reasoning" && message.live && message.
|
|
320
|
+
if (message.role === "reasoning" && message.live && scopedRegistryKey(message.toolCallPath, "") === scopeKey) {
|
|
213
321
|
changed = true;
|
|
214
322
|
return { ...message, live: false, completedAt: message.completedAt ?? now };
|
|
215
323
|
}
|
|
@@ -228,20 +336,22 @@ function settleAllLiveReasoning(state, now) {
|
|
|
228
336
|
});
|
|
229
337
|
return changed ? { ...state, messages } : state;
|
|
230
338
|
}
|
|
231
|
-
function
|
|
339
|
+
function summarizeToolCallInput(toolName, input) {
|
|
232
340
|
const objectInput = asJsonObject(input);
|
|
233
|
-
if (
|
|
234
|
-
|
|
341
|
+
if (toolName === "Program") {
|
|
342
|
+
const description = objectInput && typeof objectInput.description === "string" ? objectInput.description : "";
|
|
343
|
+
return sanitizeBoundedDisplayText(description);
|
|
235
344
|
}
|
|
236
|
-
|
|
237
|
-
if (
|
|
238
|
-
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
345
|
+
let summary;
|
|
346
|
+
if (!objectInput) {
|
|
347
|
+
summary = JSON.stringify(input);
|
|
348
|
+
} else if (typeof objectInput.command === "string") {
|
|
349
|
+
summary = objectInput.command;
|
|
350
|
+
} else {
|
|
351
|
+
const entries = Object.entries(objectInput);
|
|
352
|
+
summary = entries.length === 1 && typeof entries[0]?.[1] === "string" ? String(entries[0][1]) : JSON.stringify(objectInput);
|
|
243
353
|
}
|
|
244
|
-
return
|
|
354
|
+
return sanitizeBoundedDisplayText(summary);
|
|
245
355
|
}
|
|
246
356
|
var BASH_STREAMING_PREVIEW_LINES = 5;
|
|
247
357
|
var STREAM_TAIL_BUDGET = 64 * 1024;
|
|
@@ -320,8 +430,8 @@ function mergeToolResultText(currentText, result) {
|
|
|
320
430
|
}
|
|
321
431
|
return currentText;
|
|
322
432
|
}
|
|
323
|
-
function ensureToolMessage(state, toolCall,
|
|
324
|
-
const toolKey = scopedToolKey(
|
|
433
|
+
function ensureToolMessage(state, toolCall, toolCallPath) {
|
|
434
|
+
const toolKey = scopedToolKey(toolCallPath, toolCall.id);
|
|
325
435
|
const existingId = state.toolMessageIds[toolKey];
|
|
326
436
|
if (existingId) {
|
|
327
437
|
return withUpdatedMessage(state, existingId, (message) => {
|
|
@@ -330,21 +440,23 @@ function ensureToolMessage(state, toolCall, parentToolCallId) {
|
|
|
330
440
|
}
|
|
331
441
|
return {
|
|
332
442
|
...message,
|
|
333
|
-
input: asJsonObject(toolCall.input)
|
|
443
|
+
input: asJsonObject(toolCall.input),
|
|
444
|
+
summary: summarizeToolCallInput(toolCall.name, toolCall.input)
|
|
334
445
|
};
|
|
335
446
|
});
|
|
336
447
|
}
|
|
337
448
|
const id = createMessageId(state);
|
|
449
|
+
const storedToolCallPath = cloneToolCallPath(toolCallPath);
|
|
338
450
|
const nextState = addMessage(state, {
|
|
339
451
|
id,
|
|
340
452
|
role: "tool",
|
|
341
453
|
toolName: toolCall.name,
|
|
342
454
|
toolCallId: toolCall.id,
|
|
343
|
-
summary:
|
|
455
|
+
summary: summarizeToolCallInput(toolCall.name, toolCall.input),
|
|
344
456
|
text: "",
|
|
345
457
|
status: "pending",
|
|
346
458
|
input: asJsonObject(toolCall.input),
|
|
347
|
-
...
|
|
459
|
+
...storedToolCallPath ? { toolCallPath: storedToolCallPath } : {}
|
|
348
460
|
});
|
|
349
461
|
return {
|
|
350
462
|
...nextState,
|
|
@@ -354,8 +466,8 @@ function ensureToolMessage(state, toolCall, parentToolCallId) {
|
|
|
354
466
|
}
|
|
355
467
|
};
|
|
356
468
|
}
|
|
357
|
-
function setToolMessageStatus(state, toolCallId, status,
|
|
358
|
-
const toolMessageId = state.toolMessageIds[scopedToolKey(
|
|
469
|
+
function setToolMessageStatus(state, toolCallId, status, toolCallPath) {
|
|
470
|
+
const toolMessageId = state.toolMessageIds[scopedToolKey(toolCallPath, toolCallId)];
|
|
359
471
|
if (!toolMessageId) {
|
|
360
472
|
return state;
|
|
361
473
|
}
|
|
@@ -369,8 +481,8 @@ function setToolMessageStatus(state, toolCallId, status, parentToolCallId) {
|
|
|
369
481
|
};
|
|
370
482
|
});
|
|
371
483
|
}
|
|
372
|
-
function setToolMessageApprovalKind(state, toolCallId, approvalKind,
|
|
373
|
-
const toolMessageId = state.toolMessageIds[scopedToolKey(
|
|
484
|
+
function setToolMessageApprovalKind(state, toolCallId, approvalKind, toolCallPath) {
|
|
485
|
+
const toolMessageId = state.toolMessageIds[scopedToolKey(toolCallPath, toolCallId)];
|
|
374
486
|
if (!toolMessageId) {
|
|
375
487
|
return state;
|
|
376
488
|
}
|
|
@@ -384,8 +496,8 @@ function setToolMessageApprovalKind(state, toolCallId, approvalKind, parentToolC
|
|
|
384
496
|
};
|
|
385
497
|
});
|
|
386
498
|
}
|
|
387
|
-
function setToolMessageHidden(state, toolCallId, hidden,
|
|
388
|
-
const toolMessageId = state.toolMessageIds[scopedToolKey(
|
|
499
|
+
function setToolMessageHidden(state, toolCallId, hidden, toolCallPath) {
|
|
500
|
+
const toolMessageId = state.toolMessageIds[scopedToolKey(toolCallPath, toolCallId)];
|
|
389
501
|
if (!toolMessageId) {
|
|
390
502
|
return state;
|
|
391
503
|
}
|
|
@@ -399,12 +511,13 @@ function setToolMessageHidden(state, toolCallId, hidden, parentToolCallId) {
|
|
|
399
511
|
};
|
|
400
512
|
});
|
|
401
513
|
}
|
|
402
|
-
function applyToolResult(state, result, now,
|
|
514
|
+
function applyToolResult(state, result, now, toolCallPath) {
|
|
403
515
|
const status = asJsonObject(result.structured)?.error === "tool_rejected" ? "rejected" : result.status;
|
|
404
|
-
const toolKey = scopedToolKey(
|
|
516
|
+
const toolKey = scopedToolKey(toolCallPath, result.callId);
|
|
405
517
|
const toolMessageId = state.toolMessageIds[toolKey];
|
|
406
518
|
if (!toolMessageId) {
|
|
407
519
|
const id = createMessageId(state);
|
|
520
|
+
const storedToolCallPath = cloneToolCallPath(toolCallPath);
|
|
408
521
|
const next = addMessage(state, {
|
|
409
522
|
id,
|
|
410
523
|
completedAt: now,
|
|
@@ -415,7 +528,7 @@ function applyToolResult(state, result, now, parentToolCallId) {
|
|
|
415
528
|
summary: "",
|
|
416
529
|
text: result.output,
|
|
417
530
|
structured: result.structured,
|
|
418
|
-
...
|
|
531
|
+
...storedToolCallPath ? { toolCallPath: storedToolCallPath } : {}
|
|
419
532
|
});
|
|
420
533
|
return {
|
|
421
534
|
...next,
|
|
@@ -451,24 +564,33 @@ function createMessageState() {
|
|
|
451
564
|
};
|
|
452
565
|
}
|
|
453
566
|
function createMessageStateFromMessages(messages) {
|
|
454
|
-
const
|
|
567
|
+
const normalizedMessages = messages.map((message) => {
|
|
568
|
+
const toolCallPath = cloneToolCallPath(message.toolCallPath);
|
|
569
|
+
if (toolCallPath) return { ...message, toolCallPath };
|
|
570
|
+
if (message.toolCallPath !== void 0) {
|
|
571
|
+
const { toolCallPath: _toolCallPath, ...rootMessage } = message;
|
|
572
|
+
return rootMessage;
|
|
573
|
+
}
|
|
574
|
+
return message;
|
|
575
|
+
});
|
|
576
|
+
const nextId = normalizedMessages.reduce((max, message) => {
|
|
455
577
|
const match = /^message-(\d+)$/.exec(message.id);
|
|
456
578
|
if (!match) return max;
|
|
457
579
|
return Math.max(max, Number(match[1]) + 1);
|
|
458
|
-
}, 1) +
|
|
580
|
+
}, 1) + normalizedMessages.length;
|
|
459
581
|
const toolMessageIds = {};
|
|
460
582
|
const providerRetryMessageIds = {};
|
|
461
|
-
for (const message of
|
|
583
|
+
for (const message of normalizedMessages) {
|
|
462
584
|
if (message.role === "tool") {
|
|
463
|
-
toolMessageIds[scopedToolKey(message.
|
|
585
|
+
toolMessageIds[scopedToolKey(message.toolCallPath, message.toolCallId)] = message.id;
|
|
464
586
|
}
|
|
465
587
|
if (isProviderRetryMessage(message)) {
|
|
466
|
-
providerRetryMessageIds[scopedRegistryKey(message.
|
|
588
|
+
providerRetryMessageIds[scopedRegistryKey(message.toolCallPath, message.providerRetry.assistantMessageId)] = message.id;
|
|
467
589
|
}
|
|
468
590
|
}
|
|
469
591
|
return {
|
|
470
592
|
assistantMessageIds: {},
|
|
471
|
-
messages,
|
|
593
|
+
messages: normalizedMessages,
|
|
472
594
|
nextId,
|
|
473
595
|
providerRetryMessageIds,
|
|
474
596
|
reasoningMessageIds: {},
|
|
@@ -505,21 +627,38 @@ function appendWarningMessage(state, text) {
|
|
|
505
627
|
text
|
|
506
628
|
});
|
|
507
629
|
}
|
|
508
|
-
function applyAgentEvent(state, event,
|
|
630
|
+
function applyAgentEvent(state, event, toolCallPath, now = Date.now()) {
|
|
509
631
|
switch (event.type) {
|
|
632
|
+
case "user_message": {
|
|
633
|
+
const nestedPath = cloneToolCallPath(toolCallPath);
|
|
634
|
+
if (nestedPath === void 0) {
|
|
635
|
+
return state;
|
|
636
|
+
}
|
|
637
|
+
const text = getTextFromMessage(event.message).trim();
|
|
638
|
+
if (text.length === 0) {
|
|
639
|
+
return state;
|
|
640
|
+
}
|
|
641
|
+
return addMessage(state, {
|
|
642
|
+
id: createMessageId(state),
|
|
643
|
+
role: "user",
|
|
644
|
+
text,
|
|
645
|
+
...event.senderAgentId === void 0 ? {} : { senderAgentId: event.senderAgentId },
|
|
646
|
+
toolCallPath: nestedPath
|
|
647
|
+
});
|
|
648
|
+
}
|
|
510
649
|
case "assistant_chunk":
|
|
511
650
|
if (!event.chunk.text) {
|
|
512
651
|
return state;
|
|
513
652
|
}
|
|
514
653
|
return upsertTrackedTextMessage(
|
|
515
|
-
settleLiveReasoning(state,
|
|
654
|
+
settleLiveReasoning(state, toolCallPath, now),
|
|
516
655
|
"assistantMessageIds",
|
|
517
656
|
event.messageId,
|
|
518
657
|
"assistant",
|
|
519
658
|
event.chunk.text,
|
|
520
659
|
"append",
|
|
521
660
|
true,
|
|
522
|
-
|
|
661
|
+
toolCallPath
|
|
523
662
|
);
|
|
524
663
|
case "assistant_message": {
|
|
525
664
|
const text = getTextFromMessage(event.message).trim();
|
|
@@ -527,14 +666,14 @@ function applyAgentEvent(state, event, parentToolCallId, now = Date.now()) {
|
|
|
527
666
|
return state;
|
|
528
667
|
}
|
|
529
668
|
return upsertTrackedTextMessage(
|
|
530
|
-
settleLiveReasoning(state,
|
|
669
|
+
settleLiveReasoning(state, toolCallPath, now),
|
|
531
670
|
"assistantMessageIds",
|
|
532
671
|
event.message.id,
|
|
533
672
|
"assistant",
|
|
534
673
|
text,
|
|
535
674
|
"replace",
|
|
536
675
|
false,
|
|
537
|
-
|
|
676
|
+
toolCallPath
|
|
538
677
|
);
|
|
539
678
|
}
|
|
540
679
|
case "reasoning_chunk":
|
|
@@ -549,7 +688,7 @@ function applyAgentEvent(state, event, parentToolCallId, now = Date.now()) {
|
|
|
549
688
|
event.text,
|
|
550
689
|
"append",
|
|
551
690
|
true,
|
|
552
|
-
|
|
691
|
+
toolCallPath,
|
|
553
692
|
now
|
|
554
693
|
);
|
|
555
694
|
case "reasoning_message": {
|
|
@@ -565,7 +704,7 @@ function applyAgentEvent(state, event, parentToolCallId, now = Date.now()) {
|
|
|
565
704
|
text,
|
|
566
705
|
"replace",
|
|
567
706
|
false,
|
|
568
|
-
|
|
707
|
+
toolCallPath,
|
|
569
708
|
now
|
|
570
709
|
);
|
|
571
710
|
}
|
|
@@ -575,21 +714,21 @@ function applyAgentEvent(state, event, parentToolCallId, now = Date.now()) {
|
|
|
575
714
|
return state;
|
|
576
715
|
}
|
|
577
716
|
return upsertTrackedTextMessage(
|
|
578
|
-
settleLiveReasoning(state,
|
|
717
|
+
settleLiveReasoning(state, toolCallPath, now),
|
|
579
718
|
"assistantMessageIds",
|
|
580
719
|
event.message.id,
|
|
581
720
|
"assistant",
|
|
582
721
|
text,
|
|
583
722
|
"replace",
|
|
584
723
|
false,
|
|
585
|
-
|
|
724
|
+
toolCallPath
|
|
586
725
|
);
|
|
587
726
|
}
|
|
588
727
|
case "tool_call":
|
|
589
|
-
return ensureToolMessage(state, event.toolCall,
|
|
728
|
+
return ensureToolMessage(state, event.toolCall, toolCallPath);
|
|
590
729
|
case "tool_started": {
|
|
591
|
-
const toolState = ensureToolMessage(state, event.toolCall,
|
|
592
|
-
const toolMessageId = toolState.toolMessageIds[scopedToolKey(
|
|
730
|
+
const toolState = ensureToolMessage(state, event.toolCall, toolCallPath);
|
|
731
|
+
const toolMessageId = toolState.toolMessageIds[scopedToolKey(toolCallPath, event.toolCall.id)];
|
|
593
732
|
return withUpdatedMessage(toolState, toolMessageId, (message) => {
|
|
594
733
|
if (message.role !== "tool") {
|
|
595
734
|
return message;
|
|
@@ -602,8 +741,8 @@ function applyAgentEvent(state, event, parentToolCallId, now = Date.now()) {
|
|
|
602
741
|
});
|
|
603
742
|
}
|
|
604
743
|
case "tool_output_chunk": {
|
|
605
|
-
const toolState = ensureToolMessage(state, event.toolCall,
|
|
606
|
-
const toolMessageId = toolState.toolMessageIds[scopedToolKey(
|
|
744
|
+
const toolState = ensureToolMessage(state, event.toolCall, toolCallPath);
|
|
745
|
+
const toolMessageId = toolState.toolMessageIds[scopedToolKey(toolCallPath, event.toolCall.id)];
|
|
607
746
|
return withUpdatedMessage(toolState, toolMessageId, (message) => {
|
|
608
747
|
if (message.role !== "tool") {
|
|
609
748
|
return message;
|
|
@@ -622,14 +761,14 @@ function applyAgentEvent(state, event, parentToolCallId, now = Date.now()) {
|
|
|
622
761
|
});
|
|
623
762
|
}
|
|
624
763
|
case "tool_completed": {
|
|
625
|
-
const toolState = ensureToolMessage(state, event.toolCall,
|
|
626
|
-
return applyToolResult(toolState, event.result, now,
|
|
764
|
+
const toolState = ensureToolMessage(state, event.toolCall, toolCallPath);
|
|
765
|
+
return applyToolResult(toolState, event.result, now, toolCallPath);
|
|
627
766
|
}
|
|
628
767
|
case "tool_result":
|
|
629
|
-
return applyToolResult(state, event.result, now,
|
|
768
|
+
return applyToolResult(state, event.result, now, toolCallPath);
|
|
630
769
|
case "tool_rejected": {
|
|
631
|
-
const toolState = ensureToolMessage(state, event.toolCall,
|
|
632
|
-
const toolMessageId = toolState.toolMessageIds[scopedToolKey(
|
|
770
|
+
const toolState = ensureToolMessage(state, event.toolCall, toolCallPath);
|
|
771
|
+
const toolMessageId = toolState.toolMessageIds[scopedToolKey(toolCallPath, event.toolCall.id)];
|
|
633
772
|
return withUpdatedMessage(toolState, toolMessageId, (message) => {
|
|
634
773
|
if (message.role !== "tool") {
|
|
635
774
|
return message;
|
|
@@ -648,31 +787,48 @@ function applyAgentEvent(state, event, parentToolCallId, now = Date.now()) {
|
|
|
648
787
|
state,
|
|
649
788
|
event.assistantMessageId,
|
|
650
789
|
event.reasoningMessageId,
|
|
651
|
-
|
|
790
|
+
toolCallPath
|
|
652
791
|
);
|
|
653
792
|
case "provider_retry":
|
|
654
|
-
return upsertProviderRetryMessage(state, event,
|
|
793
|
+
return upsertProviderRetryMessage(state, event, toolCallPath, now);
|
|
655
794
|
default:
|
|
656
795
|
return state;
|
|
657
796
|
}
|
|
658
797
|
}
|
|
659
798
|
|
|
660
799
|
// src/state/questions.ts
|
|
800
|
+
function cloneQuestion(question) {
|
|
801
|
+
if (question.toolCallPath && question.toolCallPath.length > 0) {
|
|
802
|
+
return { ...question, toolCallPath: [...question.toolCallPath] };
|
|
803
|
+
}
|
|
804
|
+
if (question.toolCallPath !== void 0) {
|
|
805
|
+
const { toolCallPath: _toolCallPath, ...rootQuestion } = question;
|
|
806
|
+
return rootQuestion;
|
|
807
|
+
}
|
|
808
|
+
return question;
|
|
809
|
+
}
|
|
810
|
+
function questionKey(questionId, toolCallPath) {
|
|
811
|
+
return scopedToolKey(toolCallPath, questionId);
|
|
812
|
+
}
|
|
661
813
|
function createQuestionQueueState() {
|
|
662
814
|
return { active: null, pending: [] };
|
|
663
815
|
}
|
|
664
816
|
function enqueueQuestion(state, question) {
|
|
817
|
+
const queuedQuestion = cloneQuestion(question);
|
|
665
818
|
if (state.active === null) {
|
|
666
|
-
return { ...state, active:
|
|
819
|
+
return { ...state, active: queuedQuestion };
|
|
667
820
|
}
|
|
668
|
-
return { ...state, pending: [...state.pending,
|
|
821
|
+
return { ...state, pending: [...state.pending, queuedQuestion] };
|
|
669
822
|
}
|
|
670
|
-
function removeQuestion(state, questionId) {
|
|
671
|
-
|
|
823
|
+
function removeQuestion(state, questionId, toolCallPath) {
|
|
824
|
+
const settledKey = questionKey(questionId, toolCallPath);
|
|
825
|
+
if (state.active && questionKey(state.active.questionId, state.active.toolCallPath) === settledKey) {
|
|
672
826
|
const [active, ...pending2] = state.pending;
|
|
673
827
|
return { active: active ?? null, pending: pending2 };
|
|
674
828
|
}
|
|
675
|
-
const pending = state.pending.filter(
|
|
829
|
+
const pending = state.pending.filter(
|
|
830
|
+
(question) => questionKey(question.questionId, question.toolCallPath) !== settledKey
|
|
831
|
+
);
|
|
676
832
|
return pending.length === state.pending.length ? state : { ...state, pending };
|
|
677
833
|
}
|
|
678
834
|
|
|
@@ -732,10 +888,50 @@ function foldWorkflowDelta(state, delta) {
|
|
|
732
888
|
}
|
|
733
889
|
|
|
734
890
|
// src/state/session.ts
|
|
891
|
+
function cloneApproval(approval) {
|
|
892
|
+
const { toolCallPath, ...rootApproval } = approval;
|
|
893
|
+
return toolCallPath && toolCallPath.length > 0 ? { ...rootApproval, toolCallPath: [...toolCallPath] } : rootApproval;
|
|
894
|
+
}
|
|
895
|
+
function cloneQuestion2(question) {
|
|
896
|
+
const { toolCallPath, ...rootQuestion } = question;
|
|
897
|
+
return toolCallPath && toolCallPath.length > 0 ? { ...rootQuestion, toolCallPath: [...toolCallPath] } : rootQuestion;
|
|
898
|
+
}
|
|
899
|
+
function cloneMessage(message) {
|
|
900
|
+
const { toolCallPath, ...rootMessage } = message;
|
|
901
|
+
return toolCallPath && toolCallPath.length > 0 ? { ...rootMessage, toolCallPath: [...toolCallPath] } : rootMessage;
|
|
902
|
+
}
|
|
903
|
+
function normalizeResetState(state) {
|
|
904
|
+
return {
|
|
905
|
+
...state,
|
|
906
|
+
backgroundAgents: {
|
|
907
|
+
...state.backgroundAgents,
|
|
908
|
+
runningRunIds: [...state.backgroundAgents.runningRunIds]
|
|
909
|
+
},
|
|
910
|
+
approval: {
|
|
911
|
+
...state.approval,
|
|
912
|
+
active: state.approval.active ? cloneApproval(state.approval.active) : null,
|
|
913
|
+
pending: state.approval.pending.map(cloneApproval)
|
|
914
|
+
},
|
|
915
|
+
messages: {
|
|
916
|
+
...state.messages,
|
|
917
|
+
assistantMessageIds: { ...state.messages.assistantMessageIds },
|
|
918
|
+
messages: state.messages.messages.map(cloneMessage),
|
|
919
|
+
providerRetryMessageIds: { ...state.messages.providerRetryMessageIds },
|
|
920
|
+
reasoningMessageIds: { ...state.messages.reasoningMessageIds },
|
|
921
|
+
toolMessageIds: { ...state.messages.toolMessageIds }
|
|
922
|
+
},
|
|
923
|
+
question: {
|
|
924
|
+
...state.question,
|
|
925
|
+
active: state.question.active ? cloneQuestion2(state.question.active) : null,
|
|
926
|
+
pending: state.question.pending.map(cloneQuestion2)
|
|
927
|
+
}
|
|
928
|
+
};
|
|
929
|
+
}
|
|
735
930
|
function createSessionViewState() {
|
|
736
931
|
return {
|
|
737
932
|
approval: createApprovalQueueState(),
|
|
738
933
|
authFlow: null,
|
|
934
|
+
backgroundAgents: { running: 0, queued: 0, runningRunIds: [] },
|
|
739
935
|
busy: false,
|
|
740
936
|
mcp: { revision: 0, servers: [] },
|
|
741
937
|
messages: createMessageState(),
|
|
@@ -753,7 +949,7 @@ function reduceSessionEvent(state, event) {
|
|
|
753
949
|
let messages = applyAgentEvent(
|
|
754
950
|
state.messages,
|
|
755
951
|
event.event,
|
|
756
|
-
event.scope?.
|
|
952
|
+
event.scope?.toolCallPath,
|
|
757
953
|
event.at
|
|
758
954
|
);
|
|
759
955
|
if (event.toolStatus && event.event.type === "tool_call") {
|
|
@@ -761,14 +957,14 @@ function reduceSessionEvent(state, event) {
|
|
|
761
957
|
messages,
|
|
762
958
|
event.event.toolCall.id,
|
|
763
959
|
event.toolStatus,
|
|
764
|
-
event.scope?.
|
|
960
|
+
event.scope?.toolCallPath
|
|
765
961
|
);
|
|
766
962
|
if (event.toolStatus === "approved") {
|
|
767
963
|
messages = setToolMessageApprovalKind(
|
|
768
964
|
messages,
|
|
769
965
|
event.event.toolCall.id,
|
|
770
966
|
"auto",
|
|
771
|
-
event.scope?.
|
|
967
|
+
event.scope?.toolCallPath
|
|
772
968
|
);
|
|
773
969
|
}
|
|
774
970
|
}
|
|
@@ -782,12 +978,12 @@ function reduceSessionEvent(state, event) {
|
|
|
782
978
|
case "approval-requested":
|
|
783
979
|
return {
|
|
784
980
|
...state,
|
|
785
|
-
approval: enqueueApproval(state.approval, event.approval),
|
|
981
|
+
approval: enqueueApproval(state.approval, cloneApproval(event.approval)),
|
|
786
982
|
messages: setToolMessageHidden(
|
|
787
983
|
state.messages,
|
|
788
984
|
event.approval.toolCallId,
|
|
789
985
|
true,
|
|
790
|
-
event.approval.
|
|
986
|
+
event.approval.toolCallPath
|
|
791
987
|
)
|
|
792
988
|
};
|
|
793
989
|
case "approval-settled": {
|
|
@@ -795,31 +991,30 @@ function reduceSessionEvent(state, event) {
|
|
|
795
991
|
state.messages,
|
|
796
992
|
event.toolCallId,
|
|
797
993
|
event.status,
|
|
798
|
-
event.
|
|
994
|
+
event.toolCallPath
|
|
799
995
|
);
|
|
800
996
|
if (event.status === "approved" && event.by === "client") {
|
|
801
|
-
stamped = setToolMessageApprovalKind(
|
|
802
|
-
stamped,
|
|
803
|
-
event.toolCallId,
|
|
804
|
-
"user",
|
|
805
|
-
event.parentToolCallId
|
|
806
|
-
);
|
|
997
|
+
stamped = setToolMessageApprovalKind(stamped, event.toolCallId, "user", event.toolCallPath);
|
|
807
998
|
}
|
|
999
|
+
const settledKey = scopedToolKey(event.toolCallPath, event.toolCallId);
|
|
808
1000
|
return {
|
|
809
1001
|
...state,
|
|
810
1002
|
approval: removeApproval(
|
|
811
1003
|
state.approval,
|
|
812
|
-
(item) => item.
|
|
1004
|
+
(item) => scopedToolKey(item.toolCallPath, item.toolCallId) === settledKey
|
|
813
1005
|
),
|
|
814
1006
|
// Approved tools materialize as a transcript row; rejected ones never
|
|
815
1007
|
// ran and stay hidden.
|
|
816
|
-
messages: event.status === "approved" ? setToolMessageHidden(stamped, event.toolCallId, false, event.
|
|
1008
|
+
messages: event.status === "approved" ? setToolMessageHidden(stamped, event.toolCallId, false, event.toolCallPath) : stamped
|
|
817
1009
|
};
|
|
818
1010
|
}
|
|
819
1011
|
case "question-asked":
|
|
820
1012
|
return { ...state, question: enqueueQuestion(state.question, event.question) };
|
|
821
1013
|
case "question-settled":
|
|
822
|
-
return {
|
|
1014
|
+
return {
|
|
1015
|
+
...state,
|
|
1016
|
+
question: removeQuestion(state.question, event.questionId, event.toolCallPath)
|
|
1017
|
+
};
|
|
823
1018
|
case "turn-status": {
|
|
824
1019
|
const messages = event.busy ? state.messages : settleAllLiveReasoning(state.messages, event.at);
|
|
825
1020
|
return {
|
|
@@ -830,6 +1025,15 @@ function reduceSessionEvent(state, event) {
|
|
|
830
1025
|
queued: event.queued ?? []
|
|
831
1026
|
};
|
|
832
1027
|
}
|
|
1028
|
+
case "background-agent-status":
|
|
1029
|
+
return {
|
|
1030
|
+
...state,
|
|
1031
|
+
backgroundAgents: {
|
|
1032
|
+
running: event.running,
|
|
1033
|
+
queued: event.queued,
|
|
1034
|
+
runningRunIds: [...event.runningRunIds]
|
|
1035
|
+
}
|
|
1036
|
+
};
|
|
833
1037
|
case "info":
|
|
834
1038
|
return { ...state, messages: appendInfoMessage(state.messages, event.message) };
|
|
835
1039
|
case "error":
|
|
@@ -856,7 +1060,7 @@ function reduceSessionEvent(state, event) {
|
|
|
856
1060
|
};
|
|
857
1061
|
}
|
|
858
1062
|
case "state-reset":
|
|
859
|
-
return event.state;
|
|
1063
|
+
return normalizeResetState(event.state);
|
|
860
1064
|
default:
|
|
861
1065
|
return state;
|
|
862
1066
|
}
|
|
@@ -869,10 +1073,13 @@ export {
|
|
|
869
1073
|
enqueueApproval,
|
|
870
1074
|
dequeueActiveApproval,
|
|
871
1075
|
removeApproval,
|
|
1076
|
+
DISPLAY_TEXT_MAX_BYTES,
|
|
1077
|
+
sanitizeBoundedDisplayText,
|
|
872
1078
|
asJsonObject,
|
|
873
1079
|
scopedToolKey,
|
|
874
1080
|
isProviderRetryMessage,
|
|
875
1081
|
settleAllLiveReasoning,
|
|
1082
|
+
summarizeToolCallInput,
|
|
876
1083
|
setToolMessageStatus,
|
|
877
1084
|
setToolMessageApprovalKind,
|
|
878
1085
|
setToolMessageHidden,
|