@agent-native/core 0.84.40 → 0.84.42
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/core/CHANGELOG.md +12 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/production-agent.ts +205 -50
- package/dist/agent/production-agent.d.ts +2 -3
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +168 -44
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +2 -2
- package/dist/observability/routes.d.ts +5 -5
- 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
|
@@ -560,6 +560,7 @@ function maxRetriesForError(err) {
|
|
|
560
560
|
}
|
|
561
561
|
const TOOL_INPUT_ACTIVITY_INTERVAL_MS = 1500;
|
|
562
562
|
const ACTION_PREPARATION_NO_PROGRESS_TIMEOUT_MS = 90_000;
|
|
563
|
+
const ACTION_PREPARATION_ZERO_BYTE_RESTART_LIMIT = 2;
|
|
563
564
|
const MAX_TEXT_ATTACHMENT_CHARS = 60_000;
|
|
564
565
|
const MAX_SELECTION_CONTEXT_CHARS = 8_000;
|
|
565
566
|
const MAX_RESOURCE_INVENTORY_ITEMS = 40;
|
|
@@ -1977,6 +1978,7 @@ export async function runAgentLoop(opts) {
|
|
|
1977
1978
|
const toolInputBytes = new Map();
|
|
1978
1979
|
let lastToolInputActivityAt = 0;
|
|
1979
1980
|
const activeToolInputs = new Map();
|
|
1981
|
+
let zeroByteToolInputRestart;
|
|
1980
1982
|
let endedForActionPreparationNoProgress = false;
|
|
1981
1983
|
const sendToolInputActivity = (toolName, toolInputId, progressBytes, force = false) => {
|
|
1982
1984
|
const now = Date.now();
|
|
@@ -2031,6 +2033,37 @@ export async function runAgentLoop(opts) {
|
|
|
2031
2033
|
bytes,
|
|
2032
2034
|
});
|
|
2033
2035
|
};
|
|
2036
|
+
const resetZeroByteToolInputRestart = (toolName) => {
|
|
2037
|
+
if (!zeroByteToolInputRestart)
|
|
2038
|
+
return;
|
|
2039
|
+
if (!toolName || zeroByteToolInputRestart.toolName === toolName) {
|
|
2040
|
+
zeroByteToolInputRestart = undefined;
|
|
2041
|
+
}
|
|
2042
|
+
};
|
|
2043
|
+
const noteZeroByteToolInputStart = (toolName) => {
|
|
2044
|
+
if (!toolName)
|
|
2045
|
+
return false;
|
|
2046
|
+
const now = Date.now();
|
|
2047
|
+
if (zeroByteToolInputRestart?.toolName === toolName) {
|
|
2048
|
+
zeroByteToolInputRestart = {
|
|
2049
|
+
...zeroByteToolInputRestart,
|
|
2050
|
+
lastStartedAt: now,
|
|
2051
|
+
count: zeroByteToolInputRestart.count + 1,
|
|
2052
|
+
};
|
|
2053
|
+
}
|
|
2054
|
+
else {
|
|
2055
|
+
zeroByteToolInputRestart = {
|
|
2056
|
+
toolName,
|
|
2057
|
+
firstStartedAt: now,
|
|
2058
|
+
lastStartedAt: now,
|
|
2059
|
+
count: 1,
|
|
2060
|
+
};
|
|
2061
|
+
}
|
|
2062
|
+
return (zeroByteToolInputRestart.count >
|
|
2063
|
+
ACTION_PREPARATION_ZERO_BYTE_RESTART_LIMIT &&
|
|
2064
|
+
now - zeroByteToolInputRestart.firstStartedAt >=
|
|
2065
|
+
ACTION_PREPARATION_NO_PROGRESS_TIMEOUT_MS);
|
|
2066
|
+
};
|
|
2034
2067
|
const clearActiveToolInputs = () => {
|
|
2035
2068
|
activeToolInputs.clear();
|
|
2036
2069
|
};
|
|
@@ -2060,6 +2093,7 @@ export async function runAgentLoop(opts) {
|
|
|
2060
2093
|
}
|
|
2061
2094
|
}
|
|
2062
2095
|
if (event.type === "text-delta") {
|
|
2096
|
+
resetZeroByteToolInputRestart();
|
|
2063
2097
|
streamedAssistantText += event.text;
|
|
2064
2098
|
send({ type: "text", text: event.text });
|
|
2065
2099
|
}
|
|
@@ -2078,6 +2112,14 @@ export async function runAgentLoop(opts) {
|
|
|
2078
2112
|
trackActiveToolInput(key, event.name, 0);
|
|
2079
2113
|
}
|
|
2080
2114
|
sendToolInputActivity(event.name, key, undefined, true);
|
|
2115
|
+
if (noteZeroByteToolInputStart(event.name)) {
|
|
2116
|
+
send({
|
|
2117
|
+
type: "auto_continue",
|
|
2118
|
+
reason: "no_progress",
|
|
2119
|
+
});
|
|
2120
|
+
endedForActionPreparationNoProgress = true;
|
|
2121
|
+
break;
|
|
2122
|
+
}
|
|
2081
2123
|
}
|
|
2082
2124
|
else if (event.type === "tool-input-delta") {
|
|
2083
2125
|
const key = event.id ?? event.name;
|
|
@@ -2092,6 +2134,7 @@ export async function runAgentLoop(opts) {
|
|
|
2092
2134
|
toolInputBytes.set(key, progressBytes);
|
|
2093
2135
|
if (progressBytes > previous) {
|
|
2094
2136
|
trackActiveToolInput(key, toolName, progressBytes);
|
|
2137
|
+
resetZeroByteToolInputRestart(toolName);
|
|
2095
2138
|
}
|
|
2096
2139
|
}
|
|
2097
2140
|
sendToolInputActivity(toolName, key, progressBytes);
|
|
@@ -2102,9 +2145,11 @@ export async function runAgentLoop(opts) {
|
|
|
2102
2145
|
else if (event.type === "tool-call") {
|
|
2103
2146
|
// The authoritative tool-call blocks arrive in assistant-content.
|
|
2104
2147
|
resetActiveToolInput(event.name, event.id);
|
|
2148
|
+
resetZeroByteToolInputRestart(event.name);
|
|
2105
2149
|
}
|
|
2106
2150
|
else if (event.type === "tool-call-error") {
|
|
2107
2151
|
resetActiveToolInput(event.name, event.id);
|
|
2152
|
+
resetZeroByteToolInputRestart(event.name);
|
|
2108
2153
|
toolCallErrors.set(event.id, {
|
|
2109
2154
|
name: event.name,
|
|
2110
2155
|
input: event.input,
|
|
@@ -3135,38 +3180,110 @@ function isPreparingActionActivityEvent(event) {
|
|
|
3135
3180
|
return label.startsWith("preparing ") && label.includes(" action");
|
|
3136
3181
|
}
|
|
3137
3182
|
export function lastUnfinishedPreparingActionToolFromEvents(events) {
|
|
3138
|
-
|
|
3139
|
-
|
|
3183
|
+
const active = new Map();
|
|
3184
|
+
const idlessToolStarts = new Map();
|
|
3185
|
+
const removeOldestMatchingActivePreparation = (tool, shouldRemove = () => true) => {
|
|
3186
|
+
let oldest;
|
|
3187
|
+
for (const [key, value] of active) {
|
|
3188
|
+
if (value.tool !== tool || !shouldRemove(value))
|
|
3189
|
+
continue;
|
|
3190
|
+
if (!oldest || value.order < oldest.order) {
|
|
3191
|
+
oldest = {
|
|
3192
|
+
key,
|
|
3193
|
+
order: value.order,
|
|
3194
|
+
};
|
|
3195
|
+
}
|
|
3196
|
+
}
|
|
3197
|
+
if (oldest)
|
|
3198
|
+
active.delete(oldest.key);
|
|
3199
|
+
return Boolean(oldest);
|
|
3200
|
+
};
|
|
3201
|
+
const removeMatchingActivePreparation = (event) => {
|
|
3202
|
+
const id = event.id?.trim();
|
|
3203
|
+
const tool = event.tool?.trim();
|
|
3204
|
+
if (!tool)
|
|
3205
|
+
return;
|
|
3206
|
+
if (id) {
|
|
3207
|
+
if (!active.delete(`id:${id}`)) {
|
|
3208
|
+
removeOldestMatchingActivePreparation(tool, (value) => !value.id);
|
|
3209
|
+
}
|
|
3210
|
+
return;
|
|
3211
|
+
}
|
|
3212
|
+
if (event.type === "tool_start") {
|
|
3213
|
+
if (removeOldestMatchingActivePreparation(tool)) {
|
|
3214
|
+
idlessToolStarts.set(tool, (idlessToolStarts.get(tool) ?? 0) + 1);
|
|
3215
|
+
}
|
|
3216
|
+
return;
|
|
3217
|
+
}
|
|
3218
|
+
const startedCount = idlessToolStarts.get(tool) ?? 0;
|
|
3219
|
+
if (startedCount > 0) {
|
|
3220
|
+
if (startedCount === 1) {
|
|
3221
|
+
idlessToolStarts.delete(tool);
|
|
3222
|
+
}
|
|
3223
|
+
else {
|
|
3224
|
+
idlessToolStarts.set(tool, startedCount - 1);
|
|
3225
|
+
}
|
|
3226
|
+
return;
|
|
3227
|
+
}
|
|
3228
|
+
removeOldestMatchingActivePreparation(tool);
|
|
3229
|
+
};
|
|
3230
|
+
events.forEach((event, order) => {
|
|
3140
3231
|
if (isPreparingActionActivityEvent(event)) {
|
|
3141
3232
|
const tool = event.tool?.trim();
|
|
3142
3233
|
if (tool) {
|
|
3143
|
-
|
|
3234
|
+
const id = event.id?.trim();
|
|
3235
|
+
const key = id ? `id:${id}` : `tool:${tool}:${order}`;
|
|
3236
|
+
active.set(key, {
|
|
3144
3237
|
tool,
|
|
3145
|
-
|
|
3146
|
-
|
|
3238
|
+
order,
|
|
3239
|
+
...(id ? { id } : {}),
|
|
3240
|
+
});
|
|
3147
3241
|
}
|
|
3148
|
-
|
|
3242
|
+
return;
|
|
3149
3243
|
}
|
|
3150
3244
|
if (event.type === "tool_start" || event.type === "tool_done") {
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
if (active &&
|
|
3154
|
-
((id && active.id === id) || (!id && tool && active.tool === tool))) {
|
|
3155
|
-
active = null;
|
|
3156
|
-
}
|
|
3157
|
-
continue;
|
|
3245
|
+
removeMatchingActivePreparation(event);
|
|
3246
|
+
return;
|
|
3158
3247
|
}
|
|
3159
3248
|
if (event.type === "error" && isRecoverableContinuationError(event)) {
|
|
3160
|
-
|
|
3249
|
+
return;
|
|
3161
3250
|
}
|
|
3162
3251
|
if (event.type === "clear" ||
|
|
3163
3252
|
event.type === "done" ||
|
|
3164
3253
|
event.type === "error" ||
|
|
3165
3254
|
event.type === "missing_api_key") {
|
|
3166
|
-
active
|
|
3255
|
+
active.clear();
|
|
3256
|
+
idlessToolStarts.clear();
|
|
3257
|
+
}
|
|
3258
|
+
});
|
|
3259
|
+
let latest;
|
|
3260
|
+
for (const value of active.values()) {
|
|
3261
|
+
if (!latest || value.order > latest.order) {
|
|
3262
|
+
latest = value;
|
|
3167
3263
|
}
|
|
3168
3264
|
}
|
|
3169
|
-
return
|
|
3265
|
+
return latest?.tool;
|
|
3266
|
+
}
|
|
3267
|
+
function endsAfterCompletedToolWithoutAssistantFinal(run) {
|
|
3268
|
+
let completedToolAfterLastAssistantText = false;
|
|
3269
|
+
for (const { event } of run.events) {
|
|
3270
|
+
if (event.type === "text" && event.text.trim().length > 0) {
|
|
3271
|
+
completedToolAfterLastAssistantText = false;
|
|
3272
|
+
continue;
|
|
3273
|
+
}
|
|
3274
|
+
if (event.type === "tool_done" && event.isError !== true) {
|
|
3275
|
+
completedToolAfterLastAssistantText = true;
|
|
3276
|
+
continue;
|
|
3277
|
+
}
|
|
3278
|
+
if (event.type === "clear" ||
|
|
3279
|
+
event.type === "error" ||
|
|
3280
|
+
event.type === "missing_api_key" ||
|
|
3281
|
+
event.type === "auto_continue" ||
|
|
3282
|
+
event.type === "loop_limit") {
|
|
3283
|
+
completedToolAfterLastAssistantText = false;
|
|
3284
|
+
}
|
|
3285
|
+
}
|
|
3286
|
+
return completedToolAfterLastAssistantText;
|
|
3170
3287
|
}
|
|
3171
3288
|
function lastUnfinishedPreparingActionTool(run) {
|
|
3172
3289
|
return lastUnfinishedPreparingActionToolFromEvents(run.events.map(({ event }) => event));
|
|
@@ -3182,8 +3299,15 @@ export function backgroundContinuationReasonForRun(run) {
|
|
|
3182
3299
|
if (last?.type === "error" && isRecoverableContinuationError(last)) {
|
|
3183
3300
|
return continuationReasonForResumableError(new EngineError(last.error, { errorCode: last.errorCode }));
|
|
3184
3301
|
}
|
|
3302
|
+
if (endsAfterCompletedToolWithoutAssistantFinal(run)) {
|
|
3303
|
+
return "stream_ended";
|
|
3304
|
+
}
|
|
3185
3305
|
return "run_timeout";
|
|
3186
3306
|
}
|
|
3307
|
+
function endsAtContinuationBoundary(run) {
|
|
3308
|
+
return (endsAtInternalContinuationBoundary(run) ||
|
|
3309
|
+
endsAfterCompletedToolWithoutAssistantFinal(run));
|
|
3310
|
+
}
|
|
3187
3311
|
/**
|
|
3188
3312
|
* Hard cap on server-driven background→background continuation chunks for a
|
|
3189
3313
|
* single logical turn. A `backgroundFunction` run gets a ~13-min soft timeout,
|
|
@@ -3195,14 +3319,13 @@ export const MAX_BACKGROUND_RUN_CONTINUATIONS = 20;
|
|
|
3195
3319
|
/**
|
|
3196
3320
|
* Whether the background worker should self-fire the next server-driven
|
|
3197
3321
|
* continuation chunk. True only when this is a background worker run that ended
|
|
3198
|
-
* at a recoverable
|
|
3199
|
-
* still under its budget.
|
|
3200
|
-
* booting the whole handler.
|
|
3322
|
+
* at a recoverable unfinished boundary (not aborted/stopped) and the chain is
|
|
3323
|
+
* still under its budget. Aborted / user-stopped runs do NOT chain.
|
|
3201
3324
|
*/
|
|
3202
3325
|
export function shouldChainBackgroundContinuation(opts) {
|
|
3203
3326
|
return (opts.isBackgroundWorker &&
|
|
3204
3327
|
opts.run.status !== "aborted" &&
|
|
3205
|
-
|
|
3328
|
+
endsAtContinuationBoundary(opts.run) &&
|
|
3206
3329
|
opts.continuationCount < MAX_BACKGROUND_RUN_CONTINUATIONS);
|
|
3207
3330
|
}
|
|
3208
3331
|
export async function claimBackgroundWorkerRunEarly(opts) {
|
|
@@ -4122,10 +4245,15 @@ export function createProductionAgentHandler(options) {
|
|
|
4122
4245
|
turnId: effectiveTurnId,
|
|
4123
4246
|
}
|
|
4124
4247
|
: null;
|
|
4248
|
+
const willChainBackgroundContinuation = (run) => shouldChainBackgroundContinuation({
|
|
4249
|
+
isBackgroundWorker,
|
|
4250
|
+
run,
|
|
4251
|
+
continuationCount: backgroundContinuationCount,
|
|
4252
|
+
});
|
|
4125
4253
|
const completeTrackedProgressRun = async (run, completionError) => {
|
|
4126
4254
|
if (!trackedProgressRunId || !trackedProgressOwner)
|
|
4127
4255
|
return;
|
|
4128
|
-
if (!completionError &&
|
|
4256
|
+
if (!completionError && willChainBackgroundContinuation(run)) {
|
|
4129
4257
|
return;
|
|
4130
4258
|
}
|
|
4131
4259
|
const terminalStatus = run.status === "aborted"
|
|
@@ -4210,7 +4338,7 @@ export function createProductionAgentHandler(options) {
|
|
|
4210
4338
|
// below, they did not "throw").
|
|
4211
4339
|
if (isBackgroundWorker &&
|
|
4212
4340
|
run.status === "errored" &&
|
|
4213
|
-
!
|
|
4341
|
+
!willChainBackgroundContinuation(run)) {
|
|
4214
4342
|
const errEvent = [...run.events]
|
|
4215
4343
|
.reverse()
|
|
4216
4344
|
.find((e) => e.event.type === "error")?.event;
|
|
@@ -4231,28 +4359,24 @@ export function createProductionAgentHandler(options) {
|
|
|
4231
4359
|
// agent-teams `fireInternalDispatch({ body: { mode: "continue" }})`
|
|
4232
4360
|
// chain. Bounded by MAX_BACKGROUND_RUN_CONTINUATIONS. Aborted /
|
|
4233
4361
|
// user-stopped runs do NOT chain.
|
|
4234
|
-
|
|
4235
|
-
|
|
4236
|
-
|
|
4237
|
-
|
|
4238
|
-
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
isBackgroundWorker,
|
|
4253
|
-
run,
|
|
4254
|
-
continuationCount: backgroundContinuationCount,
|
|
4255
|
-
})) {
|
|
4362
|
+
// Self-chain server-side for EVERY durable worker, not only the
|
|
4363
|
+
// ones inside a `-background` function. Server-driven
|
|
4364
|
+
// continuation is the whole point of durable background: the run
|
|
4365
|
+
// must survive the client disconnecting (closed tab), so it
|
|
4366
|
+
// cannot depend on the browser re-POSTing `auto_continue`. A
|
|
4367
|
+
// worker on the regular ~60s function — a Netlify routing miss,
|
|
4368
|
+
// or a non-Netlify host (Vercel/Cloudflare/Render/Fly) that
|
|
4369
|
+
// never emits a `-background` function — checkpoints at the 40s
|
|
4370
|
+
// soft-timeout and self-dispatches the next 40s chunk; a worker
|
|
4371
|
+
// in a real `-background` function chains ~13-min chunks. Only
|
|
4372
|
+
// the per-chunk BUDGET differs by function type (gated by
|
|
4373
|
+
// `runsInBackgroundFunction` at the startRun call below); the
|
|
4374
|
+
// continuation itself must stay server-driven on both. (The
|
|
4375
|
+
// self-chain is only reachable when the initial dispatch already
|
|
4376
|
+
// succeeded — a dispatch fast-fail degrades to the inline
|
|
4377
|
+
// foreground fallback, which is not a worker and rides the
|
|
4378
|
+
// connected client's auto_continue instead.)
|
|
4379
|
+
if (willChainBackgroundContinuation(run)) {
|
|
4256
4380
|
// Mint the next chunk's runId here and sign the dispatch token
|
|
4257
4381
|
// over it, so the `_process-run` route's HMAC check and the
|
|
4258
4382
|
// worker's run identity agree. Fresh runId (not this chunk's) so
|