@gajae-code/coding-agent 0.11.4 → 0.11.5

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 (146) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/dist/types/cli/notify-cli.d.ts +5 -1
  3. package/dist/types/config/settings-schema.d.ts +20 -0
  4. package/dist/types/export/html/template.generated.d.ts +1 -1
  5. package/dist/types/gjc-runtime/deep-interview-state.d.ts +28 -0
  6. package/dist/types/gjc-runtime/launch-tmux.d.ts +3 -0
  7. package/dist/types/gjc-runtime/managed-owner-admission.d.ts +42 -0
  8. package/dist/types/gjc-runtime/managed-owner-supervisor.d.ts +41 -0
  9. package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +9 -0
  10. package/dist/types/gjc-runtime/ultragoal-owner-loss-recovery.d.ts +94 -0
  11. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +1 -0
  12. package/dist/types/harness-control-plane/owner.d.ts +3 -1
  13. package/dist/types/internal-urls/local-protocol.d.ts +18 -0
  14. package/dist/types/main.d.ts +2 -2
  15. package/dist/types/modes/components/ansi-display-validator.d.ts +2 -0
  16. package/dist/types/modes/components/notifications-settings-editor.d.ts +4 -0
  17. package/dist/types/modes/components/session-observer-overlay.d.ts +3 -1
  18. package/dist/types/modes/components/settings-selector.d.ts +2 -0
  19. package/dist/types/modes/components/tool-transcript-format.d.ts +58 -0
  20. package/dist/types/modes/components/transcript-viewer-overlay.d.ts +3 -0
  21. package/dist/types/modes/controllers/goal-mode-controller.d.ts +8 -1
  22. package/dist/types/modes/controllers/selector-controller.d.ts +15 -0
  23. package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +3 -0
  24. package/dist/types/modes/shared/agent-wire/workflow-gate-types.d.ts +16 -0
  25. package/dist/types/plan-mode/approved-plan.d.ts +1 -0
  26. package/dist/types/runtime-mcp/manager.d.ts +2 -0
  27. package/dist/types/runtime-mcp/oauth-discovery.d.ts +12 -1
  28. package/dist/types/sdk/bus/chat-daemon-cli.d.ts +3 -1
  29. package/dist/types/sdk/bus/chat-daemon-control.d.ts +39 -4
  30. package/dist/types/sdk/bus/config-commands.d.ts +8 -0
  31. package/dist/types/sdk/bus/config.d.ts +22 -0
  32. package/dist/types/sdk/bus/index.d.ts +162 -2
  33. package/dist/types/sdk/bus/notification-service.d.ts +71 -8
  34. package/dist/types/sdk/bus/session-control.d.ts +11 -8
  35. package/dist/types/sdk/bus/telegram-daemon-cli.d.ts +5 -0
  36. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +6 -3
  37. package/dist/types/sdk/bus/telegram-daemon-control.d.ts +23 -4
  38. package/dist/types/sdk/bus/telegram-daemon.d.ts +214 -20
  39. package/dist/types/sdk/session.d.ts +16 -1
  40. package/dist/types/secrets/index.d.ts +4 -2
  41. package/dist/types/secrets/obfuscator.d.ts +1 -1
  42. package/dist/types/session/agent-session.d.ts +31 -2
  43. package/dist/types/session/artifacts.d.ts +30 -8
  44. package/dist/types/session/internal/managed-session-scope.d.ts +7 -0
  45. package/dist/types/session/internal/managed-session-storage.d.ts +63 -6
  46. package/dist/types/session/session-manager.d.ts +88 -18
  47. package/dist/types/session/session-storage.d.ts +27 -0
  48. package/dist/types/session/streaming-output.d.ts +4 -0
  49. package/dist/types/task/executor.d.ts +9 -0
  50. package/dist/types/tool-discovery/tool-index.d.ts +2 -0
  51. package/dist/types/tools/ask.d.ts +48 -0
  52. package/dist/types/tools/bash-skill-urls.d.ts +2 -2
  53. package/dist/types/tools/index.d.ts +4 -0
  54. package/package.json +9 -9
  55. package/scripts/generate-sdk-operation-inventory.ts +2 -0
  56. package/src/cli/notify-cli.ts +40 -18
  57. package/src/commands/sdk.ts +7 -2
  58. package/src/config/settings-schema.ts +17 -0
  59. package/src/daemon/runtime.ts +2 -2
  60. package/src/defaults/gjc/skills/deep-interview/SKILL.md +70 -49
  61. package/src/defaults/gjc/skills/ralplan/SKILL.md +0 -6
  62. package/src/defaults/gjc/skills/team/SKILL.md +7 -9
  63. package/src/defaults/gjc/skills/ultragoal/SKILL.md +0 -2
  64. package/src/eval/py/index.ts +2 -2
  65. package/src/export/html/template.generated.ts +1 -1
  66. package/src/extensibility/plugins/marketplace/manager.ts +94 -10
  67. package/src/gjc-runtime/deep-interview-recorder.ts +7 -0
  68. package/src/gjc-runtime/deep-interview-runtime.ts +10 -0
  69. package/src/gjc-runtime/deep-interview-state.ts +127 -3
  70. package/src/gjc-runtime/launch-tmux.ts +61 -6
  71. package/src/gjc-runtime/managed-owner-admission.ts +352 -0
  72. package/src/gjc-runtime/managed-owner-supervisor.ts +162 -0
  73. package/src/gjc-runtime/state-runtime.ts +38 -12
  74. package/src/gjc-runtime/team-runtime.ts +1 -1
  75. package/src/gjc-runtime/tmux-owner-isolation.ts +117 -0
  76. package/src/gjc-runtime/ultragoal-owner-loss-recovery.ts +328 -0
  77. package/src/gjc-runtime/ultragoal-runtime.ts +14 -0
  78. package/src/harness-control-plane/owner.ts +59 -3
  79. package/src/hindsight/client.ts +65 -2
  80. package/src/internal-urls/agent-protocol.ts +76 -5
  81. package/src/internal-urls/docs-index.generated.ts +7 -6
  82. package/src/internal-urls/local-protocol.ts +416 -18
  83. package/src/lsp/index.ts +4 -88
  84. package/src/main.ts +37 -13
  85. package/src/modes/acp/acp-agent.ts +8 -0
  86. package/src/modes/components/ansi-display-validator.ts +54 -0
  87. package/src/modes/components/notifications-settings-editor.ts +26 -2
  88. package/src/modes/components/session-observer-overlay.ts +40 -24
  89. package/src/modes/components/settings-selector.ts +3 -1
  90. package/src/modes/components/tool-transcript-format.ts +321 -0
  91. package/src/modes/components/transcript-viewer-overlay.ts +39 -17
  92. package/src/modes/controllers/goal-mode-controller.ts +20 -1
  93. package/src/modes/controllers/plan-mode-controller.ts +2 -0
  94. package/src/modes/controllers/runtime-mcp-command-controller.ts +3 -0
  95. package/src/modes/controllers/selector-controller.ts +70 -8
  96. package/src/modes/interactive-mode.ts +1 -0
  97. package/src/modes/shared/agent-wire/deep-interview-gate.ts +31 -1
  98. package/src/modes/shared/agent-wire/workflow-gate-broker.ts +17 -1
  99. package/src/modes/shared/agent-wire/workflow-gate-schema.ts +4 -2
  100. package/src/modes/shared/agent-wire/workflow-gate-types.ts +120 -3
  101. package/src/plan-mode/approved-plan.ts +3 -1
  102. package/src/prompts/agents/architect.md +1 -0
  103. package/src/prompts/agents/critic.md +1 -0
  104. package/src/prompts/system/custom-system-prompt.md +1 -1
  105. package/src/prompts/system/system-prompt.md +2 -1
  106. package/src/prompts/tools/lsp.md +3 -3
  107. package/src/runtime-mcp/manager.ts +16 -1
  108. package/src/runtime-mcp/oauth-discovery.ts +116 -26
  109. package/src/sdk/broker/session-index.ts +122 -64
  110. package/src/sdk/bus/chat-daemon-cli.ts +72 -10
  111. package/src/sdk/bus/chat-daemon-control.ts +526 -130
  112. package/src/sdk/bus/chat-daemon-runtime.ts +1 -0
  113. package/src/sdk/bus/config-commands.ts +22 -0
  114. package/src/sdk/bus/config.ts +37 -0
  115. package/src/sdk/bus/index.ts +384 -92
  116. package/src/sdk/bus/lifecycle-control-runtime.ts +53 -3
  117. package/src/sdk/bus/notification-service.ts +284 -62
  118. package/src/sdk/bus/recent-activity.ts +14 -5
  119. package/src/sdk/bus/session-control.ts +150 -44
  120. package/src/sdk/bus/telegram-daemon-cli.ts +20 -14
  121. package/src/sdk/bus/telegram-daemon-contract.ts +6 -3
  122. package/src/sdk/bus/telegram-daemon-control.ts +294 -76
  123. package/src/sdk/bus/telegram-daemon.ts +1788 -267
  124. package/src/sdk/protocol/operation-inventory.generated.json +11 -0
  125. package/src/sdk/session.ts +155 -27
  126. package/src/secrets/index.ts +53 -10
  127. package/src/secrets/obfuscator.ts +21 -18
  128. package/src/secrets/regex.ts +2 -0
  129. package/src/session/agent-session.ts +495 -253
  130. package/src/session/artifacts.ts +188 -23
  131. package/src/session/context-estimation.ts +7 -2
  132. package/src/session/internal/managed-session-scope.ts +200 -38
  133. package/src/session/internal/managed-session-storage.ts +1152 -46
  134. package/src/session/session-manager.ts +1055 -148
  135. package/src/session/session-storage.ts +109 -11
  136. package/src/session/streaming-output.ts +6 -0
  137. package/src/task/executor.ts +71 -26
  138. package/src/task/index.ts +63 -20
  139. package/src/tool-discovery/tool-index.ts +18 -0
  140. package/src/tools/ask.ts +129 -32
  141. package/src/tools/bash-skill-urls.ts +14 -3
  142. package/src/tools/bash.ts +2 -2
  143. package/src/tools/index.ts +4 -0
  144. package/src/tools/plan-mode-guard.ts +1 -0
  145. package/src/tools/sqlite-reader.ts +3 -2
  146. package/src/tools/subagent.ts +2 -0
@@ -2,6 +2,8 @@ import * as fs from "node:fs";
2
2
  import type { Settings } from "../../config/settings";
3
3
  import type { DaemonRuntimeInfo } from "../../daemon/control-types";
4
4
  import { HEARTBEAT_TTL_MS } from "./daemon-paths";
5
+ import { type DaemonTransitionLock, type NotificationEndpointFile, type NotificationEndpointFileIdentity, type NotificationExactUnlinkResult } from "./notification-service";
6
+ import { type DaemonProcessReference } from "./telegram-daemon-control";
5
7
  export { DAEMON_GENERATION, NOTIFICATION_PROTOCOL_VERSION } from "./telegram-daemon-contract";
6
8
  import { type ControlServerLike, type LifecycleControlServerFactory } from "./lifecycle-control-runtime";
7
9
  import type { OrchestratorDeps } from "./lifecycle-orchestrator";
@@ -10,9 +12,16 @@ import { type AliasTable, type CallbackRoute } from "./telegram-reference";
10
12
  export type EnsureDaemonResult = "owner_spawned" | "attached" | "disabled" | "blocked";
11
13
  /** Detailed result for orchestration that must distinguish a #2028 handoff from a fresh spawn. */
12
14
  export type EnsureTelegramDaemonDetailedResult = "spawned" | "reloaded" | "attached" | "disabled" | "blocked_identity";
15
+ export type TelegramDaemonOwnershipPhase = "provisional" | "ready" | "retired";
13
16
  export interface DaemonState {
14
17
  pid: number;
18
+ /** OS process-start provenance; mandatory for PID-authorized ownership actions. */
19
+ incarnation: string;
15
20
  ownerId: string;
21
+ /** Unique, durable identity for one ownership acquisition. */
22
+ acquisitionId?: string;
23
+ /** A provisional owner is physical-live but MUST NOT be attached as ready. */
24
+ ownershipPhase?: TelegramDaemonOwnershipPhase;
16
25
  tokenFingerprint: string;
17
26
  chatId: string;
18
27
  startedAt: number;
@@ -26,10 +35,8 @@ export interface DaemonState {
26
35
  version: 1;
27
36
  /**
28
37
  * Operational daemon generation of the process that owns the lock, distinct
29
- * from the persisted state-schema {@link DaemonState.version}. It records the
30
- * wire generation ({@link DAEMON_GENERATION}) the owning daemon speaks so a
31
- * freshly-upgraded host can detect — and reload — a still-live pre-upgrade
32
- * daemon whose schema version is unchanged. Absent on pre-generation state.
38
+ * from both the persisted state-schema {@link DaemonState.version} and the
39
+ * notification wire protocol version. Absent on pre-generation state.
33
40
  */
34
41
  generation?: number;
35
42
  stoppedAt?: number;
@@ -45,8 +52,14 @@ export interface TelegramDaemonFs {
45
52
  }>;
46
53
  readdir(path: string): Promise<string[]>;
47
54
  chmod(path: string, mode: number): Promise<void>;
55
+ stat?(path: string): Promise<{
56
+ mtimeMs: number;
57
+ }>;
58
+ readEndpointFile?(path: string): Promise<NotificationEndpointFile>;
59
+ exactUnlink?(path: string, identity: NotificationEndpointFileIdentity): Promise<NotificationExactUnlinkResult>;
48
60
  }
49
61
  export interface SpawnResult {
62
+ pid?: number;
50
63
  unref?: () => void;
51
64
  }
52
65
  export interface TelegramDaemonDeps {
@@ -54,6 +67,10 @@ export interface TelegramDaemonDeps {
54
67
  now?: () => number;
55
68
  pid?: number;
56
69
  pidAlive?: (pid: number) => boolean;
70
+ /** Opens an identity-stable process authority for destructive lifecycle operations. */
71
+ processReference?: (pid: number) => DaemonProcessReference | undefined;
72
+ /** Returns immutable process-start provenance, or undefined when unsupported. */
73
+ pidIncarnation?: (pid: number) => string | undefined;
57
74
  spawn?: (command: string, args: string[], opts: {
58
75
  detached: boolean;
59
76
  stdio: "ignore";
@@ -71,6 +88,8 @@ export interface TelegramDaemonDeps {
71
88
  sendSignal?: (pid: number, signal: NodeJS.Signals) => void;
72
89
  sleep?: (ms: number) => Promise<void>;
73
90
  waitStepMs?: number;
91
+ /** Bounded startup-readiness timeout; injectable for deterministic handoff tests. */
92
+ readinessTimeoutMs?: number;
74
93
  }
75
94
  export declare const HEARTBEAT_INTERVAL_MS = 5000;
76
95
  export { HEARTBEAT_TTL_MS };
@@ -104,6 +123,53 @@ export declare function startDaemonLifecycleControl(input: {
104
123
  auditRedactionKey: Uint8Array;
105
124
  env?: NodeJS.ProcessEnv;
106
125
  }): OrchestratorDeps;
126
+ export declare function tryCreateOwnershipLock(fsImpl: TelegramDaemonFs, file: string, initialization: OwnershipLockMetadata): Promise<boolean>;
127
+ type OwnershipLockMetadata = {
128
+ pid: number;
129
+ incarnation: string;
130
+ ownerId?: string;
131
+ acquisitionId?: string;
132
+ startedAt: number;
133
+ };
134
+ type LegacyOwnershipLockMetadata = {
135
+ pid: number;
136
+ incarnation?: string;
137
+ startedAt: number;
138
+ };
139
+ type OwnershipLockRead = {
140
+ kind: "missing";
141
+ } | {
142
+ kind: "malformed";
143
+ raw: string;
144
+ mtimeMs?: number;
145
+ } | {
146
+ kind: "legacy";
147
+ metadata: LegacyOwnershipLockMetadata;
148
+ } | {
149
+ kind: "valid";
150
+ metadata: OwnershipLockMetadata;
151
+ };
152
+ /** Read lock provenance without treating a corrupt legacy artifact as a filesystem failure. */
153
+ export declare function readOwnershipLock(fsImpl: TelegramDaemonFs, file: string): Promise<OwnershipLockRead>;
154
+ /**
155
+ * A live initializer lock only proves that a concurrent publisher is active.
156
+ * It never proves a ready daemon: legacy or unavailable provenance remains
157
+ * blocked, while a canonical mismatch proves PID reuse and can be reclaimed
158
+ * under the transition lock.
159
+ */
160
+ export declare function liveOwnershipLockDecision(input: {
161
+ lock: OwnershipLockRead;
162
+ pidAlive: (pid: number) => boolean;
163
+ pidIncarnation: (pid: number) => string | undefined;
164
+ }): {
165
+ acquired: false;
166
+ attached: false;
167
+ blocked: true;
168
+ } | {
169
+ acquired: false;
170
+ attached: false;
171
+ provisional: true;
172
+ } | undefined;
107
173
  export declare function registerNotificationRoot(input: {
108
174
  settings: Settings;
109
175
  cwd: string;
@@ -121,12 +187,39 @@ export declare function unregisterNotificationRoot(input: {
121
187
  sessionId: string;
122
188
  fs?: TelegramDaemonFs;
123
189
  }): Promise<UnregisterNotificationRootResult>;
190
+ export declare function hasSafeDaemonStateShape(state: unknown): state is DaemonState;
191
+ /** True for a physically live owner with this configuration, including legacy generations. */
192
+ export declare function isPhysicalMatchingOwner(input: {
193
+ state: DaemonState | undefined;
194
+ tokenFingerprint: string;
195
+ chatId: string;
196
+ pidAlive: (pid: number) => boolean;
197
+ pidIncarnation?: (pid: number) => string | undefined;
198
+ }): boolean;
199
+ /** True only for a signalable legacy owner or this generation's ready bound child. */
200
+ export declare function isSignalableMatchingOwner(input: {
201
+ state: DaemonState | undefined;
202
+ tokenFingerprint: string;
203
+ chatId: string;
204
+ pidAlive: (pid: number) => boolean;
205
+ pidIncarnation?: (pid: number) => string | undefined;
206
+ }): boolean;
124
207
  export declare function isFreshLiveOwner(input: {
125
208
  state: DaemonState | undefined;
126
209
  now: number;
127
210
  tokenFingerprint: string;
128
211
  chatId: string;
129
212
  pidAlive: (pid: number) => boolean;
213
+ pidIncarnation?: (pid: number) => string | undefined;
214
+ }): boolean;
215
+ /** True only when a physically live matching owner can serve this build's daemon lifecycle contract. */
216
+ export declare function isCurrentCompatibleOwner(input: {
217
+ state: DaemonState | undefined;
218
+ now: number;
219
+ tokenFingerprint: string;
220
+ chatId: string;
221
+ pidAlive: (pid: number) => boolean;
222
+ pidIncarnation?: (pid: number) => string | undefined;
130
223
  }): boolean;
131
224
  export declare function acquireDaemonOwnership(input: {
132
225
  settings: Settings;
@@ -137,6 +230,7 @@ export declare function acquireDaemonOwnership(input: {
137
230
  now?: () => number;
138
231
  pid?: number;
139
232
  pidAlive?: (pid: number) => boolean;
233
+ pidIncarnation?: (pid: number) => string | undefined;
140
234
  randomId?: () => string;
141
235
  /** Permit one Windows source launcher PID to daemon PID handoff. */
142
236
  allowPidRebind?: boolean;
@@ -145,32 +239,100 @@ export declare function acquireDaemonOwnership(input: {
145
239
  }): Promise<{
146
240
  acquired: boolean;
147
241
  ownerId?: string;
242
+ acquisitionId?: string;
148
243
  attached?: boolean;
149
244
  blocked?: boolean;
245
+ provisional?: boolean;
150
246
  reason?: "identity_mismatch";
151
247
  reloadRequired?: boolean;
152
248
  }>;
153
249
  export declare function renewDaemonHeartbeat(input: {
154
250
  settings: Settings;
155
251
  ownerId: string;
156
- /** Bind a daemon heartbeat to the Telegram identity that acquired the lock. */
252
+ acquisitionId?: string;
253
+ tokenFingerprint?: string;
254
+ chatId?: string;
255
+ fs?: TelegramDaemonFs;
256
+ now?: () => number;
257
+ pid?: number;
258
+ generation?: number;
259
+ pidIncarnation?: (pid: number) => string | undefined;
260
+ sleep?: (ms: number) => Promise<void>;
261
+ stealRetries?: number;
262
+ stealRetryDelayMs?: number;
263
+ }): Promise<boolean>;
264
+ /** Acquire the lifecycle transition lock with bounded retry for bind/retire races. */
265
+ export declare function acquireTransitionLock(input: {
266
+ fs: TelegramDaemonFs;
267
+ path: string;
268
+ pidAlive?: (pid: number) => boolean;
269
+ pidIncarnation?: (pid: number) => string | undefined;
270
+ sleep?: (ms: number) => Promise<void>;
271
+ retries?: number;
272
+ retryDelayMs?: number;
273
+ }): Promise<DaemonTransitionLock | undefined>;
274
+ /** Retire only the unchanged provisional acquisition after bounded readiness fails. */
275
+ export declare function retireProvisionalDaemonOwnership(input: {
276
+ settings: Settings;
277
+ ownerId: string;
278
+ acquisitionId?: string;
279
+ pidIncarnation?: (pid: number) => string | undefined;
280
+ pidAlive?: (pid: number) => boolean;
281
+ /** Detached child PID, when the launcher successfully reported it. */
282
+ pid: number;
283
+ /** PID which created the provisional reservation before the child was bound. */
284
+ launcherPid?: number;
285
+ fs?: TelegramDaemonFs;
286
+ now?: () => number;
287
+ sleep?: (ms: number) => Promise<void>;
288
+ stealRetries?: number;
289
+ stealRetryDelayMs?: number;
290
+ /** Only no-child confirmation may retire a ready-like launcher publication. */
291
+ allowReadyWithoutChildPid?: boolean;
292
+ }): Promise<boolean>;
293
+ /** Wait for a matching current-generation daemon to publish a ready state. */
294
+ export declare function waitForTelegramDaemonReady(input: {
295
+ settings: Settings;
296
+ ownerId?: string;
297
+ acquisitionId?: string;
298
+ pid?: number;
299
+ excludedPid?: number;
157
300
  tokenFingerprint: string;
158
301
  chatId: string;
159
- /** Require the current daemon PID after the launcher-to-daemon PID rebind. */
160
- pid: number;
161
302
  fs?: TelegramDaemonFs;
162
303
  now?: () => number;
304
+ pidAlive?: (pid: number) => boolean;
305
+ pidIncarnation?: (pid: number) => string | undefined;
306
+ sleep?: (ms: number) => Promise<void>;
307
+ waitStepMs?: number;
308
+ timeoutMs?: number;
163
309
  }): Promise<boolean>;
164
- export declare function releaseDaemonOwnership(input: {
310
+ /** Confirm the provisional owner or retire only its unchanged acquisition. */
311
+ export declare function confirmTelegramDaemonSpawn(input: {
165
312
  settings: Settings;
166
- ownerId: string;
167
- /** Bind release to the Telegram identity that acquired the lock. */
313
+ spawned: TelegramSpawnOwnerResult;
168
314
  tokenFingerprint: string;
169
315
  chatId: string;
170
- /** Require the current daemon PID after the launcher-to-daemon PID rebind. */
171
316
  pid: number;
172
317
  fs?: TelegramDaemonFs;
173
318
  now?: () => number;
319
+ pidAlive?: (pid: number) => boolean;
320
+ pidIncarnation?: (pid: number) => string | undefined;
321
+ sleep?: (ms: number) => Promise<void>;
322
+ waitStepMs?: number;
323
+ timeoutMs?: number;
324
+ }): Promise<boolean>;
325
+ export declare function releaseDaemonOwnership(input: {
326
+ settings: Settings;
327
+ ownerId: string;
328
+ acquisitionId?: string;
329
+ tokenFingerprint?: string;
330
+ chatId?: string;
331
+ pid?: number;
332
+ generation?: number;
333
+ pidIncarnation?: (pid: number) => string | undefined;
334
+ fs?: TelegramDaemonFs;
335
+ now?: () => number;
174
336
  }): Promise<void>;
175
337
  /** Read the persisted daemon ownership state (or undefined when absent). */
176
338
  export declare function readDaemonState(settings: Pick<Settings, "getAgentDir">, fs?: TelegramDaemonFs): Promise<DaemonState | undefined>;
@@ -182,18 +344,30 @@ export interface TelegramSpawnOwnerInput {
182
344
  tokenFingerprint: string;
183
345
  chatId: string;
184
346
  }
185
- export interface TelegramSpawnOwnerResult {
186
- result: EnsureDaemonResult;
187
- ownerId?: string;
347
+ export interface TelegramSpawnAcquisition {
348
+ readonly ownerId: string;
349
+ readonly acquisitionId: string;
350
+ /** PID of the launcher which reserved provisional ownership before spawn. */
351
+ readonly launcherPid?: number;
352
+ /** Actual detached child which must publish the ready owner state. */
353
+ readonly pid?: number;
354
+ }
355
+ export type TelegramSpawnOwnerResult = {
356
+ result: "owner_spawned";
357
+ acquisition: TelegramSpawnAcquisition;
358
+ runtime: DaemonRuntimeInfo;
359
+ warnings: string[];
360
+ } | {
361
+ result: "attached";
188
362
  runtime: DaemonRuntimeInfo;
189
363
  warnings: string[];
190
- /**
191
- * Set when ownership was NOT acquired because a still-live owner is running an
192
- * older daemon generation. The caller must hand off via a reload rather than
193
- * attach; see {@link ensureTelegramDaemonRunning}.
194
- */
195
364
  reloadRequired?: boolean;
196
- }
365
+ } | {
366
+ result: "blocked";
367
+ runtime: DaemonRuntimeInfo;
368
+ warnings: string[];
369
+ reloadRequired?: boolean;
370
+ };
197
371
  /**
198
372
  * Build the detached spawn command/args for the daemon-internal entrypoint.
199
373
  * Source mode prepends the entry script so the respawn loads edited source;
@@ -331,6 +505,7 @@ export interface TelegramDaemonOptions {
331
505
  pid?: number;
332
506
  /** Liveness probe for skipping dead-PID endpoint records in {@link TelegramNotificationDaemon.scanRoots}. */
333
507
  pidAlive?: (pid: number) => boolean;
508
+ pidIncarnation?: (pid: number) => string | undefined;
334
509
  botApi?: BotApi;
335
510
  control?: DaemonControlHooks;
336
511
  /**
@@ -358,6 +533,10 @@ export interface TelegramDaemonOptions {
358
533
  richDraft?: {
359
534
  enabled: boolean;
360
535
  };
536
+ /** Tool start/completion messages (enabled by default). */
537
+ toolActivity?: {
538
+ enabled: boolean;
539
+ };
361
540
  /**
362
541
  * Per-session Telegram forum-topic naming. `nameTemplate` supports the
363
542
  * `{repo}`, `{branch}`, and `{title}` placeholders; unset preserves the
@@ -403,6 +582,16 @@ export declare class TelegramNotificationDaemon {
403
582
  readonly messageRoutes: Map<string | number, CallbackRoute | Omit<CallbackRoute, "answer">>;
404
583
  /** Telegram message id backing each streamed `${sessionId}:${coalesceKey}`, for in-place edits. */
405
584
  private readonly liveMessages;
585
+ /** Endpoint-bound ownership for visible or dispatching tool bubbles. */
586
+ private readonly toolActivityOwners;
587
+ private readonly revokedToolEndpoints;
588
+ private readonly unresolvedToolTerminalizations;
589
+ private toolTerminalizationChain;
590
+ private toolActivityPolicyEpoch;
591
+ private toolActivityStopping;
592
+ private readonly replayToolActivityEpochs;
593
+ private toolShutdownBarrier;
594
+ private toolActivityAmbiguous;
406
595
  readonly sessions: Map<string, SessionSocket>;
407
596
  private readonly runtime;
408
597
  private readonly sessionRouter;
@@ -525,6 +714,9 @@ export declare class TelegramNotificationDaemon {
525
714
  * (so a delayed old close cannot delete a replacement), and best-effort closes
526
715
  * the socket. `scanRoots()` then reconnects the session.
527
716
  */
717
+ private enqueueToolTerminalization;
718
+ private scheduleVisibleToolTerminalization;
719
+ private beginToolActivityShutdown;
528
720
  private dropSession;
529
721
  private deleteMessageRoutes;
530
722
  private static readonly THREADED_FRAMES;
@@ -543,6 +735,8 @@ export declare class TelegramNotificationDaemon {
543
735
  private topicIdentityBase;
544
736
  private topicOwnerForIdentity;
545
737
  private sessionCanClaimIdentity;
738
+ private toolActivityOwner;
739
+ private toolActivityAuthorityIsCurrent;
546
740
  private submitThreadedFrame;
547
741
  private existingTopicForPrivateChat;
548
742
  private topicAuthorityLease;
@@ -1,4 +1,4 @@
1
- import { type AgentTelemetryConfig, ThinkingLevel } from "@gajae-code/agent-core";
1
+ import { type AgentMessage, type AgentTelemetryConfig, ThinkingLevel } from "@gajae-code/agent-core";
2
2
  import { type AuthCredentialSelector, type Model, type ProviderSessionState } from "@gajae-code/ai";
3
3
  import { createAppendOnlyContextManager, providerSupportsAppendOnlyAuto, resolveAppendOnlyMode } from "../append-only-mode";
4
4
  import { type Rule } from "../capability/rule";
@@ -25,6 +25,19 @@ import { type BuildSystemPromptResult } from "../system-prompt";
25
25
  import { BashTool, BUILTIN_TOOLS, createTools, EditTool, EvalTool, FindTool, HIDDEN_TOOLS, type LspStartupServerInfo, loadSshTool, ReadTool, ResolveTool, SearchTool, type Tool, type ToolSession, WebSearchTool, WriteTool } from "../tools";
26
26
  import { EventBus } from "../utils/event-bus";
27
27
  import { type WorkspaceTree } from "../workspace-tree";
28
+ /**
29
+ * Reconcile a resumed transcript that ends on an unpaired tool call.
30
+ *
31
+ * When a subagent finishes by calling `yield` (or any turn is torn down right
32
+ * after a tool executes), the terminating abort can land before the tool
33
+ * result is persisted, leaving the saved session ending on an assistant
34
+ * `toolCall` with no matching `toolResult`. Replaying that history verbatim on
35
+ * resume produces an invalid provider request (a tool_use not followed by a
36
+ * tool_result) and the resumed turn fails immediately. Synthesize a placeholder
37
+ * result for any such trailing unpaired tool call so a resumed session always
38
+ * starts from a valid, paired history. No-op for well-formed transcripts.
39
+ */
40
+ export declare function reconcileTrailingToolCalls(messages: AgentMessage[]): AgentMessage[];
28
41
  export interface CreateAgentSessionOptions {
29
42
  /** Working directory for project-local discovery. Default: getProjectDir() */
30
43
  cwd?: string;
@@ -152,6 +165,8 @@ export interface CreateAgentSessionOptions {
152
165
  notificationHostModeSupported?: boolean;
153
166
  /** Whether this host mode can own the root SDK endpoint. Default: true. */
154
167
  sdkHostModeSupported?: boolean;
168
+ /** Override configured Discord/Slack daemon readiness, primarily for embedded hosts and deterministic tests. */
169
+ ensureNotificationProviderDaemon?: (provider: "discord" | "slack", settings: Settings) => Promise<unknown>;
155
170
  /**
156
171
  * Opt-in OpenTelemetry instrumentation forwarded to the underlying Agent.
157
172
  * Passing `{}` enables the loop's GenAI-semantic-convention spans. See
@@ -1,8 +1,10 @@
1
- import type { SecretEntry } from "./obfuscator";
1
+ import { type SecretEntry, SecretObfuscator } from "./obfuscator";
2
+ /** Create an obfuscator using the process-local authenticated-placeholder key. */
3
+ export declare function createSecretObfuscator(entries: SecretEntry[]): SecretObfuscator;
2
4
  export { deobfuscateSessionContext, obfuscateMessages, type SecretEntry, SecretObfuscator } from "./obfuscator";
3
5
  /**
4
6
  * Load secrets from project-local and global secrets.yml files.
5
- * Project-local entries override global entries with matching content.
7
+ * Project-local plain entries override global entries with matching content.
6
8
  */
7
9
  export declare function loadSecrets(cwd: string, agentDir: string): Promise<SecretEntry[]>;
8
10
  /** Collect environment variable values that look like secrets. */
@@ -9,7 +9,7 @@ export interface SecretEntry {
9
9
  }
10
10
  export declare class SecretObfuscator {
11
11
  #private;
12
- constructor(entries: SecretEntry[]);
12
+ constructor(entries: SecretEntry[], key?: Uint8Array);
13
13
  hasSecrets(): boolean;
14
14
  /** Obfuscate all secrets in text. Bidirectional placeholders for obfuscate mode, one-way for replace. */
15
15
  obfuscate(text: string): string;
@@ -101,7 +101,7 @@ export { DefaultModelSelectionRecoveryError } from "./default-model-selection";
101
101
  import type { ClientBridge, ClientBridgePermissionOption, ClientBridgePermissionOutcome, ClientBridgePermissionToolCall } from "./client-bridge";
102
102
  import { type ContributionPrepOptions, type ContributionPrepResult } from "./contribution-prep";
103
103
  import { type CustomMessage } from "./messages";
104
- import type { BranchSummaryEntry, CompactionEntry, NewSessionOptions, SessionContext, SessionManager, SessionManagerCloseOutcome } from "./session-manager";
104
+ import type { BranchSummaryEntry, CompactionEntry, NewSessionOptions, RecoveryHydrationContext, RecoveryHydrationPromotionFence, SessionContext, SessionManager, SessionManagerCloseOutcome } from "./session-manager";
105
105
  import { ToolChoiceQueue } from "./tool-choice-queue";
106
106
  import { YieldQueue } from "./yield-queue";
107
107
  /** Session-specific events that extend the core AgentEvent */
@@ -250,12 +250,30 @@ export interface AgentSessionConfig {
250
250
  discoveryMode?: "off" | "mcp-only" | "all";
251
251
  /** MCP tool names to activate for the current session when discovery mode is enabled. */
252
252
  initialSelectedMCPToolNames?: string[];
253
- /** Whether constructor-provided MCP defaults should be persisted immediately. */
253
+ /** Built-in discoverable tool names restored for the current all-discovery session. */
254
+ initialSelectedDiscoveredBuiltinToolNames?: string[];
255
+ /** Discoverable built-ins active for configured or explicit reasons independently of persisted discovery selection. */
256
+ initialBaselineDiscoveredBuiltinToolNames?: string[];
257
+ /** Whether an MCP selection was explicitly supplied to the constructor, including an empty selection. */
258
+ initialMCPToolSelectionIsExplicit?: boolean;
259
+ /** Whether a discoverable built-in selection was explicitly supplied to the constructor, including an empty selection. */
260
+ initialDiscoveredBuiltinToolSelectionIsExplicit?: boolean;
261
+ /** Whether constructor-provided MCP selections should be persisted immediately. */
254
262
  persistInitialMCPToolSelection?: boolean;
263
+ /** Whether constructor-provided discovered-built-in selections should be persisted immediately. */
264
+ persistInitialDiscoveredBuiltinToolSelection?: boolean;
265
+ /** Explicit MCP authority to write for a new session; distinct from active fallback tools. */
266
+ initialPersistedMCPToolNames?: string[];
267
+ /** Explicit discovered built-in authority to write for a new session; distinct from active fallback tools. */
268
+ initialPersistedDiscoveredBuiltinToolNames?: string[];
269
+ /** Immutable predecessor authority while a recovery host is read-only. */
270
+ recoveryHydrationContext?: RecoveryHydrationContext;
255
271
  /** MCP server names whose tools should seed discovery-mode sessions whenever those servers are connected. */
256
272
  defaultSelectedMCPServerNames?: string[];
257
273
  /** MCP tool names that should seed brand-new sessions created from this AgentSession. */
258
274
  defaultSelectedMCPToolNames?: string[];
275
+ /** MCP capabilities that are always active and never part of persisted user selection. */
276
+ mandatoryMCPToolNames?: string[];
259
277
  /** TTSR manager for time-traveling stream rules */
260
278
  ttsrManager?: TtsrManager;
261
279
  /** Secret obfuscator for deobfuscating streaming edit content */
@@ -472,6 +490,13 @@ export declare class AgentSession {
472
490
  };
473
491
  readonly rawSseDebugBuffer: RawSseDebugBuffer;
474
492
  constructor(config: AgentSessionConfig);
493
+ /**
494
+ * Resolves when constructor-time workflow-gate tool restoration (ask
495
+ * registration plus durable active-workflow attachment) has settled. The
496
+ * SDK factory awaits this so a resumed canonical workflow session is
497
+ * returned with `ask` already resident.
498
+ */
499
+ get workflowGateToolRestoration(): Promise<void>;
475
500
  /** Model registry for API key resolution and model discovery */
476
501
  get modelRegistry(): ModelRegistry;
477
502
  /** Advance the tool-choice queue and return the next directive for the upcoming LLM call. */
@@ -704,6 +729,10 @@ export declare class AgentSession {
704
729
  /** All messages including custom types like BashExecutionMessage */
705
730
  get messages(): AgentMessage[];
706
731
  get transcriptPromptGeneration(): number;
732
+ /** The immutable recovery authority, present only before external ownership promotion. */
733
+ get recoveryHydrationContext(): RecoveryHydrationContext | undefined;
734
+ /** Enables normal session mutations after the owner has published its durable fence and writer lease. */
735
+ promoteRecoveryHydrationAfterOwnershipReadyFence(fence: RecoveryHydrationPromotionFence): Promise<void>;
707
736
  /** Main startup calls this exactly once, after a strict open returned `kind: "opened"`. */
708
737
  continuePersistedHistory(): Promise<void>;
709
738
  buildDisplaySessionContext(): SessionContext;
@@ -1,3 +1,18 @@
1
+ /**
2
+ * Session-scoped artifact storage for truncated tool outputs.
3
+ *
4
+ * Artifacts are stored in a directory alongside the session file,
5
+ * accessible via artifact:// URLs.
6
+ */
7
+ import { type ManagedSessionDescendantStore } from "./internal/managed-session-storage";
8
+ export interface ManagedOutputGeneration {
9
+ outputFilename: string;
10
+ metadataFilename: string;
11
+ outputSizeBytes: number;
12
+ outputSha256: string;
13
+ metadataSizeBytes: number;
14
+ metadataSha256: string;
15
+ }
1
16
  export interface ArtifactSaveOptions {
2
17
  maxBytes?: number;
3
18
  }
@@ -16,32 +31,39 @@ export declare class ArtifactManager {
16
31
  /**
17
32
  * @param dir Directory that will hold artifact files. Created lazily on first save.
18
33
  */
19
- constructor(dir: string);
34
+ constructor(target: string | ManagedSessionDescendantStore);
20
35
  /**
21
36
  * Artifact directory path.
22
37
  * Directory may not exist until first artifact is saved.
23
38
  */
24
39
  get dir(): string;
40
+ getManagedRootAuthority(): import("./internal/managed-session-storage").ManagedDirectoryRoot | undefined;
41
+ getManagedSubtreeRootAuthority(): import("./internal/managed-session-storage").ManagedDirectoryRoot | undefined;
42
+ getManagedStore(): ManagedSessionDescendantStore | undefined;
43
+ assertManagedBinding(): void;
44
+ replaceNamed(filename: string, content: string): Promise<void>;
45
+ replaceNamedBytes(filename: string, bytes: Uint8Array): Promise<void>;
46
+ publishManagedOutputGeneration(selectorFilename: string, outputFilenamePrefix: string, outputBytes: Uint8Array, metadataBytes: Uint8Array): Promise<void>;
47
+ publishNamedNoReplace(filename: string, bytes: Uint8Array): Promise<void>;
25
48
  /**
26
49
  * Atomically allocate next artifact ID.
27
50
  * IDs are sequential within the session.
28
51
  */
29
52
  allocateId(): number;
30
53
  /**
31
- * Allocate a new artifact path and ID without writing content.
54
+ * Reserve an artifact ID without exposing a writable managed pathname.
32
55
  *
33
- * @param toolType Tool name for file extension (e.g., "bash", "read")
56
+ * Streaming callers that only understand bare paths fail closed; use `save`
57
+ * for terminally published artifact content.
34
58
  */
35
59
  allocatePath(toolType: string): Promise<{
36
60
  id: string;
37
- path: string;
61
+ path?: string;
38
62
  }>;
39
63
  /**
40
64
  * Save content as an artifact and return the artifact ID.
41
- *
42
- * @param content Full content to save
43
- * @param toolType Tool name for file extension (e.g., "bash", "read")
44
- * @returns Artifact ID (numeric string)
65
+ * Content is written to a private temporary inode, synced, and linked into
66
+ * the artifact directory only after the complete terminal payload exists.
45
67
  */
46
68
  save(content: string, toolType: string, options?: ArtifactSaveOptions): Promise<string>;
47
69
  /**
@@ -1,3 +1,4 @@
1
+ import * as native from "@gajae-code/natives";
1
2
  import type { ResumeSessionIdentity } from "../session-manager";
2
3
  export declare const MANAGED_SESSION_LAYOUT_VERSION: 2;
3
4
  export declare const MANAGED_SESSION_IDENTITY_VERSION: 1;
@@ -14,6 +15,12 @@ export interface ManagedScope {
14
15
  directoryPath: string;
15
16
  platform: "posix" | "win32";
16
17
  }
18
+ export declare function managedDirectoryAuthorityForScope(scope: ManagedScope): native.RecoveryFsRoot | undefined;
19
+ export declare function managedDirectoryIdentityForScope(scope: ManagedScope): {
20
+ dev: bigint;
21
+ ino: bigint;
22
+ };
23
+ export declare function managedRootForScope(scope: ManagedScope): import("./managed-session-storage").ManagedDirectoryRoot;
17
24
  export type ManagedMigrationPolicy = "copy-retain" | "disabled";
18
25
  export type ManagedScopeErrorCode = "cwd_missing" | "cwd_not_directory" | "identity_unavailable" | "network_unsupported" | "sessions_root_unavailable" | "binding_conflict" | "binding_invalid";
19
26
  export type ManagedScopeResolution = {