@gajae-code/coding-agent 0.11.4 → 0.11.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 (154) 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 +31 -1
  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 +42 -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 +11 -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 +98 -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/session/yield-queue.d.ts +8 -0
  50. package/dist/types/task/executor.d.ts +9 -0
  51. package/dist/types/tool-discovery/tool-index.d.ts +2 -0
  52. package/dist/types/tools/ask.d.ts +48 -0
  53. package/dist/types/tools/bash-skill-urls.d.ts +2 -2
  54. package/dist/types/tools/index.d.ts +4 -0
  55. package/package.json +9 -9
  56. package/scripts/generate-sdk-operation-inventory.ts +3 -0
  57. package/src/cli/notify-cli.ts +40 -18
  58. package/src/commands/sdk.ts +7 -2
  59. package/src/config/settings-schema.ts +31 -1
  60. package/src/daemon/runtime.ts +2 -2
  61. package/src/defaults/gjc/skills/deep-interview/SKILL.md +70 -49
  62. package/src/defaults/gjc/skills/ralplan/SKILL.md +0 -6
  63. package/src/defaults/gjc/skills/team/SKILL.md +7 -9
  64. package/src/defaults/gjc/skills/ultragoal/SKILL.md +0 -2
  65. package/src/eval/py/index.ts +2 -2
  66. package/src/export/html/template.generated.ts +1 -1
  67. package/src/extensibility/plugins/marketplace/manager.ts +94 -10
  68. package/src/gjc-runtime/deep-interview-recorder.ts +7 -0
  69. package/src/gjc-runtime/deep-interview-runtime.ts +10 -0
  70. package/src/gjc-runtime/deep-interview-state.ts +127 -3
  71. package/src/gjc-runtime/launch-tmux.ts +61 -6
  72. package/src/gjc-runtime/managed-owner-admission.ts +352 -0
  73. package/src/gjc-runtime/managed-owner-supervisor.ts +162 -0
  74. package/src/gjc-runtime/state-runtime.ts +38 -12
  75. package/src/gjc-runtime/team-runtime.ts +1 -1
  76. package/src/gjc-runtime/tmux-owner-isolation.ts +117 -0
  77. package/src/gjc-runtime/ultragoal-owner-loss-recovery.ts +328 -0
  78. package/src/gjc-runtime/ultragoal-runtime.ts +14 -0
  79. package/src/harness-control-plane/owner.ts +59 -3
  80. package/src/hindsight/client.ts +65 -2
  81. package/src/internal-urls/agent-protocol.ts +76 -5
  82. package/src/internal-urls/docs-index.generated.ts +8 -7
  83. package/src/internal-urls/local-protocol.ts +416 -18
  84. package/src/lsp/index.ts +4 -88
  85. package/src/main.ts +36 -12
  86. package/src/modes/acp/acp-agent.ts +8 -0
  87. package/src/modes/components/ansi-display-validator.ts +54 -0
  88. package/src/modes/components/notifications-settings-editor.ts +26 -2
  89. package/src/modes/components/session-observer-overlay.ts +40 -24
  90. package/src/modes/components/settings-selector.ts +3 -1
  91. package/src/modes/components/tool-transcript-format.ts +321 -0
  92. package/src/modes/components/transcript-viewer-overlay.ts +39 -17
  93. package/src/modes/controllers/command-controller.ts +15 -0
  94. package/src/modes/controllers/extension-ui-controller.ts +14 -2
  95. package/src/modes/controllers/goal-mode-controller.ts +20 -1
  96. package/src/modes/controllers/plan-mode-controller.ts +2 -0
  97. package/src/modes/controllers/runtime-mcp-command-controller.ts +3 -0
  98. package/src/modes/controllers/selector-controller.ts +70 -8
  99. package/src/modes/interactive-mode.ts +1 -0
  100. package/src/modes/runtime-init.ts +16 -2
  101. package/src/modes/shared/agent-wire/deep-interview-gate.ts +31 -1
  102. package/src/modes/shared/agent-wire/workflow-gate-broker.ts +17 -1
  103. package/src/modes/shared/agent-wire/workflow-gate-schema.ts +4 -2
  104. package/src/modes/shared/agent-wire/workflow-gate-types.ts +120 -3
  105. package/src/plan-mode/approved-plan.ts +3 -1
  106. package/src/prompts/agents/architect.md +1 -0
  107. package/src/prompts/agents/critic.md +1 -0
  108. package/src/prompts/system/custom-system-prompt.md +1 -1
  109. package/src/prompts/system/system-prompt.md +2 -1
  110. package/src/prompts/tools/lsp.md +3 -3
  111. package/src/runtime-mcp/manager.ts +16 -1
  112. package/src/runtime-mcp/oauth-discovery.ts +116 -26
  113. package/src/sdk/broker/session-index.ts +122 -64
  114. package/src/sdk/bus/chat-daemon-cli.ts +72 -10
  115. package/src/sdk/bus/chat-daemon-control.ts +538 -130
  116. package/src/sdk/bus/chat-daemon-runtime.ts +1 -0
  117. package/src/sdk/bus/config-commands.ts +22 -0
  118. package/src/sdk/bus/config.ts +37 -0
  119. package/src/sdk/bus/index.ts +388 -94
  120. package/src/sdk/bus/lifecycle-control-runtime.ts +53 -3
  121. package/src/sdk/bus/notification-service.ts +284 -62
  122. package/src/sdk/bus/recent-activity.ts +14 -5
  123. package/src/sdk/bus/session-control.ts +150 -44
  124. package/src/sdk/bus/telegram-daemon-cli.ts +20 -14
  125. package/src/sdk/bus/telegram-daemon-contract.ts +11 -3
  126. package/src/sdk/bus/telegram-daemon-control.ts +312 -76
  127. package/src/sdk/bus/telegram-daemon.ts +1916 -285
  128. package/src/sdk/host/control/dispatch.ts +14 -5
  129. package/src/sdk/protocol/operation-inventory.generated.json +29 -0
  130. package/src/sdk/session.ts +155 -27
  131. package/src/secrets/index.ts +53 -10
  132. package/src/secrets/obfuscator.ts +21 -18
  133. package/src/secrets/regex.ts +2 -0
  134. package/src/session/agent-session.ts +1447 -852
  135. package/src/session/artifacts.ts +188 -23
  136. package/src/session/context-estimation.ts +7 -2
  137. package/src/session/internal/managed-session-scope.ts +200 -38
  138. package/src/session/internal/managed-session-storage.ts +1152 -46
  139. package/src/session/session-manager.ts +1121 -148
  140. package/src/session/session-storage.ts +109 -11
  141. package/src/session/streaming-output.ts +6 -0
  142. package/src/session/yield-queue.ts +20 -0
  143. package/src/slash-commands/builtin-registry.ts +36 -0
  144. package/src/task/executor.ts +71 -26
  145. package/src/task/index.ts +63 -20
  146. package/src/tool-discovery/tool-index.ts +18 -0
  147. package/src/tools/ask-answer-registry.ts +15 -1
  148. package/src/tools/ask.ts +129 -32
  149. package/src/tools/bash-skill-urls.ts +14 -3
  150. package/src/tools/bash.ts +2 -2
  151. package/src/tools/index.ts +4 -0
  152. package/src/tools/plan-mode-guard.ts +1 -0
  153. package/src/tools/sqlite-reader.ts +3 -2
  154. package/src/tools/subagent.ts +2 -0
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  getCurrentTelegramActivationMarker,
3
+ isNotificationStreamingEnabled,
3
4
  isSessionNotificationsEnabled,
4
5
  isTelegramConfigured,
5
6
  type NotificationConfig,
@@ -22,7 +23,7 @@ export interface BoundNotificationSession<Context extends NotificationSessionCon
22
23
  }
23
24
 
24
25
  export type NotificationEndpointStartResult = "started" | "already" | "disabled" | "failed";
25
- export type TelegramDaemonPreflightResult = "ready" | "blocked_identity";
26
+ export type TelegramDaemonPreflightResult = "ready" | "blocked_identity" | "failed";
26
27
 
27
28
  /**
28
29
  * The endpoint implementation is deliberately injected. The controller owns
@@ -38,6 +39,10 @@ export interface NotificationSessionRuntime<Context extends NotificationSessionC
38
39
  * emit a frame. `blocked_identity` is fail-closed and starts nothing.
39
40
  */
40
41
  ensureTelegramDaemon?(binding: BoundNotificationSession<Context>): Promise<TelegramDaemonPreflightResult>;
42
+ /** Refresh mutable delivery policy from the same configuration snapshot used for reconciliation. */
43
+ refreshPolicy?(binding: BoundNotificationSession<Context>, policy: NotificationRuntimePolicy): void;
44
+ /** Enables delivery only after the controller has committed a stable policy. */
45
+ activate?(binding: BoundNotificationSession<Context>): void;
41
46
  }
42
47
 
43
48
  export interface NotificationSessionStatus {
@@ -53,6 +58,13 @@ export interface NotificationSessionReconcileResult {
53
58
  status: NotificationSessionStatus;
54
59
  }
55
60
 
61
+ export interface NotificationRuntimePolicy {
62
+ redact: boolean;
63
+ verbosity: NotificationConfig["verbosity"];
64
+ stream: boolean;
65
+ mode: "provisional" | "committed";
66
+ }
67
+
56
68
  export interface NotificationSessionControllerOptions {
57
69
  /** Gate A result, resolved once by the SDK from the canonical host predicate. */
58
70
  eligible: boolean;
@@ -69,6 +81,8 @@ export interface NotificationSessionControllerOptions {
69
81
  * operation with `isSessionNotificationsEnabled`. Gate C verifies complete
70
82
  * Telegram ownership before a generic endpoint is allowed to start.
71
83
  */
84
+ const MAX_RECONCILE_ATTEMPTS = 3;
85
+
72
86
  export class NotificationSessionController {
73
87
  readonly #eligible: boolean;
74
88
  readonly #getConfig: () => NotificationConfig;
@@ -129,17 +143,12 @@ export class NotificationSessionController {
129
143
  if (this.#blockedRuntimeSessions.delete(previousSessionId)) this.#blockedRuntimeSessions.add(nextSessionId);
130
144
  if (this.#shuttingDownSessions.delete(previousSessionId)) this.#shuttingDownSessions.add(nextSessionId);
131
145
 
132
- const previousOperation = this.#sessionOperations.get(previousSessionId);
133
- if (!previousOperation) return;
134
- const nextOperation = this.#sessionOperations.get(nextSessionId);
135
- const completion = nextOperation ? previousOperation.then(() => nextOperation) : previousOperation;
146
+ // Operations are bound to the identity captured when they were queued. Moving
147
+ // a predecessor operation to the successor makes successor startup wait for
148
+ // predecessor teardown, while that teardown can itself await successor
149
+ // authority: a session-switch deadlock. Leave new-session operations owned by
150
+ // their new key and let the predecessor operation settle independently.
136
151
  this.#sessionOperations.delete(previousSessionId);
137
- this.#sessionOperations.set(nextSessionId, completion);
138
- void completion.then(() => {
139
- if (this.#sessionOperations.get(nextSessionId) === completion) {
140
- this.#sessionOperations.delete(nextSessionId);
141
- }
142
- });
143
152
  }
144
153
 
145
154
  query<Context extends NotificationSessionContext>(context: Context): NotificationSessionStatus {
@@ -176,9 +185,15 @@ export class NotificationSessionController {
176
185
  try {
177
186
  return await this.#enqueue(binding, async () => {
178
187
  const runtime = this.#runtime as NotificationSessionRuntime<Context> | undefined;
179
- const stopped = runtime?.isRunning(binding) ? await runtime.stop(binding) : false;
188
+ this.#refreshPolicy(runtime, binding, { redact: true, verbosity: "lean", stream: false }, "provisional");
189
+ if (runtime?.isRunning(binding)) {
190
+ const stopped = await runtime.stop(binding);
191
+ if (!stopped || runtime.isRunning(binding)) {
192
+ throw new Error("Notification runtime remained active while entering blocked identity state");
193
+ }
194
+ }
180
195
  this.#blockedRuntimeSessions.add(binding.sessionId);
181
- return stopped;
196
+ return true;
182
197
  });
183
198
  } finally {
184
199
  binding.unbind();
@@ -231,47 +246,138 @@ export class NotificationSessionController {
231
246
  async #reconcile<Context extends NotificationSessionContext>(
232
247
  binding: BoundNotificationSession<Context>,
233
248
  ): Promise<NotificationSessionReconcileResult> {
234
- const cfg = this.#getConfig();
235
249
  const runtime = this.#runtime as NotificationSessionRuntime<Context> | undefined;
236
- const status = this.#status(binding, cfg, runtime);
237
- if (!status.effectiveEnabled) {
238
- if (runtime && status.running) await runtime.stop(binding);
239
- return { outcome: status.running ? "stopped" : "disabled", status: this.#status(binding, cfg, runtime) };
240
- }
241
-
242
- if (!runtime) return { outcome: "disabled", status };
243
- if (getCurrentTelegramActivationMarker(cfg)) {
244
- if (runtime.isRunning(binding)) await runtime.stop(binding);
245
- this.#blockedRuntimeSessions.add(binding.sessionId);
246
- return { outcome: "disabled", status: this.#status(binding, cfg, runtime) };
247
- }
248
- if (isTelegramConfigured(cfg)) {
250
+ for (let attempt = 0; attempt < MAX_RECONCILE_ATTEMPTS; attempt++) {
251
+ this.#refreshPolicy(runtime, binding, { redact: true, verbosity: "lean", stream: false }, "provisional");
252
+ let cfg: NotificationConfig;
249
253
  try {
250
- const ensured = await runtime.ensureTelegramDaemon?.(binding);
251
- if (ensured !== "ready") {
252
- if (runtime.isRunning(binding)) await runtime.stop(binding);
253
- this.#blockedRuntimeSessions.add(binding.sessionId);
254
- return { outcome: "disabled", status: this.#status(binding, cfg, runtime) };
255
- }
254
+ cfg = this.#getConfig();
256
255
  } catch {
256
+ if (runtime?.isRunning(binding)) await runtime.stop(binding);
257
+ return { outcome: "failed", status: this.#failClosedStatus(binding, runtime) };
258
+ }
259
+ const status = this.#status(binding, cfg, runtime);
260
+ if (!status.effectiveEnabled) {
261
+ if (runtime && status.running) await runtime.stop(binding);
262
+ if (!this.#isCurrentConfig(cfg)) continue;
263
+ return { outcome: status.running ? "stopped" : "disabled", status: this.#status(binding, cfg, runtime) };
264
+ }
265
+
266
+ if (!runtime) return { outcome: "disabled", status };
267
+ if (getCurrentTelegramActivationMarker(cfg)) {
257
268
  if (runtime.isRunning(binding)) await runtime.stop(binding);
258
269
  this.#blockedRuntimeSessions.add(binding.sessionId);
259
- return { outcome: "failed", status: this.#status(binding, cfg, runtime) };
270
+ if (!this.#isCurrentConfig(cfg)) continue;
271
+ return { outcome: "disabled", status: this.#status(binding, cfg, runtime) };
272
+ }
273
+ if (isTelegramConfigured(cfg)) {
274
+ try {
275
+ const ensured = await runtime.ensureTelegramDaemon?.(binding);
276
+ if (!this.#isCurrentConfig(cfg)) continue;
277
+ if (ensured !== "ready") {
278
+ if (runtime.isRunning(binding)) await runtime.stop(binding);
279
+ this.#blockedRuntimeSessions.add(binding.sessionId);
280
+ return {
281
+ outcome: ensured === "failed" ? "failed" : "disabled",
282
+ status: this.#status(binding, cfg, runtime),
283
+ };
284
+ }
285
+ } catch {
286
+ if (!this.#isCurrentConfig(cfg)) continue;
287
+ if (runtime.isRunning(binding)) await runtime.stop(binding);
288
+ this.#blockedRuntimeSessions.add(binding.sessionId);
289
+ return { outcome: "failed", status: this.#status(binding, cfg, runtime) };
290
+ }
260
291
  }
292
+
293
+ const current = this.#status(binding, cfg, runtime);
294
+ if (!current.effectiveEnabled || !this.#isCurrentConfig(cfg)) continue;
295
+ this.#refreshPolicy(
296
+ runtime,
297
+ binding,
298
+ {
299
+ redact: cfg.redact,
300
+ verbosity: cfg.verbosity,
301
+ stream: isNotificationStreamingEnabled({ cfg, env: this.#env }),
302
+ },
303
+ "committed",
304
+ );
305
+ const outcome = current.running ? "already" : await runtime.start(binding);
306
+ // start() may have created a cold runtime after the first committed refresh.
307
+ // Reapply the stable policy before activate() exposes any notification output.
308
+ this.#refreshPolicy(
309
+ runtime,
310
+ binding,
311
+ {
312
+ redact: cfg.redact,
313
+ verbosity: cfg.verbosity,
314
+ stream: isNotificationStreamingEnabled({ cfg, env: this.#env }),
315
+ },
316
+ "committed",
317
+ );
318
+ if (!this.#isCurrentConfig(cfg)) {
319
+ this.#refreshPolicy(runtime, binding, { redact: true, verbosity: "lean", stream: false }, "provisional");
320
+ const stopped = runtime.isRunning(binding) ? await runtime.stop(binding) : true;
321
+ if (!stopped || runtime.isRunning(binding)) {
322
+ return { outcome: "failed", status: this.#status(binding, this.#getConfig(), runtime) };
323
+ }
324
+ continue;
325
+ }
326
+ runtime.activate?.(binding);
327
+ const afterStart = this.#status(binding, cfg, runtime);
328
+ if (!afterStart.effectiveEnabled) {
329
+ if (afterStart.running) await runtime.stop(binding);
330
+ return {
331
+ outcome: afterStart.running ? "stopped" : "disabled",
332
+ status: this.#status(binding, cfg, runtime),
333
+ };
334
+ }
335
+ return { outcome, status: afterStart };
261
336
  }
262
337
 
263
- const current = this.#status(binding, cfg, runtime);
264
- if (!current.effectiveEnabled) {
265
- if (current.running) await runtime.stop(binding);
266
- return { outcome: current.running ? "stopped" : "disabled", status: this.#status(binding, cfg, runtime) };
338
+ this.#refreshPolicy(runtime, binding, { redact: true, verbosity: "lean", stream: false }, "provisional");
339
+ let cfg: NotificationConfig;
340
+ try {
341
+ cfg = this.#getConfig();
342
+ } catch {
343
+ if (runtime?.isRunning(binding)) await runtime.stop(binding);
344
+ return { outcome: "failed", status: this.#failClosedStatus(binding, runtime) };
267
345
  }
268
- const outcome = current.running ? "already" : await runtime.start(binding);
269
- const afterStart = this.#status(binding, cfg, runtime);
270
- if (!afterStart.effectiveEnabled) {
271
- if (afterStart.running) await runtime.stop(binding);
272
- return { outcome: afterStart.running ? "stopped" : "disabled", status: this.#status(binding, cfg, runtime) };
346
+ const status = this.#status(binding, cfg, runtime);
347
+ if (!runtime || !status.running) return { outcome: "disabled", status };
348
+ const stopped = await runtime.stop(binding);
349
+ const finalStatus = this.#status(binding, cfg, runtime);
350
+ return { outcome: stopped && !finalStatus.running ? "stopped" : "failed", status: finalStatus };
351
+ }
352
+
353
+ #failClosedStatus<Context extends NotificationSessionContext>(
354
+ binding: BoundNotificationSession<Context>,
355
+ runtime: NotificationSessionRuntime<Context> | undefined,
356
+ ): NotificationSessionStatus {
357
+ return {
358
+ eligible: this.#eligible,
359
+ locallyEnabled: false,
360
+ effectiveEnabled: false,
361
+ running: runtime?.isRunning(binding) ?? false,
362
+ environment: "off",
363
+ };
364
+ }
365
+
366
+ #refreshPolicy<Context extends NotificationSessionContext>(
367
+ runtime: NotificationSessionRuntime<Context> | undefined,
368
+ binding: BoundNotificationSession<Context>,
369
+ policy: Omit<NotificationRuntimePolicy, "mode">,
370
+ mode: NotificationRuntimePolicy["mode"],
371
+ ): void {
372
+ runtime?.refreshPolicy?.(binding, { ...policy, mode });
373
+ }
374
+
375
+ #isCurrentConfig(cfg: NotificationConfig): boolean {
376
+ try {
377
+ return Bun.deepEquals(this.#getConfig(), cfg, true);
378
+ } catch {
379
+ return false;
273
380
  }
274
- return { outcome, status: this.#status(binding, cfg, runtime) };
275
381
  }
276
382
 
277
383
  #enqueue<Context extends NotificationSessionContext, Result>(
@@ -81,6 +81,8 @@ export function createLightweightDaemonSettings(input: {
81
81
  return snapshot.telegram.chatId;
82
82
  case "notifications.telegram.btw.enabled":
83
83
  return snapshot.telegram.btw.enabled;
84
+ case "notifications.telegram.streaming.enabled":
85
+ return snapshot.telegram.streaming.enabled;
84
86
  case "notifications.discord.botToken":
85
87
  return snapshot.discord.botToken;
86
88
  case "notifications.discord.applicationId":
@@ -179,6 +181,22 @@ function ownerProcessIsAlive(ownerId: string, deps: RunDaemonInternalDeps): bool
179
181
  return (deps.pidAlive ?? defaultPidAlive)(ownerPid);
180
182
  }
181
183
 
184
+ /** Creates owner-fenced daemon control hooks for the CLI lifecycle boundary. */
185
+ export function createDaemonControlHooks(settings: Settings) {
186
+ return {
187
+ shouldStop: async (owner: string) => {
188
+ const req = await readTelegramControlRequest(settings);
189
+ return Boolean(req && (!req.ownerId || req.ownerId === owner));
190
+ },
191
+ clear: async (owner: string) => {
192
+ const req = await readTelegramControlRequest(settings);
193
+ // Only clear a request that targets this daemon owner, so an exiting
194
+ // daemon never erases a newer request meant for a different owner.
195
+ if (req && (!req.ownerId || req.ownerId === owner)) await clearTelegramControlRequest(settings, req.requestId);
196
+ },
197
+ };
198
+ }
199
+
182
200
  export async function runDaemonSmoke(opts: { agentDir?: string } = {}): Promise<void> {
183
201
  const agentDir = opts.agentDir ?? fs.mkdtempSync(path.join(process.cwd(), ".telegram-daemon-smoke-"));
184
202
  const settings = createLightweightDaemonSettings({ agentDir, rawConfig: {} });
@@ -215,23 +233,11 @@ export async function runDaemonInternal(argv: string[], deps: RunDaemonInternalD
215
233
  idleTimeoutMs: cfg.idleTimeoutMs,
216
234
  rich: cfg.rich,
217
235
  richDraft: cfg.richDraft,
236
+ toolActivity: cfg.toolActivity,
218
237
  topics: cfg.topics,
219
238
  btw: cfg.btw,
220
239
  pid: deps.processPid ?? process.pid,
221
- control: {
222
- shouldStop: async owner => {
223
- const req = await readTelegramControlRequest(settings as Settings);
224
- return Boolean(req && (!req.ownerId || req.ownerId === owner));
225
- },
226
- clear: async owner => {
227
- const req = await readTelegramControlRequest(settings as Settings);
228
- // Only clear a request that targets this daemon owner, so an exiting
229
- // daemon never erases a newer request meant for a different owner.
230
- if (req && (!req.ownerId || req.ownerId === owner)) {
231
- await clearTelegramControlRequest(settings as Settings, req.requestId);
232
- }
233
- },
234
- },
240
+ control: createDaemonControlHooks(settings as Settings),
235
241
  });
236
242
  // Signals are a process concern: install them at the daemon-internal boundary,
237
243
  // not inside the embeddable daemon class. SIGTERM is the reload wakeup path.
@@ -13,7 +13,15 @@ export const NOTIFICATION_PROTOCOL_VERSION = 3;
13
13
  * freshly-upgraded host must still recognize an older, still-live daemon that
14
14
  * predates capability-gated frame enforcement and trigger a reload. Bump this
15
15
  * on every daemon-behavior change independent of the wire version.
16
- * The current development baseline already includes #2299's generation 4;
17
- * this behavior change therefore advances the operational generation to 5.
16
+ * The current development baseline already includes #2299's generation 4,
17
+ * incarnation fencing in generation 5, owner-lock authority in generation 6,
18
+ * identity-atomic transition markers in generation 7, stable signaling plus
19
+ * tri-state foreign-owner provenance in generation 8, retained managed
20
+ * filesystem authority changes in generation 9, SDK-startup auto-reclaim of a
21
+ * confirmed-dead owner's lock in generation 10, legacy stopped-tombstone
22
+ * reclamation in generation 11, force-escalated SIGKILL of an unresponsive
23
+ * older-generation owner during automatic generation-upgrade reload in
24
+ * generation 12, and restored macOS daemon signaling (kill(2) with a start-time
25
+ * incarnation recheck, replacing the darwin no-op) in generation 13.
18
26
  */
19
- export const DAEMON_GENERATION = 5;
27
+ export const DAEMON_GENERATION = 13;