remote-codex 0.11.3 → 0.11.4

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/README.md +4 -0
  2. package/apps/relay-server/dist/index.js +35 -2
  3. package/apps/supervisor-api/dist/chunk-ZWZQVPDT.js +27893 -0
  4. package/apps/supervisor-api/dist/index.js +4 -25727
  5. package/apps/supervisor-api/dist/worker-index.d.ts +2 -0
  6. package/apps/supervisor-api/dist/worker-index.js +197 -0
  7. package/apps/supervisor-web/dist/assets/index-CbdWtyx0.js +5 -0
  8. package/apps/supervisor-web/dist/assets/index-Di1JBevU.css +1 -0
  9. package/apps/supervisor-web/dist/assets/thread-ui-ICfwCbte.js +3604 -0
  10. package/apps/supervisor-web/dist/assets/ui-vendor-D1uxdi-d.js +430 -0
  11. package/apps/supervisor-web/dist/index.html +6 -7
  12. package/bin/remote-codex.mjs +534 -19
  13. package/package.json +41 -2
  14. package/packages/agent-runtime/src/types.ts +2 -1
  15. package/packages/codex/src/appServerManager.ts +1 -0
  16. package/packages/codex/src/historyItems.test.ts +45 -0
  17. package/packages/codex/src/historyItems.ts +22 -0
  18. package/packages/codex/src/runtimeAdapter.ts +6 -0
  19. package/packages/codex/src/types.ts +2 -1
  20. package/packages/db/migrations/0018_control_plane.sql +129 -0
  21. package/packages/db/migrations/0019_control_plane_projects.sql +19 -0
  22. package/packages/db/migrations/0020_control_workspace_status.sql +1 -0
  23. package/packages/db/migrations/0021_control_sandbox_lifecycle_fields.sql +3 -0
  24. package/packages/db/migrations/0022_control_sandbox_resource_profile.sql +1 -0
  25. package/packages/db/migrations/0023_control_usage_import_state.sql +18 -0
  26. package/packages/db/migrations/0024_control_auth.sql +23 -0
  27. package/packages/db/migrations/0025_control_harness_credentials.sql +29 -0
  28. package/packages/db/migrations/0026_control_harness_usage_events.sql +27 -0
  29. package/packages/db/src/schema.ts +305 -1
  30. package/packages/shared/src/index.ts +32 -0
  31. package/packages/shared/src/tokens.ts +137 -0
  32. package/apps/supervisor-web/dist/assets/index-CBIze1VS.css +0 -1
  33. package/apps/supervisor-web/dist/assets/index-YpGAPjED.js +0 -4
  34. package/apps/supervisor-web/dist/assets/thread-ui-BEieA99i.css +0 -1
  35. package/apps/supervisor-web/dist/assets/thread-ui-CF80LEEN.js +0 -3613
  36. package/apps/supervisor-web/dist/assets/ui-vendor-CW6egZBG.js +0 -430
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remote-codex",
3
- "version": "0.11.3",
3
+ "version": "0.11.4",
4
4
  "description": "Local web supervisor for Codex workspaces and threads.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -40,17 +40,56 @@
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",
43
44
  "dev:stop": "node scripts/stop-dev-servers.mjs",
44
45
  "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",
45
81
  "service:start": "node scripts/service-manager.mjs start",
46
82
  "service:stop": "node scripts/service-manager.mjs stop",
47
83
  "service:status": "node scripts/service-manager.mjs status",
48
84
  "service:restart": "node scripts/service-restart.mjs launch",
49
- "prepack": "pnpm build",
85
+ "prepack": "pnpm build:package",
50
86
  "build": "pnpm -r --if-present build",
87
+ "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 .",
51
89
  "lint": "pnpm -r --if-present lint",
52
90
  "typecheck": "pnpm -r --if-present typecheck",
53
91
  "test": "NODE_ENV=test pnpm -r --if-present test",
92
+ "test:phase-zero-six-evidence": "vitest run scripts/phase-zero-six-evidence.test.ts",
54
93
  "test:e2e": "playwright test",
55
94
  "db:migrate": "pnpm --filter @remote-codex/db db:migrate",
56
95
  "impeccable": "impeccable",
@@ -227,6 +227,7 @@ export interface ReadAgentSessionOptions {
227
227
  export interface StartAgentSessionInput {
228
228
  cwd: string;
229
229
  model: string;
230
+ reasoningEffort?: string | null;
230
231
  approvalMode: 'yolo' | 'guarded';
231
232
  sandboxMode?: 'read-only' | 'workspace-write' | 'danger-full-access' | null;
232
233
  performanceMode?: 'standard' | 'fast' | null;
@@ -253,13 +254,13 @@ export interface StartAgentTurnInput {
253
254
  providerSessionId: string;
254
255
  prompt: string;
255
256
  displayPrompt?: string | null;
257
+ developerInstructions?: string | null;
256
258
  model?: string | null;
257
259
  reasoningEffort?: string | null;
258
260
  collaborationMode?: 'default' | 'plan' | null;
259
261
  sandboxMode?: 'read-only' | 'workspace-write' | 'danger-full-access' | null;
260
262
  workspacePath?: string | null;
261
263
  performanceMode?: 'standard' | 'fast' | null;
262
- developerInstructions?: string | null;
263
264
  hidden?: boolean;
264
265
  displayTurnId?: string | null;
265
266
  }
@@ -387,6 +387,7 @@ export class CodexAppServerManager extends EventEmitter {
387
387
  const response = await this.client!.request<{ thread: any; model: string; reasoningEffort?: ReasoningEffort | null; sandbox?: string | null }>('thread/start', {
388
388
  cwd: input.cwd,
389
389
  model: input.model,
390
+ effort: input.effort,
390
391
  serviceTier: input.serviceTier,
391
392
  approvalPolicy: input.approvalPolicy,
392
393
  sandbox: input.sandbox ?? null,
@@ -84,6 +84,51 @@ describe('codex history item persistence policy', () => {
84
84
  });
85
85
  });
86
86
 
87
+ it('keeps MCP tool result text extractable for plugin artifacts', () => {
88
+ const artifactText = [
89
+ 'Created a 3D molecule artifact for Methane.',
90
+ '',
91
+ '```remote-codex-artifact',
92
+ '{"type":"remote-codex.artifact","artifactType":"chemistry.molecule3d","title":"Methane","payload":{"format":"xyz","content":["5\\nmethane example"]}}',
93
+ '```',
94
+ ].join('\n');
95
+
96
+ const turn = codexTurnToAgentTurn({
97
+ id: 'turn-1',
98
+ status: 'completed',
99
+ error: null,
100
+ items: [
101
+ {
102
+ id: 'mcp-tool-1',
103
+ type: 'mcpToolCall',
104
+ status: 'completed',
105
+ action: {
106
+ mcpServer: 'remote_codex_plugins',
107
+ toolName: 'remote_codex_render_molecule',
108
+ },
109
+ result: {
110
+ output: {
111
+ content: [
112
+ {
113
+ type: 'text',
114
+ text: artifactText,
115
+ },
116
+ ],
117
+ },
118
+ },
119
+ },
120
+ ],
121
+ });
122
+
123
+ const toolItem = turn.items[0];
124
+ expect(toolItem).toMatchObject({
125
+ kind: 'toolCall',
126
+ text: 'remote_codex_plugins/remote_codex_render_molecule',
127
+ detailText: expect.stringContaining('```remote-codex-artifact'),
128
+ });
129
+ expect(toolItem?.detailText).toContain('\n```remote-codex-artifact\n');
130
+ });
131
+
87
132
  it('keeps Codex collab agent tool calls visible while running', () => {
88
133
  expect(
89
134
  liveCodexItemToHistoryItem(
@@ -191,6 +191,10 @@ function summarizeToolCallText(text: string) {
191
191
  return lines.find((line) => line.trim().length > 0) ?? lines[0] ?? 'Tool call';
192
192
  }
193
193
 
194
+ function containsRemoteCodexArtifact(text: string | null | undefined) {
195
+ return /```(?:artifact|remote-codex-artifact)\b/i.test(text ?? '');
196
+ }
197
+
194
198
  function deferCommandHistoryItem(
195
199
  item: ThreadHistoryItemDto & { kind: 'commandExecution' },
196
200
  deferredDetails: Map<string, ThreadHistoryItemDetailDto>,
@@ -305,6 +309,19 @@ function valueFromNestedRecords(
305
309
  return null;
306
310
  }
307
311
 
312
+ function textContentFromMcpToolResult(value: unknown) {
313
+ if (!isRecord(value) || !Array.isArray(value.content)) {
314
+ return null;
315
+ }
316
+
317
+ const texts = value.content
318
+ .map((entry) =>
319
+ isRecord(entry) && entry.type === 'text' ? stringOrNull(entry.text) : null,
320
+ )
321
+ .filter((entry): entry is string => Boolean(entry));
322
+ return texts.length > 0 ? texts.join('\n\n') : null;
323
+ }
324
+
308
325
  function formatToolCallHistoryItem(
309
326
  item: CodexTurnItem,
310
327
  deferredDetails?: Map<string, ThreadHistoryItemDetailDto>,
@@ -356,6 +373,7 @@ function formatToolCallHistoryItem(
356
373
  const resultPayload = output ?? result;
357
374
  const argumentText = safeJsonStringify(argumentPayload);
358
375
  const resultText = safeJsonStringify(resultPayload);
376
+ const resultContentText = textContentFromMcpToolResult(resultPayload);
359
377
 
360
378
  if (argumentText) {
361
379
  detailLines.push('', 'Arguments', argumentText);
@@ -363,6 +381,9 @@ function formatToolCallHistoryItem(
363
381
  if (resultText) {
364
382
  detailLines.push('', 'Result', resultText);
365
383
  }
384
+ if (resultContentText && resultContentText !== resultText) {
385
+ detailLines.push('', 'Result Text', resultContentText);
386
+ }
366
387
 
367
388
  const historyItem: ThreadHistoryItemDto = {
368
389
  id: item.id,
@@ -375,6 +396,7 @@ function formatToolCallHistoryItem(
375
396
 
376
397
  if (
377
398
  deferredDetails &&
399
+ !containsRemoteCodexArtifact(historyItem.detailText) &&
378
400
  (Boolean(argumentText) ||
379
401
  Boolean(resultText) ||
380
402
  (historyItem.detailText?.length ?? 0) > 240)
@@ -606,6 +606,9 @@ export class CodexRuntimeAdapter extends EventEmitter implements AgentRuntime {
606
606
  model: input.model,
607
607
  approvalPolicy: input.approvalMode === 'guarded' ? 'on-request' : 'never',
608
608
  };
609
+ if (input.reasoningEffort !== undefined) {
610
+ startInput.effort = input.reasoningEffort as ReasoningEffort | null;
611
+ }
609
612
  if (input.sandboxMode !== undefined) {
610
613
  startInput.sandbox = input.sandboxMode as SandboxMode | null;
611
614
  }
@@ -658,6 +661,9 @@ export class CodexRuntimeAdapter extends EventEmitter implements AgentRuntime {
658
661
  threadId: input.providerSessionId,
659
662
  prompt: input.prompt,
660
663
  };
664
+ if (input.developerInstructions !== undefined) {
665
+ turnInput.developerInstructions = input.developerInstructions;
666
+ }
661
667
  if (input.model !== undefined) {
662
668
  turnInput.model = input.model;
663
669
  }
@@ -293,6 +293,7 @@ export interface CodexThreadGoalRecord {
293
293
  export interface ThreadStartInput {
294
294
  cwd: string;
295
295
  model: string;
296
+ effort?: ReasoningEffort | null;
296
297
  approvalPolicy: 'never' | 'on-request';
297
298
  sandbox?: SandboxMode | null;
298
299
  serviceTier?: ServiceTier | null;
@@ -317,12 +318,12 @@ export interface ThreadRollbackInput {
317
318
  export interface TurnStartInput {
318
319
  threadId: string;
319
320
  prompt: string;
321
+ developerInstructions?: string | null;
320
322
  model?: string | null;
321
323
  effort?: ReasoningEffort | null;
322
324
  collaborationMode?: CollaborationModeKind | null;
323
325
  sandboxPolicy?: SandboxPolicy | null;
324
326
  serviceTier?: ServiceTier | null;
325
- developerInstructions?: string | null;
326
327
  }
327
328
 
328
329
  export interface TurnSteerInput {
@@ -0,0 +1,129 @@
1
+ CREATE TABLE IF NOT EXISTS control_users (
2
+ id TEXT PRIMARY KEY NOT NULL,
3
+ auth_provider TEXT NOT NULL,
4
+ auth_subject TEXT NOT NULL,
5
+ email TEXT NOT NULL,
6
+ display_name TEXT,
7
+ status TEXT NOT NULL DEFAULT 'active',
8
+ plan TEXT NOT NULL DEFAULT 'developer',
9
+ created_at TEXT NOT NULL,
10
+ updated_at TEXT NOT NULL,
11
+ last_seen_at TEXT,
12
+ UNIQUE(auth_provider, auth_subject)
13
+ );
14
+
15
+ CREATE TABLE IF NOT EXISTS control_sandboxes (
16
+ id TEXT PRIMARY KEY NOT NULL,
17
+ user_id TEXT NOT NULL UNIQUE,
18
+ state TEXT NOT NULL,
19
+ image TEXT NOT NULL,
20
+ region TEXT NOT NULL,
21
+ k8s_namespace TEXT,
22
+ k8s_pod_name TEXT,
23
+ router_base_url TEXT,
24
+ worker_service_name TEXT,
25
+ s3_prefix TEXT NOT NULL,
26
+ gateway_key_id TEXT,
27
+ last_started_at TEXT,
28
+ last_seen_at TEXT,
29
+ idle_timeout_at TEXT,
30
+ created_at TEXT NOT NULL,
31
+ updated_at TEXT NOT NULL,
32
+ FOREIGN KEY(user_id) REFERENCES control_users(id)
33
+ );
34
+
35
+ CREATE TABLE IF NOT EXISTS control_workspaces (
36
+ id TEXT PRIMARY KEY NOT NULL,
37
+ user_id TEXT NOT NULL,
38
+ sandbox_id TEXT NOT NULL,
39
+ name TEXT NOT NULL,
40
+ slug TEXT NOT NULL,
41
+ path TEXT NOT NULL,
42
+ source_type TEXT NOT NULL,
43
+ git_url TEXT,
44
+ default_branch TEXT,
45
+ created_at TEXT NOT NULL,
46
+ updated_at TEXT NOT NULL,
47
+ UNIQUE(sandbox_id, slug),
48
+ FOREIGN KEY(user_id) REFERENCES control_users(id),
49
+ FOREIGN KEY(sandbox_id) REFERENCES control_sandboxes(id)
50
+ );
51
+
52
+ CREATE TABLE IF NOT EXISTS control_sessions (
53
+ id TEXT PRIMARY KEY NOT NULL,
54
+ user_id TEXT NOT NULL,
55
+ sandbox_id TEXT NOT NULL,
56
+ workspace_id TEXT NOT NULL,
57
+ provider TEXT NOT NULL,
58
+ worker_session_id TEXT,
59
+ title TEXT NOT NULL,
60
+ status TEXT NOT NULL,
61
+ last_activity_at TEXT,
62
+ created_at TEXT NOT NULL,
63
+ updated_at TEXT NOT NULL,
64
+ FOREIGN KEY(user_id) REFERENCES control_users(id),
65
+ FOREIGN KEY(sandbox_id) REFERENCES control_sandboxes(id),
66
+ FOREIGN KEY(workspace_id) REFERENCES control_workspaces(id)
67
+ );
68
+
69
+ CREATE TABLE IF NOT EXISTS control_gateway_users (
70
+ id TEXT PRIMARY KEY NOT NULL,
71
+ user_id TEXT NOT NULL,
72
+ provider TEXT NOT NULL,
73
+ external_user_id TEXT NOT NULL,
74
+ created_at TEXT NOT NULL,
75
+ UNIQUE(provider, external_user_id),
76
+ UNIQUE(user_id, provider),
77
+ FOREIGN KEY(user_id) REFERENCES control_users(id)
78
+ );
79
+
80
+ CREATE TABLE IF NOT EXISTS control_gateway_keys (
81
+ id TEXT PRIMARY KEY NOT NULL,
82
+ user_id TEXT NOT NULL,
83
+ sandbox_id TEXT NOT NULL,
84
+ provider TEXT NOT NULL,
85
+ external_key_id TEXT NOT NULL,
86
+ key_ciphertext TEXT,
87
+ status TEXT NOT NULL,
88
+ created_at TEXT NOT NULL,
89
+ rotated_at TEXT,
90
+ revoked_at TEXT,
91
+ UNIQUE(provider, external_key_id),
92
+ UNIQUE(sandbox_id, provider),
93
+ FOREIGN KEY(user_id) REFERENCES control_users(id),
94
+ FOREIGN KEY(sandbox_id) REFERENCES control_sandboxes(id)
95
+ );
96
+
97
+ CREATE TABLE IF NOT EXISTS control_usage_events (
98
+ id TEXT PRIMARY KEY NOT NULL,
99
+ user_id TEXT NOT NULL,
100
+ sandbox_id TEXT NOT NULL,
101
+ workspace_id TEXT,
102
+ session_id TEXT,
103
+ gateway_key_id TEXT,
104
+ provider TEXT NOT NULL,
105
+ model TEXT NOT NULL,
106
+ input_tokens INTEGER NOT NULL DEFAULT 0,
107
+ output_tokens INTEGER NOT NULL DEFAULT 0,
108
+ cached_tokens INTEGER NOT NULL DEFAULT 0,
109
+ cost_usd REAL NOT NULL DEFAULT 0,
110
+ external_request_id TEXT,
111
+ occurred_at TEXT NOT NULL,
112
+ imported_at TEXT NOT NULL,
113
+ FOREIGN KEY(user_id) REFERENCES control_users(id),
114
+ FOREIGN KEY(sandbox_id) REFERENCES control_sandboxes(id)
115
+ );
116
+
117
+ CREATE TABLE IF NOT EXISTS control_audit_logs (
118
+ id TEXT PRIMARY KEY NOT NULL,
119
+ user_id TEXT,
120
+ action TEXT NOT NULL,
121
+ resource_type TEXT NOT NULL,
122
+ resource_id TEXT,
123
+ metadata_json TEXT NOT NULL,
124
+ created_at TEXT NOT NULL
125
+ );
126
+
127
+ CREATE INDEX IF NOT EXISTS control_workspaces_user_idx ON control_workspaces(user_id);
128
+ CREATE INDEX IF NOT EXISTS control_sessions_workspace_idx ON control_sessions(workspace_id);
129
+ CREATE INDEX IF NOT EXISTS control_usage_user_occurred_idx ON control_usage_events(user_id, occurred_at);
@@ -0,0 +1,19 @@
1
+ ALTER TABLE control_users ADD COLUMN billing_customer_id TEXT;
2
+ ALTER TABLE control_users ADD COLUMN quota_profile TEXT NOT NULL DEFAULT 'developer';
3
+ ALTER TABLE control_sandboxes ADD COLUMN status_reason TEXT;
4
+
5
+ CREATE TABLE IF NOT EXISTS control_projects (
6
+ id TEXT PRIMARY KEY NOT NULL,
7
+ user_id TEXT NOT NULL,
8
+ name TEXT NOT NULL,
9
+ slug TEXT NOT NULL,
10
+ status TEXT NOT NULL DEFAULT 'active',
11
+ created_at TEXT NOT NULL,
12
+ updated_at TEXT NOT NULL,
13
+ UNIQUE(user_id, slug),
14
+ FOREIGN KEY(user_id) REFERENCES control_users(id)
15
+ );
16
+
17
+ ALTER TABLE control_workspaces ADD COLUMN project_id TEXT REFERENCES control_projects(id);
18
+
19
+ CREATE INDEX IF NOT EXISTS control_projects_user_idx ON control_projects(user_id);
@@ -0,0 +1 @@
1
+ ALTER TABLE control_workspaces ADD COLUMN status TEXT NOT NULL DEFAULT 'active';
@@ -0,0 +1,3 @@
1
+ ALTER TABLE control_sandboxes ADD COLUMN startup_progress INTEGER NOT NULL DEFAULT 0;
2
+ ALTER TABLE control_sandboxes ADD COLUMN last_failure_code TEXT;
3
+ ALTER TABLE control_sandboxes ADD COLUMN last_failure_message TEXT;
@@ -0,0 +1 @@
1
+ ALTER TABLE control_sandboxes ADD COLUMN resource_profile TEXT NOT NULL DEFAULT 'standard';
@@ -0,0 +1,18 @@
1
+ CREATE TABLE IF NOT EXISTS control_usage_import_state (
2
+ id TEXT PRIMARY KEY NOT NULL,
3
+ provider TEXT NOT NULL,
4
+ source TEXT NOT NULL,
5
+ cursor TEXT,
6
+ last_started_at TEXT,
7
+ last_succeeded_at TEXT,
8
+ last_failed_at TEXT,
9
+ last_failure_message TEXT,
10
+ last_source_count INTEGER NOT NULL DEFAULT 0,
11
+ last_imported_count INTEGER NOT NULL DEFAULT 0,
12
+ last_duplicate_count INTEGER NOT NULL DEFAULT 0,
13
+ last_failure_count INTEGER NOT NULL DEFAULT 0,
14
+ updated_at TEXT NOT NULL
15
+ );
16
+
17
+ CREATE UNIQUE INDEX IF NOT EXISTS control_usage_import_state_provider_source_idx
18
+ ON control_usage_import_state(provider, source);
@@ -0,0 +1,23 @@
1
+ CREATE TABLE IF NOT EXISTS control_auth_identities (
2
+ id TEXT PRIMARY KEY NOT NULL,
3
+ user_id TEXT NOT NULL,
4
+ auth_provider TEXT NOT NULL,
5
+ auth_subject TEXT NOT NULL,
6
+ email TEXT,
7
+ display_name TEXT,
8
+ created_at TEXT NOT NULL,
9
+ updated_at TEXT NOT NULL,
10
+ UNIQUE(auth_provider, auth_subject),
11
+ FOREIGN KEY(user_id) REFERENCES control_users(id)
12
+ );
13
+
14
+ CREATE TABLE IF NOT EXISTS control_password_credentials (
15
+ id TEXT PRIMARY KEY NOT NULL,
16
+ user_id TEXT NOT NULL UNIQUE,
17
+ email TEXT NOT NULL UNIQUE,
18
+ password_hash TEXT NOT NULL,
19
+ created_at TEXT NOT NULL,
20
+ updated_at TEXT NOT NULL,
21
+ last_used_at TEXT,
22
+ FOREIGN KEY(user_id) REFERENCES control_users(id)
23
+ );
@@ -0,0 +1,29 @@
1
+ CREATE TABLE IF NOT EXISTS control_harness_users (
2
+ id TEXT PRIMARY KEY NOT NULL,
3
+ user_id TEXT NOT NULL,
4
+ provider TEXT NOT NULL,
5
+ external_user_id TEXT NOT NULL,
6
+ created_at TEXT NOT NULL,
7
+ UNIQUE(provider, external_user_id),
8
+ UNIQUE(user_id, provider),
9
+ FOREIGN KEY(user_id) REFERENCES control_users(id)
10
+ );
11
+
12
+ CREATE TABLE IF NOT EXISTS control_harness_keys (
13
+ id TEXT PRIMARY KEY NOT NULL,
14
+ user_id TEXT NOT NULL,
15
+ sandbox_id TEXT NOT NULL,
16
+ provider TEXT NOT NULL,
17
+ external_key_id TEXT NOT NULL,
18
+ key_ciphertext TEXT,
19
+ secret_name TEXT,
20
+ secret_key TEXT,
21
+ status TEXT NOT NULL,
22
+ created_at TEXT NOT NULL,
23
+ rotated_at TEXT,
24
+ revoked_at TEXT,
25
+ UNIQUE(provider, external_key_id),
26
+ UNIQUE(sandbox_id, provider),
27
+ FOREIGN KEY(user_id) REFERENCES control_users(id),
28
+ FOREIGN KEY(sandbox_id) REFERENCES control_sandboxes(id)
29
+ );
@@ -0,0 +1,27 @@
1
+ CREATE TABLE IF NOT EXISTS control_harness_usage_events (
2
+ id TEXT PRIMARY KEY NOT NULL,
3
+ user_id TEXT NOT NULL,
4
+ sandbox_id TEXT NOT NULL,
5
+ workspace_id TEXT,
6
+ session_id TEXT,
7
+ provider TEXT NOT NULL,
8
+ module TEXT NOT NULL,
9
+ tool TEXT,
10
+ run_id TEXT,
11
+ job_id TEXT,
12
+ external_event_id TEXT,
13
+ compute_units REAL NOT NULL DEFAULT 0,
14
+ cost_usd REAL NOT NULL DEFAULT 0,
15
+ status TEXT NOT NULL DEFAULT 'unknown',
16
+ metadata_json TEXT NOT NULL DEFAULT '{}',
17
+ occurred_at TEXT NOT NULL,
18
+ imported_at TEXT NOT NULL,
19
+ FOREIGN KEY(user_id) REFERENCES control_users(id),
20
+ FOREIGN KEY(sandbox_id) REFERENCES control_sandboxes(id),
21
+ FOREIGN KEY(workspace_id) REFERENCES control_workspaces(id),
22
+ FOREIGN KEY(session_id) REFERENCES control_sessions(id)
23
+ );
24
+
25
+ CREATE UNIQUE INDEX IF NOT EXISTS control_harness_usage_provider_event_idx
26
+ ON control_harness_usage_events(provider, external_event_id)
27
+ WHERE external_event_id IS NOT NULL;