@sayknow-cli/coding-agent 0.2.7 → 0.3.1

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 (216) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/types/async/job-manager.d.ts +3 -1
  3. package/dist/types/cli/daemon-cli.d.ts +25 -0
  4. package/dist/types/cli/notify-cli.d.ts +25 -0
  5. package/dist/types/cli/setup-cli.d.ts +20 -1
  6. package/dist/types/commands/daemon.d.ts +41 -0
  7. package/dist/types/commands/notify.d.ts +41 -0
  8. package/dist/types/config/model-profile-activation.d.ts +12 -0
  9. package/dist/types/config/model-profiles.d.ts +2 -1
  10. package/dist/types/config/model-registry.d.ts +3 -3
  11. package/dist/types/config/models-config-schema.d.ts +5 -0
  12. package/dist/types/config/settings-schema.d.ts +136 -3
  13. package/dist/types/coordinator/contract.d.ts +1 -1
  14. package/dist/types/daemon/builtin.d.ts +20 -0
  15. package/dist/types/daemon/control-types.d.ts +57 -0
  16. package/dist/types/daemon/runtime.d.ts +25 -0
  17. package/dist/types/extensibility/extensions/types.d.ts +8 -0
  18. package/dist/types/extensibility/shared-events.d.ts +1 -0
  19. package/dist/types/i18n/messages/en.d.ts +1 -0
  20. package/dist/types/lsp/types.d.ts +2 -0
  21. package/dist/types/modes/components/oauth-selector.d.ts +2 -0
  22. package/dist/types/modes/components/settings-defs.d.ts +1 -0
  23. package/dist/types/modes/controllers/selector-controller.d.ts +2 -2
  24. package/dist/types/modes/interactive-mode.d.ts +1 -1
  25. package/dist/types/modes/shared/agent-wire/unattended-session.d.ts +10 -0
  26. package/dist/types/modes/theme/theme.d.ts +1 -1
  27. package/dist/types/modes/types.d.ts +7 -1
  28. package/dist/types/notifications/attachment-registry.d.ts +17 -0
  29. package/dist/types/notifications/chat-adapters.d.ts +9 -0
  30. package/dist/types/notifications/config-commands.d.ts +26 -0
  31. package/dist/types/notifications/config.d.ts +69 -0
  32. package/dist/types/notifications/engine.d.ts +59 -0
  33. package/dist/types/notifications/helpers.d.ts +55 -0
  34. package/dist/types/notifications/html-format.d.ts +62 -0
  35. package/dist/types/notifications/index.d.ts +28 -0
  36. package/dist/types/notifications/managed-daemon.d.ts +48 -0
  37. package/dist/types/notifications/rate-limit-pool.d.ts +93 -0
  38. package/dist/types/notifications/telegram-cli.d.ts +19 -0
  39. package/dist/types/notifications/telegram-daemon-cli.d.ts +11 -0
  40. package/dist/types/notifications/telegram-daemon-control.d.ts +56 -0
  41. package/dist/types/notifications/telegram-daemon.d.ts +295 -0
  42. package/dist/types/notifications/telegram-reference.d.ts +111 -0
  43. package/dist/types/notifications/threaded-inbound.d.ts +77 -0
  44. package/dist/types/notifications/threaded-render.d.ts +71 -0
  45. package/dist/types/notifications/topic-registry.d.ts +67 -0
  46. package/dist/types/rlm/index.d.ts +12 -0
  47. package/dist/types/session/agent-session.d.ts +41 -2
  48. package/dist/types/session/auth-storage.d.ts +1 -1
  49. package/dist/types/setup/credential-auto-import.d.ts +63 -0
  50. package/dist/types/setup/credential-import.d.ts +3 -0
  51. package/dist/types/setup/host-plugin-setup.d.ts +39 -0
  52. package/dist/types/skc-runtime/launch-tmux.d.ts +1 -0
  53. package/dist/types/skc-runtime/ralplan-runtime.d.ts +1 -1
  54. package/dist/types/skc-runtime/state-writer.d.ts +2 -0
  55. package/dist/types/skc-runtime/tmux-common.d.ts +3 -0
  56. package/dist/types/skc-runtime/tmux-sessions.d.ts +2 -0
  57. package/dist/types/skc-runtime/ultragoal-guard.d.ts +15 -0
  58. package/dist/types/skc-runtime/ultragoal-runtime.d.ts +14 -0
  59. package/dist/types/tools/ask-answer-registry.d.ts +13 -0
  60. package/dist/types/tools/fetch.d.ts +23 -0
  61. package/dist/types/tools/index.d.ts +19 -0
  62. package/dist/types/tools/subagent.d.ts +3 -0
  63. package/dist/types/tools/telegram-send.d.ts +32 -0
  64. package/dist/types/web/insane/bridge.d.ts +103 -0
  65. package/dist/types/web/insane/url-guard.d.ts +22 -0
  66. package/dist/types/web/search/provider.d.ts +18 -1
  67. package/dist/types/web/search/providers/insane.d.ts +53 -0
  68. package/dist/types/web/search/providers/text-citations.d.ts +23 -0
  69. package/dist/types/web/search/types.d.ts +12 -4
  70. package/package.json +10 -8
  71. package/scripts/build-binary.ts +3 -0
  72. package/scripts/verify-insane-vendor.ts +132 -0
  73. package/src/async/job-manager.ts +5 -1
  74. package/src/cli/args.ts +1 -1
  75. package/src/cli/daemon-cli.ts +122 -0
  76. package/src/cli/fast-help.ts +1 -1
  77. package/src/cli/notify-cli.ts +421 -0
  78. package/src/cli/setup-cli.ts +173 -84
  79. package/src/cli.ts +3 -3
  80. package/src/commands/daemon.ts +47 -0
  81. package/src/commands/notify.ts +61 -0
  82. package/src/commands/setup.ts +11 -1
  83. package/src/commands/team.ts +1 -1
  84. package/src/config/model-profile-activation.ts +74 -5
  85. package/src/config/model-profiles.ts +7 -4
  86. package/src/config/model-registry.ts +6 -3
  87. package/src/config/models-config-schema.ts +1 -1
  88. package/src/config/settings-schema.ts +143 -0
  89. package/src/coordinator/contract.ts +3 -0
  90. package/src/coordinator-mcp/server.ts +270 -1
  91. package/src/daemon/builtin.ts +46 -0
  92. package/src/daemon/control-types.ts +65 -0
  93. package/src/daemon/runtime.ts +51 -0
  94. package/src/defaults/skc/rules/ponytail.md +68 -0
  95. package/src/defaults/skc/skills/ralplan/SKILL.md +11 -4
  96. package/src/defaults/skc/skills/ultragoal/SKILL.md +16 -0
  97. package/src/edit/modes/replace.ts +1 -1
  98. package/src/extensibility/extensions/runner.ts +4 -0
  99. package/src/extensibility/extensions/types.ts +8 -0
  100. package/src/extensibility/shared-events.ts +1 -0
  101. package/src/goals/tools/goal-tool.ts +11 -2
  102. package/src/hashline/hash.ts +1 -1
  103. package/src/i18n/messages/de.ts +1 -0
  104. package/src/i18n/messages/en.ts +1 -0
  105. package/src/i18n/messages/es.ts +1 -0
  106. package/src/i18n/messages/fr.ts +1 -0
  107. package/src/i18n/messages/ja.ts +1 -0
  108. package/src/i18n/messages/ko.ts +1 -0
  109. package/src/i18n/messages/zh.ts +1 -0
  110. package/src/internal-urls/docs-index.generated.ts +12 -10
  111. package/src/lsp/config.ts +16 -3
  112. package/src/lsp/defaults.json +7 -0
  113. package/src/lsp/types.ts +2 -0
  114. package/src/main.ts +30 -0
  115. package/src/modes/acp/acp-event-mapper.ts +1 -0
  116. package/src/modes/components/hook-editor.ts +7 -2
  117. package/src/modes/components/oauth-selector.ts +19 -0
  118. package/src/modes/components/settings-defs.ts +2 -1
  119. package/src/modes/components/settings-selector.ts +7 -2
  120. package/src/modes/controllers/event-controller.ts +35 -0
  121. package/src/modes/controllers/selector-controller.ts +80 -17
  122. package/src/modes/interactive-mode.ts +52 -4
  123. package/src/modes/runtime-init.ts +1 -0
  124. package/src/modes/shared/agent-wire/event-contract.ts +1 -0
  125. package/src/modes/shared/agent-wire/event-envelope.ts +1 -0
  126. package/src/modes/shared/agent-wire/event-observation.ts +16 -0
  127. package/src/modes/shared/agent-wire/unattended-session.ts +22 -0
  128. package/src/modes/theme/theme.ts +4 -0
  129. package/src/modes/types.ts +7 -1
  130. package/src/modes/utils/context-usage.ts +2 -2
  131. package/src/modes/utils/ui-helpers.ts +23 -0
  132. package/src/notifications/attachment-registry.ts +23 -0
  133. package/src/notifications/chat-adapters.ts +147 -0
  134. package/src/notifications/config-commands.ts +50 -0
  135. package/src/notifications/config.ts +128 -0
  136. package/src/notifications/engine.ts +100 -0
  137. package/src/notifications/helpers.ts +135 -0
  138. package/src/notifications/html-format.ts +389 -0
  139. package/src/notifications/index.ts +842 -0
  140. package/src/notifications/managed-daemon.ts +163 -0
  141. package/src/notifications/rate-limit-pool.ts +179 -0
  142. package/src/notifications/telegram-cli.ts +194 -0
  143. package/src/notifications/telegram-daemon-cli.ts +74 -0
  144. package/src/notifications/telegram-daemon-control.ts +370 -0
  145. package/src/notifications/telegram-daemon.ts +1591 -0
  146. package/src/notifications/telegram-reference.ts +335 -0
  147. package/src/notifications/threaded-inbound.ts +136 -0
  148. package/src/notifications/threaded-render.ts +173 -0
  149. package/src/notifications/topic-registry.ts +133 -0
  150. package/src/rlm/index.ts +19 -0
  151. package/src/sdk.ts +16 -0
  152. package/src/session/agent-session.ts +195 -54
  153. package/src/session/auth-storage.ts +3 -0
  154. package/src/session/session-dump-format.ts +43 -2
  155. package/src/session/session-manager.ts +39 -5
  156. package/src/setup/credential-auto-import.ts +258 -0
  157. package/src/setup/credential-import.ts +17 -0
  158. package/src/setup/hermes/templates/operator-instructions.v1.md +10 -0
  159. package/src/setup/host-plugin-setup.ts +142 -0
  160. package/src/skc-runtime/deep-interview-recorder.ts +2 -2
  161. package/src/skc-runtime/launch-tmux.ts +27 -5
  162. package/src/skc-runtime/ledger-event-renderer.ts +1 -0
  163. package/src/skc-runtime/ralplan-runtime.ts +2 -2
  164. package/src/skc-runtime/state-runtime.ts +18 -10
  165. package/src/skc-runtime/state-writer.ts +8 -8
  166. package/src/skc-runtime/tmux-common.ts +8 -0
  167. package/src/skc-runtime/tmux-sessions.ts +8 -1
  168. package/src/skc-runtime/ultragoal-guard.ts +57 -2
  169. package/src/skc-runtime/ultragoal-runtime.ts +105 -19
  170. package/src/skc-runtime/workflow-manifest.generated.json +56 -2
  171. package/src/skc-runtime/workflow-manifest.ts +18 -3
  172. package/src/slash-commands/builtin-registry.ts +4 -1
  173. package/src/task/executor.ts +5 -1
  174. package/src/tools/ask-answer-registry.ts +25 -0
  175. package/src/tools/ask.ts +77 -6
  176. package/src/tools/fetch.ts +78 -1
  177. package/src/tools/image-gen.ts +5 -8
  178. package/src/tools/index.ts +22 -0
  179. package/src/tools/inspect-image.ts +16 -11
  180. package/src/tools/subagent-render.ts +7 -0
  181. package/src/tools/subagent.ts +38 -7
  182. package/src/tools/telegram-send.ts +137 -0
  183. package/src/web/insane/bridge.ts +350 -0
  184. package/src/web/insane/url-guard.ts +155 -0
  185. package/src/web/search/provider.ts +77 -18
  186. package/src/web/search/providers/anthropic.ts +70 -3
  187. package/src/web/search/providers/codex.ts +1 -119
  188. package/src/web/search/providers/gemini.ts +99 -0
  189. package/src/web/search/providers/insane.ts +551 -0
  190. package/src/web/search/providers/openai-compatible.ts +66 -32
  191. package/src/web/search/providers/text-citations.ts +111 -0
  192. package/src/web/search/types.ts +13 -2
  193. package/vendor/insane-search/LICENSE +21 -0
  194. package/vendor/insane-search/MANIFEST.json +24 -0
  195. package/vendor/insane-search/engine/__init__.py +23 -0
  196. package/vendor/insane-search/engine/__main__.py +128 -0
  197. package/vendor/insane-search/engine/bias_check.py +183 -0
  198. package/vendor/insane-search/engine/executor.py +254 -0
  199. package/vendor/insane-search/engine/fetch_chain.py +725 -0
  200. package/vendor/insane-search/engine/learning.py +175 -0
  201. package/vendor/insane-search/engine/phase0.py +214 -0
  202. package/vendor/insane-search/engine/safety.py +91 -0
  203. package/vendor/insane-search/engine/templates/package.json +11 -0
  204. package/vendor/insane-search/engine/templates/playwright_mobile_chrome.js +188 -0
  205. package/vendor/insane-search/engine/templates/playwright_real_chrome.js +243 -0
  206. package/vendor/insane-search/engine/tests/test_hardening.py +57 -0
  207. package/vendor/insane-search/engine/tests/test_smoke.py +152 -0
  208. package/vendor/insane-search/engine/tests/test_u1.py +200 -0
  209. package/vendor/insane-search/engine/tests/test_u4.py +131 -0
  210. package/vendor/insane-search/engine/tests/test_u5.py +163 -0
  211. package/vendor/insane-search/engine/tests/test_u7.py +124 -0
  212. package/vendor/insane-search/engine/transport.py +211 -0
  213. package/vendor/insane-search/engine/url_transforms.py +98 -0
  214. package/vendor/insane-search/engine/validators.py +331 -0
  215. package/vendor/insane-search/engine/waf_detector.py +214 -0
  216. package/vendor/insane-search/engine/waf_profiles.yaml +162 -0
@@ -20,6 +20,7 @@ import type { PythonResult } from "../../eval/py/executor";
20
20
  import type { BashResult } from "../../exec/bash-executor";
21
21
  import type { ExecOptions, ExecResult } from "../../exec/exec";
22
22
  import type { CustomEditor } from "../../modes/components/custom-editor";
23
+ import type { WorkflowGateEmitter } from "../../modes/shared/agent-wire/unattended-session";
23
24
  import type { Theme } from "../../modes/theme/theme";
24
25
  import type { CustomMessage } from "../../session/messages";
25
26
  import type { ReadonlySessionManager, SessionManager } from "../../session/session-manager";
@@ -206,6 +207,11 @@ export interface ExtensionContext {
206
207
  getSystemPrompt(): string[];
207
208
  /** @deprecated Use hasPendingMessages() instead */
208
209
  hasQueuedMessages(): boolean;
210
+ /**
211
+ * Unattended workflow-gate bridge. Present only when the session runs in
212
+ * unattended/RPC mode; `undefined` in interactive/TUI mode (notify-only).
213
+ */
214
+ workflowGate?: WorkflowGateEmitter;
209
215
  }
210
216
  /**
211
217
  * Extended context for command handlers.
@@ -834,6 +840,8 @@ export interface ExtensionContextActions {
834
840
  getContextUsage: () => ContextUsage | undefined;
835
841
  compact: (instructionsOrOptions?: string | CompactOptions) => Promise<void>;
836
842
  getSystemPrompt: () => string[];
843
+ /** Unattended workflow-gate bridge (present only in unattended/RPC mode). */
844
+ getWorkflowGate?: () => WorkflowGateEmitter | undefined;
837
845
  }
838
846
  /** Actions for ExtensionCommandContext (ctx.* in command handlers). */
839
847
  export interface ExtensionCommandContextActions {
@@ -172,6 +172,7 @@ export interface AutoCompactionEndEvent {
172
172
  errorMessage?: string;
173
173
  /** True when compaction was skipped for a benign reason (no model, no candidates, nothing to compact). */
174
174
  skipped?: boolean;
175
+ continuationSkipReason?: "auto_continue_disabled_non_resumable_tail";
175
176
  }
176
177
  /** Fired when auto-retry starts */
177
178
  export interface AutoRetryStartEvent {
@@ -30,6 +30,7 @@ export declare const en: {
30
30
  readonly "settings.tab.appearance": "Appearance";
31
31
  readonly "settings.tab.model": "Model";
32
32
  readonly "settings.tab.interaction": "Interaction";
33
+ readonly "settings.tab.notifications": "Notifications";
33
34
  readonly "settings.tab.context": "Context";
34
35
  readonly "settings.tab.memory": "Memory";
35
36
  readonly "settings.tab.editing": "Editing";
@@ -224,6 +224,8 @@ export interface ServerConfig {
224
224
  /** Per-server warmup timeout in milliseconds. Overrides the global WARMUP_TIMEOUT_MS for this server during startup. */
225
225
  warmupTimeoutMs?: number;
226
226
  capabilities?: ServerCapabilities;
227
+ /** Names of lower-precedence servers to drop when this server is available. */
228
+ supersedes?: string[];
227
229
  /** If true, this is a linter/formatter server (e.g., Biome) - used only for diagnostics/actions, not type intelligence */
228
230
  isLinter?: boolean;
229
231
  /** Resolved absolute path to the command binary (set during config loading) */
@@ -1,5 +1,6 @@
1
1
  import { Container } from "@sayknow-cli/tui";
2
2
  import type { AuthStorage } from "../../session/auth-storage";
3
+ import type { ImportableCredential } from "../../setup/credential-import";
3
4
  /**
4
5
  * Component that renders an OAuth provider selector.
5
6
  */
@@ -8,6 +9,7 @@ export declare class OAuthSelectorComponent extends Container {
8
9
  constructor(mode: "login" | "logout", authStorage: AuthStorage, onSelect: (providerId: string) => void, onCancel: () => void, options?: {
9
10
  validateAuth?: (providerId: string) => Promise<boolean>;
10
11
  requestRender?: () => void;
12
+ externalCredentialCandidates?: ImportableCredential[];
11
13
  });
12
14
  stopValidation(): void;
13
15
  dispose(): void;
@@ -37,6 +37,7 @@ export interface SubmenuSettingDef extends BaseSettingDef {
37
37
  }
38
38
  export interface TextInputSettingDef extends BaseSettingDef {
39
39
  type: "text";
40
+ secret?: boolean;
40
41
  }
41
42
  export type SettingDef = BooleanSettingDef | EnumSettingDef | SubmenuSettingDef | TextInputSettingDef;
42
43
  /** Get all setting definitions with UI */
@@ -1,5 +1,5 @@
1
1
  import type { Component } from "@sayknow-cli/tui";
2
- import type { InteractiveModeContext } from "../../modes/types";
2
+ import type { InteractiveModeContext, OAuthSelectorOptions } from "../../modes/types";
3
3
  import type { JobsObserver } from "../jobs-observer";
4
4
  import type { SessionObserverRegistry } from "../session-observer-registry";
5
5
  export declare class SelectorController {
@@ -44,7 +44,7 @@ export declare class SelectorController {
44
44
  showSessionSelector(): Promise<void>;
45
45
  handleResumeSession(sessionPath: string): Promise<void>;
46
46
  handleSessionDeleteCommand(): Promise<void>;
47
- showOAuthSelector(mode: "login" | "logout", providerId?: string): Promise<void>;
47
+ showOAuthSelector(mode: "login" | "logout", providerId?: string, options?: OAuthSelectorOptions): Promise<void>;
48
48
  showDebugSelector(): void;
49
49
  showSessionObserver(registry: SessionObserverRegistry): void;
50
50
  /**
@@ -221,7 +221,7 @@ export declare class InteractiveMode implements InteractiveModeContext {
221
221
  showSessionSelector(): void;
222
222
  handleResumeSession(sessionPath: string): Promise<void>;
223
223
  handleSessionDeleteCommand(): Promise<void>;
224
- showOAuthSelector(mode: "login" | "logout", providerId?: string): Promise<void>;
224
+ showOAuthSelector(mode: "login" | "logout", providerId?: string, options?: import("./types").OAuthSelectorOptions): Promise<void>;
225
225
  showHookConfirm(title: string, message: string): Promise<boolean>;
226
226
  handleCtrlC(): void;
227
227
  handleCtrlD(): void;
@@ -34,6 +34,14 @@ export interface WorkflowGateEmitter {
34
34
  isUnattended(): boolean;
35
35
  /** Open + emit a gate; resolves with the agent's answer (from workflow_gate_response). */
36
36
  emitGate(input: OpenGateInput): Promise<unknown>;
37
+ /**
38
+ * Optional bridge surface (present on {@link UnattendedSessionControlPlane}) that
39
+ * lets an in-process extension observe emitted gates and answer them — used by
40
+ * the notifications SDK to resolve a real ask gate from a remote reply.
41
+ */
42
+ onGateEmitted?(listener: (gate: RpcWorkflowGate) => void): () => void;
43
+ resolveGate?(response: RpcWorkflowGateResponse): Promise<RpcWorkflowGateResolution>;
44
+ listPendingGates?(): RpcWorkflowGate[];
37
45
  }
38
46
  export interface UnattendedSessionOptions {
39
47
  runId: string;
@@ -61,6 +69,8 @@ export declare class UnattendedSessionControlPlane implements RpcUnattendedContr
61
69
  private readonly opts;
62
70
  constructor(opts: UnattendedSessionOptions);
63
71
  isUnattended(): boolean;
72
+ /** Observe every emitted gate (e.g. so an extension can map an ask to its gate_id). */
73
+ onGateEmitted(listener: (gate: RpcWorkflowGate) => void): () => void;
64
74
  get controller(): UnattendedRunController | undefined;
65
75
  negotiate(declaration: RpcUnattendedDeclaration): RpcUnattendedAccepted;
66
76
  preflightCommand(command: RpcCommand): void;
@@ -6,7 +6,7 @@ export type SymbolPreset = "unicode" | "nerd" | "ascii";
6
6
  /**
7
7
  * All available symbol keys organized by category.
8
8
  */
9
- export type SymbolKey = "status.success" | "status.error" | "status.warning" | "status.info" | "status.pending" | "status.disabled" | "status.enabled" | "status.running" | "status.shadowed" | "status.aborted" | "nav.cursor" | "nav.selected" | "nav.expand" | "nav.collapse" | "nav.back" | "tree.branch" | "tree.last" | "tree.vertical" | "tree.horizontal" | "tree.hook" | "boxRound.topLeft" | "boxRound.topRight" | "boxRound.bottomLeft" | "boxRound.bottomRight" | "boxRound.horizontal" | "boxRound.vertical" | "boxSharp.topLeft" | "boxSharp.topRight" | "boxSharp.bottomLeft" | "boxSharp.bottomRight" | "boxSharp.horizontal" | "boxSharp.vertical" | "boxSharp.cross" | "boxSharp.teeDown" | "boxSharp.teeUp" | "boxSharp.teeRight" | "boxSharp.teeLeft" | "sep.powerline" | "sep.powerlineThin" | "sep.powerlineLeft" | "sep.powerlineRight" | "sep.powerlineThinLeft" | "sep.powerlineThinRight" | "sep.block" | "sep.space" | "sep.asciiLeft" | "sep.asciiRight" | "sep.dot" | "sep.slash" | "sep.pipe" | "icon.model" | "icon.plan" | "icon.goal" | "icon.pause" | "icon.folder" | "icon.scratchFolder" | "icon.file" | "icon.git" | "icon.branch" | "icon.pr" | "icon.tokens" | "icon.context" | "icon.cost" | "icon.time" | "icon.pi" | "icon.agents" | "icon.cache" | "icon.input" | "icon.output" | "icon.host" | "icon.session" | "icon.package" | "icon.warning" | "icon.rewind" | "icon.auto" | "icon.fast" | "icon.extensionSkill" | "icon.extensionTool" | "icon.extensionSlashCommand" | "icon.extensionMcp" | "icon.extensionRule" | "icon.extensionHook" | "icon.extensionPrompt" | "icon.extensionContextFile" | "icon.extensionInstruction" | "icon.mic" | "thinking.minimal" | "thinking.low" | "thinking.medium" | "thinking.high" | "thinking.xhigh" | "thinking.max" | "checkbox.checked" | "checkbox.unchecked" | "format.bullet" | "format.dash" | "format.bracketLeft" | "format.bracketRight" | "md.quoteBorder" | "md.hrChar" | "md.bullet" | "lang.default" | "lang.typescript" | "lang.javascript" | "lang.python" | "lang.rust" | "lang.go" | "lang.java" | "lang.c" | "lang.cpp" | "lang.csharp" | "lang.ruby" | "lang.php" | "lang.swift" | "lang.kotlin" | "lang.shell" | "lang.html" | "lang.css" | "lang.json" | "lang.yaml" | "lang.markdown" | "lang.sql" | "lang.docker" | "lang.lua" | "lang.text" | "lang.env" | "lang.toml" | "lang.xml" | "lang.ini" | "lang.conf" | "lang.log" | "lang.csv" | "lang.tsv" | "lang.image" | "lang.pdf" | "lang.archive" | "lang.binary" | "tab.appearance" | "tab.model" | "tab.interaction" | "tab.context" | "tab.editing" | "tab.tools" | "tab.memory" | "tab.tasks" | "tab.providers";
9
+ export type SymbolKey = "status.success" | "status.error" | "status.warning" | "status.info" | "status.pending" | "status.disabled" | "status.enabled" | "status.running" | "status.shadowed" | "status.aborted" | "nav.cursor" | "nav.selected" | "nav.expand" | "nav.collapse" | "nav.back" | "tree.branch" | "tree.last" | "tree.vertical" | "tree.horizontal" | "tree.hook" | "boxRound.topLeft" | "boxRound.topRight" | "boxRound.bottomLeft" | "boxRound.bottomRight" | "boxRound.horizontal" | "boxRound.vertical" | "boxSharp.topLeft" | "boxSharp.topRight" | "boxSharp.bottomLeft" | "boxSharp.bottomRight" | "boxSharp.horizontal" | "boxSharp.vertical" | "boxSharp.cross" | "boxSharp.teeDown" | "boxSharp.teeUp" | "boxSharp.teeRight" | "boxSharp.teeLeft" | "sep.powerline" | "sep.powerlineThin" | "sep.powerlineLeft" | "sep.powerlineRight" | "sep.powerlineThinLeft" | "sep.powerlineThinRight" | "sep.block" | "sep.space" | "sep.asciiLeft" | "sep.asciiRight" | "sep.dot" | "sep.slash" | "sep.pipe" | "icon.model" | "icon.plan" | "icon.goal" | "icon.pause" | "icon.folder" | "icon.scratchFolder" | "icon.file" | "icon.git" | "icon.branch" | "icon.pr" | "icon.tokens" | "icon.context" | "icon.cost" | "icon.time" | "icon.pi" | "icon.agents" | "icon.cache" | "icon.input" | "icon.output" | "icon.host" | "icon.session" | "icon.package" | "icon.warning" | "icon.rewind" | "icon.auto" | "icon.fast" | "icon.extensionSkill" | "icon.extensionTool" | "icon.extensionSlashCommand" | "icon.extensionMcp" | "icon.extensionRule" | "icon.extensionHook" | "icon.extensionPrompt" | "icon.extensionContextFile" | "icon.extensionInstruction" | "icon.mic" | "thinking.minimal" | "thinking.low" | "thinking.medium" | "thinking.high" | "thinking.xhigh" | "thinking.max" | "checkbox.checked" | "checkbox.unchecked" | "format.bullet" | "format.dash" | "format.bracketLeft" | "format.bracketRight" | "md.quoteBorder" | "md.hrChar" | "md.bullet" | "lang.default" | "lang.typescript" | "lang.javascript" | "lang.python" | "lang.rust" | "lang.go" | "lang.java" | "lang.c" | "lang.cpp" | "lang.csharp" | "lang.ruby" | "lang.php" | "lang.swift" | "lang.kotlin" | "lang.shell" | "lang.html" | "lang.css" | "lang.json" | "lang.yaml" | "lang.markdown" | "lang.sql" | "lang.docker" | "lang.lua" | "lang.text" | "lang.env" | "lang.toml" | "lang.xml" | "lang.ini" | "lang.conf" | "lang.log" | "lang.csv" | "lang.tsv" | "lang.image" | "lang.pdf" | "lang.archive" | "lang.binary" | "tab.appearance" | "tab.model" | "tab.interaction" | "tab.notifications" | "tab.context" | "tab.editing" | "tab.tools" | "tab.memory" | "tab.tasks" | "tab.providers";
10
10
  export type SpinnerType = "status" | "activity";
11
11
  export declare const THEME_COLOR_KEYS: readonly ["accent", "border", "borderAccent", "borderMuted", "success", "error", "warning", "muted", "dim", "text", "thinkingText", "selectedBg", "userMessageBg", "userMessageText", "customMessageBg", "customMessageText", "customMessageLabel", "toolPendingBg", "toolSuccessBg", "toolErrorBg", "toolTitle", "toolOutput", "mdHeading", "mdLink", "mdLinkUrl", "mdCode", "mdCodeBlock", "mdCodeBlockBorder", "mdQuote", "mdQuoteBorder", "mdHr", "mdListBullet", "toolDiffAdded", "toolDiffRemoved", "toolDiffContext", "syntaxComment", "syntaxKeyword", "syntaxFunction", "syntaxVariable", "syntaxString", "syntaxNumber", "syntaxType", "syntaxOperator", "syntaxPunctuation", "thinkingOff", "thinkingMinimal", "thinkingLow", "thinkingMedium", "thinkingHigh", "thinkingXhigh", "bashMode", "pythonMode", "statusLineBg", "statusLineSep", "statusLineModel", "statusLinePath", "statusLineGitClean", "statusLineGitDirty", "statusLineContext", "statusLineSpend", "statusLineStaged", "statusLineDirty", "statusLineUntracked", "statusLineOutput", "statusLineCost", "statusLineSubagents"];
12
12
  export type ThemeColor = "accent" | "border" | "borderAccent" | "borderMuted" | "success" | "error" | "warning" | "muted" | "dim" | "text" | "thinkingText" | "userMessageText" | "customMessageText" | "customMessageLabel" | "toolTitle" | "toolOutput" | "mdHeading" | "mdLink" | "mdLinkUrl" | "mdCode" | "mdCodeBlock" | "mdCodeBlockBorder" | "mdQuote" | "mdQuoteBorder" | "mdHr" | "mdListBullet" | "toolDiffAdded" | "toolDiffRemoved" | "toolDiffContext" | "syntaxComment" | "syntaxKeyword" | "syntaxFunction" | "syntaxVariable" | "syntaxString" | "syntaxNumber" | "syntaxType" | "syntaxOperator" | "syntaxPunctuation" | "thinkingOff" | "thinkingMinimal" | "thinkingLow" | "thinkingMedium" | "thinkingHigh" | "thinkingXhigh" | "bashMode" | "pythonMode" | "statusLineSep" | "statusLineModel" | "statusLinePath" | "statusLineGitClean" | "statusLineGitDirty" | "statusLineContext" | "statusLineSpend" | "statusLineStaged" | "statusLineDirty" | "statusLineUntracked" | "statusLineOutput" | "statusLineCost" | "statusLineSubagents";
@@ -12,6 +12,7 @@ import type { MCPManager } from "../runtime-mcp";
12
12
  import type { AgentSession, AgentSessionEvent } from "../session/agent-session";
13
13
  import type { HistoryStorage } from "../session/history-storage";
14
14
  import type { SessionContext, SessionManager } from "../session/session-manager";
15
+ import type { CredentialAutoImportOptions } from "../setup/credential-auto-import";
15
16
  import type { LspStartupServerInfo } from "../tools";
16
17
  import type { AssistantMessageComponent } from "./components/assistant-message";
17
18
  import type { BashExecutionComponent } from "./components/bash-execution";
@@ -226,7 +227,7 @@ export interface InteractiveModeContext {
226
227
  showSessionSelector(): void;
227
228
  handleResumeSession(sessionPath: string): Promise<void>;
228
229
  handleSessionDeleteCommand(): Promise<void>;
229
- showOAuthSelector(mode: "login" | "logout", providerId?: string): Promise<void>;
230
+ showOAuthSelector(mode: "login" | "logout", providerId?: string, options?: OAuthSelectorOptions): Promise<void>;
230
231
  showHookConfirm(title: string, message: string): Promise<boolean>;
231
232
  showDebugSelector(): void;
232
233
  showSessionObserver(): void;
@@ -276,3 +277,8 @@ export interface InteractiveModeContext {
276
277
  showExtensionError(extensionPath: string, error: string): void;
277
278
  showToolError(toolName: string, error: string): void;
278
279
  }
280
+ export interface OAuthSelectorOptions {
281
+ allowExternalCredentialDiscovery?: boolean;
282
+ trigger?: "bare-login";
283
+ externalCredentialDiscover?: CredentialAutoImportOptions["discover"];
284
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Process-wide registry mapping a session id to a sink that delivers a local
3
+ * file to the session's connected Telegram chat as a document. Registered by
4
+ * the notifications extension; consumed by the telegram_send tool.
5
+ */
6
+ /** Delivers a local file to the session's Telegram chat. */
7
+ export type TelegramFileSink = (file: {
8
+ path: string;
9
+ caption?: string;
10
+ }) => Promise<{
11
+ ok: boolean;
12
+ error?: string;
13
+ }>;
14
+ /** Register `sink` for `sessionId`. Returns a disposer that clears it. */
15
+ export declare function registerTelegramFileSink(sessionId: string, sink: TelegramFileSink): () => void;
16
+ /** The Telegram file sink for `sessionId`, if one is registered. */
17
+ export declare function getTelegramFileSink(sessionId: string): TelegramFileSink | undefined;
@@ -0,0 +1,9 @@
1
+ import type { NotificationPresentationAdapter } from "./engine";
2
+ type AdapterKind = "discord" | "slack";
3
+ interface ChatAdapterOptions {
4
+ kind: AdapterKind;
5
+ channelId?: string;
6
+ }
7
+ export declare function createDiscordAdapter(opts?: Omit<ChatAdapterOptions, "kind">): NotificationPresentationAdapter;
8
+ export declare function createSlackAdapter(opts?: Omit<ChatAdapterOptions, "kind">): NotificationPresentationAdapter;
9
+ export {};
@@ -0,0 +1,26 @@
1
+ /**
2
+ * In-thread configuration slash commands for the threaded session surface.
3
+ *
4
+ * Replies are thread-native now (the old `/answer <sessionId> …` command is
5
+ * removed), but the user can still adjust per-surface behaviour from inside a
6
+ * session thread with small slash commands:
7
+ *
8
+ * - `/verbose` switch the mirror to verbose (full tool output + reasoning)
9
+ * - `/lean` switch back to lean (assistant text + tool names)
10
+ * - `/verbosity lean|verbose`
11
+ * - `/redact on|off` toggle redaction of streamed content
12
+ *
13
+ * This parser is pure so the command grammar is unit-testable; the daemon maps
14
+ * the returned change onto a `config_command` frame / settings update.
15
+ */
16
+ /** A parsed in-thread configuration change. */
17
+ export interface ConfigCommandChange {
18
+ verbosity?: "lean" | "verbose";
19
+ redact?: boolean;
20
+ }
21
+ /**
22
+ * Parse an in-thread config command. Returns the requested change, or
23
+ * `undefined` when the text is not a recognised config command (so the daemon
24
+ * can fall through to treating it as a free-text injection).
25
+ */
26
+ export declare function parseInThreadConfigCommand(text: string): ConfigCommandChange | undefined;
@@ -0,0 +1,69 @@
1
+ import type { Settings } from "../config/settings";
2
+ export interface NotificationConfig {
3
+ enabled: boolean;
4
+ botToken?: string;
5
+ chatId?: string;
6
+ discord: {
7
+ botToken?: string;
8
+ channelId?: string;
9
+ };
10
+ slack: {
11
+ botToken?: string;
12
+ channelId?: string;
13
+ };
14
+ redact: boolean;
15
+ verbosity: "lean" | "verbose";
16
+ idleTimeoutMs: number;
17
+ }
18
+ /** Read typed config from Settings. */
19
+ export declare function getNotificationConfig(settings: Settings): NotificationConfig;
20
+ /** Is global config sufficient for auto-on (enabled + at least one configured adapter)? */
21
+ export declare function isGloballyConfigured(cfg: NotificationConfig): boolean;
22
+ /** Resolve whether the notifications extension should be registered at SDK startup. */
23
+ export declare function shouldRegisterNotificationsExtension(input: {
24
+ env: NodeJS.ProcessEnv;
25
+ cfg?: NotificationConfig;
26
+ }): boolean;
27
+ /**
28
+ * Resolve whether THIS session should run notifications.
29
+ * Precedence (highest first):
30
+ * 1) env.SKC_NOTIFICATIONS === "0" -> false (hard opt-out)
31
+ * 2) sessionDisabled === true -> false (local /notify off)
32
+ * 3) env.SKC_NOTIFICATIONS === "1" || env.SKC_NOTIFICATIONS_TOKEN present -> true (legacy explicit)
33
+ * 4) isGloballyConfigured(cfg) -> true (global auto-on)
34
+ * 5) otherwise false
35
+ */
36
+ export declare function isSessionNotificationsEnabled(input: {
37
+ cfg: NotificationConfig;
38
+ env: NodeJS.ProcessEnv;
39
+ sessionDisabled: boolean;
40
+ }): boolean;
41
+ /** Mask a bot token for display: first 4 chars + "…" + "(len N)"; "(unset)" when undefined/empty. Never reveal full token. */
42
+ export declare function maskToken(token: string | undefined): string;
43
+ /** Stable non-reversible fingerprint of a token: sha256 hex, first 12 chars. */
44
+ export declare function tokenFingerprint(token: string): string;
45
+ /** Short session tag for display, e.g. last 6 chars of sessionId. */
46
+ export declare function sessionTag(sessionId: string): string;
47
+ export interface RedactableAction {
48
+ id: string;
49
+ kind: string;
50
+ sessionId: string;
51
+ question?: string;
52
+ options?: string[];
53
+ summary?: string;
54
+ }
55
+ /**
56
+ * When redact is true, strip sensitive content for remote delivery:
57
+ * - ask: NOT redacted. An ask is an interactive prompt the human must read and
58
+ * answer on the remote surface; redacting its question/options would make it
59
+ * unanswerable, defeating remote answering. Asks are returned unchanged.
60
+ * - idle: summary removed, (no question/options).
61
+ * When redact is false, return the action unchanged.
62
+ *
63
+ * Redaction still applies to streamed content frames (turn_stream, context_update,
64
+ * image_attachment) which are suppressed at their emit sites, not here.
65
+ */
66
+ export declare function buildRedactedAction(action: RedactableAction, opts: {
67
+ redact: boolean;
68
+ sessionTag: string;
69
+ }): RedactableAction;
@@ -0,0 +1,59 @@
1
+ import { type RedactableAction } from "./config";
2
+ export type NotificationEvent = ({
3
+ type: "action_needed";
4
+ } & RedactableAction) | {
5
+ type: "action_resolved";
6
+ id: string;
7
+ sessionId: string;
8
+ resolvedBy?: string;
9
+ } | {
10
+ type: "frame";
11
+ sessionId: string;
12
+ frame: Record<string, unknown>;
13
+ };
14
+ export interface NotificationReplyRoute {
15
+ sessionId: string;
16
+ actionId: string;
17
+ answer: number | string | {
18
+ selected?: Array<number | string>;
19
+ custom?: string;
20
+ };
21
+ }
22
+ export interface NotificationAdapterPayload {
23
+ adapter: string;
24
+ channelKey?: string;
25
+ body: unknown;
26
+ route?: Omit<NotificationReplyRoute, "answer">;
27
+ }
28
+ export interface NotificationPresentationAdapter {
29
+ readonly kind: "telegram" | "discord" | "slack";
30
+ render(event: NotificationEvent): NotificationAdapterPayload[];
31
+ mapInbound(input: unknown): NotificationReplyRoute | undefined;
32
+ }
33
+ export interface EngineSessionSink {
34
+ sendReply(route: NotificationReplyRoute): void;
35
+ }
36
+ export interface NotificationEngineOptions {
37
+ redact: boolean;
38
+ sessionTag: (sessionId: string) => string;
39
+ }
40
+ /**
41
+ * Shared presentation engine for managed notification clients.
42
+ *
43
+ * It owns fanout, redaction boundaries, pending-action routing, and reply
44
+ * delivery into session sinks. Transport adapters stay pure: render an internal
45
+ * event into a public-safe payload and map an inbound transport interaction
46
+ * back into a session/action answer.
47
+ */
48
+ export declare class NotificationPresentationEngine {
49
+ private readonly opts;
50
+ readonly adapters: readonly NotificationPresentationAdapter[];
51
+ private readonly sessions;
52
+ private readonly pending;
53
+ constructor(adapters: readonly NotificationPresentationAdapter[], opts: NotificationEngineOptions);
54
+ connectSession(sessionId: string, sink: EngineSessionSink): void;
55
+ dropSession(sessionId: string): void;
56
+ fanout(event: NotificationEvent): NotificationAdapterPayload[];
57
+ routeInbound(adapterKind: NotificationPresentationAdapter["kind"], input: unknown): boolean;
58
+ private redactEvent;
59
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Pure helpers for the notifications extension.
3
+ *
4
+ * Kept side-effect-free so the mapping logic (ask extraction, idle summary,
5
+ * dedupe keys) is unit-testable without a live session or the native server.
6
+ */
7
+ import { type RedactableAction } from "./config";
8
+ /** A pending ask derived from an `ask` tool call. */
9
+ export interface PendingAsk {
10
+ /** Action id: `${toolCallId}:${questionId}`. */
11
+ id: string;
12
+ /** Question text. */
13
+ question: string;
14
+ /** Option labels (may be empty for free-text questions). */
15
+ options: string[];
16
+ }
17
+ /** Truncate text to `max` chars, appending an ellipsis when cut. */
18
+ export declare function truncate(text: string, max?: number): string;
19
+ /** Stable per-turn idle dedupe key so exactly one idle action fires per turn. */
20
+ export declare function idleDedupeKey(sessionId: string, turnIndex: number): string;
21
+ /**
22
+ * Extract pending asks from an `ask` tool call input.
23
+ *
24
+ * Defensive: tolerates partial/unknown shapes and always returns an array.
25
+ */
26
+ export declare function asksFromAskInput(toolCallId: string, input: unknown): PendingAsk[];
27
+ /** Prepare an action JSON payload for remote notification delivery. */
28
+ export declare function notificationActionPayload<T extends RedactableAction>(action: T, opts: {
29
+ redact: boolean;
30
+ sessionTag: string;
31
+ }): RedactableAction;
32
+ /** Extract a plain-text summary from an agent message's content, if any. */
33
+ export declare function summaryFromMessage(message: unknown, max?: number): string | undefined;
34
+ /**
35
+ * Extract an idle summary from an `agent_end` event's settled message list: the
36
+ * last message that yields text (i.e. the final assistant message; tool-result
37
+ * messages have no text and are skipped).
38
+ *
39
+ * `agent_end` fires exactly once when the agent loop settles to await the user,
40
+ * so emitting idle from this — instead of per-`turn_end` — produces exactly one
41
+ * idle notification per genuine idle, eliminating the multi-turn flood.
42
+ */
43
+ export declare function summaryFromMessages(messages: unknown, max?: number): string | undefined;
44
+ /** An agent-produced image extracted from a message's content. */
45
+ export interface ExtractedImage {
46
+ source: string;
47
+ mime: string;
48
+ data: string;
49
+ }
50
+ /**
51
+ * Extract agent-produced images (`{ type: "image", data, mimeType }` blocks)
52
+ * from a message's content — e.g. computer-use/browser screenshots or tool
53
+ * image outputs — for `image_attachment` delivery.
54
+ */
55
+ export declare function imageAttachmentsFromMessage(message: unknown, source?: string): ExtractedImage[];
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Telegram HTML formatting helpers for the notifications SDK.
3
+ *
4
+ * All notifications-SDK Telegram output is sent with `parse_mode: "HTML"`. This
5
+ * module is the single source of truth for: escaping dynamic text, converting a
6
+ * bounded markdown subset into Telegram HTML, safely truncating a finished
7
+ * message to Telegram's 4096-char limit without breaking tags/entities, and
8
+ * laying out inline-keyboard buttons as a numbered grid.
9
+ *
10
+ * Discipline: escape first, tag second. Telegram only parses a small tag set
11
+ * (b, i, u, s, code, pre, a, blockquote, tg-spoiler); a stray `<` or unbalanced
12
+ * tag can make Telegram reject the whole message, so dynamic text is always
13
+ * escaped before any tag is emitted.
14
+ */
15
+ export declare const TELEGRAM_PARSE_MODE: "HTML";
16
+ export declare const TELEGRAM_MESSAGE_LIMIT = 4096;
17
+ /** Escape text for Telegram HTML body content (`& < >`). */
18
+ export declare function escapeHtml(value: string): string;
19
+ /** Bold the given raw text (escaped internally). */
20
+ export declare function bold(raw: string): string;
21
+ /** Italicize the given raw text (escaped internally). */
22
+ export declare function italic(raw: string): string;
23
+ /** Render the given raw text as inline code (escaped internally). */
24
+ export declare function code(raw: string): string;
25
+ /** Render the given raw text as a preformatted block (escaped internally). */
26
+ export declare function pre(raw: string): string;
27
+ /**
28
+ * Convert a bounded markdown subset into Telegram HTML. Supported: fenced code,
29
+ * inline code, `**bold**`, `*italic*`, `[text](url)` (safe schemes only),
30
+ * `#` headers, `>` blockquotes, and GFM tables (rendered as a monospace block).
31
+ * Unsupported or malformed markdown is left as escaped literal text — never
32
+ * emitted as unbalanced tags.
33
+ */
34
+ export declare function markdownToTelegramHtml(markdown: string): string;
35
+ /**
36
+ * Truncate a finished Telegram HTML message to at most `max` chars without
37
+ * splitting a tag or entity, closing any still-open allowed tags and appending
38
+ * `marker`. The final string is guaranteed to be <= `max`.
39
+ */
40
+ export declare function truncateTelegramHtml(message: string, max?: number, marker?: string): string;
41
+ /** Finalize an optional message: undefined passthrough, else safe-truncate. */
42
+ export declare function finalizeTelegramHtml(message?: string): string | undefined;
43
+ /**
44
+ * One-based, plain-text button label (Telegram does not parse HTML in labels).
45
+ *
46
+ * Strips any leading `N.`/`N)` index already embedded in the label (e.g.
47
+ * deep-interview options pre-numbered by the ask tool) and applies the canonical
48
+ * one-based button index instead. This avoids duplicated numbering like
49
+ * `1. 1. …` and keeps the displayed number aligned with the button's real index.
50
+ */
51
+ export declare function buttonLabel(label: string, index: number): string;
52
+ export interface InlineButton {
53
+ text: string;
54
+ callback_data: string;
55
+ }
56
+ /**
57
+ * Lay out option labels as a numbered button grid. Long buttons take a
58
+ * full-width row; runs of short buttons are packed into rows of up to 3. The
59
+ * callback value comes from `callbackForIndex(i)` using the original zero-based
60
+ * option index — layout never changes callback semantics.
61
+ */
62
+ export declare function buildButtonGrid(labels: string[], callbackForIndex: (index: number) => string): InlineButton[][];
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Notifications extension.
3
+ *
4
+ * Hosts a per-session loopback WebSocket notification server (the Rust core via
5
+ * N-API) and bridges SKC session events + the `ask` tool to it so a remote client
6
+ * (e.g. a Telegram bot) can both see action-needed signals and ANSWER them —
7
+ * without requiring RPC/unattended mode:
8
+ *
9
+ * - `ask` (interactive): registers an {@link AskAnswerSource}; the ask tool races
10
+ * the local UI against a remote reply. First valid answer wins; a local answer
11
+ * aborts the remote wait (and broadcasts `action_resolved` resolvedBy=local).
12
+ * - `ask` (unattended/RPC): observes emitted workflow gates and resolves the real
13
+ * gate on a remote reply via `ctx.workflowGate`.
14
+ * - `turn_end` -> `action_needed` (kind `idle`, deduped per turn).
15
+ * - `session_shutdown` -> stop the server + deregister the answer source.
16
+ *
17
+ * Enable with Settings notifications config, `SKC_NOTIFICATIONS=1` (a token is
18
+ * generated), or `SKC_NOTIFICATIONS_TOKEN`.
19
+ */
20
+ import type { ExtensionFactory } from "../extensibility/extensions";
21
+ /**
22
+ * Best-effort real repository name (no git spawn): resolves the main worktree
23
+ * root directory so linked worktrees report the repo (e.g. `sayknow-cli`)
24
+ * instead of the worktree directory (e.g. `feat-foo-01047f11`).
25
+ */
26
+ export declare function readGitRepoName(cwd: string): string | undefined;
27
+ export declare function notificationsEnabled(): boolean;
28
+ export declare const createNotificationsExtension: ExtensionFactory;
@@ -0,0 +1,48 @@
1
+ import type { Settings } from "../config/settings";
2
+ import { RateLimitPool } from "./rate-limit-pool";
3
+ import { type ThreadedSend } from "./threaded-render";
4
+ export interface ManagedNotificationDaemonFs {
5
+ readdir(path: string): Promise<string[]>;
6
+ }
7
+ export interface ManagedSessionSocket {
8
+ sessionId: string;
9
+ token: string;
10
+ ws: WebSocket;
11
+ pending: Map<string, {
12
+ sessionId: string;
13
+ actionId: string;
14
+ }>;
15
+ capable: boolean;
16
+ lastPongAt: number;
17
+ awaitingNonce: string | undefined;
18
+ pingTimer: ReturnType<typeof setInterval> | undefined;
19
+ }
20
+ export interface ManagedNotificationDaemonOptions {
21
+ settings: Settings;
22
+ fs: ManagedNotificationDaemonFs;
23
+ WebSocketImpl?: typeof WebSocket;
24
+ now?: () => number;
25
+ setIntervalImpl?: typeof setInterval;
26
+ clearIntervalImpl?: typeof clearInterval;
27
+ rateLimitPool?: RateLimitPool<{
28
+ send: ThreadedSend;
29
+ topicId?: string;
30
+ }>;
31
+ }
32
+ export declare abstract class ManagedNotificationDaemon {
33
+ protected readonly opts: ManagedNotificationDaemonOptions;
34
+ readonly sessions: Map<string, ManagedSessionSocket>;
35
+ readonly pool: RateLimitPool<{
36
+ send: ThreadedSend;
37
+ topicId?: string;
38
+ }>;
39
+ protected constructor(opts: ManagedNotificationDaemonOptions);
40
+ scanRoots(): Promise<void>;
41
+ connectSession(sessionId: string, url: string, token: string): ManagedSessionSocket;
42
+ protected handleSessionMessage(session: ManagedSessionSocket, msg: Record<string, unknown>): Promise<boolean>;
43
+ protected renderFrame(frame: Record<string, unknown>): ThreadedSend | undefined;
44
+ protected dropSession(session: ManagedSessionSocket): void;
45
+ protected abstract readRoots(): Promise<string[]>;
46
+ private sendHello;
47
+ private startLiveness;
48
+ }