@agent-native/core 0.84.61 → 0.84.63
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/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +12 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/production-agent.ts +199 -118
- package/corpus/core/src/client/sse-event-processor.ts +1 -0
- package/corpus/templates/design/app/hooks/useAgentEditRequest.ts +1 -1
- package/corpus/templates/design/changelog/2026-07-02-design-now-marks-stopped-agent-actions-clearly-and-keeps-var.md +6 -0
- package/corpus/templates/design/changelog/2026-07-02-design-now-recovers-cleanly-when-an-edit-screen-action-stall.md +6 -0
- package/corpus/templates/design/server/plugins/agent-chat.ts +2 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +197 -126
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +1 -0
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +3 -3
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +3 -3
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/package.json +1 -1
|
@@ -1978,10 +1978,12 @@ export async function runAgentLoop(opts) {
|
|
|
1978
1978
|
...(typeof progressBytes === "number" ? { progressBytes } : {}),
|
|
1979
1979
|
});
|
|
1980
1980
|
};
|
|
1981
|
-
const
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1981
|
+
const actionPreparationDeadlineAt = () => {
|
|
1982
|
+
let deadlineAt = Number.POSITIVE_INFINITY;
|
|
1983
|
+
if (zeroByteToolInputRestart) {
|
|
1984
|
+
deadlineAt = Math.min(deadlineAt, zeroByteToolInputRestart.firstStartedAt +
|
|
1985
|
+
ACTION_PREPARATION_NO_PROGRESS_TIMEOUT_MS);
|
|
1986
|
+
}
|
|
1985
1987
|
let earliestStartedAt = Number.POSITIVE_INFINITY;
|
|
1986
1988
|
let latestPositiveProgressAt = 0;
|
|
1987
1989
|
for (const active of activeToolInputs.values()) {
|
|
@@ -1996,9 +1998,71 @@ export async function runAgentLoop(opts) {
|
|
|
1996
1998
|
const progressAt = latestPositiveProgressAt > 0
|
|
1997
1999
|
? latestPositiveProgressAt
|
|
1998
2000
|
: earliestStartedAt;
|
|
1999
|
-
if (
|
|
2000
|
-
|
|
2001
|
-
|
|
2001
|
+
if (Number.isFinite(progressAt)) {
|
|
2002
|
+
deadlineAt = Math.min(deadlineAt, progressAt + ACTION_PREPARATION_NO_PROGRESS_TIMEOUT_MS);
|
|
2003
|
+
}
|
|
2004
|
+
return Number.isFinite(deadlineAt) ? deadlineAt : undefined;
|
|
2005
|
+
};
|
|
2006
|
+
const hasActionPreparationStalled = () => {
|
|
2007
|
+
const deadlineAt = actionPreparationDeadlineAt();
|
|
2008
|
+
return deadlineAt !== undefined && Date.now() >= deadlineAt;
|
|
2009
|
+
};
|
|
2010
|
+
const checkpointActionPreparationNoProgress = () => {
|
|
2011
|
+
if (endedForActionPreparationNoProgress)
|
|
2012
|
+
return;
|
|
2013
|
+
send({
|
|
2014
|
+
type: "auto_continue",
|
|
2015
|
+
reason: "no_progress",
|
|
2016
|
+
});
|
|
2017
|
+
endedForActionPreparationNoProgress = true;
|
|
2018
|
+
};
|
|
2019
|
+
let eventIteratorReturnRequested = false;
|
|
2020
|
+
const requestEventIteratorReturn = (iterator, awaitReturn) => {
|
|
2021
|
+
if (eventIteratorReturnRequested)
|
|
2022
|
+
return;
|
|
2023
|
+
eventIteratorReturnRequested = true;
|
|
2024
|
+
let returnPromise;
|
|
2025
|
+
try {
|
|
2026
|
+
returnPromise = iterator.return?.();
|
|
2027
|
+
}
|
|
2028
|
+
catch {
|
|
2029
|
+
return;
|
|
2030
|
+
}
|
|
2031
|
+
if (!returnPromise)
|
|
2032
|
+
return;
|
|
2033
|
+
if (awaitReturn)
|
|
2034
|
+
return returnPromise.catch(() => undefined);
|
|
2035
|
+
void returnPromise.catch(() => undefined);
|
|
2036
|
+
};
|
|
2037
|
+
const nextEngineEventWithActionPreparationTimeout = async (iterator) => {
|
|
2038
|
+
const deadlineAt = actionPreparationDeadlineAt();
|
|
2039
|
+
if (deadlineAt === undefined)
|
|
2040
|
+
return iterator.next();
|
|
2041
|
+
const timeoutMs = Math.max(0, deadlineAt - Date.now());
|
|
2042
|
+
if (timeoutMs <= 0) {
|
|
2043
|
+
checkpointActionPreparationNoProgress();
|
|
2044
|
+
requestEventIteratorReturn(iterator, false);
|
|
2045
|
+
return { done: true, value: undefined };
|
|
2046
|
+
}
|
|
2047
|
+
let timeoutId;
|
|
2048
|
+
const next = iterator.next();
|
|
2049
|
+
void next.catch(() => undefined);
|
|
2050
|
+
const timeout = new Promise((resolve) => {
|
|
2051
|
+
timeoutId = setTimeout(() => resolve("timeout"), timeoutMs);
|
|
2052
|
+
});
|
|
2053
|
+
try {
|
|
2054
|
+
const result = await Promise.race([next, timeout]);
|
|
2055
|
+
if (result === "timeout") {
|
|
2056
|
+
checkpointActionPreparationNoProgress();
|
|
2057
|
+
requestEventIteratorReturn(iterator, false);
|
|
2058
|
+
return { done: true, value: undefined };
|
|
2059
|
+
}
|
|
2060
|
+
return result;
|
|
2061
|
+
}
|
|
2062
|
+
finally {
|
|
2063
|
+
if (timeoutId)
|
|
2064
|
+
clearTimeout(timeoutId);
|
|
2065
|
+
}
|
|
2002
2066
|
};
|
|
2003
2067
|
const trackActiveToolInput = (key, toolName, bytes) => {
|
|
2004
2068
|
const now = Date.now();
|
|
@@ -2043,137 +2107,144 @@ export async function runAgentLoop(opts) {
|
|
|
2043
2107
|
now - zeroByteToolInputRestart.firstStartedAt >=
|
|
2044
2108
|
ACTION_PREPARATION_NO_PROGRESS_TIMEOUT_MS);
|
|
2045
2109
|
};
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
}
|
|
2055
|
-
// In-loop processor seam (stream hook). Each chunk is offered to every
|
|
2056
|
-
// processor's `processOutputStream` before the loop handles it. A
|
|
2057
|
-
// processor `abort()` throws a TripWire; catch it locally so it is not
|
|
2058
|
-
// mistaken for a retryable engine error, then break out cleanly.
|
|
2059
|
-
if (processorChain) {
|
|
2060
|
-
try {
|
|
2061
|
-
await processorChain.runStream(event);
|
|
2110
|
+
const eventIterator = eventStream[Symbol.asyncIterator]();
|
|
2111
|
+
let eventIteratorDone = false;
|
|
2112
|
+
try {
|
|
2113
|
+
while (true) {
|
|
2114
|
+
const nextEvent = await nextEngineEventWithActionPreparationTimeout(eventIterator);
|
|
2115
|
+
if (nextEvent.done) {
|
|
2116
|
+
eventIteratorDone = true;
|
|
2117
|
+
break;
|
|
2062
2118
|
}
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2119
|
+
const event = nextEvent.value;
|
|
2120
|
+
if (hasActionPreparationStalled()) {
|
|
2121
|
+
checkpointActionPreparationNoProgress();
|
|
2122
|
+
break;
|
|
2123
|
+
}
|
|
2124
|
+
// In-loop processor seam (stream hook). Each chunk is offered to every
|
|
2125
|
+
// processor's `processOutputStream` before the loop handles it. A
|
|
2126
|
+
// processor `abort()` throws a TripWire; catch it locally so it is not
|
|
2127
|
+
// mistaken for a retryable engine error, then break out cleanly.
|
|
2128
|
+
if (processorChain) {
|
|
2129
|
+
try {
|
|
2130
|
+
await processorChain.runStream(event);
|
|
2131
|
+
}
|
|
2132
|
+
catch (err) {
|
|
2133
|
+
if (err instanceof TripWire) {
|
|
2134
|
+
emitTripwire(err);
|
|
2135
|
+
break;
|
|
2136
|
+
}
|
|
2137
|
+
throw err;
|
|
2067
2138
|
}
|
|
2068
|
-
throw err;
|
|
2069
2139
|
}
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
send({ type: "text", text: event.text });
|
|
2075
|
-
}
|
|
2076
|
-
else if (event.type === "thinking-delta") {
|
|
2077
|
-
thinkingBuffer += event.text;
|
|
2078
|
-
// Forward thinking deltas as a distinct event type so the UI
|
|
2079
|
-
// can render a collapsible "Thinking…" cell while the model
|
|
2080
|
-
// reasons, then collapse it when content arrives.
|
|
2081
|
-
send({ type: "thinking", text: event.text });
|
|
2082
|
-
}
|
|
2083
|
-
else if (event.type === "tool-input-start") {
|
|
2084
|
-
const key = event.id ?? event.name;
|
|
2085
|
-
if (key && event.name) {
|
|
2086
|
-
toolInputNames.set(key, event.name);
|
|
2087
|
-
toolInputBytes.set(key, 0);
|
|
2088
|
-
trackActiveToolInput(key, event.name, 0);
|
|
2140
|
+
if (event.type === "text-delta") {
|
|
2141
|
+
resetZeroByteToolInputRestart();
|
|
2142
|
+
streamedAssistantText += event.text;
|
|
2143
|
+
send({ type: "text", text: event.text });
|
|
2089
2144
|
}
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
});
|
|
2096
|
-
endedForActionPreparationNoProgress = true;
|
|
2097
|
-
break;
|
|
2145
|
+
else if (event.type === "thinking-delta") {
|
|
2146
|
+
thinkingBuffer += event.text;
|
|
2147
|
+
// Forward thinking deltas as a distinct event type so the UI
|
|
2148
|
+
// can render a collapsible "Thinking…" cell while the model
|
|
2149
|
+
// reasons, then collapse it when content arrives.
|
|
2150
|
+
send({ type: "thinking", text: event.text });
|
|
2098
2151
|
}
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2152
|
+
else if (event.type === "tool-input-start") {
|
|
2153
|
+
const key = event.id ?? event.name;
|
|
2154
|
+
if (key && event.name) {
|
|
2155
|
+
toolInputNames.set(key, event.name);
|
|
2156
|
+
toolInputBytes.set(key, 0);
|
|
2157
|
+
trackActiveToolInput(key, event.name, 0);
|
|
2158
|
+
}
|
|
2159
|
+
sendToolInputActivity(event.name, key, undefined, true);
|
|
2160
|
+
if (noteZeroByteToolInputStart(event.name)) {
|
|
2161
|
+
send({
|
|
2162
|
+
type: "auto_continue",
|
|
2163
|
+
reason: "no_progress",
|
|
2164
|
+
});
|
|
2165
|
+
endedForActionPreparationNoProgress = true;
|
|
2166
|
+
break;
|
|
2167
|
+
}
|
|
2168
|
+
}
|
|
2169
|
+
else if (event.type === "tool-input-delta") {
|
|
2170
|
+
const key = event.id ?? event.name;
|
|
2171
|
+
const toolName = event.name ??
|
|
2172
|
+
(event.id ? toolInputNames.get(event.id) : undefined);
|
|
2173
|
+
let progressBytes;
|
|
2174
|
+
let startedZeroByteInput = false;
|
|
2175
|
+
if (key) {
|
|
2176
|
+
const hadByteRecord = toolInputBytes.has(key);
|
|
2177
|
+
const previous = hadByteRecord
|
|
2178
|
+
? (toolInputBytes.get(key) ?? 0)
|
|
2179
|
+
: 0;
|
|
2180
|
+
progressBytes =
|
|
2181
|
+
previous +
|
|
2182
|
+
new TextEncoder().encode(event.text ?? "").byteLength;
|
|
2183
|
+
toolInputBytes.set(key, progressBytes);
|
|
2184
|
+
if (!hadByteRecord || progressBytes > previous) {
|
|
2185
|
+
trackActiveToolInput(key, toolName, progressBytes);
|
|
2186
|
+
if (progressBytes > 0) {
|
|
2187
|
+
resetZeroByteToolInputRestart();
|
|
2188
|
+
}
|
|
2189
|
+
else if (!hadByteRecord) {
|
|
2190
|
+
startedZeroByteInput = true;
|
|
2191
|
+
}
|
|
2122
2192
|
}
|
|
2123
2193
|
}
|
|
2194
|
+
sendToolInputActivity(toolName, key, progressBytes);
|
|
2195
|
+
if (startedZeroByteInput &&
|
|
2196
|
+
toolName &&
|
|
2197
|
+
noteZeroByteToolInputStart(toolName)) {
|
|
2198
|
+
send({
|
|
2199
|
+
type: "auto_continue",
|
|
2200
|
+
reason: "no_progress",
|
|
2201
|
+
});
|
|
2202
|
+
endedForActionPreparationNoProgress = true;
|
|
2203
|
+
break;
|
|
2204
|
+
}
|
|
2124
2205
|
}
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
toolName &&
|
|
2128
|
-
noteZeroByteToolInputStart(toolName)) {
|
|
2129
|
-
send({
|
|
2130
|
-
type: "auto_continue",
|
|
2131
|
-
reason: "no_progress",
|
|
2132
|
-
});
|
|
2133
|
-
endedForActionPreparationNoProgress = true;
|
|
2134
|
-
break;
|
|
2206
|
+
else if (event.type === "gateway-heartbeat") {
|
|
2207
|
+
send({ type: "stream_keepalive" });
|
|
2135
2208
|
}
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
toolCallErrors.set(event.id, {
|
|
2145
|
-
name: event.name,
|
|
2146
|
-
input: event.input,
|
|
2147
|
-
error: event.error,
|
|
2148
|
-
});
|
|
2149
|
-
}
|
|
2150
|
-
else if (event.type === "assistant-content") {
|
|
2151
|
-
assistantContent = event.parts;
|
|
2152
|
-
}
|
|
2153
|
-
else if (event.type === "usage") {
|
|
2154
|
-
usage.inputTokens += event.inputTokens;
|
|
2155
|
-
usage.outputTokens += event.outputTokens;
|
|
2156
|
-
usage.cacheReadTokens += event.cacheReadTokens ?? 0;
|
|
2157
|
-
usage.cacheWriteTokens += event.cacheWriteTokens ?? 0;
|
|
2158
|
-
}
|
|
2159
|
-
else if (event.type === "stop") {
|
|
2160
|
-
terminalStopReason = event.reason;
|
|
2161
|
-
if (event.reason === "error") {
|
|
2162
|
-
throw new EngineError(event.error ?? "Engine stream error", {
|
|
2163
|
-
errorCode: event.errorCode,
|
|
2164
|
-
upgradeUrl: event.upgradeUrl,
|
|
2165
|
-
statusCode: event.statusCode,
|
|
2166
|
-
providerRetryable: event.providerRetryable,
|
|
2209
|
+
else if (event.type === "tool-call") {
|
|
2210
|
+
// The authoritative tool-call blocks arrive in assistant-content.
|
|
2211
|
+
}
|
|
2212
|
+
else if (event.type === "tool-call-error") {
|
|
2213
|
+
toolCallErrors.set(event.id, {
|
|
2214
|
+
name: event.name,
|
|
2215
|
+
input: event.input,
|
|
2216
|
+
error: event.error,
|
|
2167
2217
|
});
|
|
2168
2218
|
}
|
|
2219
|
+
else if (event.type === "assistant-content") {
|
|
2220
|
+
assistantContent = event.parts;
|
|
2221
|
+
}
|
|
2222
|
+
else if (event.type === "usage") {
|
|
2223
|
+
usage.inputTokens += event.inputTokens;
|
|
2224
|
+
usage.outputTokens += event.outputTokens;
|
|
2225
|
+
usage.cacheReadTokens += event.cacheReadTokens ?? 0;
|
|
2226
|
+
usage.cacheWriteTokens += event.cacheWriteTokens ?? 0;
|
|
2227
|
+
}
|
|
2228
|
+
else if (event.type === "stop") {
|
|
2229
|
+
terminalStopReason = event.reason;
|
|
2230
|
+
if (event.reason === "error") {
|
|
2231
|
+
throw new EngineError(event.error ?? "Engine stream error", {
|
|
2232
|
+
errorCode: event.errorCode,
|
|
2233
|
+
upgradeUrl: event.upgradeUrl,
|
|
2234
|
+
statusCode: event.statusCode,
|
|
2235
|
+
providerRetryable: event.providerRetryable,
|
|
2236
|
+
});
|
|
2237
|
+
}
|
|
2238
|
+
}
|
|
2239
|
+
if (hasActionPreparationStalled()) {
|
|
2240
|
+
checkpointActionPreparationNoProgress();
|
|
2241
|
+
break;
|
|
2242
|
+
}
|
|
2169
2243
|
}
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
});
|
|
2175
|
-
endedForActionPreparationNoProgress = true;
|
|
2176
|
-
break;
|
|
2244
|
+
}
|
|
2245
|
+
finally {
|
|
2246
|
+
if (!eventIteratorDone) {
|
|
2247
|
+
await requestEventIteratorReturn(eventIterator, !eventIteratorReturnRequested);
|
|
2177
2248
|
}
|
|
2178
2249
|
}
|
|
2179
2250
|
if (endedForActionPreparationNoProgress) {
|