@botcord/daemon 0.2.4 → 0.2.6

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 (93) hide show
  1. package/dist/agent-discovery.d.ts +7 -3
  2. package/dist/agent-discovery.js +9 -1
  3. package/dist/agent-workspace.d.ts +62 -0
  4. package/dist/agent-workspace.js +140 -10
  5. package/dist/config.d.ts +49 -1
  6. package/dist/config.js +57 -1
  7. package/dist/control-channel.d.ts +1 -4
  8. package/dist/control-channel.js +1 -4
  9. package/dist/daemon-config-map.d.ts +29 -12
  10. package/dist/daemon-config-map.js +105 -8
  11. package/dist/daemon.d.ts +2 -0
  12. package/dist/daemon.js +52 -5
  13. package/dist/doctor.d.ts +27 -1
  14. package/dist/doctor.js +22 -1
  15. package/dist/gateway/cli-resolver.d.ts +34 -0
  16. package/dist/gateway/cli-resolver.js +74 -0
  17. package/dist/gateway/dispatcher.d.ts +66 -1
  18. package/dist/gateway/dispatcher.js +583 -56
  19. package/dist/gateway/gateway.d.ts +29 -1
  20. package/dist/gateway/gateway.js +10 -0
  21. package/dist/gateway/index.d.ts +2 -0
  22. package/dist/gateway/index.js +2 -0
  23. package/dist/gateway/policy-resolver.d.ts +57 -0
  24. package/dist/gateway/policy-resolver.js +123 -0
  25. package/dist/gateway/runtimes/acp-stream.d.ts +99 -0
  26. package/dist/gateway/runtimes/acp-stream.js +394 -0
  27. package/dist/gateway/runtimes/codex.js +7 -0
  28. package/dist/gateway/runtimes/hermes-agent.d.ts +83 -0
  29. package/dist/gateway/runtimes/hermes-agent.js +180 -0
  30. package/dist/gateway/runtimes/ndjson-stream.d.ts +7 -2
  31. package/dist/gateway/runtimes/ndjson-stream.js +16 -3
  32. package/dist/gateway/runtimes/openclaw-acp.d.ts +44 -0
  33. package/dist/gateway/runtimes/openclaw-acp.js +500 -0
  34. package/dist/gateway/runtimes/registry.d.ts +4 -0
  35. package/dist/gateway/runtimes/registry.js +22 -0
  36. package/dist/gateway/transcript-paths.d.ts +30 -0
  37. package/dist/gateway/transcript-paths.js +114 -0
  38. package/dist/gateway/transcript.d.ts +123 -0
  39. package/dist/gateway/transcript.js +147 -0
  40. package/dist/gateway/types.d.ts +31 -0
  41. package/dist/index.js +286 -27
  42. package/dist/mention-scan.d.ts +22 -0
  43. package/dist/mention-scan.js +35 -0
  44. package/dist/provision.d.ts +73 -3
  45. package/dist/provision.js +373 -12
  46. package/dist/system-context.d.ts +5 -4
  47. package/dist/system-context.js +35 -5
  48. package/dist/turn-text.js +20 -1
  49. package/dist/url-utils.d.ts +9 -0
  50. package/dist/url-utils.js +18 -0
  51. package/dist/user-auth.js +0 -2
  52. package/dist/working-memory.js +1 -1
  53. package/package.json +2 -1
  54. package/src/__tests__/agent-workspace.test.ts +93 -0
  55. package/src/__tests__/daemon-config-map.test.ts +79 -0
  56. package/src/__tests__/openclaw-acp.test.ts +234 -0
  57. package/src/__tests__/policy-resolver.test.ts +124 -0
  58. package/src/__tests__/policy-updated-handler.test.ts +144 -0
  59. package/src/__tests__/provision.test.ts +160 -0
  60. package/src/__tests__/system-context.test.ts +52 -0
  61. package/src/__tests__/url-utils.test.ts +37 -0
  62. package/src/agent-discovery.ts +12 -4
  63. package/src/agent-workspace.ts +173 -9
  64. package/src/config.ts +132 -4
  65. package/src/control-channel.ts +1 -4
  66. package/src/daemon-config-map.ts +156 -12
  67. package/src/daemon.ts +66 -5
  68. package/src/doctor.ts +49 -2
  69. package/src/gateway/__tests__/dispatcher.test.ts +440 -2
  70. package/src/gateway/__tests__/hermes-agent-adapter.test.ts +302 -0
  71. package/src/gateway/__tests__/transcript.test.ts +496 -0
  72. package/src/gateway/cli-resolver.ts +92 -0
  73. package/src/gateway/dispatcher.ts +681 -58
  74. package/src/gateway/gateway.ts +46 -0
  75. package/src/gateway/index.ts +25 -0
  76. package/src/gateway/policy-resolver.ts +171 -0
  77. package/src/gateway/runtimes/acp-stream.ts +535 -0
  78. package/src/gateway/runtimes/codex.ts +7 -0
  79. package/src/gateway/runtimes/hermes-agent.ts +206 -0
  80. package/src/gateway/runtimes/ndjson-stream.ts +16 -3
  81. package/src/gateway/runtimes/openclaw-acp.ts +606 -0
  82. package/src/gateway/runtimes/registry.ts +24 -0
  83. package/src/gateway/transcript-paths.ts +145 -0
  84. package/src/gateway/transcript.ts +300 -0
  85. package/src/gateway/types.ts +32 -0
  86. package/src/index.ts +295 -30
  87. package/src/mention-scan.ts +38 -0
  88. package/src/provision.ts +446 -20
  89. package/src/system-context.ts +41 -9
  90. package/src/turn-text.ts +22 -1
  91. package/src/url-utils.ts +17 -0
  92. package/src/user-auth.ts +0 -2
  93. package/src/working-memory.ts +1 -1
@@ -1,7 +1,8 @@
1
1
  import { type ChannelBackoffOptions } from "./channel-manager.js";
2
2
  import { type RuntimeFactory } from "./dispatcher.js";
3
3
  import { type GatewayLogger } from "./log.js";
4
- import type { ChannelAdapter, GatewayChannelConfig, GatewayConfig, GatewayRoute, GatewayRuntimeSnapshot, InboundObserver, OutboundObserver, SystemContextBuilder, UserTurnBuilder } from "./types.js";
4
+ import { type TranscriptWriter } from "./transcript.js";
5
+ import type { ChannelAdapter, GatewayChannelConfig, GatewayConfig, GatewayInboundMessage, GatewayRoute, GatewayRuntimeSnapshot, InboundObserver, OutboundObserver, SystemContextBuilder, UserTurnBuilder } from "./types.js";
5
6
  /** Constructor options for `Gateway`. */
6
7
  export interface GatewayBootOptions {
7
8
  config: GatewayConfig;
@@ -35,6 +36,33 @@ export interface GatewayBootOptions {
35
36
  * bookkeeping like loop-risk tracking.
36
37
  */
37
38
  onOutbound?: OutboundObserver;
39
+ /**
40
+ * Optional attention gate (PR3, design §4.2). Forwarded to the dispatcher
41
+ * verbatim — see {@link Dispatcher} for semantics. Returning `false` skips
42
+ * the runtime turn while preserving ack + onInbound side effects.
43
+ */
44
+ attentionGate?: (message: GatewayInboundMessage) => Promise<boolean> | boolean;
45
+ /**
46
+ * Resolve the per-agent hub URL for an inbound message. Forwarded to the
47
+ * dispatcher as `RuntimeRunOptions.hubUrl` so spawned CLI subprocesses
48
+ * (`BOTCORD_HUB`) target the correct hub for the owning agent.
49
+ */
50
+ resolveHubUrl?: (accountId: string) => string | undefined;
51
+ /**
52
+ * Persistent NDJSON transcript writer (design §3 / §6). Optional — when
53
+ * omitted the dispatcher uses a noop writer. Pass `transcriptEnabled` plus
54
+ * `transcriptRootDir` to let the gateway construct one for you.
55
+ */
56
+ transcript?: TranscriptWriter;
57
+ /**
58
+ * Tri-state convenience: if `transcript` is not provided, the gateway
59
+ * constructs a writer using this flag plus `transcriptRootDir`. Use
60
+ * {@link resolveTranscriptEnabled} to combine `BOTCORD_TRANSCRIPT` env with
61
+ * the persistent daemon-config flag.
62
+ */
63
+ transcriptEnabled?: boolean;
64
+ /** Root directory for transcript files. Defaults to `~/.botcord/agents`. */
65
+ transcriptRootDir?: string;
38
66
  }
39
67
  /**
40
68
  * Top-level gateway bootstrap. Wires `ChannelManager` → `Dispatcher` →
@@ -3,6 +3,7 @@ import { Dispatcher } from "./dispatcher.js";
3
3
  import { consoleLogger } from "./log.js";
4
4
  import { createRuntime } from "./runtimes/registry.js";
5
5
  import { DEFAULT_SESSION_STORE_MAX_ENTRY_AGE_MS, SessionStore } from "./session-store.js";
6
+ import { createTranscriptWriter, } from "./transcript.js";
6
7
  /** Default runtime factory: delegates to the built-in registry; ignores extraArgs at construction. */
7
8
  const defaultRuntimeFactory = (runtimeId) => createRuntime(runtimeId);
8
9
  /**
@@ -53,6 +54,12 @@ export class Gateway {
53
54
  maxEntryAgeMs: opts.sessionStoreMaxEntryAgeMs ?? DEFAULT_SESSION_STORE_MAX_ENTRY_AGE_MS,
54
55
  });
55
56
  const runtimeFactory = opts.createRuntime ?? defaultRuntimeFactory;
57
+ const transcript = opts.transcript
58
+ ?? createTranscriptWriter({
59
+ log: this.log,
60
+ enabled: opts.transcriptEnabled === true,
61
+ rootDir: opts.transcriptRootDir,
62
+ });
56
63
  this.dispatcher = new Dispatcher({
57
64
  config: this.config,
58
65
  channels: this.channelMap,
@@ -65,6 +72,9 @@ export class Gateway {
65
72
  composeUserTurn: opts.composeUserTurn,
66
73
  onOutbound: opts.onOutbound,
67
74
  managedRoutes: this.managedRoutes,
75
+ attentionGate: opts.attentionGate,
76
+ resolveHubUrl: opts.resolveHubUrl,
77
+ transcript,
68
78
  });
69
79
  this.channelManager = new ChannelManager({
70
80
  config: this.config,
@@ -8,6 +8,8 @@ export { resolveRoute, matchesRoute } from "./router.js";
8
8
  export { ChannelManager, type ChannelManagerOptions, type ChannelBackoffOptions } from "./channel-manager.js";
9
9
  export { Dispatcher, type DispatcherOptions, type RuntimeFactory } from "./dispatcher.js";
10
10
  export { Gateway, type GatewayBootOptions } from "./gateway.js";
11
+ export { createTranscriptWriter, resolveTranscriptEnabled, defaultTranscriptRoot, truncateTextField, TRANSCRIPT_TEXT_LIMIT, TRANSCRIPT_FILE_LIMIT, type TranscriptWriter, type TranscriptRecord, type InboundTranscriptRecord, type DispatchedTranscriptRecord, type ComposeFailedTranscriptRecord, type OutboundTranscriptRecord, type TurnErrorTranscriptRecord, type AttentionSkippedTranscriptRecord, type DroppedTranscriptRecord, type DeliveryStatus, type DroppedReason, } from "./transcript.js";
12
+ export { safePathSegment, transcriptFilePath, transcriptRoomDir, transcriptAgentRoot, } from "./transcript-paths.js";
11
13
  export { ClaudeCodeAdapter, probeClaude, resolveClaudeCommand, } from "./runtimes/claude-code.js";
12
14
  export { CodexAdapter, probeCodex, resolveCodexCommand } from "./runtimes/codex.js";
13
15
  export { GeminiAdapter, probeGemini, resolveGeminiCommand } from "./runtimes/gemini.js";
@@ -8,6 +8,8 @@ export { resolveRoute, matchesRoute } from "./router.js";
8
8
  export { ChannelManager } from "./channel-manager.js";
9
9
  export { Dispatcher } from "./dispatcher.js";
10
10
  export { Gateway } from "./gateway.js";
11
+ export { createTranscriptWriter, resolveTranscriptEnabled, defaultTranscriptRoot, truncateTextField, TRANSCRIPT_TEXT_LIMIT, TRANSCRIPT_FILE_LIMIT, } from "./transcript.js";
12
+ export { safePathSegment, transcriptFilePath, transcriptRoomDir, transcriptAgentRoot, } from "./transcript-paths.js";
11
13
  export { ClaudeCodeAdapter, probeClaude, resolveClaudeCommand, } from "./runtimes/claude-code.js";
12
14
  export { CodexAdapter, probeCodex, resolveCodexCommand } from "./runtimes/codex.js";
13
15
  export { GeminiAdapter, probeGemini, resolveGeminiCommand } from "./runtimes/gemini.js";
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Daemon-side per-agent attention-policy cache (PR3, design §5).
3
+ *
4
+ * The dispatcher consults this resolver after `onInbound` fires and before
5
+ * the runtime turn enqueues. Cache layout:
6
+ *
7
+ * - `agent_id` → global default policy (seeded by
8
+ * `provision_agent` + `policy_updated{agent}`).
9
+ * - `agent_id:room_id` → genuine per-room override only — installed
10
+ * exclusively via `put` from a per-room
11
+ * `policy_updated` frame. Inheritance reads
12
+ * never write here.
13
+ *
14
+ * `resolve(agent, room)` checks the room key first, then falls back to the
15
+ * global key. This means a per-room override always wins, and the global
16
+ * propagates to every room without explicit fan-out (a global update only
17
+ * needs to refresh the agent_id entry).
18
+ *
19
+ * `invalidate(agent_id, room_id)` drops the matching room entry; the next
20
+ * resolve falls through to the global. `invalidate(agent_id)` drops every
21
+ * entry for that agent — both global and any room overrides — used when
22
+ * the agent is revoked or the cache must rebuild from scratch.
23
+ */
24
+ import type { AttentionPolicy } from "@botcord/protocol-core";
25
+ /** Public surface — kept narrow so the dispatcher can mock easily in tests. */
26
+ export interface PolicyResolverLike {
27
+ resolve(agentId: string, roomId: string | null): Promise<AttentionPolicy>;
28
+ invalidate(agentId: string, roomId?: string): void;
29
+ /**
30
+ * Install (or replace) the cached policy entry for an agent / room. Used
31
+ * by the `policy_updated` control-frame handler to apply embedded policy
32
+ * payloads without forcing a refetch.
33
+ */
34
+ put(agentId: string, roomId: string | null, policy: AttentionPolicy): void;
35
+ }
36
+ export interface PolicyResolverOptions {
37
+ /** Fetcher for the per-agent default. Returning `undefined` means "no policy known"; the resolver falls back to `mode=always`. */
38
+ fetchGlobal: (agentId: string) => Promise<AttentionPolicy | undefined>;
39
+ /**
40
+ * Optional per-room fetcher. PR2 supplies this; PR3 leaves it
41
+ * unimplemented and the resolver collapses to the global policy.
42
+ */
43
+ fetchEffective?: (agentId: string, roomId: string) => Promise<AttentionPolicy | undefined>;
44
+ /** Cache TTL in milliseconds. Defaults to 5 minutes. */
45
+ ttlMs?: number;
46
+ }
47
+ export declare class PolicyResolver implements PolicyResolverLike {
48
+ private readonly fetchGlobal;
49
+ private readonly fetchEffective?;
50
+ private readonly ttlMs;
51
+ private readonly cache;
52
+ constructor(opts: PolicyResolverOptions);
53
+ resolve(agentId: string, roomId: string | null): Promise<AttentionPolicy>;
54
+ private safeFetch;
55
+ invalidate(agentId: string, roomId?: string): void;
56
+ put(agentId: string, roomId: string | null, policy: AttentionPolicy): void;
57
+ }
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Daemon-side per-agent attention-policy cache (PR3, design §5).
3
+ *
4
+ * The dispatcher consults this resolver after `onInbound` fires and before
5
+ * the runtime turn enqueues. Cache layout:
6
+ *
7
+ * - `agent_id` → global default policy (seeded by
8
+ * `provision_agent` + `policy_updated{agent}`).
9
+ * - `agent_id:room_id` → genuine per-room override only — installed
10
+ * exclusively via `put` from a per-room
11
+ * `policy_updated` frame. Inheritance reads
12
+ * never write here.
13
+ *
14
+ * `resolve(agent, room)` checks the room key first, then falls back to the
15
+ * global key. This means a per-room override always wins, and the global
16
+ * propagates to every room without explicit fan-out (a global update only
17
+ * needs to refresh the agent_id entry).
18
+ *
19
+ * `invalidate(agent_id, room_id)` drops the matching room entry; the next
20
+ * resolve falls through to the global. `invalidate(agent_id)` drops every
21
+ * entry for that agent — both global and any room overrides — used when
22
+ * the agent is revoked or the cache must rebuild from scratch.
23
+ */
24
+ const DEFAULT_TTL_MS = 5 * 60 * 1000;
25
+ const FETCH_FAILED = Symbol("fetch_failed");
26
+ /**
27
+ * Force DM rooms (`rm_dm_*`) to `mode: "always"` per design §4.2 — UI never
28
+ * lets the user mute a DM, but a stale cache from before a UX bug is cheap
29
+ * to defend against here.
30
+ */
31
+ function maybeForceDm(roomId, policy) {
32
+ if (roomId && roomId.startsWith("rm_dm_") && policy.mode !== "always") {
33
+ return { ...policy, mode: "always" };
34
+ }
35
+ return policy;
36
+ }
37
+ function defaultPolicy() {
38
+ return { mode: "always", keywords: [] };
39
+ }
40
+ export class PolicyResolver {
41
+ fetchGlobal;
42
+ fetchEffective;
43
+ ttlMs;
44
+ cache = new Map();
45
+ constructor(opts) {
46
+ this.fetchGlobal = opts.fetchGlobal;
47
+ this.fetchEffective = opts.fetchEffective;
48
+ this.ttlMs = opts.ttlMs ?? DEFAULT_TTL_MS;
49
+ }
50
+ async resolve(agentId, roomId) {
51
+ const now = Date.now();
52
+ // 1. Per-room cache — populated either by a `policy_updated{room_id}`
53
+ // push (genuine override) or by a prior `fetchEffective` cold-start.
54
+ if (roomId) {
55
+ const roomHit = this.cache.get(cacheKey(agentId, roomId));
56
+ if (roomHit && roomHit.expiresAt > now)
57
+ return roomHit.policy;
58
+ }
59
+ // 2. If a per-room fetcher is wired, treat it as authoritative for cold
60
+ // rooms — it returns the override-merged effective policy and so must
61
+ // not be skipped just because the global cache is warm.
62
+ if (roomId && this.fetchEffective) {
63
+ const fetched = await this.safeFetch(() => this.fetchEffective(agentId, roomId));
64
+ if (fetched === FETCH_FAILED)
65
+ return defaultPolicy();
66
+ const policy = fetched ?? defaultPolicy();
67
+ this.cache.set(cacheKey(agentId, roomId), {
68
+ policy: maybeForceDm(roomId, policy),
69
+ expiresAt: now + this.ttlMs,
70
+ });
71
+ return maybeForceDm(roomId, policy);
72
+ }
73
+ // 3. No room override known — inherit from the cached agent-wide global.
74
+ // Without this layer, group messages collapsed to mode=always whenever
75
+ // the daemon ran without a per-room fetcher (the current production
76
+ // state), silently breaking global mention_only/muted.
77
+ const globalKey = cacheKey(agentId, null);
78
+ const globalHit = this.cache.get(globalKey);
79
+ if (globalHit && globalHit.expiresAt > now) {
80
+ return maybeForceDm(roomId, globalHit.policy);
81
+ }
82
+ // 4. Cold start for global.
83
+ const fetched = await this.safeFetch(() => this.fetchGlobal(agentId));
84
+ if (fetched === FETCH_FAILED)
85
+ return defaultPolicy();
86
+ const policy = fetched ?? defaultPolicy();
87
+ this.cache.set(globalKey, { policy, expiresAt: now + this.ttlMs });
88
+ return maybeForceDm(roomId, policy);
89
+ }
90
+ async safeFetch(fn) {
91
+ try {
92
+ return await fn();
93
+ }
94
+ catch {
95
+ // Fail-open: a fetch error must not silence the agent. The caller
96
+ // returns the default policy without caching so the next resolve retries.
97
+ return FETCH_FAILED;
98
+ }
99
+ }
100
+ invalidate(agentId, roomId) {
101
+ if (roomId !== undefined) {
102
+ this.cache.delete(cacheKey(agentId, roomId));
103
+ return;
104
+ }
105
+ // Drop every entry for this agent.
106
+ const prefix = agentId + ":";
107
+ for (const key of Array.from(this.cache.keys())) {
108
+ if (key === agentId || key.startsWith(prefix)) {
109
+ this.cache.delete(key);
110
+ }
111
+ }
112
+ }
113
+ put(agentId, roomId, policy) {
114
+ const key = cacheKey(agentId, roomId);
115
+ this.cache.set(key, {
116
+ policy: maybeForceDm(roomId, policy),
117
+ expiresAt: Date.now() + this.ttlMs,
118
+ });
119
+ }
120
+ }
121
+ function cacheKey(agentId, roomId) {
122
+ return roomId ? `${agentId}:${roomId}` : agentId;
123
+ }
@@ -0,0 +1,99 @@
1
+ import type { RuntimeAdapter, RuntimeProbeResult, RuntimeRunOptions, RuntimeRunResult, StreamBlock } from "../types.js";
2
+ /** ACP protocol version this client targets. */
3
+ export declare const ACP_PROTOCOL_VERSION = 1;
4
+ export interface AcpInitializeResult {
5
+ protocolVersion?: number;
6
+ agentInfo?: {
7
+ name?: string;
8
+ version?: string;
9
+ };
10
+ agentCapabilities?: Record<string, unknown>;
11
+ authMethods?: Array<{
12
+ id?: string;
13
+ name?: string;
14
+ description?: string;
15
+ }>;
16
+ [k: string]: unknown;
17
+ }
18
+ export interface AcpPermissionOption {
19
+ optionId: string;
20
+ name?: string;
21
+ /**
22
+ * ACP option kind. Common values: `allow_once`, `allow_always`,
23
+ * `reject_once`, `reject_always`. Treated as opaque by the base class —
24
+ * subclasses inspect `.kind` to pick the right outcome.
25
+ */
26
+ kind?: string;
27
+ [k: string]: unknown;
28
+ }
29
+ export interface AcpPermissionRequest {
30
+ sessionId: string;
31
+ toolCall?: {
32
+ name?: string;
33
+ rawInput?: unknown;
34
+ [k: string]: unknown;
35
+ };
36
+ options: AcpPermissionOption[];
37
+ [k: string]: unknown;
38
+ }
39
+ export type AcpPermissionResponse = {
40
+ outcome: {
41
+ outcome: "selected";
42
+ optionId: string;
43
+ };
44
+ } | {
45
+ outcome: {
46
+ outcome: "cancelled";
47
+ };
48
+ };
49
+ export interface AcpUpdateParams {
50
+ sessionId: string;
51
+ update: {
52
+ sessionUpdate?: string;
53
+ [k: string]: unknown;
54
+ };
55
+ [k: string]: unknown;
56
+ }
57
+ /** Hooks exposed to subclasses to react to inbound traffic during a turn. */
58
+ export interface AcpTurnHooks {
59
+ /** Called for each `session/update` notification. */
60
+ onUpdate(params: AcpUpdateParams, ctx: AcpUpdateCtx): void;
61
+ /** Called for `session/request_permission` requests. Must resolve to an outcome. */
62
+ onPermissionRequest(req: AcpPermissionRequest): Promise<AcpPermissionResponse>;
63
+ }
64
+ export interface AcpUpdateCtx {
65
+ /** Append to the turn's running assistant text. */
66
+ appendAssistantText(text: string): void;
67
+ /** Forward a normalized StreamBlock to `opts.onBlock`. */
68
+ emitBlock(block: StreamBlock): void;
69
+ /** 1-based sequence within this turn. */
70
+ seq: number;
71
+ }
72
+ export declare abstract class AcpRuntimeAdapter implements RuntimeAdapter {
73
+ abstract readonly id: string;
74
+ probe?(): RuntimeProbeResult;
75
+ protected abstract resolveBinary(opts: RuntimeRunOptions): string;
76
+ /** Argv tail (excluding the binary). ACP servers usually take none. */
77
+ protected buildArgs(_opts: RuntimeRunOptions): string[];
78
+ protected abstract spawnEnv(opts: RuntimeRunOptions): NodeJS.ProcessEnv;
79
+ /** Subclass hook: react to one `session/update` notification. */
80
+ protected abstract onUpdate(params: AcpUpdateParams, ctx: AcpUpdateCtx): void;
81
+ /** Subclass hook: respond to a `session/request_permission` request. */
82
+ protected abstract onPermissionRequest(req: AcpPermissionRequest, opts: RuntimeRunOptions): Promise<AcpPermissionResponse>;
83
+ /** Runtime-specific clientCapabilities sent on initialize. */
84
+ protected clientCapabilities(): Record<string, unknown>;
85
+ /** Runtime-specific clientInfo sent on initialize. */
86
+ protected clientInfo(): {
87
+ name: string;
88
+ version: string;
89
+ };
90
+ /**
91
+ * Hook invoked synchronously before spawn. Subclasses use this to write
92
+ * systemContext to disk (e.g. `<cwd>/AGENTS.md`).
93
+ */
94
+ protected prepareTurn(_opts: RuntimeRunOptions): void;
95
+ /** cwd passed to ACP `session/new` / `session/load`. Typically `opts.cwd`. */
96
+ protected sessionCwd(opts: RuntimeRunOptions): string;
97
+ run(opts: RuntimeRunOptions): Promise<RuntimeRunResult>;
98
+ private withTimeout;
99
+ }