@agent-native/core 0.220.0 → 0.220.1-nightly-20261024183236
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/action.d.ts +18 -0
- package/dist/action.js +15 -0
- package/dist/agent/production-agent.js +42 -15
- package/dist/agent/run-manager.js +24 -4
- package/dist/automation/index.d.ts +1 -1
- package/dist/client/sse-event-processor.d.ts +7 -0
- package/dist/client/sse-event-processor.js +3 -2
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/integrations/google-docs-poller.d.ts +9 -1
- package/dist/integrations/google-docs-poller.js +16 -5
- package/dist/integrations/pending-tasks-retry-job.d.ts +1 -1
- package/dist/integrations/pending-tasks-retry-job.js +6 -2
- package/dist/integrations/plugin.js +36 -5
- package/dist/integrations/remote-push-delivery-job.d.ts +1 -1
- package/dist/integrations/remote-push-delivery-job.js +6 -2
- package/dist/integrations/remote-retry-job.d.ts +1 -1
- package/dist/integrations/remote-retry-job.js +6 -2
- package/dist/jobs/actions/manage-recurring-job.d.ts +1 -1
- package/dist/notifications/routes.d.ts +4 -4
- package/dist/observability/routes.d.ts +7 -7
- package/dist/org/handlers.d.ts +1 -1
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +4 -4
- package/dist/provider-api/actions/provider-api.d.ts +4 -4
- package/dist/provider-api/corpus-jobs.js +3 -21
- package/dist/provider-api/index.js +17 -23
- package/dist/provider-api/request-body.d.ts +1 -0
- package/dist/provider-api/request-body.js +50 -0
- package/dist/provider-api/staging.js +2 -20
- package/dist/secrets/routes.d.ts +10 -10
- package/dist/server/agent-engine-default-model-route.d.ts +1 -1
- package/dist/server/agent-engine-ollama-models-route.d.ts +1 -1
- package/dist/server/background-jobs.d.ts +3 -0
- package/dist/server/background-jobs.js +33 -0
- package/dist/server/core-routes-plugin.js +20 -2
- package/dist/server/dev-database-lifecycle.js +13 -3
- package/dist/server/interval-job.d.ts +2 -1
- package/dist/server/interval-job.js +1 -1
- package/dist/shared/poll-engine.d.ts +2 -1
- package/dist/shared/poll-engine.js +25 -1
- package/dist/shared/progress-events.d.ts +1 -0
- package/dist/shared/progress-events.js +13 -0
- package/dist/triggers/actions/manage-automation.d.ts +6 -6
- package/dist/triggers/dispatcher.d.ts +1 -0
- package/dist/triggers/dispatcher.js +28 -2
- package/package.json +3 -3
package/dist/action.d.ts
CHANGED
|
@@ -23,8 +23,26 @@ export interface ActionAutomationContext {
|
|
|
23
23
|
triggerName: string;
|
|
24
24
|
policyId?: string;
|
|
25
25
|
}
|
|
26
|
+
export declare const DEFAULT_TOOL_RESULT_CHARS = 50000;
|
|
27
|
+
/**
|
|
28
|
+
* The most characters of a pretty-printed result the agent keeps for a call:
|
|
29
|
+
* the action's own limit, else the agent-wide limit, else the default, bounded
|
|
30
|
+
* by any hard limit. The agent loop and actions that fit their own results
|
|
31
|
+
* resolve it here so they cannot disagree.
|
|
32
|
+
*/
|
|
33
|
+
export declare function resolveToolMaxResultChars(action: {
|
|
34
|
+
maxResultChars?: number;
|
|
35
|
+
}, limits?: {
|
|
36
|
+
maxResultChars?: number;
|
|
37
|
+
hardMaxResultChars?: number;
|
|
38
|
+
}): number;
|
|
26
39
|
export interface ActionRunContext {
|
|
27
40
|
send?: (event: AgentChatEvent) => void;
|
|
41
|
+
/**
|
|
42
|
+
* Result cap the agent loop enforces on this call, from
|
|
43
|
+
* `resolveToolMaxResultChars`. Absent outside the agent loop.
|
|
44
|
+
*/
|
|
45
|
+
maxResultChars?: number;
|
|
28
46
|
userEmail?: string;
|
|
29
47
|
/**
|
|
30
48
|
* Request headers for server-only actions that must delegate to an
|
package/dist/action.js
CHANGED
|
@@ -48,6 +48,21 @@ export async function recordInvalidActionCall(entry, args, ctx, error) {
|
|
|
48
48
|
}
|
|
49
49
|
return true;
|
|
50
50
|
}
|
|
51
|
+
export const DEFAULT_TOOL_RESULT_CHARS = 50_000;
|
|
52
|
+
/**
|
|
53
|
+
* The most characters of a pretty-printed result the agent keeps for a call:
|
|
54
|
+
* the action's own limit, else the agent-wide limit, else the default, bounded
|
|
55
|
+
* by any hard limit. The agent loop and actions that fit their own results
|
|
56
|
+
* resolve it here so they cannot disagree.
|
|
57
|
+
*/
|
|
58
|
+
export function resolveToolMaxResultChars(action, limits) {
|
|
59
|
+
const configured = action.maxResultChars ??
|
|
60
|
+
limits?.maxResultChars ??
|
|
61
|
+
DEFAULT_TOOL_RESULT_CHARS;
|
|
62
|
+
return typeof limits?.hardMaxResultChars === "number"
|
|
63
|
+
? Math.min(configured, limits.hardMaxResultChars)
|
|
64
|
+
: configured;
|
|
65
|
+
}
|
|
51
66
|
export class ActionContractError extends Error {
|
|
52
67
|
actionContractError = true;
|
|
53
68
|
errorCode;
|
|
@@ -9,7 +9,7 @@ import { parseA2AAgentActivityPart } from "../a2a/activity.js";
|
|
|
9
9
|
import { actionCallIsRetrySafe, actionChangeResource, classifyActionCall, } from "../action-call-classification.js";
|
|
10
10
|
import { runOutsideActionInvocation, vouchedOutcome } from "../action-frame.js";
|
|
11
11
|
import { ACTION_CHAT_UI_RECORD_CHANGE_RENDERER, normalizeActionChangeResult, } from "../action-ui.js";
|
|
12
|
-
import { AgentConnectionRequiredError, describeToolParameterSignature, isActionContractError, isActionHiddenFromEveryAgentSurface, isAgentActionStopError, isAgentConnectionRequiredError, actionCallIntent, ActionInputValidationError, invocationEffect, runPreparedActionCall, boundActionCallIntent, preparedCallArguments, preparedCallNeedsApproval, recordInvalidActionCall, stripUnsupportedSchemaKeywords, } from "../action.js";
|
|
12
|
+
import { AgentConnectionRequiredError, describeToolParameterSignature, isActionContractError, isActionHiddenFromEveryAgentSurface, isAgentActionStopError, isAgentConnectionRequiredError, actionCallIntent, ActionInputValidationError, invocationEffect, runPreparedActionCall, boundActionCallIntent, preparedCallArguments, preparedCallNeedsApproval, recordInvalidActionCall, resolveToolMaxResultChars, stripUnsupportedSchemaKeywords, } from "../action.js";
|
|
13
13
|
import { getAppConfig } from "../app-config/index.js";
|
|
14
14
|
import { MAX_BACKGROUND_RUN_CONTINUATIONS, MAX_CONSECUTIVE_NO_PROGRESS_CONTINUATIONS, MAX_TURN_WALL_CLOCK_MS, } from "../app-config/run-lifecycle-invariants.js";
|
|
15
15
|
import { readAppState } from "../application-state/script-helpers.js";
|
|
@@ -1239,6 +1239,7 @@ const VISIBLE_RETRY_THRESHOLD_MS = 10_000;
|
|
|
1239
1239
|
*/
|
|
1240
1240
|
const FOREGROUND_FIRST_MODEL_EVENT_TIMEOUT_MS = 25_000;
|
|
1241
1241
|
const EMPTY_FINAL_RESPONSE_RETRY_LIMIT = 2;
|
|
1242
|
+
const MAX_CONSECUTIVE_MAX_TOKENS_CONTINUATIONS = 3;
|
|
1242
1243
|
const TRUNCATED_TOOL_CALL_RETRY_LIMIT = 2;
|
|
1243
1244
|
const MAIN_CHAT_INTERNAL_CONTINUATION_LIMIT = 6;
|
|
1244
1245
|
const RUN_BUDGET_EXHAUSTED_ERROR_CODE = "run_budget_exhausted";
|
|
@@ -5151,6 +5152,8 @@ export async function runAgentLoop(opts) {
|
|
|
5151
5152
|
const blockedA2ATargets = new Map();
|
|
5152
5153
|
let finalGuardRetries = 0;
|
|
5153
5154
|
let emptyFinalResponseRetries = 0;
|
|
5155
|
+
let preserveReasoningEffortForEmptyResponseRetries = false;
|
|
5156
|
+
let consecutiveMaxTokensContinuations = 0;
|
|
5154
5157
|
let truncatedToolCallRetries = 0;
|
|
5155
5158
|
let iterations = 0;
|
|
5156
5159
|
let endedAtLoopLimit = false;
|
|
@@ -5888,21 +5891,42 @@ export async function runAgentLoop(opts) {
|
|
|
5888
5891
|
send({ type: "text", text });
|
|
5889
5892
|
};
|
|
5890
5893
|
if (toolCallParts.length === 0) {
|
|
5894
|
+
const hasEmptyFinalResponse = collectTextParts(assistantContentForHistory).trim().length === 0 &&
|
|
5895
|
+
streamedAssistantText.trim().length === 0;
|
|
5891
5896
|
if (followUpResults.length > 0)
|
|
5892
5897
|
messages.push({ role: "user", content: followUpResults });
|
|
5893
5898
|
if (terminalStopReason === "max_tokens") {
|
|
5894
|
-
|
|
5895
|
-
|
|
5896
|
-
|
|
5899
|
+
if (hasEmptyFinalResponse) {
|
|
5900
|
+
preserveReasoningEffortForEmptyResponseRetries = true;
|
|
5901
|
+
}
|
|
5902
|
+
else if (consecutiveMaxTokensContinuations <
|
|
5903
|
+
MAX_CONSECUTIVE_MAX_TOKENS_CONTINUATIONS) {
|
|
5904
|
+
consecutiveMaxTokensContinuations += 1;
|
|
5905
|
+
flushUnstreamedAssistantText();
|
|
5906
|
+
appendAgentLoopContinuation(messages, "max_tokens");
|
|
5907
|
+
continue;
|
|
5908
|
+
}
|
|
5909
|
+
else {
|
|
5910
|
+
flushUnstreamedAssistantText();
|
|
5911
|
+
terminalActionStop = {
|
|
5912
|
+
message: "This response kept reaching the output-token limit after several continuations and may be incomplete. Ask for a shorter or narrower answer.",
|
|
5913
|
+
errorCode: "max_tokens_continuation_limit",
|
|
5914
|
+
};
|
|
5915
|
+
sendTerminalActionStop(terminalActionStop);
|
|
5916
|
+
break;
|
|
5917
|
+
}
|
|
5918
|
+
}
|
|
5919
|
+
else {
|
|
5920
|
+
consecutiveMaxTokensContinuations = 0;
|
|
5897
5921
|
}
|
|
5898
|
-
const hasEmptyFinalResponse = collectTextParts(assistantContentForHistory).trim().length === 0 &&
|
|
5899
|
-
streamedAssistantText.trim().length === 0;
|
|
5900
5922
|
if (followUpResults.length > 0 && terminalStopReason === "tool_use") {
|
|
5901
5923
|
if (streamedAssistantText)
|
|
5902
5924
|
send({ type: "clear" });
|
|
5903
5925
|
continue;
|
|
5904
5926
|
}
|
|
5905
|
-
if (followUpResults.length > 0 &&
|
|
5927
|
+
if (followUpResults.length > 0 &&
|
|
5928
|
+
hasEmptyFinalResponse &&
|
|
5929
|
+
terminalStopReason !== "max_tokens") {
|
|
5906
5930
|
flushUnstreamedAssistantText();
|
|
5907
5931
|
continue;
|
|
5908
5932
|
}
|
|
@@ -5910,9 +5934,13 @@ export async function runAgentLoop(opts) {
|
|
|
5910
5934
|
completedFollowUpSuggestions = undefined;
|
|
5911
5935
|
if (emptyFinalResponseRetries < EMPTY_FINAL_RESPONSE_RETRY_LIMIT) {
|
|
5912
5936
|
emptyFinalResponseRetries += 1;
|
|
5937
|
+
const previousMaxOutputTokens = resolveMaxOutputTokensForEngine(engine.name, effectiveMaxOutputTokens, model);
|
|
5913
5938
|
effectiveMaxOutputTokens =
|
|
5914
5939
|
resolveEmptyResponseRetryMaxOutputTokens(model);
|
|
5915
|
-
|
|
5940
|
+
if (!preserveReasoningEffortForEmptyResponseRetries ||
|
|
5941
|
+
effectiveMaxOutputTokens <= previousMaxOutputTokens) {
|
|
5942
|
+
effectiveReasoningEffort = stepDownReasoningEffort(effectiveReasoningEffort);
|
|
5943
|
+
}
|
|
5916
5944
|
send({ type: "clear" });
|
|
5917
5945
|
appendAgentLoopContinuation(messages, "max_tokens");
|
|
5918
5946
|
continue;
|
|
@@ -6006,6 +6034,8 @@ export async function runAgentLoop(opts) {
|
|
|
6006
6034
|
}
|
|
6007
6035
|
}
|
|
6008
6036
|
emptyFinalResponseRetries = 0;
|
|
6037
|
+
preserveReasoningEffortForEmptyResponseRetries = false;
|
|
6038
|
+
consecutiveMaxTokensContinuations = 0;
|
|
6009
6039
|
effectiveMaxOutputTokens = opts.maxOutputTokens;
|
|
6010
6040
|
effectiveReasoningEffort = opts.reasoningEffort;
|
|
6011
6041
|
if (loopBreakerCloseout)
|
|
@@ -6014,6 +6044,8 @@ export async function runAgentLoop(opts) {
|
|
|
6014
6044
|
}
|
|
6015
6045
|
finalGuardRetries = 0;
|
|
6016
6046
|
emptyFinalResponseRetries = 0;
|
|
6047
|
+
preserveReasoningEffortForEmptyResponseRetries = false;
|
|
6048
|
+
consecutiveMaxTokensContinuations = 0;
|
|
6017
6049
|
const toolCallTruncatedByOutputCap = terminalStopReason === "max_tokens";
|
|
6018
6050
|
if (toolCallTruncatedByOutputCap) {
|
|
6019
6051
|
if (truncatedToolCallRetries < TRUNCATED_TOOL_CALL_RETRY_LIMIT) {
|
|
@@ -6600,14 +6632,8 @@ export async function runAgentLoop(opts) {
|
|
|
6600
6632
|
};
|
|
6601
6633
|
}
|
|
6602
6634
|
}
|
|
6603
|
-
const DEFAULT_TOOL_RESULT_CHARS = 50_000;
|
|
6604
6635
|
const toolTimeoutMs = toolTimeoutMsFor(actionEntry);
|
|
6605
|
-
const
|
|
6606
|
-
opts.toolLimits?.maxResultChars ??
|
|
6607
|
-
DEFAULT_TOOL_RESULT_CHARS;
|
|
6608
|
-
const toolMaxResultChars = typeof opts.toolLimits?.hardMaxResultChars === "number"
|
|
6609
|
-
? Math.min(configuredToolMaxResultChars, opts.toolLimits.hardMaxResultChars)
|
|
6610
|
-
: configuredToolMaxResultChars;
|
|
6636
|
+
const toolMaxResultChars = resolveToolMaxResultChars(actionEntry, opts.toolLimits);
|
|
6611
6637
|
const executionFence = currentExecutionFence();
|
|
6612
6638
|
const logicalExecutionStepId = logicalExecutionStepIds.get(originalToolCall);
|
|
6613
6639
|
const interruptedStep = executionFence && toolCallJournal
|
|
@@ -7201,6 +7227,7 @@ export async function runAgentLoop(opts) {
|
|
|
7201
7227
|
signal,
|
|
7202
7228
|
actionName: toolCall.name,
|
|
7203
7229
|
toolCallId: toolCall.id,
|
|
7230
|
+
maxResultChars: toolMaxResultChars,
|
|
7204
7231
|
...(wasApproved ? { approvedToolCallKey: approvalKey } : {}),
|
|
7205
7232
|
};
|
|
7206
7233
|
actionInvoked = true;
|
|
@@ -5,6 +5,7 @@ import { recordAgentRun } from "../observability/metrics.js";
|
|
|
5
5
|
import { parseServiceIdentityEmail } from "../org/service-identity.js";
|
|
6
6
|
import { ServicePrincipalRefusedError } from "../org/service-principal-guard.js";
|
|
7
7
|
import { captureError } from "../server/capture-error.js";
|
|
8
|
+
import { isNonProgressEventType } from "../shared/progress-events.js";
|
|
8
9
|
import { EXECUTION_LEASE_LOST_ABORT_REASON } from "./abort-reasons.js";
|
|
9
10
|
import { isLlmCredentialError, LLM_MISSING_CREDENTIALS_ERROR_CODE, LLM_MISSING_CREDENTIALS_MESSAGE, } from "./engine/credential-errors.js";
|
|
10
11
|
import { classifyTerminalErrorCode, describeErrorWithCauses, isProviderConnectionError, } from "./engine/error-detail.js";
|
|
@@ -86,6 +87,7 @@ export function resolveRunNoProgressTimeoutMs(params) {
|
|
|
86
87
|
return ceiling;
|
|
87
88
|
return override === 0 ? 0 : Math.min(override, ceiling);
|
|
88
89
|
}
|
|
90
|
+
const MAX_CONSECUTIVE_EMPTY_MODEL_CALLS = 3;
|
|
89
91
|
function inFlightWorkDelta(event) {
|
|
90
92
|
switch (event.type) {
|
|
91
93
|
case "tool_start":
|
|
@@ -577,6 +579,7 @@ export function startRun(runId, threadId, runFn, onComplete, options) {
|
|
|
577
579
|
chunkAbort = new AbortController();
|
|
578
580
|
armChunkSoftTimeout();
|
|
579
581
|
lastRealProgressAt = Date.now();
|
|
582
|
+
consecutiveEmptyModelCalls = 0;
|
|
580
583
|
return chunkAbort.signal;
|
|
581
584
|
},
|
|
582
585
|
};
|
|
@@ -888,8 +891,6 @@ export function startRun(runId, threadId, runFn, onComplete, options) {
|
|
|
888
891
|
scheduleProgressWrite();
|
|
889
892
|
};
|
|
890
893
|
const shouldBumpProgressForEvent = (event) => {
|
|
891
|
-
if (event.type === "stream_keepalive")
|
|
892
|
-
return false;
|
|
893
894
|
if (event.type === "clear") {
|
|
894
895
|
for (const [key, bytes] of preparingActivityBytes) {
|
|
895
896
|
const toolKey = preparingActivityTools.get(key) ?? key;
|
|
@@ -897,8 +898,9 @@ export function startRun(runId, threadId, runFn, onComplete, options) {
|
|
|
897
898
|
}
|
|
898
899
|
preparingActivityBytes.clear();
|
|
899
900
|
preparingActivityTools.clear();
|
|
900
|
-
return false;
|
|
901
901
|
}
|
|
902
|
+
if (isNonProgressEventType(event.type))
|
|
903
|
+
return false;
|
|
902
904
|
if (event.type === "activity" && isPreparingActionActivityEvent(event)) {
|
|
903
905
|
const toolKey = event.tool?.trim() || event.label.trim();
|
|
904
906
|
const activityKey = `${toolKey}:${event.id?.trim() || "no-id"}`;
|
|
@@ -954,6 +956,11 @@ export function startRun(runId, threadId, runFn, onComplete, options) {
|
|
|
954
956
|
return true;
|
|
955
957
|
};
|
|
956
958
|
let lastRealProgressAt = Date.now();
|
|
959
|
+
// Every completed model call refreshes the silence clock (its result is still
|
|
960
|
+
// being processed), so a loop of calls that produce no text, tool call or
|
|
961
|
+
// result is caught by counting consecutive empty calls instead.
|
|
962
|
+
let consecutiveEmptyModelCalls = 0;
|
|
963
|
+
let modelCallHadProgress = false;
|
|
957
964
|
let inFlightWorkCount = 0;
|
|
958
965
|
let inFlightMarkerSince = null;
|
|
959
966
|
let inFlightMarkerWrite = Promise.resolve();
|
|
@@ -1163,7 +1170,8 @@ export function startRun(runId, threadId, runFn, onComplete, options) {
|
|
|
1163
1170
|
if (inFlightWorkCount > 0)
|
|
1164
1171
|
return;
|
|
1165
1172
|
const silentForMs = Date.now() - lastRealProgressAt;
|
|
1166
|
-
|
|
1173
|
+
const emptyCallLoop = consecutiveEmptyModelCalls > MAX_CONSECUTIVE_EMPTY_MODEL_CALLS;
|
|
1174
|
+
if (!emptyCallLoop && silentForMs < noProgressTimeoutMs)
|
|
1167
1175
|
return;
|
|
1168
1176
|
const lastEventType = run.events.at(-1)?.event.type;
|
|
1169
1177
|
if (!chunkAbort) {
|
|
@@ -1410,8 +1418,20 @@ export function startRun(runId, threadId, runFn, onComplete, options) {
|
|
|
1410
1418
|
trackInFlightWork(runEvent.event);
|
|
1411
1419
|
if (shouldBumpProgressForEvent(runEvent.event)) {
|
|
1412
1420
|
lastRealProgressAt = Date.now();
|
|
1421
|
+
consecutiveEmptyModelCalls = 0;
|
|
1422
|
+
modelCallHadProgress = true;
|
|
1413
1423
|
bumpProgressIfDue();
|
|
1414
1424
|
}
|
|
1425
|
+
else if (runEvent.event.type === "model_stream") {
|
|
1426
|
+
if (runEvent.event.status === "start") {
|
|
1427
|
+
modelCallHadProgress = false;
|
|
1428
|
+
}
|
|
1429
|
+
else {
|
|
1430
|
+
lastRealProgressAt = Date.now();
|
|
1431
|
+
if (!modelCallHadProgress)
|
|
1432
|
+
consecutiveEmptyModelCalls++;
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1415
1435
|
};
|
|
1416
1436
|
const queueRunEvent = (pendingWrite, runEvent) => {
|
|
1417
1437
|
let resolve;
|
|
@@ -139,8 +139,8 @@ export declare function createAutomationCallbackHandler(runtime: AutomationRunti
|
|
|
139
139
|
duplicate?: undefined;
|
|
140
140
|
eventId?: undefined;
|
|
141
141
|
} | {
|
|
142
|
-
error?: undefined;
|
|
143
142
|
accepted: boolean;
|
|
144
143
|
duplicate: boolean;
|
|
145
144
|
eventId: string;
|
|
145
|
+
error?: undefined;
|
|
146
146
|
}>;
|
|
@@ -297,6 +297,13 @@ export type PreparingActionState = {
|
|
|
297
297
|
entries?: Map<string, PreparingActionEntry>;
|
|
298
298
|
toolEntries?: Map<string, PreparingActionEntry>;
|
|
299
299
|
};
|
|
300
|
+
/**
|
|
301
|
+
* The browser's durable liveness cursor must mirror the server's
|
|
302
|
+
* `last_progress_at` predicate. The stream watchdog intentionally has a
|
|
303
|
+
* broader background policy, so it cannot be reused for this cursor: raw
|
|
304
|
+
* keepalives and repeated zero-byte preparation are not proof of real work.
|
|
305
|
+
*/
|
|
306
|
+
export declare function isDurableProgressEvent(ev: SSEEvent, actionPreparationProgress?: boolean): boolean;
|
|
300
307
|
/**
|
|
301
308
|
* Locate the tool call an `approval_required` event refers to.
|
|
302
309
|
*
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { LLM_MISSING_CREDENTIALS_ERROR_CODE, LLM_MISSING_CREDENTIALS_MESSAGE, } from "../agent/engine/credential-errors.js";
|
|
2
2
|
import { BUILDER_GATEWAY_INTERNAL_ERROR_CODE, PROVIDER_TRANSIENT_REJECTION_ERROR_CODE, } from "../agent/engine/error-detail.js";
|
|
3
3
|
import { normalizeConnectRequiredResult } from "../shared/connect-required.js";
|
|
4
|
+
import { isNonProgressEventType } from "../shared/progress-events.js";
|
|
4
5
|
import { toolInputsMatch } from "../shared/tool-inputs.js";
|
|
5
6
|
import { emitChatFirstOpenApp } from "./chat-first-state.js";
|
|
6
7
|
import { formatChatErrorText, normalizeChatError } from "./error-format.js";
|
|
@@ -205,8 +206,8 @@ function isMeaningfulProgressEvent(ev, actionPreparationProgress, options) {
|
|
|
205
206
|
* broader background policy, so it cannot be reused for this cursor: raw
|
|
206
207
|
* keepalives and repeated zero-byte preparation are not proof of real work.
|
|
207
208
|
*/
|
|
208
|
-
function isDurableProgressEvent(ev, actionPreparationProgress) {
|
|
209
|
-
if (ev.type
|
|
209
|
+
export function isDurableProgressEvent(ev, actionPreparationProgress) {
|
|
210
|
+
if (isNonProgressEventType(ev.type))
|
|
210
211
|
return false;
|
|
211
212
|
if (ev.type === "activity" && isPreparingActionActivity(ev)) {
|
|
212
213
|
return actionPreparationProgress === true;
|
|
@@ -17,4 +17,12 @@ export declare function stopGoogleDocsWatchChannel(accessToken: string, channelI
|
|
|
17
17
|
export declare function processChanges(options: GoogleDocsPollerOptions): Promise<void>;
|
|
18
18
|
export declare function handlePushNotification(): Promise<void>;
|
|
19
19
|
export declare function startGoogleDocsPoller(options: GoogleDocsPollerOptions): Promise<void>;
|
|
20
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Stops polling and watch renewal, then retires the shared Drive watch.
|
|
22
|
+
* With `shuttingDown`, it instead waits for a poll in flight to finish and
|
|
23
|
+
* leaves the watch registered for the servers still running, or the next
|
|
24
|
+
* start, to renew or replace.
|
|
25
|
+
*/
|
|
26
|
+
export declare function stopGoogleDocsPoller(options?: {
|
|
27
|
+
shuttingDown?: boolean;
|
|
28
|
+
}): Promise<void>;
|
|
@@ -663,26 +663,37 @@ function startPollLoop(options, intervalMs) {
|
|
|
663
663
|
pollerStartTimer = null;
|
|
664
664
|
pollerJob = startIntervalJob(poll, { intervalMs });
|
|
665
665
|
}, 5000);
|
|
666
|
+
pollerStartTimer.unref?.();
|
|
666
667
|
const email = getServiceAccountEmail();
|
|
667
668
|
if (process.env.DEBUG) {
|
|
668
669
|
console.log(`[google-docs] Poll loop started (interval: ${intervalMs / 1000}s, service account: ${email ?? "not configured"})`);
|
|
669
670
|
}
|
|
670
671
|
}
|
|
671
|
-
|
|
672
|
+
/**
|
|
673
|
+
* Stops polling and watch renewal, then retires the shared Drive watch.
|
|
674
|
+
* With `shuttingDown`, it instead waits for a poll in flight to finish and
|
|
675
|
+
* leaves the watch registered for the servers still running, or the next
|
|
676
|
+
* start, to renew or replace.
|
|
677
|
+
*/
|
|
678
|
+
export async function stopGoogleDocsPoller(options = {}) {
|
|
672
679
|
pollerGeneration += 1;
|
|
673
680
|
activeOptions = null;
|
|
674
681
|
if (pollerStartTimer) {
|
|
675
682
|
clearTimeout(pollerStartTimer);
|
|
676
683
|
pollerStartTimer = null;
|
|
677
684
|
}
|
|
678
|
-
if (pollerJob) {
|
|
679
|
-
pollerJob.stop();
|
|
680
|
-
pollerJob = null;
|
|
681
|
-
}
|
|
682
685
|
if (watchRenewalTimer) {
|
|
683
686
|
clearTimeout(watchRenewalTimer);
|
|
684
687
|
watchRenewalTimer = null;
|
|
685
688
|
}
|
|
689
|
+
const running = pollerJob;
|
|
690
|
+
pollerJob = null;
|
|
691
|
+
if (options.shuttingDown) {
|
|
692
|
+
await running?.stop();
|
|
693
|
+
return;
|
|
694
|
+
}
|
|
695
|
+
// Disabling doesn't wait on a slow poll; the stop ends its scheduling.
|
|
696
|
+
void running?.stop();
|
|
686
697
|
try {
|
|
687
698
|
const stopResult = await stopWatch();
|
|
688
699
|
if (stopResult.status === "retry") {
|
|
@@ -14,4 +14,4 @@ export declare function retryStuckPendingTasks(input?: string | PendingTasksSwee
|
|
|
14
14
|
export declare function startPendingTasksRetryJob(options?: {
|
|
15
15
|
webhookBaseUrl?: string;
|
|
16
16
|
}): void;
|
|
17
|
-
export declare function stopPendingTasksRetryJob(): void
|
|
17
|
+
export declare function stopPendingTasksRetryJob(): Promise<void>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getDbExec } from "../db/client.js";
|
|
2
|
+
import { registerBackgroundJobStop } from "../server/background-jobs.js";
|
|
2
3
|
import { startIntervalJob, } from "../server/interval-job.js";
|
|
3
4
|
import { hasActiveIntegrationCampaign } from "./integration-campaigns-store.js";
|
|
4
5
|
import { configuredIntegrationDurableDispatchScopes, dispatchPendingIntegrationTask, isIntegrationDurableDispatchEnabledForTask, } from "./integration-durable-dispatch.js";
|
|
@@ -242,16 +243,19 @@ export function startPendingTasksRetryJob(options) {
|
|
|
242
243
|
});
|
|
243
244
|
}, 10_000);
|
|
244
245
|
startupTimer.unref?.();
|
|
246
|
+
registerBackgroundJobStop(stopPendingTasksRetryJob);
|
|
245
247
|
if (process.env.DEBUG) {
|
|
246
248
|
console.log(`[integrations] Pending-tasks retry job started (every ${RETRY_INTERVAL_MS / 1000}s)`);
|
|
247
249
|
}
|
|
248
250
|
}
|
|
249
|
-
export function stopPendingTasksRetryJob() {
|
|
251
|
+
export async function stopPendingTasksRetryJob() {
|
|
250
252
|
if (startupTimer) {
|
|
251
253
|
clearTimeout(startupTimer);
|
|
252
254
|
startupTimer = null;
|
|
253
255
|
}
|
|
254
|
-
job
|
|
256
|
+
const running = job;
|
|
255
257
|
job = null;
|
|
256
258
|
activeWebhookBaseUrl = undefined;
|
|
259
|
+
if (running)
|
|
260
|
+
await running.stop();
|
|
257
261
|
}
|
|
@@ -8,6 +8,7 @@ import { getOrgContext } from "../org/context.js";
|
|
|
8
8
|
import { loadResourcesForPrompt } from "../server/agent-chat-plugin.js";
|
|
9
9
|
import { withConfiguredAppBasePath } from "../server/app-base-path.js";
|
|
10
10
|
import { getSession } from "../server/auth.js";
|
|
11
|
+
import { registerBackgroundJobStop } from "../server/background-jobs.js";
|
|
11
12
|
import { FRAMEWORK_ROUTE_PREFIX } from "../server/core-routes-plugin.js";
|
|
12
13
|
import { resolveSecret } from "../server/credential-provider.js";
|
|
13
14
|
import { getH3App, markDefaultPluginProvided, } from "../server/framework-request-handler.js";
|
|
@@ -41,13 +42,13 @@ import { dispatchPendingIntegrationTask, INTEGRATION_CAMPAIGN_PROCESSOR_FIELD, I
|
|
|
41
42
|
import { forgetIntegrationMemory, integrationMemoryActions, listIntegrationMemory, rememberForIntegrationScope, } from "./integration-memory.js";
|
|
42
43
|
import { extractBearerToken, verifyInternalToken } from "./internal-token.js";
|
|
43
44
|
import { setMountedChannels, } from "./mounted-channels.js";
|
|
44
|
-
import { retryStuckPendingTasks, startPendingTasksRetryJob, } from "./pending-tasks-retry-job.js";
|
|
45
|
+
import { retryStuckPendingTasks, startPendingTasksRetryJob, stopPendingTasksRetryJob, } from "./pending-tasks-retry-job.js";
|
|
45
46
|
import { claimPendingTask, getNextPendingTaskForThread, getPendingTask, insertPendingTask, isDuplicateEventError, MAX_PENDING_TASK_ATTEMPTS, MAX_RECOVERABLE_PENDING_TASK_AGE_MS, markTaskCompleted, markTaskDeliveryRetryable, markTaskFailed, markTaskRetryable, stageTaskDeliveryPayload, } from "./pending-tasks-store.js";
|
|
46
47
|
import { claimNextComputerCommand, claimNextRemoteCommand, enqueueComputerCommand, enqueueRemoteCommand as enqueueRemoteCommandRow, isRemoteCommandKind, listRemoteCommandsForOwner, updateRemoteCommandResult, } from "./remote-commands-store.js";
|
|
47
48
|
import { authenticateRemoteDeviceToken, createRemoteDevice, getRemoteComputerCapabilities, getRemoteExecutionCapabilities, getRemoteDeviceForOwner, listRemoteDevicesForOwner, revokeRemoteDeviceForOwner, toPublicRemoteDevice, unregisterRemoteDevice, updateRemoteDeviceDetails, } from "./remote-devices-store.js";
|
|
48
|
-
import { startRemotePushDeliveryJob } from "./remote-push-delivery-job.js";
|
|
49
|
+
import { startRemotePushDeliveryJob, stopRemotePushDeliveryJob, } from "./remote-push-delivery-job.js";
|
|
49
50
|
import { listRemotePushNotificationsForOwner, listRemotePushRegistrationsForOwner, queueRemotePushNotifications, toPublicRemotePushRegistration, unregisterRemotePushRegistrationForOwner, upsertRemotePushRegistration, } from "./remote-push-store.js";
|
|
50
|
-
import { startRemoteCommandsRetryJob } from "./remote-retry-job.js";
|
|
51
|
+
import { startRemoteCommandsRetryJob, stopRemoteCommandsRetryJob, } from "./remote-retry-job.js";
|
|
51
52
|
import { insertRemoteRunEvents, listRemoteRunEvents, } from "./remote-run-events-store.js";
|
|
52
53
|
import { integrationRequestContext, resolveIntegrationRequestScope, } from "./request-scope.js";
|
|
53
54
|
import { listIntegrationScopes, saveIntegrationScope } from "./scope-store.js";
|
|
@@ -176,6 +177,17 @@ function startA2AContinuationRetryJob(adapters) {
|
|
|
176
177
|
});
|
|
177
178
|
}, 10_000);
|
|
178
179
|
a2aContinuationStartupTimer.unref?.();
|
|
180
|
+
registerBackgroundJobStop(stopA2AContinuationRetryJob);
|
|
181
|
+
}
|
|
182
|
+
async function stopA2AContinuationRetryJob() {
|
|
183
|
+
if (a2aContinuationStartupTimer) {
|
|
184
|
+
clearTimeout(a2aContinuationStartupTimer);
|
|
185
|
+
a2aContinuationStartupTimer = null;
|
|
186
|
+
}
|
|
187
|
+
const running = a2aContinuationJob;
|
|
188
|
+
a2aContinuationJob = null;
|
|
189
|
+
if (running)
|
|
190
|
+
await running.stop();
|
|
179
191
|
}
|
|
180
192
|
export const BUILT_IN_INTEGRATION_ADAPTER_FACTORIES = Object.freeze([
|
|
181
193
|
{ platform: "slack", create: slackAdapter },
|
|
@@ -2749,8 +2761,10 @@ export function createIntegrationsPlugin(options) {
|
|
|
2749
2761
|
startA2AContinuationRetryJob(adapterMap);
|
|
2750
2762
|
startRemoteCommandsRetryJob();
|
|
2751
2763
|
startRemotePushDeliveryJob();
|
|
2764
|
+
let googleDocsStartTimer = null;
|
|
2752
2765
|
if (adapterMap.has("google-docs")) {
|
|
2753
|
-
setTimeout(() => {
|
|
2766
|
+
googleDocsStartTimer = setTimeout(() => {
|
|
2767
|
+
googleDocsStartTimer = null;
|
|
2754
2768
|
void runGoogleDocsPollerTransition(async () => {
|
|
2755
2769
|
const config = await getIntegrationConfig("google-docs");
|
|
2756
2770
|
const ownerEmail = config?.owner?.trim();
|
|
@@ -2763,7 +2777,24 @@ export function createIntegrationsPlugin(options) {
|
|
|
2763
2777
|
await startGoogleDocsPoller(createGoogleDocsPollerOptions(ownerEmail));
|
|
2764
2778
|
});
|
|
2765
2779
|
}, 2000);
|
|
2766
|
-
|
|
2780
|
+
googleDocsStartTimer.unref?.();
|
|
2781
|
+
}
|
|
2782
|
+
const stopIntegrationBackgroundJobs = async () => {
|
|
2783
|
+
await Promise.all([
|
|
2784
|
+
stopPendingTasksRetryJob(),
|
|
2785
|
+
stopA2AContinuationRetryJob(),
|
|
2786
|
+
stopRemoteCommandsRetryJob(),
|
|
2787
|
+
stopRemotePushDeliveryJob(),
|
|
2788
|
+
]);
|
|
2789
|
+
if (googleDocsStartTimer) {
|
|
2790
|
+
clearTimeout(googleDocsStartTimer);
|
|
2791
|
+
googleDocsStartTimer = null;
|
|
2792
|
+
}
|
|
2793
|
+
if (adapterMap.has("google-docs")) {
|
|
2794
|
+
await runGoogleDocsPollerTransition(() => stopGoogleDocsPoller({ shuttingDown: true }));
|
|
2795
|
+
}
|
|
2796
|
+
};
|
|
2797
|
+
registerBackgroundJobStop(stopIntegrationBackgroundJobs);
|
|
2767
2798
|
}
|
|
2768
2799
|
if (process.env.DEBUG)
|
|
2769
2800
|
console.log(`[integrations] Mounted integration routes for: ${adapters.map((a) => a.platform).join(", ")}`);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { registerBackgroundJobStop } from "../server/background-jobs.js";
|
|
1
2
|
import { startIntervalJob, } from "../server/interval-job.js";
|
|
2
3
|
import { deliverPendingRemotePushNotifications } from "./remote-push-delivery.js";
|
|
3
4
|
const DELIVERY_INTERVAL_MS = 60_000;
|
|
@@ -23,12 +24,15 @@ export function startRemotePushDeliveryJob() {
|
|
|
23
24
|
});
|
|
24
25
|
}, 10_000);
|
|
25
26
|
startupTimer.unref?.();
|
|
27
|
+
registerBackgroundJobStop(stopRemotePushDeliveryJob);
|
|
26
28
|
}
|
|
27
|
-
export function stopRemotePushDeliveryJob() {
|
|
29
|
+
export async function stopRemotePushDeliveryJob() {
|
|
28
30
|
if (startupTimer) {
|
|
29
31
|
clearTimeout(startupTimer);
|
|
30
32
|
startupTimer = null;
|
|
31
33
|
}
|
|
32
|
-
job
|
|
34
|
+
const running = job;
|
|
33
35
|
job = null;
|
|
36
|
+
if (running)
|
|
37
|
+
await running.stop();
|
|
34
38
|
}
|
|
@@ -3,4 +3,4 @@ export declare function retryRemoteCommands(): Promise<{
|
|
|
3
3
|
failed: number;
|
|
4
4
|
}>;
|
|
5
5
|
export declare function startRemoteCommandsRetryJob(): void;
|
|
6
|
-
export declare function stopRemoteCommandsRetryJob(): void
|
|
6
|
+
export declare function stopRemoteCommandsRetryJob(): Promise<void>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { registerBackgroundJobStop } from "../server/background-jobs.js";
|
|
1
2
|
import { startIntervalJob, } from "../server/interval-job.js";
|
|
2
3
|
import { retryStaleRemoteCommands } from "./remote-commands-store.js";
|
|
3
4
|
const RETRY_INTERVAL_MS = 60_000;
|
|
@@ -29,12 +30,15 @@ export function startRemoteCommandsRetryJob() {
|
|
|
29
30
|
});
|
|
30
31
|
}, 10_000);
|
|
31
32
|
startupTimer.unref?.();
|
|
33
|
+
registerBackgroundJobStop(stopRemoteCommandsRetryJob);
|
|
32
34
|
}
|
|
33
|
-
export function stopRemoteCommandsRetryJob() {
|
|
35
|
+
export async function stopRemoteCommandsRetryJob() {
|
|
34
36
|
if (startupTimer) {
|
|
35
37
|
clearTimeout(startupTimer);
|
|
36
38
|
startupTimer = null;
|
|
37
39
|
}
|
|
38
|
-
job
|
|
40
|
+
const running = job;
|
|
39
41
|
job = null;
|
|
42
|
+
if (running)
|
|
43
|
+
await running.stop();
|
|
40
44
|
}
|
|
@@ -13,10 +13,10 @@ declare const _default: import("../../action.js").ActionDefinition<{
|
|
|
13
13
|
enabled?: undefined;
|
|
14
14
|
nextRun?: undefined;
|
|
15
15
|
} | {
|
|
16
|
-
deleted?: undefined;
|
|
17
16
|
resourceId: string;
|
|
18
17
|
name: string;
|
|
19
18
|
enabled: boolean;
|
|
20
19
|
nextRun: string;
|
|
20
|
+
deleted?: undefined;
|
|
21
21
|
}>;
|
|
22
22
|
export default _default;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export declare function createNotificationsHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<"" | import("./types.js").Notification[] | {
|
|
2
2
|
count: number;
|
|
3
|
-
updated?: undefined;
|
|
4
3
|
error?: undefined;
|
|
5
4
|
ok?: undefined;
|
|
5
|
+
updated?: undefined;
|
|
6
6
|
} | {
|
|
7
7
|
count?: undefined;
|
|
8
8
|
updated: number;
|
|
@@ -10,12 +10,12 @@ export declare function createNotificationsHandler(): import("h3").EventHandlerW
|
|
|
10
10
|
ok?: undefined;
|
|
11
11
|
} | {
|
|
12
12
|
count?: undefined;
|
|
13
|
-
updated?: undefined;
|
|
14
13
|
error: string;
|
|
15
14
|
ok?: undefined;
|
|
15
|
+
updated?: undefined;
|
|
16
16
|
} | {
|
|
17
17
|
count?: undefined;
|
|
18
|
-
updated?: undefined;
|
|
19
|
-
ok: boolean;
|
|
20
18
|
error?: undefined;
|
|
19
|
+
ok: boolean;
|
|
20
|
+
updated?: undefined;
|
|
21
21
|
}>>;
|
|
@@ -27,43 +27,43 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
|
|
|
27
27
|
thumbsUpRate: number;
|
|
28
28
|
avgEvalScore: number;
|
|
29
29
|
} | {
|
|
30
|
+
ok?: undefined;
|
|
30
31
|
error: string;
|
|
31
32
|
message: string;
|
|
32
33
|
summary?: undefined;
|
|
33
34
|
spans?: undefined;
|
|
34
|
-
ok?: undefined;
|
|
35
35
|
} | {
|
|
36
|
+
error?: undefined;
|
|
37
|
+
ok?: undefined;
|
|
36
38
|
message?: undefined;
|
|
37
39
|
summary: import("./types.js").TraceSummary;
|
|
38
40
|
spans: import("./types.js").TraceSpan[];
|
|
41
|
+
} | {
|
|
39
42
|
error?: undefined;
|
|
40
43
|
ok?: undefined;
|
|
41
|
-
} | {
|
|
42
44
|
message?: undefined;
|
|
43
45
|
summary?: undefined;
|
|
44
46
|
spans?: undefined;
|
|
45
47
|
traceMissing: true;
|
|
46
48
|
id: string;
|
|
49
|
+
} | {
|
|
47
50
|
error?: undefined;
|
|
48
51
|
ok?: undefined;
|
|
49
|
-
} | {
|
|
50
52
|
message?: undefined;
|
|
51
53
|
summary?: undefined;
|
|
52
54
|
spans?: undefined;
|
|
53
55
|
traceMissing?: undefined;
|
|
54
56
|
id: string;
|
|
55
|
-
error?: undefined;
|
|
56
|
-
ok?: undefined;
|
|
57
57
|
} | {
|
|
58
|
+
ok?: undefined;
|
|
58
59
|
message?: undefined;
|
|
59
60
|
summary?: undefined;
|
|
60
61
|
spans?: undefined;
|
|
61
62
|
error: any;
|
|
62
|
-
ok?: undefined;
|
|
63
63
|
} | {
|
|
64
|
+
error?: undefined;
|
|
64
65
|
message?: undefined;
|
|
65
66
|
summary?: undefined;
|
|
66
67
|
spans?: undefined;
|
|
67
|
-
error?: undefined;
|
|
68
68
|
ok: boolean;
|
|
69
69
|
}>>;
|