@nuvin/session 0.2.0-rc.14 → 0.2.0-rc.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/{chunk-KQHHBUVY.js → chunk-B6S3CWQX.js} +109 -6
- package/dist/{chunk-GIJGWYH4.js → chunk-BCFY6CHJ.js} +1 -1
- package/dist/client/index.js +2 -2
- package/dist/controller/index.js +1 -1
- package/dist/protocol/index.js +1 -1
- package/dist/protocol/version.d.ts +1 -1
- package/dist/state/index.js +3 -1
- package/dist/state/messages.d.ts +20 -0
- package/dist/state/messages.d.ts.map +1 -1
- package/dist/test-utils/index.js +1 -1
- package/package.json +2 -2
|
@@ -54,6 +54,9 @@ function scopedToolKey(parentToolCallId, toolCallId) {
|
|
|
54
54
|
|
|
55
55
|
// src/state/messages.ts
|
|
56
56
|
import { getReasoningTextFromMessage, getTextFromMessage } from "@nuvin/agent-core/formats";
|
|
57
|
+
function isProviderRetryMessage(message) {
|
|
58
|
+
return message.role === "warning" && message.providerRetry !== void 0;
|
|
59
|
+
}
|
|
57
60
|
function createMessageId(state) {
|
|
58
61
|
return `message-${state.nextId}`;
|
|
59
62
|
}
|
|
@@ -73,6 +76,98 @@ function addMessage(state, message) {
|
|
|
73
76
|
function scopedRegistryKey(scope, id) {
|
|
74
77
|
return scope ? `${scope}::${id}` : id;
|
|
75
78
|
}
|
|
79
|
+
function compactError(error) {
|
|
80
|
+
const normalized = error.trim().replace(/\s+/g, " ");
|
|
81
|
+
return normalized.length <= 160 ? normalized : `${normalized.slice(0, 159)}\u2026`;
|
|
82
|
+
}
|
|
83
|
+
function retryDelayText(delayMs) {
|
|
84
|
+
return delayMs === 0 ? "retry now" : `retry after ${Math.max(1, Math.ceil(delayMs / 1e3))}s`;
|
|
85
|
+
}
|
|
86
|
+
function retryReasonText(event) {
|
|
87
|
+
if (event.reason === "transport_error") return "Network interrupted";
|
|
88
|
+
if (event.reason === "stream_error") return "Provider stream interrupted";
|
|
89
|
+
return event.status === void 0 ? "Provider unavailable" : `Provider unavailable (HTTP ${event.status})`;
|
|
90
|
+
}
|
|
91
|
+
function formatProviderRetryText(event) {
|
|
92
|
+
return `${retryReasonText(event)}: ${compactError(event.error)} \xB7 ${retryDelayText(event.delayMs)} \xB7 attempt ${event.attempt + 1}/${event.maxAttempts}`;
|
|
93
|
+
}
|
|
94
|
+
function removeProviderStreamMessages(state, assistantMessageId, reasoningMessageId, parentToolCallId) {
|
|
95
|
+
const assistantKey = scopedRegistryKey(parentToolCallId, assistantMessageId);
|
|
96
|
+
const reasoningKey = scopedRegistryKey(parentToolCallId, reasoningMessageId);
|
|
97
|
+
const assistantRowId = state.assistantMessageIds[assistantKey];
|
|
98
|
+
const reasoningRowId = state.reasoningMessageIds[reasoningKey];
|
|
99
|
+
if (!assistantRowId && !reasoningRowId) {
|
|
100
|
+
return state;
|
|
101
|
+
}
|
|
102
|
+
const removedIds = /* @__PURE__ */ new Set();
|
|
103
|
+
if (assistantRowId) {
|
|
104
|
+
removedIds.add(assistantRowId);
|
|
105
|
+
}
|
|
106
|
+
if (reasoningRowId) {
|
|
107
|
+
removedIds.add(reasoningRowId);
|
|
108
|
+
}
|
|
109
|
+
const assistantMessageIds = { ...state.assistantMessageIds };
|
|
110
|
+
const reasoningMessageIds = { ...state.reasoningMessageIds };
|
|
111
|
+
if (assistantRowId) {
|
|
112
|
+
delete assistantMessageIds[assistantKey];
|
|
113
|
+
}
|
|
114
|
+
if (reasoningRowId) {
|
|
115
|
+
delete reasoningMessageIds[reasoningKey];
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
...state,
|
|
119
|
+
assistantMessageIds,
|
|
120
|
+
reasoningMessageIds,
|
|
121
|
+
messages: state.messages.filter((message) => !removedIds.has(message.id))
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
function upsertProviderRetryMessage(state, event, parentToolCallId, now) {
|
|
125
|
+
const trackingKey = scopedRegistryKey(parentToolCallId, event.assistantMessageId);
|
|
126
|
+
const existingId = state.providerRetryMessageIds[trackingKey];
|
|
127
|
+
const attempt = {
|
|
128
|
+
failedAttempt: event.attempt,
|
|
129
|
+
maxAttempts: event.maxAttempts,
|
|
130
|
+
error: event.error,
|
|
131
|
+
delayMs: event.delayMs,
|
|
132
|
+
reason: event.reason,
|
|
133
|
+
...event.status !== void 0 ? { status: event.status } : {},
|
|
134
|
+
occurredAt: now
|
|
135
|
+
};
|
|
136
|
+
const text = formatProviderRetryText(event);
|
|
137
|
+
if (existingId) {
|
|
138
|
+
return withUpdatedMessage(state, existingId, (message) => {
|
|
139
|
+
if (!isProviderRetryMessage(message)) {
|
|
140
|
+
return message;
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
...message,
|
|
144
|
+
text,
|
|
145
|
+
providerRetry: {
|
|
146
|
+
...message.providerRetry,
|
|
147
|
+
attempts: [...message.providerRetry.attempts, attempt]
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
const id = createMessageId(state);
|
|
153
|
+
const nextState = addMessage(state, {
|
|
154
|
+
id,
|
|
155
|
+
role: "warning",
|
|
156
|
+
text,
|
|
157
|
+
...parentToolCallId ? { parentToolCallId } : {},
|
|
158
|
+
providerRetry: {
|
|
159
|
+
assistantMessageId: event.assistantMessageId,
|
|
160
|
+
attempts: [attempt]
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
return {
|
|
164
|
+
...nextState,
|
|
165
|
+
providerRetryMessageIds: {
|
|
166
|
+
...nextState.providerRetryMessageIds,
|
|
167
|
+
[trackingKey]: id
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
}
|
|
76
171
|
function upsertTrackedTextMessage(state, registryKey, messageId, role, text, mode, live, parentToolCallId, timing) {
|
|
77
172
|
const trackingKey = scopedRegistryKey(parentToolCallId, messageId);
|
|
78
173
|
const existingId = state[registryKey][trackingKey];
|
|
@@ -350,6 +445,7 @@ function createMessageState() {
|
|
|
350
445
|
assistantMessageIds: {},
|
|
351
446
|
messages: [],
|
|
352
447
|
nextId: 1,
|
|
448
|
+
providerRetryMessageIds: {},
|
|
353
449
|
reasoningMessageIds: {},
|
|
354
450
|
toolMessageIds: {}
|
|
355
451
|
};
|
|
@@ -361,15 +457,20 @@ function createMessageStateFromMessages(messages) {
|
|
|
361
457
|
return Math.max(max, Number(match[1]) + 1);
|
|
362
458
|
}, 1) + messages.length;
|
|
363
459
|
const toolMessageIds = {};
|
|
460
|
+
const providerRetryMessageIds = {};
|
|
364
461
|
for (const message of messages) {
|
|
365
462
|
if (message.role === "tool") {
|
|
366
463
|
toolMessageIds[scopedToolKey(message.parentToolCallId, message.toolCallId)] = message.id;
|
|
367
464
|
}
|
|
465
|
+
if (isProviderRetryMessage(message)) {
|
|
466
|
+
providerRetryMessageIds[scopedRegistryKey(message.parentToolCallId, message.providerRetry.assistantMessageId)] = message.id;
|
|
467
|
+
}
|
|
368
468
|
}
|
|
369
469
|
return {
|
|
370
470
|
assistantMessageIds: {},
|
|
371
471
|
messages,
|
|
372
472
|
nextId,
|
|
473
|
+
providerRetryMessageIds,
|
|
373
474
|
reasoningMessageIds: {},
|
|
374
475
|
toolMessageIds
|
|
375
476
|
};
|
|
@@ -542,14 +643,15 @@ function applyAgentEvent(state, event, parentToolCallId, now = Date.now()) {
|
|
|
542
643
|
};
|
|
543
644
|
});
|
|
544
645
|
}
|
|
545
|
-
case "
|
|
546
|
-
|
|
547
|
-
const delayText = event.delayMs > 0 ? `in ${delaySeconds}s` : "now";
|
|
548
|
-
return appendWarningMessage(
|
|
646
|
+
case "provider_stream_reset":
|
|
647
|
+
return removeProviderStreamMessages(
|
|
549
648
|
state,
|
|
550
|
-
|
|
649
|
+
event.assistantMessageId,
|
|
650
|
+
event.reasoningMessageId,
|
|
651
|
+
parentToolCallId
|
|
551
652
|
);
|
|
552
|
-
|
|
653
|
+
case "provider_retry":
|
|
654
|
+
return upsertProviderRetryMessage(state, event, parentToolCallId, now);
|
|
553
655
|
default:
|
|
554
656
|
return state;
|
|
555
657
|
}
|
|
@@ -769,6 +871,7 @@ export {
|
|
|
769
871
|
removeApproval,
|
|
770
872
|
asJsonObject,
|
|
771
873
|
scopedToolKey,
|
|
874
|
+
isProviderRetryMessage,
|
|
772
875
|
settleAllLiveReasoning,
|
|
773
876
|
setToolMessageStatus,
|
|
774
877
|
setToolMessageApprovalKind,
|
package/dist/client/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createSessionViewState,
|
|
3
3
|
reduceSessionEvent
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-B6S3CWQX.js";
|
|
5
5
|
import {
|
|
6
6
|
PROTOCOL_VERSION
|
|
7
|
-
} from "../chunk-
|
|
7
|
+
} from "../chunk-BCFY6CHJ.js";
|
|
8
8
|
|
|
9
9
|
// src/client/endpoint.ts
|
|
10
10
|
var TerminalEndpointError = class extends Error {
|
package/dist/controller/index.js
CHANGED
package/dist/protocol/index.js
CHANGED
package/dist/state/index.js
CHANGED
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
dequeueActiveApproval,
|
|
19
19
|
enqueueApproval,
|
|
20
20
|
enqueueQuestion,
|
|
21
|
+
isProviderRetryMessage,
|
|
21
22
|
isToolAutoApprovedForMode,
|
|
22
23
|
reduceSessionEvent,
|
|
23
24
|
removeApproval,
|
|
@@ -27,7 +28,7 @@ import {
|
|
|
27
28
|
setToolMessageHidden,
|
|
28
29
|
setToolMessageStatus,
|
|
29
30
|
settleAllLiveReasoning
|
|
30
|
-
} from "../chunk-
|
|
31
|
+
} from "../chunk-B6S3CWQX.js";
|
|
31
32
|
export {
|
|
32
33
|
BACKGROUNDLESS_TOOLS,
|
|
33
34
|
appendErrorMessage,
|
|
@@ -45,6 +46,7 @@ export {
|
|
|
45
46
|
dirContains,
|
|
46
47
|
enqueueApproval,
|
|
47
48
|
enqueueQuestion,
|
|
49
|
+
isProviderRetryMessage,
|
|
48
50
|
isToolAutoApprovedForMode,
|
|
49
51
|
reduceSessionEvent,
|
|
50
52
|
removeApproval,
|
package/dist/state/messages.d.ts
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
import type { AgentEvent, JsonObject } from "@nuvin/agent-core/shared";
|
|
2
2
|
export type ToolMessageStatus = "approved" | "error" | "ok" | "pending" | "rejected" | "running";
|
|
3
|
+
export type ProviderRetryAttemptRecord = {
|
|
4
|
+
failedAttempt: number;
|
|
5
|
+
maxAttempts: number;
|
|
6
|
+
error: string;
|
|
7
|
+
delayMs: number;
|
|
8
|
+
reason: "http_status" | "stream_error" | "transport_error";
|
|
9
|
+
status?: number;
|
|
10
|
+
occurredAt: number;
|
|
11
|
+
};
|
|
12
|
+
export type ProviderRetryMessageDetail = {
|
|
13
|
+
assistantMessageId: string;
|
|
14
|
+
attempts: ProviderRetryAttemptRecord[];
|
|
15
|
+
};
|
|
3
16
|
export type BasicTuiMessage = {
|
|
4
17
|
id: string;
|
|
5
18
|
live?: boolean;
|
|
@@ -23,6 +36,7 @@ export type BasicTuiMessage = {
|
|
|
23
36
|
model?: string;
|
|
24
37
|
/** Local wall-clock time (ms epoch) a user message was sent; shown in the UI. */
|
|
25
38
|
createdAt?: number;
|
|
39
|
+
providerRetry?: ProviderRetryMessageDetail;
|
|
26
40
|
};
|
|
27
41
|
export type ToolTuiMessage = {
|
|
28
42
|
approvalKind?: "auto" | "user";
|
|
@@ -74,10 +88,16 @@ type StreamPreviewAcc = {
|
|
|
74
88
|
trailingWsNewlines: number;
|
|
75
89
|
};
|
|
76
90
|
export type TuiMessage = BasicTuiMessage | ToolTuiMessage;
|
|
91
|
+
export type ProviderRetryTuiMessage = BasicTuiMessage & {
|
|
92
|
+
role: "warning";
|
|
93
|
+
providerRetry: ProviderRetryMessageDetail;
|
|
94
|
+
};
|
|
95
|
+
export declare function isProviderRetryMessage(message: TuiMessage): message is ProviderRetryTuiMessage;
|
|
77
96
|
export type MessageState = {
|
|
78
97
|
assistantMessageIds: Record<string, string>;
|
|
79
98
|
messages: TuiMessage[];
|
|
80
99
|
nextId: number;
|
|
100
|
+
providerRetryMessageIds: Record<string, string>;
|
|
81
101
|
reasoningMessageIds: Record<string, string>;
|
|
82
102
|
toolMessageIds: Record<string, string>;
|
|
83
103
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/state/messages.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,UAAU,
|
|
1
|
+
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/state/messages.ts"],"names":[],"mappings":"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"}
|
package/dist/test-utils/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nuvin/session",
|
|
3
|
-
"version": "0.2.0-rc.
|
|
3
|
+
"version": "0.2.0-rc.15",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Headless session layer for Nuvin: state reducers, wire protocol, session controller",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
52
|
"ws": "^8.20.0",
|
|
53
|
-
"@nuvin/agent-core": "^0.2.0-rc.
|
|
53
|
+
"@nuvin/agent-core": "^0.2.0-rc.11"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
56
|
"@types/node": "^22.0.0",
|