@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.
@@ -1978,10 +1978,12 @@ export async function runAgentLoop(opts) {
1978
1978
  ...(typeof progressBytes === "number" ? { progressBytes } : {}),
1979
1979
  });
1980
1980
  };
1981
- const hasActionPreparationStalled = () => {
1982
- if (activeToolInputs.size === 0)
1983
- return false;
1984
- const now = Date.now();
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 (!Number.isFinite(progressAt))
2000
- return false;
2001
- return now - progressAt >= ACTION_PREPARATION_NO_PROGRESS_TIMEOUT_MS;
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
- for await (const event of eventStream) {
2047
- if (hasActionPreparationStalled()) {
2048
- send({
2049
- type: "auto_continue",
2050
- reason: "no_progress",
2051
- });
2052
- endedForActionPreparationNoProgress = true;
2053
- break;
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
- catch (err) {
2064
- if (err instanceof TripWire) {
2065
- emitTripwire(err);
2066
- break;
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
- if (event.type === "text-delta") {
2072
- resetZeroByteToolInputRestart();
2073
- streamedAssistantText += event.text;
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
- sendToolInputActivity(event.name, key, undefined, true);
2091
- if (noteZeroByteToolInputStart(event.name)) {
2092
- send({
2093
- type: "auto_continue",
2094
- reason: "no_progress",
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
- else if (event.type === "tool-input-delta") {
2101
- const key = event.id ?? event.name;
2102
- const toolName = event.name ??
2103
- (event.id ? toolInputNames.get(event.id) : undefined);
2104
- let progressBytes;
2105
- let startedZeroByteInput = false;
2106
- if (key) {
2107
- const hadByteRecord = toolInputBytes.has(key);
2108
- const previous = hadByteRecord
2109
- ? (toolInputBytes.get(key) ?? 0)
2110
- : 0;
2111
- progressBytes =
2112
- previous +
2113
- new TextEncoder().encode(event.text ?? "").byteLength;
2114
- toolInputBytes.set(key, progressBytes);
2115
- if (!hadByteRecord || progressBytes > previous) {
2116
- trackActiveToolInput(key, toolName, progressBytes);
2117
- if (progressBytes > 0) {
2118
- resetZeroByteToolInputRestart(toolName);
2119
- }
2120
- else if (!hadByteRecord) {
2121
- startedZeroByteInput = true;
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
- sendToolInputActivity(toolName, key, progressBytes);
2126
- if (startedZeroByteInput &&
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
- else if (event.type === "gateway-heartbeat") {
2138
- send({ type: "stream_keepalive" });
2139
- }
2140
- else if (event.type === "tool-call") {
2141
- // The authoritative tool-call blocks arrive in assistant-content.
2142
- }
2143
- else if (event.type === "tool-call-error") {
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
- if (hasActionPreparationStalled()) {
2171
- send({
2172
- type: "auto_continue",
2173
- reason: "no_progress",
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) {