@agent-native/core 0.157.9 → 0.157.10
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/templates/analytics/scripts/first-party-analytics-postgres-retention-dry-run.sql +5 -2
- package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +3 -1
- package/corpus/templates/analytics/server/lib/first-party-analytics-purge.ts +18 -6
- package/corpus/templates/analytics/server/plugins/db.ts +139 -0
- package/corpus/templates/slides/AGENTS.md +14 -10
- package/corpus/templates/slides/actions/export-pptx.ts +1094 -94
- package/corpus/templates/slides/actions/import-file.ts +16 -1
- package/corpus/templates/slides/actions/import-pptx.ts +61 -43
- package/corpus/templates/slides/app/components/deck/SlideRenderer.tsx +222 -29
- package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +71 -0
- package/corpus/templates/slides/app/global.css +19 -1
- package/corpus/templates/slides/app/lib/export-google-slides-client.ts +44 -5
- package/corpus/templates/slides/app/lib/export-pdf-client.ts +13 -6
- package/corpus/templates/slides/app/lib/export-pptx-client.ts +556 -25
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +18 -1
- package/corpus/templates/slides/server/handlers/import/html-converter.ts +1176 -26
- package/corpus/templates/slides/server/handlers/import/pdf-fidelity-parser.ts +13 -0
- package/corpus/templates/slides/server/handlers/import/pptx-assets.ts +24 -0
- package/corpus/templates/slides/shared/aspect-ratios.ts +13 -1
- package/dist/agent/harness/migrations.js +24 -3
- package/dist/agent/production-agent.js +11 -0
- package/dist/agent/run-loop-with-resume.js +21 -4
- package/dist/automations/service.d.ts +6 -0
- package/dist/automations/service.js +35 -0
- package/dist/cli/code-agent-connector.d.ts +4 -1
- package/dist/cli/code-agent-connector.js +154 -23
- package/dist/cli/code-agent-executor.js +255 -13
- package/dist/cli/code-agent-runs.d.ts +1 -0
- package/dist/cli/code-agent-runs.js +2 -1
- package/dist/cli/portal-workspace.d.ts +49 -0
- package/dist/cli/portal-workspace.js +320 -0
- package/dist/client/AssistantChat.d.ts +1 -1
- package/dist/client/AssistantChat.js +39 -61
- package/dist/client/ConnectBuilderCard.js +33 -5
- package/dist/client/agent-chat-adapter.js +4 -0
- package/dist/client/analytics-platform.d.ts +2 -0
- package/dist/client/analytics-platform.js +12 -0
- package/dist/client/analytics.d.ts +3 -0
- package/dist/client/analytics.js +10 -1
- package/dist/client/chat/index.d.ts +1 -0
- package/dist/client/chat/index.js +1 -0
- package/dist/client/chat/run-recovery.d.ts +2 -1
- package/dist/client/chat/run-recovery.js +2 -2
- package/dist/client/chat-first/apps-rail.d.ts +1 -1
- package/dist/client/chat-first/apps-rail.js +11 -11
- package/dist/client/chat-first/surface-tabs.d.ts +1 -1
- package/dist/client/chat-first/surface-tabs.js +38 -36
- package/dist/client/chat-first/types.d.ts +5 -0
- package/dist/client/chat-first.d.ts +1 -1
- package/dist/client/chat-first.js +4 -4
- package/dist/client/desktop-local-code-change.d.ts +10 -0
- package/dist/client/desktop-local-code-change.js +16 -0
- package/dist/client/index.d.ts +1 -0
- package/dist/client/index.js +1 -0
- package/dist/client/observability/ThumbsFeedback.js +21 -7
- package/dist/client/terminal/AgentTerminal.d.ts +9 -1
- package/dist/client/terminal/AgentTerminal.js +38 -3
- package/dist/client/terminal/index.d.ts +1 -1
- package/dist/client/terminal/index.js +1 -1
- package/dist/client/track.js +9 -1
- package/dist/client/use-action.js +3 -0
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/deploy/build.js +1 -1
- package/dist/ingestion/pptx.d.ts +78 -0
- package/dist/ingestion/pptx.js +991 -107
- package/dist/integrations/index.d.ts +3 -3
- package/dist/integrations/index.js +2 -2
- package/dist/integrations/plugin.js +95 -6
- package/dist/integrations/remote-commands-store.d.ts +5 -0
- package/dist/integrations/remote-commands-store.js +63 -22
- package/dist/integrations/remote-devices-store.d.ts +2 -1
- package/dist/integrations/remote-devices-store.js +46 -0
- package/dist/integrations/remote-types.d.ts +20 -0
- package/dist/jobs/actions/list-recurring-jobs.d.ts +3 -0
- package/dist/jobs/actions/list-recurring-jobs.js +5 -1
- package/dist/jobs/frontmatter.d.ts +16 -0
- package/dist/jobs/frontmatter.js +53 -0
- package/dist/jobs/remote-execution.d.ts +42 -0
- package/dist/jobs/remote-execution.js +287 -0
- package/dist/jobs/scheduler.js +102 -3
- package/dist/jobs/tools.js +50 -3
- package/dist/notifications/routes.d.ts +4 -4
- package/dist/provider-api/actions/custom-provider-registration.d.ts +9 -9
- package/dist/provider-api/actions/provider-api.d.ts +14 -14
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/server/action-routes.js +4 -2
- package/dist/server/agent-chat-plugin.js +6 -0
- package/dist/server/agent-run-context.d.ts +1 -0
- package/dist/server/agent-run-context.js +9 -0
- package/dist/server/core-routes-plugin.js +8 -1
- package/dist/server/request-context.d.ts +2 -0
- package/dist/shared/analytics-platform.d.ts +6 -0
- package/dist/shared/analytics-platform.js +14 -0
- package/dist/shared/mcp-embed-headers.d.ts +1 -1
- package/dist/shared/mcp-embed-headers.js +1 -1
- package/dist/styles/agent-native.css +83 -103
- package/dist/terminal/cli-registry.js +5 -0
- package/dist/tracking/registry.js +9 -1
- package/dist/triggers/actions/list-automations.d.ts +3 -0
- package/dist/triggers/actions/list-automations.js +3 -0
- package/dist/triggers/actions/manage-automation.d.ts +9 -0
- package/dist/triggers/actions/manage-automation.js +14 -2
- package/dist/triggers/actions.js +82 -7
- package/docs/content/template-content-developers.mdx +48 -0
- package/docs/content/toolkit-ui.mdx +57 -0
- package/package.json +2 -2
|
@@ -6,15 +6,15 @@ export { resolveDefaultIntegrationExecutionContext } from "./identity.js";
|
|
|
6
6
|
export { upsertVerifiedIntegrationIdentity, type IntegrationIdentityLink, } from "./identity-links-store.js";
|
|
7
7
|
export { BUILT_IN_INTEGRATION_CATALOG, INTEGRATION_CATEGORIES, getIntegrationCatalogEntry, listBuiltInChannelIntegrations, listIntegrationCatalog, type BuiltInChannelId, type AutomationCapabilities, type ChannelCapabilities, type IntegrationAvailability, type IntegrationCatalogEntry, type IntegrationCategory, type IntegrationCredentialRequirement, type IntegrationIconKey, type IntegrationSupportMaturity, } from "./catalog.js";
|
|
8
8
|
export { BUILT_IN_INTEGRATION_ADAPTER_FACTORIES, BUILT_IN_INTEGRATION_ADAPTER_IDS, createBuiltInIntegrationAdapters, createIntegrationsPlugin, defaultIntegrationsPlugin, enqueueRemoteCommand, } from "./plugin.js";
|
|
9
|
-
export { createRemoteDevice, getRemoteComputerCapabilities, listRemoteDevicesForOwner, revokeRemoteDeviceForOwner, unregisterRemoteDevice, } from "./remote-devices-store.js";
|
|
10
|
-
export { claimNextComputerCommand, enqueueComputerCommand, getRemoteCommand, listRemoteCommandsForOwner, } from "./remote-commands-store.js";
|
|
9
|
+
export { createRemoteDevice, getRemoteComputerCapabilities, getRemoteExecutionCapabilities, listRemoteDevicesForOwner, revokeRemoteDeviceForOwner, unregisterRemoteDevice, } from "./remote-devices-store.js";
|
|
10
|
+
export { claimNextComputerCommand, enqueueComputerCommand, getRemoteCommand, getRemoteCommandByIdempotencyKey, listRemoteCommandsForOwner, } from "./remote-commands-store.js";
|
|
11
11
|
export { createRemoteBrowserActionEntries, type CreateRemoteBrowserActionEntriesOptions, } from "./remote-browser-actions.js";
|
|
12
12
|
export { assertValidComputerCommandEnvelope, computeComputerActionHash, computerOperationRequiresApproval, ComputerSupervisionError, } from "./computer-supervision.js";
|
|
13
13
|
export { authorizeComputerOperation, createComputerApprovalRequest, decideComputerApproval, getComputerApprovalForOwner, listComputerApprovalsForOwner, type ComputerApprovalRecord, type ComputerApprovalStatus, } from "./computer-supervision-store.js";
|
|
14
14
|
export { insertRemoteLiveViewEvents } from "./remote-run-events-store.js";
|
|
15
15
|
export { listRemotePushNotificationsForOwner, listRemotePushRegistrationsForOwner, queueRemotePushNotifications, toPublicRemotePushRegistration, unregisterRemotePushRegistrationForOwner, upsertRemotePushRegistration, } from "./remote-push-store.js";
|
|
16
16
|
export { deliverPendingRemotePushNotifications } from "./remote-push-delivery.js";
|
|
17
|
-
export type { ComputerApprovalScope, ComputerCommandAction, ComputerCommandEnvelope, ComputerOperationClass, PublicRemotePushRegistration, PublicRemoteDevice, RemoteComputerCapabilities, RemoteCommand, RemoteDevice, RemoteDeviceMetadata, RemoteLiveViewEvent, RemotePushNotification, RemotePushRegistration, RemoteRunEvent, } from "./remote-types.js";
|
|
17
|
+
export type { ComputerApprovalScope, ComputerCommandAction, ComputerCommandEnvelope, ComputerOperationClass, PublicRemotePushRegistration, PublicRemoteDevice, RemoteComputerCapabilities, RemoteExecutionCapabilities, RemoteExecutionBackend, RemoteExecutionPersistence, RemoteExecutionWorkload, RemoteCommand, RemoteDevice, RemoteDeviceMetadata, RemoteLiveViewEvent, RemotePushNotification, RemotePushRegistration, RemoteRunEvent, } from "./remote-types.js";
|
|
18
18
|
export { resolveSlackBotTokenForIncoming, slackAdapter, type SlackAdapterOptions, } from "./adapters/slack.js";
|
|
19
19
|
export { telegramAdapter } from "./adapters/telegram.js";
|
|
20
20
|
export { whatsappAdapter } from "./adapters/whatsapp.js";
|
|
@@ -6,8 +6,8 @@ export { upsertVerifiedIntegrationIdentity, } from "./identity-links-store.js";
|
|
|
6
6
|
export { BUILT_IN_INTEGRATION_CATALOG, INTEGRATION_CATEGORIES, getIntegrationCatalogEntry, listBuiltInChannelIntegrations, listIntegrationCatalog, } from "./catalog.js";
|
|
7
7
|
// Plugin
|
|
8
8
|
export { BUILT_IN_INTEGRATION_ADAPTER_FACTORIES, BUILT_IN_INTEGRATION_ADAPTER_IDS, createBuiltInIntegrationAdapters, createIntegrationsPlugin, defaultIntegrationsPlugin, enqueueRemoteCommand, } from "./plugin.js";
|
|
9
|
-
export { createRemoteDevice, getRemoteComputerCapabilities, listRemoteDevicesForOwner, revokeRemoteDeviceForOwner, unregisterRemoteDevice, } from "./remote-devices-store.js";
|
|
10
|
-
export { claimNextComputerCommand, enqueueComputerCommand, getRemoteCommand, listRemoteCommandsForOwner, } from "./remote-commands-store.js";
|
|
9
|
+
export { createRemoteDevice, getRemoteComputerCapabilities, getRemoteExecutionCapabilities, listRemoteDevicesForOwner, revokeRemoteDeviceForOwner, unregisterRemoteDevice, } from "./remote-devices-store.js";
|
|
10
|
+
export { claimNextComputerCommand, enqueueComputerCommand, getRemoteCommand, getRemoteCommandByIdempotencyKey, listRemoteCommandsForOwner, } from "./remote-commands-store.js";
|
|
11
11
|
export { createRemoteBrowserActionEntries, } from "./remote-browser-actions.js";
|
|
12
12
|
export { assertValidComputerCommandEnvelope, computeComputerActionHash, computerOperationRequiresApproval, ComputerSupervisionError, } from "./computer-supervision.js";
|
|
13
13
|
export { authorizeComputerOperation, createComputerApprovalRequest, decideComputerApproval, getComputerApprovalForOwner, listComputerApprovalsForOwner, } from "./computer-supervision-store.js";
|
|
@@ -40,7 +40,7 @@ import { extractBearerToken, verifyInternalToken } from "./internal-token.js";
|
|
|
40
40
|
import { retryStuckPendingTasks, startPendingTasksRetryJob, } from "./pending-tasks-retry-job.js";
|
|
41
41
|
import { claimPendingTask, getNextPendingTaskForThread, getPendingTask, insertPendingTask, isDuplicateEventError, MAX_PENDING_TASK_ATTEMPTS, markTaskCompleted, markTaskDeliveryRetryable, markTaskFailed, markTaskRetryable, stageTaskDeliveryPayload, } from "./pending-tasks-store.js";
|
|
42
42
|
import { claimNextComputerCommand, claimNextRemoteCommand, enqueueComputerCommand, enqueueRemoteCommand as enqueueRemoteCommandRow, isRemoteCommandKind, listRemoteCommandsForOwner, updateRemoteCommandResult, } from "./remote-commands-store.js";
|
|
43
|
-
import { authenticateRemoteDeviceToken, createRemoteDevice, getRemoteComputerCapabilities, getRemoteDeviceForOwner, listRemoteDevicesForOwner, revokeRemoteDeviceForOwner, toPublicRemoteDevice, unregisterRemoteDevice, updateRemoteDeviceDetails, } from "./remote-devices-store.js";
|
|
43
|
+
import { authenticateRemoteDeviceToken, createRemoteDevice, getRemoteComputerCapabilities, getRemoteExecutionCapabilities, getRemoteDeviceForOwner, listRemoteDevicesForOwner, revokeRemoteDeviceForOwner, toPublicRemoteDevice, unregisterRemoteDevice, updateRemoteDeviceDetails, } from "./remote-devices-store.js";
|
|
44
44
|
import { startRemotePushDeliveryJob } from "./remote-push-delivery-job.js";
|
|
45
45
|
import { listRemotePushNotificationsForOwner, listRemotePushRegistrationsForOwner, queueRemotePushNotifications, toPublicRemotePushRegistration, unregisterRemotePushRegistrationForOwner, upsertRemotePushRegistration, } from "./remote-push-store.js";
|
|
46
46
|
import { startRemoteCommandsRetryJob } from "./remote-retry-job.js";
|
|
@@ -290,9 +290,17 @@ export async function enqueueRemoteCommand(envelope) {
|
|
|
290
290
|
limit: 10,
|
|
291
291
|
});
|
|
292
292
|
const requestedDeviceId = readString(command.hostId) ?? readString(command.deviceId);
|
|
293
|
-
const device =
|
|
293
|
+
const device = requestedDeviceId
|
|
294
294
|
? devices.find((candidate) => candidate.id === requestedDeviceId)
|
|
295
|
-
:
|
|
295
|
+
: devices[0];
|
|
296
|
+
if (requestedDeviceId && !device) {
|
|
297
|
+
return {
|
|
298
|
+
ok: false,
|
|
299
|
+
hostOnline: false,
|
|
300
|
+
hostStatus: "offline",
|
|
301
|
+
error: "The requested execution host is not paired with this account.",
|
|
302
|
+
};
|
|
303
|
+
}
|
|
296
304
|
if (!device) {
|
|
297
305
|
return {
|
|
298
306
|
ok: false,
|
|
@@ -353,6 +361,13 @@ function remoteCodeCommandParams(command) {
|
|
|
353
361
|
cwd: readString(command.cwd),
|
|
354
362
|
goalId: readString(command.goalId) ?? "task",
|
|
355
363
|
permissionMode: readString(command.permissionMode),
|
|
364
|
+
engine: readString(command.engine),
|
|
365
|
+
model: readString(command.model),
|
|
366
|
+
effort: readString(command.effort),
|
|
367
|
+
reasoningEffort: readString(command.reasoningEffort),
|
|
368
|
+
runId: readString(command.runId),
|
|
369
|
+
workload: readString(command.workload) ?? "code-agent",
|
|
370
|
+
metadata: readObject(command.metadata) ?? undefined,
|
|
356
371
|
};
|
|
357
372
|
}
|
|
358
373
|
if (type === "continue") {
|
|
@@ -389,6 +404,13 @@ function enqueueBodyToRemoteCodeCommand(body) {
|
|
|
389
404
|
cwd: payload.cwd,
|
|
390
405
|
goalId: payload.goalId,
|
|
391
406
|
permissionMode: payload.permissionMode,
|
|
407
|
+
engine: payload.engine,
|
|
408
|
+
model: payload.model,
|
|
409
|
+
effort: payload.effort,
|
|
410
|
+
reasoningEffort: payload.reasoningEffort,
|
|
411
|
+
runId: payload.runId,
|
|
412
|
+
workload: payload.workload,
|
|
413
|
+
metadata: payload.metadata,
|
|
392
414
|
};
|
|
393
415
|
}
|
|
394
416
|
if (operation === "code-agent.run.follow-up") {
|
|
@@ -473,6 +495,14 @@ async function hasOnlineRemoteDevice(ownerEmail, orgId) {
|
|
|
473
495
|
}
|
|
474
496
|
function remoteDeviceToHost(device) {
|
|
475
497
|
const online = device.status === "active" && isRemoteDeviceOnline(device);
|
|
498
|
+
const executionCapabilities = getRemoteExecutionCapabilities(device);
|
|
499
|
+
const capabilities = [
|
|
500
|
+
...(executionCapabilities?.workloads ?? []).map((workload) => `workload:${workload}`),
|
|
501
|
+
...(executionCapabilities?.engines ?? []).map((engine) => `engine:${engine}`),
|
|
502
|
+
...(executionCapabilities?.acceptsScheduledWork ? ["scheduled-task"] : []),
|
|
503
|
+
...(executionCapabilities?.acceptsPortalHandoffs ? ["portal"] : []),
|
|
504
|
+
...(device.metadata?.computerCapabilities ? ["computer"] : []),
|
|
505
|
+
];
|
|
476
506
|
return {
|
|
477
507
|
id: device.id,
|
|
478
508
|
name: device.label,
|
|
@@ -484,6 +514,8 @@ function remoteDeviceToHost(device) {
|
|
|
484
514
|
platform: device.platform ?? "desktop",
|
|
485
515
|
appVersion: device.appVersion ?? undefined,
|
|
486
516
|
hostName: device.hostName ?? undefined,
|
|
517
|
+
capabilities,
|
|
518
|
+
executionCapabilities: executionCapabilities ?? undefined,
|
|
487
519
|
metadata: device.metadata ?? undefined,
|
|
488
520
|
device: toPublicRemoteDevice(device),
|
|
489
521
|
};
|
|
@@ -672,7 +704,13 @@ export function createIntegrationsPlugin(options) {
|
|
|
672
704
|
return runWithRequestContext(context, fn);
|
|
673
705
|
}
|
|
674
706
|
async function requireRemoteDevice(event) {
|
|
675
|
-
|
|
707
|
+
// Some managed proxies omit Authorization before a serverless function
|
|
708
|
+
// sees the request. Keep the device secret in a dedicated TLS-only
|
|
709
|
+
// header as a transport fallback; the value is still hashed and looked
|
|
710
|
+
// up by authenticateRemoteDeviceToken, never persisted raw.
|
|
711
|
+
const token = extractBearerToken(getRequestHeader(event, "authorization")) ??
|
|
712
|
+
(getRequestHeader(event, "x-agent-native-device-token")?.trim() ||
|
|
713
|
+
null);
|
|
676
714
|
const device = await authenticateRemoteDeviceToken(token);
|
|
677
715
|
if (device)
|
|
678
716
|
return device;
|
|
@@ -785,6 +823,8 @@ export function createIntegrationsPlugin(options) {
|
|
|
785
823
|
const label = typeof body.label === "string" && body.label.trim()
|
|
786
824
|
? body.label.trim().slice(0, 200)
|
|
787
825
|
: "Remote device";
|
|
826
|
+
const metadata = readObject(body.metadata);
|
|
827
|
+
const hasExecutionCapabilities = Object.prototype.hasOwnProperty.call(body, "executionCapabilities");
|
|
788
828
|
const { device, token } = await createRemoteDevice({
|
|
789
829
|
ownerEmail: ctx.ownerEmail,
|
|
790
830
|
orgId: ctx.orgId,
|
|
@@ -792,7 +832,16 @@ export function createIntegrationsPlugin(options) {
|
|
|
792
832
|
platform: readString(body.platform),
|
|
793
833
|
appVersion: readString(body.appVersion) ?? readString(body.version),
|
|
794
834
|
hostName: readString(body.hostName) ?? readString(body.hostname),
|
|
795
|
-
metadata:
|
|
835
|
+
metadata: metadata || hasExecutionCapabilities
|
|
836
|
+
? {
|
|
837
|
+
...(metadata ?? {}),
|
|
838
|
+
...(hasExecutionCapabilities
|
|
839
|
+
? {
|
|
840
|
+
executionCapabilities: readRemoteExecutionCapabilities(body.executionCapabilities),
|
|
841
|
+
}
|
|
842
|
+
: {}),
|
|
843
|
+
}
|
|
844
|
+
: null,
|
|
796
845
|
});
|
|
797
846
|
return { device: toPublicRemoteDevice(device), token };
|
|
798
847
|
}));
|
|
@@ -1224,7 +1273,8 @@ export function createIntegrationsPlugin(options) {
|
|
|
1224
1273
|
let pollingDevice = device;
|
|
1225
1274
|
if (method === "POST" &&
|
|
1226
1275
|
(Object.prototype.hasOwnProperty.call(body, "computerCapabilities") ||
|
|
1227
|
-
Object.prototype.hasOwnProperty.call(body, "browserSession")
|
|
1276
|
+
Object.prototype.hasOwnProperty.call(body, "browserSession") ||
|
|
1277
|
+
Object.prototype.hasOwnProperty.call(body, "executionCapabilities"))) {
|
|
1228
1278
|
const updated = await updateRemoteDeviceDetails({
|
|
1229
1279
|
id: device.id,
|
|
1230
1280
|
metadata: {
|
|
@@ -1234,6 +1284,11 @@ export function createIntegrationsPlugin(options) {
|
|
|
1234
1284
|
computerCapabilities: readComputerCapabilities(body.computerCapabilities),
|
|
1235
1285
|
}
|
|
1236
1286
|
: {}),
|
|
1287
|
+
...(Object.prototype.hasOwnProperty.call(body, "executionCapabilities")
|
|
1288
|
+
? {
|
|
1289
|
+
executionCapabilities: readRemoteExecutionCapabilities(body.executionCapabilities),
|
|
1290
|
+
}
|
|
1291
|
+
: {}),
|
|
1237
1292
|
...(Object.prototype.hasOwnProperty.call(body, "browserSession")
|
|
1238
1293
|
? { browserSession: readBrowserSession(body.browserSession) }
|
|
1239
1294
|
: {}),
|
|
@@ -2753,6 +2808,40 @@ function readComputerCapabilities(value) {
|
|
|
2753
2808
|
desktop: readSurface(input?.desktop, true),
|
|
2754
2809
|
};
|
|
2755
2810
|
}
|
|
2811
|
+
function readRemoteExecutionCapabilities(value) {
|
|
2812
|
+
const input = readObject(value);
|
|
2813
|
+
if (!input)
|
|
2814
|
+
return {};
|
|
2815
|
+
const backend = readString(input.backend);
|
|
2816
|
+
const persistence = readString(input.persistence);
|
|
2817
|
+
const list = (candidate, max) => [
|
|
2818
|
+
...new Set(Array.isArray(candidate)
|
|
2819
|
+
? candidate
|
|
2820
|
+
.filter((entry) => typeof entry === "string")
|
|
2821
|
+
.map((entry) => entry.trim().slice(0, 120))
|
|
2822
|
+
.filter(Boolean)
|
|
2823
|
+
: []),
|
|
2824
|
+
].slice(0, max);
|
|
2825
|
+
return {
|
|
2826
|
+
...(backend === "desktop" ||
|
|
2827
|
+
backend === "container" ||
|
|
2828
|
+
backend === "kubernetes" ||
|
|
2829
|
+
backend === "external"
|
|
2830
|
+
? { backend }
|
|
2831
|
+
: {}),
|
|
2832
|
+
workloads: list(input.workloads, 16),
|
|
2833
|
+
engines: list(input.engines, 32),
|
|
2834
|
+
...(typeof input.acceptsScheduledWork === "boolean"
|
|
2835
|
+
? { acceptsScheduledWork: input.acceptsScheduledWork }
|
|
2836
|
+
: {}),
|
|
2837
|
+
...(persistence === "local-files" ||
|
|
2838
|
+
persistence === "persistent-volume" ||
|
|
2839
|
+
persistence === "ephemeral"
|
|
2840
|
+
? { persistence }
|
|
2841
|
+
: {}),
|
|
2842
|
+
adapters: list(input.adapters, 32),
|
|
2843
|
+
};
|
|
2844
|
+
}
|
|
2756
2845
|
function readBrowserSession(value) {
|
|
2757
2846
|
if (value === null)
|
|
2758
2847
|
return null;
|
|
@@ -8,6 +8,7 @@ export declare function enqueueRemoteCommand(input: {
|
|
|
8
8
|
params?: unknown;
|
|
9
9
|
platform?: string | null;
|
|
10
10
|
externalThreadId?: string | null;
|
|
11
|
+
idempotencyKey?: string | null;
|
|
11
12
|
nextCheckAt?: number;
|
|
12
13
|
}): Promise<RemoteCommand>;
|
|
13
14
|
export declare function enqueueComputerCommand(input: {
|
|
@@ -18,6 +19,10 @@ export declare function enqueueComputerCommand(input: {
|
|
|
18
19
|
platform?: string | null;
|
|
19
20
|
}): Promise<RemoteCommand>;
|
|
20
21
|
export declare function getRemoteCommand(id: string): Promise<RemoteCommand | null>;
|
|
22
|
+
export declare function getRemoteCommandByIdempotencyKey(input: {
|
|
23
|
+
deviceId: string;
|
|
24
|
+
idempotencyKey: string;
|
|
25
|
+
}): Promise<RemoteCommand | null>;
|
|
21
26
|
export declare function listRemoteCommandsForOwner(input: {
|
|
22
27
|
ownerEmail: string;
|
|
23
28
|
orgId?: string | null;
|
|
@@ -83,6 +83,7 @@ function rowToCommand(row) {
|
|
|
83
83
|
result: parseJson(row.result_json, null),
|
|
84
84
|
platform: row.platform ?? null,
|
|
85
85
|
externalThreadId: row.external_thread_id ?? null,
|
|
86
|
+
idempotencyKey: row.idempotency_key ?? null,
|
|
86
87
|
computerOperation: row.kind === "computer-operation"
|
|
87
88
|
? (params?.envelope ?? null)
|
|
88
89
|
: null,
|
|
@@ -151,28 +152,52 @@ export async function enqueueRemoteCommand(input) {
|
|
|
151
152
|
const client = getDbExec();
|
|
152
153
|
const now = Date.now();
|
|
153
154
|
const id = `remote-command-${now}-${randomHex(8)}`;
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
155
|
+
const idempotencyKey = normalizeIdempotencyKey(input.idempotencyKey);
|
|
156
|
+
if (idempotencyKey) {
|
|
157
|
+
const existing = await getRemoteCommandByIdempotencyKey({
|
|
158
|
+
deviceId: input.deviceId,
|
|
159
|
+
idempotencyKey,
|
|
160
|
+
});
|
|
161
|
+
if (existing)
|
|
162
|
+
return existing;
|
|
163
|
+
}
|
|
164
|
+
try {
|
|
165
|
+
await client.execute({
|
|
166
|
+
sql: `INSERT INTO integration_remote_commands
|
|
167
|
+
(id, device_id, owner_email, org_id, kind, params_json, status, result_json,
|
|
168
|
+
platform, external_thread_id, idempotency_key, attempts, next_check_at,
|
|
169
|
+
created_at, updated_at)
|
|
170
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
171
|
+
args: [
|
|
172
|
+
id,
|
|
173
|
+
input.deviceId,
|
|
174
|
+
input.ownerEmail,
|
|
175
|
+
input.orgId ?? null,
|
|
176
|
+
input.kind,
|
|
177
|
+
JSON.stringify(input.params ?? {}),
|
|
178
|
+
"pending",
|
|
179
|
+
null,
|
|
180
|
+
input.platform ?? null,
|
|
181
|
+
input.externalThreadId ?? null,
|
|
182
|
+
idempotencyKey,
|
|
183
|
+
0,
|
|
184
|
+
input.nextCheckAt ?? now,
|
|
185
|
+
now,
|
|
186
|
+
now,
|
|
187
|
+
],
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
catch (error) {
|
|
191
|
+
if (idempotencyKey && isUniqueConstraintError(error)) {
|
|
192
|
+
const existing = await getRemoteCommandByIdempotencyKey({
|
|
193
|
+
deviceId: input.deviceId,
|
|
194
|
+
idempotencyKey,
|
|
195
|
+
});
|
|
196
|
+
if (existing)
|
|
197
|
+
return existing;
|
|
198
|
+
}
|
|
199
|
+
throw error;
|
|
200
|
+
}
|
|
176
201
|
const command = await getRemoteCommand(id);
|
|
177
202
|
if (!command)
|
|
178
203
|
throw new Error("remote command insert failed");
|
|
@@ -259,6 +284,16 @@ export async function getRemoteCommand(id) {
|
|
|
259
284
|
});
|
|
260
285
|
return rows[0] ? rowToCommand(rows[0]) : null;
|
|
261
286
|
}
|
|
287
|
+
export async function getRemoteCommandByIdempotencyKey(input) {
|
|
288
|
+
await ensureTable();
|
|
289
|
+
const { rows } = await getDbExec().execute({
|
|
290
|
+
sql: `SELECT * FROM integration_remote_commands
|
|
291
|
+
WHERE device_id = ? AND idempotency_key = ?
|
|
292
|
+
LIMIT 1`,
|
|
293
|
+
args: [input.deviceId, input.idempotencyKey],
|
|
294
|
+
});
|
|
295
|
+
return rows[0] ? rowToCommand(rows[0]) : null;
|
|
296
|
+
}
|
|
262
297
|
export async function listRemoteCommandsForOwner(input) {
|
|
263
298
|
await ensureTable();
|
|
264
299
|
const limit = Math.max(1, Math.min(input.limit ?? 100, 250));
|
|
@@ -529,6 +564,12 @@ function randomHex(byteLength) {
|
|
|
529
564
|
.map((byte) => byte.toString(16).padStart(2, "0"))
|
|
530
565
|
.join("");
|
|
531
566
|
}
|
|
567
|
+
function normalizeIdempotencyKey(value) {
|
|
568
|
+
if (typeof value !== "string")
|
|
569
|
+
return null;
|
|
570
|
+
const normalized = value.trim();
|
|
571
|
+
return normalized ? normalized.slice(0, 512) : null;
|
|
572
|
+
}
|
|
532
573
|
function affectedRows(result) {
|
|
533
574
|
return (result.rowsAffected ??
|
|
534
575
|
Number(result.rowCount ?? 0));
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type { PublicRemoteDevice, RemoteComputerCapabilities, RemoteDevice, RemoteDeviceMetadata } from "./remote-types.js";
|
|
1
|
+
import type { PublicRemoteDevice, RemoteComputerCapabilities, RemoteDevice, RemoteDeviceMetadata, RemoteExecutionCapabilities } from "./remote-types.js";
|
|
2
2
|
export declare function toPublicRemoteDevice(device: RemoteDevice): PublicRemoteDevice;
|
|
3
3
|
export declare function getRemoteComputerCapabilities(device: Pick<RemoteDevice, "metadata">): RemoteComputerCapabilities | null;
|
|
4
|
+
export declare function getRemoteExecutionCapabilities(device: Pick<RemoteDevice, "metadata">): RemoteExecutionCapabilities | null;
|
|
4
5
|
export declare function createRemoteDevice(input: {
|
|
5
6
|
ownerEmail: string;
|
|
6
7
|
orgId?: string | null;
|
|
@@ -115,6 +115,12 @@ export function getRemoteComputerCapabilities(device) {
|
|
|
115
115
|
return null;
|
|
116
116
|
return normalizeComputerCapabilities(value);
|
|
117
117
|
}
|
|
118
|
+
export function getRemoteExecutionCapabilities(device) {
|
|
119
|
+
const value = device.metadata?.executionCapabilities;
|
|
120
|
+
if (!value || typeof value !== "object")
|
|
121
|
+
return null;
|
|
122
|
+
return normalizeExecutionCapabilities(value);
|
|
123
|
+
}
|
|
118
124
|
export async function createRemoteDevice(input) {
|
|
119
125
|
await ensureTable();
|
|
120
126
|
const client = getDbExec();
|
|
@@ -334,6 +340,9 @@ function serializeRemoteDeviceMetadata(metadata) {
|
|
|
334
340
|
if (metadata.computerCapabilities !== undefined) {
|
|
335
341
|
normalized.computerCapabilities = normalizeComputerCapabilities(metadata.computerCapabilities);
|
|
336
342
|
}
|
|
343
|
+
if (metadata.executionCapabilities !== undefined) {
|
|
344
|
+
normalized.executionCapabilities = normalizeExecutionCapabilities(metadata.executionCapabilities);
|
|
345
|
+
}
|
|
337
346
|
const json = JSON.stringify(normalized);
|
|
338
347
|
if (new TextEncoder().encode(json).byteLength > 32_768) {
|
|
339
348
|
throw new Error("Remote device metadata exceeds 32 KiB");
|
|
@@ -365,6 +374,43 @@ function normalizeComputerCapabilities(value) {
|
|
|
365
374
|
}
|
|
366
375
|
return result;
|
|
367
376
|
}
|
|
377
|
+
function normalizeExecutionCapabilities(value) {
|
|
378
|
+
const result = {};
|
|
379
|
+
if (value.backend === "desktop" ||
|
|
380
|
+
value.backend === "container" ||
|
|
381
|
+
value.backend === "kubernetes" ||
|
|
382
|
+
value.backend === "external") {
|
|
383
|
+
result.backend = value.backend;
|
|
384
|
+
}
|
|
385
|
+
if (Array.isArray(value.workloads)) {
|
|
386
|
+
result.workloads = normalizeStringList(value.workloads, 16).filter((entry) => entry === "code-agent" ||
|
|
387
|
+
entry === "scheduled-code" ||
|
|
388
|
+
entry === "external-agent");
|
|
389
|
+
}
|
|
390
|
+
if (Array.isArray(value.engines)) {
|
|
391
|
+
result.engines = normalizeStringList(value.engines, 32);
|
|
392
|
+
}
|
|
393
|
+
if (typeof value.acceptsScheduledWork === "boolean") {
|
|
394
|
+
result.acceptsScheduledWork = value.acceptsScheduledWork;
|
|
395
|
+
}
|
|
396
|
+
if (value.persistence === "local-files" ||
|
|
397
|
+
value.persistence === "persistent-volume" ||
|
|
398
|
+
value.persistence === "ephemeral") {
|
|
399
|
+
result.persistence = value.persistence;
|
|
400
|
+
}
|
|
401
|
+
if (Array.isArray(value.adapters)) {
|
|
402
|
+
result.adapters = normalizeStringList(value.adapters, 32);
|
|
403
|
+
}
|
|
404
|
+
return result;
|
|
405
|
+
}
|
|
406
|
+
function normalizeStringList(value, max) {
|
|
407
|
+
return [
|
|
408
|
+
...new Set(value
|
|
409
|
+
.filter((entry) => typeof entry === "string")
|
|
410
|
+
.map((entry) => entry.trim().slice(0, 120))
|
|
411
|
+
.filter(Boolean)),
|
|
412
|
+
].slice(0, max);
|
|
413
|
+
}
|
|
368
414
|
function looksLikeLargeBase64(value) {
|
|
369
415
|
return value.length > 4_096 && /[A-Za-z0-9+/]{4_096,}={0,2}/.test(value);
|
|
370
416
|
}
|
|
@@ -1,4 +1,22 @@
|
|
|
1
1
|
export type RemoteCommandKind = "create-run" | "list-runs" | "get-run" | "append-followup" | "approve" | "deny" | "stop" | "status" | "computer-operation";
|
|
2
|
+
/** Workloads a paired execution host may accept from the relay. */
|
|
3
|
+
export type RemoteExecutionWorkload = "code-agent" | "scheduled-code" | "external-agent";
|
|
4
|
+
export type RemoteExecutionBackend = "desktop" | "container" | "kubernetes" | "external";
|
|
5
|
+
export type RemoteExecutionPersistence = "local-files" | "persistent-volume" | "ephemeral";
|
|
6
|
+
/**
|
|
7
|
+
* Non-secret capabilities advertised by a host during its heartbeat. The
|
|
8
|
+
* relay uses this to reject a requested workload before queueing it, while
|
|
9
|
+
* leaving room for a future Kubernetes or external-agent adapter.
|
|
10
|
+
*/
|
|
11
|
+
export interface RemoteExecutionCapabilities {
|
|
12
|
+
backend?: RemoteExecutionBackend;
|
|
13
|
+
workloads?: RemoteExecutionWorkload[];
|
|
14
|
+
engines?: string[];
|
|
15
|
+
acceptsScheduledWork?: boolean;
|
|
16
|
+
acceptsPortalHandoffs?: boolean;
|
|
17
|
+
persistence?: RemoteExecutionPersistence;
|
|
18
|
+
adapters?: string[];
|
|
19
|
+
}
|
|
2
20
|
export type ComputerOperationClass = "browser.observe" | "browser.control" | "desktop.observe" | "desktop.control";
|
|
3
21
|
export type ComputerApprovalScope = "once" | "run" | "task";
|
|
4
22
|
export interface ComputerCommandAction {
|
|
@@ -40,6 +58,7 @@ export interface RemoteComputerCapabilities {
|
|
|
40
58
|
}
|
|
41
59
|
export interface RemoteDeviceMetadata extends Record<string, unknown> {
|
|
42
60
|
computerCapabilities?: RemoteComputerCapabilities;
|
|
61
|
+
executionCapabilities?: RemoteExecutionCapabilities;
|
|
43
62
|
}
|
|
44
63
|
export type RemoteCommandStatus = "pending" | "claimed" | "running" | "completed" | "failed";
|
|
45
64
|
export type RemoteDeviceStatus = "active" | "inactive";
|
|
@@ -85,6 +104,7 @@ export interface RemoteCommand {
|
|
|
85
104
|
result: unknown;
|
|
86
105
|
platform: string | null;
|
|
87
106
|
externalThreadId: string | null;
|
|
107
|
+
idempotencyKey?: string | null;
|
|
88
108
|
computerOperation?: ComputerCommandEnvelope | null;
|
|
89
109
|
attempts: number;
|
|
90
110
|
nextCheckAt: number;
|
|
@@ -14,6 +14,9 @@ export interface RecurringJobActionItem {
|
|
|
14
14
|
lastError: string | null;
|
|
15
15
|
nextRun: string | null;
|
|
16
16
|
createdBy: string | null;
|
|
17
|
+
executionHostId: string | null;
|
|
18
|
+
executionEngine: string | null;
|
|
19
|
+
executionCwd: string | null;
|
|
17
20
|
mcpTools: string[];
|
|
18
21
|
canUpdate: boolean;
|
|
19
22
|
}
|
|
@@ -59,7 +59,8 @@ export default defineAction({
|
|
|
59
59
|
const { meta, body } = parseJobFrontmatter(full.content);
|
|
60
60
|
if (!jobBelongsToApp(meta, ctx?.appId))
|
|
61
61
|
continue;
|
|
62
|
-
const canUpdate = scope === "personal" ||
|
|
62
|
+
const canUpdate = scope === "personal" ||
|
|
63
|
+
(await authorizeJobMutation(owner, meta)) === null;
|
|
63
64
|
jobs.push({
|
|
64
65
|
id: full.id,
|
|
65
66
|
name: jobName(full.path),
|
|
@@ -78,6 +79,9 @@ export default defineAction({
|
|
|
78
79
|
lastError: meta.lastError ?? null,
|
|
79
80
|
nextRun: nextRun(meta),
|
|
80
81
|
createdBy: meta.createdBy ?? null,
|
|
82
|
+
executionHostId: meta.executionHostId ?? null,
|
|
83
|
+
executionEngine: meta.executionEngine ?? null,
|
|
84
|
+
executionCwd: meta.executionCwd ?? null,
|
|
81
85
|
mcpTools: meta.mcpTools ?? [],
|
|
82
86
|
canUpdate,
|
|
83
87
|
});
|
|
@@ -52,6 +52,22 @@ export interface JobFrontmatter {
|
|
|
52
52
|
domain?: string;
|
|
53
53
|
/** Explicit application owner used by the recurring-job scheduler. */
|
|
54
54
|
appId?: string;
|
|
55
|
+
/** Optional paired execution host for code-agent work. */
|
|
56
|
+
executionHostId?: string;
|
|
57
|
+
/** Optional engine id understood by the selected execution host. */
|
|
58
|
+
executionEngine?: string;
|
|
59
|
+
/** Optional host-local workspace path used by code-agent work. */
|
|
60
|
+
executionCwd?: string;
|
|
61
|
+
/** Stable remote dispatch key for a running host-targeted job. */
|
|
62
|
+
remoteRequestId?: string;
|
|
63
|
+
/** Durable relay command id for a running host-targeted job. */
|
|
64
|
+
remoteCommandId?: string;
|
|
65
|
+
/** Durable remote code-agent run id, when the host has started one. */
|
|
66
|
+
remoteRunId?: string;
|
|
67
|
+
/** Durable automation history row associated with the remote dispatch. */
|
|
68
|
+
remoteAutomationRunId?: string;
|
|
69
|
+
/** Whether a completed remote dispatch should advance the cron schedule. */
|
|
70
|
+
remoteAdvanceSchedule?: boolean;
|
|
55
71
|
/**
|
|
56
72
|
* Optional application-owned policy id carried into actions by the trusted
|
|
57
73
|
* trigger dispatcher. It is not model-supplied action input.
|
package/dist/jobs/frontmatter.js
CHANGED
|
@@ -18,6 +18,15 @@ const MAX_JOB_MCP_TOOLS = 64;
|
|
|
18
18
|
const JOB_MCP_TOOL_NAME_RE = /^mcp__[^\s]+__[^\s]+$/;
|
|
19
19
|
const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n)?([\s\S]*)$/;
|
|
20
20
|
const DELEGATED_POLICY_ID_RE = /^[a-z0-9][a-z0-9._:-]{0,127}$/i;
|
|
21
|
+
const EXECUTION_ID_RE = /^[a-z0-9][a-z0-9._:-]{0,127}$/i;
|
|
22
|
+
const REMOTE_ID_RE = /^[a-z0-9][a-z0-9@+._:/-]{0,511}$/i;
|
|
23
|
+
function assertBoundedFrontmatterValue(value, label, pattern) {
|
|
24
|
+
if (value === undefined)
|
|
25
|
+
return;
|
|
26
|
+
if (!pattern.test(value)) {
|
|
27
|
+
throw new Error(`${label} must be a bounded opaque identifier.`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
21
30
|
/**
|
|
22
31
|
* Normalize the non-secret MCP capability references persisted with a job.
|
|
23
32
|
* Tool names are opaque framework identifiers; URLs and credentials never
|
|
@@ -159,6 +168,30 @@ function parseKnownField(meta, key, rawValue) {
|
|
|
159
168
|
case "appId":
|
|
160
169
|
meta.appId = value || undefined;
|
|
161
170
|
break;
|
|
171
|
+
case "executionHostId":
|
|
172
|
+
meta.executionHostId = value || undefined;
|
|
173
|
+
break;
|
|
174
|
+
case "executionEngine":
|
|
175
|
+
meta.executionEngine = value || undefined;
|
|
176
|
+
break;
|
|
177
|
+
case "executionCwd":
|
|
178
|
+
meta.executionCwd = value || undefined;
|
|
179
|
+
break;
|
|
180
|
+
case "remoteRequestId":
|
|
181
|
+
meta.remoteRequestId = value || undefined;
|
|
182
|
+
break;
|
|
183
|
+
case "remoteCommandId":
|
|
184
|
+
meta.remoteCommandId = value || undefined;
|
|
185
|
+
break;
|
|
186
|
+
case "remoteRunId":
|
|
187
|
+
meta.remoteRunId = value || undefined;
|
|
188
|
+
break;
|
|
189
|
+
case "remoteAutomationRunId":
|
|
190
|
+
meta.remoteAutomationRunId = value || undefined;
|
|
191
|
+
break;
|
|
192
|
+
case "remoteAdvanceSchedule":
|
|
193
|
+
meta.remoteAdvanceSchedule = value !== "false";
|
|
194
|
+
break;
|
|
162
195
|
case "delegatedPolicyId":
|
|
163
196
|
meta.delegatedPolicyId = value || undefined;
|
|
164
197
|
break;
|
|
@@ -211,6 +244,16 @@ export function buildJobResourceContent(meta, body) {
|
|
|
211
244
|
!DELEGATED_POLICY_ID_RE.test(meta.delegatedPolicyId)) {
|
|
212
245
|
throw new Error("Delegated automation policy IDs must be 1-128 letters, numbers, dots, underscores, colons, or hyphens.");
|
|
213
246
|
}
|
|
247
|
+
assertBoundedFrontmatterValue(meta.executionHostId, "Execution host IDs", EXECUTION_ID_RE);
|
|
248
|
+
assertBoundedFrontmatterValue(meta.executionEngine, "Execution engine IDs", EXECUTION_ID_RE);
|
|
249
|
+
assertBoundedFrontmatterValue(meta.remoteRequestId, "Remote request IDs", REMOTE_ID_RE);
|
|
250
|
+
assertBoundedFrontmatterValue(meta.remoteCommandId, "Remote command IDs", REMOTE_ID_RE);
|
|
251
|
+
assertBoundedFrontmatterValue(meta.remoteRunId, "Remote run IDs", REMOTE_ID_RE);
|
|
252
|
+
assertBoundedFrontmatterValue(meta.remoteAutomationRunId, "Remote automation run IDs", REMOTE_ID_RE);
|
|
253
|
+
if (meta.executionCwd !== undefined &&
|
|
254
|
+
(meta.executionCwd.length > 1024 || /[\r\n]/.test(meta.executionCwd))) {
|
|
255
|
+
throw new Error("Execution workspace paths must be at most 1024 characters.");
|
|
256
|
+
}
|
|
214
257
|
const lines = [
|
|
215
258
|
"---",
|
|
216
259
|
`schedule: ${JSON.stringify(meta.schedule)}`,
|
|
@@ -225,6 +268,16 @@ export function buildJobResourceContent(meta, body) {
|
|
|
225
268
|
pushString(lines, "domain", meta.domain);
|
|
226
269
|
pushString(lines, "appId", meta.appId);
|
|
227
270
|
pushString(lines, "delegatedPolicyId", meta.delegatedPolicyId);
|
|
271
|
+
pushString(lines, "executionHostId", meta.executionHostId);
|
|
272
|
+
pushString(lines, "executionEngine", meta.executionEngine);
|
|
273
|
+
pushString(lines, "executionCwd", meta.executionCwd);
|
|
274
|
+
pushString(lines, "remoteRequestId", meta.remoteRequestId);
|
|
275
|
+
pushString(lines, "remoteCommandId", meta.remoteCommandId);
|
|
276
|
+
pushString(lines, "remoteRunId", meta.remoteRunId);
|
|
277
|
+
pushString(lines, "remoteAutomationRunId", meta.remoteAutomationRunId);
|
|
278
|
+
if (meta.remoteAdvanceSchedule !== undefined) {
|
|
279
|
+
lines.push(`remoteAdvanceSchedule: ${meta.remoteAdvanceSchedule}`);
|
|
280
|
+
}
|
|
228
281
|
// Keep the long-standing human-readable owner shape used by existing
|
|
229
282
|
// resources and diagnostics; values that can contain free-form text use
|
|
230
283
|
// JSON quoting below.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { RemoteCommand } from "../integrations/remote-types.js";
|
|
2
|
+
import { type Resource } from "../resources/store.js";
|
|
3
|
+
import { type JobFrontmatter } from "./frontmatter.js";
|
|
4
|
+
/** A remote run is allowed to be long-lived, but never indefinitely invisible. */
|
|
5
|
+
export declare const REMOTE_AUTOMATION_MAX_ACTIVE_MS: number;
|
|
6
|
+
export type RemoteAutomationState = "not-remote" | "active" | "completed" | "failed";
|
|
7
|
+
export interface RemoteAutomationStatus {
|
|
8
|
+
state: RemoteAutomationState;
|
|
9
|
+
command?: RemoteCommand;
|
|
10
|
+
error?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface RemoteAutomationDispatchInput {
|
|
13
|
+
resource: Resource;
|
|
14
|
+
meta: JobFrontmatter;
|
|
15
|
+
body: string;
|
|
16
|
+
ownerEmail: string;
|
|
17
|
+
orgId?: string;
|
|
18
|
+
appId?: string;
|
|
19
|
+
prompt: string;
|
|
20
|
+
title: string;
|
|
21
|
+
historyId?: string;
|
|
22
|
+
/** Whether completing this remote dispatch should advance its cron schedule. */
|
|
23
|
+
advanceSchedule?: boolean;
|
|
24
|
+
now?: Date;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Queue one host-targeted automation. The request key is derived from the
|
|
28
|
+
* persisted start timestamp, so a worker crash between the queue write and
|
|
29
|
+
* resource bookkeeping cannot create a second remote run.
|
|
30
|
+
*/
|
|
31
|
+
export declare function dispatchRemoteAutomation(input: RemoteAutomationDispatchInput): Promise<{
|
|
32
|
+
command: RemoteCommand;
|
|
33
|
+
requestId: string;
|
|
34
|
+
automationRunId?: string;
|
|
35
|
+
}>;
|
|
36
|
+
export declare function getRemoteAutomationStatus(input: {
|
|
37
|
+
meta: JobFrontmatter;
|
|
38
|
+
ownerEmail: string;
|
|
39
|
+
orgId?: string;
|
|
40
|
+
now?: Date;
|
|
41
|
+
}): Promise<RemoteAutomationStatus>;
|
|
42
|
+
export declare function finishRemoteAutomationHistory(meta: Pick<JobFrontmatter, "remoteAutomationRunId">, state: "completed" | "failed", error?: string): Promise<void>;
|