remote-codex 0.11.19 → 0.11.20
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/apps/relay-server/dist/index.js +48 -1
- package/apps/supervisor-api/dist/index.js +27939 -4
- package/apps/supervisor-web/dist/assets/index-BdeDlmJY.js +5 -0
- package/apps/supervisor-web/dist/assets/index-BmBS1Wzk.css +1 -0
- package/apps/supervisor-web/dist/assets/thread-ui-C5nUCiEf.js +3624 -0
- package/apps/supervisor-web/dist/index.html +3 -3
- package/package.json +1 -39
- package/packages/agent-runtime/src/types.ts +3 -1
- package/packages/claude/src/runtimeAdapter.test.ts +25 -0
- package/packages/claude/src/runtimeAdapter.ts +85 -24
- package/packages/codex/src/appServerManager.test.ts +2 -1
- package/packages/codex/src/historyItems.test.ts +26 -0
- package/packages/codex/src/historyItems.ts +17 -0
- package/packages/codex/src/runtimeAdapter.ts +1 -1
- package/packages/db/src/repositories.ts +0 -123
- package/packages/db/src/schema.ts +1 -334
- package/packages/opencode/src/runtimeAdapter.test.ts +13 -1
- package/packages/opencode/src/runtimeAdapter.ts +1 -2
- package/packages/shared/src/index.ts +4 -1
- package/apps/supervisor-api/dist/chunk-IZBNFCMP.js +0 -29346
- package/apps/supervisor-api/dist/worker-index.d.ts +0 -2
- package/apps/supervisor-api/dist/worker-index.js +0 -197
- package/apps/supervisor-web/dist/assets/index-CUnlRID-.js +0 -6
- package/apps/supervisor-web/dist/assets/index-D3I41SIH.css +0 -1
- package/apps/supervisor-web/dist/assets/thread-ui-TBhog-RK.js +0 -3614
- package/packages/db/migrations/0018_control_plane.sql +0 -129
- package/packages/db/migrations/0019_control_plane_projects.sql +0 -19
- package/packages/db/migrations/0020_control_workspace_status.sql +0 -1
- package/packages/db/migrations/0021_control_sandbox_lifecycle_fields.sql +0 -3
- package/packages/db/migrations/0022_control_sandbox_resource_profile.sql +0 -1
- package/packages/db/migrations/0023_control_usage_import_state.sql +0 -18
- package/packages/db/migrations/0024_control_auth.sql +0 -23
- package/packages/db/migrations/0025_control_harness_credentials.sql +0 -29
- package/packages/db/migrations/0026_control_harness_usage_events.sql +0 -27
- package/packages/db/migrations/0027_harness_job_watches.sql +0 -24
|
@@ -4,16 +4,16 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>Remote Codex Supervisor</title>
|
|
7
|
-
<script type="module" crossorigin src="/assets/index-
|
|
7
|
+
<script type="module" crossorigin src="/assets/index-BdeDlmJY.js"></script>
|
|
8
8
|
<link rel="modulepreload" crossorigin href="/assets/react-vendor-CgLzZcV4.js">
|
|
9
9
|
<link rel="modulepreload" crossorigin href="/assets/ui-vendor-CeKGesq3.js">
|
|
10
10
|
<link rel="modulepreload" crossorigin href="/assets/graph-vendor-DVPtkh3h.js">
|
|
11
11
|
<link rel="modulepreload" crossorigin href="/assets/terminal-vendor-B365Go3Z.js">
|
|
12
12
|
<link rel="modulepreload" crossorigin href="/assets/markdown-vendor-BQJfKm05.js">
|
|
13
|
-
<link rel="modulepreload" crossorigin href="/assets/thread-ui-
|
|
13
|
+
<link rel="modulepreload" crossorigin href="/assets/thread-ui-C5nUCiEf.js">
|
|
14
14
|
<link rel="stylesheet" crossorigin href="/assets/graph-vendor-C5ap-Sga.css">
|
|
15
15
|
<link rel="stylesheet" crossorigin href="/assets/terminal-vendor-Beg8tuEN.css">
|
|
16
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
16
|
+
<link rel="stylesheet" crossorigin href="/assets/index-BmBS1Wzk.css">
|
|
17
17
|
</head>
|
|
18
18
|
<body class="bg-stone-950">
|
|
19
19
|
<div id="root"></div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "remote-codex",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.20",
|
|
4
4
|
"description": "Local web supervisor for Codex workspaces and threads.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -40,44 +40,8 @@
|
|
|
40
40
|
},
|
|
41
41
|
"scripts": {
|
|
42
42
|
"dev": "concurrently -k -n api,web -c blue,green \"pnpm --filter @remote-codex/supervisor-api dev\" \"pnpm --filter @remote-codex/supervisor-web dev\"",
|
|
43
|
-
"dev:control-plane": "pnpm --filter @remote-codex/control-plane-api dev",
|
|
44
43
|
"dev:stop": "node scripts/stop-dev-servers.mjs",
|
|
45
44
|
"dev:reset": "pnpm dev:stop && pnpm dev",
|
|
46
|
-
"smoke:local-route-token": "tsx scripts/local-route-token-smoke.ts",
|
|
47
|
-
"smoke:local-worker-checkpoint": "tsx scripts/local-worker-checkpoint-smoke.ts",
|
|
48
|
-
"smoke:production-auth": "tsx scripts/local-production-auth-smoke.ts",
|
|
49
|
-
"smoke:harness-admin-contract": "tsx scripts/harness-admin-contract-smoke.ts",
|
|
50
|
-
"smoke:harness-k8s-secret": "tsx scripts/harness-k8s-secret-smoke.ts",
|
|
51
|
-
"smoke:provider-gateway": "tsx scripts/provider-gateway-smoke.ts",
|
|
52
|
-
"smoke:staging-phase-one": "tsx scripts/staging-phase-one-smoke.ts",
|
|
53
|
-
"collect:harness-integration-evidence": "tsx scripts/collect-harness-integration-evidence.ts",
|
|
54
|
-
"collect:aws-staging-preflight-evidence": "tsx scripts/collect-aws-staging-preflight-evidence.ts",
|
|
55
|
-
"collect:phase-zero-six-evidence": "tsx scripts/run-phase-zero-six-staging-evidence.ts",
|
|
56
|
-
"verify:aws-staging-preflight-evidence": "tsx scripts/verify-aws-staging-preflight-evidence.ts",
|
|
57
|
-
"verify:staging-phase-one-evidence": "tsx scripts/verify-staging-phase-one-evidence.ts",
|
|
58
|
-
"verify:phase-zero-six-env-ready": "tsx scripts/verify-phase-zero-six-env-ready.ts",
|
|
59
|
-
"verify:phase-zero-six-evidence": "tsx scripts/verify-phase-zero-six-evidence.ts",
|
|
60
|
-
"verify:harness-integration-evidence": "tsx scripts/verify-harness-integration-evidence.ts",
|
|
61
|
-
"verify:harness-evidence-review": "tsx scripts/verify-harness-evidence-review.ts",
|
|
62
|
-
"verify:harness-evidence-env": "tsx scripts/verify-harness-evidence-env.ts",
|
|
63
|
-
"verify:phase-zero-six-artifacts-safe": "tsx scripts/verify-phase-zero-six-artifacts-safe.ts",
|
|
64
|
-
"verify:github-staging-evidence-env": "tsx scripts/verify-github-staging-evidence-env.ts",
|
|
65
|
-
"phase-zero-six:env": "pnpm verify:phase-zero-six-env-ready",
|
|
66
|
-
"phase-zero-six:env:aws": "pnpm verify:phase-zero-six-env-ready -- --skip-staging-smoke",
|
|
67
|
-
"phase-zero-six:env:report": "pnpm verify:phase-zero-six-env-ready -- --format text --no-fail",
|
|
68
|
-
"phase-zero-six:env:aws:report": "pnpm verify:phase-zero-six-env-ready -- --skip-staging-smoke --format text --no-fail",
|
|
69
|
-
"phase-zero-six:template": "pnpm verify:phase-zero-six-env-ready -- --write-env-template ./.temp/phase-zero-six-evidence/phase-zero-six.env.sh",
|
|
70
|
-
"phase-zero-six:template:aws": "pnpm verify:phase-zero-six-env-ready -- --skip-staging-smoke --write-env-template ./.temp/phase-zero-six-evidence/aws-preflight.env.sh",
|
|
71
|
-
"phase-zero-six:collect": "pnpm collect:phase-zero-six-evidence -- --output-dir ./.temp/phase-zero-six-evidence/latest",
|
|
72
|
-
"phase-zero-six:collect:aws": "pnpm collect:phase-zero-six-evidence -- --output-dir ./.temp/phase-zero-six-evidence/latest-aws --skip-staging-smoke",
|
|
73
|
-
"phase-zero-six:apply": "pnpm collect:phase-zero-six-evidence -- --from-output-dir ./.temp/phase-zero-six-evidence/latest --output-dir ./.temp/phase-zero-six-evidence/latest-apply --apply-ready",
|
|
74
|
-
"phase-zero-six:apply:aws": "pnpm collect:phase-zero-six-evidence -- --from-output-dir ./.temp/phase-zero-six-evidence/latest-aws --output-dir ./.temp/phase-zero-six-evidence/latest-aws-apply --apply-ready --skip-staging-smoke",
|
|
75
|
-
"phase-zero-six:audit": "pnpm verify:phase-zero-six-evidence",
|
|
76
|
-
"phase-zero-six:audit:report": "pnpm verify:phase-zero-six-evidence -- --format text",
|
|
77
|
-
"phase-zero-six:github-env": "pnpm verify:github-staging-evidence-env",
|
|
78
|
-
"phase-zero-six:github-env:report": "pnpm verify:github-staging-evidence-env -- --format text --no-fail",
|
|
79
|
-
"phase-zero-six:github-env:template": "tsx scripts/configure-github-staging-evidence-env.ts --write-template ./.temp/phase-zero-six-evidence/github-staging.env.sh",
|
|
80
|
-
"phase-zero-six:github-env:configure": "tsx scripts/configure-github-staging-evidence-env.ts",
|
|
81
45
|
"service:start": "node scripts/service-manager.mjs start",
|
|
82
46
|
"service:stop": "node scripts/service-manager.mjs stop",
|
|
83
47
|
"service:status": "node scripts/service-manager.mjs status",
|
|
@@ -85,11 +49,9 @@
|
|
|
85
49
|
"prepack": "pnpm build:package",
|
|
86
50
|
"build": "pnpm -r --if-present build",
|
|
87
51
|
"build:package": "pnpm --filter @remote-codex/supervisor-api build && pnpm --filter @remote-codex/relay-server build && pnpm --filter @remote-codex/supervisor-web build",
|
|
88
|
-
"build:worker-image": "docker build -f Dockerfile.worker -t remote-codex-worker:local .",
|
|
89
52
|
"lint": "pnpm -r --if-present lint",
|
|
90
53
|
"typecheck": "pnpm -r --if-present typecheck",
|
|
91
54
|
"test": "NODE_ENV=test pnpm -r --if-present test",
|
|
92
|
-
"test:phase-zero-six-evidence": "vitest run scripts/phase-zero-six-evidence.test.ts",
|
|
93
55
|
"test:e2e": "playwright test",
|
|
94
56
|
"db:migrate": "pnpm --filter @remote-codex/db db:migrate",
|
|
95
57
|
"impeccable": "impeccable",
|
|
@@ -250,6 +250,31 @@ describe('ClaudeRuntimeAdapter', () => {
|
|
|
250
250
|
expect(sdkOptions[0]).not.toHaveProperty('tools');
|
|
251
251
|
});
|
|
252
252
|
|
|
253
|
+
it('updates slash toolbox items from Claude SDK system init commands', async () => {
|
|
254
|
+
const adapter = makeAdapter(() => [
|
|
255
|
+
{
|
|
256
|
+
...systemInit(),
|
|
257
|
+
slash_commands: ['compact', 'usage', 'code-review'],
|
|
258
|
+
},
|
|
259
|
+
result(),
|
|
260
|
+
]);
|
|
261
|
+
|
|
262
|
+
await adapter.startSession({
|
|
263
|
+
cwd: '/tmp/workspace',
|
|
264
|
+
model: 'sonnet',
|
|
265
|
+
approvalMode: 'guarded',
|
|
266
|
+
sandboxMode: 'workspace-write',
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
expect(adapter.managementSchema.toolboxItems).toEqual([
|
|
270
|
+
expect.objectContaining({ action: 'mcp', command: '/mcp', panel: 'mcp' }),
|
|
271
|
+
expect.objectContaining({ action: 'prompt', command: '/code-review' }),
|
|
272
|
+
expect.objectContaining({ action: 'prompt', command: '/compact' }),
|
|
273
|
+
expect.objectContaining({ action: 'prompt', command: '/usage' }),
|
|
274
|
+
expect.objectContaining({ action: 'unsupported', command: '/btw' }),
|
|
275
|
+
]);
|
|
276
|
+
});
|
|
277
|
+
|
|
253
278
|
it('maps thread sandbox modes to Claude permission and sandbox settings', async () => {
|
|
254
279
|
const turnOptions: Record<string, unknown>[] = [];
|
|
255
280
|
const adapter = makeAdapter((_prompt, options) => {
|
|
@@ -20,6 +20,7 @@ import type {
|
|
|
20
20
|
AgentRuntime,
|
|
21
21
|
AgentRuntimeEvent,
|
|
22
22
|
AgentRuntimeManagementSchema,
|
|
23
|
+
AgentRuntimeToolboxItemSchema,
|
|
23
24
|
AgentRuntimeStatus,
|
|
24
25
|
AgentSessionDetail,
|
|
25
26
|
AgentSessionSummary,
|
|
@@ -103,6 +104,7 @@ interface SDKMessage {
|
|
|
103
104
|
session_id?: string;
|
|
104
105
|
cwd?: string;
|
|
105
106
|
model?: string;
|
|
107
|
+
slash_commands?: unknown;
|
|
106
108
|
uuid?: string;
|
|
107
109
|
message?: unknown;
|
|
108
110
|
event?: unknown;
|
|
@@ -387,7 +389,7 @@ export const claudeCapabilities: AgentProviderCapabilities = {
|
|
|
387
389
|
controls: {
|
|
388
390
|
planMode: true,
|
|
389
391
|
permissionRequests: false,
|
|
390
|
-
sandboxMode:
|
|
392
|
+
sandboxMode: false,
|
|
391
393
|
performanceMode: false,
|
|
392
394
|
goals: false,
|
|
393
395
|
},
|
|
@@ -407,6 +409,78 @@ export const claudeCapabilities: AgentProviderCapabilities = {
|
|
|
407
409
|
},
|
|
408
410
|
};
|
|
409
411
|
|
|
412
|
+
function normalizeClaudeSlashCommands(value: unknown) {
|
|
413
|
+
if (!Array.isArray(value)) {
|
|
414
|
+
return [];
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
const commands = new Set<string>();
|
|
418
|
+
for (const entry of value) {
|
|
419
|
+
if (typeof entry !== 'string') {
|
|
420
|
+
continue;
|
|
421
|
+
}
|
|
422
|
+
const command = entry.trim().replace(/^\/+/, '');
|
|
423
|
+
if (command) {
|
|
424
|
+
commands.add(command);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
return [...commands].sort((left, right) => left.localeCompare(right));
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
function claudeSlashLabel(command: string) {
|
|
431
|
+
switch (command) {
|
|
432
|
+
case 'compact':
|
|
433
|
+
return '/compact';
|
|
434
|
+
case 'clear':
|
|
435
|
+
return '/clear';
|
|
436
|
+
case 'context':
|
|
437
|
+
return '/context';
|
|
438
|
+
case 'usage':
|
|
439
|
+
return '/usage';
|
|
440
|
+
default:
|
|
441
|
+
return `/${command}`;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
function buildClaudeToolboxItems(
|
|
446
|
+
slashCommands: string[],
|
|
447
|
+
): AgentRuntimeToolboxItemSchema[] {
|
|
448
|
+
const discovered = new Set(slashCommands);
|
|
449
|
+
const items: AgentRuntimeToolboxItemSchema[] = [
|
|
450
|
+
{
|
|
451
|
+
action: 'mcp',
|
|
452
|
+
command: '/mcp',
|
|
453
|
+
label: 'MCP',
|
|
454
|
+
description: 'Open MCP status for this Claude Code session.',
|
|
455
|
+
panel: 'mcp',
|
|
456
|
+
},
|
|
457
|
+
];
|
|
458
|
+
|
|
459
|
+
for (const command of slashCommands) {
|
|
460
|
+
if (command === 'mcp') {
|
|
461
|
+
continue;
|
|
462
|
+
}
|
|
463
|
+
items.push({
|
|
464
|
+
action: 'prompt',
|
|
465
|
+
command: `/${command}`,
|
|
466
|
+
label: claudeSlashLabel(command),
|
|
467
|
+
description: 'Claude Code slash command discovered from the SDK session.',
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
if (!discovered.has('btw')) {
|
|
472
|
+
items.push({
|
|
473
|
+
action: 'unsupported',
|
|
474
|
+
command: '/btw',
|
|
475
|
+
label: '/btw',
|
|
476
|
+
description:
|
|
477
|
+
'Not listed by the current Claude Agent SDK session; it may require the interactive Claude TTY or a different Claude Code version.',
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
return items;
|
|
482
|
+
}
|
|
483
|
+
|
|
410
484
|
const DEFAULT_CLAUDE_MODELS: AgentModel[] = [
|
|
411
485
|
{
|
|
412
486
|
id: 'sonnet',
|
|
@@ -959,8 +1033,6 @@ function queryOptionsForRuntime(
|
|
|
959
1033
|
},
|
|
960
1034
|
env: {
|
|
961
1035
|
...process.env,
|
|
962
|
-
CLAUDE_CONFIG_DIR: input.home,
|
|
963
|
-
CLAUDE_HOME: input.home,
|
|
964
1036
|
CLAUDE_AGENT_SDK_CLIENT_APP: input.clientApp,
|
|
965
1037
|
},
|
|
966
1038
|
};
|
|
@@ -1022,9 +1094,7 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
|
|
|
1022
1094
|
};
|
|
1023
1095
|
readonly managementSchema: AgentRuntimeManagementSchema = {
|
|
1024
1096
|
hostConfigFiles: [],
|
|
1025
|
-
toolboxItems: [
|
|
1026
|
-
{ action: 'mcp', command: '/mcp', label: 'MCP', panel: 'mcp' },
|
|
1027
|
-
],
|
|
1097
|
+
toolboxItems: buildClaudeToolboxItems([]),
|
|
1028
1098
|
hookCommandTemplates: [],
|
|
1029
1099
|
providerConfigFormat: 'none',
|
|
1030
1100
|
mcpConfigFormat: 'none',
|
|
@@ -1069,6 +1139,12 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
|
|
|
1069
1139
|
return { ...this.status };
|
|
1070
1140
|
}
|
|
1071
1141
|
|
|
1142
|
+
private updateToolboxItemsFromSystemInit(message: SDKMessage) {
|
|
1143
|
+
this.managementSchema.toolboxItems = buildClaudeToolboxItems(
|
|
1144
|
+
normalizeClaudeSlashCommands(message.slash_commands),
|
|
1145
|
+
);
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1072
1148
|
async start() {
|
|
1073
1149
|
await fs.mkdir(this.options.home, { recursive: true });
|
|
1074
1150
|
if (!this.options.query && !this.options.sdk) {
|
|
@@ -1222,6 +1298,7 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
|
|
|
1222
1298
|
for await (const message of query) {
|
|
1223
1299
|
rawMessages.push(message);
|
|
1224
1300
|
if (message.type === 'system' && message.subtype === 'init') {
|
|
1301
|
+
this.updateToolboxItemsFromSystemInit(message);
|
|
1225
1302
|
const sessionId = message.session_id;
|
|
1226
1303
|
if (sessionId) {
|
|
1227
1304
|
providerSessionId = sessionId;
|
|
@@ -1558,6 +1635,7 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
|
|
|
1558
1635
|
this.captureUsage(state, message);
|
|
1559
1636
|
|
|
1560
1637
|
if (message.type === 'system' && message.subtype === 'init') {
|
|
1638
|
+
this.updateToolboxItemsFromSystemInit(message);
|
|
1561
1639
|
if (message.session_id) {
|
|
1562
1640
|
this.sessionCwds.set(message.session_id, message.cwd ?? '');
|
|
1563
1641
|
this.sessionModels.set(message.session_id, message.model ?? null);
|
|
@@ -2084,24 +2162,7 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
|
|
|
2084
2162
|
}
|
|
2085
2163
|
|
|
2086
2164
|
private async withClaudeConfigEnv<T>(callback: () => Promise<T>): Promise<T> {
|
|
2087
|
-
|
|
2088
|
-
const previousClaudeHome = process.env.CLAUDE_HOME;
|
|
2089
|
-
process.env.CLAUDE_CONFIG_DIR = this.options.home;
|
|
2090
|
-
process.env.CLAUDE_HOME = this.options.home;
|
|
2091
|
-
try {
|
|
2092
|
-
return await callback();
|
|
2093
|
-
} finally {
|
|
2094
|
-
if (previousConfigDir === undefined) {
|
|
2095
|
-
delete process.env.CLAUDE_CONFIG_DIR;
|
|
2096
|
-
} else {
|
|
2097
|
-
process.env.CLAUDE_CONFIG_DIR = previousConfigDir;
|
|
2098
|
-
}
|
|
2099
|
-
if (previousClaudeHome === undefined) {
|
|
2100
|
-
delete process.env.CLAUDE_HOME;
|
|
2101
|
-
} else {
|
|
2102
|
-
process.env.CLAUDE_HOME = previousClaudeHome;
|
|
2103
|
-
}
|
|
2104
|
-
}
|
|
2165
|
+
return callback();
|
|
2105
2166
|
}
|
|
2106
2167
|
|
|
2107
2168
|
private markFailed(error: unknown) {
|
|
@@ -5,6 +5,7 @@ import { PassThrough, Writable } from 'node:stream';
|
|
|
5
5
|
import { describe, expect, it } from 'vitest';
|
|
6
6
|
|
|
7
7
|
import { CodexAppServerManager } from './appServerManager';
|
|
8
|
+
import type { CodexUserInput } from './types';
|
|
8
9
|
|
|
9
10
|
async function waitForCondition(
|
|
10
11
|
condition: () => boolean,
|
|
@@ -198,7 +199,7 @@ describe('CodexAppServerManager', () => {
|
|
|
198
199
|
});
|
|
199
200
|
|
|
200
201
|
it('forwards structured image input when starting a turn', async () => {
|
|
201
|
-
const expectedInput = [
|
|
202
|
+
const expectedInput: CodexUserInput[] = [
|
|
202
203
|
{ type: 'text', text: 'Inspect this ', text_elements: [] },
|
|
203
204
|
{ type: 'localImage', path: '/tmp/workspace/photo.png' },
|
|
204
205
|
{ type: 'text', text: ' and summarize.', text_elements: [] },
|
|
@@ -117,6 +117,32 @@ describe('codex history item persistence policy', () => {
|
|
|
117
117
|
]);
|
|
118
118
|
});
|
|
119
119
|
|
|
120
|
+
it('maps local image content with a path back to a photo token', () => {
|
|
121
|
+
const turn = codexTurnToAgentTurn({
|
|
122
|
+
id: 'turn-1',
|
|
123
|
+
status: 'completed',
|
|
124
|
+
error: null,
|
|
125
|
+
items: [
|
|
126
|
+
{
|
|
127
|
+
id: 'user-1',
|
|
128
|
+
type: 'userMessage',
|
|
129
|
+
content: [
|
|
130
|
+
{ type: 'text', text: 'Inspect this' },
|
|
131
|
+
{
|
|
132
|
+
type: 'localImage',
|
|
133
|
+
path: '/tmp/workspace/.temp/threads/thread-1/photo.png',
|
|
134
|
+
} as any,
|
|
135
|
+
],
|
|
136
|
+
},
|
|
137
|
+
],
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
expect(turn.items[0]).toMatchObject({
|
|
141
|
+
kind: 'userMessage',
|
|
142
|
+
text: 'Inspect this\n[PHOTO /tmp/workspace/.temp/threads/thread-1/photo.png]',
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
|
|
120
146
|
it('keeps MCP tool result text extractable for plugin artifacts', () => {
|
|
121
147
|
const artifactText = [
|
|
122
148
|
'Created a 3D molecule artifact for Methane.',
|
|
@@ -178,6 +178,23 @@ function textFromContentEntries(
|
|
|
178
178
|
return entry.text;
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
+
const assetEntry = entry as {
|
|
182
|
+
path?: unknown;
|
|
183
|
+
imagePath?: unknown;
|
|
184
|
+
filePath?: unknown;
|
|
185
|
+
};
|
|
186
|
+
const assetPath =
|
|
187
|
+
typeof assetEntry.path === 'string'
|
|
188
|
+
? assetEntry.path
|
|
189
|
+
: typeof assetEntry.imagePath === 'string'
|
|
190
|
+
? assetEntry.imagePath
|
|
191
|
+
: typeof assetEntry.filePath === 'string'
|
|
192
|
+
? assetEntry.filePath
|
|
193
|
+
: null;
|
|
194
|
+
if (entry.type === 'localImage' && assetPath) {
|
|
195
|
+
return `[PHOTO ${assetPath}]`;
|
|
196
|
+
}
|
|
197
|
+
|
|
181
198
|
return `[${entry.type}]`;
|
|
182
199
|
})
|
|
183
200
|
.join('\n')
|
|
@@ -5,8 +5,6 @@ import { and, desc, eq, inArray } from 'drizzle-orm';
|
|
|
5
5
|
import { DatabaseClient } from './client';
|
|
6
6
|
import { getDefaultHostRecord } from './client';
|
|
7
7
|
import {
|
|
8
|
-
harnessJobWatches,
|
|
9
|
-
harnessNotifyRegistrations,
|
|
10
8
|
notifications,
|
|
11
9
|
shellSessions,
|
|
12
10
|
threadActivityNotes,
|
|
@@ -946,124 +944,3 @@ export function deleteNotificationsByThreadId(db: DatabaseClient, threadId: stri
|
|
|
946
944
|
export function deleteWorkspaceRecord(db: DatabaseClient, id: string) {
|
|
947
945
|
db.delete(workspaces).where(eq(workspaces.id, id)).run();
|
|
948
946
|
}
|
|
949
|
-
|
|
950
|
-
export type HarnessJobWatchStatus = 'pending' | 'delivered' | 'failed';
|
|
951
|
-
|
|
952
|
-
export interface UpsertHarnessNotifyRegistrationInput {
|
|
953
|
-
agentId: string;
|
|
954
|
-
hookToken: string;
|
|
955
|
-
secret: string;
|
|
956
|
-
callbackUrl: string;
|
|
957
|
-
}
|
|
958
|
-
|
|
959
|
-
export function getHarnessNotifyRegistration(db: DatabaseClient) {
|
|
960
|
-
return db
|
|
961
|
-
.select()
|
|
962
|
-
.from(harnessNotifyRegistrations)
|
|
963
|
-
.where(eq(harnessNotifyRegistrations.id, 'default'))
|
|
964
|
-
.get();
|
|
965
|
-
}
|
|
966
|
-
|
|
967
|
-
export function upsertHarnessNotifyRegistration(
|
|
968
|
-
db: DatabaseClient,
|
|
969
|
-
input: UpsertHarnessNotifyRegistrationInput,
|
|
970
|
-
) {
|
|
971
|
-
const now = new Date().toISOString();
|
|
972
|
-
const existing = getHarnessNotifyRegistration(db);
|
|
973
|
-
if (existing) {
|
|
974
|
-
db.update(harnessNotifyRegistrations)
|
|
975
|
-
.set({
|
|
976
|
-
agentId: input.agentId,
|
|
977
|
-
hookToken: input.hookToken,
|
|
978
|
-
secret: input.secret,
|
|
979
|
-
callbackUrl: input.callbackUrl,
|
|
980
|
-
updatedAt: now,
|
|
981
|
-
})
|
|
982
|
-
.where(eq(harnessNotifyRegistrations.id, 'default'))
|
|
983
|
-
.run();
|
|
984
|
-
return getHarnessNotifyRegistration(db)!;
|
|
985
|
-
}
|
|
986
|
-
const record = {
|
|
987
|
-
id: 'default',
|
|
988
|
-
agentId: input.agentId,
|
|
989
|
-
hookToken: input.hookToken,
|
|
990
|
-
secret: input.secret,
|
|
991
|
-
callbackUrl: input.callbackUrl,
|
|
992
|
-
registeredAt: now,
|
|
993
|
-
updatedAt: now,
|
|
994
|
-
};
|
|
995
|
-
db.insert(harnessNotifyRegistrations).values(record).run();
|
|
996
|
-
return record;
|
|
997
|
-
}
|
|
998
|
-
|
|
999
|
-
export interface CreateHarnessJobWatchInput {
|
|
1000
|
-
jobId: string;
|
|
1001
|
-
threadId: string;
|
|
1002
|
-
title?: string | null;
|
|
1003
|
-
}
|
|
1004
|
-
|
|
1005
|
-
export function getHarnessJobWatchByJobId(db: DatabaseClient, jobId: string) {
|
|
1006
|
-
return db
|
|
1007
|
-
.select()
|
|
1008
|
-
.from(harnessJobWatches)
|
|
1009
|
-
.where(eq(harnessJobWatches.jobId, jobId))
|
|
1010
|
-
.get();
|
|
1011
|
-
}
|
|
1012
|
-
|
|
1013
|
-
export function listHarnessJobWatches(db: DatabaseClient) {
|
|
1014
|
-
return db.select().from(harnessJobWatches).orderBy(desc(harnessJobWatches.createdAt)).all();
|
|
1015
|
-
}
|
|
1016
|
-
|
|
1017
|
-
export function listPendingHarnessJobWatches(db: DatabaseClient) {
|
|
1018
|
-
return db
|
|
1019
|
-
.select()
|
|
1020
|
-
.from(harnessJobWatches)
|
|
1021
|
-
.where(eq(harnessJobWatches.status, 'pending'))
|
|
1022
|
-
.all();
|
|
1023
|
-
}
|
|
1024
|
-
|
|
1025
|
-
export function upsertHarnessJobWatch(db: DatabaseClient, input: CreateHarnessJobWatchInput) {
|
|
1026
|
-
const now = new Date().toISOString();
|
|
1027
|
-
const existing = getHarnessJobWatchByJobId(db, input.jobId);
|
|
1028
|
-
if (existing) {
|
|
1029
|
-
db.update(harnessJobWatches)
|
|
1030
|
-
.set({
|
|
1031
|
-
threadId: input.threadId,
|
|
1032
|
-
title: input.title ?? existing.title,
|
|
1033
|
-
updatedAt: now,
|
|
1034
|
-
})
|
|
1035
|
-
.where(eq(harnessJobWatches.id, existing.id))
|
|
1036
|
-
.run();
|
|
1037
|
-
return getHarnessJobWatchByJobId(db, input.jobId)!;
|
|
1038
|
-
}
|
|
1039
|
-
const record = {
|
|
1040
|
-
id: randomUUID(),
|
|
1041
|
-
jobId: input.jobId,
|
|
1042
|
-
threadId: input.threadId,
|
|
1043
|
-
title: input.title ?? null,
|
|
1044
|
-
status: 'pending',
|
|
1045
|
-
lastJobStatus: null,
|
|
1046
|
-
lastError: null,
|
|
1047
|
-
createdAt: now,
|
|
1048
|
-
updatedAt: now,
|
|
1049
|
-
deliveredAt: null,
|
|
1050
|
-
};
|
|
1051
|
-
db.insert(harnessJobWatches).values(record).run();
|
|
1052
|
-
return record;
|
|
1053
|
-
}
|
|
1054
|
-
|
|
1055
|
-
export function updateHarnessJobWatch(
|
|
1056
|
-
db: DatabaseClient,
|
|
1057
|
-
id: string,
|
|
1058
|
-
input: {
|
|
1059
|
-
status?: HarnessJobWatchStatus;
|
|
1060
|
-
lastJobStatus?: string | null;
|
|
1061
|
-
lastError?: string | null;
|
|
1062
|
-
deliveredAt?: string | null;
|
|
1063
|
-
},
|
|
1064
|
-
) {
|
|
1065
|
-
db.update(harnessJobWatches)
|
|
1066
|
-
.set({ ...input, updatedAt: new Date().toISOString() })
|
|
1067
|
-
.where(eq(harnessJobWatches.id, id))
|
|
1068
|
-
.run();
|
|
1069
|
-
}
|