@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.
- package/CHANGELOG.md +23 -0
- package/dist/types/cli/notify-cli.d.ts +5 -1
- package/dist/types/config/settings-schema.d.ts +31 -1
- package/dist/types/export/html/template.generated.d.ts +1 -1
- package/dist/types/gjc-runtime/deep-interview-state.d.ts +28 -0
- package/dist/types/gjc-runtime/launch-tmux.d.ts +3 -0
- package/dist/types/gjc-runtime/managed-owner-admission.d.ts +42 -0
- package/dist/types/gjc-runtime/managed-owner-supervisor.d.ts +41 -0
- package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +9 -0
- package/dist/types/gjc-runtime/ultragoal-owner-loss-recovery.d.ts +94 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +1 -0
- package/dist/types/harness-control-plane/owner.d.ts +3 -1
- package/dist/types/internal-urls/local-protocol.d.ts +18 -0
- package/dist/types/main.d.ts +2 -2
- package/dist/types/modes/components/ansi-display-validator.d.ts +2 -0
- package/dist/types/modes/components/notifications-settings-editor.d.ts +4 -0
- package/dist/types/modes/components/session-observer-overlay.d.ts +3 -1
- package/dist/types/modes/components/settings-selector.d.ts +2 -0
- package/dist/types/modes/components/tool-transcript-format.d.ts +58 -0
- package/dist/types/modes/components/transcript-viewer-overlay.d.ts +3 -0
- package/dist/types/modes/controllers/goal-mode-controller.d.ts +8 -1
- package/dist/types/modes/controllers/selector-controller.d.ts +15 -0
- package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +3 -0
- package/dist/types/modes/shared/agent-wire/workflow-gate-types.d.ts +16 -0
- package/dist/types/plan-mode/approved-plan.d.ts +1 -0
- package/dist/types/runtime-mcp/manager.d.ts +2 -0
- package/dist/types/runtime-mcp/oauth-discovery.d.ts +12 -1
- package/dist/types/sdk/bus/chat-daemon-cli.d.ts +3 -1
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +42 -4
- package/dist/types/sdk/bus/config-commands.d.ts +8 -0
- package/dist/types/sdk/bus/config.d.ts +22 -0
- package/dist/types/sdk/bus/index.d.ts +162 -2
- package/dist/types/sdk/bus/notification-service.d.ts +71 -8
- package/dist/types/sdk/bus/session-control.d.ts +11 -8
- package/dist/types/sdk/bus/telegram-daemon-cli.d.ts +5 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +11 -3
- package/dist/types/sdk/bus/telegram-daemon-control.d.ts +23 -4
- package/dist/types/sdk/bus/telegram-daemon.d.ts +214 -20
- package/dist/types/sdk/session.d.ts +16 -1
- package/dist/types/secrets/index.d.ts +4 -2
- package/dist/types/secrets/obfuscator.d.ts +1 -1
- package/dist/types/session/agent-session.d.ts +31 -2
- package/dist/types/session/artifacts.d.ts +30 -8
- package/dist/types/session/internal/managed-session-scope.d.ts +7 -0
- package/dist/types/session/internal/managed-session-storage.d.ts +63 -6
- package/dist/types/session/session-manager.d.ts +98 -18
- package/dist/types/session/session-storage.d.ts +27 -0
- package/dist/types/session/streaming-output.d.ts +4 -0
- package/dist/types/session/yield-queue.d.ts +8 -0
- package/dist/types/task/executor.d.ts +9 -0
- package/dist/types/tool-discovery/tool-index.d.ts +2 -0
- package/dist/types/tools/ask.d.ts +48 -0
- package/dist/types/tools/bash-skill-urls.d.ts +2 -2
- package/dist/types/tools/index.d.ts +4 -0
- package/package.json +9 -9
- package/scripts/generate-sdk-operation-inventory.ts +3 -0
- package/src/cli/notify-cli.ts +40 -18
- package/src/commands/sdk.ts +7 -2
- package/src/config/settings-schema.ts +31 -1
- package/src/daemon/runtime.ts +2 -2
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +70 -49
- package/src/defaults/gjc/skills/ralplan/SKILL.md +0 -6
- package/src/defaults/gjc/skills/team/SKILL.md +7 -9
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +0 -2
- package/src/eval/py/index.ts +2 -2
- package/src/export/html/template.generated.ts +1 -1
- package/src/extensibility/plugins/marketplace/manager.ts +94 -10
- package/src/gjc-runtime/deep-interview-recorder.ts +7 -0
- package/src/gjc-runtime/deep-interview-runtime.ts +10 -0
- package/src/gjc-runtime/deep-interview-state.ts +127 -3
- package/src/gjc-runtime/launch-tmux.ts +61 -6
- package/src/gjc-runtime/managed-owner-admission.ts +352 -0
- package/src/gjc-runtime/managed-owner-supervisor.ts +162 -0
- package/src/gjc-runtime/state-runtime.ts +38 -12
- package/src/gjc-runtime/team-runtime.ts +1 -1
- package/src/gjc-runtime/tmux-owner-isolation.ts +117 -0
- package/src/gjc-runtime/ultragoal-owner-loss-recovery.ts +328 -0
- package/src/gjc-runtime/ultragoal-runtime.ts +14 -0
- package/src/harness-control-plane/owner.ts +59 -3
- package/src/hindsight/client.ts +65 -2
- package/src/internal-urls/agent-protocol.ts +76 -5
- package/src/internal-urls/docs-index.generated.ts +8 -7
- package/src/internal-urls/local-protocol.ts +416 -18
- package/src/lsp/index.ts +4 -88
- package/src/main.ts +36 -12
- package/src/modes/acp/acp-agent.ts +8 -0
- package/src/modes/components/ansi-display-validator.ts +54 -0
- package/src/modes/components/notifications-settings-editor.ts +26 -2
- package/src/modes/components/session-observer-overlay.ts +40 -24
- package/src/modes/components/settings-selector.ts +3 -1
- package/src/modes/components/tool-transcript-format.ts +321 -0
- package/src/modes/components/transcript-viewer-overlay.ts +39 -17
- package/src/modes/controllers/command-controller.ts +15 -0
- package/src/modes/controllers/extension-ui-controller.ts +14 -2
- package/src/modes/controllers/goal-mode-controller.ts +20 -1
- package/src/modes/controllers/plan-mode-controller.ts +2 -0
- package/src/modes/controllers/runtime-mcp-command-controller.ts +3 -0
- package/src/modes/controllers/selector-controller.ts +70 -8
- package/src/modes/interactive-mode.ts +1 -0
- package/src/modes/runtime-init.ts +16 -2
- package/src/modes/shared/agent-wire/deep-interview-gate.ts +31 -1
- package/src/modes/shared/agent-wire/workflow-gate-broker.ts +17 -1
- package/src/modes/shared/agent-wire/workflow-gate-schema.ts +4 -2
- package/src/modes/shared/agent-wire/workflow-gate-types.ts +120 -3
- package/src/plan-mode/approved-plan.ts +3 -1
- package/src/prompts/agents/architect.md +1 -0
- package/src/prompts/agents/critic.md +1 -0
- package/src/prompts/system/custom-system-prompt.md +1 -1
- package/src/prompts/system/system-prompt.md +2 -1
- package/src/prompts/tools/lsp.md +3 -3
- package/src/runtime-mcp/manager.ts +16 -1
- package/src/runtime-mcp/oauth-discovery.ts +116 -26
- package/src/sdk/broker/session-index.ts +122 -64
- package/src/sdk/bus/chat-daemon-cli.ts +72 -10
- package/src/sdk/bus/chat-daemon-control.ts +538 -130
- package/src/sdk/bus/chat-daemon-runtime.ts +1 -0
- package/src/sdk/bus/config-commands.ts +22 -0
- package/src/sdk/bus/config.ts +37 -0
- package/src/sdk/bus/index.ts +388 -94
- package/src/sdk/bus/lifecycle-control-runtime.ts +53 -3
- package/src/sdk/bus/notification-service.ts +284 -62
- package/src/sdk/bus/recent-activity.ts +14 -5
- package/src/sdk/bus/session-control.ts +150 -44
- package/src/sdk/bus/telegram-daemon-cli.ts +20 -14
- package/src/sdk/bus/telegram-daemon-contract.ts +11 -3
- package/src/sdk/bus/telegram-daemon-control.ts +312 -76
- package/src/sdk/bus/telegram-daemon.ts +1916 -285
- package/src/sdk/host/control/dispatch.ts +14 -5
- package/src/sdk/protocol/operation-inventory.generated.json +29 -0
- package/src/sdk/session.ts +155 -27
- package/src/secrets/index.ts +53 -10
- package/src/secrets/obfuscator.ts +21 -18
- package/src/secrets/regex.ts +2 -0
- package/src/session/agent-session.ts +1447 -852
- package/src/session/artifacts.ts +188 -23
- package/src/session/context-estimation.ts +7 -2
- package/src/session/internal/managed-session-scope.ts +200 -38
- package/src/session/internal/managed-session-storage.ts +1152 -46
- package/src/session/session-manager.ts +1121 -148
- package/src/session/session-storage.ts +109 -11
- package/src/session/streaming-output.ts +6 -0
- package/src/session/yield-queue.ts +20 -0
- package/src/slash-commands/builtin-registry.ts +36 -0
- package/src/task/executor.ts +71 -26
- package/src/task/index.ts +63 -20
- package/src/tool-discovery/tool-index.ts +18 -0
- package/src/tools/ask-answer-registry.ts +15 -1
- package/src/tools/ask.ts +129 -32
- package/src/tools/bash-skill-urls.ts +14 -3
- package/src/tools/bash.ts +2 -2
- package/src/tools/index.ts +4 -0
- package/src/tools/plan-mode-guard.ts +1 -0
- package/src/tools/sqlite-reader.ts +3 -2
- 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
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
237
|
-
|
|
238
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
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
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
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
|
-
*
|
|
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 =
|
|
27
|
+
export const DAEMON_GENERATION = 13;
|