remote-codex 0.11.18 → 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.
Files changed (36) hide show
  1. package/apps/relay-server/dist/index.js +48 -1
  2. package/apps/supervisor-api/dist/index.js +27939 -4
  3. package/apps/supervisor-web/dist/assets/index-BdeDlmJY.js +5 -0
  4. package/apps/supervisor-web/dist/assets/index-BmBS1Wzk.css +1 -0
  5. package/apps/supervisor-web/dist/assets/thread-ui-C5nUCiEf.js +3624 -0
  6. package/apps/supervisor-web/dist/index.html +3 -3
  7. package/package.json +1 -39
  8. package/packages/agent-runtime/src/types.ts +3 -1
  9. package/packages/claude/src/runtimeAdapter.test.ts +25 -0
  10. package/packages/claude/src/runtimeAdapter.ts +85 -24
  11. package/packages/codex/src/appServerManager.test.ts +2 -1
  12. package/packages/codex/src/historyItems.test.ts +26 -0
  13. package/packages/codex/src/historyItems.ts +17 -0
  14. package/packages/codex/src/runtimeAdapter.ts +1 -1
  15. package/packages/db/migrations/0028_thread_turn_display_prompt.sql +1 -0
  16. package/packages/db/src/repositories.ts +6 -123
  17. package/packages/db/src/schema.ts +2 -334
  18. package/packages/opencode/src/runtimeAdapter.test.ts +13 -1
  19. package/packages/opencode/src/runtimeAdapter.ts +1 -2
  20. package/packages/shared/src/index.ts +4 -1
  21. package/apps/supervisor-api/dist/chunk-TGPTF6DT.js +0 -29479
  22. package/apps/supervisor-api/dist/worker-index.d.ts +0 -2
  23. package/apps/supervisor-api/dist/worker-index.js +0 -197
  24. package/apps/supervisor-web/dist/assets/index-C6wykq95.js +0 -6
  25. package/apps/supervisor-web/dist/assets/index-D3I41SIH.css +0 -1
  26. package/apps/supervisor-web/dist/assets/thread-ui-DnSeQdfq.js +0 -3614
  27. package/packages/db/migrations/0018_control_plane.sql +0 -129
  28. package/packages/db/migrations/0019_control_plane_projects.sql +0 -19
  29. package/packages/db/migrations/0020_control_workspace_status.sql +0 -1
  30. package/packages/db/migrations/0021_control_sandbox_lifecycle_fields.sql +0 -3
  31. package/packages/db/migrations/0022_control_sandbox_resource_profile.sql +0 -1
  32. package/packages/db/migrations/0023_control_usage_import_state.sql +0 -18
  33. package/packages/db/migrations/0024_control_auth.sql +0 -23
  34. package/packages/db/migrations/0025_control_harness_credentials.sql +0 -29
  35. package/packages/db/migrations/0026_control_harness_usage_events.sql +0 -27
  36. 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-C6wykq95.js"></script>
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-DnSeQdfq.js">
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-D3I41SIH.css">
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.18",
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",
@@ -109,7 +109,9 @@ export type AgentRuntimeToolboxAction =
109
109
  | 'fork'
110
110
  | 'skills'
111
111
  | 'mcp'
112
- | 'hooks';
112
+ | 'hooks'
113
+ | 'prompt'
114
+ | 'unsupported';
113
115
 
114
116
  export interface AgentRuntimeToolboxItemSchema {
115
117
  action: AgentRuntimeToolboxAction;
@@ -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: true,
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
- const previousConfigDir = process.env.CLAUDE_CONFIG_DIR;
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')
@@ -137,7 +137,7 @@ export const codexCapabilities: AgentProviderCapabilities = {
137
137
  controls: {
138
138
  planMode: true,
139
139
  permissionRequests: true,
140
- sandboxMode: true,
140
+ sandboxMode: false,
141
141
  performanceMode: true,
142
142
  goals: true,
143
143
  },
@@ -0,0 +1 @@
1
+ ALTER TABLE thread_turn_metadata ADD COLUMN display_prompt TEXT;
@@ -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,
@@ -76,6 +74,7 @@ export interface UpsertThreadTurnMetadataInput {
76
74
  pricingModelKey?: string | null;
77
75
  pricingTierKey?: string | null;
78
76
  tokenUsageJson?: string | null;
77
+ displayPrompt?: string | null;
79
78
  }
80
79
 
81
80
  export interface CreateThreadPendingSteerRecordInput {
@@ -392,6 +391,10 @@ export function upsertThreadTurnMetadata(
392
391
  input.tokenUsageJson !== undefined
393
392
  ? input.tokenUsageJson
394
393
  : existing.tokenUsageJson,
394
+ displayPrompt:
395
+ input.displayPrompt !== undefined
396
+ ? input.displayPrompt
397
+ : existing.displayPrompt,
395
398
  updatedAt: now,
396
399
  })
397
400
  .where(eq(threadTurnMetadata.id, existing.id))
@@ -410,6 +413,7 @@ export function upsertThreadTurnMetadata(
410
413
  pricingModelKey: input.pricingModelKey ?? null,
411
414
  pricingTierKey: input.pricingTierKey ?? null,
412
415
  tokenUsageJson: input.tokenUsageJson ?? null,
416
+ displayPrompt: input.displayPrompt ?? null,
413
417
  createdAt: now,
414
418
  updatedAt: now,
415
419
  })
@@ -940,124 +944,3 @@ export function deleteNotificationsByThreadId(db: DatabaseClient, threadId: stri
940
944
  export function deleteWorkspaceRecord(db: DatabaseClient, id: string) {
941
945
  db.delete(workspaces).where(eq(workspaces.id, id)).run();
942
946
  }
943
-
944
- export type HarnessJobWatchStatus = 'pending' | 'delivered' | 'failed';
945
-
946
- export interface UpsertHarnessNotifyRegistrationInput {
947
- agentId: string;
948
- hookToken: string;
949
- secret: string;
950
- callbackUrl: string;
951
- }
952
-
953
- export function getHarnessNotifyRegistration(db: DatabaseClient) {
954
- return db
955
- .select()
956
- .from(harnessNotifyRegistrations)
957
- .where(eq(harnessNotifyRegistrations.id, 'default'))
958
- .get();
959
- }
960
-
961
- export function upsertHarnessNotifyRegistration(
962
- db: DatabaseClient,
963
- input: UpsertHarnessNotifyRegistrationInput,
964
- ) {
965
- const now = new Date().toISOString();
966
- const existing = getHarnessNotifyRegistration(db);
967
- if (existing) {
968
- db.update(harnessNotifyRegistrations)
969
- .set({
970
- agentId: input.agentId,
971
- hookToken: input.hookToken,
972
- secret: input.secret,
973
- callbackUrl: input.callbackUrl,
974
- updatedAt: now,
975
- })
976
- .where(eq(harnessNotifyRegistrations.id, 'default'))
977
- .run();
978
- return getHarnessNotifyRegistration(db)!;
979
- }
980
- const record = {
981
- id: 'default',
982
- agentId: input.agentId,
983
- hookToken: input.hookToken,
984
- secret: input.secret,
985
- callbackUrl: input.callbackUrl,
986
- registeredAt: now,
987
- updatedAt: now,
988
- };
989
- db.insert(harnessNotifyRegistrations).values(record).run();
990
- return record;
991
- }
992
-
993
- export interface CreateHarnessJobWatchInput {
994
- jobId: string;
995
- threadId: string;
996
- title?: string | null;
997
- }
998
-
999
- export function getHarnessJobWatchByJobId(db: DatabaseClient, jobId: string) {
1000
- return db
1001
- .select()
1002
- .from(harnessJobWatches)
1003
- .where(eq(harnessJobWatches.jobId, jobId))
1004
- .get();
1005
- }
1006
-
1007
- export function listHarnessJobWatches(db: DatabaseClient) {
1008
- return db.select().from(harnessJobWatches).orderBy(desc(harnessJobWatches.createdAt)).all();
1009
- }
1010
-
1011
- export function listPendingHarnessJobWatches(db: DatabaseClient) {
1012
- return db
1013
- .select()
1014
- .from(harnessJobWatches)
1015
- .where(eq(harnessJobWatches.status, 'pending'))
1016
- .all();
1017
- }
1018
-
1019
- export function upsertHarnessJobWatch(db: DatabaseClient, input: CreateHarnessJobWatchInput) {
1020
- const now = new Date().toISOString();
1021
- const existing = getHarnessJobWatchByJobId(db, input.jobId);
1022
- if (existing) {
1023
- db.update(harnessJobWatches)
1024
- .set({
1025
- threadId: input.threadId,
1026
- title: input.title ?? existing.title,
1027
- updatedAt: now,
1028
- })
1029
- .where(eq(harnessJobWatches.id, existing.id))
1030
- .run();
1031
- return getHarnessJobWatchByJobId(db, input.jobId)!;
1032
- }
1033
- const record = {
1034
- id: randomUUID(),
1035
- jobId: input.jobId,
1036
- threadId: input.threadId,
1037
- title: input.title ?? null,
1038
- status: 'pending',
1039
- lastJobStatus: null,
1040
- lastError: null,
1041
- createdAt: now,
1042
- updatedAt: now,
1043
- deliveredAt: null,
1044
- };
1045
- db.insert(harnessJobWatches).values(record).run();
1046
- return record;
1047
- }
1048
-
1049
- export function updateHarnessJobWatch(
1050
- db: DatabaseClient,
1051
- id: string,
1052
- input: {
1053
- status?: HarnessJobWatchStatus;
1054
- lastJobStatus?: string | null;
1055
- lastError?: string | null;
1056
- deliveredAt?: string | null;
1057
- },
1058
- ) {
1059
- db.update(harnessJobWatches)
1060
- .set({ ...input, updatedAt: new Date().toISOString() })
1061
- .where(eq(harnessJobWatches.id, id))
1062
- .run();
1063
- }