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