@lmctl-ai/lmctl 0.1.342 → 0.2.3

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 +116 -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 +1062 -1400
  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 +122 -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,19 @@
1
+ /**
2
+ * ACP provider profile registry.
3
+ *
4
+ * The generic ACP engine (transports/acp.ts) looks up the profile for a
5
+ * tool here; everything provider-specific lives in the profile modules.
6
+ */
7
+ import type { AcpProviderProfile } from "./types.js";
8
+ export declare function acpProfileForTool(tool: string): AcpProviderProfile | undefined;
9
+ /**
10
+ * Read-only view of the per-profile `{ binary, baseArgs }` table for
11
+ * tests that pin the per-provider argv shape (e.g. flag-position
12
+ * regression checks). Production code reads the profiles directly; this
13
+ * alias exists only so the test suite can import a frozen view without
14
+ * making the table mutable from the outside.
15
+ */
16
+ export declare const ACP_TOOLS_FOR_TEST: Readonly<Record<string, Readonly<{
17
+ binary: string;
18
+ args: readonly string[];
19
+ }>>>;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Kimi ACP profile.
3
+ *
4
+ * Kimi's ACP subcommand has no argv permission flag: the posture is
5
+ * protocol-level (`session/set_config_option` with configId "mode"),
6
+ * applied BEFORE any model config and BEFORE the first prompt, and it is
7
+ * FAIL-CLOSED — lmctl cannot answer `session/request_permission`, so a
8
+ * rejected mode change would leave the subprocess hung on its first tool
9
+ * call. Ordering and policy are the `sessionSetup` data below.
10
+ *
11
+ * Kimi 0.28.x exposes only an on/off thinking toggle, so a declared
12
+ * `effort=` cannot be honored here and is silently ignored.
13
+ */
14
+ import type { AcpProviderProfile } from "./types.js";
15
+ export declare function detectKimiRuntimeDead(resultText: string): string | null;
16
+ /**
17
+ * Kimi logs a multi-line `Error handling notification … -32601 Method not
18
+ * found` block for the mandatory `notifications/initialized` notification
19
+ * on every spawn. It is routine, not a failure — strip exactly that block
20
+ * (and only that block) from the stderr tail attached to a no-result
21
+ * error report.
22
+ */
23
+ export declare function filterKimiExpectedStderr(stderr: string): string;
24
+ export declare const kimiProfile: AcpProviderProfile;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * OpenCode ACP profile.
3
+ *
4
+ * OpenCode's ACP mode rejects every permission-bypass argv flag
5
+ * (`--dangerously-skip-permissions` is a `run`-subcommand-only flag,
6
+ * verified empirically). YOLO is satisfied via
7
+ * `<cwd>/.opencode/opencode.json` carrying `"permission": "allow"`,
8
+ * installed defensively before every chat spawn (preSpawn) so even
9
+ * pre-fix-seeded teams get the YOLO key without a re-create. The install
10
+ * no longer writes lmctl MCP entries.
11
+ *
12
+ * The profile also owns `probeOpenCodeAcpConfig` — a separate probe entry
13
+ * point (purpose "probe") used by lint/seed via teamfile-compat.ts. It
14
+ * intentionally differs from the chat spawn: no runtime permission-config
15
+ * install (initialize/session creation and config-option validation
16
+ * cannot request tools or incur an LLM call), and a rejected model/effort
17
+ * option is reported as probe evidence rather than absorbed as a warning.
18
+ */
19
+ import type { AcpProviderProfile, OpenCodeAcpConfigProbeOptions, OpenCodeAcpConfigProbeResult } from "./types.js";
20
+ export declare const openCodeProfile: AcpProviderProfile;
21
+ /**
22
+ * Verify opencode's live per-session model/effort contract without prompting.
23
+ * No project permission config is installed: initialize/session creation and
24
+ * config-option validation cannot request tools or incur an LLM call.
25
+ */
26
+ export declare function probeOpenCodeAcpConfig(opts: OpenCodeAcpConfigProbeOptions): Promise<OpenCodeAcpConfigProbeResult>;
@@ -0,0 +1,258 @@
1
+ /**
2
+ * `AcpProviderProfile` — the per-provider half of the ACP transport.
3
+ *
4
+ * transports/acp.ts is the generic protocol engine (RPC channel,
5
+ * lifecycle/abort/hold-open scaffolding, initialize → session/load|new →
6
+ * session/prompt skeleton, resume contract). Everything that used to be a
7
+ * per-provider `if (tool === …)` branch lives behind this interface in
8
+ * `acp-profiles/{kimi,opencode,copilot,gemini,hermes}.ts`.
9
+ *
10
+ * Conventions (binding, from the batch-redesign v2 review):
11
+ * - Hooks receive ONE typed context object (no two-argument hooks):
12
+ * permissionMode, model, effort, acpMode, purpose ('chat' | 'probe'),
13
+ * and a warnings channel. Hook-specific inputs arrive on an extension
14
+ * of that object (AcpSpawnContext, AcpSessionContext, …).
15
+ * - Ordering/failure policy of post-open configuration is explicit
16
+ * per-profile DATA: `sessionSetup` array order is the wire order and
17
+ * each step declares `onFailure: "fail-closed" | "non-fatal"`. The
18
+ * driver interprets the policy; profiles never branch on it inline.
19
+ * - Result handling uses stateful hooks: gemini's replay isolation lives
20
+ * in the output processor via `isolateResult`; kimi's runtime-dead
21
+ * detection is the `onExitFinalization` telemetry hook. There are no
22
+ * stateless classifyResult/filterStderr result transforms.
23
+ * - `requireResume` is the generic driver's resume contract, NOT a
24
+ * profile capability.
25
+ */
26
+ import type { PermissionMode } from "../../yolo-contract.js";
27
+ import type { AcpRpcChannel } from "../acp-rpc.js";
28
+ /**
29
+ * ACP session mode ids this transport may set. Kept as a closed type so a
30
+ * typo cannot reach the wire as an unrecognized mode id.
31
+ */
32
+ export type AcpMode = "default" | "accept_edits" | "dont_ask";
33
+ /** Why an ACP process is being spawned. */
34
+ export type AcpPurpose = "chat" | "probe";
35
+ /**
36
+ * Typed context handed to every profile hook.
37
+ */
38
+ export interface AcpProfileContext {
39
+ /** Resolved permission mode for this spawn ("yolo" is the default). */
40
+ readonly permissionMode: PermissionMode;
41
+ /** Per-session model override, when the caller declared one. */
42
+ readonly model?: string;
43
+ /** Per-session effort / variant override, when the caller declared one. */
44
+ readonly effort?: string;
45
+ /**
46
+ * Explicit ACP `session/set_mode` mode id, validated by the driver
47
+ * against the profile's `modeVocabulary` before spawn (fail-closed).
48
+ */
49
+ readonly acpMode?: AcpMode;
50
+ readonly purpose: AcpPurpose;
51
+ /**
52
+ * Warnings channel. Strings pushed here are human-readable records of
53
+ * provider config the turn proceeded past; for purpose "chat" they land
54
+ * on `AcpResult.warnings` (task #232), for purpose "probe" they are
55
+ * probe evidence.
56
+ */
57
+ readonly warnings: {
58
+ push(warning: string): void;
59
+ };
60
+ }
61
+ /** preSpawn hook context: the profile context plus the spawn target. */
62
+ export interface AcpSpawnContext extends AcpProfileContext {
63
+ readonly cwd: string;
64
+ /** The already-built spawn environment; profiles may mutate it. */
65
+ readonly env: NodeJS.ProcessEnv;
66
+ }
67
+ /** afterSessionOpen step context: the profile context plus the open session. */
68
+ export interface AcpSessionContext extends AcpProfileContext {
69
+ readonly sessionId: string;
70
+ /** Raw `session/new` / `session/load` response (model catalogs, currentModelId…). */
71
+ readonly response: Record<string, unknown>;
72
+ readonly sendRpc: AcpRpcChannel["sendRpc"];
73
+ }
74
+ /** onExitFinalization hook context: the finalized turn result. */
75
+ export interface AcpFinalizeContext extends AcpProfileContext {
76
+ readonly sessionId: string;
77
+ readonly result: string;
78
+ readonly stopReason: string;
79
+ readonly exitCode: number | null;
80
+ }
81
+ /**
82
+ * Bundle-open identity verification context: everything a profile needs to
83
+ * prove the requested session was genuinely loaded.
84
+ */
85
+ export interface AcpSessionIdentityContext extends AcpProfileContext {
86
+ readonly requestedSessionId: string;
87
+ /** Raw `session/load` response (its sessionId echo, if any, was already
88
+ * checked by the driver: a DIFFERENT echoed id always fails before this
89
+ * hook runs). */
90
+ readonly loadResponse: Record<string, unknown>;
91
+ readonly cwd: string;
92
+ /** The spawn env — provider-home overrides (KIMI_CODE_HOME etc.) reach
93
+ * the verifier through here. */
94
+ readonly env: NodeJS.ProcessEnv;
95
+ }
96
+ /**
97
+ * Per-profile session-identity strategy for BUNDLE open (`openAcpSession`
98
+ * strict mode). The driver never falls back to "assume the requested id":
99
+ * a profile must declare how a genuine load is proven.
100
+ *
101
+ * - Providers whose session/load echoes the session id need nothing here:
102
+ * the driver's exact-echo check is the whole strategy (default when this
103
+ * field is absent — and the absence of an echo then fails closed).
104
+ * - Providers whose load does NOT echo an id declare a hook:
105
+ * kimi verifies via its on-disk session store; hermes relies on its
106
+ * fail-closed load (a non-throwing load IS the proof — see the profile).
107
+ */
108
+ export interface AcpSessionIdentity {
109
+ /** Verify at bundle open that the requested session was genuinely loaded.
110
+ * Throw on failure. Only called when the load response carried no
111
+ * contradicting echo. */
112
+ verifyLoadedSession(ctx: AcpSessionIdentityContext): Promise<void> | void;
113
+ /**
114
+ * Cheap between-messages re-check (catches an external re-seed/delete
115
+ * mid-bundle before the next prompt is sent). Optional — providers whose
116
+ * identity is pinned by the live process need nothing here (the per-turn
117
+ * expectedSession check upstream catches teamfile rotation).
118
+ */
119
+ recheckSession?(ctx: {
120
+ requestedSessionId: string;
121
+ cwd: string;
122
+ env: NodeJS.ProcessEnv;
123
+ }): Promise<void> | void;
124
+ }
125
+ /** Structured description of a session-step failure, built by the step. */
126
+ export interface AcpStepFailure {
127
+ /** errorLog event name. The driver merges `{ event, provider, ...fields }`. */
128
+ readonly event: string;
129
+ readonly fields: Record<string, unknown>;
130
+ /** Human-readable stderr line (trailing newline included), when surfaced there. */
131
+ readonly stderrLine?: string;
132
+ /** Warnings-channel entry — the config the turn proceeded past. */
133
+ readonly warning?: string;
134
+ /** Thrown message; used only by fail-closed steps. */
135
+ readonly message: string;
136
+ }
137
+ /**
138
+ * One ordered post-open configuration step (after session/new|load, before
139
+ * the first prompt).
140
+ */
141
+ export interface AcpSessionStep {
142
+ /** Stable id for logs and tests, e.g. "permission-mode" | "session-mode" | "model" | "effort". */
143
+ readonly id: string;
144
+ /**
145
+ * Failure policy — profile DATA, interpreted by the driver:
146
+ * - "fail-closed": a rejected step aborts the turn before
147
+ * session/prompt (a provider that refused the posture can hang on its
148
+ * first tool call).
149
+ * - "non-fatal": record the failure (errorLog + stderr + warnings
150
+ * channel) and proceed with whatever config was already active.
151
+ */
152
+ readonly onFailure: "fail-closed" | "non-fatal";
153
+ applies(ctx: AcpSessionContext): boolean;
154
+ run(ctx: AcpSessionContext): Promise<void>;
155
+ describeFailure(ctx: AcpSessionContext, error: unknown): AcpStepFailure;
156
+ /** dlogv line on success (debug-gated). */
157
+ successLog?(ctx: AcpSessionContext): string;
158
+ }
159
+ /** Options for the opencode profile's probe entry point. */
160
+ export interface OpenCodeAcpConfigProbeOptions {
161
+ cwd: string;
162
+ model: string;
163
+ effort?: string;
164
+ env?: NodeJS.ProcessEnv;
165
+ binaryPath?: string;
166
+ timeoutMs?: number;
167
+ }
168
+ export type OpenCodeAcpConfigProbeResult = {
169
+ status: "verified";
170
+ } | {
171
+ status: "rejected";
172
+ option: "model" | "effort";
173
+ value: string;
174
+ error: string;
175
+ } | {
176
+ status: "unavailable";
177
+ reason: "spawn" | "handshake" | "timeout";
178
+ error?: string;
179
+ };
180
+ export interface AcpProviderProfile {
181
+ /** Provider key: "kimi" | "opencode" | "copilot" | "gemini" | "hermes". */
182
+ readonly name: string;
183
+ /** Default binary; per-call overridable via `AcpOptions.binaryPath`. */
184
+ readonly binary: string;
185
+ /**
186
+ * Static base argv (the historical ACP_TOOLS entry). Per-call rewrites
187
+ * (permission mode, model) happen in `argv()` — never by mutating this.
188
+ */
189
+ readonly baseArgs: readonly string[];
190
+ /**
191
+ * Effective spawn argv for the resolved context. Absorbs the base args
192
+ * plus plan-mode rewrites (gemini's `-y` → `--approval-mode plan` swap)
193
+ * and spawn-time model flags (gemini `--model`). May warn (copilot
194
+ * plan/model gaps) or throw (hermes plan refusal).
195
+ */
196
+ argv(ctx: AcpProfileContext): string[];
197
+ /**
198
+ * Which mode the YOLO argv pre-flight asserts against the produced argv:
199
+ * the effective permission mode (default), or always "yolo" for
200
+ * providers that deliberately keep their yolo flag in plan mode
201
+ * (copilot).
202
+ */
203
+ readonly yoloAssertion?: "effective" | "always-yolo";
204
+ /**
205
+ * Pre-spawn filesystem/env preparation: opencode installs its runtime
206
+ * permission config, hermes sets HERMES_YOLO_MODE. Runs before the
207
+ * cwd-existence pre-check (historical ordering).
208
+ */
209
+ preSpawn?(ctx: AcpSpawnContext): void;
210
+ /**
211
+ * ACP `session/set_mode` vocabulary for providers whose permission
212
+ * posture is an ACP mode ladder (hermes). `known` is what the provider
213
+ * implements, `selectable` the subset lmctl is willing to drive a member
214
+ * into. Absent ⇒ the provider declares no vocabulary and any acpMode
215
+ * request is rejected fail-closed before spawn.
216
+ */
217
+ readonly modeVocabulary?: {
218
+ known: readonly AcpMode[];
219
+ selectable: readonly AcpMode[];
220
+ };
221
+ /**
222
+ * Ordered post-open configuration steps. Array order IS the wire order
223
+ * (kimi: permission mode before model; hermes: set_mode before
224
+ * set_model; opencode: model before effort). Per-step failure policy is
225
+ * the step's `onFailure`.
226
+ */
227
+ readonly sessionSetup?: readonly AcpSessionStep[];
228
+ /**
229
+ * Result-text isolation applied INSIDE the stateful output processor
230
+ * (parsers/acp.ts) — gemini's latest-agent-message replay isolation.
231
+ */
232
+ readonly isolateResult?: (text: string) => string;
233
+ /**
234
+ * Bundle-open session-identity strategy (see AcpSessionIdentity).
235
+ * Absent ⇒ the driver requires an exact sessionId echo in the load
236
+ * response and fails closed without one.
237
+ */
238
+ readonly sessionIdentity?: AcpSessionIdentity;
239
+ /**
240
+ * Structured telemetry emitted during exit finalization, after the
241
+ * output processor has produced the final result (kimi runtime-dead).
242
+ */
243
+ onExitFinalization?(ctx: AcpFinalizeContext): void;
244
+ /**
245
+ * Filters provider-known-benign stderr blocks out of the no-result
246
+ * error report (kimi's notifications/initialized -32601 block). This is
247
+ * error-path stderr hygiene, not a result transform.
248
+ */
249
+ filterExpectedStderr?(stderr: string): string;
250
+ /**
251
+ * Separate probe entry point (opencode only): verifies the provider's
252
+ * live per-session model/effort contract WITHOUT prompting. It
253
+ * intentionally differs from the chat spawn — no runtime
254
+ * permission-config install, and a rejected model/effort option is
255
+ * reported as probe evidence rather than absorbed as a warning.
256
+ */
257
+ readonly probe?: (opts: OpenCodeAcpConfigProbeOptions) => Promise<OpenCodeAcpConfigProbeResult>;
258
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * ACP JSON-RPC 2.0 channel over a spawned child's stdin/stdout.
3
+ *
4
+ * Pure protocol plumbing shared by the chat engine (transports/acp.ts)
5
+ * and profile-owned entry points that run their own spawn/RPC lifecycle
6
+ * (the opencode config probe in transports/acp-profiles/opencode.ts).
7
+ */
8
+ import type { ChildProcess } from "node:child_process";
9
+ export interface AcpRpcChannel {
10
+ sendRpc(method: string, params?: Record<string, unknown>): Promise<Record<string, unknown>>;
11
+ sendNotification(method: string, params?: Record<string, unknown>): void;
12
+ close(error?: Error): void;
13
+ }
14
+ export declare function createAcpRpcChannel(child: ChildProcess, opts: {
15
+ provider: string;
16
+ onEvent?: (event: Record<string, unknown>) => void;
17
+ onNotification?: (event: Record<string, unknown>) => void;
18
+ }): AcpRpcChannel;
@@ -0,0 +1,215 @@
1
+ /**
2
+ * Generic ACP (Agent Client Protocol) transport — the protocol engine.
3
+ *
4
+ * Works with any CLI tool that supports ACP (Gemini, Copilot, OpenCode, Kimi,
5
+ * Hermes). Spawns the tool and communicates via ACP JSON-RPC 2.0 over
6
+ * stdin/stdout.
7
+ *
8
+ * Protocol:
9
+ * initialize → session/new (or session/load) → session/prompt
10
+ * ← session/update (agent_message_chunk, streaming)
11
+ * ← result with stopReason
12
+ *
13
+ * Everything provider-specific (argv shapes, plan-mode rewrites, config
14
+ * installs, post-open mode/model/effort configuration, result isolation,
15
+ * exit telemetry, the opencode config probe) lives behind
16
+ * `AcpProviderProfile` in `./acp-profiles/`; this file is the generic
17
+ * driver: RPC channel lifecycle, abort/hold-open scaffolding, the
18
+ * initialize → load/new → prompt skeleton, and the resume contract
19
+ * (`requireResume` — deliberately NOT a profile capability).
20
+ *
21
+ * `sendAcpPrompt` is internally phased (spawn → open → configure → prompt
22
+ * → finalize) so a future `openAcpSession(profile, opts)` bundle seam can
23
+ * hoist the spawn+open phases out of the per-turn prompt phase without
24
+ * restructuring (batch redesign, item 2/3).
25
+ */
26
+ import { type HoldOpenOptions } from "./hold-open.js";
27
+ import { type PermissionMode } from "../yolo-contract.js";
28
+ import { type AcpResult } from "../parsers/acp.js";
29
+ import { ACP_TOOLS_FOR_TEST } from "./acp-profiles/index.js";
30
+ import type { AcpMode } from "./acp-profiles/types.js";
31
+ import { detectKimiRuntimeDead } from "./acp-profiles/kimi.js";
32
+ import { probeOpenCodeAcpConfig } from "./acp-profiles/opencode.js";
33
+ export { acpSessionIdFromResponse, acpStopReasonFromResponse, createAcpOutputProcessor, isolateGeminiLatestAgentMessage, supportsAcp, } from "../parsers/acp.js";
34
+ export type { AcpOutputProcessor, AcpOutputProcessorResult, AcpResult, } from "../parsers/acp.js";
35
+ export { ACP_TOOLS_FOR_TEST, detectKimiRuntimeDead, probeOpenCodeAcpConfig };
36
+ export type { AcpMode } from "./acp-profiles/types.js";
37
+ export type { OpenCodeAcpConfigProbeOptions, OpenCodeAcpConfigProbeResult, } from "./acp-profiles/types.js";
38
+ export interface AcpOptions {
39
+ /** Existing session ID to load. If omitted, creates a new session. */
40
+ sessionId?: string;
41
+ cwd: string;
42
+ env?: NodeJS.ProcessEnv;
43
+ /** Tool name — determines the provider profile (binary, argv, hooks) */
44
+ tool: string;
45
+ /**
46
+ * Override the spawned binary path. When set, this exact path is
47
+ * spawned (with the profile's standard ACP args) instead of the
48
+ * profile's default binary. Used for troubleshooting custom
49
+ * builds. (Task 63 fix 3.)
50
+ */
51
+ binaryPath?: string;
52
+ /** If true, failed session/load is an error. If false, falls back to session/new. */
53
+ requireResume?: boolean;
54
+ /**
55
+ * Per-session model override. Honored for Gemini via a
56
+ * spawn-time `--model` flag and for OpenCode/Kimi via ACP
57
+ * `session/set_config_option`. Format: `providerID/modelID` (e.g.
58
+ * `anthropic/claude-3-5-sonnet-20241022`). Copilot does not expose
59
+ * a per-session ACP model selection surface.
60
+ */
61
+ model?: string;
62
+ /**
63
+ * Per-session effort / variant override. OpenCode uses ACP configId
64
+ * `"effort"`. Kimi 0.28.x exposes only an on/off thinking toggle,
65
+ * so its model-defined effort cannot be overridden here.
66
+ */
67
+ effort?: string;
68
+ /**
69
+ * Teamfile alias of the spawned member, retained for diagnostics and
70
+ * provider-specific policy.
71
+ */
72
+ alias?: string;
73
+ /**
74
+ * Permission mode for this turn (default "yolo"). How "plan" is
75
+ * realized is profile policy: gemini's argv swaps `-y` for
76
+ * `--approval-mode plan`, kimi sets its protocol-level mode to "plan",
77
+ * copilot keeps `--yolo` and warns (no plan flag exists), opencode is
78
+ * config-file based, and hermes refuses plan mode outright.
79
+ *
80
+ * Caveat (gemini): plan mode in gemini-cli filters `write_file` by
81
+ * workspace path only; `run_shell_command` can still escape. A
82
+ * follow-up will tighten the sandbox for plan mode.
83
+ */
84
+ permissionMode?: PermissionMode;
85
+ /** Called for each text delta */
86
+ onDelta?: (text: string) => void;
87
+ /** Called for each raw JSON-RPC event */
88
+ onEvent?: (event: Record<string, unknown>) => void;
89
+ /** Abort signal — kills the provider process when aborted */
90
+ signal?: AbortSignal;
91
+ /**
92
+ * Defer closing the child's stdin past the turn's result while the session
93
+ * still has outstanding `run_in_background` dispatches (task #68, extended to
94
+ * ACP). Absent ⇒ the exact pre-existing synchronous close. See
95
+ * {@link HoldOpenOptions}.
96
+ */
97
+ holdOpen?: HoldOpenOptions;
98
+ /**
99
+ * Explicit ACP `session/set_mode` mode id to apply to the session on
100
+ * every spawn, after `session/new`/`session/load` completes.
101
+ *
102
+ * Needed by providers whose ACP mode ladder is NOT covered by an argv
103
+ * permission flag and NOT expressible through lmctl's own
104
+ * `permissionMode` vocabulary. Hermes is the only such provider today:
105
+ * its ladder (`default`/`accept_edits`/`dont_ask`) governs the file-EDIT
106
+ * gate, which `HERMES_YOLO_MODE` does not approve, so the mode must be
107
+ * set per process or every edit stalls on a
108
+ * `session/request_permission` round-trip this client cannot answer.
109
+ *
110
+ * Validated at the call site against the profile's mode vocabulary —
111
+ * an unconfigured value throws fail-closed (`assertAcpModeSupported`)
112
+ * rather than being forwarded as an unknown mode id.
113
+ */
114
+ acpMode?: AcpMode;
115
+ }
116
+ /**
117
+ * Fail-closed guard for `AcpOptions.acpMode`. Throws for a provider whose
118
+ * profile declares no mode vocabulary (nothing to validate against ⇒
119
+ * never guess) and for a mode outside the vocabulary's selectable subset.
120
+ */
121
+ export declare function assertAcpModeSupported(tool: string, mode: AcpMode): void;
122
+ export declare function filterExpectedAcpStderr(tool: string, stderr: string): string;
123
+ /**
124
+ * Send a prompt via ACP to any supported tool.
125
+ */
126
+ export declare function sendAcpPrompt(prompt: string, opts: AcpOptions): Promise<AcpResult>;
127
+ /**
128
+ * Per-turn options for an open ACP bundle session. The spawn-time profile
129
+ * context (permissionMode, model, effort, acpMode) was fixed at open; a
130
+ * turn only carries its streaming callbacks and an abort signal.
131
+ */
132
+ export interface AcpSessionTurnOptions {
133
+ /** Called for each text delta belonging to THIS turn. */
134
+ onDelta?: (text: string) => void;
135
+ /** Called for each raw JSON-RPC event during THIS turn. */
136
+ onEvent?: (event: Record<string, unknown>) => void;
137
+ /**
138
+ * Abort signal for THIS turn. Abortion rejects the turn with
139
+ * `Error("Aborted")`, sends a best-effort `session/cancel`
140
+ * notification, and leaves the session open and usable — the child is
141
+ * NOT killed (contrast with `sendAcpPrompt`, where abort terminates the
142
+ * one-shot process). Chunks a provider still emits for the cancelled
143
+ * turn are dropped by the output processor.
144
+ */
145
+ signal?: AbortSignal;
146
+ }
147
+ export interface AcpSessionTurnResult {
148
+ result: string;
149
+ sessionId: string;
150
+ stopReason: string;
151
+ durationMs: number;
152
+ /**
153
+ * The open-time profile configuration warnings (task #232 model/effort
154
+ * drops) are reported on the FIRST turn's result — in bundle mode the
155
+ * profile's sessionSetup runs once at open, not per spawn.
156
+ */
157
+ warnings?: string[];
158
+ }
159
+ /**
160
+ * An open ACP bundle session: one provider process serving many prompts.
161
+ *
162
+ * Lifecycle contract (agreed in the batch-redesign v2 review before
163
+ * implementation; pinned by tests/providers/acp-bundle.test.ts):
164
+ *
165
+ * - Per-turn completion boundary: a turn ends at its `session/prompt`
166
+ * result. The stateful output processor is retained across turns and
167
+ * reset per turn (`beginTurn`), so chunks arriving after the boundary
168
+ * (provider replay trailing a load, late deltas) never leak into the
169
+ * next turn's result.
170
+ * - Cancellation: aborting a turn rejects that turn, sends
171
+ * `session/cancel`, and keeps the session usable.
172
+ * - Hold-open policy: the process is NOT closed between turns — the
173
+ * persistence IS the hold (the per-turn stdin close of
174
+ * `sendAcpPrompt` is suppressed). `close()` runs the caller's
175
+ * `holdOpen` outstanding-work check ONCE before ending stdin, so the
176
+ * final turn's background dispatches are not orphaned.
177
+ * - Unexpected exit: a mid-bundle process exit rejects the in-flight
178
+ * turn and every later turn with an error naming the number of
179
+ * completed prompts.
180
+ * - Idempotent, bounded close: `close()` may be called repeatedly and
181
+ * during an in-flight turn; a child that ignores stdin EOF is
182
+ * terminated after a bounded wait — close never hangs.
183
+ * - Identity: open requires `session/load` to return the EXACT
184
+ * requested session id (no fallback to the requested id, no
185
+ * session/new fallback) before prompt 1 is sent.
186
+ */
187
+ export interface AcpSession {
188
+ readonly tool: string;
189
+ /** The verified session id (exact match to the requested id). */
190
+ readonly sessionId: string;
191
+ /** Successfully completed turns — the count bundle errors report. */
192
+ readonly completedTurns: number;
193
+ sendTurn(prompt: string, opts?: AcpSessionTurnOptions): Promise<AcpSessionTurnResult>;
194
+ close(): Promise<void>;
195
+ }
196
+ export interface AcpSessionOptions extends AcpOptions {
197
+ /**
198
+ * Shutdown cap: how long close() waits for the child to exit after
199
+ * stdin EOF before escalating to the terminate ladder (which is itself
200
+ * bounded by the same cap per step). Default 2000ms. Exists so tests
201
+ * and operator-driven shutdowns can tighten the bound.
202
+ */
203
+ closeTimeoutMs?: number;
204
+ }
205
+ /**
206
+ * Open a persistent ACP session for `opts.tool`: spawn → initialize →
207
+ * session/load (strict identity, fail-closed) → profile sessionSetup —
208
+ * everything `sendAcpPrompt` does per turn EXCEPT the prompt phase, which
209
+ * becomes `session.sendTurn`.
210
+ *
211
+ * `requireResume` is implied: a bundle without its exact teamfile session
212
+ * is always an error. `opts.signal` is intentionally not consulted here —
213
+ * bundle cancellation is per turn (`AcpSessionTurnOptions.signal`).
214
+ */
215
+ export declare function openAcpSession(opts: AcpSessionOptions): Promise<AcpSession>;