@lensagent/core 0.1.0 → 0.1.2

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 (99) hide show
  1. package/README.md +63 -4
  2. package/config/lens.example.jsonc +36 -13
  3. package/dist/adapters/executor-router.d.ts +20 -0
  4. package/dist/adapters/executor-router.js +86 -0
  5. package/dist/adapters/executor.d.ts +9 -3
  6. package/dist/adapters/executor.js +196 -61
  7. package/dist/adapters/lsp.d.ts +2 -1
  8. package/dist/adapters/lsp.js +98 -41
  9. package/dist/adapters/mcp.js +52 -17
  10. package/dist/adapters/revert.d.ts +5 -4
  11. package/dist/adapters/revert.js +6 -64
  12. package/dist/adapters/web.d.ts +9 -0
  13. package/dist/adapters/web.js +244 -0
  14. package/dist/config/files.d.ts +14 -0
  15. package/dist/config/files.js +57 -0
  16. package/dist/config/runtime.d.ts +3 -4
  17. package/dist/config/runtime.js +116 -97
  18. package/dist/config/schema.d.ts +56 -10
  19. package/dist/config/schema.js +48 -8
  20. package/dist/contracts/capability.d.ts +27 -1
  21. package/dist/contracts/domain.d.ts +1 -0
  22. package/dist/contracts/model.d.ts +33 -6
  23. package/dist/contracts/review.d.ts +17 -0
  24. package/dist/contracts/review.js +1 -0
  25. package/dist/core/access-policy.d.ts +40 -0
  26. package/dist/core/access-policy.js +68 -0
  27. package/dist/core/access.d.ts +30 -2
  28. package/dist/core/access.js +159 -26
  29. package/dist/core/context-boundaries.d.ts +25 -0
  30. package/dist/core/context-boundaries.js +106 -0
  31. package/dist/core/context.d.ts +3 -0
  32. package/dist/core/context.js +11 -0
  33. package/dist/core/host.js +261 -173
  34. package/dist/entry/pair.d.ts +1 -0
  35. package/dist/entry/pair.js +14 -0
  36. package/dist/entry/run.js +1 -1
  37. package/dist/execution/lsp-process.d.ts +3 -2
  38. package/dist/execution/lsp-process.js +46 -28
  39. package/dist/http/server.d.ts +5 -0
  40. package/dist/http/server.js +262 -80
  41. package/dist/runtime/agent.d.ts +9 -4
  42. package/dist/runtime/agent.js +361 -286
  43. package/dist/runtime/artifacts.d.ts +13 -1
  44. package/dist/runtime/artifacts.js +137 -10
  45. package/dist/runtime/cache.d.ts +59 -16
  46. package/dist/runtime/cache.js +124 -43
  47. package/dist/runtime/calls.d.ts +1 -1
  48. package/dist/runtime/calls.js +164 -48
  49. package/dist/runtime/capabilities.d.ts +2 -2
  50. package/dist/runtime/capabilities.js +4 -5
  51. package/dist/runtime/cleanup.d.ts +5 -2
  52. package/dist/runtime/cleanup.js +48 -17
  53. package/dist/runtime/compaction.d.ts +9 -0
  54. package/dist/runtime/compaction.js +52 -0
  55. package/dist/runtime/context.d.ts +8 -4
  56. package/dist/runtime/context.js +115 -128
  57. package/dist/runtime/events.d.ts +7 -0
  58. package/dist/runtime/events.js +83 -0
  59. package/dist/runtime/images.d.ts +14 -0
  60. package/dist/runtime/images.js +82 -0
  61. package/dist/runtime/kernel-capabilities.d.ts +7 -1
  62. package/dist/runtime/kernel-capabilities.js +146 -68
  63. package/dist/runtime/model-audit.d.ts +9 -0
  64. package/dist/runtime/model-audit.js +39 -0
  65. package/dist/runtime/model.d.ts +109 -4
  66. package/dist/runtime/model.js +368 -143
  67. package/dist/runtime/observability.d.ts +1 -0
  68. package/dist/runtime/observability.js +60 -15
  69. package/dist/runtime/policy.d.ts +1 -1
  70. package/dist/runtime/policy.js +2 -2
  71. package/dist/runtime/profile.d.ts +2 -1
  72. package/dist/runtime/profile.js +12 -15
  73. package/dist/runtime/projects.d.ts +3 -1
  74. package/dist/runtime/projects.js +38 -17
  75. package/dist/runtime/ptc-worker.d.ts +1 -0
  76. package/dist/runtime/ptc-worker.js +97 -0
  77. package/dist/runtime/ptc.d.ts +16 -0
  78. package/dist/runtime/ptc.js +128 -0
  79. package/dist/runtime/results.d.ts +5 -4
  80. package/dist/runtime/results.js +29 -51
  81. package/dist/runtime/review.d.ts +4 -1
  82. package/dist/runtime/review.js +101 -22
  83. package/dist/runtime/scheduler.js +1 -0
  84. package/dist/runtime/skills.js +34 -5
  85. package/dist/runtime/task-audit.d.ts +21 -0
  86. package/dist/runtime/task-audit.js +122 -0
  87. package/dist/runtime/time.d.ts +9 -0
  88. package/dist/runtime/time.js +18 -0
  89. package/dist/runtime/tokenizer.d.ts +2 -0
  90. package/dist/runtime/tokenizer.js +26 -1
  91. package/dist/store/database.js +26 -1
  92. package/dist/store/event-log.d.ts +1 -0
  93. package/dist/store/event-log.js +22 -5
  94. package/dist/store/schema.d.ts +1 -1
  95. package/dist/store/schema.js +50 -1
  96. package/dist/store/store.d.ts +65 -19
  97. package/dist/store/store.js +986 -188
  98. package/package.json +18 -8
  99. package/proto/executor.proto +69 -55
@@ -9,10 +9,14 @@ export class EventLog {
9
9
  path;
10
10
  #tail = Promise.resolve();
11
11
  #hash = null;
12
+ #error;
12
13
  constructor(root, path, fsync) {
13
14
  this.fsync = fsync;
14
15
  this.path = resolve(root, path);
15
16
  }
17
+ get failed() {
18
+ return this.#error !== undefined;
19
+ }
16
20
  async load() {
17
21
  const records = await readEventLog(this.path).catch((error) => {
18
22
  if (isMissing(error))
@@ -22,21 +26,34 @@ export class EventLog {
22
26
  this.#hash = records.at(-1)?.hash ?? null;
23
27
  }
24
28
  append(revision, state) {
29
+ if (this.failed)
30
+ return;
25
31
  this.#tail = this.#tail.then(async () => {
32
+ if (this.failed)
33
+ return;
26
34
  const base = { revision, occurred_at: now(), previous_hash: this.#hash, state };
27
35
  const record = { ...base, hash: hash(base) };
28
36
  await mkdir(dirname(this.path), { recursive: true });
29
37
  await appendFile(this.path, `${JSON.stringify(record)}\n`, 'utf8');
30
38
  if (this.fsync) {
31
- const file = await open(this.path, 'r');
32
- await file.sync();
33
- await file.close();
39
+ const file = await open(this.path, 'r+');
40
+ try {
41
+ await file.sync();
42
+ }
43
+ finally {
44
+ await file.close();
45
+ }
34
46
  }
35
47
  this.#hash = record.hash;
48
+ }).catch((error) => {
49
+ // A failed append may have written only part of a record. Do not extend that chain.
50
+ this.#error = error instanceof Error ? error : new Error(String(error));
36
51
  });
37
52
  }
38
- close() {
39
- return this.#tail;
53
+ async close() {
54
+ await this.#tail;
55
+ if (this.#error !== undefined)
56
+ throw this.#error;
40
57
  }
41
58
  }
42
59
  export async function readEventLog(path) {
@@ -1 +1 @@
1
- export declare const schema = "\nPRAGMA foreign_keys = ON;\nPRAGMA journal_mode = WAL;\nPRAGMA synchronous = NORMAL;\nPRAGMA busy_timeout = 5000;\n\nCREATE TABLE IF NOT EXISTS instance (\n id INTEGER PRIMARY KEY CHECK (id = 1),\n epoch TEXT NOT NULL,\n state_revision INTEGER NOT NULL CHECK (state_revision >= 0),\n created_at TEXT NOT NULL\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS projects (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL UNIQUE,\n archived_at TEXT,\n revision INTEGER NOT NULL CHECK (revision >= 0),\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS workspaces (\n id TEXT PRIMARY KEY,\n project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,\n name TEXT NOT NULL,\n is_default INTEGER NOT NULL CHECK (is_default IN (0, 1)),\n revision INTEGER NOT NULL CHECK (revision >= 0),\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL,\n UNIQUE(project_id, name)\n) STRICT;\nCREATE UNIQUE INDEX IF NOT EXISTS workspace_default ON workspaces(project_id) WHERE is_default = 1;\n\nCREATE TABLE IF NOT EXISTS workspace_paths (\n workspace_id TEXT NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,\n ordinal INTEGER NOT NULL CHECK (ordinal >= 0),\n path TEXT NOT NULL,\n PRIMARY KEY(workspace_id, ordinal),\n UNIQUE(workspace_id, path)\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS tasks (\n id TEXT PRIMARY KEY,\n project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,\n title TEXT NOT NULL,\n archived_at TEXT,\n revision INTEGER NOT NULL CHECK (revision >= 0),\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS sessions (\n id TEXT PRIMARY KEY,\n task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,\n title TEXT NOT NULL,\n archived_at TEXT,\n revision INTEGER NOT NULL CHECK (revision >= 0),\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS agents (\n id TEXT PRIMARY KEY,\n task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,\n session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,\n workspace_id TEXT NOT NULL REFERENCES workspaces(id) ON DELETE RESTRICT,\n parent_agent_id TEXT REFERENCES agents(id) ON DELETE SET NULL,\n name TEXT NOT NULL,\n state TEXT NOT NULL CHECK (state IN ('IDLE', 'RUNNING', 'WAITING', 'STOPPED')),\n model TEXT NOT NULL,\n model_reasoning_effort TEXT NOT NULL,\n policy_preset TEXT NOT NULL,\n context_revision INTEGER NOT NULL CHECK (context_revision >= 0),\n revision INTEGER NOT NULL CHECK (revision >= 0),\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS messages (\n id TEXT PRIMARY KEY,\n agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,\n source_agent_id TEXT REFERENCES agents(id) ON DELETE SET NULL,\n role TEXT NOT NULL CHECK (role IN ('system', 'user', 'assistant', 'tool')),\n content TEXT NOT NULL,\n metadata_json TEXT,\n created_at TEXT NOT NULL\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS pending_messages (\n id TEXT PRIMARY KEY,\n agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,\n message_id TEXT NOT NULL REFERENCES messages(id) ON DELETE CASCADE,\n source_agent_id TEXT REFERENCES agents(id) ON DELETE SET NULL,\n content TEXT NOT NULL,\n ordinal INTEGER NOT NULL CHECK (ordinal >= 0),\n revision INTEGER NOT NULL CHECK (revision >= 0),\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL,\n UNIQUE(agent_id, ordinal)\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS runs (\n id TEXT PRIMARY KEY,\n agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,\n goal_message_ids_json TEXT NOT NULL DEFAULT '[]',\n initial_step_pending INTEGER NOT NULL DEFAULT 0 CHECK (initial_step_pending IN (0, 1)),\n state TEXT NOT NULL CHECK (state IN ('READY', 'RUNNING', 'WAITING', 'TERMINAL')),\n outcome TEXT CHECK (outcome IN ('success', 'failed', 'cancelled', 'interrupted')),\n error_code TEXT,\n revision INTEGER NOT NULL CHECK (revision >= 0),\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS steps (\n id TEXT PRIMARY KEY,\n run_id TEXT NOT NULL REFERENCES runs(id) ON DELETE CASCADE,\n turn_group_id TEXT NOT NULL REFERENCES context_turn_groups(id) ON DELETE RESTRICT,\n ordinal INTEGER NOT NULL CHECK (ordinal >= 0),\n state TEXT NOT NULL CHECK (state IN ('RUNNING', 'WAITING', 'TERMINAL')),\n outcome TEXT CHECK (outcome IN ('success', 'failed', 'cancelled', 'interrupted')),\n error_code TEXT,\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL,\n UNIQUE(run_id, ordinal)\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS model_attempts (\n id TEXT PRIMARY KEY,\n agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,\n step_id TEXT REFERENCES steps(id) ON DELETE CASCADE,\n kind TEXT NOT NULL CHECK (kind IN ('agent', 'compaction')),\n sequence INTEGER NOT NULL CHECK (sequence >= 0),\n model TEXT NOT NULL,\n state TEXT NOT NULL CHECK (state IN ('PREPARED', 'RUNNING', 'TERMINAL')),\n outcome TEXT CHECK (outcome IN ('success', 'failed', 'cancelled', 'interrupted')),\n profile_id TEXT NOT NULL,\n profile_version INTEGER NOT NULL CHECK (profile_version >= 1),\n profile_digest TEXT NOT NULL,\n config_digest TEXT NOT NULL,\n context_revision INTEGER NOT NULL CHECK (context_revision >= 0),\n request_digest TEXT NOT NULL,\n preparation_json TEXT NOT NULL,\n usage_json TEXT,\n error_code TEXT,\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL,\n UNIQUE(agent_id, kind, sequence)\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS attempt_capabilities (\n attempt_id TEXT NOT NULL REFERENCES model_attempts(id) ON DELETE CASCADE,\n ordinal INTEGER NOT NULL CHECK (ordinal >= 0),\n capability_id TEXT NOT NULL,\n schema_json TEXT NOT NULL,\n PRIMARY KEY(attempt_id, ordinal),\n UNIQUE(attempt_id, capability_id)\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS attempt_tool_ordinals (\n attempt_id TEXT NOT NULL REFERENCES model_attempts(id) ON DELETE CASCADE,\n ordinal INTEGER NOT NULL CHECK (ordinal >= 0),\n provider_tool_id TEXT,\n complete INTEGER NOT NULL CHECK (complete IN (0, 1)),\n PRIMARY KEY(attempt_id, ordinal)\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS call_partitions (\n id TEXT PRIMARY KEY,\n step_id TEXT NOT NULL REFERENCES steps(id) ON DELETE CASCADE,\n ordinal INTEGER NOT NULL CHECK (ordinal >= 0),\n mode TEXT NOT NULL CHECK (mode IN ('parallel', 'serial')),\n created_at TEXT NOT NULL,\n UNIQUE(step_id, ordinal)\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS calls (\n id TEXT PRIMARY KEY,\n project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,\n workspace_id TEXT NOT NULL REFERENCES workspaces(id) ON DELETE RESTRICT,\n agent_id TEXT REFERENCES agents(id) ON DELETE SET NULL,\n step_id TEXT REFERENCES steps(id) ON DELETE CASCADE,\n attempt_id TEXT REFERENCES model_attempts(id) ON DELETE CASCADE,\n partition_id TEXT REFERENCES call_partitions(id) ON DELETE CASCADE,\n parent_call_id TEXT REFERENCES calls(id) ON DELETE CASCADE,\n source TEXT NOT NULL CHECK (source IN ('user', 'llm', 'program')),\n capability_id TEXT NOT NULL,\n provider_call_id TEXT,\n call_ordinal INTEGER CHECK (call_ordinal >= 0),\n input_json TEXT NOT NULL,\n effect TEXT NOT NULL CHECK (effect IN ('read_only', 'workspace_write', 'external', 'full')),\n policy TEXT CHECK (policy IN ('read_only', 'workspace_write', 'external', 'ask', 'auto_approve', 'full_access')),\n state TEXT NOT NULL CHECK (state IN ('WAITING', 'QUEUED', 'RUNNING', 'TERMINAL')),\n wait_reason TEXT CHECK (wait_reason IN ('user_input', 'policy_review')),\n outcome TEXT CHECK (outcome IN ('success', 'failed', 'cancelled', 'interrupted')),\n outcome_unknown INTEGER NOT NULL CHECK (outcome_unknown IN (0, 1)),\n auto_review_passed INTEGER CHECK (auto_review_passed IN (0, 1)),\n revision INTEGER NOT NULL CHECK (revision >= 0),\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL,\n UNIQUE(attempt_id, call_ordinal)\n) STRICT;\nCREATE UNIQUE INDEX IF NOT EXISTS calls_by_provider_call ON calls(attempt_id, provider_call_id) WHERE provider_call_id IS NOT NULL;\n\nCREATE TABLE IF NOT EXISTS results (\n id TEXT PRIMARY KEY,\n call_id TEXT NOT NULL UNIQUE REFERENCES calls(id) ON DELETE CASCADE,\n outcome TEXT NOT NULL CHECK (outcome IN ('success', 'failed', 'cancelled', 'interrupted')),\n content_json TEXT NOT NULL,\n truncated INTEGER NOT NULL DEFAULT 0 CHECK (truncated IN (0, 1)),\n error_code TEXT,\n error_message TEXT,\n created_at TEXT NOT NULL\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS artifacts (\n id TEXT PRIMARY KEY,\n project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,\n name TEXT NOT NULL,\n media_type TEXT NOT NULL,\n relative_path TEXT NOT NULL,\n sha256 TEXT NOT NULL,\n size_bytes INTEGER NOT NULL CHECK (size_bytes >= 0),\n created_at TEXT NOT NULL\n) STRICT;\nCREATE UNIQUE INDEX IF NOT EXISTS artifact_identity ON artifacts(project_id, sha256, name, media_type);\nCREATE INDEX IF NOT EXISTS artifacts_by_path ON artifacts(relative_path);\n\nCREATE TABLE IF NOT EXISTS result_artifacts (\n result_id TEXT NOT NULL REFERENCES results(id) ON DELETE CASCADE,\n artifact_id TEXT NOT NULL REFERENCES artifacts(id) ON DELETE RESTRICT,\n PRIMARY KEY(result_id, artifact_id)\n) STRICT;\n\n-- No table for write_file/edit_file file history: it belongs to the Executor, which owns the files\n-- and therefore the only authoritative view of what they contained. Core keeps Call semantics and\n-- asks the Executor to revert by call_id. See\n-- docs/architecture/harness-level3/09-file-snapshot-storage-decision.md.\n\nCREATE TABLE IF NOT EXISTS context_turn_groups (\n id TEXT PRIMARY KEY,\n agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,\n ordinal INTEGER NOT NULL CHECK (ordinal >= 0),\n kind TEXT NOT NULL CHECK (kind IN ('turn', 'compaction', 'included', 'ledger')),\n state TEXT NOT NULL CHECK (state IN ('OPEN', 'COMPLETE', 'ABORTED')),\n revision INTEGER NOT NULL CHECK (revision >= 0),\n created_at TEXT NOT NULL,\n completed_at TEXT,\n UNIQUE(agent_id, ordinal)\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS context_items (\n id TEXT PRIMARY KEY,\n agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,\n turn_group_id TEXT NOT NULL REFERENCES context_turn_groups(id) ON DELETE CASCADE,\n turn_group_ordinal INTEGER NOT NULL CHECK (turn_group_ordinal >= 0),\n kind TEXT NOT NULL CHECK (kind IN ('message', 'tool_calls', 'result', 'compaction')),\n source_id TEXT NOT NULL,\n role TEXT CHECK (role IN ('system', 'user', 'assistant', 'tool')),\n content TEXT NOT NULL,\n tool_call_id TEXT,\n tool_name TEXT,\n ordinal INTEGER NOT NULL CHECK (ordinal >= 0),\n visible INTEGER NOT NULL CHECK (visible IN (0, 1)),\n locked INTEGER NOT NULL CHECK (locked IN (0, 1)),\n revision INTEGER NOT NULL CHECK (revision >= 0),\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL,\n UNIQUE(agent_id, ordinal),\n UNIQUE(turn_group_id, turn_group_ordinal)\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS context_overrides (\n id TEXT PRIMARY KEY,\n agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,\n target_kind TEXT NOT NULL CHECK (target_kind IN ('agent', 'message', 'result', 'artifact')),\n target_id TEXT NOT NULL,\n instructions TEXT NOT NULL,\n revision INTEGER NOT NULL CHECK (revision >= 0),\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL,\n UNIQUE(agent_id, target_kind, target_id)\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS projection_decisions (\n id TEXT PRIMARY KEY,\n agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,\n attempt_id TEXT REFERENCES model_attempts(id) ON DELETE CASCADE,\n stage TEXT NOT NULL,\n source_tokens INTEGER NOT NULL CHECK (source_tokens >= 0),\n projected_tokens INTEGER NOT NULL CHECK (projected_tokens >= 0),\n details_json TEXT NOT NULL,\n created_at TEXT NOT NULL\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS capability_assignments (\n agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,\n capability_id TEXT NOT NULL,\n policy TEXT NOT NULL CHECK (policy IN ('read_only', 'workspace_write', 'external', 'ask', 'auto_approve', 'full_access')),\n assigned_at TEXT NOT NULL,\n PRIMARY KEY(agent_id, capability_id)\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS capability_loads (\n agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,\n capability_id TEXT NOT NULL,\n loaded_at TEXT NOT NULL,\n PRIMARY KEY(agent_id, capability_id),\n FOREIGN KEY(agent_id, capability_id) REFERENCES capability_assignments(agent_id, capability_id) ON DELETE CASCADE\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS agent_plans (\n agent_id TEXT PRIMARY KEY REFERENCES agents(id) ON DELETE CASCADE,\n items_json TEXT NOT NULL,\n revision INTEGER NOT NULL CHECK (revision >= 0),\n updated_at TEXT NOT NULL\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS ingress_requests (\n client_id TEXT NOT NULL,\n key TEXT NOT NULL,\n operation TEXT NOT NULL,\n request_digest TEXT NOT NULL,\n response_json TEXT NOT NULL,\n created_at TEXT NOT NULL,\n PRIMARY KEY(client_id, key)\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS side_effect_ledgers (\n id TEXT PRIMARY KEY,\n step_id TEXT NOT NULL REFERENCES steps(id) ON DELETE CASCADE,\n content TEXT NOT NULL,\n created_at TEXT NOT NULL\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS tasks_by_project ON tasks(project_id, archived_at, updated_at DESC);\nCREATE INDEX IF NOT EXISTS sessions_by_task ON sessions(task_id, archived_at, updated_at DESC);\nCREATE INDEX IF NOT EXISTS agents_by_session ON agents(session_id, parent_agent_id, created_at);\nCREATE INDEX IF NOT EXISTS pending_by_agent ON pending_messages(agent_id, ordinal);\nCREATE INDEX IF NOT EXISTS runs_by_agent ON runs(agent_id, created_at DESC);\nCREATE UNIQUE INDEX IF NOT EXISTS one_live_run_per_agent ON runs(agent_id) WHERE state != 'TERMINAL';\nCREATE INDEX IF NOT EXISTS attempts_by_step ON model_attempts(step_id, sequence);\nCREATE INDEX IF NOT EXISTS calls_by_project ON calls(project_id, state, created_at DESC);\nCREATE INDEX IF NOT EXISTS context_by_agent ON context_items(agent_id, ordinal);\nCREATE INDEX IF NOT EXISTS artifacts_by_project ON artifacts(project_id, created_at DESC);\n";
1
+ export declare const schema = "\nPRAGMA foreign_keys = ON;\nPRAGMA journal_mode = WAL;\nPRAGMA synchronous = NORMAL;\nPRAGMA busy_timeout = 5000;\n\nCREATE TABLE IF NOT EXISTS instance (\n id INTEGER PRIMARY KEY CHECK (id = 1),\n epoch TEXT NOT NULL,\n state_revision INTEGER NOT NULL CHECK (state_revision >= 0),\n created_at TEXT NOT NULL\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS projects (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL UNIQUE,\n archived_at TEXT,\n revision INTEGER NOT NULL CHECK (revision >= 0),\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS workspaces (\n executor_ref TEXT,\n executor_id TEXT NOT NULL DEFAULT 'remote' CHECK (executor_id IN ('local', 'remote')),\n id TEXT PRIMARY KEY,\n project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,\n name TEXT NOT NULL,\n is_default INTEGER NOT NULL CHECK (is_default IN (0, 1)),\n revision INTEGER NOT NULL CHECK (revision >= 0),\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL,\n UNIQUE(project_id, name)\n) STRICT;\nCREATE UNIQUE INDEX IF NOT EXISTS workspace_default ON workspaces(project_id) WHERE is_default = 1;\n\nCREATE TABLE IF NOT EXISTS workspace_paths (\n workspace_id TEXT NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,\n ordinal INTEGER NOT NULL CHECK (ordinal >= 0),\n path TEXT NOT NULL,\n PRIMARY KEY(workspace_id, ordinal),\n UNIQUE(workspace_id, path)\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS tasks (\n id TEXT PRIMARY KEY,\n project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,\n title TEXT NOT NULL,\n archived_at TEXT,\n revision INTEGER NOT NULL CHECK (revision >= 0),\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS sessions (\n id TEXT PRIMARY KEY,\n task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,\n title TEXT NOT NULL,\n archived_at TEXT,\n revision INTEGER NOT NULL CHECK (revision >= 0),\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS agents (\n id TEXT PRIMARY KEY,\n task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,\n session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,\n workspace_id TEXT NOT NULL REFERENCES workspaces(id) ON DELETE RESTRICT,\n parent_agent_id TEXT REFERENCES agents(id) ON DELETE SET NULL,\n name TEXT NOT NULL,\n state TEXT NOT NULL CHECK (state IN ('IDLE', 'RUNNING', 'WAITING', 'STOPPED')),\n model TEXT NOT NULL,\n model_reasoning_effort TEXT NOT NULL,\n policy_preset TEXT NOT NULL,\n context_revision INTEGER NOT NULL CHECK (context_revision >= 0),\n revision INTEGER NOT NULL CHECK (revision >= 0),\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS messages (\n id TEXT PRIMARY KEY,\n agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,\n source_agent_id TEXT REFERENCES agents(id) ON DELETE SET NULL,\n role TEXT NOT NULL CHECK (role IN ('system', 'user', 'assistant', 'tool')),\n content TEXT NOT NULL,\n metadata_json TEXT,\n created_at TEXT NOT NULL\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS pending_messages (\n id TEXT PRIMARY KEY,\n agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,\n message_id TEXT NOT NULL REFERENCES messages(id) ON DELETE CASCADE,\n source_agent_id TEXT REFERENCES agents(id) ON DELETE SET NULL,\n content TEXT NOT NULL,\n ordinal INTEGER NOT NULL CHECK (ordinal >= 0),\n revision INTEGER NOT NULL CHECK (revision >= 0),\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL,\n UNIQUE(agent_id, ordinal)\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS runs (\n id TEXT PRIMARY KEY,\n agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,\n goal_message_ids_json TEXT NOT NULL DEFAULT '[]',\n initial_step_pending INTEGER NOT NULL DEFAULT 0 CHECK (initial_step_pending IN (0, 1)),\n state TEXT NOT NULL CHECK (state IN ('READY', 'RUNNING', 'WAITING', 'TERMINAL')),\n outcome TEXT CHECK (outcome IN ('success', 'failed', 'cancelled', 'interrupted')),\n error_code TEXT,\n revision INTEGER NOT NULL CHECK (revision >= 0),\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS steps (\n id TEXT PRIMARY KEY,\n run_id TEXT NOT NULL REFERENCES runs(id) ON DELETE CASCADE,\n turn_group_id TEXT NOT NULL REFERENCES context_turn_groups(id) ON DELETE RESTRICT,\n ordinal INTEGER NOT NULL CHECK (ordinal >= 0),\n state TEXT NOT NULL CHECK (state IN ('RUNNING', 'WAITING', 'TERMINAL')),\n outcome TEXT CHECK (outcome IN ('success', 'failed', 'cancelled', 'interrupted')),\n error_code TEXT,\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL,\n UNIQUE(run_id, ordinal)\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS model_attempts (\n id TEXT PRIMARY KEY,\n agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,\n step_id TEXT REFERENCES steps(id) ON DELETE CASCADE,\n kind TEXT NOT NULL CHECK (kind IN ('agent', 'compaction')),\n sequence INTEGER NOT NULL CHECK (sequence >= 0),\n model TEXT NOT NULL,\n state TEXT NOT NULL CHECK (state IN ('PREPARED', 'RUNNING', 'TERMINAL')),\n outcome TEXT CHECK (outcome IN ('success', 'failed', 'cancelled', 'interrupted')),\n profile_id TEXT NOT NULL,\n profile_version INTEGER NOT NULL CHECK (profile_version >= 1),\n profile_digest TEXT NOT NULL,\n config_digest TEXT NOT NULL,\n context_revision INTEGER NOT NULL CHECK (context_revision >= 0),\n request_digest TEXT NOT NULL,\n request_json TEXT,\n observation_json TEXT,\n preparation_json TEXT NOT NULL,\n usage_json TEXT,\n error_code TEXT,\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL,\n UNIQUE(agent_id, kind, sequence)\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS attempt_capabilities (\n attempt_id TEXT NOT NULL REFERENCES model_attempts(id) ON DELETE CASCADE,\n ordinal INTEGER NOT NULL CHECK (ordinal >= 0),\n capability_id TEXT NOT NULL,\n schema_json TEXT NOT NULL,\n PRIMARY KEY(attempt_id, ordinal),\n UNIQUE(attempt_id, capability_id)\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS attempt_tool_ordinals (\n attempt_id TEXT NOT NULL REFERENCES model_attempts(id) ON DELETE CASCADE,\n ordinal INTEGER NOT NULL CHECK (ordinal >= 0),\n provider_tool_id TEXT,\n complete INTEGER NOT NULL CHECK (complete IN (0, 1)),\n PRIMARY KEY(attempt_id, ordinal)\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS call_partitions (\n id TEXT PRIMARY KEY,\n step_id TEXT NOT NULL REFERENCES steps(id) ON DELETE CASCADE,\n ordinal INTEGER NOT NULL CHECK (ordinal >= 0),\n mode TEXT NOT NULL CHECK (mode IN ('parallel', 'serial')),\n created_at TEXT NOT NULL,\n UNIQUE(step_id, ordinal)\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS calls (\n id TEXT PRIMARY KEY,\n project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,\n workspace_id TEXT NOT NULL REFERENCES workspaces(id) ON DELETE RESTRICT,\n agent_id TEXT REFERENCES agents(id) ON DELETE SET NULL,\n step_id TEXT REFERENCES steps(id) ON DELETE CASCADE,\n attempt_id TEXT REFERENCES model_attempts(id) ON DELETE CASCADE,\n partition_id TEXT REFERENCES call_partitions(id) ON DELETE CASCADE,\n parent_call_id TEXT REFERENCES calls(id) ON DELETE CASCADE,\n source TEXT NOT NULL CHECK (source IN ('user', 'llm', 'program')),\n capability_id TEXT NOT NULL,\n provider_call_id TEXT,\n call_ordinal INTEGER CHECK (call_ordinal >= 0),\n input_json TEXT NOT NULL,\n continuation_json TEXT,\n effect TEXT NOT NULL CHECK (effect IN ('read_only', 'workspace_write', 'external', 'full')),\n policy TEXT CHECK (policy IN ('read_only', 'workspace_write', 'external', 'ask', 'auto_approve', 'full_access')),\n state TEXT NOT NULL CHECK (state IN ('WAITING', 'QUEUED', 'RUNNING', 'TERMINAL')),\n wait_reason TEXT CHECK (wait_reason IN ('user_input', 'policy_review')),\n outcome TEXT CHECK (outcome IN ('success', 'failed', 'cancelled', 'interrupted')),\n outcome_unknown INTEGER NOT NULL CHECK (outcome_unknown IN (0, 1)),\n auto_review_passed INTEGER CHECK (auto_review_passed IN (0, 1)),\n auto_review_json TEXT,\n revision INTEGER NOT NULL CHECK (revision >= 0),\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL,\n UNIQUE(attempt_id, call_ordinal)\n) STRICT;\nCREATE UNIQUE INDEX IF NOT EXISTS calls_by_provider_call ON calls(attempt_id, provider_call_id) WHERE provider_call_id IS NOT NULL;\n\nCREATE TABLE IF NOT EXISTS results (\n capture TEXT NOT NULL DEFAULT 'unknown' CHECK (capture IN ('complete', 'partial', 'unknown')),\n stored TEXT NOT NULL DEFAULT 'unknown' CHECK (stored IN ('full', 'unknown')),\n id TEXT PRIMARY KEY,\n call_id TEXT NOT NULL UNIQUE REFERENCES calls(id) ON DELETE CASCADE,\n outcome TEXT NOT NULL CHECK (outcome IN ('success', 'failed', 'cancelled', 'interrupted')),\n content_json TEXT NOT NULL,\n truncated INTEGER NOT NULL DEFAULT 0 CHECK (truncated IN (0, 1)),\n error_code TEXT,\n error_message TEXT,\n created_at TEXT NOT NULL\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS artifacts (\n id TEXT PRIMARY KEY,\n project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,\n name TEXT NOT NULL,\n media_type TEXT NOT NULL,\n relative_path TEXT NOT NULL,\n sha256 TEXT NOT NULL,\n size_bytes INTEGER NOT NULL CHECK (size_bytes >= 0),\n created_at TEXT NOT NULL\n) STRICT;\nCREATE UNIQUE INDEX IF NOT EXISTS artifact_identity ON artifacts(project_id, sha256, name, media_type);\nCREATE INDEX IF NOT EXISTS artifacts_by_path ON artifacts(relative_path);\n\nCREATE TABLE IF NOT EXISTS result_artifacts (\n result_id TEXT NOT NULL REFERENCES results(id) ON DELETE CASCADE,\n artifact_id TEXT NOT NULL REFERENCES artifacts(id) ON DELETE RESTRICT,\n PRIMARY KEY(result_id, artifact_id)\n) STRICT;\n\n-- No table for edit_file/patch_file file history: it belongs to the Executor, which owns the files\n-- and therefore the only authoritative view of what they contained. Core keeps Call semantics and\n-- asks the Executor to revert by call_id. See\n-- docs/architecture/harness-level3/09-file-snapshot-storage-decision.md.\n\nCREATE TABLE IF NOT EXISTS context_turn_groups (\n id TEXT PRIMARY KEY,\n agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,\n ordinal INTEGER NOT NULL CHECK (ordinal >= 0),\n kind TEXT NOT NULL CHECK (kind IN ('turn', 'compaction', 'included', 'ledger')),\n state TEXT NOT NULL CHECK (state IN ('OPEN', 'COMPLETE', 'ABORTED')),\n revision INTEGER NOT NULL CHECK (revision >= 0),\n created_at TEXT NOT NULL,\n completed_at TEXT,\n UNIQUE(agent_id, ordinal)\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS context_items (\n id TEXT PRIMARY KEY,\n agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,\n turn_group_id TEXT NOT NULL REFERENCES context_turn_groups(id) ON DELETE CASCADE,\n turn_group_ordinal INTEGER NOT NULL CHECK (turn_group_ordinal >= 0),\n kind TEXT NOT NULL CHECK (kind IN ('message', 'tool_calls', 'result', 'compaction')),\n source_id TEXT NOT NULL,\n role TEXT CHECK (role IN ('system', 'user', 'assistant', 'tool')),\n content TEXT NOT NULL,\n tool_call_id TEXT,\n tool_name TEXT,\n ordinal INTEGER NOT NULL CHECK (ordinal >= 0),\n visible INTEGER NOT NULL CHECK (visible IN (0, 1)),\n locked INTEGER NOT NULL CHECK (locked IN (0, 1)),\n revision INTEGER NOT NULL CHECK (revision >= 0),\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL,\n UNIQUE(agent_id, ordinal),\n UNIQUE(turn_group_id, turn_group_ordinal)\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS context_overrides (\n id TEXT PRIMARY KEY,\n agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,\n target_kind TEXT NOT NULL CHECK (target_kind IN ('agent', 'message', 'result', 'artifact')),\n target_id TEXT NOT NULL,\n instructions TEXT NOT NULL,\n revision INTEGER NOT NULL CHECK (revision >= 0),\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL,\n UNIQUE(agent_id, target_kind, target_id)\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS todo_reminder_state (\n agent_id TEXT PRIMARY KEY REFERENCES agents(id) ON DELETE CASCADE,\n last_context_rowid INTEGER NOT NULL DEFAULT 0,\n pending_tokens INTEGER NOT NULL DEFAULT 0 CHECK (pending_tokens >= 0)\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS working_context_items (\n context_item_id TEXT PRIMARY KEY REFERENCES context_items(id) ON DELETE CASCADE,\n agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,\n source_revision INTEGER NOT NULL,\n content TEXT NOT NULL,\n snipped INTEGER NOT NULL DEFAULT 0 CHECK (snipped IN (0, 1))\n) STRICT;\nCREATE INDEX IF NOT EXISTS working_context_agent ON working_context_items(agent_id);\n\nCREATE TABLE IF NOT EXISTS working_contexts (\n agent_id TEXT PRIMARY KEY REFERENCES agents(id) ON DELETE CASCADE,\n source_revision INTEGER NOT NULL,\n revision INTEGER NOT NULL,\n digest TEXT NOT NULL,\n prefixes_json TEXT NOT NULL DEFAULT '[]',\n messages_json TEXT,\n request_frame_json TEXT,\n updated_at TEXT NOT NULL\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS context_compactions (\n context_item_id TEXT PRIMARY KEY REFERENCES context_items(id) ON DELETE CASCADE,\n source_revision INTEGER NOT NULL,\n covered_item_ids_json TEXT NOT NULL,\n focus TEXT\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS projection_decisions (\n id TEXT PRIMARY KEY,\n agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,\n attempt_id TEXT REFERENCES model_attempts(id) ON DELETE CASCADE,\n stage TEXT NOT NULL,\n source_tokens INTEGER NOT NULL CHECK (source_tokens >= 0),\n projected_tokens INTEGER NOT NULL CHECK (projected_tokens >= 0),\n details_json TEXT NOT NULL,\n created_at TEXT NOT NULL\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS capability_assignments (\n agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,\n capability_id TEXT NOT NULL,\n policy TEXT NOT NULL CHECK (policy IN ('read_only', 'workspace_write', 'external', 'ask', 'auto_approve', 'full_access')),\n assigned_at TEXT NOT NULL,\n PRIMARY KEY(agent_id, capability_id)\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS capability_loads (\n agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,\n capability_id TEXT NOT NULL,\n loaded_at TEXT NOT NULL,\n PRIMARY KEY(agent_id, capability_id),\n FOREIGN KEY(agent_id, capability_id) REFERENCES capability_assignments(agent_id, capability_id) ON DELETE CASCADE\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS agent_plans (\n agent_id TEXT PRIMARY KEY REFERENCES agents(id) ON DELETE CASCADE,\n items_json TEXT NOT NULL,\n revision INTEGER NOT NULL CHECK (revision >= 0),\n updated_at TEXT NOT NULL\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS ingress_requests (\n client_id TEXT NOT NULL,\n key TEXT NOT NULL,\n operation TEXT NOT NULL,\n request_digest TEXT NOT NULL,\n response_json TEXT NOT NULL,\n created_at TEXT NOT NULL,\n PRIMARY KEY(client_id, key)\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS side_effect_ledgers (\n id TEXT PRIMARY KEY,\n step_id TEXT NOT NULL REFERENCES steps(id) ON DELETE CASCADE,\n content TEXT NOT NULL,\n created_at TEXT NOT NULL\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS purge_cleanups (\n id TEXT PRIMARY KEY,\n project_id TEXT NOT NULL REFERENCES projects(id),\n executor_state_id TEXT NOT NULL,\n payload_json TEXT NOT NULL,\n created_at TEXT NOT NULL\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS tasks_by_project ON tasks(project_id, archived_at, updated_at DESC);\nCREATE INDEX IF NOT EXISTS sessions_by_task ON sessions(task_id, archived_at, updated_at DESC);\nCREATE INDEX IF NOT EXISTS agents_by_session ON agents(session_id, parent_agent_id, created_at);\nCREATE INDEX IF NOT EXISTS pending_by_agent ON pending_messages(agent_id, ordinal);\nCREATE INDEX IF NOT EXISTS runs_by_agent ON runs(agent_id, created_at DESC);\nCREATE UNIQUE INDEX IF NOT EXISTS one_live_run_per_agent ON runs(agent_id) WHERE state != 'TERMINAL';\nCREATE INDEX IF NOT EXISTS attempts_by_step ON model_attempts(step_id, sequence);\nCREATE INDEX IF NOT EXISTS calls_by_project ON calls(project_id, state, created_at DESC);\nCREATE INDEX IF NOT EXISTS context_by_agent ON context_items(agent_id, ordinal);\nCREATE INDEX IF NOT EXISTS artifacts_by_project ON artifacts(project_id, created_at DESC);\n";
@@ -21,6 +21,8 @@ CREATE TABLE IF NOT EXISTS projects (
21
21
  ) STRICT;
22
22
 
23
23
  CREATE TABLE IF NOT EXISTS workspaces (
24
+ executor_ref TEXT,
25
+ executor_id TEXT NOT NULL DEFAULT 'remote' CHECK (executor_id IN ('local', 'remote')),
24
26
  id TEXT PRIMARY KEY,
25
27
  project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
26
28
  name TEXT NOT NULL,
@@ -141,6 +143,8 @@ CREATE TABLE IF NOT EXISTS model_attempts (
141
143
  config_digest TEXT NOT NULL,
142
144
  context_revision INTEGER NOT NULL CHECK (context_revision >= 0),
143
145
  request_digest TEXT NOT NULL,
146
+ request_json TEXT,
147
+ observation_json TEXT,
144
148
  preparation_json TEXT NOT NULL,
145
149
  usage_json TEXT,
146
150
  error_code TEXT,
@@ -189,6 +193,7 @@ CREATE TABLE IF NOT EXISTS calls (
189
193
  provider_call_id TEXT,
190
194
  call_ordinal INTEGER CHECK (call_ordinal >= 0),
191
195
  input_json TEXT NOT NULL,
196
+ continuation_json TEXT,
192
197
  effect TEXT NOT NULL CHECK (effect IN ('read_only', 'workspace_write', 'external', 'full')),
193
198
  policy TEXT CHECK (policy IN ('read_only', 'workspace_write', 'external', 'ask', 'auto_approve', 'full_access')),
194
199
  state TEXT NOT NULL CHECK (state IN ('WAITING', 'QUEUED', 'RUNNING', 'TERMINAL')),
@@ -196,6 +201,7 @@ CREATE TABLE IF NOT EXISTS calls (
196
201
  outcome TEXT CHECK (outcome IN ('success', 'failed', 'cancelled', 'interrupted')),
197
202
  outcome_unknown INTEGER NOT NULL CHECK (outcome_unknown IN (0, 1)),
198
203
  auto_review_passed INTEGER CHECK (auto_review_passed IN (0, 1)),
204
+ auto_review_json TEXT,
199
205
  revision INTEGER NOT NULL CHECK (revision >= 0),
200
206
  created_at TEXT NOT NULL,
201
207
  updated_at TEXT NOT NULL,
@@ -204,6 +210,8 @@ CREATE TABLE IF NOT EXISTS calls (
204
210
  CREATE UNIQUE INDEX IF NOT EXISTS calls_by_provider_call ON calls(attempt_id, provider_call_id) WHERE provider_call_id IS NOT NULL;
205
211
 
206
212
  CREATE TABLE IF NOT EXISTS results (
213
+ capture TEXT NOT NULL DEFAULT 'unknown' CHECK (capture IN ('complete', 'partial', 'unknown')),
214
+ stored TEXT NOT NULL DEFAULT 'unknown' CHECK (stored IN ('full', 'unknown')),
207
215
  id TEXT PRIMARY KEY,
208
216
  call_id TEXT NOT NULL UNIQUE REFERENCES calls(id) ON DELETE CASCADE,
209
217
  outcome TEXT NOT NULL CHECK (outcome IN ('success', 'failed', 'cancelled', 'interrupted')),
@@ -233,7 +241,7 @@ CREATE TABLE IF NOT EXISTS result_artifacts (
233
241
  PRIMARY KEY(result_id, artifact_id)
234
242
  ) STRICT;
235
243
 
236
- -- No table for write_file/edit_file file history: it belongs to the Executor, which owns the files
244
+ -- No table for edit_file/patch_file file history: it belongs to the Executor, which owns the files
237
245
  -- and therefore the only authoritative view of what they contained. Core keeps Call semantics and
238
246
  -- asks the Executor to revert by call_id. See
239
247
  -- docs/architecture/harness-level3/09-file-snapshot-storage-decision.md.
@@ -283,6 +291,39 @@ CREATE TABLE IF NOT EXISTS context_overrides (
283
291
  UNIQUE(agent_id, target_kind, target_id)
284
292
  ) STRICT;
285
293
 
294
+ CREATE TABLE IF NOT EXISTS todo_reminder_state (
295
+ agent_id TEXT PRIMARY KEY REFERENCES agents(id) ON DELETE CASCADE,
296
+ last_context_rowid INTEGER NOT NULL DEFAULT 0,
297
+ pending_tokens INTEGER NOT NULL DEFAULT 0 CHECK (pending_tokens >= 0)
298
+ ) STRICT;
299
+
300
+ CREATE TABLE IF NOT EXISTS working_context_items (
301
+ context_item_id TEXT PRIMARY KEY REFERENCES context_items(id) ON DELETE CASCADE,
302
+ agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
303
+ source_revision INTEGER NOT NULL,
304
+ content TEXT NOT NULL,
305
+ snipped INTEGER NOT NULL DEFAULT 0 CHECK (snipped IN (0, 1))
306
+ ) STRICT;
307
+ CREATE INDEX IF NOT EXISTS working_context_agent ON working_context_items(agent_id);
308
+
309
+ CREATE TABLE IF NOT EXISTS working_contexts (
310
+ agent_id TEXT PRIMARY KEY REFERENCES agents(id) ON DELETE CASCADE,
311
+ source_revision INTEGER NOT NULL,
312
+ revision INTEGER NOT NULL,
313
+ digest TEXT NOT NULL,
314
+ prefixes_json TEXT NOT NULL DEFAULT '[]',
315
+ messages_json TEXT,
316
+ request_frame_json TEXT,
317
+ updated_at TEXT NOT NULL
318
+ ) STRICT;
319
+
320
+ CREATE TABLE IF NOT EXISTS context_compactions (
321
+ context_item_id TEXT PRIMARY KEY REFERENCES context_items(id) ON DELETE CASCADE,
322
+ source_revision INTEGER NOT NULL,
323
+ covered_item_ids_json TEXT NOT NULL,
324
+ focus TEXT
325
+ ) STRICT;
326
+
286
327
  CREATE TABLE IF NOT EXISTS projection_decisions (
287
328
  id TEXT PRIMARY KEY,
288
329
  agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
@@ -334,6 +375,14 @@ CREATE TABLE IF NOT EXISTS side_effect_ledgers (
334
375
  created_at TEXT NOT NULL
335
376
  ) STRICT;
336
377
 
378
+ CREATE TABLE IF NOT EXISTS purge_cleanups (
379
+ id TEXT PRIMARY KEY,
380
+ project_id TEXT NOT NULL REFERENCES projects(id),
381
+ executor_state_id TEXT NOT NULL,
382
+ payload_json TEXT NOT NULL,
383
+ created_at TEXT NOT NULL
384
+ ) STRICT;
385
+
337
386
  CREATE INDEX IF NOT EXISTS tasks_by_project ON tasks(project_id, archived_at, updated_at DESC);
338
387
  CREATE INDEX IF NOT EXISTS sessions_by_task ON sessions(task_id, archived_at, updated_at DESC);
339
388
  CREATE INDEX IF NOT EXISTS agents_by_session ON agents(session_id, parent_agent_id, created_at);
@@ -1,6 +1,8 @@
1
- import { type EntityRef, type Json, type Page } from '../core/domain.js';
1
+ import type { Observability } from '../runtime/observability.js';
2
+ import { type EntityRef, type Json, type ModelMessage, type ModelResponse, type Page } from '../core/domain.js';
2
3
  import type { EventBus } from '../runtime/events.js';
3
4
  import type { Sqlite } from './database.js';
5
+ import type { AutoReviewResult, ReviewContext } from '../contracts/review.js';
4
6
  import type { EventLog } from './event-log.js';
5
7
  export type Document = Readonly<Record<string, Json>>;
6
8
  interface PageOptions {
@@ -27,7 +29,10 @@ export interface IngressCommand {
27
29
  }
28
30
  export declare class Store {
29
31
  #private;
30
- constructor(database: Sqlite, events: EventBus, eventLog?: EventLog);
32
+ readonly observability?: Observability | undefined;
33
+ /** Persist a model tool batch before reviews or execution can yield control. */
34
+ commitToolBatch(projectId: string, command: () => readonly string[]): readonly string[];
35
+ constructor(database: Sqlite, events: EventBus, eventLog?: EventLog, observability?: Observability | undefined);
31
36
  close(): Promise<void>;
32
37
  instance(): Document;
33
38
  stateRevision(): number;
@@ -41,7 +46,7 @@ export declare class Store {
41
46
  listProjects(options: PageOptions, archive?: 'active' | 'archived' | 'all'): Page<Document>;
42
47
  defaultProject(): Document | null;
43
48
  getProject(projectId: string): Document;
44
- createProject(name: string, paths: readonly string[]): Document;
49
+ createProject(name: string, paths: readonly string[], executorId?: string): Document;
45
50
  updateProject(projectId: string, expectedRevision: number, name: string): Document;
46
51
  archiveProject(projectId: string, expectedRevision: number, archived: boolean): Document;
47
52
  listWorkspaces(projectId: string): readonly Document[];
@@ -56,7 +61,7 @@ export declare class Store {
56
61
  */
57
62
  projectWorkspacePaths(projectId: string, workspaceId: string): readonly string[];
58
63
  getWorkspace(projectId: string, workspaceId: string): Document;
59
- createWorkspace(projectId: string, name: string, paths: readonly string[], makeDefault: boolean): Document;
64
+ createWorkspace(projectId: string, name: string, paths: readonly string[], makeDefault: boolean, executorId?: string): Document;
60
65
  updateWorkspace(projectId: string, workspaceId: string, expectedRevision: number, name: string, paths: readonly string[], makeDefault: boolean): Document;
61
66
  deleteWorkspace(projectId: string, workspaceId: string, expectedRevision: number): void;
62
67
  listTasks(projectId: string, options: PageOptions, archived?: boolean): Page<Document>;
@@ -75,7 +80,7 @@ export declare class Store {
75
80
  createTask(projectId: string, title: string): Document;
76
81
  updateTask(projectId: string, taskId: string, expectedRevision: number, title: string): Document;
77
82
  archiveTask(projectId: string, taskId: string, expectedRevision: number, archived: boolean): Document;
78
- deleteTask(projectId: string, taskId: string, expectedRevision: number): Document;
83
+ deleteTask(projectId: string, taskId: string, expectedRevision: number, executorStateId: string): Document;
79
84
  listSessions(projectId: string, taskId: string, options: PageOptions, archived?: boolean): Page<Document>;
80
85
  getSession(projectId: string, sessionId: string): Document;
81
86
  /**
@@ -127,6 +132,10 @@ export declare class Store {
127
132
  readonly loadedCapabilities: readonly string[];
128
133
  readonly forkContext: 'full' | 'compact' | 'none';
129
134
  readonly compactSummary?: string;
135
+ readonly compactCoveredItemIds?: readonly string[];
136
+ readonly compactRetainedItemIds?: readonly string[];
137
+ readonly compactFocus?: string;
138
+ readonly expectedParentContextRevision?: number;
130
139
  readonly initialPrompt?: string;
131
140
  readonly initialMessage?: string;
132
141
  readonly ingress?: IngressCommand;
@@ -146,7 +155,7 @@ export declare class Store {
146
155
  };
147
156
  enqueueSubagentMessage(projectId: string, parentAgentId: string, targetAgentId: string, content: string, ingress: IngressCommand): Document;
148
157
  broadcastSubagentMessage(projectId: string, parentAgentId: string, content: string, ingress: IngressCommand): Document;
149
- waitForSubagent(projectId: string, sourceAgentId: string, targetAgentId: string, timeoutMs: number, signal: AbortSignal): Promise<Document>;
158
+ waitForSubagent(projectId: string, sourceAgentId: string, targetAgentId: string, timeoutMs: number, signal: AbortSignal, afterMessageId?: string): Promise<Document>;
150
159
  listPending(projectId: string, agentId: string, options: PageOptions): Page<Document>;
151
160
  getPending(projectId: string, pendingId: string): Document;
152
161
  updatePending(projectId: string, pendingId: string, expectedRevision: number, content: string): Document;
@@ -191,7 +200,9 @@ export declare class Store {
191
200
  }): Document;
192
201
  getAttempt(projectId: string, attemptId: string): Document;
193
202
  listAttempts(projectId: string, field: 'agent_id' | 'step_id', ownerId: string, options: PageOptions): Page<Document>;
194
- transitionAttempt(projectId: string, attemptId: string, from: string, to: string, outcome?: string | null, usage?: Json | null, errorCode?: string | null): Document;
203
+ latestModelAttempt(projectId: string, agentId: string, kind?: 'agent' | 'compaction', model?: string): Document | null;
204
+ cacheUsage(projectId: string, agentId: string): Document;
205
+ transitionAttempt(projectId: string, attemptId: string, from: string, to: string, outcome?: string | null, usage?: Json | null, errorCode?: string | null, observation?: Json | null): Document;
195
206
  recordToolOrdinal(projectId: string, attemptId: string, ordinal: number, providerToolId: string | null, complete: boolean): void;
196
207
  createPartition(projectId: string, stepId: string, mode: 'parallel' | 'serial'): Document;
197
208
  listPartitions(projectId: string, stepId: string, options: PageOptions): Page<Document>;
@@ -207,6 +218,7 @@ export declare class Store {
207
218
  readonly providerCallId?: string;
208
219
  readonly callOrdinal?: number;
209
220
  readonly arguments: Json;
221
+ readonly prompt?: Json;
210
222
  readonly effect: string;
211
223
  readonly policy?: string;
212
224
  readonly state: 'WAITING' | 'QUEUED';
@@ -245,10 +257,19 @@ export declare class Store {
245
257
  readonly call: Document;
246
258
  readonly result: Document | null;
247
259
  };
248
- reviewCall(projectId: string, callId: string, approved: boolean): Document;
249
- startCall(projectId: string, callId: string): Document;
260
+ reviewCall(projectId: string, callId: string, review: AutoReviewResult): Document;
261
+ autoReviewContext(projectId: string, agentId: string): ReviewContext;
262
+ consumePendingForStep(projectId: string, stepId: string): readonly Document[];
263
+ interruptCall(projectId: string, callId: string, errorCode: string, message: string): Document;
264
+ interruptProjectCalls(projectId: string): void;
265
+ callAdmission(projectId: string, callId: string): 'ready' | 'blocked' | 'inactive';
266
+ startCall(projectId: string, callId: string, validate?: (call: Document) => void): Document;
267
+ failQueuedCall(projectId: string, callId: string, errorCode: string, errorMessage: string | null, content?: Json): {
268
+ readonly call: Document;
269
+ readonly result: Document;
270
+ };
250
271
  waitCall(projectId: string, callId: string, prompt: Json): Document;
251
- completeCall(projectId: string, callId: string, outcome: string, content: Json, errorCode?: string | null, errorMessage?: string | null, artifacts?: readonly string[], truncated?: boolean): {
272
+ completeCall(projectId: string, callId: string, outcome: string, content: Json, errorCode?: string | null, errorMessage?: string | null, artifacts?: readonly string[], truncated?: boolean, capture?: 'complete' | 'partial' | 'unknown', stored?: 'full' | 'unknown'): {
252
273
  readonly call: Document;
253
274
  readonly result: Document;
254
275
  };
@@ -259,10 +280,9 @@ export declare class Store {
259
280
  getResult(projectId: string, resultId: string): Document;
260
281
  /**
261
282
  * 02A `ResultDocument`. One `content_json` column becomes `text` or `structured` according to its
262
- * JSON type, which is the same split `normalizeOutput` makes when it decides what to store: a
263
- * Capability producing prose stores a string, one producing records stores an object or array, and
264
- * either is `null` here when the payload was too large to inline and became an Artifact instead.
265
- * That is why a client reading a large Result has to fall back to `artifact_ids` and ART-01.
283
+ * JSON type. Short results retain their original type; long non-PTC results carry a text preview
284
+ * and full-body Artifact. Earlier results may have null content with only an Artifact reference.
285
+ * Clients reading the full large Result use `artifact_ids` and ART-01.
266
286
  *
267
287
  * `error_code` stays off the wire here because the documented shape does not carry it; a failed
268
288
  * Call reports its code on `CallSummary.error_code`.
@@ -277,6 +297,14 @@ export declare class Store {
277
297
  readonly sizeBytes: number;
278
298
  }): Document;
279
299
  getArtifact(projectId: string, artifactId: string): Document;
300
+ getAgentArtifact(projectId: string, agentId: string, artifactId: string): Document;
301
+ readHistoryItem(projectId: string, agentId: string, contextId: string): Document;
302
+ searchHistory(projectId: string, agentId: string, query: string, after: number, limit: number, scope?: {
303
+ readonly agent_id?: string;
304
+ readonly session_id?: string;
305
+ readonly task_id?: string;
306
+ readonly after_context_item_id?: string;
307
+ }): Document;
280
308
  listArtifacts(projectId: string, options: PageOptions): Page<Document>;
281
309
  /**
282
310
  * 02A `ContextPageQuery.direction`. `backward` walks from the newest item towards the oldest,
@@ -286,14 +314,24 @@ export declare class Store {
286
314
  * paged away from the conversation instead of into its history.
287
315
  */
288
316
  listContext(projectId: string, agentId: string, options: PageOptions, direction?: 'forward' | 'backward'): Page<Document>;
289
- contextSource(projectId: string, agentId: string): {
317
+ contextSource(projectId: string, agentId: string, completedOnly?: boolean): {
290
318
  readonly agent: Document;
291
319
  readonly items: readonly Document[];
320
+ readonly workingItems: readonly Document[];
321
+ readonly unsettledItemIds: readonly string[];
322
+ readonly todo: Document | null;
292
323
  readonly overrides: readonly Document[];
293
324
  readonly capabilities: readonly Document[];
294
325
  };
295
- getPlan(projectId: string, agentId: string): Document | null;
296
- updatePlan(projectId: string, agentId: string, items: Json): Document;
326
+ workingContext(projectId: string, agentId: string): Document;
327
+ saveWorkingContext(projectId: string, agentId: string, sourceRevision: number, edits: readonly {
328
+ readonly id: string;
329
+ readonly content: string;
330
+ }[], messages: Json, prefixes: Json, requestFrame: Json): void;
331
+ compactionSources(projectId: string, contextItemId: string): Document;
332
+ getTodo(projectId: string, agentId: string): Document | null;
333
+ updateTodo(projectId: string, agentId: string, items: Json): Document;
334
+ syncTodoReminder(projectId: string, agentId: string): void;
297
335
  getContextItem(projectId: string, contextItemId: string): Document;
298
336
  updateContextItem(projectId: string, contextItemId: string, expectedRevision: number, content: string): Document;
299
337
  setContextVisibility(projectId: string, agentId: string, turnGroupIds: readonly string[], visible: boolean, expectedContextRevision: number): readonly Document[];
@@ -307,7 +345,7 @@ export declare class Store {
307
345
  listProjectionDecisions(projectId: string, agentId: string, options: PageOptions): Page<Document>;
308
346
  resetProjection(projectId: string, agentId: string, expectedContextRevision: number, stages?: readonly string[]): Document;
309
347
  appendProjectionDecision(projectId: string, agentId: string, attemptId: string | null, stage: string, sourceTokens: number, projectedTokens: number, details: Json): Document;
310
- compactContext(projectId: string, agentId: string, summary: string, preserveFromOrdinal: number): Document;
348
+ compactContext(projectId: string, agentId: string, summary: string, coveredItemIds: readonly string[], expectedContextRevision: number, focus?: string, expectedAgentRevision?: number): Document;
311
349
  forkContext(projectId: string, sourceAgentId: string, targetAgentId: string, throughOrdinal: number): readonly Document[];
312
350
  createContextFork(projectId: string, sourceAgentId: string, input: {
313
351
  readonly throughTurnGroupId: string;
@@ -316,6 +354,8 @@ export declare class Store {
316
354
  readonly model: string;
317
355
  readonly modelReasoningEffort: string;
318
356
  }): Document;
357
+ /** Persist one completed Provider response atomically with its tool batch. */
358
+ appendModelResponse(projectId: string, agentId: string, stepId: string, text: string, calls: NonNullable<ModelMessage['toolCalls']>, response: ModelResponse): Document;
319
359
  appendAssistantMessage(projectId: string, agentId: string, stepId: string, content: string): Document;
320
360
  appendToolCalls(projectId: string, agentId: string, stepId: string, calls: readonly {
321
361
  readonly id: string;
@@ -338,7 +378,13 @@ export declare class Store {
338
378
  stepOutput(projectId: string, stepId: string): Document;
339
379
  storageStats(): Document;
340
380
  exportState(): Json;
341
- purgeArchived(projectId: string, before: string): Document;
381
+ purgeArchived(projectId: string, before: string, executorStateId: string): Document;
382
+ pendingPurges(projectId: string): readonly Document[];
383
+ completePurge(projectId: string, cleanupId: string): void;
384
+ artifactPathReferenced(relativePath: string): boolean;
342
385
  snapshot(projectId: string, maxEntities: number): Document;
386
+ workspaceOwner(workspaceId: string): {
387
+ readonly projectId: string;
388
+ };
343
389
  }
344
390
  export {};