@agent-native/core 0.157.8 → 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/README.md +1 -1
- package/corpus/templates/analytics/actions/purge-first-party-analytics-postgres.ts +26 -8
- package/corpus/templates/analytics/scripts/first-party-analytics-postgres-retention-dry-run.sql +261 -0
- package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +3 -1
- package/corpus/templates/analytics/server/lib/first-party-analytics-purge.ts +75 -31
- package/corpus/templates/analytics/server/lib/workspace-feature-flags.ts +4 -1
- 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 +1311 -121
- package/corpus/templates/slides/actions/import-file.ts +28 -1
- package/corpus/templates/slides/actions/import-pptx.ts +69 -42
- 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 +601 -30
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +18 -1
- package/corpus/templates/slides/server/handlers/import/html-converter.ts +1240 -22
- package/corpus/templates/slides/server/handlers/import/pdf-fidelity-parser.ts +329 -64
- package/corpus/templates/slides/server/handlers/import/pptx-assets.ts +24 -0
- package/corpus/templates/slides/server/lib/source-import.ts +10 -1
- package/corpus/templates/slides/shared/aspect-ratios.ts +13 -1
- package/dist/agent/harness/migrations.d.ts +4 -0
- package/dist/agent/harness/migrations.js +60 -0
- package/dist/agent/production-agent.d.ts +2 -0
- package/dist/agent/production-agent.js +15 -0
- package/dist/agent/run-loop-with-resume.d.ts +1 -0
- package/dist/agent/run-loop-with-resume.js +44 -12
- package/dist/agent/run-migrations.d.ts +8 -0
- package/dist/agent/run-migrations.js +61 -0
- package/dist/automations/service.d.ts +6 -0
- package/dist/automations/service.js +35 -0
- package/dist/chat-threads/migrations.d.ts +1 -0
- package/dist/chat-threads/migrations.js +1 -0
- package/dist/chat-threads/schema-migrations.d.ts +8 -0
- package/dist/chat-threads/schema-migrations.js +60 -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 +43 -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/message-components.d.ts +2 -0
- package/dist/client/chat/message-components.js +11 -1
- 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 +53 -26
- package/dist/client/observability/useObservability.d.ts +1 -1
- package/dist/client/observability/useObservability.js +6 -3
- 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/awareness.d.ts +2 -2
- package/dist/deploy/build.js +1 -1
- package/dist/feature-flags/a2a-action-route.js +4 -2
- package/dist/feature-flags/permissions.d.ts +2 -0
- package/dist/feature-flags/permissions.js +6 -1
- package/dist/ingestion/pptx.d.ts +92 -1
- package/dist/ingestion/pptx.js +1180 -115
- 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/localization/core-messages/ar-SA.js +4 -0
- package/dist/localization/core-messages/de-DE.js +4 -0
- package/dist/localization/core-messages/en-US.d.ts +4 -0
- package/dist/localization/core-messages/en-US.js +4 -0
- package/dist/localization/core-messages/es-ES.js +4 -0
- package/dist/localization/core-messages/fr-FR.js +4 -0
- package/dist/localization/core-messages/hi-IN.js +4 -0
- package/dist/localization/core-messages/ja-JP.js +4 -0
- package/dist/localization/core-messages/ko-KR.js +4 -0
- package/dist/localization/core-messages/pt-BR.js +4 -0
- package/dist/localization/core-messages/zh-CN.js +4 -0
- package/dist/localization/core-messages/zh-TW.js +4 -0
- package/dist/localization/core-messages.d.ts +4 -0
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/feedback.js +40 -16
- package/dist/observability/routes.d.ts +6 -6
- package/dist/observability/routes.js +15 -3
- package/dist/observability/store.js +2 -0
- package/dist/observability/traces.js +5 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +16 -16
- package/dist/provider-api/actions/provider-api.d.ts +13 -13
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/action-routes.d.ts +7 -5
- package/dist/server/action-routes.js +13 -6
- package/dist/server/agent-chat-plugin.js +6 -0
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/agent-run-context.d.ts +1 -0
- package/dist/server/agent-run-context.js +9 -0
- package/dist/server/better-auth-instance.js +4 -0
- package/dist/server/core-routes-plugin.js +8 -1
- package/dist/server/realtime-voice.js +2 -1
- package/dist/server/release-migrations.js +16 -0
- package/dist/server/request-context.d.ts +2 -0
- package/dist/server/transcribe-voice.d.ts +1 -1
- 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/dist/usage/migrations.d.ts +4 -0
- package/dist/usage/migrations.js +45 -0
- package/docs/content/template-content-developers.mdx +48 -0
- package/docs/content/toolkit-ui.mdx +57 -0
- package/package.json +3 -3
|
@@ -6,14 +6,25 @@ import { assertValidComputerCommandEnvelope } from "../integrations/computer-sup
|
|
|
6
6
|
import { serializeBoundedRemoteJson } from "../integrations/remote-json-safety.js";
|
|
7
7
|
import { executeDenyCodeAgentApproval, executePendingCodeAgentApproval, } from "./code-agent-executor.js";
|
|
8
8
|
import { appendCodeAgentTranscriptEvent, codeAgentRunTranscriptPath, codeAgentStoreRoot, createCodeAgentRunRecord, getCodeAgentRunRecord, listCodeAgentRunRecords, normalizeCodeAgentPermissionMode, queueCodeAgentFollowUp, updateCodeAgentRunRecord, } from "./code-agent-runs.js";
|
|
9
|
+
import { loadPortalEnvironment, preparePortalWorkspace, parsePortalHandoff, } from "./portal-workspace.js";
|
|
9
10
|
const DEVICE_PATH_ENV = "AGENT_NATIVE_REMOTE_DEVICE_PATH";
|
|
10
11
|
const COMPUTER_BRIDGE_URL_ENV = "AGENT_NATIVE_COMPUTER_BRIDGE_URL";
|
|
11
12
|
const COMPUTER_BRIDGE_TOKEN_ENV = "AGENT_NATIVE_COMPUTER_BRIDGE_TOKEN";
|
|
12
13
|
const COMPUTER_CAPABILITIES_ENV = "AGENT_NATIVE_COMPUTER_CAPABILITIES";
|
|
14
|
+
const REMOTE_ENGINES_ENV = "AGENT_NATIVE_REMOTE_ENGINES";
|
|
13
15
|
const DEFAULT_POLL_INTERVAL_MS = 2_000;
|
|
14
16
|
const MAX_POLL_INTERVAL_MS = 30_000;
|
|
15
17
|
const MAX_TRANSCRIPT_EVENTS_PER_BATCH = 50;
|
|
16
18
|
const activeRunners = new Map();
|
|
19
|
+
export const DEFAULT_REMOTE_EXECUTION_CAPABILITIES = {
|
|
20
|
+
backend: "desktop",
|
|
21
|
+
workloads: ["code-agent", "scheduled-code"],
|
|
22
|
+
engines: ["codex-cli", "claude-cli", "pi-cli", "opencode-cli"],
|
|
23
|
+
acceptsScheduledWork: true,
|
|
24
|
+
acceptsPortalHandoffs: true,
|
|
25
|
+
persistence: "local-files",
|
|
26
|
+
adapters: ["agent-native-code"],
|
|
27
|
+
};
|
|
17
28
|
export function remoteDeviceConfigPath() {
|
|
18
29
|
return path.resolve(process.env[DEVICE_PATH_ENV] ??
|
|
19
30
|
path.join(os.homedir(), ".agent-native", "remote-device.json"));
|
|
@@ -32,6 +43,7 @@ export function loadRemoteCodeAgentDeviceConfig(configPath = remoteDeviceConfigP
|
|
|
32
43
|
relayUrl: firstStringValue(raw.relayUrl, raw.url, raw.baseUrl),
|
|
33
44
|
deviceId: firstStringValue(raw.deviceId, raw.id),
|
|
34
45
|
deviceName: firstStringValue(raw.deviceName, raw.name),
|
|
46
|
+
workspacePath: firstStringValue(raw.workspacePath, raw.workspace, raw.cwd, raw.projectPath),
|
|
35
47
|
pollIntervalMs: Number.isFinite(pollIntervalMs) && pollIntervalMs > 0
|
|
36
48
|
? Math.min(Math.max(pollIntervalMs, 500), MAX_POLL_INTERVAL_MS)
|
|
37
49
|
: undefined,
|
|
@@ -77,6 +89,7 @@ class RemoteCodeAgentConnector {
|
|
|
77
89
|
output;
|
|
78
90
|
transcriptCursors = new Map();
|
|
79
91
|
remoteRunIds = new Set();
|
|
92
|
+
terminalCommandIds = new Set();
|
|
80
93
|
stopped = false;
|
|
81
94
|
computerBridge = loadLocalComputerBridgeConfig();
|
|
82
95
|
constructor(config, relayUrl, output) {
|
|
@@ -140,6 +153,7 @@ class RemoteCodeAgentConnector {
|
|
|
140
153
|
"run-events",
|
|
141
154
|
],
|
|
142
155
|
computerCapabilities: this.computerBridge?.capabilities ?? {},
|
|
156
|
+
executionCapabilities: remoteExecutionCapabilities(),
|
|
143
157
|
activeRunIds: Array.from(this.remoteRunIds),
|
|
144
158
|
});
|
|
145
159
|
const commands = normalizeCommands(response);
|
|
@@ -180,25 +194,65 @@ class RemoteCodeAgentConnector {
|
|
|
180
194
|
async computerOperation(command) {
|
|
181
195
|
return dispatchComputerOperationToLocalBridge(this.computerBridge, command.params.envelope);
|
|
182
196
|
}
|
|
183
|
-
createRun(command) {
|
|
197
|
+
async createRun(command) {
|
|
184
198
|
const prompt = firstTextValue(command.params.prompt, command.params.message, command.params.input);
|
|
185
199
|
if (!prompt) {
|
|
186
200
|
return { ok: false, error: "Missing prompt." };
|
|
187
201
|
}
|
|
188
202
|
const goalId = firstStringValue(command.params.goalId) ?? "task";
|
|
189
|
-
const
|
|
203
|
+
const metadata = isObject(command.params.metadata)
|
|
204
|
+
? command.params.metadata
|
|
205
|
+
: {};
|
|
206
|
+
let cwd = resolveCommandCwd(command.params.cwd ?? this.config.workspacePath);
|
|
207
|
+
let portalWorkspace;
|
|
208
|
+
let portalEnvironment;
|
|
209
|
+
if (metadata.portal !== undefined) {
|
|
210
|
+
try {
|
|
211
|
+
portalWorkspace = await preparePortalWorkspace({
|
|
212
|
+
handoff: parsePortalHandoff(metadata.portal),
|
|
213
|
+
workspacePath: this.config.workspacePath,
|
|
214
|
+
});
|
|
215
|
+
portalEnvironment = loadPortalEnvironment(portalWorkspace.environmentRoot);
|
|
216
|
+
cwd = portalWorkspace.workspacePath;
|
|
217
|
+
}
|
|
218
|
+
catch (error) {
|
|
219
|
+
return {
|
|
220
|
+
ok: false,
|
|
221
|
+
error: error instanceof Error ? error.message : String(error),
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
const requestedRunId = firstStringValue(command.params.runId);
|
|
226
|
+
if (portalWorkspace && requestedRunId && !isSafeRunId(requestedRunId)) {
|
|
227
|
+
return { ok: false, error: "Portal run id is invalid." };
|
|
228
|
+
}
|
|
229
|
+
if (portalWorkspace && requestedRunId) {
|
|
230
|
+
const existing = getCodeAgentRunRecord(requestedRunId);
|
|
231
|
+
const existingRemote = isObject(existing?.metadata?.remote)
|
|
232
|
+
? existing.metadata.remote
|
|
233
|
+
: {};
|
|
234
|
+
if (existing &&
|
|
235
|
+
firstStringValue(existingRemote.commandId) === command.id) {
|
|
236
|
+
this.remoteRunIds.add(existing.id);
|
|
237
|
+
this.transcriptCursors.set(existing.id, { offset: 0, seq: 0 });
|
|
238
|
+
if (!activeRunners.has(existing.id)) {
|
|
239
|
+
this.spawnRunner(existing.id, existing.cwd, existing.permissionMode, portalEnvironment?.values);
|
|
240
|
+
}
|
|
241
|
+
return { ok: true, runId: existing.id, run: existing, resumed: true };
|
|
242
|
+
}
|
|
243
|
+
}
|
|
190
244
|
const permissionMode = normalizeCodeAgentPermissionMode(command.params.permissionMode) ??
|
|
191
245
|
"full-auto";
|
|
192
246
|
const engine = firstStringValue(command.params.engine);
|
|
193
247
|
const model = firstStringValue(command.params.model);
|
|
194
248
|
const effort = firstStringValue(command.params.effort, command.params.reasoningEffort);
|
|
195
|
-
const metadata = isObject(command.params.metadata)
|
|
196
|
-
? command.params.metadata
|
|
197
|
-
: {};
|
|
198
249
|
const run = createCodeAgentRunRecord({
|
|
250
|
+
...(portalWorkspace && requestedRunId ? { id: requestedRunId } : {}),
|
|
199
251
|
goalId,
|
|
200
252
|
title: firstStringValue(command.params.title) ?? titleFromPrompt(prompt),
|
|
201
|
-
subtitle:
|
|
253
|
+
subtitle: portalWorkspace
|
|
254
|
+
? `Portal on ${this.config.deviceName ?? "paired computer"}`
|
|
255
|
+
: "Remote coding task",
|
|
202
256
|
status: "queued",
|
|
203
257
|
phase: "queued",
|
|
204
258
|
permissionMode,
|
|
@@ -213,6 +267,20 @@ class RemoteCodeAgentConnector {
|
|
|
213
267
|
{ label: "Prompt", value: truncateForDisplay(prompt, 160) },
|
|
214
268
|
{ label: "Agent", value: "Remote connector" },
|
|
215
269
|
{ label: "Mode", value: permissionMode },
|
|
270
|
+
...(portalWorkspace
|
|
271
|
+
? [
|
|
272
|
+
{
|
|
273
|
+
label: "Workspace",
|
|
274
|
+
value: `Portal · ${portalWorkspace.workspacePath}`,
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
label: "Environment",
|
|
278
|
+
value: portalEnvironment?.files.length
|
|
279
|
+
? `Loaded ${portalEnvironment.files.join(", ")}`
|
|
280
|
+
: "Using paired computer environment",
|
|
281
|
+
},
|
|
282
|
+
]
|
|
283
|
+
: []),
|
|
216
284
|
],
|
|
217
285
|
metadata: {
|
|
218
286
|
...metadata,
|
|
@@ -225,7 +293,26 @@ class RemoteCodeAgentConnector {
|
|
|
225
293
|
commandId: command.id,
|
|
226
294
|
deviceId: this.config.deviceId,
|
|
227
295
|
relayUrl: this.relayUrl,
|
|
296
|
+
...(portalWorkspace ? { remoteRunId: run.id } : {}),
|
|
228
297
|
},
|
|
298
|
+
...(portalWorkspace
|
|
299
|
+
? {
|
|
300
|
+
portal: portalWorkspace.handoff,
|
|
301
|
+
executionResidence: {
|
|
302
|
+
schemaVersion: 1,
|
|
303
|
+
kind: "portal",
|
|
304
|
+
hostId: this.config.deviceId,
|
|
305
|
+
hostLabel: this.config.deviceName ?? os.hostname(),
|
|
306
|
+
workspacePath: portalWorkspace.workspacePath,
|
|
307
|
+
sourceBranch: portalWorkspace.handoff.sourceBranch,
|
|
308
|
+
sourceCommit: portalWorkspace.handoff.commit,
|
|
309
|
+
handoffId: portalWorkspace.handoff.handoffId,
|
|
310
|
+
envPolicy: portalWorkspace.handoff.envPolicy,
|
|
311
|
+
environmentFiles: portalEnvironment?.files ?? [],
|
|
312
|
+
environmentLoadedAt: new Date().toISOString(),
|
|
313
|
+
},
|
|
314
|
+
}
|
|
315
|
+
: {}),
|
|
229
316
|
},
|
|
230
317
|
});
|
|
231
318
|
appendCodeAgentTranscriptEvent({
|
|
@@ -242,7 +329,7 @@ class RemoteCodeAgentConnector {
|
|
|
242
329
|
});
|
|
243
330
|
this.remoteRunIds.add(run.id);
|
|
244
331
|
this.transcriptCursors.set(run.id, { offset: 0, seq: 0 });
|
|
245
|
-
this.spawnRunner(run.id, cwd, permissionMode);
|
|
332
|
+
this.spawnRunner(run.id, cwd, permissionMode, portalEnvironment?.values);
|
|
246
333
|
return { ok: true, runId: run.id, run };
|
|
247
334
|
}
|
|
248
335
|
appendFollowUp(command) {
|
|
@@ -424,7 +511,7 @@ class RemoteCodeAgentConnector {
|
|
|
424
511
|
activeRunIds: Array.from(activeRunners.keys()),
|
|
425
512
|
};
|
|
426
513
|
}
|
|
427
|
-
spawnRunner(runId, cwd, permissionMode) {
|
|
514
|
+
spawnRunner(runId, cwd, permissionMode, portalEnvironment) {
|
|
428
515
|
if (activeRunners.has(runId))
|
|
429
516
|
return;
|
|
430
517
|
const invocation = resolveCodeAgentCliInvocation();
|
|
@@ -434,6 +521,7 @@ class RemoteCodeAgentConnector {
|
|
|
434
521
|
stdio: ["ignore", "pipe", "pipe"],
|
|
435
522
|
env: {
|
|
436
523
|
...process.env,
|
|
524
|
+
...portalEnvironment,
|
|
437
525
|
AGENT_NATIVE_CODE_AGENTS_HOME: codeAgentStoreRoot(),
|
|
438
526
|
...(permissionMode
|
|
439
527
|
? { AGENT_NATIVE_CODE_AGENT_PERMISSION_MODE: permissionMode }
|
|
@@ -484,28 +572,50 @@ class RemoteCodeAgentConnector {
|
|
|
484
572
|
for (const runId of this.remoteRunIds) {
|
|
485
573
|
const cursor = this.transcriptCursors.get(runId) ?? { offset: 0, seq: 0 };
|
|
486
574
|
const batch = readTranscriptBatch(runId, cursor.offset);
|
|
487
|
-
if (batch.events.length
|
|
488
|
-
this.
|
|
489
|
-
|
|
490
|
-
|
|
575
|
+
if (batch.events.length > 0) {
|
|
576
|
+
await this.postJson("/_agent-native/integrations/remote/run-events", {
|
|
577
|
+
deviceId: this.config.deviceId,
|
|
578
|
+
remoteRunId: runId,
|
|
579
|
+
cursor: { offset: batch.nextOffset },
|
|
580
|
+
events: batch.events.map((event, index) => ({
|
|
581
|
+
seq: cursor.seq + index,
|
|
582
|
+
event,
|
|
583
|
+
})),
|
|
491
584
|
});
|
|
492
|
-
continue;
|
|
493
585
|
}
|
|
494
|
-
await this.postJson("/_agent-native/integrations/remote/run-events", {
|
|
495
|
-
deviceId: this.config.deviceId,
|
|
496
|
-
remoteRunId: runId,
|
|
497
|
-
cursor: { offset: batch.nextOffset },
|
|
498
|
-
events: batch.events.map((event, index) => ({
|
|
499
|
-
seq: cursor.seq + index,
|
|
500
|
-
event,
|
|
501
|
-
})),
|
|
502
|
-
});
|
|
503
586
|
this.transcriptCursors.set(runId, {
|
|
504
587
|
offset: batch.nextOffset,
|
|
505
588
|
seq: cursor.seq + batch.events.length,
|
|
506
589
|
});
|
|
590
|
+
await this.reportRemoteRunTerminal(runId);
|
|
507
591
|
}
|
|
508
592
|
}
|
|
593
|
+
async reportRemoteRunTerminal(runId) {
|
|
594
|
+
const run = getCodeAgentRunRecord(runId);
|
|
595
|
+
if (!run || (run.status !== "completed" && run.status !== "errored")) {
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
598
|
+
const remote = isObject(run.metadata?.remote) ? run.metadata.remote : {};
|
|
599
|
+
const commandId = firstStringValue(remote.commandId);
|
|
600
|
+
if (!commandId || this.terminalCommandIds.has(commandId))
|
|
601
|
+
return;
|
|
602
|
+
const errorMessage = firstStringValue(run.metadata?.error, run.metadata?.runnerError);
|
|
603
|
+
await this.postJson("/_agent-native/integrations/remote/result", {
|
|
604
|
+
deviceId: this.config.deviceId,
|
|
605
|
+
commandId,
|
|
606
|
+
kind: "create-run",
|
|
607
|
+
ok: run.status === "completed",
|
|
608
|
+
status: run.status === "completed" ? "completed" : "failed",
|
|
609
|
+
result: {
|
|
610
|
+
ok: run.status === "completed",
|
|
611
|
+
run,
|
|
612
|
+
},
|
|
613
|
+
errorMessage: run.status === "errored"
|
|
614
|
+
? (errorMessage ?? "Remote code-agent run failed.")
|
|
615
|
+
: undefined,
|
|
616
|
+
});
|
|
617
|
+
this.terminalCommandIds.add(commandId);
|
|
618
|
+
}
|
|
509
619
|
async postCommandResult(command, result) {
|
|
510
620
|
let safeResult;
|
|
511
621
|
try {
|
|
@@ -519,12 +629,17 @@ class RemoteCodeAgentConnector {
|
|
|
519
629
|
: "Connector result rejected as unsafe.",
|
|
520
630
|
};
|
|
521
631
|
}
|
|
632
|
+
const isAcceptedCreateRun = normalizeKind(command.kind) === "create-run" && safeResult.ok !== false;
|
|
522
633
|
await this.postJson("/_agent-native/integrations/remote/result", {
|
|
523
634
|
deviceId: this.config.deviceId,
|
|
524
635
|
commandId: command.id,
|
|
525
636
|
kind: command.kind,
|
|
526
637
|
ok: safeResult.ok !== false,
|
|
527
|
-
status: safeResult.ok === false
|
|
638
|
+
status: safeResult.ok === false
|
|
639
|
+
? "failed"
|
|
640
|
+
: isAcceptedCreateRun
|
|
641
|
+
? "running"
|
|
642
|
+
: "completed",
|
|
528
643
|
result: safeResult,
|
|
529
644
|
errorMessage: typeof safeResult.error === "string" ? safeResult.error : undefined,
|
|
530
645
|
});
|
|
@@ -535,6 +650,7 @@ class RemoteCodeAgentConnector {
|
|
|
535
650
|
method: "POST",
|
|
536
651
|
headers: {
|
|
537
652
|
authorization: `Bearer ${this.config.token}`,
|
|
653
|
+
"x-agent-native-device-token": this.config.token,
|
|
538
654
|
"content-type": "application/json",
|
|
539
655
|
},
|
|
540
656
|
body: JSON.stringify(body),
|
|
@@ -712,6 +828,21 @@ function resolveCommandCwd(value) {
|
|
|
712
828
|
const cwd = firstStringValue(value);
|
|
713
829
|
return cwd ? path.resolve(cwd) : process.cwd();
|
|
714
830
|
}
|
|
831
|
+
function isSafeRunId(value) {
|
|
832
|
+
return value.length <= 160 && /^[A-Za-z0-9][A-Za-z0-9._:-]*$/.test(value);
|
|
833
|
+
}
|
|
834
|
+
function remoteExecutionCapabilities() {
|
|
835
|
+
const configured = process.env[REMOTE_ENGINES_ENV]
|
|
836
|
+
?.split(",")
|
|
837
|
+
.map((engine) => engine.trim())
|
|
838
|
+
.filter(Boolean);
|
|
839
|
+
return {
|
|
840
|
+
...DEFAULT_REMOTE_EXECUTION_CAPABILITIES,
|
|
841
|
+
...(configured?.length
|
|
842
|
+
? { engines: [...new Set(configured)].slice(0, 32) }
|
|
843
|
+
: {}),
|
|
844
|
+
};
|
|
845
|
+
}
|
|
715
846
|
function normalizeRelayUrl(value) {
|
|
716
847
|
if (!value)
|
|
717
848
|
return null;
|
|
@@ -485,6 +485,7 @@ async function executeClaudeCliRun(options) {
|
|
|
485
485
|
const streamToolOutputToStdout = options.streamToolOutputToStdout ??
|
|
486
486
|
process.env.AGENT_NATIVE_CODE_AGENT_STRUCTURED_STDOUT !== "1";
|
|
487
487
|
const assistantText = [];
|
|
488
|
+
const claudeToolNames = new Map();
|
|
488
489
|
appendCodeAgentTranscriptEvent({
|
|
489
490
|
runId: options.run.id,
|
|
490
491
|
kind: "status",
|
|
@@ -511,7 +512,7 @@ async function executeClaudeCliRun(options) {
|
|
|
511
512
|
effort: reasoningEffort,
|
|
512
513
|
signal: options.signal,
|
|
513
514
|
onEvent: (event) => {
|
|
514
|
-
const text =
|
|
515
|
+
const text = appendClaudeParticipantTranscriptEvents(options.run.id, event, claudeToolNames);
|
|
515
516
|
if (!text)
|
|
516
517
|
return;
|
|
517
518
|
assistantText.push(text);
|
|
@@ -667,19 +668,246 @@ function normalizeClaudeCliModel(model) {
|
|
|
667
668
|
function normalizeClaudeCliEffort(effort) {
|
|
668
669
|
return effort && effort !== "auto" ? effort : undefined;
|
|
669
670
|
}
|
|
670
|
-
function
|
|
671
|
-
|
|
672
|
-
return null;
|
|
671
|
+
function appendClaudeParticipantTranscriptEvents(runId, event, toolNames) {
|
|
672
|
+
const eventType = cliStringValue(event.type);
|
|
673
673
|
const message = asRecordValue(event.message);
|
|
674
|
-
const
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
const
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
674
|
+
const messageContent = Array.isArray(message?.content)
|
|
675
|
+
? message.content.filter((part) => isRecordValue(part))
|
|
676
|
+
: [];
|
|
677
|
+
const content = messageContent.length > 0
|
|
678
|
+
? messageContent
|
|
679
|
+
: eventType === "tool_use" || eventType === "tool_result"
|
|
680
|
+
? [event]
|
|
681
|
+
: [];
|
|
682
|
+
let assistantText = "";
|
|
683
|
+
for (const part of content) {
|
|
684
|
+
const partType = cliStringValue(part.type);
|
|
685
|
+
if (eventType === "assistant" && partType === "text") {
|
|
686
|
+
const text = typeof part.text === "string" ? part.text : "";
|
|
687
|
+
if (!text.trim())
|
|
688
|
+
continue;
|
|
689
|
+
assistantText += text;
|
|
690
|
+
appendCodeAgentTranscriptEvent({
|
|
691
|
+
runId,
|
|
692
|
+
kind: "system",
|
|
693
|
+
message: text,
|
|
694
|
+
metadata: { role: "assistant", source: "claude-cli" },
|
|
695
|
+
});
|
|
696
|
+
continue;
|
|
697
|
+
}
|
|
698
|
+
if (eventType === "assistant" && partType === "thinking") {
|
|
699
|
+
const thinking = cliTranscriptValue(part.thinking ?? part.text);
|
|
700
|
+
if (!thinking)
|
|
701
|
+
continue;
|
|
702
|
+
appendCodeAgentTranscriptEvent({
|
|
703
|
+
runId,
|
|
704
|
+
kind: "status",
|
|
705
|
+
message: thinking,
|
|
706
|
+
metadata: { type: "thinking", source: "claude-cli" },
|
|
707
|
+
});
|
|
708
|
+
continue;
|
|
709
|
+
}
|
|
710
|
+
if (partType === "tool_use") {
|
|
711
|
+
const toolName = cliStringValue(part.name) ?? cliStringValue(part.tool_name) ?? "tool";
|
|
712
|
+
const toolCallId = cliStringValue(part.id);
|
|
713
|
+
if (toolCallId)
|
|
714
|
+
toolNames.set(toolCallId, toolName);
|
|
715
|
+
appendCodeAgentTranscriptEvent({
|
|
716
|
+
runId,
|
|
717
|
+
kind: "status",
|
|
718
|
+
message: `Running ${toolName}.`,
|
|
719
|
+
metadata: {
|
|
720
|
+
type: "tool_start",
|
|
721
|
+
tool: toolName,
|
|
722
|
+
input: part.input ?? {},
|
|
723
|
+
...(toolCallId ? { toolCallId } : {}),
|
|
724
|
+
source: "claude-cli",
|
|
725
|
+
},
|
|
726
|
+
});
|
|
727
|
+
continue;
|
|
728
|
+
}
|
|
729
|
+
if (partType === "tool_result") {
|
|
730
|
+
const toolCallId = cliStringValue(part.tool_use_id);
|
|
731
|
+
const toolName = cliStringValue(part.tool_name) ??
|
|
732
|
+
(toolCallId ? toolNames.get(toolCallId) : undefined) ??
|
|
733
|
+
"tool";
|
|
734
|
+
const result = cliTranscriptValue(part.content ?? part.output ?? part.result ?? "");
|
|
735
|
+
appendCodeAgentTranscriptEvent({
|
|
736
|
+
runId,
|
|
737
|
+
kind: "status",
|
|
738
|
+
message: `Finished ${toolName}.`,
|
|
739
|
+
metadata: {
|
|
740
|
+
type: "tool_done",
|
|
741
|
+
tool: toolName,
|
|
742
|
+
result,
|
|
743
|
+
...(part.is_error === true ? { isError: true } : {}),
|
|
744
|
+
...(toolCallId ? { toolCallId } : {}),
|
|
745
|
+
source: "claude-cli",
|
|
746
|
+
},
|
|
747
|
+
});
|
|
748
|
+
if (toolCallId)
|
|
749
|
+
toolNames.delete(toolCallId);
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
return assistantText.trim() || null;
|
|
753
|
+
}
|
|
754
|
+
function appendCodexCliTranscriptEvent(runId, event) {
|
|
755
|
+
const eventType = cliStringValue(event.type);
|
|
756
|
+
const item = asRecordValue(event.item);
|
|
757
|
+
if (eventType?.startsWith("item.") && item) {
|
|
758
|
+
const itemType = cliStringValue(item.type);
|
|
759
|
+
if (itemType === "agent_message") {
|
|
760
|
+
const text = cliStringValue(item.text ?? item.message ?? item.content);
|
|
761
|
+
if (text) {
|
|
762
|
+
appendCodeAgentTranscriptEvent({
|
|
763
|
+
runId,
|
|
764
|
+
kind: "system",
|
|
765
|
+
message: text,
|
|
766
|
+
metadata: {
|
|
767
|
+
role: "assistant",
|
|
768
|
+
source: "codex-cli",
|
|
769
|
+
cliEventType: eventType,
|
|
770
|
+
itemType,
|
|
771
|
+
},
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
return;
|
|
775
|
+
}
|
|
776
|
+
if (itemType === "reasoning") {
|
|
777
|
+
const text = cliTranscriptValue(item.text ?? item.summary);
|
|
778
|
+
if (text) {
|
|
779
|
+
appendCodeAgentTranscriptEvent({
|
|
780
|
+
runId,
|
|
781
|
+
kind: "status",
|
|
782
|
+
message: text,
|
|
783
|
+
metadata: {
|
|
784
|
+
type: "thinking",
|
|
785
|
+
source: "codex-cli",
|
|
786
|
+
cliEventType: eventType,
|
|
787
|
+
itemType,
|
|
788
|
+
},
|
|
789
|
+
});
|
|
790
|
+
}
|
|
791
|
+
return;
|
|
792
|
+
}
|
|
793
|
+
const toolName = codexCliToolName(itemType, item);
|
|
794
|
+
if (!toolName)
|
|
795
|
+
return;
|
|
796
|
+
const toolCallId = cliStringValue(item.id);
|
|
797
|
+
const metadata = {
|
|
798
|
+
source: "codex-cli",
|
|
799
|
+
cliEventType: eventType,
|
|
800
|
+
itemType,
|
|
801
|
+
tool: toolName,
|
|
802
|
+
...(toolCallId ? { toolCallId } : {}),
|
|
803
|
+
};
|
|
804
|
+
if (eventType === "item.started") {
|
|
805
|
+
appendCodeAgentTranscriptEvent({
|
|
806
|
+
runId,
|
|
807
|
+
kind: "status",
|
|
808
|
+
message: `Running ${toolName}.`,
|
|
809
|
+
metadata: {
|
|
810
|
+
...metadata,
|
|
811
|
+
type: "tool_start",
|
|
812
|
+
input: codexCliToolInput(item),
|
|
813
|
+
},
|
|
814
|
+
});
|
|
815
|
+
}
|
|
816
|
+
else if (eventType === "item.completed") {
|
|
817
|
+
appendCodeAgentTranscriptEvent({
|
|
818
|
+
runId,
|
|
819
|
+
kind: "status",
|
|
820
|
+
message: `Finished ${toolName}.`,
|
|
821
|
+
metadata: {
|
|
822
|
+
...metadata,
|
|
823
|
+
type: "tool_done",
|
|
824
|
+
result: cliTranscriptValue(codexCliToolResult(item)),
|
|
825
|
+
},
|
|
826
|
+
});
|
|
827
|
+
}
|
|
828
|
+
else {
|
|
829
|
+
appendCodeAgentTranscriptEvent({
|
|
830
|
+
runId,
|
|
831
|
+
kind: "status",
|
|
832
|
+
message: `Working with ${toolName}.`,
|
|
833
|
+
metadata: { ...metadata, type: "activity" },
|
|
834
|
+
});
|
|
835
|
+
}
|
|
836
|
+
return;
|
|
837
|
+
}
|
|
838
|
+
if (eventType === "error" || eventType === "turn.failed") {
|
|
839
|
+
const message = cliStringValue(event.message ?? event.error) ?? eventType;
|
|
840
|
+
appendCodeAgentTranscriptEvent({
|
|
841
|
+
runId,
|
|
842
|
+
kind: "status",
|
|
843
|
+
message,
|
|
844
|
+
metadata: { type: "error", source: "codex-cli", cliEventType: eventType },
|
|
845
|
+
});
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
function codexCliToolName(itemType, item) {
|
|
849
|
+
switch (itemType) {
|
|
850
|
+
case "command_execution":
|
|
851
|
+
return "bash";
|
|
852
|
+
case "file_change":
|
|
853
|
+
return "file-change";
|
|
854
|
+
case "mcp_tool_call":
|
|
855
|
+
return (cliStringValue(item.name) ?? cliStringValue(item.tool) ?? "mcp-tool");
|
|
856
|
+
case "web_search":
|
|
857
|
+
return "web-search";
|
|
858
|
+
case "function_call":
|
|
859
|
+
return cliStringValue(item.name) ?? "function";
|
|
860
|
+
default:
|
|
861
|
+
return itemType?.includes("tool")
|
|
862
|
+
? (cliStringValue(item.name) ?? itemType)
|
|
863
|
+
: null;
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
function codexCliToolInput(item) {
|
|
867
|
+
if (typeof item.command === "string")
|
|
868
|
+
return { command: item.command };
|
|
869
|
+
return item.arguments ?? item.input ?? item.params ?? item.changes ?? {};
|
|
870
|
+
}
|
|
871
|
+
function codexCliToolResult(item) {
|
|
872
|
+
return (item.aggregated_output ??
|
|
873
|
+
item.output ??
|
|
874
|
+
item.result ??
|
|
875
|
+
item.error ??
|
|
876
|
+
item.content ??
|
|
877
|
+
item.status ??
|
|
878
|
+
"");
|
|
879
|
+
}
|
|
880
|
+
function consumeCodexJsonLine(line, onEvent) {
|
|
881
|
+
const trimmed = line.trim();
|
|
882
|
+
if (!trimmed.startsWith("{"))
|
|
883
|
+
return;
|
|
884
|
+
try {
|
|
885
|
+
const parsed = JSON.parse(trimmed);
|
|
886
|
+
if (isRecordValue(parsed))
|
|
887
|
+
onEvent?.(parsed);
|
|
888
|
+
}
|
|
889
|
+
catch {
|
|
890
|
+
// coercion-ok: older CLI output is intentionally ignored when it is not JSON.
|
|
891
|
+
// Older Codex versions and test doubles can still emit human-readable
|
|
892
|
+
// stdout. Keep that output available to terminal callers without making
|
|
893
|
+
// the structured transcript parser fail the run.
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
function cliStringValue(value) {
|
|
897
|
+
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
898
|
+
}
|
|
899
|
+
function cliTranscriptValue(value) {
|
|
900
|
+
if (typeof value === "string") {
|
|
901
|
+
return truncateBashOutput(truncateCodingOutput(value, MAX_TOOL_OUTPUT_CHARS));
|
|
902
|
+
}
|
|
903
|
+
if (value == null)
|
|
904
|
+
return "";
|
|
905
|
+
try {
|
|
906
|
+
return truncateBashOutput(truncateCodingOutput(JSON.stringify(value) ?? "", MAX_TOOL_OUTPUT_CHARS));
|
|
907
|
+
}
|
|
908
|
+
catch {
|
|
909
|
+
return truncateCodingOutput(String(value), MAX_TOOL_OUTPUT_CHARS);
|
|
910
|
+
}
|
|
683
911
|
}
|
|
684
912
|
function readClaudeParticipantResultText(events) {
|
|
685
913
|
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
@@ -1006,6 +1234,7 @@ async function executeCodexCliRun(options) {
|
|
|
1006
1234
|
"exec",
|
|
1007
1235
|
"--color",
|
|
1008
1236
|
"never",
|
|
1237
|
+
"--json",
|
|
1009
1238
|
"--skip-git-repo-check",
|
|
1010
1239
|
"--ignore-user-config",
|
|
1011
1240
|
"--output-last-message",
|
|
@@ -1034,6 +1263,9 @@ async function executeCodexCliRun(options) {
|
|
|
1034
1263
|
prompt: buildCodexCliPrompt(options.run, options.prompt),
|
|
1035
1264
|
stdout: options.stdout,
|
|
1036
1265
|
streamToolOutputToStdout,
|
|
1266
|
+
onEvent: (event) => {
|
|
1267
|
+
appendCodexCliTranscriptEvent(options.run.id, event);
|
|
1268
|
+
},
|
|
1037
1269
|
signal: options.signal,
|
|
1038
1270
|
});
|
|
1039
1271
|
if (result.exitCode !== 0) {
|
|
@@ -1163,6 +1395,7 @@ function runCodexCliProcess(options) {
|
|
|
1163
1395
|
return new Promise((resolve) => {
|
|
1164
1396
|
let stdout = "";
|
|
1165
1397
|
let stderr = "";
|
|
1398
|
+
let pendingJson = "";
|
|
1166
1399
|
let settled = false;
|
|
1167
1400
|
const child = spawn("codex", options.args, {
|
|
1168
1401
|
cwd: options.cwd,
|
|
@@ -1188,6 +1421,13 @@ function runCodexCliProcess(options) {
|
|
|
1188
1421
|
child.stdout?.on("data", (chunk) => {
|
|
1189
1422
|
const text = chunk.toString();
|
|
1190
1423
|
stdout += text;
|
|
1424
|
+
pendingJson += text;
|
|
1425
|
+
let newline = pendingJson.indexOf("\n");
|
|
1426
|
+
while (newline !== -1) {
|
|
1427
|
+
consumeCodexJsonLine(pendingJson.slice(0, newline), options.onEvent);
|
|
1428
|
+
pendingJson = pendingJson.slice(newline + 1);
|
|
1429
|
+
newline = pendingJson.indexOf("\n");
|
|
1430
|
+
}
|
|
1191
1431
|
if (options.streamToolOutputToStdout ?? true) {
|
|
1192
1432
|
options.stdout?.write(text);
|
|
1193
1433
|
}
|
|
@@ -1207,6 +1447,8 @@ function runCodexCliProcess(options) {
|
|
|
1207
1447
|
});
|
|
1208
1448
|
});
|
|
1209
1449
|
child.on("exit", (exitCode, exitSignal) => {
|
|
1450
|
+
if (pendingJson.trim())
|
|
1451
|
+
consumeCodexJsonLine(pendingJson, options.onEvent);
|
|
1210
1452
|
finish({ exitCode, exitSignal });
|
|
1211
1453
|
});
|
|
1212
1454
|
child.stdin?.end(options.prompt);
|
|
@@ -65,7 +65,8 @@ export function isCodeAgentCommandAllowed(command) {
|
|
|
65
65
|
}
|
|
66
66
|
export function createCodeAgentRunRecord(input) {
|
|
67
67
|
const now = new Date().toISOString();
|
|
68
|
-
const id =
|
|
68
|
+
const id = input.id ??
|
|
69
|
+
`${input.goalId}-${timestampSlug(now)}-${crypto.randomUUID().slice(0, 8)}`;
|
|
69
70
|
const record = {
|
|
70
71
|
schemaVersion: 1,
|
|
71
72
|
id,
|