@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/org/handlers.d.ts
CHANGED
|
@@ -73,9 +73,9 @@ export declare const setWorkspaceAppDefaultVisibilityHandler: import("h3").Event
|
|
|
73
73
|
visibility: WorkspaceAppDefaultVisibility;
|
|
74
74
|
}>>;
|
|
75
75
|
export declare const createOrgHandler: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
76
|
+
name?: undefined;
|
|
76
77
|
success: boolean;
|
|
77
78
|
id?: undefined;
|
|
78
|
-
name?: undefined;
|
|
79
79
|
role?: undefined;
|
|
80
80
|
} | {
|
|
81
81
|
success?: undefined;
|
|
@@ -75,7 +75,6 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
|
|
|
75
75
|
user: "user";
|
|
76
76
|
}>>;
|
|
77
77
|
}, z.core.$strip>>, {
|
|
78
|
-
message?: undefined;
|
|
79
78
|
id?: undefined;
|
|
80
79
|
deleted?: undefined;
|
|
81
80
|
provider?: undefined;
|
|
@@ -92,8 +91,8 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
|
|
|
92
91
|
found?: undefined;
|
|
93
92
|
registered?: undefined;
|
|
94
93
|
label?: undefined;
|
|
95
|
-
} | {
|
|
96
94
|
message?: undefined;
|
|
95
|
+
} | {
|
|
97
96
|
id?: undefined;
|
|
98
97
|
deleted?: undefined;
|
|
99
98
|
count?: undefined;
|
|
@@ -102,8 +101,8 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
|
|
|
102
101
|
provider: import("../custom-registry.js").CustomProviderConfig;
|
|
103
102
|
registered?: undefined;
|
|
104
103
|
label?: undefined;
|
|
105
|
-
} | {
|
|
106
104
|
message?: undefined;
|
|
105
|
+
} | {
|
|
107
106
|
deleted?: undefined;
|
|
108
107
|
provider?: undefined;
|
|
109
108
|
count?: undefined;
|
|
@@ -112,8 +111,8 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
|
|
|
112
111
|
id: string;
|
|
113
112
|
registered?: undefined;
|
|
114
113
|
label?: undefined;
|
|
115
|
-
} | {
|
|
116
114
|
message?: undefined;
|
|
115
|
+
} | {
|
|
117
116
|
provider?: undefined;
|
|
118
117
|
count?: undefined;
|
|
119
118
|
providers?: undefined;
|
|
@@ -122,6 +121,7 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
|
|
|
122
121
|
id: string;
|
|
123
122
|
registered?: undefined;
|
|
124
123
|
label?: undefined;
|
|
124
|
+
message?: undefined;
|
|
125
125
|
} | {
|
|
126
126
|
deleted?: undefined;
|
|
127
127
|
provider?: undefined;
|
|
@@ -316,9 +316,9 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
|
|
|
316
316
|
notes?: string | undefined;
|
|
317
317
|
scope?: "org" | "user" | undefined;
|
|
318
318
|
}, {
|
|
319
|
+
message?: undefined;
|
|
319
320
|
provider?: undefined;
|
|
320
321
|
id?: undefined;
|
|
321
|
-
message?: undefined;
|
|
322
322
|
deleted?: undefined;
|
|
323
323
|
providers: {
|
|
324
324
|
id: string;
|
|
@@ -334,9 +334,9 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
|
|
|
334
334
|
registered?: undefined;
|
|
335
335
|
label?: undefined;
|
|
336
336
|
} | {
|
|
337
|
+
message?: undefined;
|
|
337
338
|
id?: undefined;
|
|
338
339
|
count?: undefined;
|
|
339
|
-
message?: undefined;
|
|
340
340
|
deleted?: undefined;
|
|
341
341
|
providers?: undefined;
|
|
342
342
|
found: boolean;
|
|
@@ -344,9 +344,9 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
|
|
|
344
344
|
registered?: undefined;
|
|
345
345
|
label?: undefined;
|
|
346
346
|
} | {
|
|
347
|
+
message?: undefined;
|
|
347
348
|
provider?: undefined;
|
|
348
349
|
count?: undefined;
|
|
349
|
-
message?: undefined;
|
|
350
350
|
deleted?: undefined;
|
|
351
351
|
providers?: undefined;
|
|
352
352
|
found: boolean;
|
|
@@ -354,9 +354,9 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
|
|
|
354
354
|
registered?: undefined;
|
|
355
355
|
label?: undefined;
|
|
356
356
|
} | {
|
|
357
|
+
message?: undefined;
|
|
357
358
|
provider?: undefined;
|
|
358
359
|
count?: undefined;
|
|
359
|
-
message?: undefined;
|
|
360
360
|
providers?: undefined;
|
|
361
361
|
found?: undefined;
|
|
362
362
|
deleted: boolean;
|
|
@@ -3,6 +3,7 @@ import { z } from "zod";
|
|
|
3
3
|
import { defineAction } from "../action.js";
|
|
4
4
|
import { getCredentialContext } from "../server/request-context.js";
|
|
5
5
|
import { appendProviderCorpusJobHits, createProviderCorpusJob, deleteProviderCorpusJob, getProviderCorpusJob, getProviderCorpusJobHits, listProviderCorpusJobs, updateProviderCorpusJob, } from "./corpus-jobs-store.js";
|
|
6
|
+
import { setBodyValueAtPath } from "./request-body.js";
|
|
6
7
|
import { getStagedDatasetMeta, getStagedDatasetRows, stagedDatasetVersion, } from "./staged-datasets-store.js";
|
|
7
8
|
const MethodSchema = z.enum(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"]);
|
|
8
9
|
const ProviderRequestSchema = z.object({
|
|
@@ -745,7 +746,7 @@ function buildPaginatedRequest(request, pagination, state) {
|
|
|
745
746
|
query = { ...query, [pagination.cursorParam]: state.cursor };
|
|
746
747
|
}
|
|
747
748
|
if (pagination.cursorBodyPath) {
|
|
748
|
-
body =
|
|
749
|
+
body = setBodyValueAtPath(body, pagination.cursorBodyPath, state.cursor);
|
|
749
750
|
}
|
|
750
751
|
}
|
|
751
752
|
return { ...request, query, body };
|
|
@@ -758,7 +759,7 @@ function buildBatchRequest(request, batch, values) {
|
|
|
758
759
|
query = { ...query, [batch.itemQueryParam]: payload };
|
|
759
760
|
}
|
|
760
761
|
if (batch.itemBodyPath) {
|
|
761
|
-
body =
|
|
762
|
+
body = setBodyValueAtPath(body, batch.itemBodyPath, values);
|
|
762
763
|
}
|
|
763
764
|
return { ...request, query, body };
|
|
764
765
|
}
|
|
@@ -1114,25 +1115,6 @@ function getAtPath(value, path) {
|
|
|
1114
1115
|
}
|
|
1115
1116
|
return current;
|
|
1116
1117
|
}
|
|
1117
|
-
function setAtPath(base, path, value) {
|
|
1118
|
-
const parts = pathParts(path);
|
|
1119
|
-
if (!parts.length)
|
|
1120
|
-
return value;
|
|
1121
|
-
const root = base && typeof base === "object" && !Array.isArray(base)
|
|
1122
|
-
? JSON.parse(JSON.stringify(base))
|
|
1123
|
-
: {};
|
|
1124
|
-
let current = root;
|
|
1125
|
-
for (const part of parts.slice(0, -1)) {
|
|
1126
|
-
const existing = current[part];
|
|
1127
|
-
const next = existing && typeof existing === "object" && !Array.isArray(existing)
|
|
1128
|
-
? { ...existing }
|
|
1129
|
-
: {};
|
|
1130
|
-
current[part] = next;
|
|
1131
|
-
current = next;
|
|
1132
|
-
}
|
|
1133
|
-
current[parts[parts.length - 1]] = value;
|
|
1134
|
-
return root;
|
|
1135
|
-
}
|
|
1136
1118
|
function collectSearchStrings(item, textPaths, maxFields) {
|
|
1137
1119
|
const paths = (textPaths ?? []).filter((path) => path.trim());
|
|
1138
1120
|
if (!paths.length)
|
|
@@ -14,6 +14,7 @@ import { resolveWorkspaceConnectionCredentialForApp } from "../workspace-connect
|
|
|
14
14
|
import { resolveWorkspaceConnectionForApp, } from "../workspace-connections/store.js";
|
|
15
15
|
import { createProviderQuotaIdentity, createProviderRequestDedupeKey, executeWithProviderQuota, } from "./quota-governor.js";
|
|
16
16
|
export { upsertCustomProvider, deleteCustomProvider, listCustomProviders, getCustomProvider, validateCustomBaseUrl, assertCanMutateCustomProviderScope, CustomProviderAuthError, } from "./custom-registry.js";
|
|
17
|
+
import { setBodyValueAtPath } from "./request-body.js";
|
|
17
18
|
export const PROVIDER_API_IDS = [
|
|
18
19
|
"amplitude",
|
|
19
20
|
"apollo",
|
|
@@ -1583,7 +1584,7 @@ export async function executeProviderApiRequest(args, runtime) {
|
|
|
1583
1584
|
query: queryWithCursor,
|
|
1584
1585
|
});
|
|
1585
1586
|
const bodyWithCursor = extra?.bodyCursor
|
|
1586
|
-
?
|
|
1587
|
+
? setBodyValueAtPath(substituteUnknown(args.body, placeholders), extra.bodyCursor.path, extra.bodyCursor.value)
|
|
1587
1588
|
: substituteUnknown(args.body, placeholders);
|
|
1588
1589
|
const pageBody = prepareBody(bodyWithCursor, headers);
|
|
1589
1590
|
const requestKey = createProviderRequestDedupeKey({
|
|
@@ -2371,7 +2372,7 @@ async function executeCustomProviderApiRequest(args, customConfig, runtime) {
|
|
|
2371
2372
|
query: queryWithCursor,
|
|
2372
2373
|
});
|
|
2373
2374
|
const bodyWithCursor = extra?.bodyCursor
|
|
2374
|
-
?
|
|
2375
|
+
? setBodyValueAtPath(args.body, extra.bodyCursor.path, extra.bodyCursor.value)
|
|
2375
2376
|
: args.body;
|
|
2376
2377
|
const pageBody = prepareBody(bodyWithCursor, headers);
|
|
2377
2378
|
const requestKey = createProviderRequestDedupeKey({
|
|
@@ -4594,9 +4595,21 @@ function sanitizeOutboundHeaders(value) {
|
|
|
4594
4595
|
function prepareBody(body, headers) {
|
|
4595
4596
|
if (body === undefined || body === null)
|
|
4596
4597
|
return undefined;
|
|
4597
|
-
if (typeof body === "string")
|
|
4598
|
-
return body;
|
|
4599
4598
|
const hasContentType = Object.keys(headers).some((name) => name.toLowerCase() === "content-type");
|
|
4599
|
+
if (typeof body === "string") {
|
|
4600
|
+
let isJson = false;
|
|
4601
|
+
try {
|
|
4602
|
+
JSON.parse(body);
|
|
4603
|
+
isJson = true;
|
|
4604
|
+
}
|
|
4605
|
+
catch {
|
|
4606
|
+
// coercion-ok: JSON.parse is a probe; a throw means plain text, which keeps its existing content type.
|
|
4607
|
+
}
|
|
4608
|
+
if (isJson && !hasContentType) {
|
|
4609
|
+
headers["Content-Type"] = "application/json";
|
|
4610
|
+
}
|
|
4611
|
+
return body;
|
|
4612
|
+
}
|
|
4600
4613
|
if (!hasContentType)
|
|
4601
4614
|
headers["Content-Type"] = "application/json";
|
|
4602
4615
|
return JSON.stringify(body);
|
|
@@ -4919,25 +4932,6 @@ function mergeQueryObjects(base, extra) {
|
|
|
4919
4932
|
}
|
|
4920
4933
|
return extra;
|
|
4921
4934
|
}
|
|
4922
|
-
function setValueAtPath(base, path, value) {
|
|
4923
|
-
const root = base && typeof base === "object" && !Array.isArray(base)
|
|
4924
|
-
? { ...base }
|
|
4925
|
-
: {};
|
|
4926
|
-
const parts = path.split(".").filter(Boolean);
|
|
4927
|
-
if (!parts.length)
|
|
4928
|
-
return root;
|
|
4929
|
-
let current = root;
|
|
4930
|
-
for (const part of parts.slice(0, -1)) {
|
|
4931
|
-
const existing = current[part];
|
|
4932
|
-
const next = existing && typeof existing === "object" && !Array.isArray(existing)
|
|
4933
|
-
? { ...existing }
|
|
4934
|
-
: {};
|
|
4935
|
-
current[part] = next;
|
|
4936
|
-
current = next;
|
|
4937
|
-
}
|
|
4938
|
-
current[parts[parts.length - 1]] = value;
|
|
4939
|
-
return root;
|
|
4940
|
-
}
|
|
4941
4935
|
function clampTimeout(timeoutMs) {
|
|
4942
4936
|
if (!Number.isFinite(timeoutMs))
|
|
4943
4937
|
return DEFAULT_TIMEOUT_MS;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function setBodyValueAtPath(base: unknown, path: string, value: unknown): unknown;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
const JSON_OBJECT_BODY_REQUIRED = "Body cursor pagination requires a JSON object request body.";
|
|
2
|
+
// The one place a pagination cursor or batch is written into a request body.
|
|
3
|
+
// A JSON-string body keeps its other fields and is re-serialized as a string.
|
|
4
|
+
export function setBodyValueAtPath(base, path, value) {
|
|
5
|
+
const wasString = typeof base === "string";
|
|
6
|
+
let parsed = base;
|
|
7
|
+
// A blank string body is an empty body.
|
|
8
|
+
if (typeof parsed === "string" && parsed.trim() === "")
|
|
9
|
+
parsed = undefined;
|
|
10
|
+
if (typeof parsed === "string") {
|
|
11
|
+
try {
|
|
12
|
+
parsed = JSON.parse(parsed);
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
throw new Error(JSON_OBJECT_BODY_REQUIRED);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
if (parsed !== undefined &&
|
|
19
|
+
parsed !== null &&
|
|
20
|
+
(typeof parsed !== "object" || Array.isArray(parsed))) {
|
|
21
|
+
throw new Error(JSON_OBJECT_BODY_REQUIRED);
|
|
22
|
+
}
|
|
23
|
+
const root = parsed
|
|
24
|
+
? { ...parsed }
|
|
25
|
+
: {};
|
|
26
|
+
// `ids[0]` and `ids.0` are the same path.
|
|
27
|
+
const parts = path
|
|
28
|
+
.replace(/\[(\d+)\]/g, ".$1")
|
|
29
|
+
.split(".")
|
|
30
|
+
.map((part) => part.trim())
|
|
31
|
+
.filter(Boolean);
|
|
32
|
+
const isIndex = (part) => part !== undefined && /^\d+$/.test(part);
|
|
33
|
+
let current = root;
|
|
34
|
+
parts.slice(0, -1).forEach((part, index) => {
|
|
35
|
+
const existing = current[part];
|
|
36
|
+
const indexNext = isIndex(parts[index + 1]);
|
|
37
|
+
const next = Array.isArray(existing) && indexNext
|
|
38
|
+
? [...existing]
|
|
39
|
+
: existing && typeof existing === "object" && !Array.isArray(existing)
|
|
40
|
+
? { ...existing }
|
|
41
|
+
: indexNext
|
|
42
|
+
? []
|
|
43
|
+
: {};
|
|
44
|
+
current[part] = next;
|
|
45
|
+
current = next;
|
|
46
|
+
});
|
|
47
|
+
if (parts.length)
|
|
48
|
+
current[parts[parts.length - 1]] = value;
|
|
49
|
+
return wasString ? JSON.stringify(root) : root;
|
|
50
|
+
}
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
* here; redaction is applied to the response returned to the model.
|
|
25
25
|
*/
|
|
26
26
|
import { fail } from "../action.js";
|
|
27
|
+
import { setBodyValueAtPath } from "./request-body.js";
|
|
27
28
|
import { rejectFailedProviderResult } from "./result-outcome.js";
|
|
28
29
|
import { upsertStagedDataset, deriveColumns, legacyStagedDatasetId, stagedDatasetId, MAX_ROWS_PER_OWNER, } from "./staged-datasets-store.js";
|
|
29
30
|
function getAtPath(obj, path) {
|
|
@@ -64,25 +65,6 @@ function extractNextCursor(body, path) {
|
|
|
64
65
|
return null;
|
|
65
66
|
return String(val);
|
|
66
67
|
}
|
|
67
|
-
function setAtPath(base, path, value) {
|
|
68
|
-
const root = base && typeof base === "object" && !Array.isArray(base)
|
|
69
|
-
? { ...base }
|
|
70
|
-
: {};
|
|
71
|
-
const parts = path.split(".").filter(Boolean);
|
|
72
|
-
if (!parts.length)
|
|
73
|
-
return root;
|
|
74
|
-
let current = root;
|
|
75
|
-
for (const part of parts.slice(0, -1)) {
|
|
76
|
-
const existing = current[part];
|
|
77
|
-
const next = existing && typeof existing === "object" && !Array.isArray(existing)
|
|
78
|
-
? { ...existing }
|
|
79
|
-
: {};
|
|
80
|
-
current[part] = next;
|
|
81
|
-
current = next;
|
|
82
|
-
}
|
|
83
|
-
current[parts[parts.length - 1]] = value;
|
|
84
|
-
return root;
|
|
85
|
-
}
|
|
86
68
|
async function sleepMs(ms) {
|
|
87
69
|
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
88
70
|
}
|
|
@@ -153,7 +135,7 @@ export async function stagingExecuteRequest(args, execute, ctx) {
|
|
|
153
135
|
extraQuery[pagination.cursorParam] = lastCursor;
|
|
154
136
|
}
|
|
155
137
|
if (pagination.cursorBodyPath && lastCursor !== null) {
|
|
156
|
-
nextBody =
|
|
138
|
+
nextBody = setBodyValueAtPath(baseArgs.body, pagination.cursorBodyPath, lastCursor);
|
|
157
139
|
}
|
|
158
140
|
const hasCursorMode = Boolean(pagination.cursorParam) || Boolean(pagination.cursorBodyPath);
|
|
159
141
|
if (pagination.pageParam && !hasCursorMode) {
|
package/dist/secrets/routes.d.ts
CHANGED
|
@@ -57,39 +57,39 @@ export declare function createWriteSecretHandler(): import("h3").EventHandlerWit
|
|
|
57
57
|
} | {
|
|
58
58
|
error: string;
|
|
59
59
|
errorCode?: undefined;
|
|
60
|
-
ok?: undefined;
|
|
61
60
|
status?: undefined;
|
|
61
|
+
ok?: undefined;
|
|
62
62
|
} | {
|
|
63
63
|
error: string;
|
|
64
64
|
errorCode: string;
|
|
65
|
-
ok?: undefined;
|
|
66
65
|
status?: undefined;
|
|
66
|
+
ok?: undefined;
|
|
67
67
|
} | {
|
|
68
|
-
error?: undefined;
|
|
69
68
|
errorCode?: undefined;
|
|
70
69
|
ok: boolean;
|
|
71
70
|
status: string;
|
|
71
|
+
error?: undefined;
|
|
72
72
|
} | {
|
|
73
|
-
ok?: undefined;
|
|
74
73
|
error: string;
|
|
75
74
|
removed?: undefined;
|
|
75
|
+
ok?: undefined;
|
|
76
76
|
} | {
|
|
77
|
-
error?: undefined;
|
|
78
77
|
ok: boolean;
|
|
79
78
|
removed: boolean;
|
|
79
|
+
error?: undefined;
|
|
80
80
|
}>>;
|
|
81
81
|
export declare function createTestSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
82
|
-
ok?: undefined;
|
|
83
82
|
error: string;
|
|
84
83
|
note?: undefined;
|
|
84
|
+
ok?: undefined;
|
|
85
85
|
} | {
|
|
86
|
-
error?: undefined;
|
|
87
86
|
ok: boolean;
|
|
88
87
|
note?: undefined;
|
|
89
|
-
} | {
|
|
90
88
|
error?: undefined;
|
|
89
|
+
} | {
|
|
91
90
|
ok: boolean;
|
|
92
91
|
note: string;
|
|
92
|
+
error?: undefined;
|
|
93
93
|
} | {
|
|
94
94
|
note?: undefined;
|
|
95
95
|
ok: boolean;
|
|
@@ -117,11 +117,11 @@ export declare function createAdHocSecretHandler(): import("h3").EventHandlerWit
|
|
|
117
117
|
} | {
|
|
118
118
|
error: string;
|
|
119
119
|
} | {
|
|
120
|
-
error?: undefined;
|
|
121
120
|
ok: boolean;
|
|
122
121
|
key: string;
|
|
123
|
-
} | {
|
|
124
122
|
error?: undefined;
|
|
123
|
+
} | {
|
|
125
124
|
ok: boolean;
|
|
126
125
|
removed: boolean;
|
|
126
|
+
error?: undefined;
|
|
127
127
|
}>>;
|
|
@@ -8,8 +8,8 @@ export declare const AGENT_ENGINE_API_KEY_ACTION_NAME = "agent-engine-api-key";
|
|
|
8
8
|
* resolveEngine's credential detection.
|
|
9
9
|
*/
|
|
10
10
|
export declare function createAgentEngineDisconnectHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
11
|
-
error: string;
|
|
12
11
|
ok?: undefined;
|
|
12
|
+
error: string;
|
|
13
13
|
scope?: undefined;
|
|
14
14
|
} | {
|
|
15
15
|
ok: boolean;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/** How long shutdown waits for jobs to stop before it carries on without them. */
|
|
2
|
+
const STOP_TIMEOUT_MS = 5_000;
|
|
3
|
+
const stoppers = new Set();
|
|
4
|
+
export function registerBackgroundJobStop(stop) {
|
|
5
|
+
stoppers.add(stop);
|
|
6
|
+
}
|
|
7
|
+
export async function stopBackgroundJobs(timeoutMs = STOP_TIMEOUT_MS) {
|
|
8
|
+
const toStop = [...stoppers];
|
|
9
|
+
stoppers.clear();
|
|
10
|
+
const pending = new Set(toStop);
|
|
11
|
+
const stopped = Promise.all(toStop.map(async (stop) => {
|
|
12
|
+
try {
|
|
13
|
+
await stop();
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
console.error("[agent-native] background job stop failed:", error);
|
|
17
|
+
}
|
|
18
|
+
finally {
|
|
19
|
+
pending.delete(stop);
|
|
20
|
+
}
|
|
21
|
+
}));
|
|
22
|
+
let timer;
|
|
23
|
+
const timedOut = new Promise((resolve) => {
|
|
24
|
+
timer = setTimeout(resolve, timeoutMs);
|
|
25
|
+
timer.unref?.();
|
|
26
|
+
});
|
|
27
|
+
await Promise.race([stopped, timedOut]);
|
|
28
|
+
clearTimeout(timer);
|
|
29
|
+
if (pending.size > 0) {
|
|
30
|
+
const names = [...pending].map((stop) => stop.name || "anonymous");
|
|
31
|
+
console.warn(`[agent-native] background jobs still stopping after ${timeoutMs}ms; shutting down without them: ${names.join(", ")}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -62,6 +62,7 @@ import { getConfiguredAppBasePath, stripAppBasePath } from "./app-base-path.js";
|
|
|
62
62
|
import { readAnalyticsSessionId } from "./attribution.js";
|
|
63
63
|
import { getSession } from "./auth.js";
|
|
64
64
|
import { createAutomationFailureUnsubscribeHandler } from "./automation-failure-notifications.js";
|
|
65
|
+
import { registerBackgroundJobStop } from "./background-jobs.js";
|
|
65
66
|
import { getBetterAuthInternalAdapter, getBetterAuthSync, } from "./better-auth-instance.js";
|
|
66
67
|
import { resolveBuilderRequestAuthorization } from "./builder-api-auth.js";
|
|
67
68
|
import { BUILDER_CONNECT_PARAM, BUILDER_CONNECT_MODE_PARAM, BUILDER_AGENT_NATIVE_PROVISION_MODE, BUILDER_CONNECT_ATTEMPT_PARAM, BUILDER_CONNECT_STATE_COOKIE, BUILDER_ENV_KEYS, BUILDER_OPENER_PARAM, BUILDER_RELAY_FLOW_HEADER, BUILDER_RELAY_SIGNATURE_HEADER, BUILDER_RELAY_STATE_PARAM, BUILDER_RELAY_TIMESTAMP_HEADER, appendBuilderConnectToken, appendBuilderConnectStateCookie, builderConnectTrackingProperties, BUILDER_UPSTREAM_FAILURE_STATUS, createBuilderConnectState, createBuilderBrowserCallbackErrorPage, createBuilderBrowserCallbackPage, createBuilderRelayRequest, getBuilderConnectCallbackOriginFromUrl, getBuilderConnectTrackingParams, getBuilderBrowserOriginForEvent, getBuilderBrowserStatusForEvent, isBuilderAccountAlreadyExistsError, isBuilderAccountProvisioningEnabled, isBuilderConnectCallbackUrlAllowed, isSignedBuilderConnectState, normalizeBuilderAgentContext, parseBuilderConnectStateCookie, provisionBuilderAccount, resolveBuilderBranchProjectId, resolveBuilderConnectCallbackUrl, resolveBuilderConnectCallbackState, resolveBuilderPreviewRelayParentOrigin, removeBuilderConnectStateCookie, runBuilderAgent, sendBuilderPopupErrorPage, verifyBuilderRelayRequest, verifyBuilderPreviewRelayStateForCallback, verifyBuilderConnectTokenAndGetOwner, signBuilderProvisioningToken, verifyBuilderProvisioningToken, withBuilderConnectTrackingParams, } from "./builder-browser.js";
|
|
@@ -2677,8 +2678,21 @@ export function createCoreRoutesPlugin(options = {}) {
|
|
|
2677
2678
|
return;
|
|
2678
2679
|
let lastSweep = 0;
|
|
2679
2680
|
const SWEEP_INTERVAL_MS = 2 * 60 * 1000;
|
|
2680
|
-
|
|
2681
|
-
|
|
2681
|
+
let startupTimer = null;
|
|
2682
|
+
let sweepTimer = null;
|
|
2683
|
+
const stopSandboxExecutionSweep = () => {
|
|
2684
|
+
if (startupTimer) {
|
|
2685
|
+
clearTimeout(startupTimer);
|
|
2686
|
+
startupTimer = null;
|
|
2687
|
+
}
|
|
2688
|
+
if (sweepTimer) {
|
|
2689
|
+
clearInterval(sweepTimer);
|
|
2690
|
+
sweepTimer = null;
|
|
2691
|
+
}
|
|
2692
|
+
};
|
|
2693
|
+
startupTimer = setTimeout(() => {
|
|
2694
|
+
startupTimer = null;
|
|
2695
|
+
sweepTimer = setInterval(() => {
|
|
2682
2696
|
const now = Date.now();
|
|
2683
2697
|
if (now - lastSweep < SWEEP_INTERVAL_MS)
|
|
2684
2698
|
return;
|
|
@@ -2690,7 +2704,11 @@ export function createCoreRoutesPlugin(options = {}) {
|
|
|
2690
2704
|
// best-effort — never break the server
|
|
2691
2705
|
});
|
|
2692
2706
|
}, 30_000); // Check every 30s but only sweep once per 2min
|
|
2707
|
+
sweepTimer.unref?.();
|
|
2693
2708
|
}, 25_000); // Start 25s after init (after the agent sweeps)
|
|
2709
|
+
startupTimer.unref?.();
|
|
2710
|
+
registerBackgroundJobStop(stopSandboxExecutionSweep);
|
|
2711
|
+
nitroApp.hooks?.hook?.("close", stopSandboxExecutionSweep);
|
|
2694
2712
|
})();
|
|
2695
2713
|
getH3App(nitroApp).use(`${P}/debug/runtime`, defineEventHandler(async (event) => {
|
|
2696
2714
|
setResponseHeader(event, "cache-control", "no-store");
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { beginPgliteClientShutdown, closeDbExec, resumePgliteClientAccess, waitForPgliteClientOperations, } from "../db/client.js";
|
|
2
|
+
import { stopBackgroundJobs } from "./background-jobs.js";
|
|
2
3
|
const devDatabaseCloseApps = new WeakSet();
|
|
3
4
|
let devDatabaseClosePromise;
|
|
4
5
|
const devDatabaseHot = import.meta.hot;
|
|
@@ -41,10 +42,19 @@ async function closeDevDatabase(requestId) {
|
|
|
41
42
|
}
|
|
42
43
|
}
|
|
43
44
|
export function installDevDatabaseCloseHook(nitroApp) {
|
|
44
|
-
if (process.env.NODE_ENV !== "development")
|
|
45
|
-
return;
|
|
46
45
|
if (!nitroApp?.hooks?.hook || devDatabaseCloseApps.has(nitroApp))
|
|
47
46
|
return;
|
|
48
47
|
devDatabaseCloseApps.add(nitroApp);
|
|
49
|
-
|
|
48
|
+
// Jobs must stop first: a tick against a closing client becomes an
|
|
49
|
+
// unhandled rejection and its next interval keeps the process alive.
|
|
50
|
+
// Keep this on the Nitro close hook (not inside closeDevDatabase): a
|
|
51
|
+
// Vite HMR restart closes/resumes the DB through that same function and
|
|
52
|
+
// nothing restarts jobs on resume.
|
|
53
|
+
nitroApp.hooks.hook("close", async () => {
|
|
54
|
+
await stopBackgroundJobs();
|
|
55
|
+
// Only the dev server owns its database. In production, detached work
|
|
56
|
+
// such as a sandbox run can still be saving its result while Nitro closes.
|
|
57
|
+
if (process.env.NODE_ENV === "development")
|
|
58
|
+
await closeDevDatabase();
|
|
59
|
+
});
|
|
50
60
|
}
|
|
@@ -6,6 +6,7 @@ export interface IntervalJobOptions {
|
|
|
6
6
|
leading?: boolean;
|
|
7
7
|
}
|
|
8
8
|
export interface IntervalJobHandle {
|
|
9
|
-
|
|
9
|
+
/** Stops scheduling and waits for any in-flight run to settle. */
|
|
10
|
+
stop(): Promise<void>;
|
|
10
11
|
}
|
|
11
12
|
export declare function startIntervalJob(runOnce: (signal: AbortSignal) => Promise<void>, options: IntervalJobOptions): IntervalJobHandle;
|
|
@@ -9,7 +9,8 @@ export interface PollEngineOptions {
|
|
|
9
9
|
}
|
|
10
10
|
export interface PollEngineHandle {
|
|
11
11
|
start(): void;
|
|
12
|
-
|
|
12
|
+
/** Stops scheduling and waits (bounded by timeoutMs) for any in-flight attempt. */
|
|
13
|
+
stop(): Promise<void>;
|
|
13
14
|
pollNow(): void;
|
|
14
15
|
reschedule(): void;
|
|
15
16
|
readonly isRunning: boolean;
|
|
@@ -26,6 +26,7 @@ export function createPollEngine(attempt, options) {
|
|
|
26
26
|
let activeController = null;
|
|
27
27
|
let activeStopRequested = false;
|
|
28
28
|
let inFlightReport = null;
|
|
29
|
+
let currentAttempt = null;
|
|
29
30
|
function clearTimer() {
|
|
30
31
|
if (timer != null) {
|
|
31
32
|
clearTimeout(timer);
|
|
@@ -83,6 +84,7 @@ export function createPollEngine(attempt, options) {
|
|
|
83
84
|
return;
|
|
84
85
|
report(err);
|
|
85
86
|
});
|
|
87
|
+
currentAttempt = settled;
|
|
86
88
|
let timeoutError;
|
|
87
89
|
try {
|
|
88
90
|
await Promise.race([
|
|
@@ -113,6 +115,8 @@ export function createPollEngine(attempt, options) {
|
|
|
113
115
|
activeController = null;
|
|
114
116
|
if (inFlightReport === report)
|
|
115
117
|
inFlightReport = null;
|
|
118
|
+
if (currentAttempt === settled)
|
|
119
|
+
currentAttempt = null;
|
|
116
120
|
inFlight = false;
|
|
117
121
|
if (gen === generation && running)
|
|
118
122
|
schedule(gen);
|
|
@@ -130,12 +134,32 @@ export function createPollEngine(attempt, options) {
|
|
|
130
134
|
else
|
|
131
135
|
schedule(gen);
|
|
132
136
|
},
|
|
133
|
-
stop() {
|
|
137
|
+
async stop() {
|
|
134
138
|
running = false;
|
|
135
139
|
generation++;
|
|
136
140
|
clearTimer();
|
|
137
141
|
activeStopRequested = true;
|
|
138
142
|
activeController?.abort();
|
|
143
|
+
const pending = currentAttempt;
|
|
144
|
+
if (!pending)
|
|
145
|
+
return;
|
|
146
|
+
// Abort alone is not enough: an attempt that ignores the signal can
|
|
147
|
+
// hang forever and block Nitro close via stopBackgroundJobs().
|
|
148
|
+
const waitMs = getTimeoutMs();
|
|
149
|
+
let timer;
|
|
150
|
+
try {
|
|
151
|
+
await Promise.race([
|
|
152
|
+
pending,
|
|
153
|
+
new Promise((resolve) => {
|
|
154
|
+
timer = setTimeout(resolve, waitMs);
|
|
155
|
+
maybeUnref(timer);
|
|
156
|
+
}),
|
|
157
|
+
]);
|
|
158
|
+
}
|
|
159
|
+
finally {
|
|
160
|
+
if (timer != null)
|
|
161
|
+
clearTimeout(timer);
|
|
162
|
+
}
|
|
139
163
|
},
|
|
140
164
|
pollNow() {
|
|
141
165
|
if (!running || inFlight)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function isNonProgressEventType(type: string): boolean;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Event types that never count as real run progress. The server's
|
|
2
|
+
// `last_progress_at` write (run-manager) and the browser's durable liveness
|
|
3
|
+
// cursor (sse-event-processor) both start from this list, so a reattached
|
|
4
|
+
// client cannot see progress the server never persisted. Both layers add their
|
|
5
|
+
// own stateful rule for action-preparation activity on top.
|
|
6
|
+
const NON_PROGRESS_EVENT_TYPES = new Set([
|
|
7
|
+
"stream_keepalive",
|
|
8
|
+
"model_stream",
|
|
9
|
+
"clear",
|
|
10
|
+
]);
|
|
11
|
+
export function isNonProgressEventType(type) {
|
|
12
|
+
return NON_PROGRESS_EVENT_TYPES.has(type);
|
|
13
|
+
}
|