@gajae-code/coding-agent 0.12.7 → 0.12.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/dist/types/cli/notify-cli.d.ts +1 -0
  3. package/dist/types/commands/notify.d.ts +4 -0
  4. package/dist/types/commands/sdk.d.ts +23 -0
  5. package/dist/types/config/file-lock.d.ts +3 -0
  6. package/dist/types/config/model-discovery-manager.d.ts +6 -1
  7. package/dist/types/config/model-registry.d.ts +2 -0
  8. package/dist/types/config/resolve-config-value.d.ts +2 -1
  9. package/dist/types/config/settings-schema.d.ts +17 -1
  10. package/dist/types/discovery/agents-md.d.ts +11 -1
  11. package/dist/types/main.d.ts +1 -0
  12. package/dist/types/modes/components/hook-selector.d.ts +20 -0
  13. package/dist/types/modes/components/notifications-settings-editor.d.ts +53 -4
  14. package/dist/types/modes/controllers/extension-ui-controller.d.ts +1 -1
  15. package/dist/types/modes/controllers/selector-controller.d.ts +4 -2
  16. package/dist/types/modes/types.d.ts +6 -0
  17. package/dist/types/sdk/broker/broker.d.ts +5 -1
  18. package/dist/types/sdk/broker/lifecycle-ledger.d.ts +1 -0
  19. package/dist/types/sdk/bus/chat-daemon-control.d.ts +11 -1
  20. package/dist/types/sdk/bus/config.d.ts +75 -113
  21. package/dist/types/sdk/bus/daemon-paths.d.ts +1 -0
  22. package/dist/types/sdk/bus/discord-live-provider.d.ts +8 -2
  23. package/dist/types/sdk/bus/discord-provider.d.ts +19 -0
  24. package/dist/types/sdk/bus/index.d.ts +1 -1
  25. package/dist/types/sdk/bus/notification-orchestration.d.ts +127 -18
  26. package/dist/types/sdk/bus/notification-service.d.ts +54 -4
  27. package/dist/types/sdk/bus/session-control.d.ts +8 -3
  28. package/dist/types/sdk/bus/slack-live-provider.d.ts +9 -2
  29. package/dist/types/sdk/bus/slack-provider.d.ts +22 -0
  30. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +8 -1
  31. package/dist/types/sdk/client/discovery.d.ts +2 -1
  32. package/dist/types/sdk/host/query/handlers.d.ts +2 -0
  33. package/dist/types/sdk/index.d.ts +1 -0
  34. package/dist/types/sdk/providers.d.ts +22 -0
  35. package/dist/types/sdk/session-directory.d.ts +1 -1
  36. package/dist/types/session/internal/managed-session-scope.d.ts +17 -1
  37. package/dist/types/session/internal/managed-session-storage.d.ts +19 -1
  38. package/dist/types/session/internal/native-publish-outcome.d.ts +3 -2
  39. package/dist/types/session/internal/session-open-errors.d.ts +5 -0
  40. package/dist/types/session/session-manager.d.ts +3 -1
  41. package/dist/types/session/session-storage.d.ts +2 -0
  42. package/dist/types/system-prompt.d.ts +12 -1
  43. package/dist/types/tools/composer-bash-policy.d.ts +9 -1
  44. package/package.json +10 -8
  45. package/scripts/build-sdk-package-smoke.ts +51 -0
  46. package/scripts/capture-notifications-settings-showcase.ts +1 -1
  47. package/scripts/verify-gjc-sdk-canonicalization.ts +14 -1
  48. package/src/cli/notify-cli.ts +215 -99
  49. package/src/commands/notify.ts +19 -8
  50. package/src/commands/sdk.ts +54 -0
  51. package/src/config/file-lock.ts +6 -0
  52. package/src/config/model-discovery-manager.ts +50 -8
  53. package/src/config/model-profiles.ts +8 -0
  54. package/src/config/model-registry.ts +866 -76
  55. package/src/config/resolve-config-value.ts +10 -8
  56. package/src/config/settings-schema.ts +25 -15
  57. package/src/config/settings.ts +15 -15
  58. package/src/discovery/agents-md.ts +76 -19
  59. package/src/gjc-runtime/psmux-detect.ts +12 -16
  60. package/src/gjc-runtime/session-state-sidecar.ts +12 -2
  61. package/src/gjc-runtime/tmux-sessions.ts +8 -1
  62. package/src/internal-urls/docs-index.generated.ts +10 -9
  63. package/src/main.ts +43 -7
  64. package/src/modes/DESIGN.md +11 -11
  65. package/src/modes/components/extensions/inspector-panel.ts +9 -5
  66. package/src/modes/components/hook-selector.ts +155 -44
  67. package/src/modes/components/model-selector.ts +242 -49
  68. package/src/modes/components/notifications-settings-editor.ts +631 -85
  69. package/src/modes/controllers/extension-ui-controller.ts +114 -15
  70. package/src/modes/controllers/selector-controller.ts +525 -93
  71. package/src/modes/print-mode.ts +3 -0
  72. package/src/modes/types.ts +6 -0
  73. package/src/prompts/system/system-prompt.md +1 -0
  74. package/src/sdk/broker/broker.ts +54 -10
  75. package/src/sdk/broker/lifecycle-ledger.ts +34 -1
  76. package/src/sdk/broker/lifecycle.ts +47 -50
  77. package/src/sdk/bus/chat-daemon-cli.ts +12 -5
  78. package/src/sdk/bus/chat-daemon-control.ts +35 -6
  79. package/src/sdk/bus/chat-daemon-runtime.ts +16 -2
  80. package/src/sdk/bus/config.ts +534 -286
  81. package/src/sdk/bus/daemon-paths.ts +2 -0
  82. package/src/sdk/bus/discord-live-provider.ts +57 -2
  83. package/src/sdk/bus/discord-provider.ts +21 -0
  84. package/src/sdk/bus/index.ts +97 -35
  85. package/src/sdk/bus/notification-orchestration.ts +371 -68
  86. package/src/sdk/bus/notification-service.ts +571 -137
  87. package/src/sdk/bus/session-control.ts +119 -44
  88. package/src/sdk/bus/slack-live-provider.ts +78 -2
  89. package/src/sdk/bus/slack-provider.ts +24 -0
  90. package/src/sdk/bus/telegram-daemon-cli.ts +9 -2
  91. package/src/sdk/bus/telegram-daemon-contract.ts +8 -1
  92. package/src/sdk/bus/telegram-daemon-control.ts +3 -3
  93. package/src/sdk/bus/telegram-daemon.ts +23 -15
  94. package/src/sdk/client/discovery.ts +10 -4
  95. package/src/sdk/host/query/handlers.ts +20 -1
  96. package/src/sdk/index.ts +1 -0
  97. package/src/sdk/protocol/operation-inventory.generated.json +18 -0
  98. package/src/sdk/protocol/operation-registry.ts +5 -2
  99. package/src/sdk/providers.ts +51 -0
  100. package/src/sdk/session-directory.ts +1 -0
  101. package/src/sdk/session.ts +34 -14
  102. package/src/session/agent-session.ts +109 -37
  103. package/src/session/internal/managed-session-scope.ts +981 -450
  104. package/src/session/internal/managed-session-storage.ts +418 -47
  105. package/src/session/internal/native-publish-outcome.ts +15 -3
  106. package/src/session/internal/session-open-errors.ts +11 -0
  107. package/src/session/session-manager.ts +27 -3
  108. package/src/session/session-storage.ts +72 -5
  109. package/src/slash-commands/builtin-registry.ts +117 -15
  110. package/src/system-prompt.ts +28 -15
  111. package/src/task/index.ts +9 -3
  112. package/src/tools/ask.ts +1 -1
  113. package/src/tools/bash.ts +4 -2
  114. package/src/tools/composer-bash-policy.ts +21 -5
  115. package/src/tools/telegram-send.ts +4 -2
  116. package/src/utils/command-args.ts +19 -30
  117. package/src/utils/lang-from-path.ts +2 -4
@@ -22,9 +22,29 @@ export interface TelegramActivationMarker {
22
22
  export type TelegramActivationMarkers = Record<string, TelegramActivationMarker>;
23
23
  /** Validate and clone activation markers crossing the settings boundary. */
24
24
  export declare function readTelegramActivationMarkers(value?: unknown): TelegramActivationMarkers;
25
+ export type NotificationProvider = "telegram" | "discord" | "slack";
26
+ export type NotificationRuntime = "inactive" | "starting" | "ready" | "attached" | "blocked" | "failed";
27
+ export type ProviderSecretDisposition = "keep" | "replace" | "remove";
28
+ export type ProviderResolutionIssueCode = "missing" | "blank" | "wrong_type" | "invalid_container" | "contradictory";
29
+ export interface ProviderResolutionIssue {
30
+ path: string;
31
+ code: ProviderResolutionIssueCode;
32
+ }
33
+ export type NotificationProviderIssueCode = ProviderResolutionIssueCode;
34
+ export type NotificationProviderIssue = ProviderResolutionIssue;
35
+ export interface ProviderResolution {
36
+ provider: NotificationProvider;
37
+ configured: boolean;
38
+ quarantined: boolean;
39
+ desiredEnabled: boolean;
40
+ desiredSource: "explicit" | "legacy";
41
+ effectiveEnabled: boolean;
42
+ issues: readonly ProviderResolutionIssue[];
43
+ }
25
44
  export interface NotificationSettingsSnapshot {
26
45
  enabled: boolean;
27
46
  telegram: {
47
+ enabled?: boolean;
28
48
  botToken?: string;
29
49
  chatId?: string;
30
50
  activation?: Record<string, unknown>;
@@ -49,12 +69,14 @@ export interface NotificationSettingsSnapshot {
49
69
  };
50
70
  };
51
71
  discord: {
72
+ enabled?: boolean;
52
73
  botToken?: string;
53
74
  applicationId?: string;
54
75
  guildId?: string;
55
76
  parentChannelId?: string;
56
77
  };
57
78
  slack: {
79
+ enabled?: boolean;
58
80
  botToken?: string;
59
81
  appToken?: string;
60
82
  workspaceId?: string;
@@ -65,33 +87,38 @@ export interface NotificationSettingsSnapshot {
65
87
  verbosity: "lean" | "verbose";
66
88
  sessionScope: "all" | "primary";
67
89
  idleTimeoutMs: number;
90
+ /** Safe provider-local validation issues. Raw values never cross this boundary. */
91
+ providerIssues?: Partial<Record<NotificationProvider, readonly ProviderResolutionIssue[]>>;
68
92
  }
69
93
  /**
70
94
  * Narrow settings boundary for remote notification identity and behavior.
71
- * Implementations must return only user-global values, with schema defaults
72
- * applied; project settings and runtime overrides are intentionally excluded.
95
+ * Implementations return only user-global values; project settings and runtime
96
+ * overrides are intentionally excluded.
73
97
  */
74
98
  export interface NotificationSettingsReader {
75
99
  getNotificationSettingsSnapshot(): NotificationSettingsSnapshot;
76
100
  getAgentDir(): string;
77
101
  }
78
- /**
79
- * Validate and snapshot the raw global notification configuration used by both
80
- * the interactive host and the lightweight daemon process.
81
- */
102
+ /** Validate and snapshot raw global notification configuration without leaking invalid provider values. */
82
103
  export declare function parseNotificationSettingsSnapshot(rawConfig?: unknown): NotificationSettingsSnapshot;
83
104
  export interface NotificationConfig {
84
105
  enabled: boolean;
85
106
  botToken?: string;
86
107
  activation?: TelegramActivationMarkers;
87
108
  chatId?: string;
109
+ /** Optional nested Telegram intent metadata; absent means legacy configuration. */
110
+ telegram?: {
111
+ enabled?: boolean;
112
+ };
88
113
  discord: {
114
+ enabled?: boolean;
89
115
  botToken?: string;
90
116
  applicationId?: string;
91
117
  guildId?: string;
92
118
  parentChannelId?: string;
93
119
  };
94
120
  slack: {
121
+ enabled?: boolean;
95
122
  botToken?: string;
96
123
  appToken?: string;
97
124
  workspaceId?: string;
@@ -100,11 +127,6 @@ export interface NotificationConfig {
100
127
  };
101
128
  redact: boolean;
102
129
  verbosity: "lean" | "verbose";
103
- /**
104
- * Which sessions may register a notification endpoint. `all` (default)
105
- * preserves historical behavior; `primary` suppresses GJC-spawned children
106
- * (those carrying {@link SPAWN_PROVENANCE_ENV}) unless they explicitly opt in.
107
- */
108
130
  sessionScope: "all" | "primary";
109
131
  sound: "all" | "important" | "none";
110
132
  idleTimeoutMs: number;
@@ -124,62 +146,24 @@ export interface NotificationConfig {
124
146
  enabled: boolean;
125
147
  };
126
148
  topics: {
127
- /**
128
- * Optional Telegram forum-topic name template with `{repo}`, `{branch}`,
129
- * and `{title}` placeholders. Unset preserves the built-in
130
- * `{repo}/{branch} - {title}` composition (with its title/repo/branch
131
- * fallbacks).
132
- */
133
149
  nameTemplate?: string;
134
150
  };
151
+ providerIssues?: Partial<Record<NotificationProvider, readonly ProviderResolutionIssue[]>>;
135
152
  }
136
153
  /** Read typed global-only notification config from a narrow settings reader. */
137
154
  export declare function getNotificationConfig(settings: NotificationSettingsReader): NotificationConfig;
138
- declare const notificationConfigSchema: z.ZodObject<{
139
- notifications: z.ZodOptional<z.ZodObject<{
140
- enabled: z.ZodOptional<z.ZodBoolean>;
141
- discord: z.ZodOptional<z.ZodObject<{
142
- botToken: z.ZodOptional<z.ZodString>;
143
- applicationId: z.ZodOptional<z.ZodString>;
144
- guildId: z.ZodOptional<z.ZodString>;
145
- parentChannelId: z.ZodOptional<z.ZodString>;
146
- }, z.core.$loose>>;
147
- slack: z.ZodOptional<z.ZodObject<{
148
- botToken: z.ZodOptional<z.ZodString>;
149
- appToken: z.ZodOptional<z.ZodString>;
150
- workspaceId: z.ZodOptional<z.ZodString>;
151
- channelId: z.ZodOptional<z.ZodString>;
152
- authorizedUserId: z.ZodOptional<z.ZodString>;
153
- }, z.core.$loose>>;
154
- redact: z.ZodOptional<z.ZodBoolean>;
155
- verbosity: z.ZodOptional<z.ZodEnum<{
156
- lean: "lean";
157
- verbose: "verbose";
158
- }>>;
159
- }, z.core.$loose>>;
160
- }, z.core.$loose>;
155
+ declare const notificationConfigSchema: z.ZodObject<{}, z.core.$loose>;
161
156
  type NotificationConfigFile = z.infer<typeof notificationConfigSchema>;
162
157
  /** Read daemon settings through the canonical validated config-file boundary. */
163
158
  export declare function loadNotificationConfigFile(agentDir: string): LoadResult<NotificationConfigFile>;
164
- export declare function notificationConfigFromFile(value: NotificationConfigFile): Pick<NotificationConfig, "enabled" | "discord" | "slack" | "redact" | "verbosity">;
159
+ export declare function notificationConfigFromFile(value: NotificationConfigFile): NotificationConfig;
165
160
  export declare function hasNonBlankValue(value: string | undefined): boolean;
166
- /**
167
- * Resolve live assistant streaming independently of generic notification
168
- * lifecycle enablement. Explicit environment values override the durable
169
- * Telegram preference; otherwise streaming is available only to an active
170
- * configured Telegram identity.
171
- */
172
- export declare function isNotificationStreamingEnabled(input: {
173
- cfg: NotificationConfig;
174
- env: NodeJS.ProcessEnv;
175
- }): boolean;
176
- /** Is Telegram configured with usable non-blank boundary credentials? */
177
- export declare function isTelegramConfigured(cfg: NotificationConfig): cfg is NotificationConfig & {
161
+ export declare function isProviderComplete(cfg: NotificationConfig, provider: NotificationProvider): boolean;
162
+ export declare function isTelegramComplete(cfg: NotificationConfig): cfg is NotificationConfig & {
178
163
  botToken: string;
179
164
  chatId: string;
180
165
  };
181
- /** Is Discord configured with all credentials and routing identifiers required by its daemon. */
182
- export declare function isDiscordConfigured(cfg: Pick<NotificationConfig, "enabled" | "discord">): cfg is Pick<NotificationConfig, "enabled"> & {
166
+ export declare function isDiscordComplete(cfg: NotificationConfig): cfg is NotificationConfig & {
183
167
  discord: {
184
168
  botToken: string;
185
169
  applicationId: string;
@@ -187,8 +171,7 @@ export declare function isDiscordConfigured(cfg: Pick<NotificationConfig, "enabl
187
171
  parentChannelId: string;
188
172
  };
189
173
  };
190
- /** Is Slack configured with both SDK tokens and its workspace/channel routing identifiers. */
191
- export declare function isSlackConfigured(cfg: Pick<NotificationConfig, "enabled" | "slack">): cfg is Pick<NotificationConfig, "enabled"> & {
174
+ export declare function isSlackComplete(cfg: NotificationConfig): cfg is NotificationConfig & {
192
175
  slack: {
193
176
  botToken: string;
194
177
  appToken: string;
@@ -196,80 +179,59 @@ export declare function isSlackConfigured(cfg: Pick<NotificationConfig, "enabled
196
179
  channelId: string;
197
180
  };
198
181
  };
199
- /** Is global config sufficient for auto-on (enabled + at least one configured adapter)? */
200
- export declare function isGloballyConfigured(cfg: NotificationConfig): boolean;
182
+ export declare function resolveNotificationProvider(cfg: NotificationConfig, provider: NotificationProvider): ProviderResolution;
183
+ export declare function isProviderEffectivelyEnabled(cfg: NotificationConfig, provider: NotificationProvider): boolean;
184
+ export declare function hasAnyCompleteProvider(cfg: NotificationConfig): boolean;
185
+ export declare function hasAnyEffectivelyEnabledProvider(cfg: NotificationConfig): boolean;
201
186
  /**
202
- * Per-run opt-out for completion notifications, honored before settings lookups.
203
- *
204
- * `GJC_NOTIFY=off` (also `0` / `false`, case-insensitive) suppresses the
205
- * completion notification surface for this process only. `config.yml` is
206
- * untouched and child processes inherit the env var, which lets non-interactive
207
- * fleet runs (`gjc -p --no-session`) stay silent even when a user-level/global
208
- * completion notification configuration is enabled.
187
+ * Resolve generic live-stream policy. This policy only governs automatic
188
+ * current-session frames; it never changes durable provider eligibility.
209
189
  */
190
+ export type GenericNotificationSessionSource = "hard_opt_out" | "session_local_off" | "explicit_env" | "token_env" | "configured_provider" | "session_scope" | "none";
191
+ export interface GenericNotificationSessionEligibility {
192
+ enabled: boolean;
193
+ source: GenericNotificationSessionSource;
194
+ }
195
+ export type GenericNotificationStreamSource = "session_not_admitted" | "env_on" | "env_off" | "durable_telegram" | "none";
196
+ export interface GenericNotificationStreamPolicy {
197
+ enabled: boolean;
198
+ source: GenericNotificationStreamSource;
199
+ }
200
+ export interface GenericNotificationSessionEligibilityInput {
201
+ cfg: NotificationConfig;
202
+ env: NodeJS.ProcessEnv;
203
+ sessionDisabled: boolean;
204
+ spawnedByGjc?: boolean;
205
+ }
206
+ export declare function resolveGenericNotificationSessionEligibility(input: GenericNotificationSessionEligibilityInput): GenericNotificationSessionEligibility;
207
+ export declare function resolveGenericNotificationStreamPolicy(input: {
208
+ cfg: NotificationConfig;
209
+ env: NodeJS.ProcessEnv;
210
+ genericSessionEnabled: boolean;
211
+ }): GenericNotificationStreamPolicy;
210
212
  export declare function completionNotifyDisabledByEnv(env: NodeJS.ProcessEnv): boolean;
211
- /** Canonical host eligibility for the dormant notification session surface. */
212
213
  export interface NotificationHostEligibilityInput {
213
214
  env: NodeJS.ProcessEnv;
214
- /** False for host modes that cannot own a notification session endpoint. */
215
215
  hostModeSupported?: boolean;
216
- /** Task recursion depth; helper/subagent sessions must not own remote surfaces. */
217
216
  taskDepth?: number;
218
- /** Parent subagent id/prefix; present for helper/subagent sessions even when depth is omitted. */
219
217
  parentTaskPrefix?: string;
220
- /** Role-agent type/name; present for task sessions even if depth metadata is lost. */
221
218
  currentAgentType?: string;
222
- /** Canonical global session scope; absent preserves the default `all` behavior. */
223
219
  sessionScope?: NotificationConfig["sessionScope"];
224
- /** Whether this process was spawned by one of GJC's marked child spawn sites. */
225
220
  spawnedByGjc?: boolean;
226
221
  }
227
- /**
228
- * Resolve whether this host may receive the dormant notification controller.
229
- * This intentionally says nothing about whether an adapter is configured: an
230
- * eligible unconfigured host still gets a zero-side-effect control surface.
231
- */
232
- export declare function isNotificationHostEligible(input: NotificationHostEligibilityInput): boolean;
233
- /**
234
- * Legacy compatibility helper for callers that require both host eligibility
235
- * and a currently configured or explicit notification runtime.
236
- */
237
- export declare function shouldRegisterNotificationsExtension(input: {
222
+ /** Generic host eligibility for the dormant automatic notification surface. */
223
+ export declare function isGenericNotificationHostEligible(input: NotificationHostEligibilityInput): boolean;
224
+ export interface GenericNotificationRegistrationInput {
238
225
  env: NodeJS.ProcessEnv;
239
226
  cfg?: NotificationConfig;
240
- /** Task recursion depth; helper/subagent sessions must not spawn remote surfaces. */
241
227
  taskDepth?: number;
242
- /** Parent subagent id/prefix; present for helper/subagent sessions even when depth is omitted. */
243
228
  parentTaskPrefix?: string;
244
- /** Role-agent type/name; present for task sessions even if depth metadata is lost. */
245
229
  currentAgentType?: string;
246
- /**
247
- * True when this session was launched by one of GJC's own programmatic
248
- * separate-process child spawn sites (marked via {@link SPAWN_PROVENANCE_ENV}).
249
- * Under `notifications.sessionScope = "primary"` such children are suppressed
250
- * unless they explicitly opt in, so an interactive parent that fans out work
251
- * does not flood the paired chat with topics for children the user never
252
- * asked for. User-opened sessions (CLI/tmux/headless) never carry the marker.
253
- */
254
- spawnedByGjc?: boolean;
255
- }): boolean;
256
- /**
257
- * Resolve whether THIS session should run notifications.
258
- * Precedence (highest first):
259
- * 1) env.GJC_NOTIFICATIONS === "0" -> false (hard opt-out)
260
- * 2) sessionDisabled === true -> false (local /notify off)
261
- * 3) env.GJC_NOTIFICATIONS === "1" || env.GJC_NOTIFICATIONS_TOKEN present -> true (legacy explicit)
262
- * 4) spawned GJC child with `sessionScope=primary` -> false
263
- * 5) isGloballyConfigured(cfg) -> true (global auto-on)
264
- * 6) otherwise false
265
- */
266
- export declare function isSessionNotificationsEnabled(input: {
267
- cfg: NotificationConfig;
268
- env: NodeJS.ProcessEnv;
269
- sessionDisabled: boolean;
270
- /** This process was programmatically spawned by GJC (consumed at SDK startup). */
271
230
  spawnedByGjc?: boolean;
272
- }): boolean;
231
+ }
232
+ /** Generic registration admission; direct provider actions do not call this helper. */
233
+ export declare function shouldRegisterGenericNotificationsExtension(input: GenericNotificationRegistrationInput): boolean;
234
+ export declare function isGenericNotificationSessionEnabled(input: GenericNotificationSessionEligibilityInput): boolean;
273
235
  /** Mask a bot token for display: first 4 chars + "…" + "(len N)"; "(unset)" when undefined/empty. Never reveal full token. */
274
236
  export declare function maskToken(token: string | undefined): string;
275
237
  /** Stable non-reversible fingerprint of a token: sha256 hex, first 12 chars. */
@@ -13,6 +13,7 @@ export interface DaemonPaths {
13
13
  ownership: string;
14
14
  roots: string;
15
15
  steal: string;
16
+ diagnostic: string;
16
17
  aliases: string;
17
18
  seenUpdates: string;
18
19
  }
@@ -1,4 +1,4 @@
1
- import type { DiscordInboundEvent, DiscordMessageComponent, DiscordProvider, DiscordThread } from "./discord-provider";
1
+ import type { DiscordConfigurationProbeResult, DiscordDiagnosticProvider, DiscordInboundEvent, DiscordMessageComponent, DiscordOneShotTestResult, DiscordProvider, DiscordThread } from "./discord-provider";
2
2
  export interface DiscordGatewaySocket {
3
3
  readonly readyState: number;
4
4
  send(data: string): void;
@@ -21,7 +21,7 @@ export interface DiscordLiveProviderOptions {
21
21
  apiBaseUrl?: string;
22
22
  }
23
23
  /** Discord REST/Gateway implementation. The only credential is held privately and is never emitted. */
24
- export declare class DiscordLiveProvider implements DiscordProvider {
24
+ export declare class DiscordLiveProvider implements DiscordProvider, DiscordDiagnosticProvider {
25
25
  #private;
26
26
  readonly applicationId: string;
27
27
  constructor(options: DiscordLiveProviderOptions);
@@ -64,6 +64,12 @@ export declare class DiscordLiveProvider implements DiscordProvider {
64
64
  }): Promise<{
65
65
  id: string;
66
66
  } | null>;
67
+ probeConfiguration(signal?: AbortSignal): Promise<DiscordConfigurationProbeResult>;
68
+ sendOneShotTest(input: {
69
+ channelId: string;
70
+ message: string;
71
+ signal?: AbortSignal;
72
+ }): Promise<DiscordOneShotTestResult>;
67
73
  start(onEvent: (event: DiscordInboundEvent) => Promise<void>): Promise<void>;
68
74
  stop(): Promise<void>;
69
75
  }
@@ -36,6 +36,25 @@ export interface DiscordMessageComponent {
36
36
  }>;
37
37
  }>;
38
38
  }
39
+ export interface DiscordConfigurationProbeResult {
40
+ ok: boolean;
41
+ detail: string;
42
+ botUserId?: string;
43
+ }
44
+ export interface DiscordOneShotTestResult {
45
+ ok: boolean;
46
+ detail: string;
47
+ messageId?: string;
48
+ uncertain?: boolean;
49
+ }
50
+ export interface DiscordDiagnosticProvider {
51
+ probeConfiguration(signal?: AbortSignal): Promise<DiscordConfigurationProbeResult>;
52
+ sendOneShotTest(input: {
53
+ channelId: string;
54
+ message: string;
55
+ signal?: AbortSignal;
56
+ }): Promise<DiscordOneShotTestResult>;
57
+ }
39
58
  export interface DiscordProvider {
40
59
  readonly applicationId: string;
41
60
  readonly botUserId: string;
@@ -337,7 +337,7 @@ interface SessionRuntime {
337
337
  notificationRootRegistration?: {
338
338
  settings: Settings;
339
339
  cwd: string;
340
- registrationToken?: string;
340
+ registrationToken: string;
341
341
  };
342
342
  verbosity: "lean" | "verbose";
343
343
  sessionTag: string;
@@ -1,5 +1,5 @@
1
1
  import type { RawSettings, SettingsAtomicPatch, SettingsAtomicReceipt } from "../../config/settings";
2
- import { type NotificationSettingsReader, type TelegramActivationMarker, type TelegramActivationMarkers, type TelegramActivationReason, type TelegramActivationState, telegramActivationIdentity } from "./config";
2
+ import { type NotificationProvider, type NotificationSettingsReader, type ProviderSecretDisposition, type TelegramActivationMarker, type TelegramActivationMarkers, type TelegramActivationReason, type TelegramActivationState, telegramActivationIdentity } from "./config";
3
3
  import { type EnsureTelegramDaemonDetailedResult, type TelegramDaemonFs } from "./telegram-daemon";
4
4
  export type { TelegramActivationMarker, TelegramActivationMarkers, TelegramActivationReason, TelegramActivationState };
5
5
  export { telegramActivationIdentity };
@@ -41,7 +41,7 @@ export interface ProposedTelegramIdentityPreflightInput {
41
41
  export declare function proposedTelegramIdentity(input: ProposedTelegramIdentityPreflightInput): Promise<ProposedTelegramIdentity>;
42
42
  export interface NotificationConfigurationWriter extends NotificationSettingsReader {
43
43
  commitAtomicBatch(patches: readonly SettingsAtomicPatch[]): Promise<SettingsAtomicReceipt>;
44
- commitAtomicBatchWithCurrent?(buildPatches: (current: Readonly<RawSettings>) => Promise<readonly SettingsAtomicPatch[]> | readonly SettingsAtomicPatch[]): Promise<SettingsAtomicReceipt>;
44
+ commitAtomicBatchWithCurrent(buildPatches: (current: Readonly<RawSettings>) => Promise<readonly SettingsAtomicPatch[]> | readonly SettingsAtomicPatch[]): Promise<SettingsAtomicReceipt>;
45
45
  }
46
46
  export declare function createTelegramActivationMarker(input: {
47
47
  botToken: string;
@@ -55,23 +55,16 @@ export declare function observedTelegramActivationMarker(settings: NotificationS
55
55
  export declare function persistTelegramActivationMarker(settings: NotificationConfigurationWriter, marker: TelegramActivationMarker): Promise<SettingsAtomicReceipt>;
56
56
  /** Clear only the exact marker that was safely reconciled. */
57
57
  export declare function clearTelegramActivationMarker(settings: NotificationConfigurationWriter, marker: TelegramActivationMarker): Promise<void>;
58
- export type CompleteNonTelegramAdapter = "discord" | "slack";
59
58
  export type SaveTelegramInactiveAvailability = {
60
59
  available: true;
61
60
  };
62
- /**
63
- * Telegram-specific activation markers let Save inactive preserve globally
64
- * enabled Discord and Slack adapters, so the action is always available.
65
- */
61
+ /** Telegram activation markers make Save inactive independent of sibling providers. */
66
62
  export declare function getSaveTelegramInactiveAvailability(_settings: NotificationSettingsReader): SaveTelegramInactiveAvailability;
67
63
  export type SaveTelegramInactiveResult = {
68
64
  status: "saved_inactive";
69
65
  receipt: SettingsAtomicReceipt;
70
66
  };
71
- /**
72
- * Atomically persist Telegram credentials with a non-secret inactive marker.
73
- * Global notifications are disabled only when Telegram is the sole complete adapter.
74
- */
67
+ /** Atomically persist Telegram credentials, desired-off intent, and its inactive marker. */
75
68
  export declare function saveTelegramInactive(input: {
76
69
  settings: NotificationConfigurationWriter;
77
70
  botToken: string;
@@ -117,9 +110,16 @@ export interface TelegramPostCommitActivation {
117
110
  }
118
111
  export type PostCommitTelegramActivationResult = {
119
112
  status: "activated";
120
- reconnect: Exclude<TelegramDaemonReconnectOutcome, "blocked_identity">;
113
+ receipt: SettingsAtomicReceipt;
114
+ reconnect: Exclude<TelegramDaemonReconnectOutcome, "blocked_identity" | "disabled">;
115
+ } | {
116
+ status: "activation_failed";
117
+ receipt: SettingsAtomicReceipt;
118
+ reconnect?: TelegramDaemonReconnectOutcome;
119
+ message: string;
121
120
  } | {
122
121
  status: "blocked_identity";
122
+ receipt: SettingsAtomicReceipt;
123
123
  message: string;
124
124
  restore(): Promise<BlockedTelegramRestoreResult>;
125
125
  retainCommitted(): void;
@@ -135,12 +135,6 @@ export type BlockedTelegramRestoreResult = {
135
135
  } | {
136
136
  status: "still_blocked";
137
137
  };
138
- /**
139
- * Complete a committed Telegram update in the required order: identity
140
- * reconnect first, normal session reconciliation second. A post-commit foreign
141
- * owner race stops the endpoint, durably marks only that Telegram identity
142
- * inactive, then exposes an ordered CAS restore/retain choice.
143
- */
144
138
  export declare function reconcileCommittedTelegramConfiguration(input: {
145
139
  receipt: SettingsAtomicReceipt;
146
140
  activation: TelegramPostCommitActivation;
@@ -169,3 +163,118 @@ export declare function saveTelegramConfiguration(input: {
169
163
  preflight?: (input: Omit<ProposedTelegramIdentityPreflightInput, "settings">) => Promise<ProposedTelegramIdentity>;
170
164
  activation?: TelegramPostCommitActivation;
171
165
  }): Promise<SaveTelegramConfigurationResult>;
166
+ export type ProviderSecretMutation = {
167
+ action: "keep";
168
+ } | {
169
+ action: "replace";
170
+ value: string;
171
+ } | {
172
+ action: "remove";
173
+ };
174
+ export interface TelegramProviderConfigurationMutation {
175
+ provider: "telegram";
176
+ botToken: ProviderSecretMutation;
177
+ chatId?: string;
178
+ richEnabled?: boolean;
179
+ richDraftEnabled?: boolean;
180
+ streamingEnabled?: boolean;
181
+ }
182
+ export interface DiscordProviderConfigurationMutation {
183
+ provider: "discord";
184
+ botToken: ProviderSecretMutation;
185
+ applicationId?: string;
186
+ guildId?: string;
187
+ parentChannelId?: string;
188
+ }
189
+ export interface SlackProviderConfigurationMutation {
190
+ provider: "slack";
191
+ botToken: ProviderSecretMutation;
192
+ appToken: ProviderSecretMutation;
193
+ workspaceId?: string;
194
+ channelId?: string;
195
+ authorizedUserId?: string | null;
196
+ }
197
+ export type NotificationProviderConfigurationMutation = TelegramProviderConfigurationMutation | DiscordProviderConfigurationMutation | SlackProviderConfigurationMutation;
198
+ export interface NotificationProviderRuntimeAuthority {
199
+ activate(provider: NotificationProvider): Promise<void>;
200
+ deactivate(provider: NotificationProvider): Promise<void>;
201
+ }
202
+ export type NotificationProviderMutationResult = {
203
+ status: "saved";
204
+ receipt: SettingsAtomicReceipt;
205
+ observerFailed: boolean;
206
+ } | {
207
+ status: "activated";
208
+ receipt: SettingsAtomicReceipt;
209
+ observerFailed: boolean;
210
+ } | {
211
+ status: "activation_failed";
212
+ receipt: SettingsAtomicReceipt;
213
+ observerFailed: boolean;
214
+ } | {
215
+ status: "deactivation_failed";
216
+ receipt: SettingsAtomicReceipt;
217
+ observerFailed: boolean;
218
+ } | {
219
+ status: "commit_failed";
220
+ };
221
+ /**
222
+ * Sole CAS-backed provider configuration authority. It commits one selected-provider
223
+ * batch before observer/runtime effects and never reports rollback after a durable save.
224
+ */
225
+ export declare function mutateNotificationProvider(input: {
226
+ settings: NotificationConfigurationWriter;
227
+ mutation: NotificationProviderConfigurationMutation;
228
+ desiredEnabled?: boolean;
229
+ configureAndActivate?: boolean;
230
+ notifyConfigChanged?: () => Promise<void> | void;
231
+ redact?: boolean;
232
+ runtime?: NotificationProviderRuntimeAuthority;
233
+ signal?: AbortSignal;
234
+ }): Promise<NotificationProviderMutationResult>;
235
+ export type NotificationProviderRemovalResult = {
236
+ status: "removed";
237
+ receipt: SettingsAtomicReceipt;
238
+ observerFailed: boolean;
239
+ } | {
240
+ status: "deactivation_failed";
241
+ receipt?: SettingsAtomicReceipt;
242
+ observerFailed?: boolean;
243
+ } | {
244
+ status: "commit_failed_after_teardown";
245
+ } | {
246
+ status: "commit_failed";
247
+ };
248
+ /** Remove only one provider. Telegram keeps teardown-before-delete; chat providers deactivate after commit. */
249
+ export declare function removeNotificationProvider(input: {
250
+ settings: NotificationConfigurationWriter;
251
+ provider: NotificationProvider;
252
+ runtime: NotificationProviderRuntimeAuthority;
253
+ notifyConfigChanged?: () => Promise<void> | void;
254
+ }): Promise<NotificationProviderRemovalResult>;
255
+ export type GlobalNotificationMutationResult = {
256
+ status: "saved";
257
+ receipt: SettingsAtomicReceipt;
258
+ observerFailed: boolean;
259
+ } | {
260
+ status: "global_deactivation_partial";
261
+ receipt: SettingsAtomicReceipt;
262
+ failed: readonly NotificationProvider[];
263
+ observerFailed: boolean;
264
+ } | {
265
+ status: "global_activation_partial";
266
+ receipt: SettingsAtomicReceipt;
267
+ failed: readonly NotificationProvider[];
268
+ observerFailed: boolean;
269
+ } | {
270
+ status: "commit_failed";
271
+ };
272
+ /** Toggle only the global master. Provider configuration and desired intent are preserved. */
273
+ export declare function setGlobalNotificationsEnabled(input: {
274
+ settings: NotificationConfigurationWriter;
275
+ enabled: boolean;
276
+ runtime?: NotificationProviderRuntimeAuthority;
277
+ notifyConfigChanged?: () => Promise<void> | void;
278
+ }): Promise<GlobalNotificationMutationResult>;
279
+ /** Safe helper for UI contracts that must retain only the disposition, never replacement material. */
280
+ export declare function providerSecretDisposition(mutation: ProviderSecretMutation): ProviderSecretDisposition;