@gajae-code/coding-agent 0.12.11 → 0.12.13
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 +68 -0
- package/dist/types/cli/marketplace-hint.d.ts +28 -0
- package/dist/types/cli/notify-cli.d.ts +42 -1
- package/dist/types/cli/update-cli.d.ts +9 -3
- package/dist/types/cli.d.ts +10 -0
- package/dist/types/commands/notify.d.ts +6 -0
- package/dist/types/config/file-lock-gc.d.ts +13 -1
- package/dist/types/config/file-lock.d.ts +3 -0
- package/dist/types/config/model-registry.d.ts +6 -0
- package/dist/types/config/models-config-schema.d.ts +99 -0
- package/dist/types/coordinator/contract.d.ts +1 -1
- package/dist/types/coordinator-mcp/question-state.d.ts +3 -1
- package/dist/types/extensibility/extensions/index.d.ts +1 -0
- package/dist/types/extensibility/extensions/ouroboros-ooo-bridge.d.ts +13 -0
- package/dist/types/extensibility/extensions/prefix-command-bridge.d.ts +0 -1
- package/dist/types/extensibility/extensions/types.d.ts +2 -0
- package/dist/types/gjc-runtime/deep-interview-state.d.ts +14 -0
- package/dist/types/gjc-runtime/gc-runtime.d.ts +11 -0
- package/dist/types/main.d.ts +6 -2
- package/dist/types/modes/acp/acp-agent.d.ts +24 -1
- package/dist/types/modes/components/session-selector.d.ts +1 -1
- package/dist/types/modes/utils/host-status.d.ts +23 -0
- package/dist/types/runtime-mcp/types.d.ts +2 -0
- package/dist/types/sdk/broker/lifecycle.d.ts +12 -0
- package/dist/types/sdk/bus/chat-daemon-cli.d.ts +3 -0
- package/dist/types/sdk/bus/chat-daemon-command-channel.d.ts +244 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +11 -1
- package/dist/types/sdk/bus/chat-daemon-runtime.d.ts +7 -0
- package/dist/types/sdk/bus/control-plane-frames.d.ts +43 -0
- package/dist/types/sdk/bus/conversation-store.d.ts +18 -0
- package/dist/types/sdk/bus/existing-thread-readiness.d.ts +71 -0
- package/dist/types/sdk/bus/html-format.d.ts +10 -0
- package/dist/types/sdk/bus/notification-service.d.ts +10 -0
- package/dist/types/sdk/bus/slack-daemon.d.ts +27 -0
- package/dist/types/sdk/bus/slack-live-provider.d.ts +11 -0
- package/dist/types/sdk/bus/slack-provider.d.ts +14 -0
- package/dist/types/sdk/bus/slack-thread-binding.d.ts +140 -0
- package/dist/types/sdk/bus/telegram-daemon-cli.d.ts +2 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +28 -7
- package/dist/types/sdk/bus/telegram-daemon.d.ts +119 -17
- package/dist/types/sdk/bus/telegram-reference.d.ts +20 -1
- package/dist/types/sdk/bus/topic-registry.d.ts +130 -85
- package/dist/types/sdk/host/host.d.ts +52 -0
- package/dist/types/sdk/lifecycle-session.d.ts +6 -0
- package/dist/types/sdk/session-activation.d.ts +41 -0
- package/dist/types/sdk/session.d.ts +18 -0
- package/dist/types/sdk/startup-capability.d.ts +8 -1
- package/dist/types/session/agent-session.d.ts +7 -2
- package/dist/types/session/artifacts.d.ts +2 -2
- package/dist/types/session/session-manager.d.ts +40 -6
- package/dist/types/tools/index.d.ts +15 -0
- package/dist/types/utils/npm-registry.d.ts +71 -0
- package/examples/extensions/README.md +38 -4
- package/examples/extensions/ooo-bridge.ts +15 -0
- package/package.json +8 -8
- package/scripts/generate-sdk-operation-inventory.ts +4 -0
- package/src/async/job-manager.ts +9 -2
- package/src/cli/fast-help.ts +1 -0
- package/src/cli/marketplace-hint.ts +39 -0
- package/src/cli/notify-cli.ts +226 -2
- package/src/cli/plugin-cli.ts +10 -0
- package/src/cli/update-cli.ts +90 -23
- package/src/cli.ts +23 -0
- package/src/commands/notify.ts +27 -3
- package/src/commands/sdk.ts +1 -0
- package/src/config/file-lock-gc.ts +65 -26
- package/src/config/file-lock.ts +61 -16
- package/src/config/model-profiles.ts +46 -30
- package/src/config/model-registry.ts +17 -5
- package/src/config/models-config-schema.ts +9 -4
- package/src/coordinator/contract.ts +1 -0
- package/src/coordinator-mcp/question-state.ts +2 -0
- package/src/coordinator-mcp/server.ts +358 -7
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +1 -1
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +2 -0
- package/src/extensibility/extensions/index.ts +1 -0
- package/src/extensibility/extensions/ouroboros-ooo-bridge.ts +235 -0
- package/src/extensibility/extensions/prefix-command-bridge.ts +4 -9
- package/src/extensibility/extensions/runner.ts +15 -2
- package/src/extensibility/extensions/types.ts +2 -0
- package/src/gjc-runtime/deep-interview-recorder.ts +9 -5
- package/src/gjc-runtime/deep-interview-state.ts +27 -3
- package/src/gjc-runtime/gc-render.ts +8 -1
- package/src/gjc-runtime/gc-runtime.ts +15 -1
- package/src/gjc-runtime/launch-tmux.ts +53 -1
- package/src/internal-urls/docs-index.generated.ts +14 -13
- package/src/main.ts +214 -70
- package/src/modes/DESIGN.md +8 -5
- package/src/modes/acp/acp-agent.ts +123 -7
- package/src/modes/components/footer.ts +10 -18
- package/src/modes/components/model-selector.ts +52 -5
- package/src/modes/components/session-selector.ts +26 -3
- package/src/modes/components/status-line/gh.ts +23 -2
- package/src/modes/controllers/event-controller.ts +3 -0
- package/src/modes/controllers/extension-ui-controller.ts +2 -0
- package/src/modes/controllers/input-controller.ts +11 -0
- package/src/modes/controllers/selector-controller.ts +8 -2
- package/src/modes/interactive-mode.ts +7 -1
- package/src/modes/print-mode.ts +10 -0
- package/src/modes/utils/host-status.ts +38 -0
- package/src/prompts/system/project-prompt.md +19 -0
- package/src/runtime-mcp/types.ts +2 -0
- package/src/sdk/broker/lifecycle.ts +60 -3
- package/src/sdk/bus/chat-daemon-cli.ts +42 -0
- package/src/sdk/bus/chat-daemon-command-channel.ts +921 -0
- package/src/sdk/bus/chat-daemon-control.ts +16 -3
- package/src/sdk/bus/chat-daemon-runtime.ts +257 -21
- package/src/sdk/bus/control-plane-frames.ts +51 -0
- package/src/sdk/bus/conversation-store.ts +29 -1
- package/src/sdk/bus/existing-thread-readiness.ts +155 -0
- package/src/sdk/bus/html-format.ts +19 -3
- package/src/sdk/bus/index.ts +91 -12
- package/src/sdk/bus/notification-service.ts +28 -1
- package/src/sdk/bus/rich-render.ts +3 -2
- package/src/sdk/bus/slack-daemon.ts +99 -4
- package/src/sdk/bus/slack-live-provider.ts +32 -2
- package/src/sdk/bus/slack-provider.ts +11 -0
- package/src/sdk/bus/slack-thread-binding.ts +641 -0
- package/src/sdk/bus/telegram-daemon-cli.ts +11 -0
- package/src/sdk/bus/telegram-daemon-contract.ts +28 -7
- package/src/sdk/bus/telegram-daemon.ts +2007 -509
- package/src/sdk/bus/telegram-reference.ts +114 -15
- package/src/sdk/bus/topic-registry.ts +674 -181
- package/src/sdk/host/host.ts +180 -1
- package/src/sdk/lifecycle-session.ts +8 -2
- package/src/sdk/protocol/operation-inventory.generated.json +22 -0
- package/src/sdk/session-activation.ts +127 -0
- package/src/sdk/session.ts +144 -29
- package/src/sdk/startup-capability.ts +10 -1
- package/src/session/agent-session.ts +220 -13
- package/src/session/artifacts.ts +56 -13
- package/src/session/internal/managed-session-storage.ts +228 -7
- package/src/session/session-manager.ts +225 -60
- package/src/setup/provider-presets.json +3 -3
- package/src/task/index.ts +92 -44
- package/src/thinking-metadata.ts +2 -1
- package/src/tools/ask.ts +10 -0
- package/src/tools/index.ts +15 -0
- package/src/tools/todo-write.ts +4 -4
- package/src/utils/npm-registry.ts +660 -0
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { type SdkSessionEndpoint } from "../client/discovery";
|
|
2
2
|
import { ConversationStore } from "./conversation-store";
|
|
3
3
|
import { type SlackConversation } from "./slack-conversation";
|
|
4
|
+
export type { SlackThreadBindingErrorCode } from "./slack-thread-binding";
|
|
5
|
+
export { SlackThreadBindingError } from "./slack-thread-binding";
|
|
4
6
|
export declare class SlackEndpointBindingError extends Error {
|
|
5
7
|
constructor(message?: string);
|
|
6
8
|
}
|
|
@@ -11,6 +13,11 @@ export interface SlackEndpoint extends SdkSessionEndpoint {
|
|
|
11
13
|
export interface SlackSdkClient {
|
|
12
14
|
send(frame: Record<string, unknown>): void;
|
|
13
15
|
}
|
|
16
|
+
/** Proven right to bind one session at one endpoint generation. */
|
|
17
|
+
export interface SlackBindingAuthority {
|
|
18
|
+
sessionId: string;
|
|
19
|
+
endpointGeneration: number;
|
|
20
|
+
}
|
|
14
21
|
export interface SlackNotificationDaemonOptions {
|
|
15
22
|
agentDir: string;
|
|
16
23
|
repo: string;
|
|
@@ -27,6 +34,12 @@ export interface SlackNotificationDaemonOptions {
|
|
|
27
34
|
publicationOwnerId?: string;
|
|
28
35
|
publicationLeaseMs?: number;
|
|
29
36
|
resolveEndpoint?: (sessionId: string) => Promise<SlackEndpoint | null>;
|
|
37
|
+
/**
|
|
38
|
+
* Exact discovery/attachment authority for adopting an existing root. The
|
|
39
|
+
* runtime supplies index, endpoint, and attachment proof; without it only the
|
|
40
|
+
* resolvable endpoint generation can be proven.
|
|
41
|
+
*/
|
|
42
|
+
resolveBindingAuthority?: (sessionId: string) => Promise<SlackBindingAuthority | undefined>;
|
|
30
43
|
createClient: (endpoint: SlackEndpoint) => SlackSdkClient;
|
|
31
44
|
onCommand?: (sessionId: string, content: string, endpoint: SlackEndpoint, idempotencyKey: string) => Promise<boolean>;
|
|
32
45
|
}
|
|
@@ -46,6 +59,20 @@ export declare class SlackNotificationDaemon {
|
|
|
46
59
|
/** Accepted inbound effects are durably claimed before their Socket Mode ACK. */
|
|
47
60
|
handleEnvelope(envelope: SlackSocketEnvelope): Promise<boolean>;
|
|
48
61
|
postRoot(sessionId: string, body: string, endpointGeneration?: number): Promise<SlackConversation>;
|
|
62
|
+
/**
|
|
63
|
+
* Adopt an operator-supplied root in the configured workspace and channel
|
|
64
|
+
* without publishing a replacement root.
|
|
65
|
+
*
|
|
66
|
+
* The root is verified against the provider before any lock is taken, and the
|
|
67
|
+
* claim re-proves session authority inside the store lock. When the caller is
|
|
68
|
+
* the daemon command channel it also supplies `commitAuthority`, which
|
|
69
|
+
* re-proves the exact daemon owner tuple and takes terminal request
|
|
70
|
+
* authority in the same fence, so an ownership change or a cancellation
|
|
71
|
+
* between dispatch and commit leaves the store untouched. The claim targets
|
|
72
|
+
* the same session key stock publication uses, so a concurrent first
|
|
73
|
+
* notification observes the adopted root instead of posting a second one.
|
|
74
|
+
*/
|
|
75
|
+
bindExistingRoot(sessionId: string, rootTs: string, commitAuthority?: () => Promise<boolean>): Promise<SlackConversation>;
|
|
49
76
|
/** Deliver a notification into the mapped root thread, creating that root once. */
|
|
50
77
|
notify(sessionId: string, body: string, actionId?: string, endpointGeneration?: number): Promise<SlackConversation>;
|
|
51
78
|
/** Posts a safe command outcome to the active mapped root thread. */
|
|
@@ -63,4 +63,15 @@ export declare class SlackLiveProvider implements SlackProviderClient, SlackDiag
|
|
|
63
63
|
clientMsgId: string;
|
|
64
64
|
threadTs?: string;
|
|
65
65
|
}): Promise<SlackMessageSearchResult | null>;
|
|
66
|
+
/**
|
|
67
|
+
* Confirm that `ts` addresses a real message in `channel`. Slack answers
|
|
68
|
+
* `conversations.replies` with the addressed message first; a timestamp that
|
|
69
|
+
* is unknown, deleted, or lives in another channel produces a Web API error
|
|
70
|
+
* instead, which surfaces as a `SlackProviderError` the caller treats as an
|
|
71
|
+
* unverified root.
|
|
72
|
+
*/
|
|
73
|
+
findMessageByTimestamp(input: {
|
|
74
|
+
channel: string;
|
|
75
|
+
ts: string;
|
|
76
|
+
}): Promise<SlackMessageSearchResult | null>;
|
|
66
77
|
}
|
|
@@ -50,6 +50,15 @@ export interface SlackProviderClient {
|
|
|
50
50
|
clientMsgId: string;
|
|
51
51
|
threadTs?: string;
|
|
52
52
|
}): Promise<SlackMessageSearchResult | null>;
|
|
53
|
+
/**
|
|
54
|
+
* Prove that an operator-supplied timestamp addresses a real message in the
|
|
55
|
+
* requested channel. Adoption of an existing root is refused when a client
|
|
56
|
+
* cannot answer this, so verification is never silently skipped.
|
|
57
|
+
*/
|
|
58
|
+
findMessageByTimestamp?(input: {
|
|
59
|
+
channel: string;
|
|
60
|
+
ts: string;
|
|
61
|
+
}): Promise<SlackMessageSearchResult | null>;
|
|
53
62
|
readonly transportHealthy?: boolean;
|
|
54
63
|
}
|
|
55
64
|
/**
|
|
@@ -74,4 +83,9 @@ export declare class SlackProvider {
|
|
|
74
83
|
clientMsgId: string;
|
|
75
84
|
threadTs?: string;
|
|
76
85
|
}): Promise<SlackMessageSearchResult | null>;
|
|
86
|
+
/** Fail-closed root verification: a client without this capability can never confirm a root. */
|
|
87
|
+
findMessageByTimestamp(input: {
|
|
88
|
+
channel: string;
|
|
89
|
+
ts: string;
|
|
90
|
+
}): Promise<SlackMessageSearchResult | null>;
|
|
77
91
|
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adoption of an operator-supplied Slack thread as a live session's root.
|
|
3
|
+
*
|
|
4
|
+
* Three separate authorities have to agree before a mapping exists:
|
|
5
|
+
* 1. *Configuration*: a complete Slack target (both tokens plus workspace and
|
|
6
|
+
* channel) must already be configured; binding never accepts a target.
|
|
7
|
+
* 2. *Daemon*: only the running owner may mutate mappings, and only while it is
|
|
8
|
+
* still the exact owner (`ownerId`/`pid`/`incarnation`/daemon generation)
|
|
9
|
+
* captured before the request was published.
|
|
10
|
+
* 3. *Session*: the SDK session must be attachable right now — indexed, live,
|
|
11
|
+
* non-terminal, with a readable, non-stale endpoint whose pid matches the
|
|
12
|
+
* indexed host and whose generation has not rolled.
|
|
13
|
+
*
|
|
14
|
+
* Provider verification happens before any lock is taken, and the mapping commit
|
|
15
|
+
* re-proves session authority inside the store lock, so an authority that
|
|
16
|
+
* changes mid-flight leaves no mapping behind.
|
|
17
|
+
*/
|
|
18
|
+
import type { Settings } from "../../config/settings";
|
|
19
|
+
import type { SessionIndex } from "../broker/session-index";
|
|
20
|
+
import { type SdkSessionEndpoint, type SdkSessionEndpointScope } from "../client/discovery";
|
|
21
|
+
import { type EnsureChatDaemonResult } from "./chat-daemon-control";
|
|
22
|
+
import { ConversationStore } from "./conversation-store";
|
|
23
|
+
import { type SlackConversation } from "./slack-conversation";
|
|
24
|
+
export type SlackThreadBindingErrorCode = "invalid_root" | "target_not_configured" | "daemon_unavailable" | "daemon_owner_changed" | "session_not_live" | "root_not_found" | "provider_unavailable" | "root_conflict" | "session_conflict" | "binding_outcome_unknown" | "binding_failed";
|
|
25
|
+
/** A fail-closed rejection while adopting an operator-supplied Slack thread. */
|
|
26
|
+
export declare class SlackThreadBindingError extends Error {
|
|
27
|
+
readonly code: SlackThreadBindingErrorCode;
|
|
28
|
+
constructor(code: SlackThreadBindingErrorCode, message: string);
|
|
29
|
+
}
|
|
30
|
+
export declare function isBoundedSlackRootTs(value: string): boolean;
|
|
31
|
+
/** Reject a non-addressable root before any authority read or persistence. */
|
|
32
|
+
export declare function assertBoundedSlackRootTs(rootTs: string): void;
|
|
33
|
+
export interface SlackSessionBindingAuthority {
|
|
34
|
+
sessionId: string;
|
|
35
|
+
endpointGeneration: number;
|
|
36
|
+
pid: number;
|
|
37
|
+
repo: string;
|
|
38
|
+
/**
|
|
39
|
+
* The exact endpoint this authority was proven against. Callers must use it
|
|
40
|
+
* rather than re-reading discovery: a second read can observe a different
|
|
41
|
+
* process whose endpoint generation happens to repeat, and the numeric
|
|
42
|
+
* generation alone cannot tell the two apart.
|
|
43
|
+
*/
|
|
44
|
+
endpoint: SdkSessionEndpoint;
|
|
45
|
+
scope: SdkSessionEndpointScope;
|
|
46
|
+
endpointMtimeMs: number;
|
|
47
|
+
}
|
|
48
|
+
export interface SessionBindingAuthorityInput {
|
|
49
|
+
sessionIndex: SessionIndex;
|
|
50
|
+
sessionId: string;
|
|
51
|
+
readEndpoint?: (repo: string, sessionId: string, scope?: SdkSessionEndpointScope) => Promise<SdkSessionEndpoint | null>;
|
|
52
|
+
statEndpoint?: (endpointPath: string) => Promise<{
|
|
53
|
+
mtimeMs: number;
|
|
54
|
+
} | undefined>;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Resolve exact discovery/attachment authority for one session.
|
|
58
|
+
*
|
|
59
|
+
* `IndexedSession.live` alone is only pid liveness, which a terminated or
|
|
60
|
+
* unregistered session can still satisfy through pid reuse or a stale record.
|
|
61
|
+
* Adoption additionally requires an intact index replay, a non-terminal record,
|
|
62
|
+
* and a discovery endpoint that is present, well-formed, not marked stale, and
|
|
63
|
+
* owned by the same host pid the index recorded.
|
|
64
|
+
*/
|
|
65
|
+
export declare function resolveSessionBindingAuthority(input: SessionBindingAuthorityInput): Promise<SlackSessionBindingAuthority | undefined>;
|
|
66
|
+
export interface SlackThreadClaimInput {
|
|
67
|
+
store: ConversationStore<SlackConversation>;
|
|
68
|
+
/** The session root-claim key shared with stock root publication. */
|
|
69
|
+
key: string;
|
|
70
|
+
teamId: string;
|
|
71
|
+
channelId: string;
|
|
72
|
+
sessionId: string;
|
|
73
|
+
rootTs: string;
|
|
74
|
+
endpointGeneration: number;
|
|
75
|
+
/**
|
|
76
|
+
* Re-proves session authority inside the store lock, immediately before
|
|
77
|
+
* commit. It may be invoked more than once for one claim: authority here is
|
|
78
|
+
* monotone — a rolled endpoint generation, a replaced daemon owner tuple and
|
|
79
|
+
* a retired single-winner request claim never come back — so repeating the
|
|
80
|
+
* proof can only ever narrow the window, never widen it. It must therefore be
|
|
81
|
+
* idempotent and must never perform a remote call.
|
|
82
|
+
*/
|
|
83
|
+
revalidate: () => Promise<boolean>;
|
|
84
|
+
now?: () => number;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Claim an existing Slack root for a session without publishing a replacement.
|
|
88
|
+
*
|
|
89
|
+
* The claim transacts the same `intent:<sessionId>` key that stock root
|
|
90
|
+
* publication uses, so a bind and a concurrent first notification serialize on
|
|
91
|
+
* one invariant: whichever commits first owns the session's single root, and
|
|
92
|
+
* the loser observes it instead of creating a second one.
|
|
93
|
+
*/
|
|
94
|
+
export declare function claimSlackThreadBinding(input: SlackThreadClaimInput): Promise<SlackConversation>;
|
|
95
|
+
export interface ConfiguredSlackThreadBindingInput {
|
|
96
|
+
settings: Settings;
|
|
97
|
+
sessionId: string;
|
|
98
|
+
threadTs: string;
|
|
99
|
+
}
|
|
100
|
+
export interface ConfiguredSlackThreadBindingDeps {
|
|
101
|
+
ensureDaemon?: (settings: Settings) => Promise<EnsureChatDaemonResult>;
|
|
102
|
+
timeoutMs?: number;
|
|
103
|
+
pollIntervalMs?: number;
|
|
104
|
+
/** Bounded wait for a daemon that took terminal authority before the caller gave up. */
|
|
105
|
+
settleGraceMs?: number;
|
|
106
|
+
now?: () => number;
|
|
107
|
+
sleep?: (ms: number) => Promise<void>;
|
|
108
|
+
}
|
|
109
|
+
/** Safe confirmation of an applied binding. It carries identifiers only. */
|
|
110
|
+
export interface BoundSlackThread {
|
|
111
|
+
sessionId: string;
|
|
112
|
+
endpointGeneration: number;
|
|
113
|
+
teamId: string;
|
|
114
|
+
channelId: string;
|
|
115
|
+
rootTs: string;
|
|
116
|
+
ownerId: string;
|
|
117
|
+
daemonGeneration: number;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Adopt an existing Slack root for a live session through the running daemon.
|
|
121
|
+
*
|
|
122
|
+
* The CLI never writes the mapping store: it proves the configured target and
|
|
123
|
+
* the exact current owner, then asks that owner to perform the mutation. A
|
|
124
|
+
* daemon that is no longer the captured owner answers `owner_changed`, so a
|
|
125
|
+
* replacement or restart between capture and execution can never apply the
|
|
126
|
+
* request.
|
|
127
|
+
*
|
|
128
|
+
* A submission that is not answered is cancelled through the channel's
|
|
129
|
+
* single-winner arbitration before it is reported as a failure. If that
|
|
130
|
+
* cancellation loses to the daemon's own commit authority, the outcome is
|
|
131
|
+
* reported as unknown instead of as a failure, because a mapping may already
|
|
132
|
+
* exist; re-running the same command observes the settled state idempotently.
|
|
133
|
+
*
|
|
134
|
+
* The command channel proves *correlation*, never authorship: every field a
|
|
135
|
+
* response echoes is copied verbatim out of the plaintext request published
|
|
136
|
+
* beside it. A definitive answer is therefore reported only after this caller
|
|
137
|
+
* itself observes the exact mutation in the conversation store — see
|
|
138
|
+
* `corroborateBoundMapping`.
|
|
139
|
+
*/
|
|
140
|
+
export declare function bindConfiguredSlackThread(input: ConfiguredSlackThreadBindingInput, deps?: ConfiguredSlackThreadBindingDeps): Promise<BoundSlackThread>;
|
|
@@ -23,6 +23,8 @@ export interface RunDaemonInternalDeps {
|
|
|
23
23
|
clearInterval?: (timer: Timer) => void;
|
|
24
24
|
/** Reads persisted daemon ownership state; defaults to the real reader. */
|
|
25
25
|
readDaemonState?: (settings: Settings) => Promise<DaemonState | undefined>;
|
|
26
|
+
/** Loads the verified machine-local identity; injectable so daemon tests do not touch the host. */
|
|
27
|
+
loadInstallationHostId?: () => Promise<string>;
|
|
26
28
|
}
|
|
27
29
|
export declare function createLightweightDaemonSettings(input: {
|
|
28
30
|
agentDir: string;
|
|
@@ -53,15 +53,36 @@ export declare const NOTIFICATION_PROTOCOL_VERSION = 3;
|
|
|
53
53
|
* ownership, settlement, and descendant cleanup authority. Generation 38 also
|
|
54
54
|
* adds durable provider-intent admission without changing owner, reclaim,
|
|
55
55
|
* signal, or spawn authority.
|
|
56
|
+
* Generation 41 applies first-class provider-settings admission to Telegram
|
|
57
|
+
* lifecycle controls, plus cross-host topic-registry CAS convergence, host-and-epoch
|
|
58
|
+
* archive fencing, retained topic history, user-topic adoption provenance, and
|
|
59
|
+
* exact versionless shared-state upgrades with quarantined source snapshots.
|
|
56
60
|
* Generation 42 applies first-class provider-settings admission to Telegram
|
|
57
61
|
* lifecycle controls. Generation 43 applies identity-bound exact replacement
|
|
58
|
-
* cleanup shared by managed-session and daemon filesystem authority.
|
|
62
|
+
* cleanup shared by managed-session and daemon filesystem authority. Generation
|
|
63
|
+
* 44 makes callback recovery restart-safe by revoking persisted routes and
|
|
64
|
+
* callback receipts durable before routing and binds aliases to exact asks.
|
|
65
|
+
* Generation 46 stages accepted callback activation and makes callback
|
|
66
|
+
* consumption transactional under exact pending and lease authority. Generation
|
|
67
|
+
* 47 settles failed staged revocation and guards the complete callback authority
|
|
68
|
+
* and polling dependency chain. Generation 48 uses crash-durable callback
|
|
69
|
+
* receipts, a legacy-disjoint random alias namespace, and exact topic leases.
|
|
70
|
+
* Generation 49 drains every admitted session-message handler before final
|
|
71
|
+
* durable persistence and ownership release. Generation 50 resolves intermediate
|
|
72
|
+
* notifications-directory symlinks before native exact unlink while keeping
|
|
73
|
+
* final-component file symlinks fail-closed under AT_SYMLINK_NOFOLLOW (bounded
|
|
74
|
+
* #3761 multi-account activation repair). Generation 51 adds shared durable
|
|
75
|
+
* topic authority, archive recovery, and requires Telegram's documented error
|
|
76
|
+
* code for idempotent archive settlement. Generation 52 is claimed by the
|
|
77
|
+
* pre-readiness daemon-child exit diagnostics slice (#3761). Generation 53
|
|
78
|
+
* renders multi-select state for ask-tool asks, not only durable workflow
|
|
79
|
+
* gates, and renumbers pre-numbered options exactly once around the selection
|
|
80
|
+
* marker.
|
|
59
81
|
*/
|
|
60
|
-
export declare const DAEMON_GENERATION =
|
|
82
|
+
export declare const DAEMON_GENERATION = 53;
|
|
61
83
|
/**
|
|
62
|
-
* Serving-compatibility boundary for daemon lifecycle requests. Epoch
|
|
63
|
-
*
|
|
64
|
-
* epoch
|
|
65
|
-
* reload of compatible live predecessors.
|
|
84
|
+
* Serving-compatibility boundary for daemon lifecycle requests. Epoch 5
|
|
85
|
+
* requires the complete generation-36 topic authority contract, so older
|
|
86
|
+
* epoch-4 daemons cannot keep serving across an upgrade.
|
|
66
87
|
*/
|
|
67
|
-
export declare const SERVING_EPOCH =
|
|
88
|
+
export declare const SERVING_EPOCH = 5;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
|
+
import { type NativeExactFileIdentity, type NativeExactUnlinkResult } from "@gajae-code/natives";
|
|
2
3
|
import type { Settings } from "../../config/settings";
|
|
3
4
|
import type { DaemonRuntimeInfo } from "../../daemon/control-types";
|
|
4
5
|
import { HEARTBEAT_TTL_MS } from "./daemon-paths";
|
|
@@ -10,7 +11,7 @@ import { type ControlServerLike, type LifecycleControlServerFactory } from "./li
|
|
|
10
11
|
import type { OrchestratorDeps } from "./lifecycle-orchestrator";
|
|
11
12
|
import { NotificationOperatorRuntime } from "./operator-runtime";
|
|
12
13
|
import { type AliasTable, type CallbackRoute } from "./telegram-reference";
|
|
13
|
-
import { type TopicEndpointBinding } from "./topic-registry";
|
|
14
|
+
import { type TopicEndpointBinding, type TopicRegistryCasAuthority, type TopicRegistryState } from "./topic-registry";
|
|
14
15
|
export type EnsureDaemonResult = "owner_spawned" | "attached" | "disabled" | "blocked";
|
|
15
16
|
/** Detailed result for orchestration that must distinguish a #2028 handoff from a fresh spawn. */
|
|
16
17
|
export type EnsureTelegramDaemonDetailedResult = "spawned" | "reloaded" | "attached" | "disabled" | "blocked_identity";
|
|
@@ -45,6 +46,14 @@ export interface DaemonState {
|
|
|
45
46
|
servingEpoch?: number;
|
|
46
47
|
stoppedAt?: number;
|
|
47
48
|
}
|
|
49
|
+
interface ExactFileStat {
|
|
50
|
+
dev: bigint;
|
|
51
|
+
ino: bigint;
|
|
52
|
+
nlink: bigint;
|
|
53
|
+
size: bigint;
|
|
54
|
+
mtimeNs: bigint;
|
|
55
|
+
isFile(): boolean;
|
|
56
|
+
}
|
|
48
57
|
export interface TelegramDaemonFs {
|
|
49
58
|
mkdir(path: string, opts?: fs.MakeDirectoryOptions): Promise<void>;
|
|
50
59
|
readFile(path: string, encoding: BufferEncoding): Promise<string>;
|
|
@@ -52,10 +61,14 @@ export interface TelegramDaemonFs {
|
|
|
52
61
|
rename(oldPath: string, newPath: string): Promise<void>;
|
|
53
62
|
unlink(path: string): Promise<void>;
|
|
54
63
|
open(path: string, flags: string, mode?: number): Promise<{
|
|
64
|
+
sync?: () => Promise<void>;
|
|
55
65
|
close(): Promise<void>;
|
|
56
66
|
}>;
|
|
57
67
|
readdir(path: string): Promise<string[]>;
|
|
58
68
|
chmod(path: string, mode: number): Promise<void>;
|
|
69
|
+
/** Crash-atomic persistence seams. Implementations without them fail closed. */
|
|
70
|
+
fsyncFile?(path: string): Promise<void>;
|
|
71
|
+
fsyncDirectory?(path: string): Promise<void>;
|
|
59
72
|
stat?(path: string): Promise<{
|
|
60
73
|
mtimeMs: number;
|
|
61
74
|
size?: number;
|
|
@@ -64,8 +77,11 @@ export interface TelegramDaemonFs {
|
|
|
64
77
|
/** Hard-link count; required to prove a staging temp has no second name. */
|
|
65
78
|
nlink?: number;
|
|
66
79
|
ctimeMs?: number;
|
|
67
|
-
isDirectory
|
|
80
|
+
isDirectory?(): boolean;
|
|
68
81
|
}>;
|
|
82
|
+
lstat?(path: string, opts: {
|
|
83
|
+
bigint: true;
|
|
84
|
+
}): Promise<ExactFileStat>;
|
|
69
85
|
readEndpointFile?(path: string): Promise<NotificationEndpointFile>;
|
|
70
86
|
exactUnlink?(path: string, identity: NotificationEndpointFileIdentity, quarantineName?: string): Promise<NotificationExactUnlinkResult>;
|
|
71
87
|
}
|
|
@@ -151,6 +167,49 @@ export declare function startDaemonLifecycleControl(input: {
|
|
|
151
167
|
auditRedactionKey: Uint8Array;
|
|
152
168
|
env?: NodeJS.ProcessEnv;
|
|
153
169
|
}): OrchestratorDeps;
|
|
170
|
+
export declare class TopicRegistryDurabilityUnavailableError extends Error {
|
|
171
|
+
readonly code = "durability_unavailable";
|
|
172
|
+
constructor(cause: unknown);
|
|
173
|
+
}
|
|
174
|
+
type TopicRegistryExactReplace = (sourcePath: string, destinationPath: string, expectedSource: NativeExactFileIdentity, expectedDestination: NativeExactFileIdentity) => NativeExactUnlinkResult;
|
|
175
|
+
/** Publish topic authority only after its staged bytes and replacement are durable. */
|
|
176
|
+
export declare function writeTopicRegistryAtomic(fsImpl: TelegramDaemonFs, file: string, data: unknown, platform?: NodeJS.Platform, exactReplace?: TopicRegistryExactReplace, expectedDestination?: NativeExactFileIdentity): Promise<void>;
|
|
177
|
+
/**
|
|
178
|
+
* Shared-volume topic authority backed by the existing cross-process file lock.
|
|
179
|
+
* A missing authority is the only valid bootstrap state; malformed and future
|
|
180
|
+
* snapshots are never interpreted as empty state.
|
|
181
|
+
*/
|
|
182
|
+
export declare class FilesystemTopicRegistryCasAuthority implements TopicRegistryCasAuthority {
|
|
183
|
+
#private;
|
|
184
|
+
private readonly file;
|
|
185
|
+
private readonly fsImpl;
|
|
186
|
+
private readonly platform;
|
|
187
|
+
private readonly exactReplace;
|
|
188
|
+
private readonly installationHostId;
|
|
189
|
+
constructor(file: string, input: {
|
|
190
|
+
installationHostId: string;
|
|
191
|
+
fs?: TelegramDaemonFs;
|
|
192
|
+
platform?: NodeJS.Platform;
|
|
193
|
+
exactReplace?: TopicRegistryExactReplace;
|
|
194
|
+
});
|
|
195
|
+
read(): Promise<TopicRegistryState>;
|
|
196
|
+
compareAndSet(expectedGeneration: number, next: TopicRegistryState): Promise<boolean>;
|
|
197
|
+
private readLocked;
|
|
198
|
+
}
|
|
199
|
+
export interface MachineIdentityDeps {
|
|
200
|
+
platform?: NodeJS.Platform;
|
|
201
|
+
readFile?: (file: string) => Promise<string>;
|
|
202
|
+
runCommand?: (command: string, args: readonly string[]) => {
|
|
203
|
+
exitCode: number;
|
|
204
|
+
stdout: Uint8Array;
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
/** @internal */
|
|
208
|
+
export declare function parseWindowsMachineGuid(output: string): string | undefined;
|
|
209
|
+
/** @internal */
|
|
210
|
+
export declare function parseMacPlatformUuid(output: string): string | undefined;
|
|
211
|
+
/** Loads a verified machine-local identity without persisting the underlying machine ID. */
|
|
212
|
+
export declare function loadInstallationHostId(deps?: MachineIdentityDeps): Promise<string>;
|
|
154
213
|
export declare function tryCreateOwnershipLock(fsImpl: TelegramDaemonFs, file: string, initialization: OwnershipLockMetadata): Promise<boolean>;
|
|
155
214
|
type OwnershipLockMetadata = {
|
|
156
215
|
pid: number;
|
|
@@ -552,6 +611,8 @@ export interface TelegramSpawnOwnerInput {
|
|
|
552
611
|
roots?: string[];
|
|
553
612
|
tokenFingerprint: string;
|
|
554
613
|
chatId: string;
|
|
614
|
+
/** Ephemeral outbound-only validation destination for this owner launch. */
|
|
615
|
+
validationTestSupergroupChatId?: string;
|
|
555
616
|
}
|
|
556
617
|
export interface TelegramSpawnAcquisition {
|
|
557
618
|
readonly ownerId: string;
|
|
@@ -587,6 +648,7 @@ export declare function buildTelegramDaemonSpawnArgs(input: {
|
|
|
587
648
|
execPath?: string;
|
|
588
649
|
ownerId: string;
|
|
589
650
|
agentDir: string;
|
|
651
|
+
validationTestSupergroupChatId?: string;
|
|
590
652
|
}): {
|
|
591
653
|
command: string;
|
|
592
654
|
args: string[];
|
|
@@ -732,6 +794,12 @@ export interface TelegramDaemonOptions {
|
|
|
732
794
|
ownerId: string;
|
|
733
795
|
botToken: string;
|
|
734
796
|
chatId: string;
|
|
797
|
+
/**
|
|
798
|
+
* Exact ephemeral forum destination supplied to the daemon-internal validation
|
|
799
|
+
* command. Production ownership, pairing, and durable topic authority remain
|
|
800
|
+
* bound to `chatId`.
|
|
801
|
+
*/
|
|
802
|
+
validationTestSupergroupChatId?: string;
|
|
735
803
|
apiBase?: string;
|
|
736
804
|
fetchImpl?: typeof fetch;
|
|
737
805
|
fs?: TelegramDaemonFs;
|
|
@@ -793,6 +861,13 @@ export interface TelegramDaemonOptions {
|
|
|
793
861
|
topics?: {
|
|
794
862
|
nameTemplate?: string;
|
|
795
863
|
};
|
|
864
|
+
/**
|
|
865
|
+
* Optional compare-and-set store for installations that share topic state
|
|
866
|
+
* across hosts. When configured, every publication is fenced by it.
|
|
867
|
+
*/
|
|
868
|
+
topicRegistryAuthority?: TopicRegistryCasAuthority;
|
|
869
|
+
/** Stable host-local identity. Required whenever a shared authority is configured. */
|
|
870
|
+
installationHostId?: string;
|
|
796
871
|
}
|
|
797
872
|
interface SessionSocket {
|
|
798
873
|
/** Immutable key of the transport endpoint that owns this socket. */
|
|
@@ -841,6 +916,9 @@ export declare class TelegramNotificationDaemon {
|
|
|
841
916
|
private readonly opts;
|
|
842
917
|
readonly aliasTable: AliasTable;
|
|
843
918
|
readonly messageRoutes: Map<string | number, CallbackRoute | Omit<CallbackRoute, "answer">>;
|
|
919
|
+
/** Restart-revoked aliases retained only to terminalize their old keyboards on an authoritative replay. */
|
|
920
|
+
private readonly reissueBacklog;
|
|
921
|
+
private aliasPersistenceQueue;
|
|
844
922
|
/** Telegram message id backing each streamed `${sessionId}:${coalesceKey}`, for in-place edits. */
|
|
845
923
|
private readonly liveMessages;
|
|
846
924
|
/** Endpoint-bound ownership for visible or dispatching tool bubbles. */
|
|
@@ -868,6 +946,8 @@ export declare class TelegramNotificationDaemon {
|
|
|
868
946
|
private readonly botApi;
|
|
869
947
|
private readonly effects;
|
|
870
948
|
private readonly topics;
|
|
949
|
+
/** Stable host-local identity; never persisted in shared topic authority. */
|
|
950
|
+
private installationHostId;
|
|
871
951
|
/** Serializes registry snapshots so an older atomic write cannot overwrite newer rename state. */
|
|
872
952
|
/** Legacy sockets have no durable token, so explicit teardown is their revocation fence. */
|
|
873
953
|
private readonly droppedSessions;
|
|
@@ -876,6 +956,8 @@ export declare class TelegramNotificationDaemon {
|
|
|
876
956
|
private recoveryBindingClaimQueue;
|
|
877
957
|
/** Durable compensation fences retry under supervision until persistence succeeds. */
|
|
878
958
|
private readonly compensationFenceRetries;
|
|
959
|
+
/** All archive paths for one session share one durable fence and remote dispatch. */
|
|
960
|
+
private readonly archiveFlights;
|
|
879
961
|
/** Daemon edit attempts that can race an accepted user service message. */
|
|
880
962
|
private readonly daemonRenameAttempts;
|
|
881
963
|
private readonly selectedAckPending;
|
|
@@ -906,8 +988,10 @@ export declare class TelegramNotificationDaemon {
|
|
|
906
988
|
private threadedFallbackNoticeSent;
|
|
907
989
|
/** Sessions whose identity header was already sent flat (Threaded Mode off). */
|
|
908
990
|
private readonly flatIdentitySent;
|
|
909
|
-
/** Cached
|
|
910
|
-
private
|
|
991
|
+
/** Cached delivery boundary for the private owner chat or validation forum. */
|
|
992
|
+
private pairedChatPrivacy;
|
|
993
|
+
/** Latched once Telegram confirms this chat cannot host forum topics. */
|
|
994
|
+
private topicCapabilityRefused;
|
|
911
995
|
/** Bot username from getMe, cached once at owner startup for group/forum command targeting. */
|
|
912
996
|
private botUsername;
|
|
913
997
|
/** Sessions whose agent loop is currently busy (drives the typing indicator). */
|
|
@@ -972,6 +1056,7 @@ export declare class TelegramNotificationDaemon {
|
|
|
972
1056
|
/** Map a lifecycle response/error to a user-facing message (G010 surfacing). */
|
|
973
1057
|
private formatLifecycleResponse;
|
|
974
1058
|
private callBotApi;
|
|
1059
|
+
private validationTopicDestination;
|
|
975
1060
|
private readonly callBotApiClassified;
|
|
976
1061
|
private botApiWithOutcomeCollector;
|
|
977
1062
|
constructor(opts: TelegramDaemonOptions);
|
|
@@ -1005,6 +1090,12 @@ export declare class TelegramNotificationDaemon {
|
|
|
1005
1090
|
private beginToolActivityShutdown;
|
|
1006
1091
|
private dropSession;
|
|
1007
1092
|
private deleteMessageRoutes;
|
|
1093
|
+
revokeCallbackAliases(socketLease: {
|
|
1094
|
+
session: SessionSocket;
|
|
1095
|
+
token: number;
|
|
1096
|
+
logicalSessionId: string;
|
|
1097
|
+
}): void;
|
|
1098
|
+
private reissuePendingAction;
|
|
1008
1099
|
private static readonly THREADED_FRAMES;
|
|
1009
1100
|
private topicNameFor;
|
|
1010
1101
|
/**
|
|
@@ -1054,20 +1145,19 @@ export declare class TelegramNotificationDaemon {
|
|
|
1054
1145
|
private topicPastOrphanGrace;
|
|
1055
1146
|
private observeOrphanedTopic;
|
|
1056
1147
|
/** Best-effort delete of a session topic once its local notification endpoint shuts down. */
|
|
1057
|
-
|
|
1148
|
+
/** Join all close, compensation, orphan, and restart callers for one session. */
|
|
1149
|
+
private archiveTopic;
|
|
1058
1150
|
private persistTopics;
|
|
1059
1151
|
loadTopics(): Promise<void>;
|
|
1060
1152
|
/**
|
|
1061
|
-
* Rehydrate durable adoption intents after restart
|
|
1062
|
-
*
|
|
1063
|
-
*
|
|
1064
|
-
* and is removed safely without touching the user topic.
|
|
1153
|
+
* Rehydrate durable adoption intents after restart. A sidecar whose topic
|
|
1154
|
+
* already committed is stale evidence and can be removed without touching
|
|
1155
|
+
* the retained user-created topic.
|
|
1065
1156
|
*/
|
|
1066
1157
|
loadAdoptionIntents(): Promise<void>;
|
|
1067
|
-
/** Periodic sweep of expired adoption-intent sidecars (files only; no Telegram API). */
|
|
1068
1158
|
private startAdoptionSweepTimer;
|
|
1069
1159
|
private stopAdoptionSweepTimer;
|
|
1070
|
-
/** Retry crash-interrupted topic
|
|
1160
|
+
/** Retry crash-interrupted topic archives only when the durable backoff permits it. */
|
|
1071
1161
|
private reconcilePendingTopicDeletes;
|
|
1072
1162
|
/** Download one Telegram file with the Bot API's 20 MiB ceiling and one end-to-end deadline. */
|
|
1073
1163
|
private downloadTelegramFile;
|
|
@@ -1122,13 +1212,13 @@ export declare class TelegramNotificationDaemon {
|
|
|
1122
1212
|
*/
|
|
1123
1213
|
private deliverFlatFallback;
|
|
1124
1214
|
/**
|
|
1125
|
-
* Resolve
|
|
1126
|
-
*
|
|
1127
|
-
* indeterminate `getChat` result fails closed for this attempt and is retried
|
|
1128
|
-
* later.
|
|
1215
|
+
* Resolve and cache the outbound delivery boundary. The validation forum is
|
|
1216
|
+
* explicit and topic-only; all inbound and flat paths remain private-only.
|
|
1129
1217
|
*/
|
|
1218
|
+
private validationMode;
|
|
1130
1219
|
private resolvePairedChatPrivacy;
|
|
1131
|
-
|
|
1220
|
+
private pairedChatAllowsTopics;
|
|
1221
|
+
/** Keep all flat delivery and inbound control paths private-only. */
|
|
1132
1222
|
private pairedChatIsPrivate;
|
|
1133
1223
|
/** Tell the user once (per daemon run) how to enable Threaded Mode. */
|
|
1134
1224
|
private notifyThreadedFallback;
|
|
@@ -1141,7 +1231,19 @@ export declare class TelegramNotificationDaemon {
|
|
|
1141
1231
|
*/
|
|
1142
1232
|
private startOwnershipHeartbeatTimer;
|
|
1143
1233
|
private stopOwnershipHeartbeatTimer;
|
|
1144
|
-
/**
|
|
1234
|
+
/**
|
|
1235
|
+
* Run a root scan, guarding against overlapping scans from the timer + loop.
|
|
1236
|
+
*
|
|
1237
|
+
* A reconciliation pass is retried every scan interval, so a failed one must
|
|
1238
|
+
* never tear the owner down. Both callers are fatal boundaries: the timer
|
|
1239
|
+
* fires and forgets, and the run loop awaits without a handler. A rejection
|
|
1240
|
+
* escaping either one (a shared topic authority whose lock or compare-and-set
|
|
1241
|
+
* is momentarily unavailable is the observed case) reaches the process-level
|
|
1242
|
+
* handler, which exits the daemon: every session topic is then left as an
|
|
1243
|
+
* unarchived shell that answers nothing, and the sessions that were still
|
|
1244
|
+
* live lose notifications too. Report the failure and let the next scan
|
|
1245
|
+
* retry instead.
|
|
1246
|
+
*/
|
|
1145
1247
|
private runScan;
|
|
1146
1248
|
private startScanTimer;
|
|
1147
1249
|
private stopScanTimer;
|
|
@@ -51,16 +51,25 @@ export interface CallbackRoute {
|
|
|
51
51
|
sessionId: string;
|
|
52
52
|
actionId: string;
|
|
53
53
|
answer: TelegramCallbackAnswer;
|
|
54
|
+
/** Durable audit metadata only; never sufficient to route a callback after restart. */
|
|
55
|
+
chatId?: string;
|
|
56
|
+
messageId?: number;
|
|
57
|
+
ownerId?: string;
|
|
58
|
+
generation?: number;
|
|
54
59
|
}
|
|
55
60
|
export interface SerializedAliasTable {
|
|
56
|
-
version: 1;
|
|
61
|
+
version: 1 | 2;
|
|
57
62
|
next: number;
|
|
58
63
|
routes: Record<string, CallbackRoute>;
|
|
59
64
|
}
|
|
60
65
|
export interface AliasTable {
|
|
66
|
+
allocate(isReserved?: (alias: string) => boolean): string;
|
|
67
|
+
activate(alias: string, route: CallbackRoute): boolean;
|
|
61
68
|
put(route: CallbackRoute): string;
|
|
62
69
|
get(alias: string): CallbackRoute | undefined;
|
|
70
|
+
update(alias: string, patch: Partial<CallbackRoute>): boolean;
|
|
63
71
|
delete(alias: string): boolean;
|
|
72
|
+
clear(): void;
|
|
64
73
|
serialize(): SerializedAliasTable;
|
|
65
74
|
load(json: unknown): void;
|
|
66
75
|
entries(): Array<[string, CallbackRoute]>;
|
|
@@ -130,6 +139,11 @@ export declare function readEndpoint(path: string): {
|
|
|
130
139
|
pid?: number;
|
|
131
140
|
stale?: boolean;
|
|
132
141
|
};
|
|
142
|
+
/** A bounded, diagnostic-only notification mirrored to reference-client embedders. */
|
|
143
|
+
export type TelegramReferenceDiagnostic = Readonly<{
|
|
144
|
+
code: "action_unavailable";
|
|
145
|
+
requiredCapabilities: readonly string[];
|
|
146
|
+
}>;
|
|
133
147
|
/** Options for {@link runTelegramReferenceClient}. */
|
|
134
148
|
export interface TelegramReferenceOptions {
|
|
135
149
|
botToken: string;
|
|
@@ -138,6 +152,11 @@ export interface TelegramReferenceOptions {
|
|
|
138
152
|
apiBase?: string;
|
|
139
153
|
fetchImpl?: typeof fetch;
|
|
140
154
|
sound?: TelegramNotificationSound;
|
|
155
|
+
/**
|
|
156
|
+
* Additive mirror of bounded logger diagnostics. It never receives tokens,
|
|
157
|
+
* identifiers, raw frames, Telegram content, or WebSocket replies.
|
|
158
|
+
*/
|
|
159
|
+
onDiagnostic?: (diagnostic: TelegramReferenceDiagnostic) => void;
|
|
141
160
|
}
|
|
142
161
|
/**
|
|
143
162
|
* Run the reference bridge until the WebSocket closes. Sends `action_needed` to
|