@gajae-code/coding-agent 0.11.3 → 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.
- package/CHANGELOG.md +47 -0
- package/dist/types/cli/notify-cli.d.ts +5 -1
- package/dist/types/cli/worktree-cli.d.ts +8 -25
- package/dist/types/commands/gc.d.ts +4 -0
- package/dist/types/commands/worktree.d.ts +7 -32
- package/dist/types/config/settings-schema.d.ts +20 -0
- 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/gc-runtime.d.ts +9 -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/state-argv.d.ts +38 -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/custom-editor.d.ts +8 -3
- 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 +77 -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/input-controller.d.ts +8 -2
- 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/broker/broker.d.ts +1 -0
- package/dist/types/sdk/broker/discovery.d.ts +10 -0
- package/dist/types/sdk/broker/ensure.d.ts +24 -1
- package/dist/types/sdk/broker/internal.d.ts +3 -0
- package/dist/types/sdk/broker/session-index.d.ts +12 -0
- package/dist/types/sdk/broker/state-version.d.ts +2 -1
- package/dist/types/sdk/bus/chat-daemon-cli.d.ts +3 -1
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +39 -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 +6 -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 +88 -18
- package/dist/types/session/session-storage.d.ts +27 -0
- package/dist/types/session/streaming-output.d.ts +4 -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 +87 -12
- package/dist/types/tools/bash-skill-urls.d.ts +2 -2
- package/dist/types/tools/browser/screenshot-format.d.ts +8 -0
- package/dist/types/tools/browser/tab-protocol.d.ts +11 -0
- package/dist/types/tools/browser/tab-supervisor.d.ts +1 -1
- package/dist/types/tools/browser/tab-worker-path-resolver.d.ts +2 -0
- package/dist/types/tools/browser/tab-worker-smoke.d.ts +16 -0
- package/dist/types/tools/index.d.ts +4 -0
- package/dist/types/tools/path-utils.d.ts +9 -14
- package/dist/types/tools/render-utils.d.ts +1 -8
- package/dist/types/tools/resource-gc.d.ts +14 -0
- package/dist/types/utils/image-loading.d.ts +23 -0
- package/dist/types/utils/image-resize.d.ts +2 -0
- package/dist/types/utils/pasted-image-loading.d.ts +39 -0
- package/dist/types/utils/pasted-image-path.d.ts +21 -19
- package/dist/types/web/scrapers/html-to-markdown.d.ts +5 -0
- package/dist/types/web/scrapers/types.d.ts +1 -5
- package/package.json +25 -9
- package/scripts/generate-sdk-operation-inventory.ts +2 -0
- package/scripts/run-threat-model-check.ts +60 -0
- package/src/cli/notify-cli.ts +40 -18
- package/src/cli/worktree-cli.ts +9 -287
- package/src/cli.ts +10 -9
- package/src/commands/gc.ts +11 -1
- package/src/commands/sdk.ts +10 -4
- package/src/commands/worktree.ts +10 -53
- package/src/config/model-profiles.ts +15 -15
- package/src/config/settings-schema.ts +17 -0
- 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/js/shared/runtime.ts +1 -1
- 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/gc-render.ts +19 -1
- package/src/gjc-runtime/gc-runtime.ts +68 -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-argv.ts +292 -0
- package/src/gjc-runtime/state-runtime.ts +80 -222
- 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 +7 -6
- package/src/internal-urls/local-protocol.ts +416 -18
- package/src/lsp/index.ts +4 -88
- package/src/main.ts +37 -13
- package/src/modes/acp/acp-agent.ts +8 -0
- package/src/modes/components/ansi-display-validator.ts +54 -0
- package/src/modes/components/custom-editor.ts +103 -32
- package/src/modes/components/custom-provider-wizard.ts +26 -1
- package/src/modes/components/notifications-settings-editor.ts +26 -2
- package/src/modes/components/session-observer-overlay.ts +40 -37
- package/src/modes/components/settings-selector.ts +3 -1
- package/src/modes/components/tool-transcript-format.ts +393 -0
- package/src/modes/components/transcript-viewer-overlay.ts +63 -1
- package/src/modes/controllers/goal-mode-controller.ts +20 -1
- package/src/modes/controllers/input-controller.ts +98 -37
- 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 +71 -9
- package/src/modes/interactive-mode.ts +14 -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/broker.ts +139 -32
- package/src/sdk/broker/discovery.ts +125 -0
- package/src/sdk/broker/ensure.ts +103 -19
- package/src/sdk/broker/internal.ts +10 -0
- package/src/sdk/broker/session-index.ts +309 -38
- package/src/sdk/broker/state-version.ts +5 -2
- package/src/sdk/bus/chat-daemon-cli.ts +72 -10
- package/src/sdk/bus/chat-daemon-control.ts +526 -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 +384 -92
- 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 +6 -3
- package/src/sdk/bus/telegram-daemon-control.ts +294 -76
- package/src/sdk/bus/telegram-daemon.ts +1842 -281
- package/src/sdk/protocol/operation-inventory.generated.json +11 -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 +495 -253
- package/src/session/artifacts.ts +188 -23
- package/src/session/context-estimation.ts +7 -2
- package/src/session/internal/managed-session-scope.ts +215 -39
- package/src/session/internal/managed-session-storage.ts +1152 -46
- package/src/session/session-manager.ts +1055 -148
- package/src/session/session-storage.ts +109 -11
- package/src/session/streaming-output.ts +6 -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.ts +329 -57
- package/src/tools/bash-allowed-prefixes.ts +79 -29
- package/src/tools/bash-skill-urls.ts +14 -3
- package/src/tools/bash.ts +2 -2
- package/src/tools/browser/launch.ts +3 -1
- package/src/tools/browser/readable.ts +1 -1
- package/src/tools/browser/screenshot-format.ts +34 -0
- package/src/tools/browser/tab-protocol.ts +3 -0
- package/src/tools/browser/tab-supervisor.ts +125 -122
- package/src/tools/browser/tab-worker-entry.ts +33 -14
- package/src/tools/browser/tab-worker-path-resolver.ts +67 -0
- package/src/tools/browser/tab-worker-smoke.ts +87 -0
- package/src/tools/browser/tab-worker.ts +7 -5
- package/src/tools/index.ts +4 -0
- package/src/tools/path-utils.ts +42 -35
- package/src/tools/plan-mode-guard.ts +1 -0
- package/src/tools/read.ts +7 -1
- package/src/tools/render-utils.ts +1 -27
- package/src/tools/resource-gc.ts +123 -34
- package/src/tools/sqlite-reader.ts +3 -2
- package/src/tools/subagent.ts +2 -0
- package/src/utils/image-loading.ts +85 -34
- package/src/utils/image-resize.ts +16 -9
- package/src/utils/pasted-image-loading.ts +514 -0
- package/src/utils/pasted-image-path.ts +223 -98
- package/src/web/scrapers/html-to-markdown.ts +68 -0
- package/src/web/scrapers/readthedocs.ts +1 -1
- package/src/web/scrapers/types.ts +1 -68
- package/src/web/search/providers/openai-compatible.ts +76 -23
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { WriteFileOptions } from "node:fs";
|
|
1
2
|
import type { Settings } from "../../config/settings";
|
|
2
3
|
/**
|
|
3
4
|
* Strip secrets from a human-facing diagnostic string. Redacts the configured
|
|
@@ -21,7 +22,18 @@ export interface NotificationExactUnlinkResult {
|
|
|
21
22
|
ok: boolean;
|
|
22
23
|
code?: string;
|
|
23
24
|
detachedPath?: string;
|
|
24
|
-
|
|
25
|
+
/** A live publisher successor retained after an exact-unlink race. */
|
|
26
|
+
retainedSuccessorPath?: string;
|
|
27
|
+
/** An internal exchange placeholder whose verified cleanup failed. */
|
|
28
|
+
retainedPlaceholderPath?: string;
|
|
29
|
+
/** A cleanup entry whose identity could not be verified after a race. */
|
|
30
|
+
retainedUnknownPath?: string;
|
|
31
|
+
}
|
|
32
|
+
/** Read one regular file together with the identity required for exact removal. */
|
|
33
|
+
export declare function readNotificationEndpointFile(file: string): Promise<NotificationEndpointFile>;
|
|
34
|
+
/** Bump when the native exact-deletion identity contract changes. */
|
|
35
|
+
export declare const NATIVE_PATH_IDENTITY_CONTRACT_VERSION = 1;
|
|
36
|
+
export declare function exactUnlinkNotificationFile(file: string, identity: NotificationEndpointFileIdentity, quarantineName: string): NotificationExactUnlinkResult;
|
|
25
37
|
/** Minimal filesystem surface the service needs; injectable for tests. */
|
|
26
38
|
export interface NotificationServiceFs {
|
|
27
39
|
readdir(dir: string): Promise<string[]>;
|
|
@@ -29,13 +41,10 @@ export interface NotificationServiceFs {
|
|
|
29
41
|
readEndpointFile(file: string): Promise<NotificationEndpointFile>;
|
|
30
42
|
exactUnlink(file: string, identity: NotificationEndpointFileIdentity): Promise<NotificationExactUnlinkResult>;
|
|
31
43
|
unlink(file: string): Promise<void>;
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
* recovery and a concurrent daemon takeover are mutually exclusive.
|
|
37
|
-
*/
|
|
38
|
-
createExclusive(file: string): Promise<boolean>;
|
|
44
|
+
writeFile?(file: string, data: string, opts?: WriteFileOptions): Promise<void>;
|
|
45
|
+
stat?(file: string): Promise<{
|
|
46
|
+
mtimeMs: number;
|
|
47
|
+
}>;
|
|
39
48
|
}
|
|
40
49
|
/** Injectable dependencies shared across service operations. */
|
|
41
50
|
export interface NotificationServiceDeps {
|
|
@@ -143,6 +152,12 @@ export interface NotificationRecoveryReport {
|
|
|
143
152
|
endpointsKept: number;
|
|
144
153
|
endpointsUnreadable: number;
|
|
145
154
|
endpointsDetached?: string[];
|
|
155
|
+
/** Successor paths retained after an exact-unlink race, distinct from stale quarantines. */
|
|
156
|
+
endpointsRetainedSuccessors?: string[];
|
|
157
|
+
/** Internal exchange placeholders retained after verified cleanup failure. */
|
|
158
|
+
endpointsRetainedPlaceholders?: string[];
|
|
159
|
+
/** Cleanup entries retained with unverified or mismatching identity. */
|
|
160
|
+
endpointsRetainedUnknown?: string[];
|
|
146
161
|
daemon: {
|
|
147
162
|
action: DaemonRecoveryAction;
|
|
148
163
|
detail: string;
|
|
@@ -155,6 +170,53 @@ export interface RecoveryOptions {
|
|
|
155
170
|
stateRoot?: string;
|
|
156
171
|
deps?: NotificationServiceDeps;
|
|
157
172
|
}
|
|
173
|
+
export interface DaemonTransitionLock {
|
|
174
|
+
pid: number;
|
|
175
|
+
incarnation: string;
|
|
176
|
+
createdAt: number;
|
|
177
|
+
/** Unique fencing generation for this particular transition acquisition. */
|
|
178
|
+
token: string;
|
|
179
|
+
}
|
|
180
|
+
type TransitionMarkerFs = {
|
|
181
|
+
readFile(file: string, encoding: "utf8"): Promise<string>;
|
|
182
|
+
writeFile?(file: string, data: string, opts?: WriteFileOptions): Promise<void>;
|
|
183
|
+
readEndpointFile?(file: string): Promise<NotificationEndpointFile>;
|
|
184
|
+
exactUnlink?(file: string, identity: NotificationEndpointFileIdentity): Promise<NotificationExactUnlinkResult>;
|
|
185
|
+
};
|
|
186
|
+
/** True only while the exact transition acquisition still occupies the marker path. */
|
|
187
|
+
export declare function daemonTransitionLockIsHeld(input: {
|
|
188
|
+
fs: Pick<TransitionMarkerFs, "readFile" | "readEndpointFile">;
|
|
189
|
+
path: string;
|
|
190
|
+
lock: DaemonTransitionLock;
|
|
191
|
+
}): Promise<boolean>;
|
|
192
|
+
/** Removes only the caller's exact marker through the identity-bound detach primitive. */
|
|
193
|
+
export declare function releaseDaemonTransitionLock(input: {
|
|
194
|
+
fs: TransitionMarkerFs;
|
|
195
|
+
path: string;
|
|
196
|
+
lock: DaemonTransitionLock;
|
|
197
|
+
}): Promise<boolean>;
|
|
198
|
+
/**
|
|
199
|
+
* Acquire the daemon lifecycle transition lock using durable owner metadata.
|
|
200
|
+
* The full marker is published in the single O_EXCL write which reserves it;
|
|
201
|
+
* canonical markers are detached only through their captured filesystem identity.
|
|
202
|
+
*
|
|
203
|
+
* Malformed and empty markers deliberately remain blocked regardless of age. They
|
|
204
|
+
* have no owner provenance, so reclaiming them could detach a generation-6 empty
|
|
205
|
+
* reservation while its paused legacy writer can still resume. Operators must
|
|
206
|
+
* manually clean up such legacy debris after confirming no legacy process remains.
|
|
207
|
+
*/
|
|
208
|
+
export declare function acquireDaemonTransitionLock(input: {
|
|
209
|
+
fs: TransitionMarkerFs;
|
|
210
|
+
path: string;
|
|
211
|
+
pid: number;
|
|
212
|
+
pidAlive: (pid: number) => boolean;
|
|
213
|
+
pidIncarnation: (pid: number) => string | undefined;
|
|
214
|
+
now?: () => number;
|
|
215
|
+
sleep?: (ms: number) => Promise<void>;
|
|
216
|
+
retries?: number;
|
|
217
|
+
retryDelayMs?: number;
|
|
218
|
+
randomToken?: () => string;
|
|
219
|
+
}): Promise<DaemonTransitionLock | undefined>;
|
|
158
220
|
/**
|
|
159
221
|
* Ownership-protected cleanup. Removes only DEAD-owner artifacts:
|
|
160
222
|
* per-session endpoint files with positive proof of death (a stale tombstone or
|
|
@@ -168,3 +230,4 @@ export interface RecoveryOptions {
|
|
|
168
230
|
export declare function recoverNotifications(opts: RecoveryOptions): Promise<NotificationRecoveryReport>;
|
|
169
231
|
/** Render a recovery report as human-readable lines (no secrets). */
|
|
170
232
|
export declare function formatNotificationRecoveryReport(report: NotificationRecoveryReport): string;
|
|
233
|
+
export {};
|
|
@@ -14,7 +14,7 @@ export interface BoundNotificationSession<Context extends NotificationSessionCon
|
|
|
14
14
|
unbind(): void;
|
|
15
15
|
}
|
|
16
16
|
export type NotificationEndpointStartResult = "started" | "already" | "disabled" | "failed";
|
|
17
|
-
export type TelegramDaemonPreflightResult = "ready" | "blocked_identity";
|
|
17
|
+
export type TelegramDaemonPreflightResult = "ready" | "blocked_identity" | "failed";
|
|
18
18
|
/**
|
|
19
19
|
* The endpoint implementation is deliberately injected. The controller owns
|
|
20
20
|
* policy and session-local state while the extension continues to own its
|
|
@@ -29,6 +29,10 @@ export interface NotificationSessionRuntime<Context extends NotificationSessionC
|
|
|
29
29
|
* emit a frame. `blocked_identity` is fail-closed and starts nothing.
|
|
30
30
|
*/
|
|
31
31
|
ensureTelegramDaemon?(binding: BoundNotificationSession<Context>): Promise<TelegramDaemonPreflightResult>;
|
|
32
|
+
/** Refresh mutable delivery policy from the same configuration snapshot used for reconciliation. */
|
|
33
|
+
refreshPolicy?(binding: BoundNotificationSession<Context>, policy: NotificationRuntimePolicy): void;
|
|
34
|
+
/** Enables delivery only after the controller has committed a stable policy. */
|
|
35
|
+
activate?(binding: BoundNotificationSession<Context>): void;
|
|
32
36
|
}
|
|
33
37
|
export interface NotificationSessionStatus {
|
|
34
38
|
eligible: boolean;
|
|
@@ -41,6 +45,12 @@ export interface NotificationSessionReconcileResult {
|
|
|
41
45
|
outcome: NotificationEndpointStartResult | "stopped";
|
|
42
46
|
status: NotificationSessionStatus;
|
|
43
47
|
}
|
|
48
|
+
export interface NotificationRuntimePolicy {
|
|
49
|
+
redact: boolean;
|
|
50
|
+
verbosity: NotificationConfig["verbosity"];
|
|
51
|
+
stream: boolean;
|
|
52
|
+
mode: "provisional" | "committed";
|
|
53
|
+
}
|
|
44
54
|
export interface NotificationSessionControllerOptions {
|
|
45
55
|
/** Gate A result, resolved once by the SDK from the canonical host predicate. */
|
|
46
56
|
eligible: boolean;
|
|
@@ -49,13 +59,6 @@ export interface NotificationSessionControllerOptions {
|
|
|
49
59
|
/** Kept as a reference so test and embedding hosts can supply their own environment. */
|
|
50
60
|
env?: NodeJS.ProcessEnv;
|
|
51
61
|
}
|
|
52
|
-
/**
|
|
53
|
-
* Shared owner of notification session policy.
|
|
54
|
-
*
|
|
55
|
-
* Gate A is captured at creation. Gate B is evaluated for each session
|
|
56
|
-
* operation with `isSessionNotificationsEnabled`. Gate C verifies complete
|
|
57
|
-
* Telegram ownership before a generic endpoint is allowed to start.
|
|
58
|
-
*/
|
|
59
62
|
export declare class NotificationSessionController {
|
|
60
63
|
#private;
|
|
61
64
|
constructor(options: NotificationSessionControllerOptions);
|
|
@@ -30,6 +30,11 @@ export declare function createLightweightDaemonSettings(input: {
|
|
|
30
30
|
}): LightweightDaemonSettings;
|
|
31
31
|
export declare function loadLightweightDaemonSettings(agentDir: string): Promise<LightweightDaemonSettings>;
|
|
32
32
|
export declare function ownerPidFromOwnerId(ownerId: string): number | undefined;
|
|
33
|
+
/** Creates owner-fenced daemon control hooks for the CLI lifecycle boundary. */
|
|
34
|
+
export declare function createDaemonControlHooks(settings: Settings): {
|
|
35
|
+
shouldStop: (owner: string) => Promise<boolean>;
|
|
36
|
+
clear: (owner: string) => Promise<void>;
|
|
37
|
+
};
|
|
33
38
|
export declare function runDaemonSmoke(opts?: {
|
|
34
39
|
agentDir?: string;
|
|
35
40
|
}): Promise<void>;
|
|
@@ -11,7 +11,10 @@ export declare const NOTIFICATION_PROTOCOL_VERSION = 3;
|
|
|
11
11
|
* freshly-upgraded host must still recognize an older, still-live daemon that
|
|
12
12
|
* predates capability-gated frame enforcement and trigger a reload. Bump this
|
|
13
13
|
* on every daemon-behavior change independent of the wire version.
|
|
14
|
-
* The current development baseline already includes #2299's generation 4
|
|
15
|
-
*
|
|
14
|
+
* The current development baseline already includes #2299's generation 4,
|
|
15
|
+
* incarnation fencing in generation 5, owner-lock authority in generation 6,
|
|
16
|
+
* identity-atomic transition markers in generation 7, stable signaling plus
|
|
17
|
+
* tri-state foreign-owner provenance in generation 8, and retained managed
|
|
18
|
+
* filesystem authority changes in generation 9.
|
|
16
19
|
*/
|
|
17
|
-
export declare const DAEMON_GENERATION =
|
|
20
|
+
export declare const DAEMON_GENERATION = 9;
|
|
@@ -22,11 +22,18 @@ export declare function telegramControlRequestPath(agentDir: string): string;
|
|
|
22
22
|
export declare function readTelegramControlRequest(settings: Settings, fsImpl?: TelegramDaemonFs): Promise<TelegramDaemonControlRequest | undefined>;
|
|
23
23
|
export declare function writeTelegramControlRequest(settings: Settings, request: TelegramDaemonControlRequest, fsImpl?: TelegramDaemonFs): Promise<void>;
|
|
24
24
|
export declare function clearTelegramControlRequest(settings: Settings, requestId?: string, fsImpl?: TelegramDaemonFs): Promise<void>;
|
|
25
|
+
export interface DaemonProcessReference {
|
|
26
|
+
incarnation: string;
|
|
27
|
+
signalRoot(signal: NodeJS.Signals): void;
|
|
28
|
+
}
|
|
25
29
|
export interface TelegramDaemonControlDeps {
|
|
26
30
|
fs?: TelegramDaemonFs;
|
|
27
31
|
now?: () => number;
|
|
28
32
|
pidAlive?: (pid: number) => boolean;
|
|
29
|
-
|
|
33
|
+
processReference?: (pid: number) => DaemonProcessReference | undefined;
|
|
34
|
+
pidIncarnation?: (pid: number) => string | undefined;
|
|
35
|
+
/** Test seam for platform-specific default stable-process authority. */
|
|
36
|
+
platform?: NodeJS.Platform;
|
|
30
37
|
spawn?: TelegramDaemonDeps["spawn"];
|
|
31
38
|
execPath?: string;
|
|
32
39
|
/**
|
|
@@ -40,20 +47,30 @@ export interface TelegramDaemonControlDeps {
|
|
|
40
47
|
randomId?: () => string;
|
|
41
48
|
sleep?: (ms: number) => Promise<void>;
|
|
42
49
|
waitStepMs?: number;
|
|
50
|
+
/** Bounded startup-readiness timeout; injectable for deterministic controller tests. */
|
|
51
|
+
readinessTimeoutMs?: number;
|
|
43
52
|
}
|
|
53
|
+
export type TelegramGenerationReloadResult = {
|
|
54
|
+
outcome: "ready";
|
|
55
|
+
operation: DaemonOperationResult;
|
|
56
|
+
} | {
|
|
57
|
+
outcome: "failed";
|
|
58
|
+
operation: DaemonOperationResult;
|
|
59
|
+
};
|
|
44
60
|
export declare class TelegramDaemonController implements BuiltInDaemonController {
|
|
45
61
|
private readonly settings;
|
|
46
62
|
private readonly deps;
|
|
47
63
|
readonly kind: "telegram";
|
|
48
64
|
private readonly fsImpl;
|
|
49
|
-
private readonly now;
|
|
50
65
|
private readonly pidAlive;
|
|
51
|
-
private readonly
|
|
66
|
+
private readonly now;
|
|
67
|
+
private readonly processReference;
|
|
52
68
|
private readonly waitStepMs;
|
|
53
69
|
constructor(settings: Settings, deps?: TelegramDaemonControlDeps);
|
|
54
70
|
private runtimeInfo;
|
|
55
71
|
status(): Promise<DaemonStatus>;
|
|
56
72
|
private spawnDeps;
|
|
73
|
+
private spawnAndWait;
|
|
57
74
|
private sleep;
|
|
58
75
|
/**
|
|
59
76
|
* Wait until the captured pid is dead. Ownership-file movement is NOT treated
|
|
@@ -61,10 +78,12 @@ export declare class TelegramDaemonController implements BuiltInDaemonController
|
|
|
61
78
|
* which is what the no-409 invariant requires before spawning a fresh poller.
|
|
62
79
|
*/
|
|
63
80
|
private waitForPidDeath;
|
|
81
|
+
private signalCapturedOwner;
|
|
64
82
|
private result;
|
|
65
83
|
reload(opts?: DaemonOperationOptions): Promise<DaemonOperationResult>;
|
|
84
|
+
reloadForGenerationUpgrade(opts?: DaemonOperationOptions): Promise<TelegramGenerationReloadResult>;
|
|
66
85
|
stop(opts?: DaemonOperationOptions): Promise<DaemonOperationResult>;
|
|
67
86
|
private stopOrReload;
|
|
68
|
-
/** Clear our
|
|
87
|
+
/** Clear only our exact control request; a successor request must survive. */
|
|
69
88
|
private clearOwnRequest;
|
|
70
89
|
}
|
|
@@ -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}
|
|
30
|
-
* wire
|
|
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
|
-
|
|
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
|
-
|
|
310
|
+
/** Confirm the provisional owner or retire only its unchanged acquisition. */
|
|
311
|
+
export declare function confirmTelegramDaemonSpawn(input: {
|
|
165
312
|
settings: Settings;
|
|
166
|
-
|
|
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
|
|
186
|
-
|
|
187
|
-
|
|
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
|