@lmctl-ai/lmctl 0.1.341 → 0.2.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 (182) hide show
  1. package/CHANGELOG.md +131 -0
  2. package/README.md +11 -51
  3. package/data/price-aliases.json +1 -0
  4. package/data/price-data.json +1 -1
  5. package/dist/cli/batch.d.ts +2 -0
  6. package/dist/cli/busy-evidence.d.ts +27 -0
  7. package/dist/cli/chat-cycle.d.ts +59 -0
  8. package/dist/cli/chat-exec.d.ts +83 -0
  9. package/dist/cli/chat-policy.d.ts +22 -0
  10. package/dist/cli/chat.d.ts +23 -0
  11. package/dist/cli/code-resolver.d.ts +8 -0
  12. package/dist/cli/common.d.ts +54 -0
  13. package/dist/cli/context.d.ts +12 -0
  14. package/dist/cli/cost.d.ts +2 -0
  15. package/dist/cli/export.d.ts +2 -0
  16. package/dist/cli/index.d.ts +3 -0
  17. package/dist/cli/index.js +1081 -1891
  18. package/dist/cli/infra.d.ts +78 -0
  19. package/dist/cli/ls.d.ts +2 -0
  20. package/dist/cli/mailbox-cli.d.ts +6 -0
  21. package/dist/cli/managed.d.ts +62 -0
  22. package/dist/cli/mcp-install.d.ts +23 -0
  23. package/dist/cli/mcp.d.ts +2 -0
  24. package/dist/cli/paths.d.ts +50 -0
  25. package/dist/cli/plan-wizard.d.ts +42 -0
  26. package/dist/cli/plan.d.ts +20 -0
  27. package/dist/cli/queue-delivery.d.ts +107 -0
  28. package/dist/cli/ratelimit.d.ts +3 -0
  29. package/dist/cli/remote-fetch.d.ts +7 -0
  30. package/dist/cli/run-queries.d.ts +82 -0
  31. package/dist/cli/running.d.ts +5 -0
  32. package/dist/cli/schema.sql +20 -1
  33. package/dist/cli/script.d.ts +6 -0
  34. package/dist/cli/script.js +2097 -0
  35. package/dist/cli/self-identity.d.ts +25 -0
  36. package/dist/cli/session-idle.d.ts +118 -0
  37. package/dist/cli/status.d.ts +22 -0
  38. package/dist/cli/tail.d.ts +18 -0
  39. package/dist/cli/target-resolver.d.ts +139 -0
  40. package/dist/cli/team-status.d.ts +91 -0
  41. package/dist/cli/teamfile-compat.d.ts +71 -0
  42. package/dist/cli/teamfile-store.d.ts +18 -0
  43. package/dist/cli/teamfile-template.d.ts +40 -0
  44. package/dist/cli/template-env.d.ts +25 -0
  45. package/dist/cli/terminal-lock.d.ts +25 -0
  46. package/dist/cli/terminal.d.ts +23 -0
  47. package/dist/cli/time-filter.d.ts +10 -0
  48. package/dist/cli/update-notifier.d.ts +11 -0
  49. package/dist/cli/version-util.d.ts +2 -0
  50. package/dist/config/seed_templates.d.ts +1 -0
  51. package/dist/db/backfill-event-log.d.ts +26 -0
  52. package/dist/db/delegation.d.ts +37 -0
  53. package/dist/db/escalation_vocab.d.ts +45 -0
  54. package/dist/db/event-log.d.ts +40 -0
  55. package/dist/db/index.d.ts +1213 -0
  56. package/dist/db/inflight-acquire.d.ts +70 -0
  57. package/dist/db/invocation-event-projections.d.ts +65 -0
  58. package/dist/db/ledger-identity.test.d.ts +1 -0
  59. package/dist/db/live-event-projections.d.ts +92 -0
  60. package/dist/db/migrate.d.ts +51 -0
  61. package/dist/db/open-readonly.d.ts +2 -0
  62. package/dist/db/process-liveness.d.ts +37 -0
  63. package/dist/db/sqlite.d.ts +47 -0
  64. package/dist/db/sqlite.test.d.ts +1 -0
  65. package/dist/db/terminal-lock.d.ts +14 -0
  66. package/dist/db/tracked-invocation-reconcile.d.ts +52 -0
  67. package/dist/db/tracked-invocation.d.ts +116 -0
  68. package/dist/mcp/dispatch.d.ts +45 -0
  69. package/dist/mcp/mediation.d.ts +128 -0
  70. package/dist/mcp/path-warning.d.ts +7 -0
  71. package/dist/mcp/server.d.ts +63 -0
  72. package/dist/mcp/tools.d.ts +24 -0
  73. package/dist/providers/adapter.d.ts +265 -0
  74. package/dist/providers/adapters/agy-adapter.d.ts +17 -0
  75. package/dist/providers/adapters/claude-adapter.d.ts +20 -0
  76. package/dist/providers/adapters/codex-adapter.d.ts +22 -0
  77. package/dist/providers/adapters/copilot-adapter.d.ts +16 -0
  78. package/dist/providers/adapters/dsh-adapter.d.ts +89 -0
  79. package/dist/providers/adapters/gemini-adapter.d.ts +44 -0
  80. package/dist/providers/adapters/hermes-adapter.d.ts +71 -0
  81. package/dist/providers/adapters/index.d.ts +35 -0
  82. package/dist/providers/adapters/kimi-adapter.d.ts +16 -0
  83. package/dist/providers/adapters/lmplayer-adapter.d.ts +14 -0
  84. package/dist/providers/adapters/opencode-adapter.d.ts +11 -0
  85. package/dist/providers/adapters/pi-adapter.d.ts +17 -0
  86. package/dist/providers/adapters/print-turn.d.ts +29 -0
  87. package/dist/providers/adapters/qwen-adapter.d.ts +11 -0
  88. package/dist/providers/agy.d.ts +32 -0
  89. package/dist/providers/base-adapter.d.ts +107 -0
  90. package/dist/providers/base.d.ts +12 -0
  91. package/dist/providers/binary-check.d.ts +102 -0
  92. package/dist/providers/bundle.d.ts +118 -0
  93. package/dist/providers/claude-mock.d.ts +90 -0
  94. package/dist/providers/claude.d.ts +21 -0
  95. package/dist/providers/codex-config.d.ts +15 -0
  96. package/dist/providers/codex.d.ts +25 -0
  97. package/dist/providers/config/model-selection-lint.d.ts +45 -0
  98. package/dist/providers/config/team-config.d.ts +183 -0
  99. package/dist/providers/config/types.d.ts +146 -0
  100. package/dist/providers/config/user-config.d.ts +88 -0
  101. package/dist/providers/copilot.d.ts +21 -0
  102. package/dist/providers/debug.d.ts +128 -0
  103. package/dist/providers/dsh-sidecar.d.ts +65 -0
  104. package/dist/providers/dsh-zstd.d.ts +27 -0
  105. package/dist/providers/dsh.d.ts +37 -0
  106. package/dist/providers/frontmatter.d.ts +46 -0
  107. package/dist/providers/gemini-session-reader.d.ts +151 -0
  108. package/dist/providers/gemini.d.ts +39 -0
  109. package/dist/providers/hermes.d.ts +90 -0
  110. package/dist/providers/index.d.ts +421 -0
  111. package/dist/providers/kimi.d.ts +37 -0
  112. package/dist/providers/model-catalog.d.ts +141 -0
  113. package/dist/providers/one-shot-timeout.d.ts +1 -0
  114. package/dist/providers/opencode-config.d.ts +62 -0
  115. package/dist/providers/opencode.d.ts +40 -0
  116. package/dist/providers/parsers/acp.d.ts +47 -0
  117. package/dist/providers/parsers/codex-app-server.d.ts +60 -0
  118. package/dist/providers/parsers/stream-json.d.ts +57 -0
  119. package/dist/providers/passive-readers/agy.d.ts +155 -0
  120. package/dist/providers/passive-readers/copilot.d.ts +173 -0
  121. package/dist/providers/passive-readers/dsh.d.ts +34 -0
  122. package/dist/providers/passive-readers/kimi.d.ts +44 -0
  123. package/dist/providers/passive-readers/opencode.d.ts +89 -0
  124. package/dist/providers/passive-readers/qwen.d.ts +151 -0
  125. package/dist/providers/passive-readers/shared.d.ts +138 -0
  126. package/dist/providers/passive-session-reader.d.ts +188 -0
  127. package/dist/providers/path-utils.d.ts +1 -0
  128. package/dist/providers/pi.d.ts +32 -0
  129. package/dist/providers/pricing.d.ts +107 -0
  130. package/dist/providers/qwen.d.ts +22 -0
  131. package/dist/providers/ratelimit-readings.d.ts +32 -0
  132. package/dist/providers/registry.d.ts +31 -0
  133. package/dist/providers/resume.d.ts +43 -0
  134. package/dist/providers/seed-instruction.d.ts +3 -0
  135. package/dist/providers/self-session-env.d.ts +21 -0
  136. package/dist/providers/session-limit.d.ts +9 -0
  137. package/dist/providers/strip-ansi.d.ts +5 -0
  138. package/dist/providers/transport-errors.d.ts +94 -0
  139. package/dist/providers/transport-lifecycle.d.ts +63 -0
  140. package/dist/providers/transport-wrappers.d.ts +110 -0
  141. package/dist/providers/transports/acp-profiles/config-option-step.d.ts +24 -0
  142. package/dist/providers/transports/acp-profiles/copilot.d.ts +12 -0
  143. package/dist/providers/transports/acp-profiles/gemini.d.ts +22 -0
  144. package/dist/providers/transports/acp-profiles/hermes.d.ts +27 -0
  145. package/dist/providers/transports/acp-profiles/index.d.ts +19 -0
  146. package/dist/providers/transports/acp-profiles/kimi.d.ts +24 -0
  147. package/dist/providers/transports/acp-profiles/opencode.d.ts +26 -0
  148. package/dist/providers/transports/acp-profiles/types.d.ts +258 -0
  149. package/dist/providers/transports/acp-rpc.d.ts +18 -0
  150. package/dist/providers/transports/acp.d.ts +215 -0
  151. package/dist/providers/transports/codex-app-server.d.ts +190 -0
  152. package/dist/providers/transports/hold-open.d.ts +120 -0
  153. package/dist/providers/transports/manager.d.ts +21 -0
  154. package/dist/providers/transports/stream-json.d.ts +162 -0
  155. package/dist/providers/transports/types.d.ts +56 -0
  156. package/dist/providers/types.d.ts +154 -0
  157. package/dist/providers/yolo-contract.d.ts +131 -0
  158. package/dist/script/adapter.d.ts +101 -0
  159. package/dist/script/batch-script.d.ts +7 -0
  160. package/dist/script/index.d.ts +3 -0
  161. package/dist/script/lmscript.d.ts +550 -0
  162. package/dist/shared/policy.d.ts +74 -0
  163. package/dist/shared-lib/db.d.ts +25 -0
  164. package/dist/shared-lib/passive-session-reader.d.ts +2 -0
  165. package/dist/shared-lib/provider-parse.d.ts +6 -0
  166. package/dist/templates/basedir.d.ts +52 -0
  167. package/dist/uri/lmctl_uri.d.ts +38 -0
  168. package/durable-memory/model_selection.md +108 -25
  169. package/examples/README.md +103 -0
  170. package/examples/anonymous-panel.lms +3 -0
  171. package/examples/batch-prompts.txt +9 -0
  172. package/examples/batchscript.lms +67 -0
  173. package/examples/n8n/agent-lib.lms +56 -0
  174. package/examples/n8n/git-lib.lms +93 -0
  175. package/examples/n8n/mock-support.db +0 -0
  176. package/examples/n8n/multi-agent-review.lms +107 -0
  177. package/examples/n8n/news-digest.lms +159 -0
  178. package/examples/n8n/rag-search.lms +163 -0
  179. package/examples/n8n/support-agent.lms +88 -0
  180. package/examples/n8n/ticket-triage.lms +156 -0
  181. package/package.json +13 -17
  182. package/dist/cli/side_effect_classifier.json +0 -19
@@ -0,0 +1,1213 @@
1
+ import Database from './sqlite.js';
2
+ import { type AgentInflightAcquireOptions, type AgentInflightAcquireResult, type AgentInflightInput, type AgentInflightObservedHolder, type AgentInflightRow } from './delegation.js';
3
+ import { type TrackedInvocationFinishPatch, type TrackedInvocationInput, type TrackedInvocationListFilter, type TrackedInvocationRow, type TrackedInvocationTerminalClaimFilter } from './tracked-invocation.js';
4
+ import { type TerminalLockRecord } from './terminal-lock.js';
5
+ import { ESCALATION_INVALID_DECISION } from './escalation_vocab.js';
6
+ export type { TrackedInvocationFinishPatch, TrackedInvocationInput, TrackedInvocationKind, TrackedInvocationListFilter, TrackedInvocationOrigin, TrackedInvocationRow, TrackedInvocationStatus, TrackedInvocationTerminalClaimFilter, } from './tracked-invocation.js';
7
+ export type { AgentInflightAcquireOptions, AgentInflightAcquireResult, AgentInflightInput, AgentInflightObservedHolder, AgentInflightRow, } from './delegation.js';
8
+ export type { TerminalLockRecord } from './terminal-lock.js';
9
+ export interface WorkflowRow {
10
+ id: number;
11
+ name: string;
12
+ version: number;
13
+ definition_schema_version: number;
14
+ definition: string;
15
+ active_version_id: number | null;
16
+ created_at: string;
17
+ source_uri: string | null;
18
+ source_sha256: string | null;
19
+ }
20
+ export interface WorkflowVersionRow {
21
+ id: number;
22
+ workflow_id: number;
23
+ version: number;
24
+ definition: string;
25
+ author: string | null;
26
+ created_at: string;
27
+ }
28
+ export interface TriggerInstanceRow {
29
+ id: number;
30
+ workflow_id: number;
31
+ trigger_id: string;
32
+ trigger_type: string;
33
+ parameters: string;
34
+ enabled: number;
35
+ cursor: string | null;
36
+ last_poll_at: string | null;
37
+ last_failure_at: string | null;
38
+ last_failure: string | null;
39
+ created_at: string;
40
+ }
41
+ export interface TeamRow {
42
+ id: number;
43
+ name: string;
44
+ created_at: string;
45
+ }
46
+ export interface TeamMemberRow {
47
+ id: number;
48
+ team_id: number;
49
+ alias: string;
50
+ role: string;
51
+ provider: string;
52
+ model: string | null;
53
+ sessionid: string | null;
54
+ sessiondir: string;
55
+ state: string;
56
+ state_reason: string | null;
57
+ state_updated_at: string | null;
58
+ role_prompt: string | null;
59
+ prompt_snapshot: string | null;
60
+ /**
61
+ * V4.5 managed-team abstraction: when `provider` is an abstract token
62
+ * (provider1|2|3), the concrete provider chosen at seed time is
63
+ * snapshotted here. NULL = unseeded, or `provider` is already concrete.
64
+ * Frozen at seed; a later workspace-mapping change never mutates it
65
+ * (mirrors prompt_snapshot).
66
+ */
67
+ provider_resolved: string | null;
68
+ created_at: string;
69
+ }
70
+ /**
71
+ * The provider a SEEDED member actually runs as. Prefer the seed-time
72
+ * snapshot (`provider_resolved`) over the possibly-abstract `provider`
73
+ * token, so display/audit reflect the concrete provider. Pure reader —
74
+ * no DB, no resolution; safe for any team_member-shaped row.
75
+ */
76
+ export declare function effectiveProvider(m: {
77
+ provider: string;
78
+ provider_resolved?: string | null;
79
+ }): string;
80
+ export interface TeamMemberCompanionRow {
81
+ id: number;
82
+ interpreter_id: number;
83
+ companion_id: number;
84
+ created_at: string;
85
+ }
86
+ export interface ProjectRow {
87
+ id: number;
88
+ name: string;
89
+ description: string | null;
90
+ local_path: string;
91
+ workflow_name: string;
92
+ team_id: number;
93
+ default_branch: string | null;
94
+ intake_config: string | null;
95
+ test_argv: string | null;
96
+ /**
97
+ * DG-6 D-6 — per-project test command consumed by the `focused_test`
98
+ * shell-step in the bugfix-* workflows via `{{project.test_command}}`.
99
+ * NULL or empty falls back to `npm test` (backward-compat for projects
100
+ * created before the v31 migration). Set to a project-specific command
101
+ * (e.g. `go test ./...`, `cargo test --quiet`) to unlock non-Node
102
+ * projects without forking the workflow JSON.
103
+ */
104
+ test_command: string | null;
105
+ durable_memory_mode: string;
106
+ created_at: string;
107
+ }
108
+ export interface ProjectWorkflowRow {
109
+ id: number;
110
+ project_id: number;
111
+ workflow_id: number;
112
+ condition: string | null;
113
+ parameters_override: string | null;
114
+ enabled: number;
115
+ priority: number;
116
+ created_at: string;
117
+ }
118
+ export type IssueStatus = 'open' | 'assigned' | 'closed' | 'reopened';
119
+ export type IssueSeverity = 'low' | 'medium' | 'high' | 'critical';
120
+ export interface IssueRow {
121
+ id: number;
122
+ project_id: number;
123
+ title: string;
124
+ body: string;
125
+ status: IssueStatus;
126
+ severity: IssueSeverity;
127
+ labels: string | null;
128
+ ai_test_path: string | null;
129
+ source_run_id: number | null;
130
+ assigned_run_id: number | null;
131
+ closed_run_id: number | null;
132
+ commit_hash: string | null;
133
+ created_at: string;
134
+ updated_at: string;
135
+ closed_at: string | null;
136
+ }
137
+ export interface JobRow {
138
+ id: number;
139
+ project_id: number;
140
+ workflow_id_override: number | null;
141
+ source: string;
142
+ source_ref: string | null;
143
+ payload: string | null;
144
+ status: string;
145
+ priority: number;
146
+ available_at: string;
147
+ locked_by: string | null;
148
+ locked_until: string | null;
149
+ attempt_count: number;
150
+ last_error: string | null;
151
+ dedupe_key: string | null;
152
+ concurrency_key_resolved: string | null;
153
+ enqueued_at: string;
154
+ started_at: string | null;
155
+ ended_at: string | null;
156
+ run_id: number | null;
157
+ }
158
+ export type JobClaimFailureReason = 'not_found' | 'not_claimable' | 'locked' | 'concurrency_limit';
159
+ export type JobClaimByIdResult = {
160
+ claimed: true;
161
+ job: JobRow;
162
+ } | {
163
+ claimed: false;
164
+ reason: JobClaimFailureReason;
165
+ job_id: number;
166
+ status?: string;
167
+ locked_by?: string | null;
168
+ locked_until?: string | null;
169
+ };
170
+ export interface RunRow {
171
+ id: number;
172
+ job_id: number;
173
+ workflow_id: number;
174
+ team_id: number;
175
+ current_state: string | null;
176
+ terminal_state: string | null;
177
+ workflow_version_id: number | null;
178
+ started_at: string;
179
+ ended_at: string | null;
180
+ paused_at: string | null;
181
+ blocker: string | null;
182
+ parent_run_id: number | null;
183
+ /** DEF-1 — JSON map keyed by step.id: the operator's escalation-resume
184
+ * decision (chosen declared outcome token, or the reserved `@abort`).
185
+ * NULL when no escalation decision is pending. */
186
+ pending_escalation_decision: string | null;
187
+ }
188
+ export interface StepRow {
189
+ id: number;
190
+ run_id: number;
191
+ parent_step_id: number | null;
192
+ state_name: string;
193
+ outcome: string | null;
194
+ status_detail: string | null;
195
+ started_at: string;
196
+ ended_at: string | null;
197
+ }
198
+ export interface StepArtifactRow {
199
+ id: number;
200
+ step_id: number;
201
+ kind: string;
202
+ path: string | null;
203
+ content: string | null;
204
+ sha: string | null;
205
+ /** A2.5 audit-trail: 'library' | 'subprocess' | null. */
206
+ provider_call_mode: 'library' | 'subprocess' | null;
207
+ created_at: string;
208
+ }
209
+ export interface ExternalObjectRow {
210
+ id: number;
211
+ type: string;
212
+ provider: string;
213
+ project_id: number;
214
+ run_id: number | null;
215
+ external_id: string;
216
+ url: string | null;
217
+ status: string | null;
218
+ metadata: string | null;
219
+ last_seen_at: string | null;
220
+ created_at: string;
221
+ }
222
+ export interface ExternalSignalRow {
223
+ id: number;
224
+ object_id: number;
225
+ kind: string;
226
+ source: string | null;
227
+ occurred_at: string | null;
228
+ payload: string | null;
229
+ signal_hash: string | null;
230
+ handled_at: string | null;
231
+ created_at: string;
232
+ }
233
+ export interface AttentionRow {
234
+ id: number;
235
+ kind: string;
236
+ severity: string | null;
237
+ project_id: number | null;
238
+ run_id: number | null;
239
+ external_object_id: number | null;
240
+ requires_user: number;
241
+ payload: string | null;
242
+ created_at: string;
243
+ acknowledged_at: string | null;
244
+ }
245
+ export interface SubmittedPrRow {
246
+ id: number;
247
+ repo: string;
248
+ pr_url: string;
249
+ issue_url: string | null;
250
+ local_path: string | null;
251
+ branch: string | null;
252
+ head_sha: string | null;
253
+ status: string;
254
+ submitted_at: string;
255
+ last_scanned_at: string | null;
256
+ last_followup_at: string | null;
257
+ last_known_state: string | null;
258
+ last_known_checks: string | null;
259
+ last_known_review: string | null;
260
+ visibility_status: string | null;
261
+ created_by_run_id: number | null;
262
+ }
263
+ export interface DurableMemoryRow {
264
+ id: number;
265
+ project_id: number;
266
+ type: string;
267
+ name: string;
268
+ description: string | null;
269
+ body: string;
270
+ created_at: string;
271
+ updated_at: string;
272
+ }
273
+ export interface SessionEventRow {
274
+ id: number;
275
+ run_id: number;
276
+ step_id: number | null;
277
+ kind: string;
278
+ timestamp_ms: number;
279
+ payload: string;
280
+ payload_truncated: number;
281
+ overflow_artifact_id: number | null;
282
+ }
283
+ export interface RollupStatRow {
284
+ id: number;
285
+ run_id: number;
286
+ step_id: number | null;
287
+ metric_kind: string;
288
+ value: number;
289
+ bucket_start_ms: number;
290
+ bucket_end_ms: number;
291
+ updated_at: number;
292
+ }
293
+ export interface DriftSignalRow {
294
+ id: number;
295
+ team_member_id: number;
296
+ project_id: number | null;
297
+ signal_kind: string;
298
+ severity: 'low' | 'medium' | 'high';
299
+ description: string;
300
+ detected_at: number;
301
+ expires_at: number | null;
302
+ acknowledged_at: number | null;
303
+ payload: string | null;
304
+ }
305
+ export interface SessionSizeRow {
306
+ id: number;
307
+ team_name: string;
308
+ alias: string;
309
+ sessionid: string;
310
+ input_tokens: number | null;
311
+ cache_tokens: number | null;
312
+ output_tokens: number | null;
313
+ total_tokens: number | null;
314
+ observed_at: string;
315
+ }
316
+ /** webui v2 / v19 — operator transition-graph session. */
317
+ export interface WebuiSessionRow {
318
+ session_id: string;
319
+ user_id: string;
320
+ current_node: string;
321
+ node_params: string | null;
322
+ prev_node_stack: string | null;
323
+ pending_form: string | null;
324
+ chatbot_session_id: string | null;
325
+ auth_state: string;
326
+ last_transition_at: string;
327
+ created_at: string;
328
+ }
329
+ /** webui v2 / v19 — chatbot sidekick conversation. */
330
+ export interface ChatbotSessionRow {
331
+ chatbot_session_id: string;
332
+ user_id: string;
333
+ title: string | null;
334
+ created_at: string;
335
+ last_active_at: string;
336
+ status: string;
337
+ }
338
+ /** webui v2 / v19 — chatbot conversation message. */
339
+ export interface ChatbotMessageRow {
340
+ id: number;
341
+ chatbot_session_id: string;
342
+ role: string;
343
+ content: string;
344
+ tool_calls: string | null;
345
+ created_at: string;
346
+ }
347
+ /** Phase A / v20 — lmctl_config row (canonical config replacing env vars). */
348
+ export interface LmctlConfigRow {
349
+ key: string;
350
+ value: string;
351
+ updated_at: string;
352
+ }
353
+ /** R2.3 — interactive_session row (schema v12). */
354
+ export interface InteractiveSessionRow {
355
+ id: number;
356
+ run_id: number;
357
+ step_id: number;
358
+ state_name: string;
359
+ turn_count: number;
360
+ last_agent_output: string | null;
361
+ last_operator_input: string | null;
362
+ status: 'waiting_operator' | 'waiting_agent' | 'done' | 'timeout';
363
+ started_at: number;
364
+ last_activity_at: number;
365
+ }
366
+ export type InteractiveResumeResult = {
367
+ resumed: true;
368
+ session: InteractiveSessionRow;
369
+ run: RunRow;
370
+ job: JobRow;
371
+ } | {
372
+ resumed: false;
373
+ reason: 'not_found' | 'not_waiting_operator' | 'run_not_found';
374
+ };
375
+ export interface InsertResult<T> {
376
+ row: T;
377
+ inserted: boolean;
378
+ }
379
+ export type ResumeRunResult = {
380
+ resumed: true;
381
+ run: RunRow;
382
+ job: JobRow;
383
+ } | {
384
+ resumed: false;
385
+ reason: 'run_not_found' | 'workflow_not_found' | 'not_paused' | 'state_not_found' | 'invalid_outcome';
386
+ valid?: string[];
387
+ };
388
+ /**
389
+ * DEF-1 §1.3 — result of the session-FREE `resumeEscalationTxn` (parallel to
390
+ * `resumeInteractiveTxn` MINUS the `interactive_session` dependency). A
391
+ * `workflow_escalation` attention has no session row; the escalated step is
392
+ * identified by `payload.step.id`.
393
+ */
394
+ export type EscalationResumeResult = {
395
+ resumed: true;
396
+ run: RunRow;
397
+ job: JobRow;
398
+ } | {
399
+ resumed: false;
400
+ reason: 'not_found' | 'wrong_kind' | 'already_acked' | 'run_not_found' | 'no_step' | typeof ESCALATION_INVALID_DECISION;
401
+ };
402
+ /** L-7 / v28 — webui Prompt id → V4 job dispatch map. */
403
+ export interface PromptDispatchRow {
404
+ prompt_id: string;
405
+ project_id: number;
406
+ job_id: number;
407
+ text: string;
408
+ context_json: string | null;
409
+ created_at: string;
410
+ completed_at: string | null;
411
+ }
412
+ /** L-8 Gap 4 / v29 — webui workspace (single per user per D7). */
413
+ export interface WorkspaceRow {
414
+ id: string;
415
+ name: string;
416
+ idle_threshold_ms: number;
417
+ created_at: string;
418
+ deleted_at: string | null;
419
+ }
420
+ /** L-8 Gap 4 / v29 — webui project (K1: distinct from v27 `project`). */
421
+ export interface WebProjectRow {
422
+ id: number;
423
+ workspace_id: string;
424
+ name: string;
425
+ repo_path: string;
426
+ config_json: string | null;
427
+ linked_v4_project_id: number | null;
428
+ last_active_at: string | null;
429
+ created_at: string;
430
+ deleted_at: string | null;
431
+ }
432
+ /**
433
+ * MX-9 final fixup / v30: per-user mailbox cursor row. The
434
+ * VersionedMailboxClient's `getCursor()` value (a S3 `VersionId` string) is
435
+ * persisted here at the end of each successfully-processed batch so an agent
436
+ * restart resumes from the last advanced cursor instead of re-walking the
437
+ * entire backlog. `last_processed_version_id` is nullable to represent the
438
+ * cold-start sentinel ("we've checkpointed at `last_processed_at` but no
439
+ * version has yet been processed" — the seed row written before the poll
440
+ * loop starts; see `agent-mailbox-bootstrap.ts`).
441
+ */
442
+ export interface MailboxCursorRow {
443
+ user_id: string;
444
+ mailbox_key: string;
445
+ last_processed_version_id: string | null;
446
+ /**
447
+ * v35 — GET-sequence transport cursor: the highest peer stream sequence
448
+ * consumed. Null for legacy version-id rows / cold-start sentinels. The
449
+ * sequential client persists this; the versioned client uses version_id.
450
+ */
451
+ last_processed_seq?: number | null;
452
+ last_processed_at: string;
453
+ }
454
+ /** v36 — per-device monthly S3 usage counters for quota accounting. */
455
+ export interface UsageMeterRow {
456
+ user_id: string;
457
+ device_id: string;
458
+ period: string;
459
+ put_count: number;
460
+ get_count: number;
461
+ list_count: number;
462
+ updated_at: string;
463
+ }
464
+ /** v32 — path-keyed root team registry. Dark substrate for team workflow unification. */
465
+ export interface RootTeamRow {
466
+ teamfile_path: string;
467
+ name: string | null;
468
+ raw_text: string;
469
+ raw_sha256: string;
470
+ registered_at: string;
471
+ last_seeded_at: string | null;
472
+ }
473
+ /** v32 — append-only request/response audit row for cross/team sends. */
474
+ export interface TeamChatLogRow {
475
+ id: number;
476
+ root_teamfile: string;
477
+ sender_teamfile: string | null;
478
+ sender_alias: string | null;
479
+ receiver_teamfile: string;
480
+ receiver_alias: string;
481
+ message: string;
482
+ response: string | null;
483
+ origin: 'agent' | 'operator' | 'mcp' | 'unknown';
484
+ status: string;
485
+ error_detail: string | null;
486
+ truncated: number;
487
+ /**
488
+ * v61 — batch-vs-individual framing: 'batch' for rows belonging to a batch
489
+ * run, NULL for individual chats. Distinct from message_pending.message_type
490
+ * (request/response lane semantics). Deliberately NOT propagated into
491
+ * backfill-event-log copies or export (their payload shapes predate it).
492
+ */
493
+ message_type: 'batch' | null;
494
+ created_at: string;
495
+ }
496
+ /** v34 — per-mailbox daemon push watermark for team_chat_log tailing. */
497
+ export interface TeamChatLogCursorRow {
498
+ cursor_key: string;
499
+ last_pushed_id: number;
500
+ updated_at: string;
501
+ }
502
+ export interface RuntimeProcessLockRecord {
503
+ lock_id: string;
504
+ holder_pid: number;
505
+ holder_host: string;
506
+ started_at: string;
507
+ last_heartbeat: string;
508
+ }
509
+ export type RuntimeProcessLockAcquireResult = {
510
+ acquired: true;
511
+ lock: RuntimeProcessLockRecord;
512
+ previous: RuntimeProcessLockRecord | null;
513
+ } | {
514
+ acquired: false;
515
+ current: RuntimeProcessLockRecord;
516
+ };
517
+ export type RootTeamInput = RootTeamRow;
518
+ export type TeamChatLogInput = Omit<TeamChatLogRow, 'id' | 'message_type'> & {
519
+ /** v61 batch framing; omit for individual chats (stored as NULL). */
520
+ message_type?: 'batch' | null;
521
+ /** Event-only historical target attribution; not stored in team_chat_log. */
522
+ receiver_provider?: string | null;
523
+ receiver_session_id?: string | null;
524
+ /** Exact provider-visible prompt; unlike team_chat_log.message, this is not
525
+ * truncated before it is recorded in the CREATE event. */
526
+ provider_prompt?: string | null;
527
+ };
528
+ export type TeamChatLogResponsePatch = Pick<TeamChatLogRow, 'response' | 'status' | 'error_detail' | 'truncated'>;
529
+ export declare function nowIso(): string;
530
+ interface SalvagedEssentialConfig {
531
+ rootTeams: RootTeamRow[];
532
+ config: Array<{
533
+ key: string;
534
+ value: string;
535
+ }>;
536
+ }
537
+ /**
538
+ * Reads whatever essential config a DB this build can't otherwise use still
539
+ * has on disk: `root_team` (the fleet's team/nudge registrations -- "name ->
540
+ * path" plus the raw teamfile text, everything a health/discovery sweep
541
+ * needs) and `lmctl_config` (canonical api.url/api.port/api.token +
542
+ * workspace provider mappings -- "config replacing env vars", per its own
543
+ * schema comment; losing it silently would break `serve`/`apicli`
544
+ * coordination the same way losing team registrations breaks nudge).
545
+ * Opened as an INDEPENDENT connection (not the failed handle, which may be
546
+ * half-broken) and each table wrapped in its OWN try/catch: a genuinely
547
+ * corrupt file, or one that predates a given table, can't even run that
548
+ * SELECT -- that's fine, it just means nothing to salvage from that table,
549
+ * not a reason to fail the whole recovery (root_team predates lmctl_config
550
+ * by several versions, so an old-enough db could have one but not the
551
+ * other).
552
+ *
553
+ * DELIBERATE EXCLUSION (localweb v2, #104): `state_ledger_identity` is NOT read
554
+ * here and must never be added. Salvaging into a freshly created database is a
555
+ * NEW ledger generation; it re-mints its own instance id via migrate() so that
556
+ * old references (which bind to the prior instance id) can never silently resolve
557
+ * against reused numeric IDs in the new db. Only a full-file backup restore — which
558
+ * copies the whole file, not this selective salvage — retains the original id.
559
+ * Exported for the recovery/generation tests.
560
+ */
561
+ export declare function salvageEssentialConfig(dbPath: string): SalvagedEssentialConfig;
562
+ export declare function createDal(opts: {
563
+ path: string;
564
+ allowSchemaSkew?: boolean;
565
+ }): Dal;
566
+ export declare class Dal {
567
+ readonly db: Database;
568
+ private readonly stmts;
569
+ private readonly claimTxn;
570
+ private readonly claimWithConcurrencyTxn;
571
+ private readonly claimByIdWithConcurrencyTxn;
572
+ private readonly resumeRunTxn;
573
+ private readonly resumeInteractiveTxn;
574
+ private readonly resumeEscalationTxn;
575
+ private readonly tryAcquireAgentInflightTxn;
576
+ constructor(db: Database);
577
+ close(): void;
578
+ query(sql: string, ...params: unknown[]): unknown[];
579
+ insertWorkflow(input: {
580
+ name: string;
581
+ version?: number;
582
+ definition_schema_version?: number;
583
+ definition: string;
584
+ author?: string | null;
585
+ source_uri?: string | null;
586
+ source_sha256?: string | null;
587
+ }): WorkflowRow;
588
+ /**
589
+ * project_issue #31 — UPSERT semantics for workflow definitions (originally
590
+ * for `lmctl workflow load`; that verb was RETIRED in 0.1.128, `1775400`).
591
+ * Retained for DAL/schema/test compatibility; no current production call
592
+ * site.
593
+ *
594
+ * If no workflow with `input.name` exists, INSERT a fresh row + first
595
+ * workflow_version (same as `insertWorkflow`).
596
+ *
597
+ * If one exists, KEEP the same `workflow.id` and:
598
+ * - INSERT a new `workflow_version` row (auto-bumped version);
599
+ * - UPDATE `workflow.active_version_id` to the new version row;
600
+ * - UPDATE `workflow.definition` and `workflow.definition_schema_version`
601
+ * so callers reading off `workflow` (e.g. queue/worker.ts resolving via
602
+ * `workflow_id_override`) see the latest definition immediately;
603
+ * - BUMP `workflow.version` (kept in sync with the new version row so
604
+ * `findWorkflowLatest` returns the same row with a higher version).
605
+ *
606
+ * Without this, a second `workflow load` INSERTed a new `workflow` row;
607
+ * `submit-job --workflow <name>` resolved the wrong row and the engine ran
608
+ * the stale definition. Pre-existing INSERT-only path was correct for
609
+ * first-load and is preserved (backward-compat).
610
+ */
611
+ upsertWorkflow(input: {
612
+ name: string;
613
+ definition_schema_version?: number;
614
+ definition: string;
615
+ author?: string | null;
616
+ source_uri?: string | null;
617
+ source_sha256?: string | null;
618
+ }): WorkflowRow;
619
+ /**
620
+ * M1/M3 (#218) — record the composition-DSL source + its compiled
621
+ * workflow_version link (the template↔instance "template"; v25
622
+ * composition_unit, data-model.md §5). Additive accessor; no existing
623
+ * storage path forked. The compiled IR itself lives in
624
+ * workflow/workflow_version (stored via upsertWorkflow).
625
+ */
626
+ insertCompositionUnit(input: {
627
+ name: string;
628
+ version: number;
629
+ lang: string;
630
+ source: string;
631
+ source_sha: string;
632
+ compiled_workflow_version_id: number | null;
633
+ }): void;
634
+ getWorkflow(id: number): WorkflowRow | null;
635
+ getWorkflowByName(name: string, version?: number): WorkflowRow | null;
636
+ /** @deprecated use getWorkflowByName; retained as a compatibility alias. */
637
+ findWorkflowByName(name: string, version?: number): WorkflowRow | null;
638
+ insertWorkflowVersion(workflow_id: number, definition_json: string, author?: string | null): WorkflowVersionRow;
639
+ activateWorkflowVersion(workflow_id: number, workflow_version_id: number): WorkflowRow | null;
640
+ getWorkflowVersionById(id: number): WorkflowVersionRow | null;
641
+ listWorkflowVersions(workflow_id: number): WorkflowVersionRow[];
642
+ upsertTriggerInstance(input: {
643
+ workflow_id: number;
644
+ trigger_id: string;
645
+ trigger_type: string;
646
+ parameters: string;
647
+ enabled?: boolean;
648
+ }): TriggerInstanceRow;
649
+ listTriggerInstancesForWorkflow(workflow_id: number): TriggerInstanceRow[];
650
+ listEnabledTriggerInstances(): TriggerInstanceRow[];
651
+ setTriggerCursor(id: number, cursor: string | null): TriggerInstanceRow | null;
652
+ setTriggerFailure(id: number, error: string): TriggerInstanceRow | null;
653
+ private syncWorkflowTriggers;
654
+ insertTeam(input: {
655
+ name: string;
656
+ }): TeamRow;
657
+ getTeam(id: number): TeamRow | null;
658
+ findTeamByName(name: string): TeamRow | null;
659
+ listTeams(): TeamRow[];
660
+ insertTeamMember(input: {
661
+ team_id: number;
662
+ alias: string;
663
+ role?: string;
664
+ provider: string;
665
+ model?: string | null;
666
+ sessionid?: string | null;
667
+ sessiondir: string;
668
+ }): TeamMemberRow;
669
+ listTeamMembers(team_id: number): TeamMemberRow[];
670
+ findTeamMemberByAlias(team_id: number, alias: string): TeamMemberRow | null;
671
+ updateTeamMemberSession(id: number, sessionid: string): TeamMemberRow | null;
672
+ setMemberState(id: number, state: string, reason?: string | null): TeamMemberRow | null;
673
+ listMembersInState(state: string): TeamMemberRow[];
674
+ setMemberPromptSnapshot(id: number, snapshot: string | null): TeamMemberRow | null;
675
+ /**
676
+ * Snapshot the concrete provider chosen at seed time. Written by the
677
+ * seedMember chokepoint only; frozen thereafter (a workspace-mapping
678
+ * change does not retroactively re-resolve seeded members).
679
+ */
680
+ setMemberProviderResolved(id: number, concrete: string | null): TeamMemberRow | null;
681
+ setMemberRolePrompt(id: number, role_prompt: string | null): TeamMemberRow | null;
682
+ /**
683
+ * webui v2 — partial update of a team member's mutable metadata
684
+ * (role_prompt, model, provider). Only patches keys that are present;
685
+ * pass `null` for `role_prompt`/`model` to clear, leave a key undefined
686
+ * to preserve the current column value. Returns `null` if the member
687
+ * id does not exist.
688
+ */
689
+ updateTeamMemberMeta(id: number, patch: {
690
+ role_prompt?: string | null;
691
+ model?: string | null;
692
+ provider?: string;
693
+ }): TeamMemberRow | null;
694
+ insertTeamMemberCompanion(input: {
695
+ interpreter_id: number;
696
+ companion_id: number;
697
+ }): TeamMemberCompanionRow;
698
+ insertProject(input: {
699
+ name: string;
700
+ description?: string | null;
701
+ local_path: string;
702
+ workflow_name: string;
703
+ team_id: number;
704
+ default_branch?: string | null;
705
+ intake_config?: string | null;
706
+ test_argv?: string | null;
707
+ /**
708
+ * DG-6 D-6 — per-project test command surfaced as
709
+ * `{{project.test_command}}` in workflow templates. NULL → substituter
710
+ * returns `npm test` for backward-compat. See ProjectRow.test_command.
711
+ */
712
+ test_command?: string | null;
713
+ durable_memory_mode?: 'folder' | 'db';
714
+ }): ProjectRow;
715
+ listProjects(): ProjectRow[];
716
+ getProject(id: number): ProjectRow | null;
717
+ findProjectByName(name: string): ProjectRow | null;
718
+ setProjectIntakeConfig(project_id: number, intake_config: string | null): ProjectRow | null;
719
+ /**
720
+ * webui v2 — partial update of project settings (N15 page).
721
+ *
722
+ * Patch semantics: only fields present in `patch` are written. Each field
723
+ * uses `hasOwnProperty` checks so that a caller can set a value to `null`
724
+ * to clear (`default_branch`, `intake_config`) without affecting other
725
+ * columns. Returns `null` when the project does not exist.
726
+ */
727
+ updateProjectSettings(project_id: number, patch: {
728
+ local_path?: string;
729
+ durable_memory_mode?: 'folder' | 'db';
730
+ default_branch?: string | null;
731
+ intake_config?: string | null;
732
+ }): ProjectRow | null;
733
+ listProjectsWithIntakeEnabled(): ProjectRow[];
734
+ getProjectByJob(job_id: number): ProjectRow | null;
735
+ insertProjectWorkflow(input: {
736
+ project_id: number;
737
+ workflow_id: number;
738
+ condition?: string | null;
739
+ parameters_override?: string | null;
740
+ priority?: number;
741
+ enabled?: boolean;
742
+ }): ProjectWorkflowRow;
743
+ getProjectWorkflow(id: number): ProjectWorkflowRow | null;
744
+ listProjectWorkflows(project_id: number, opts?: {
745
+ enabled_only?: boolean;
746
+ }): ProjectWorkflowRow[];
747
+ removeProjectWorkflow(id: number): boolean;
748
+ setProjectWorkflowEnabled(id: number, enabled: boolean): ProjectWorkflowRow | null;
749
+ createIssue(input: {
750
+ project_id: number;
751
+ title: string;
752
+ body: string;
753
+ severity?: IssueSeverity;
754
+ labels?: string | null;
755
+ ai_test_path?: string | null;
756
+ source_run_id?: number | null;
757
+ }): IssueRow;
758
+ getIssue(id: number): IssueRow | null;
759
+ listIssues(project_id: number, opts?: {
760
+ status?: IssueStatus[];
761
+ severity?: IssueSeverity[];
762
+ ai_test_path?: string;
763
+ }): IssueRow[];
764
+ claimNextIssue(project_id: number, assigned_run_id: number): IssueRow | null;
765
+ claimIssue(id: number, assigned_run_id: number): IssueRow | null;
766
+ closeIssue(id: number, closed_run_id: number, commit_hash?: string | null): IssueRow;
767
+ reopenIssue(id: number, source_run_id: number): IssueRow;
768
+ updateIssue(id: number, patch: {
769
+ title?: string;
770
+ body?: string;
771
+ severity?: IssueSeverity;
772
+ labels?: string | null;
773
+ ai_test_path?: string | null;
774
+ }): IssueRow;
775
+ listWorkflows(): WorkflowRow[];
776
+ /**
777
+ * Idempotent get-or-create. If the session row exists it is returned
778
+ * unchanged (no transition mutation); otherwise a fresh row is inserted
779
+ * at the root node (N1) with no parameters.
780
+ */
781
+ getOrCreateWebuiSession(session_id: string, user_id: string): WebuiSessionRow;
782
+ getWebuiSession(session_id: string): WebuiSessionRow | null;
783
+ /**
784
+ * Persist a transition. Caller is responsible for stringifying JSON
785
+ * payloads; nullable fields accept explicit `null`. `last_transition_at`
786
+ * is stamped automatically.
787
+ */
788
+ updateWebuiSessionTransition(session_id: string, current_node: string, node_params: string | null, prev_node_stack: string | null, pending_form: string | null): WebuiSessionRow;
789
+ /** Attach (or detach when `null`) a chatbot conversation to the session. */
790
+ setWebuiSessionChatbot(session_id: string, chatbot_session_id: string | null): WebuiSessionRow;
791
+ /** Create a fresh chatbot conversation for `user_id`. Returns the row. */
792
+ createChatbotSession(user_id: string, title?: string | null): ChatbotSessionRow;
793
+ getChatbotSession(chatbot_session_id: string): ChatbotSessionRow | null;
794
+ listChatbotSessions(user_id: string): ChatbotSessionRow[];
795
+ /**
796
+ * Append a message and bump the parent conversation's `last_active_at`.
797
+ * Returns the inserted row. Throws on FK violation if `chatbot_session_id`
798
+ * is unknown.
799
+ */
800
+ appendChatbotMessage(chatbot_session_id: string, role: string, content: string, tool_calls?: string | null): ChatbotMessageRow;
801
+ /** Messages in insertion order (oldest first). */
802
+ listChatbotMessages(chatbot_session_id: string): ChatbotMessageRow[];
803
+ /** Returns the config value for `key`, or null if not set. */
804
+ getConfig(key: string): string | null;
805
+ /** Upsert a config value. Stamps `updated_at` to now. */
806
+ setConfig(key: string, value: string): void;
807
+ /** All config rows as a plain object (alphabetical key order). */
808
+ getConfigAll(): Record<string, string>;
809
+ enqueueJob(input: {
810
+ project_id: number;
811
+ workflow_id_override?: number | null;
812
+ source: string;
813
+ source_ref?: string | null;
814
+ payload?: string | null;
815
+ priority?: number;
816
+ available_at?: string;
817
+ dedupe_key?: string | null;
818
+ concurrency_key?: string | null;
819
+ }): JobRow;
820
+ claimNextJob(input: {
821
+ worker_id: string;
822
+ lease_ms: number;
823
+ }): JobRow | null;
824
+ claimNextJobWithConcurrency(input: {
825
+ worker_id: string;
826
+ lease_ms: number;
827
+ evaluator: (job: JobRow) => {
828
+ concurrency_key: string | null;
829
+ concurrent_limit: number;
830
+ };
831
+ }): JobRow | null;
832
+ claimJobByIdWithConcurrency(input: {
833
+ job_id: number;
834
+ worker_id: string;
835
+ lease_ms: number;
836
+ evaluator: (job: JobRow) => {
837
+ concurrency_key: string | null;
838
+ concurrent_limit: number;
839
+ };
840
+ }): JobClaimByIdResult;
841
+ releaseJob(id: number, status: string, last_error?: string | null): JobRow | null;
842
+ getJob(id: number): JobRow | null;
843
+ findJobBySourceAndDedupe(source: string, dedupe_key: string): JobRow | null;
844
+ insertRun(input: {
845
+ job_id: number;
846
+ workflow_id: number;
847
+ team_id: number;
848
+ current_state?: string | null;
849
+ parent_run_id?: number | null;
850
+ }): RunRow;
851
+ setJobRun(job_id: number, run_id: number): JobRow | null;
852
+ insertPromptDispatch(input: {
853
+ prompt_id: string;
854
+ project_id: number;
855
+ job_id: number;
856
+ text: string;
857
+ context_json?: string | null;
858
+ }): PromptDispatchRow;
859
+ getPromptDispatch(prompt_id: string): PromptDispatchRow | null;
860
+ setPromptDispatchCompleted(prompt_id: string, completed_at?: string): PromptDispatchRow | null;
861
+ /** Read the workspace row. Returns null if absent or soft-deleted. */
862
+ getWorkspace(id: string): WorkspaceRow | null;
863
+ /**
864
+ * Lazy-create the workspace (per L-MD §K6) if not present, then read it.
865
+ * Used by `Query.workspace` so a fresh DB always returns a row even before
866
+ * the operator creates any project. The "default" id matches the pre-L-8
867
+ * resolver constant; schema.sql + the v29 migration both seed this row,
868
+ * so this is a belt-and-braces upsert in the rare case the row was hand-
869
+ * deleted (e.g. test fixtures).
870
+ */
871
+ ensureWorkspace(opts?: {
872
+ id?: string;
873
+ name?: string;
874
+ idleThresholdMs?: number;
875
+ }): WorkspaceRow;
876
+ /** List active (not-soft-deleted) web_projects for a workspace. */
877
+ listWebProjectsActive(workspace_id: string): WebProjectRow[];
878
+ /** Read a web_project by id (returns soft-deleted rows too — callers filter). */
879
+ getWebProject(id: number): WebProjectRow | null;
880
+ insertWebProject(input: {
881
+ workspace_id: string;
882
+ name: string;
883
+ repo_path: string;
884
+ config_json?: string | null;
885
+ linked_v4_project_id?: number | null;
886
+ }): WebProjectRow;
887
+ updateWebProject(input: {
888
+ id: number;
889
+ name?: string;
890
+ config_json?: string | null;
891
+ }): WebProjectRow | null;
892
+ /** Soft-delete (sets `deleted_at`). Returns false if the row was absent or
893
+ * already soft-deleted. Prompt history rows in `prompt_dispatch` retain
894
+ * the FK reference; the UI is expected to hide deleted projects. */
895
+ softDeleteWebProject(id: number): boolean;
896
+ /** Read the cursor row for one resolved mailbox object, or null if absent (cold start). */
897
+ loadMailboxCursor(user_id: string, mailbox_key: string): MailboxCursorRow | null;
898
+ /**
899
+ * Upsert the cursor row. Caller pattern: invoke AFTER each
900
+ * successfully-processed message so partial failures don't advance the
901
+ * cursor. `last_processed_version_id` may be null to record the
902
+ * cold-start sentinel before the first version is processed.
903
+ */
904
+ saveMailboxCursor(row: MailboxCursorRow): void;
905
+ /** Increment this device's monthly op counters by the given deltas (upsert). */
906
+ addUsage(args: {
907
+ user_id: string;
908
+ device_id: string;
909
+ period: string;
910
+ put: number;
911
+ get: number;
912
+ list: number;
913
+ updated_at: string;
914
+ }): void;
915
+ /** Read one device's counters for a period, or null. */
916
+ loadUsage(user_id: string, device_id: string, period: string): UsageMeterRow | null;
917
+ /** Sum a user's op counts across all this-DB-local devices for a period. */
918
+ sumUsageForPeriod(user_id: string, period: string): {
919
+ put: number;
920
+ get: number;
921
+ list: number;
922
+ };
923
+ upsertRootTeam(row: RootTeamInput): void;
924
+ getRootTeam(teamfile_path: string): RootTeamRow | null;
925
+ listRootTeams(): RootTeamRow[];
926
+ insertTeamChatLog(row: TeamChatLogInput): number;
927
+ beginTeamChatLog(row: TeamChatLogInput): number;
928
+ finishTeamChatLog(id: number, row: TeamChatLogInput): void;
929
+ setCurrentlyHandlingMessage(teamfile: string, alias: string, messageId: string): void;
930
+ clearCurrentlyHandlingMessage(teamfile: string, alias: string, expectedMessageId: string): boolean;
931
+ listTeamChatLog(root_teamfile: string): TeamChatLogRow[];
932
+ listTeamChatLogSinceId(afterId: number, limit?: number): TeamChatLogRow[];
933
+ getMaxTeamChatLogId(): number;
934
+ loadTeamChatLogCursor(cursor_key: string): TeamChatLogCursorRow | null;
935
+ saveTeamChatLogCursor(row: TeamChatLogCursorRow): void;
936
+ startTrackedInvocation(input: TrackedInvocationInput): TrackedInvocationRow;
937
+ private startTrackedInvocationInner;
938
+ getTrackedInvocation(id: number): TrackedInvocationRow | null;
939
+ /**
940
+ * lmctl's own dispatch audit trail for a target (who dispatched, when,
941
+ * how it ended), joined alongside the provider-native message history
942
+ * rather than folded into it, since a `tracked_invocation` row and a
943
+ * provider turn are not guaranteed 1:1 (retries, aborts).
944
+ */
945
+ listTrackedInvocationsForTarget(targetTeamfile: string, targetAlias: string, opts?: {
946
+ sinceMs?: number;
947
+ untilMs?: number;
948
+ limit?: number;
949
+ }): TrackedInvocationRow[];
950
+ listAllRunningTrackedInvocations(): TrackedInvocationRow[];
951
+ listRecentlyFinishedTrackedInvocations(sinceIso: string): TrackedInvocationRow[];
952
+ /**
953
+ * Finished children (ended within `sinceIso`) of the given invocations --
954
+ * the lineage-completion query `lmctl running --tree` uses so a batch root
955
+ * renders ALL its per-turn children, not just the ones that survived the
956
+ * flat view's latest-per-target suppression. Point-in-time running/dead
957
+ * children need no retrieval here: the running snapshot already lists
958
+ * every status='running' row regardless of parentage.
959
+ */
960
+ listFinishedTrackedInvocationChildren(parentIds: number[], sinceIso: string): TrackedInvocationRow[];
961
+ finishTrackedInvocation(id: number, patch: TrackedInvocationFinishPatch): TrackedInvocationRow | null;
962
+ reclaimTrackedInvocationIfUnchanged(id: number, observed: Pick<TrackedInvocationRow, 'holder_pid' | 'holder_host' | 'started_at' | 'last_heartbeat_at'>, patch: TrackedInvocationFinishPatch): TrackedInvocationRow | null;
963
+ heartbeatTrackedInvocation(id: number, heartbeatAt?: string): TrackedInvocationRow | null;
964
+ updateTrackedInvocationRequestPayload(id: number, requestPayload: string): TrackedInvocationRow | null;
965
+ /**
966
+ * Slice A item 1 — persist the CREATE-time message linkage onto the still-
967
+ * running invocation: the legacy `team_chat_log_id` column AND the canonical
968
+ * `invocation_state.request_message_id` (`msg_tcl_<id>`), in one transaction.
969
+ * Set-once + running-only (CAS via the statement guard), so a process that
970
+ * dies moments later leaves a durable link the dead-holder reconciler can
971
+ * follow to terminalize the exact message. Returns null if the guard didn't
972
+ * match (not running, or already linked).
973
+ */
974
+ linkTrackedInvocationMessage(id: number, teamChatLogId: number): TrackedInvocationRow | null;
975
+ /**
976
+ * Slice A item 2 — dead-holder abort that ALSO terminalizes the linked
977
+ * message, atomically. The invocation abort CAS is the single gate: exactly
978
+ * one caller flips `running -> aborted` (later sweeps and a late normal
979
+ * finalizer all fail the guard), and only that winner terminalizes the linked
980
+ * chat-log message. The message terminalization is itself first-writer-wins
981
+ * (guarded on the chat-log row still being 'running') and idempotent (the
982
+ * HANDLED event has a deterministic id that dedupes), so an already-'ok'
983
+ * message is never overwritten and no duplicate terminal event is appended.
984
+ * Returns the aborted row, or null if the abort CAS lost.
985
+ */
986
+ abortTrackedInvocationAndTerminalizeMessage(id: number, observed: Pick<TrackedInvocationRow, 'holder_pid' | 'holder_host' | 'started_at' | 'last_heartbeat_at'>, errorDetail: string): TrackedInvocationRow | null;
987
+ /** Terminalize a still-running chat-log message to an error outcome and append
988
+ * its HANDLED event. Runs inside the abort transaction. No-op when the message
989
+ * is missing or already terminal (first-writer-wins). */
990
+ private terminalizeLinkedChatMessage;
991
+ listClaimableTerminalTrackedInvocations(filter: TrackedInvocationTerminalClaimFilter): TrackedInvocationRow[];
992
+ claimTrackedInvocationNotification(id: number, notifiedBy: string, notifiedAt?: string): TrackedInvocationRow | null;
993
+ listTrackedInvocations(filter?: TrackedInvocationListFilter): TrackedInvocationRow[];
994
+ maxTrackedInvocationId(filter?: Pick<TrackedInvocationListFilter, 'from' | 'scope_teamfile'>): number;
995
+ listRunningTrackedInvocations(filter?: Omit<TrackedInvocationListFilter, 'status'>): TrackedInvocationRow[];
996
+ updateTeamChatLogResponse(id: number, patch: TeamChatLogResponsePatch): void;
997
+ getAgentInflight(receiver_teamfile: string, receiver_alias: string): AgentInflightRow | null;
998
+ listAgentInflight(): AgentInflightRow[];
999
+ tryAcquireAgentInflight(row: AgentInflightInput, options?: AgentInflightAcquireOptions): AgentInflightAcquireResult;
1000
+ heartbeatAgentInflight(row: AgentInflightInput, leaseId: string, heartbeatAt?: string): boolean;
1001
+ clearAgentInflight(receiver_teamfile: string, receiver_alias: string): void;
1002
+ clearObservedTerminalAgentInflight(receiver_teamfile: string, receiver_alias: string, observed: AgentInflightObservedHolder): boolean;
1003
+ releaseAgentInflight(row: AgentInflightInput, leaseId?: string): boolean;
1004
+ clearAgentInflightOlderThan(stale_before: string): number;
1005
+ getTerminalLock(teamfile: string, alias: string): TerminalLockRecord | null;
1006
+ setTerminalLock(lock: TerminalLockRecord): void;
1007
+ heartbeatTerminalLock(teamfile: string, alias: string, lockId: string, heartbeatAt: string): boolean;
1008
+ clearTerminalLock(teamfile: string, alias: string, lockId: string): boolean;
1009
+ getRuntimeProcessLock(scope: string, key: string, kind: string): RuntimeProcessLockRecord | null;
1010
+ tryAcquireRuntimeProcessLock(scope: string, key: string, kind: string, staleBefore: string, now?: string): RuntimeProcessLockAcquireResult;
1011
+ clearRuntimeProcessLock(scope: string, key: string, kind: string, lockId: string): boolean;
1012
+ getRun(id: number): RunRow | null;
1013
+ setRunCurrentState(run_id: number, current_state: string | null): RunRow | null;
1014
+ setRunPausedState(run_id: number, current_state: string, paused_at: string): RunRow | null;
1015
+ /** DEF-1 §1.3 — set/clear `run.pending_escalation_decision` (the step.id-
1016
+ * keyed JSON map). Pass the serialized map, or `null` to clear it when the
1017
+ * engine consume-point has drained the last entry. */
1018
+ setRunPendingEscalationDecision(run_id: number, value: string | null): RunRow | null;
1019
+ setRunTerminalState(run_id: number, terminal_state: string): RunRow | null;
1020
+ pauseJob(id: number): JobRow | null;
1021
+ resumeRun(run_id: number, outcome: string): ResumeRunResult;
1022
+ insertStep(input: {
1023
+ run_id: number;
1024
+ state_name: string;
1025
+ parent_step_id?: number | null;
1026
+ outcome?: string | null;
1027
+ status_detail?: string | null;
1028
+ }): StepRow;
1029
+ finishStep(id: number, outcome?: string | null, status_detail?: string | null): StepRow | null;
1030
+ /**
1031
+ * All `step` rows for a run, ascending by id (= transition order; id is
1032
+ * the autoincrement PK written once per state/node execution by
1033
+ * `insertStep`). Additive read over existing rows — no schema change,
1034
+ * no parallel event stream. The per-transition observable the
1035
+ * workflow-microservice mediation core reads (design §3/§4).
1036
+ */
1037
+ listStepsForRun(run_id: number): StepRow[];
1038
+ /**
1039
+ * Return the most recent non-null outcome per state_name for a run.
1040
+ * Used by the engine on resume to rehydrate ExecutionContext.outcomes so
1041
+ * downstream `{{outcomes.<state_id>}}` substitutions resolve against the
1042
+ * Pause's operator-chosen outcome (and any other completed steps).
1043
+ */
1044
+ listLatestStepOutcomesByState(run_id: number): Record<string, string>;
1045
+ insertSubmittedPr(input: {
1046
+ repo: string;
1047
+ pr_url: string;
1048
+ issue_url?: string | null;
1049
+ local_path?: string | null;
1050
+ branch?: string | null;
1051
+ head_sha?: string | null;
1052
+ status?: string;
1053
+ submitted_at?: string;
1054
+ last_known_state?: string | null;
1055
+ created_by_run_id?: number | null;
1056
+ }): SubmittedPrRow;
1057
+ listSubmittedPrs(opts?: {
1058
+ status?: string;
1059
+ }): SubmittedPrRow[];
1060
+ getSubmittedPr(id: number): SubmittedPrRow | null;
1061
+ findSubmittedPrByUrl(pr_url: string): SubmittedPrRow | null;
1062
+ insertStepArtifact(input: {
1063
+ step_id: number;
1064
+ kind: string;
1065
+ path?: string | null;
1066
+ content?: string | null;
1067
+ sha?: string | null;
1068
+ /** A2.5 audit-trail: 'library' | 'subprocess' | null (non-chat step). */
1069
+ provider_call_mode?: 'library' | 'subprocess' | null;
1070
+ }): StepArtifactRow;
1071
+ insertExternalObject(input: {
1072
+ type: string;
1073
+ provider: string;
1074
+ project_id: number;
1075
+ run_id?: number | null;
1076
+ external_id: string;
1077
+ url?: string | null;
1078
+ status?: string | null;
1079
+ metadata?: string | null;
1080
+ last_seen_at?: string | null;
1081
+ }): InsertResult<ExternalObjectRow>;
1082
+ insertExternalSignal(input: {
1083
+ object_id: number;
1084
+ kind: string;
1085
+ source?: string | null;
1086
+ occurred_at?: string | null;
1087
+ payload?: string | null;
1088
+ signal_hash?: string | null;
1089
+ }): InsertResult<ExternalSignalRow>;
1090
+ raiseAttention(input: {
1091
+ kind: string;
1092
+ severity?: string | null;
1093
+ project_id?: number | null;
1094
+ run_id?: number | null;
1095
+ external_object_id?: number | null;
1096
+ requires_user?: boolean;
1097
+ payload?: string | null;
1098
+ }): AttentionRow;
1099
+ acknowledgeAttention(id: number): AttentionRow | null;
1100
+ upsertDurableMemory(input: {
1101
+ project_id: number;
1102
+ type: string;
1103
+ name: string;
1104
+ description?: string | null;
1105
+ body: string;
1106
+ }): DurableMemoryRow;
1107
+ getDurableMemoryByName(project_id: number, name: string): DurableMemoryRow | null;
1108
+ /**
1109
+ * webui v2 — remove a durable-memory chapter from the DB. Returns `true`
1110
+ * when a row was deleted, `false` when the chapter did not exist.
1111
+ * No-op + false is the contract; callers should map false to 404.
1112
+ */
1113
+ deleteDurableMemory(project_id: number, name: string): boolean;
1114
+ listDurableMemory(project_id: number, type?: string): DurableMemoryRow[];
1115
+ indexDurableMemory(project_id: number, types?: string[]): Array<Pick<DurableMemoryRow, 'name' | 'description' | 'type' | 'updated_at'>>;
1116
+ recordSessionSize(input: {
1117
+ team_name: string;
1118
+ alias: string;
1119
+ sessionid: string;
1120
+ input_tokens?: number | null;
1121
+ cache_tokens?: number | null;
1122
+ output_tokens?: number | null;
1123
+ total_tokens?: number | null;
1124
+ observed_at?: string;
1125
+ }): SessionSizeRow;
1126
+ latestSessionSizes(): SessionSizeRow[];
1127
+ sessionSizeHistory(team_name: string, alias: string, limit?: number): SessionSizeRow[];
1128
+ insertSessionEvent(input: {
1129
+ run_id: number;
1130
+ step_id: number | null;
1131
+ kind: string;
1132
+ timestamp_ms: number;
1133
+ payload: string;
1134
+ payload_truncated?: number;
1135
+ overflow_artifact_id?: number | null;
1136
+ }): SessionEventRow;
1137
+ listSessionEvents(run_id: number, sinceId?: number): SessionEventRow[];
1138
+ listSessionEventsSince(sinceId: number, limit?: number): SessionEventRow[];
1139
+ /**
1140
+ * P3 (briefs/lmctl0-verbs-native-port-design.md §2.4 / §2.5) —
1141
+ * read-only accessor for the most-recent `agent_call` session_event for
1142
+ * a run. Returns `{alias,provider,model?}` parsed from the payload (the
1143
+ * five-field shape the engine adapter wrote — the engine moved to lmbee
1144
+ * with the daemon, 2026-09-27), or null when the run has no agent_call yet.
1145
+ *
1146
+ * Run-keyed ORDER BY id DESC — NOT a `payload.step_id == session_event
1147
+ * .step_id` join (that join is INVALID for the workflow_escalation
1148
+ * case: `escalate()` writes a NEW escalation-marker step row whose id
1149
+ * goes into `attention.payload.step_id`, while the prior `agent_call`
1150
+ * lives under a DIFFERENT step row, so step_id join would chase a row
1151
+ * that has no `agent_call`). The design's §6.7 load-bearing test
1152
+ * pins this distinction.
1153
+ */
1154
+ latestAgentCall(run_id: number): {
1155
+ alias: string;
1156
+ provider: string;
1157
+ model?: string;
1158
+ } | null;
1159
+ /**
1160
+ * P3 — read-only token rollup across ALL `agent_call` events for a run.
1161
+ * Sums `payload.tokens_in` / `payload.tokens_out` (the fields the engine
1162
+ * writes). Used by `info --run` for the per-run aggregate; never reads
1163
+ * cost/price (R4 baked).
1164
+ */
1165
+ agentCallTokenTotals(run_id: number): {
1166
+ totalInputTokens: number;
1167
+ totalOutputTokens: number;
1168
+ agentCallCount: number;
1169
+ };
1170
+ addRollupValue(input: {
1171
+ run_id: number;
1172
+ step_id: number | null;
1173
+ metric_kind: string;
1174
+ value: number;
1175
+ bucket_start_ms: number;
1176
+ bucket_end_ms: number;
1177
+ updated_at: number;
1178
+ }): void;
1179
+ listRollupStats(run_id: number): RollupStatRow[];
1180
+ getRollupCursor(): number;
1181
+ setRollupCursor(eventId: number): void;
1182
+ insertDriftSignal(input: {
1183
+ team_member_id: number;
1184
+ project_id?: number | null;
1185
+ signal_kind: string;
1186
+ severity: 'low' | 'medium' | 'high';
1187
+ description: string;
1188
+ detected_at: number;
1189
+ expires_at?: number | null;
1190
+ payload?: string | null;
1191
+ }): DriftSignalRow;
1192
+ listDriftSignals(opts?: {
1193
+ team_member_id?: number;
1194
+ project_id?: number;
1195
+ unacked?: boolean;
1196
+ }): DriftSignalRow[];
1197
+ hasUnackedDriftSignal(team_member_id: number, project_id: number | null, signal_kind: string): boolean;
1198
+ acknowledgeDriftSignal(id: number): DriftSignalRow | null;
1199
+ insertInteractiveSession(input: {
1200
+ run_id: number;
1201
+ step_id: number;
1202
+ state_name: string;
1203
+ status: InteractiveSessionRow['status'];
1204
+ }): InteractiveSessionRow;
1205
+ getInteractiveSessionByStepId(step_id: number): InteractiveSessionRow | null;
1206
+ getInteractiveSessionByRunAndState(run_id: number, state_name: string): InteractiveSessionRow | null;
1207
+ setInteractiveAgentOutput(id: number, output: string, status: InteractiveSessionRow['status']): void;
1208
+ resumeInteractive(step_id: number, input: string, done: boolean): InteractiveResumeResult;
1209
+ /** DEF-1 §1.3 — operator answers a `workflow_escalation`. `decision` is one
1210
+ * of the escalated step's declared outcome tokens, or the reserved
1211
+ * `@abort`. Session-free; persists the decision and un-pauses the run. */
1212
+ resumeEscalation(attention_id: number, decision: string): EscalationResumeResult;
1213
+ }