@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.
- package/CHANGELOG.md +131 -0
- package/README.md +11 -51
- package/data/price-aliases.json +1 -0
- package/data/price-data.json +1 -1
- package/dist/cli/batch.d.ts +2 -0
- package/dist/cli/busy-evidence.d.ts +27 -0
- package/dist/cli/chat-cycle.d.ts +59 -0
- package/dist/cli/chat-exec.d.ts +83 -0
- package/dist/cli/chat-policy.d.ts +22 -0
- package/dist/cli/chat.d.ts +23 -0
- package/dist/cli/code-resolver.d.ts +8 -0
- package/dist/cli/common.d.ts +54 -0
- package/dist/cli/context.d.ts +12 -0
- package/dist/cli/cost.d.ts +2 -0
- package/dist/cli/export.d.ts +2 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.js +1081 -1891
- package/dist/cli/infra.d.ts +78 -0
- package/dist/cli/ls.d.ts +2 -0
- package/dist/cli/mailbox-cli.d.ts +6 -0
- package/dist/cli/managed.d.ts +62 -0
- package/dist/cli/mcp-install.d.ts +23 -0
- package/dist/cli/mcp.d.ts +2 -0
- package/dist/cli/paths.d.ts +50 -0
- package/dist/cli/plan-wizard.d.ts +42 -0
- package/dist/cli/plan.d.ts +20 -0
- package/dist/cli/queue-delivery.d.ts +107 -0
- package/dist/cli/ratelimit.d.ts +3 -0
- package/dist/cli/remote-fetch.d.ts +7 -0
- package/dist/cli/run-queries.d.ts +82 -0
- package/dist/cli/running.d.ts +5 -0
- package/dist/cli/schema.sql +20 -1
- package/dist/cli/script.d.ts +6 -0
- package/dist/cli/script.js +2097 -0
- package/dist/cli/self-identity.d.ts +25 -0
- package/dist/cli/session-idle.d.ts +118 -0
- package/dist/cli/status.d.ts +22 -0
- package/dist/cli/tail.d.ts +18 -0
- package/dist/cli/target-resolver.d.ts +139 -0
- package/dist/cli/team-status.d.ts +91 -0
- package/dist/cli/teamfile-compat.d.ts +71 -0
- package/dist/cli/teamfile-store.d.ts +18 -0
- package/dist/cli/teamfile-template.d.ts +40 -0
- package/dist/cli/template-env.d.ts +25 -0
- package/dist/cli/terminal-lock.d.ts +25 -0
- package/dist/cli/terminal.d.ts +23 -0
- package/dist/cli/time-filter.d.ts +10 -0
- package/dist/cli/update-notifier.d.ts +11 -0
- package/dist/cli/version-util.d.ts +2 -0
- package/dist/config/seed_templates.d.ts +1 -0
- package/dist/db/backfill-event-log.d.ts +26 -0
- package/dist/db/delegation.d.ts +37 -0
- package/dist/db/escalation_vocab.d.ts +45 -0
- package/dist/db/event-log.d.ts +40 -0
- package/dist/db/index.d.ts +1213 -0
- package/dist/db/inflight-acquire.d.ts +70 -0
- package/dist/db/invocation-event-projections.d.ts +65 -0
- package/dist/db/ledger-identity.test.d.ts +1 -0
- package/dist/db/live-event-projections.d.ts +92 -0
- package/dist/db/migrate.d.ts +51 -0
- package/dist/db/open-readonly.d.ts +2 -0
- package/dist/db/process-liveness.d.ts +37 -0
- package/dist/db/sqlite.d.ts +47 -0
- package/dist/db/sqlite.test.d.ts +1 -0
- package/dist/db/terminal-lock.d.ts +14 -0
- package/dist/db/tracked-invocation-reconcile.d.ts +52 -0
- package/dist/db/tracked-invocation.d.ts +116 -0
- package/dist/mcp/dispatch.d.ts +45 -0
- package/dist/mcp/mediation.d.ts +128 -0
- package/dist/mcp/path-warning.d.ts +7 -0
- package/dist/mcp/server.d.ts +63 -0
- package/dist/mcp/tools.d.ts +24 -0
- package/dist/providers/adapter.d.ts +265 -0
- package/dist/providers/adapters/agy-adapter.d.ts +17 -0
- package/dist/providers/adapters/claude-adapter.d.ts +20 -0
- package/dist/providers/adapters/codex-adapter.d.ts +22 -0
- package/dist/providers/adapters/copilot-adapter.d.ts +16 -0
- package/dist/providers/adapters/dsh-adapter.d.ts +89 -0
- package/dist/providers/adapters/gemini-adapter.d.ts +44 -0
- package/dist/providers/adapters/hermes-adapter.d.ts +71 -0
- package/dist/providers/adapters/index.d.ts +35 -0
- package/dist/providers/adapters/kimi-adapter.d.ts +16 -0
- package/dist/providers/adapters/lmplayer-adapter.d.ts +14 -0
- package/dist/providers/adapters/opencode-adapter.d.ts +11 -0
- package/dist/providers/adapters/pi-adapter.d.ts +17 -0
- package/dist/providers/adapters/print-turn.d.ts +29 -0
- package/dist/providers/adapters/qwen-adapter.d.ts +11 -0
- package/dist/providers/agy.d.ts +32 -0
- package/dist/providers/base-adapter.d.ts +107 -0
- package/dist/providers/base.d.ts +12 -0
- package/dist/providers/binary-check.d.ts +102 -0
- package/dist/providers/bundle.d.ts +118 -0
- package/dist/providers/claude-mock.d.ts +90 -0
- package/dist/providers/claude.d.ts +21 -0
- package/dist/providers/codex-config.d.ts +15 -0
- package/dist/providers/codex.d.ts +25 -0
- package/dist/providers/config/model-selection-lint.d.ts +45 -0
- package/dist/providers/config/team-config.d.ts +183 -0
- package/dist/providers/config/types.d.ts +146 -0
- package/dist/providers/config/user-config.d.ts +88 -0
- package/dist/providers/copilot.d.ts +21 -0
- package/dist/providers/debug.d.ts +128 -0
- package/dist/providers/dsh-sidecar.d.ts +65 -0
- package/dist/providers/dsh-zstd.d.ts +27 -0
- package/dist/providers/dsh.d.ts +37 -0
- package/dist/providers/frontmatter.d.ts +46 -0
- package/dist/providers/gemini-session-reader.d.ts +151 -0
- package/dist/providers/gemini.d.ts +39 -0
- package/dist/providers/hermes.d.ts +90 -0
- package/dist/providers/index.d.ts +421 -0
- package/dist/providers/kimi.d.ts +37 -0
- package/dist/providers/model-catalog.d.ts +141 -0
- package/dist/providers/one-shot-timeout.d.ts +1 -0
- package/dist/providers/opencode-config.d.ts +62 -0
- package/dist/providers/opencode.d.ts +40 -0
- package/dist/providers/parsers/acp.d.ts +47 -0
- package/dist/providers/parsers/codex-app-server.d.ts +60 -0
- package/dist/providers/parsers/stream-json.d.ts +57 -0
- package/dist/providers/passive-readers/agy.d.ts +155 -0
- package/dist/providers/passive-readers/copilot.d.ts +173 -0
- package/dist/providers/passive-readers/dsh.d.ts +34 -0
- package/dist/providers/passive-readers/kimi.d.ts +44 -0
- package/dist/providers/passive-readers/opencode.d.ts +89 -0
- package/dist/providers/passive-readers/qwen.d.ts +151 -0
- package/dist/providers/passive-readers/shared.d.ts +138 -0
- package/dist/providers/passive-session-reader.d.ts +188 -0
- package/dist/providers/path-utils.d.ts +1 -0
- package/dist/providers/pi.d.ts +32 -0
- package/dist/providers/pricing.d.ts +107 -0
- package/dist/providers/qwen.d.ts +22 -0
- package/dist/providers/ratelimit-readings.d.ts +32 -0
- package/dist/providers/registry.d.ts +31 -0
- package/dist/providers/resume.d.ts +43 -0
- package/dist/providers/seed-instruction.d.ts +3 -0
- package/dist/providers/self-session-env.d.ts +21 -0
- package/dist/providers/session-limit.d.ts +9 -0
- package/dist/providers/strip-ansi.d.ts +5 -0
- package/dist/providers/transport-errors.d.ts +94 -0
- package/dist/providers/transport-lifecycle.d.ts +63 -0
- package/dist/providers/transport-wrappers.d.ts +110 -0
- package/dist/providers/transports/acp-profiles/config-option-step.d.ts +24 -0
- package/dist/providers/transports/acp-profiles/copilot.d.ts +12 -0
- package/dist/providers/transports/acp-profiles/gemini.d.ts +22 -0
- package/dist/providers/transports/acp-profiles/hermes.d.ts +27 -0
- package/dist/providers/transports/acp-profiles/index.d.ts +19 -0
- package/dist/providers/transports/acp-profiles/kimi.d.ts +24 -0
- package/dist/providers/transports/acp-profiles/opencode.d.ts +26 -0
- package/dist/providers/transports/acp-profiles/types.d.ts +258 -0
- package/dist/providers/transports/acp-rpc.d.ts +18 -0
- package/dist/providers/transports/acp.d.ts +215 -0
- package/dist/providers/transports/codex-app-server.d.ts +190 -0
- package/dist/providers/transports/hold-open.d.ts +120 -0
- package/dist/providers/transports/manager.d.ts +21 -0
- package/dist/providers/transports/stream-json.d.ts +162 -0
- package/dist/providers/transports/types.d.ts +56 -0
- package/dist/providers/types.d.ts +154 -0
- package/dist/providers/yolo-contract.d.ts +131 -0
- package/dist/script/adapter.d.ts +101 -0
- package/dist/script/batch-script.d.ts +7 -0
- package/dist/script/index.d.ts +3 -0
- package/dist/script/lmscript.d.ts +550 -0
- package/dist/shared/policy.d.ts +74 -0
- package/dist/shared-lib/db.d.ts +25 -0
- package/dist/shared-lib/passive-session-reader.d.ts +2 -0
- package/dist/shared-lib/provider-parse.d.ts +6 -0
- package/dist/templates/basedir.d.ts +52 -0
- package/dist/uri/lmctl_uri.d.ts +38 -0
- package/durable-memory/model_selection.md +108 -25
- package/examples/README.md +103 -0
- package/examples/anonymous-panel.lms +3 -0
- package/examples/batch-prompts.txt +9 -0
- package/examples/batchscript.lms +67 -0
- package/examples/n8n/agent-lib.lms +56 -0
- package/examples/n8n/git-lib.lms +93 -0
- package/examples/n8n/mock-support.db +0 -0
- package/examples/n8n/multi-agent-review.lms +107 -0
- package/examples/n8n/news-digest.lms +159 -0
- package/examples/n8n/rag-search.lms +163 -0
- package/examples/n8n/support-agent.lms +88 -0
- package/examples/n8n/ticket-triage.lms +156 -0
- package/package.json +13 -17
- package/dist/cli/side_effect_classifier.json +0 -19
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import type { AliasTable } from "./model-catalog.js";
|
|
2
|
+
export declare const PRICE_DATA_URL = "https://lmctl.com/dataset/model_list.json";
|
|
3
|
+
export declare const PRICE_ALIASES_URL = "https://lmctl.com/dataset/aliases.json";
|
|
4
|
+
export type PriceDataSource = "network" | "cache" | "bundled" | "pinned" | "unavailable";
|
|
5
|
+
export interface PriceRecord {
|
|
6
|
+
source: string;
|
|
7
|
+
source_model_id: string;
|
|
8
|
+
provider?: string | null;
|
|
9
|
+
mode?: string | null;
|
|
10
|
+
record_id?: string | null;
|
|
11
|
+
gateway_id?: string | null;
|
|
12
|
+
context_window?: {
|
|
13
|
+
input_tokens?: number | null;
|
|
14
|
+
} | null;
|
|
15
|
+
pricing: {
|
|
16
|
+
input_per_1m_tokens: number | null;
|
|
17
|
+
output_per_1m_tokens: number | null;
|
|
18
|
+
input_cache_read_per_1m_tokens: number | null;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export interface PriceCatalog {
|
|
22
|
+
schema_version: number;
|
|
23
|
+
generated_at: string;
|
|
24
|
+
records: PriceRecord[];
|
|
25
|
+
}
|
|
26
|
+
export interface PriceDataResult {
|
|
27
|
+
catalog: PriceCatalog | null;
|
|
28
|
+
source: PriceDataSource;
|
|
29
|
+
generatedAt: string | null;
|
|
30
|
+
}
|
|
31
|
+
export interface CatalogSnapshot {
|
|
32
|
+
catalog: PriceCatalog | null;
|
|
33
|
+
aliases: AliasTable | null;
|
|
34
|
+
source: PriceDataSource;
|
|
35
|
+
generatedAt: string | null;
|
|
36
|
+
}
|
|
37
|
+
export declare function isValidCatalog(payload: unknown): payload is PriceCatalog;
|
|
38
|
+
export declare function isValidAliases(payload: unknown): payload is AliasTable;
|
|
39
|
+
export interface LoadCatalogOptions {
|
|
40
|
+
fetchImpl?: typeof fetch;
|
|
41
|
+
cacheDir?: string;
|
|
42
|
+
/** Override for the bundled model_list path; `null` disables that fallback. */
|
|
43
|
+
bundledPath?: string | null;
|
|
44
|
+
/** Override for the bundled aliases path; `null` disables that fallback. */
|
|
45
|
+
bundledAliasesPath?: string | null;
|
|
46
|
+
/** Override for LMCTL_PRICE_DATA; `null` forces unpinned. */
|
|
47
|
+
pinDir?: string | null;
|
|
48
|
+
/** Test seam for the TTL gate (defaults to Date.now()). */
|
|
49
|
+
now?: number;
|
|
50
|
+
/** Test seam overriding LMCTL_PRICE_TTL (ms). */
|
|
51
|
+
ttlMs?: number;
|
|
52
|
+
}
|
|
53
|
+
/** Dataset generated_at of the most recently loaded snapshot, for --debug surfaces. */
|
|
54
|
+
export declare function datasetGeneratedAt(): string | null;
|
|
55
|
+
/** Test seam: drop the memoized no-argument snapshot (process-level memo). */
|
|
56
|
+
export declare function _resetCatalogSnapshotMemoForTests(): void;
|
|
57
|
+
/**
|
|
58
|
+
* The one snapshot loader for the lmprice integration. Never throws. One
|
|
59
|
+
* snapshot load per process invocation: the no-argument call (the normal
|
|
60
|
+
* command path) is memoized module-wide, so lint/seed/cost members never
|
|
61
|
+
* trigger per-member re-downloads; calls with explicit options (tests,
|
|
62
|
+
* seams) always execute.
|
|
63
|
+
*/
|
|
64
|
+
export declare function loadCatalogSnapshot(opts?: LoadCatalogOptions): Promise<CatalogSnapshot>;
|
|
65
|
+
/**
|
|
66
|
+
* The one function `lmctl cost`'s Phase 1 callers use. Never throws, never
|
|
67
|
+
* blocks longer than `VERSION_CHECK_TIMEOUT_MS` for the "is there anything
|
|
68
|
+
* new" check regardless of network state (per resource, outside the TTL) --
|
|
69
|
+
* a HEAD request specifically so a slow/large BODY download can never blow
|
|
70
|
+
* that budget. Thin wrapper over loadCatalogSnapshot: the model-list half
|
|
71
|
+
* of the snapshot with the same result shape as before the two-resource
|
|
72
|
+
* extension.
|
|
73
|
+
*/
|
|
74
|
+
export declare function loadPriceData(opts?: LoadCatalogOptions): Promise<PriceDataResult>;
|
|
75
|
+
/**
|
|
76
|
+
* Records are source-specific and NOT reconciled across sources (lmprice's
|
|
77
|
+
* own documented behavior) -- the same model can appear many times (bare id,
|
|
78
|
+
* `vendor/id`, `region/vendor/id`, ...) with the litellm and openrouter
|
|
79
|
+
* sources potentially disagreeing. `records` is already sorted by
|
|
80
|
+
* (source, provider, source_model_id) -- "litellm" sorts before "openrouter"
|
|
81
|
+
* -- so preferring the first exact-bare-id match, then the first
|
|
82
|
+
* suffix (`/<modelId>`) match, is a deterministic, if arbitrary, tie-break
|
|
83
|
+
* rather than an average/reconciliation across disagreeing sources.
|
|
84
|
+
*
|
|
85
|
+
* Superseded for provider-aware resolution by model-catalog.ts's
|
|
86
|
+
* resolveModel(); kept for `lmctl cost` Phase 1 compatibility.
|
|
87
|
+
*/
|
|
88
|
+
export declare function findModelPrice(catalog: PriceCatalog, modelId: string): PriceRecord["pricing"] | null;
|
|
89
|
+
/**
|
|
90
|
+
* Phase 1 cost model (approved design): the session's existing AGGREGATE
|
|
91
|
+
* token totals x one model's rate -- not a true per-model breakdown (see
|
|
92
|
+
* `runCost`'s `approximate` flag for when a session touched more than one
|
|
93
|
+
* model; that's a separate, later sub-task). `cachedInput` is priced at the
|
|
94
|
+
* cache-READ rate only -- lmprice's `input_cache_write_per_1m_tokens` isn't
|
|
95
|
+
* consumed here because lctl-src's own token accounting
|
|
96
|
+
* (`totalCachedInputTokens`) has never distinguished cache-write from
|
|
97
|
+
* cache-read tokens; conflating them into the (usually cheaper) read rate
|
|
98
|
+
* would UNDERSTATE cost for a cache-write-heavy session, but overstating via
|
|
99
|
+
* the (usually pricier) write rate for every cache hit is worse on average.
|
|
100
|
+
* Returns `null` when any needed price is missing -- never a partial/guessed
|
|
101
|
+
* number.
|
|
102
|
+
*/
|
|
103
|
+
export declare function computeCostUsd(tokens: {
|
|
104
|
+
input: number;
|
|
105
|
+
output: number;
|
|
106
|
+
cachedInput: number;
|
|
107
|
+
}, pricing: PriceRecord["pricing"] | null): number | null;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Qwen Code session provider.
|
|
3
|
+
*
|
|
4
|
+
* Qwen Code is a Claude Code fork — same JSONL session format in ~/.qwen/.
|
|
5
|
+
* This provider reuses the Claude provider logic with Qwen-specific paths.
|
|
6
|
+
*/
|
|
7
|
+
import type { SessionProvider, SessionSummary, SessionDetail } from "./types.js";
|
|
8
|
+
interface QwenProviderOptions {
|
|
9
|
+
qwenDir?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare class QwenProvider implements SessionProvider {
|
|
12
|
+
name: "qwen";
|
|
13
|
+
private baseDir;
|
|
14
|
+
constructor(opts?: QwenProviderOptions);
|
|
15
|
+
discoverSessions(): Promise<SessionSummary[]>;
|
|
16
|
+
private extractSummary;
|
|
17
|
+
loadSession(sessionId: string): Promise<SessionDetail | null>;
|
|
18
|
+
private loadFromFile;
|
|
19
|
+
resolveSessionPath(sessionId: string): Promise<string | null>;
|
|
20
|
+
deleteSession(sessionId: string): Promise<boolean>;
|
|
21
|
+
}
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export declare const VALID_RATELIMIT_TARGETS: readonly ["all", "codex", "agy", "claude", "kimi"];
|
|
2
|
+
export interface RatelimitRunResult {
|
|
3
|
+
status: number | null;
|
|
4
|
+
stdout: string;
|
|
5
|
+
stderr: string;
|
|
6
|
+
signal?: string | null;
|
|
7
|
+
error?: (Error & {
|
|
8
|
+
code?: string;
|
|
9
|
+
}) | undefined;
|
|
10
|
+
}
|
|
11
|
+
export type RatelimitRunner = (bin: string, args: string[], timeoutMs: number) => RatelimitRunResult;
|
|
12
|
+
export interface RatelimitFsDeps {
|
|
13
|
+
exists(path: string): boolean;
|
|
14
|
+
}
|
|
15
|
+
export declare function _setRatelimitRunnerForTests(fn: RatelimitRunner | null): void;
|
|
16
|
+
export declare function _setRatelimitFsDepsForTests(deps: Partial<RatelimitFsDeps> | null): void;
|
|
17
|
+
export interface RatelimitReadingsOptions {
|
|
18
|
+
providers?: string[];
|
|
19
|
+
targets?: string[];
|
|
20
|
+
timeoutSeconds?: number;
|
|
21
|
+
env?: NodeJS.ProcessEnv;
|
|
22
|
+
progress?: (message: string) => void;
|
|
23
|
+
}
|
|
24
|
+
export type RatelimitReadingsResult = {
|
|
25
|
+
status: 'ok';
|
|
26
|
+
readings: Record<string, unknown>[];
|
|
27
|
+
} | {
|
|
28
|
+
status: 'error';
|
|
29
|
+
error: string;
|
|
30
|
+
readings: [];
|
|
31
|
+
};
|
|
32
|
+
export declare function queryRatelimitReadings(options?: RatelimitReadingsOptions): RatelimitReadingsResult;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { SessionProvider, SessionSummary, SessionDetail, ProviderName, WatchTarget } from "./types.js";
|
|
2
|
+
import type { ProviderAdapter } from "./adapter.js";
|
|
3
|
+
export type { ProviderAdapter };
|
|
4
|
+
export declare class ProviderRegistry {
|
|
5
|
+
private providers;
|
|
6
|
+
private adapters;
|
|
7
|
+
register(provider: SessionProvider): void;
|
|
8
|
+
listAll(filter?: {
|
|
9
|
+
provider?: ProviderName;
|
|
10
|
+
workspace?: string;
|
|
11
|
+
limit?: number;
|
|
12
|
+
from?: Date;
|
|
13
|
+
to?: Date;
|
|
14
|
+
}): Promise<SessionSummary[]>;
|
|
15
|
+
getSession(sessionId: string, provider?: ProviderName): Promise<SessionDetail | null>;
|
|
16
|
+
resolveSession(sessionId: string, provider?: ProviderName): Promise<{
|
|
17
|
+
provider: SessionProvider;
|
|
18
|
+
filePath: string;
|
|
19
|
+
} | null>;
|
|
20
|
+
resolveSessions(sessionId: string, provider?: ProviderName): Promise<Array<{
|
|
21
|
+
provider: SessionProvider;
|
|
22
|
+
filePath: string;
|
|
23
|
+
}>>;
|
|
24
|
+
getWatchTargets(): WatchTarget[];
|
|
25
|
+
deleteSession(sessionId: string, provider?: ProviderName): Promise<boolean>;
|
|
26
|
+
registerAdapter(adapter: ProviderAdapter): void;
|
|
27
|
+
getAdapter(name: ProviderName): ProviderAdapter | undefined;
|
|
28
|
+
getAllAdapters(): ProviderAdapter[];
|
|
29
|
+
}
|
|
30
|
+
export declare function createRegistry(...providers: SessionProvider[]): ProviderRegistry;
|
|
31
|
+
export declare function createAdapterRegistry(adapters: ProviderAdapter[]): ProviderRegistry;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { ProviderName, SessionDetail } from './types.js';
|
|
2
|
+
export interface ResumeModelIgnored {
|
|
3
|
+
provider: string;
|
|
4
|
+
model: string;
|
|
5
|
+
reason: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Defensive sessionid format guard for the step-in spawn (design §2.4 /
|
|
9
|
+
* §10.B B3). Provider-minted ids are UUID-/slug-class; anything else is
|
|
10
|
+
* refused before any spawn (no shell, no injection surface).
|
|
11
|
+
*/
|
|
12
|
+
export declare const SESSIONID_RE: RegExp;
|
|
13
|
+
export declare function isResumableProvider(p: string): p is ProviderName;
|
|
14
|
+
/** Test-only: drop the cached registry between cases. */
|
|
15
|
+
export declare function _resetResumeRegistryForTests(): void;
|
|
16
|
+
/**
|
|
17
|
+
* The native resume incantation as an argv array (argv[0] is the PROVIDER
|
|
18
|
+
* binary — never lmctl). Returns null if `provider` has no registered
|
|
19
|
+
* adapter / resumable template. Tokenizes the native `resumeTemplate`
|
|
20
|
+
* (no quoted args in any template) and appends the YOLO suffix.
|
|
21
|
+
*/
|
|
22
|
+
export declare function resumeIncantationArgv(provider: string, sessionId: string, opts?: {
|
|
23
|
+
isLead?: boolean;
|
|
24
|
+
model?: string;
|
|
25
|
+
onModelIgnored?: (ignored: ResumeModelIgnored) => void;
|
|
26
|
+
}): string[] | null;
|
|
27
|
+
/** Display form of {@link resumeIncantationArgv} (seed-print + >200K UX). */
|
|
28
|
+
export declare function resumeIncantationString(provider: string, sessionId: string, opts?: {
|
|
29
|
+
model?: string;
|
|
30
|
+
}): string | null;
|
|
31
|
+
/**
|
|
32
|
+
* Provider-agnostic session read (no provider round-trip, no `tokens`):
|
|
33
|
+
* the native per-provider session reader the registry already owns.
|
|
34
|
+
*/
|
|
35
|
+
export declare function loadSessionDetail(sessionId: string, provider: ProviderName): Promise<SessionDetail | null>;
|
|
36
|
+
/**
|
|
37
|
+
* P2 — bare-sessionid auto-detect: ask the registry which provider(s)
|
|
38
|
+
* a sessionid resolves to. Returns matching provider names (0/1/N).
|
|
39
|
+
* Callers map 0 ⇒ not-found, 1 ⇒ use that provider, N ⇒ ambiguous (ask
|
|
40
|
+
* the user to pass `--provider`). Thin wrapper over the existing
|
|
41
|
+
* `registry().resolveSessions` — no new coupling.
|
|
42
|
+
*/
|
|
43
|
+
export declare function findSessionProviders(sessionId: string): Promise<ProviderName[]>;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare const SEED_ACK = "ACK: waiting for explicit lmctl task.";
|
|
2
|
+
export declare const SEED_INITIAL_PROMPT: string;
|
|
3
|
+
export declare const MANUAL_SEED_POST_LAUNCH_NOTE = "send this exact lmctl seed instruction: \"lmctl seed initial connection only. Create or pin this provider session, then stop. Do not inspect files, run commands, infer a task, delegate, or edit. A dispatch that dies mid-turn is usually a timeout you set -- use --idle-timeout, not a shell timeout. Reply exactly: ACK: waiting for explicit lmctl task.\"";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export declare const LMCTL_SELF_SESSIONID = "LMCTL_SELF_SESSIONID";
|
|
2
|
+
export declare const LMCTL_LAUNCH_SOURCE = "LMCTL_LAUNCH_SOURCE";
|
|
3
|
+
export type LmctlLaunchSource = 'chat' | 'terminal' | 'unknown';
|
|
4
|
+
/**
|
|
5
|
+
* A shell/profile can export `CLAUDE_CODE_EXECPATH` pointing at a binary
|
|
6
|
+
* for the WRONG platform (a real incident: a WSL environment inherited a
|
|
7
|
+
* Windows `claude.exe` from a cross-platform npm install). The `claude`
|
|
8
|
+
* CLI itself trusts this override unconditionally and re-execs into it,
|
|
9
|
+
* so a bad value there breaks every claude-provider spawn with a generic
|
|
10
|
+
* "native binary not installed" error that reads as a global install
|
|
11
|
+
* problem rather than one bad env var. lmctl can't fix the CLI's own
|
|
12
|
+
* re-exec logic, but it fully controls the env it hands the child, so it
|
|
13
|
+
* strips an override that's clearly wrong for the current platform
|
|
14
|
+
* (non-Windows + `.exe`, or a path that doesn't exist) before spawning —
|
|
15
|
+
* the child then falls back to its own default binary discovery instead
|
|
16
|
+
* of blindly trusting a broken pointer.
|
|
17
|
+
*/
|
|
18
|
+
export declare function sanitizeClaudeExecPathEnv(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
|
|
19
|
+
export declare function withProviderCwdEnv(env: NodeJS.ProcessEnv | undefined, cwd: string): NodeJS.ProcessEnv;
|
|
20
|
+
export declare function withSelfSessionEnv(env: NodeJS.ProcessEnv | undefined, sessionId: string | undefined): NodeJS.ProcessEnv | undefined;
|
|
21
|
+
export declare function withProviderSpawnEnv(env: NodeJS.ProcessEnv | undefined, cwd: string, sessionId: string | undefined, launchSource?: LmctlLaunchSource): NodeJS.ProcessEnv;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ConversationMessage } from './types.js';
|
|
2
|
+
export interface SessionLimitState {
|
|
3
|
+
state: 'LIMITED';
|
|
4
|
+
resets_at: string | null;
|
|
5
|
+
reset_time: string;
|
|
6
|
+
timezone: string | null;
|
|
7
|
+
detected_at: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function detectSessionLimit(messages: ConversationMessage[]): SessionLimitState | null;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Strips ANSI/VT100 escape sequences (color codes, etc.) so provider error
|
|
2
|
+
* text captured from a child process's stderr reads as plain text instead
|
|
3
|
+
* of literal escape-code noise wherever it's surfaced (a captured log, a
|
|
4
|
+
* terminal that doesn't render them, JSON output). */
|
|
5
|
+
export declare function stripAnsiCodes(text: string): string;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Named error taxonomy for the transport layer (claude stream-json,
|
|
3
|
+
* codex app-server, ACP). Replaces the prior `new Error("RPC error: ...")`
|
|
4
|
+
* string-dump pattern with typed classes that preserve structured fields
|
|
5
|
+
* so downstream callers can branch on numeric codes / inspect the raw
|
|
6
|
+
* line / read the original cause without re-parsing the message.
|
|
7
|
+
*
|
|
8
|
+
* Shape mirrors the claude-agent-sdk-python error hierarchy
|
|
9
|
+
* (CLIConnectionError / CLINotFoundError / ProcessError /
|
|
10
|
+
* CLIJSONDecodeError / MessageParseError) flagged in
|
|
11
|
+
* `tasks/83-claude-sdk-research.md` §4 takeaway 3, and the
|
|
12
|
+
* `createRpcError` helper that the codex-app-server transport
|
|
13
|
+
* adopted from openai/codex-plugin-cc (research:
|
|
14
|
+
* `tasks/83-codex-transport-compare.md`).
|
|
15
|
+
*
|
|
16
|
+
* All classes extend `TransportError`, which extends `Error` — every
|
|
17
|
+
* instance still satisfies `instanceof Error` so existing
|
|
18
|
+
* `try/catch (e: unknown)` sites continue to work without changes.
|
|
19
|
+
* The added discriminator is `instanceof TransportError` (and the
|
|
20
|
+
* specific subclasses) for callers who want the structured fields.
|
|
21
|
+
*
|
|
22
|
+
* The existing `TurnAbortError` (in `base-adapter.ts`) is a separate
|
|
23
|
+
* concern — user-/idle-/provider-cancel reasons surfaced by the
|
|
24
|
+
* higher-level chat orchestrator — and lives outside this taxonomy.
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* Base class for transport-layer failures. Carries `name` set to the
|
|
28
|
+
* concrete subclass name (so error messages on uncaught throws
|
|
29
|
+
* identify the subtype) and propagates `cause` per the standard
|
|
30
|
+
* Error options shape.
|
|
31
|
+
*/
|
|
32
|
+
export declare class TransportError extends Error {
|
|
33
|
+
constructor(message: string, options?: {
|
|
34
|
+
cause?: unknown;
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Failed to establish or maintain the subprocess/stdio channel.
|
|
39
|
+
* Examples: `spawn` ENOENT, `spawn` permission denied, write to
|
|
40
|
+
* already-closed stdin during a transport-level shutdown.
|
|
41
|
+
*/
|
|
42
|
+
export declare class TransportConnectionError extends TransportError {
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The subprocess exited unexpectedly — either before the protocol
|
|
46
|
+
* reached its terminal state, or with a non-zero exit code that the
|
|
47
|
+
* transport considers an error condition.
|
|
48
|
+
*
|
|
49
|
+
* `exitCode` is `null` when the child was killed by signal without an
|
|
50
|
+
* explicit exit code; `signal` carries the POSIX signal name in that
|
|
51
|
+
* case (e.g. `"SIGKILL"`). `stderr` is best-effort — the transport
|
|
52
|
+
* captures the tail of the child's stderr if it was piped.
|
|
53
|
+
*/
|
|
54
|
+
export declare class TransportProcessError extends TransportError {
|
|
55
|
+
readonly exitCode: number | null;
|
|
56
|
+
readonly signal: string | null;
|
|
57
|
+
readonly stderr?: string | undefined;
|
|
58
|
+
constructor(message: string, exitCode: number | null, signal?: string | null, stderr?: string | undefined);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* A line read off the wire could not be JSON-parsed. The raw `line`
|
|
62
|
+
* is preserved (truncated to a sane length by the caller before
|
|
63
|
+
* construction if it could be very large) so investigators can see
|
|
64
|
+
* exactly what the transport rejected. `cause` is the original
|
|
65
|
+
* `SyntaxError` from `JSON.parse`.
|
|
66
|
+
*/
|
|
67
|
+
export declare class TransportJsonError extends TransportError {
|
|
68
|
+
readonly line: string;
|
|
69
|
+
constructor(message: string, line: string, cause: unknown);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* The peer responded with a JSON-RPC error object. `rpcCode` is the
|
|
73
|
+
* numeric code from the JSON-RPC `error.code` field when present, and
|
|
74
|
+
* `rpcData` carries the entire `error` payload (including `message`,
|
|
75
|
+
* `data`, and any provider-specific fields like
|
|
76
|
+
* codex-plugin-cc's broker-busy sentinel `-32001`).
|
|
77
|
+
*
|
|
78
|
+
* Callers who care about specific codes branch on `err.rpcCode`;
|
|
79
|
+
* generic catchers see a plain `Error` with a human-readable message.
|
|
80
|
+
*/
|
|
81
|
+
export declare class TransportRpcError extends TransportError {
|
|
82
|
+
readonly rpcCode?: number;
|
|
83
|
+
readonly rpcData?: unknown;
|
|
84
|
+
constructor(message: string, rpcCode?: number, rpcData?: unknown);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Convenience constructor that mirrors the prior `createRpcError`
|
|
88
|
+
* helper signature in `codex-app-server.ts`: pass the human message
|
|
89
|
+
* plus the raw JSON-RPC error object, and the helper extracts
|
|
90
|
+
* `code` (if numeric) and stores the whole object as `rpcData`.
|
|
91
|
+
*
|
|
92
|
+
* This is the migration target for `createRpcError` callers.
|
|
93
|
+
*/
|
|
94
|
+
export declare function transportRpcErrorFromResponse(message: string, errorObj?: unknown): TransportRpcError;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Graceful-shutdown ladder for transport child processes.
|
|
3
|
+
*
|
|
4
|
+
* Replaces the prior `child.kill()` (synchronous, hard SIGTERM, no
|
|
5
|
+
* grace) pattern across the claude / codex / ACP transports. The bug
|
|
6
|
+
* this fixes is documented in the claude-agent-sdk-python transport
|
|
7
|
+
* (`subprocess_cli.py:548-568` and the linked issue #625): when
|
|
8
|
+
* `claude` is killed mid-flush its session JSONL loses the last
|
|
9
|
+
* assistant message. The same risk class applies to any provider CLI
|
|
10
|
+
* that persists state on shutdown — codex (`thread/*.jsonl`),
|
|
11
|
+
* opencode (`opencode.db`), gemini (`~/.gemini/tmp/.../chats/`).
|
|
12
|
+
*
|
|
13
|
+
* Ladder:
|
|
14
|
+
* 1. (optional) close stdin so the child observes EOF.
|
|
15
|
+
* 2. wait `softDelayMs` for the child to exit on its own.
|
|
16
|
+
* 3. send SIGTERM (or `taskkill /T /F /PID <pid>` on Windows) and
|
|
17
|
+
* wait `killDelayMs`.
|
|
18
|
+
* 4. send SIGKILL (no-op on Windows, taskkill is already hard).
|
|
19
|
+
* 5. wait up to a fixed final grace for the exit event before
|
|
20
|
+
* returning.
|
|
21
|
+
*
|
|
22
|
+
* Returns a `TerminateResult` describing which path actually ended
|
|
23
|
+
* the child and total elapsed wall-clock — useful for tests and for
|
|
24
|
+
* future telemetry on shutdown latency.
|
|
25
|
+
*/
|
|
26
|
+
import type { ChildProcess } from "node:child_process";
|
|
27
|
+
export interface TerminateOptions {
|
|
28
|
+
/** Call `child.stdin?.end()` first to signal clean EOF. Default: true. */
|
|
29
|
+
stdinEnd?: boolean;
|
|
30
|
+
/** Wait ms for child to exit naturally after stdin.end. Default: 5000. */
|
|
31
|
+
softDelayMs?: number;
|
|
32
|
+
/** Wait ms after SIGTERM before SIGKILL. Default: 5000. */
|
|
33
|
+
killDelayMs?: number;
|
|
34
|
+
/**
|
|
35
|
+
* Test seam — replace the Windows process-tree killer
|
|
36
|
+
* (`taskkill /T /F /PID <pid>`) with a no-op or recorder. Defaults
|
|
37
|
+
* to a real `spawnSync("taskkill", ...)` call on Windows; ignored
|
|
38
|
+
* on Unix.
|
|
39
|
+
*/
|
|
40
|
+
runTaskkill?: (pid: number) => void;
|
|
41
|
+
/** Test seam — override `process.platform`. */
|
|
42
|
+
platform?: NodeJS.Platform;
|
|
43
|
+
}
|
|
44
|
+
/** Which step in the ladder actually finished the child. */
|
|
45
|
+
export type TerminateSignal = "natural" | "stdin" | "SIGTERM" | "SIGKILL" | "abandoned";
|
|
46
|
+
export interface TerminateResult {
|
|
47
|
+
signal: TerminateSignal;
|
|
48
|
+
elapsedMs: number;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Gracefully terminate a transport child process.
|
|
52
|
+
*
|
|
53
|
+
* Safe to call on an already-exited child (returns immediately with
|
|
54
|
+
* `signal: "natural"`). Safe to call multiple times concurrently
|
|
55
|
+
* — each call observes the same exit event independently.
|
|
56
|
+
*
|
|
57
|
+
* Errors are swallowed deliberately: this function is meant to be
|
|
58
|
+
* called from cleanup paths (abort handlers, IIFE catch blocks,
|
|
59
|
+
* timeout firings) where re-throwing would mask the original
|
|
60
|
+
* failure. Failures during termination are reflected in the returned
|
|
61
|
+
* `TerminateResult.signal` (`"abandoned"` is the only failure shape).
|
|
62
|
+
*/
|
|
63
|
+
export declare function terminateGracefully(child: ChildProcess, opts?: TerminateOptions): Promise<TerminateResult>;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transport wrappers for use by provider adapters.
|
|
3
|
+
* These wrap the existing transport modules, mapping their result types
|
|
4
|
+
* to the adapter's TurnResult.
|
|
5
|
+
*
|
|
6
|
+
* Each wrapper also classifies the transport's native event stream into
|
|
7
|
+
* normalized ProgressEvents (see adapter.ts) so that downstream consumers
|
|
8
|
+
* (idle timer, stderr printer) have a single format to deal with.
|
|
9
|
+
*/
|
|
10
|
+
import { type AcpMode } from "./transports/acp.js";
|
|
11
|
+
import type { ProgressEvent, TurnOptions, TurnResult } from "./adapter.js";
|
|
12
|
+
import type { BundleSession, BundleSessionOpenOptions } from "./bundle.js";
|
|
13
|
+
/**
|
|
14
|
+
* Translate one stream-json event into zero or more ProgressEvents.
|
|
15
|
+
* Stream-json uses Claude's native message format:
|
|
16
|
+
* - {"type":"system","subtype":"init",...}
|
|
17
|
+
* - {"type":"assistant","message":{"content":[{type:"text"|"tool_use"|"thinking"...}]}}
|
|
18
|
+
* - {"type":"user","message":{"content":[{type:"tool_result",...}]}}
|
|
19
|
+
* - {"type":"result",...}
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* Stateful factory for classifyStreamJsonEvent. The returned closure
|
|
23
|
+
* carries a per-turn id→startMs Map that lets it populate `elapsedMs`
|
|
24
|
+
* on tool_result events by correlating against the matching tool_use
|
|
25
|
+
* (Claude stream-json doesn't supply duration directly).
|
|
26
|
+
*
|
|
27
|
+
* Production callers (`streamJsonTurn`) instantiate ONCE per turn and
|
|
28
|
+
* reuse across all events. Unit tests that exercise a single event in
|
|
29
|
+
* isolation can keep using the bare `classifyStreamJsonEvent` free
|
|
30
|
+
* function below — they just won't get elapsedMs populated, which is
|
|
31
|
+
* fine because there's no matching pair to compute it from.
|
|
32
|
+
*/
|
|
33
|
+
export declare function makeStreamJsonClassifier(): (event: Record<string, unknown>, emit: (e: ProgressEvent) => void) => void;
|
|
34
|
+
export declare function classifyStreamJsonEvent(event: Record<string, unknown>, emit: (e: ProgressEvent) => void): void;
|
|
35
|
+
export declare function streamJsonTurn(prompt: string, opts: TurnOptions & {
|
|
36
|
+
binary: string;
|
|
37
|
+
}): Promise<TurnResult>;
|
|
38
|
+
/**
|
|
39
|
+
* Open a persistent stream-json bundle session (batch bundle mode)
|
|
40
|
+
* for the claude/qwen adapters' `openSession`. Bundle resume is strict:
|
|
41
|
+
* the session file must exist and its header must carry the requested id.
|
|
42
|
+
*/
|
|
43
|
+
export declare function openStreamJsonBundleSession(opts: BundleSessionOpenOptions & {
|
|
44
|
+
binary: string;
|
|
45
|
+
}): Promise<BundleSession>;
|
|
46
|
+
/**
|
|
47
|
+
* Classify ACP session/update notifications. ACP wraps everything in
|
|
48
|
+
* {"method":"session/update","params":{"update":{"sessionUpdate":"<kind>",...}}}
|
|
49
|
+
*
|
|
50
|
+
* Common sessionUpdate kinds (per the ACP spec used by Zed and friends):
|
|
51
|
+
* - agent_message_chunk → text (already streamed via onDelta)
|
|
52
|
+
* - agent_thought_chunk → thinking
|
|
53
|
+
* - tool_call → tool_use
|
|
54
|
+
* - tool_call_update → tool_result (completed/failed), other "running…" (in_progress)
|
|
55
|
+
* - plan → other
|
|
56
|
+
*/
|
|
57
|
+
/**
|
|
58
|
+
* Stateful factory for classifyAcpEvent. Same shape and rationale as
|
|
59
|
+
* makeStreamJsonClassifier — see that doc. Carries per-turn id→startMs
|
|
60
|
+
* for the tool_call → tool_call_update correlation that drives
|
|
61
|
+
* `elapsedMs` on tool_result events.
|
|
62
|
+
*/
|
|
63
|
+
export declare function makeAcpClassifier(): (event: Record<string, unknown>, emit: (e: ProgressEvent) => void) => void;
|
|
64
|
+
export declare function classifyAcpEvent(event: Record<string, unknown>, emit: (e: ProgressEvent) => void): void;
|
|
65
|
+
export declare function acpTurn(prompt: string, opts: TurnOptions & {
|
|
66
|
+
binary: string;
|
|
67
|
+
acpTool?: string;
|
|
68
|
+
acpMode?: AcpMode;
|
|
69
|
+
}): Promise<TurnResult>;
|
|
70
|
+
/**
|
|
71
|
+
* Open a persistent ACP bundle session (batch bundle mode) for a
|
|
72
|
+
* provider adapter's `openSession` implementation. `acpTool`/`acpMode`
|
|
73
|
+
* mirror `acpTurn`; the lifecycle contract is in
|
|
74
|
+
* `src/providers/bundle.ts` and the engine in `transports/acp.ts`'s
|
|
75
|
+
* `openAcpSession`.
|
|
76
|
+
*
|
|
77
|
+
* Bundle resume is ALWAYS strict: requireResume is forced and the loaded
|
|
78
|
+
* session id must equal the requested id exactly.
|
|
79
|
+
*/
|
|
80
|
+
export declare function openAcpBundleSession(opts: BundleSessionOpenOptions & {
|
|
81
|
+
binary: string;
|
|
82
|
+
acpTool?: string;
|
|
83
|
+
acpMode?: AcpMode;
|
|
84
|
+
}): Promise<BundleSession>;
|
|
85
|
+
/**
|
|
86
|
+
* Classify Codex app-server JSON-RPC notifications.
|
|
87
|
+
*
|
|
88
|
+
* Per codex-rs/app-server/README.md (Turn events / Items), the per-item
|
|
89
|
+
* lifecycle is always: `item/started` → zero or more item-specific deltas
|
|
90
|
+
* → `item/completed`. Items are a tagged union (`item.type`) including
|
|
91
|
+
* `commandExecution`, `fileChange`, `mcpToolCall`, `webSearch`, `reasoning`,
|
|
92
|
+
* `agentMessage`, etc.
|
|
93
|
+
*
|
|
94
|
+
* Item-specific deltas we care about:
|
|
95
|
+
* - item/agentMessage/delta → text (already streamed via onDelta)
|
|
96
|
+
* - item/reasoning/summaryTextDelta → thinking
|
|
97
|
+
* - item/reasoning/textDelta → thinking
|
|
98
|
+
* - item/commandExecution/outputDelta → text (suppressed; lifecycle covers it)
|
|
99
|
+
*/
|
|
100
|
+
export declare function classifyCodexEvent(event: Record<string, unknown>, emit: (e: ProgressEvent) => void): void;
|
|
101
|
+
export declare function codexAppServerTurn(prompt: string, opts: TurnOptions & {
|
|
102
|
+
binary: string;
|
|
103
|
+
}): Promise<TurnResult>;
|
|
104
|
+
/**
|
|
105
|
+
* Open a persistent codex app-server bundle session (batch bundle
|
|
106
|
+
* mode) for the codex adapter's `openSession`. Bundle resume is
|
|
107
|
+
* strict: thread/resume must succeed and return the exact requested
|
|
108
|
+
* thread id; the thread/start fallback is disabled in bundle mode.
|
|
109
|
+
*/
|
|
110
|
+
export declare function openCodexBundleSession(opts: BundleSessionOpenOptions): Promise<BundleSession>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared `session/set_config_option` step factory for the profiles whose
|
|
3
|
+
* per-session model/effort surface is an ACP config option (opencode, kimi).
|
|
4
|
+
*
|
|
5
|
+
* The failure semantics are NON-FATAL by contract (task #232): the turn
|
|
6
|
+
* proceeds with whatever config was already active, and the drop is
|
|
7
|
+
* recorded on all three channels — stderr line, `acp_config_option_failed`
|
|
8
|
+
* errorLog record, and a warnings-channel entry that lands on
|
|
9
|
+
* `AcpResult.warnings`.
|
|
10
|
+
*
|
|
11
|
+
* "_failed", not "_rejected": the driver's catch wraps `sendRpc`, so it
|
|
12
|
+
* also fires on transport disconnect, not only on the provider rejecting
|
|
13
|
+
* the value. Naming it "rejected" would conflate provider-validation noise
|
|
14
|
+
* with channel failure — exactly the misreading these counts exist to
|
|
15
|
+
* prevent.
|
|
16
|
+
*/
|
|
17
|
+
import type { AcpSessionContext, AcpSessionStep } from "./types.js";
|
|
18
|
+
export declare function setConfigOptionStep(opts: {
|
|
19
|
+
id: string;
|
|
20
|
+
provider: string;
|
|
21
|
+
configId: string;
|
|
22
|
+
/** The requested value; undefined ⇒ the step does not apply. */
|
|
23
|
+
value: (ctx: AcpSessionContext) => string | undefined;
|
|
24
|
+
}): AcpSessionStep;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copilot ACP profile.
|
|
3
|
+
*
|
|
4
|
+
* Copilot's `--acp` mode has no plan-mode flag and no per-session model
|
|
5
|
+
* selection surface (verified against copilot-cli 1.0.35). Both gaps are
|
|
6
|
+
* surfaced once per spawn — a stderr line the user sees plus an
|
|
7
|
+
* unconditional errorLog record fleet tooling can count — and the turn
|
|
8
|
+
* proceeds on the yolo/default surface rather than hanging on permission
|
|
9
|
+
* prompts it cannot answer.
|
|
10
|
+
*/
|
|
11
|
+
import type { AcpProviderProfile } from "./types.js";
|
|
12
|
+
export declare const copilotProfile: AcpProviderProfile;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gemini ACP profile.
|
|
3
|
+
*
|
|
4
|
+
* Permission posture is argv-based: `-y` (yolo) works before or after
|
|
5
|
+
* `--acp`, and the companion `--skip-trust` is passed unconditionally —
|
|
6
|
+
* gemini's workspace-trust gate (~/.gemini/trustedFolders.json) downgrades
|
|
7
|
+
* approval mode to "default" in untrusted folders, silently disarming
|
|
8
|
+
* `-y`; `--skip-trust` is the per-invocation escape hatch (no-op in
|
|
9
|
+
* already-trusted folders). Plan mode swaps the `-y` token for
|
|
10
|
+
* `--approval-mode plan`, keeping companions intact.
|
|
11
|
+
*
|
|
12
|
+
* Caveat: gemini's `--approval-mode plan` only filters `write_file` by
|
|
13
|
+
* workspace path — the agent can still escape via `run_shell_command`
|
|
14
|
+
* (documented in yolo-contract.ts; a tighter sandbox is a follow-up).
|
|
15
|
+
*
|
|
16
|
+
* Gemini replays prior assistant chunks during `session/load` and
|
|
17
|
+
* concatenates earlier image turns into the reply text; keeping only the
|
|
18
|
+
* latest agent message is the output processor's job (parsers/acp.ts),
|
|
19
|
+
* wired in via `isolateResult`.
|
|
20
|
+
*/
|
|
21
|
+
import type { AcpProviderProfile } from "./types.js";
|
|
22
|
+
export declare const geminiProfile: AcpProviderProfile;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hermes (Hermes Agent) ACP profile.
|
|
3
|
+
*
|
|
4
|
+
* `hermes acp` IS the ACP stdio server (same launcher as the `hermes-acp`
|
|
5
|
+
* console script); it takes no permission flag. The posture is two-part:
|
|
6
|
+
*
|
|
7
|
+
* 1. Shell gate: `HERMES_YOLO_MODE=1` on the process env (yolo mode only;
|
|
8
|
+
* hermes REFUSES plan mode outright).
|
|
9
|
+
* 2. Edit gate: ACP `session/set_mode` — Hermes's mode ladder
|
|
10
|
+
* (`HermesACPAgent._MODES`, `acp_adapter/server.py:233-243`) governs
|
|
11
|
+
* the file-EDIT approval gate, which `HERMES_YOLO_MODE` does NOT
|
|
12
|
+
* approve. An unset mode leaves every edit waiting on a
|
|
13
|
+
* `session/request_permission` round-trip lmctl cannot answer, so the
|
|
14
|
+
* mode is applied after session/new|load, before the model, on EVERY
|
|
15
|
+
* spawn (the mode lives on the in-memory session), FAIL-CLOSED.
|
|
16
|
+
*
|
|
17
|
+
* Model selection is `session/set_model` (also fail-closed), NOT
|
|
18
|
+
* `session/set_config_option`: hermes's config-option handler stores an
|
|
19
|
+
* unrecognized configId verbatim and applies nothing
|
|
20
|
+
* (`acp_adapter/server.py:958-977`). Its model ids are the ENCODED
|
|
21
|
+
* `provider:model` form it advertises in session/new
|
|
22
|
+
* (`acp_adapter/model_catalog.py:146-152`) — a bare model name resolves
|
|
23
|
+
* the wrong provider. An unchanged model is not re-set (avoids rebuilding
|
|
24
|
+
* the Hermes agent).
|
|
25
|
+
*/
|
|
26
|
+
import type { AcpProviderProfile } from "./types.js";
|
|
27
|
+
export declare const hermesProfile: AcpProviderProfile;
|