@sayknow-cli/coding-agent 0.3.12 → 0.3.15

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 (248) hide show
  1. package/dist/types/autoresearch/tools/init-experiment.d.ts +1 -1
  2. package/dist/types/autoresearch/tools/log-experiment.d.ts +2 -2
  3. package/dist/types/cli/classify-install-target.d.ts +0 -10
  4. package/dist/types/cli/daemon-cli.d.ts +2 -0
  5. package/dist/types/cli/initial-message.d.ts +1 -1
  6. package/dist/types/cli/malloc-env-guard.d.ts +65 -0
  7. package/dist/types/cli/malloc-env-guard.test.d.ts +1 -0
  8. package/dist/types/cli/notify-cli.d.ts +3 -1
  9. package/dist/types/cli/session-picker.d.ts +4 -3
  10. package/dist/types/cli/update-cli.d.ts +22 -3
  11. package/dist/types/cli.d.ts +5 -0
  12. package/dist/types/commands/daemon.d.ts +6 -2
  13. package/dist/types/commands/launch.d.ts +1 -0
  14. package/dist/types/commands/notify.d.ts +6 -0
  15. package/dist/types/commands/web-search.d.ts +1 -1
  16. package/dist/types/commit/agentic/tools/propose-changelog.d.ts +4 -4
  17. package/dist/types/commit/agentic/tools/propose-commit.d.ts +9 -9
  18. package/dist/types/commit/agentic/tools/schemas.d.ts +9 -9
  19. package/dist/types/commit/agentic/tools/split-commit.d.ts +9 -9
  20. package/dist/types/commit/analysis/conventional.d.ts +1 -1
  21. package/dist/types/commit/analysis/summary.d.ts +1 -1
  22. package/dist/types/commit/changelog/generate.d.ts +3 -3
  23. package/dist/types/commit/changelog/index.d.ts +2 -2
  24. package/dist/types/commit/map-reduce/map-phase.d.ts +1 -1
  25. package/dist/types/commit/map-reduce/reduce-phase.d.ts +1 -1
  26. package/dist/types/commit/shared-llm.d.ts +9 -9
  27. package/dist/types/config/keybindings.d.ts +134 -129
  28. package/dist/types/config/model-profile-activation.d.ts +1 -0
  29. package/dist/types/config/model-registry.d.ts +35 -35
  30. package/dist/types/config/models-config-schema.d.ts +111 -111
  31. package/dist/types/config/settings-schema.d.ts +56 -16
  32. package/dist/types/config/settings.d.ts +2 -0
  33. package/dist/types/coordinator/contract.d.ts +1 -1
  34. package/dist/types/coordinator-mcp/model-preset.d.ts +45 -0
  35. package/dist/types/coordinator-mcp/policy.d.ts +3 -0
  36. package/dist/types/coordinator-mcp/server.d.ts +79 -6
  37. package/dist/types/coordinator-mcp/session-reaper.d.ts +53 -0
  38. package/dist/types/daemon/control-types.d.ts +10 -0
  39. package/dist/types/daemon/operator-contract.d.ts +47 -0
  40. package/dist/types/defaults/skc/extensions/grok-cli-vendor/src/models/catalog.d.ts +4 -0
  41. package/dist/types/defaults/skc/extensions/grok-cli-vendor/src/shared/errors.d.ts +12 -12
  42. package/dist/types/discovery/helpers.d.ts +1 -1
  43. package/dist/types/discovery/substitute-plugin-root.d.ts +0 -4
  44. package/dist/types/edit/modes/patch.d.ts +2 -2
  45. package/dist/types/extensibility/extensions/types.d.ts +3 -1
  46. package/dist/types/goals/tools/goal-tool.d.ts +6 -6
  47. package/dist/types/i18n/messages/en.d.ts +8 -8
  48. package/dist/types/internal-urls/local-protocol.d.ts +13 -9
  49. package/dist/types/lsp/index.d.ts +9 -9
  50. package/dist/types/lsp/types.d.ts +9 -9
  51. package/dist/types/main.d.ts +66 -2
  52. package/dist/types/modes/acp/acp-agent.d.ts +19 -2
  53. package/dist/types/modes/acp/acp-mode.d.ts +12 -0
  54. package/dist/types/modes/components/assistant-message.d.ts +1 -1
  55. package/dist/types/modes/components/custom-editor.d.ts +2 -0
  56. package/dist/types/modes/components/hook-selector.d.ts +5 -0
  57. package/dist/types/modes/components/irc-sidebar.d.ts +32 -0
  58. package/dist/types/modes/components/pet-selector.d.ts +11 -0
  59. package/dist/types/modes/components/sayknow-pet-widget.d.ts +59 -0
  60. package/dist/types/modes/components/session-selector.d.ts +22 -9
  61. package/dist/types/modes/components/settings-selector.d.ts +2 -0
  62. package/dist/types/modes/components/status-line/types.d.ts +1 -1
  63. package/dist/types/modes/components/status-line.d.ts +0 -10
  64. package/dist/types/modes/components/tool-execution.d.ts +1 -0
  65. package/dist/types/modes/components/user-message.d.ts +2 -1
  66. package/dist/types/modes/controllers/event-controller.d.ts +4 -0
  67. package/dist/types/modes/controllers/selector-controller.d.ts +1 -0
  68. package/dist/types/modes/interactive-mode.d.ts +24 -2
  69. package/dist/types/modes/irc-observation-ledger.d.ts +22 -0
  70. package/dist/types/modes/print-mode.d.ts +3 -4
  71. package/dist/types/modes/rpc/rpc-client.d.ts +7 -1
  72. package/dist/types/modes/rpc/rpc-mode.d.ts +2 -1
  73. package/dist/types/modes/rpc/rpc-types.d.ts +17 -1
  74. package/dist/types/modes/shared/agent-wire/deep-interview-gate.d.ts +8 -0
  75. package/dist/types/modes/shared/agent-wire/unattended-run-controller.d.ts +5 -0
  76. package/dist/types/modes/shared/agent-wire/unattended-session.d.ts +45 -0
  77. package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +42 -2
  78. package/dist/types/modes/theme/defaults/index.d.ts +568 -568
  79. package/dist/types/modes/types.d.ts +28 -2
  80. package/dist/types/modes/utils/abort-message.d.ts +1 -1
  81. package/dist/types/modes/utils/context-usage.d.ts +4 -19
  82. package/dist/types/modes/utils/irc-message.d.ts +38 -0
  83. package/dist/types/modes/utils/ui-helpers.d.ts +13 -4
  84. package/dist/types/notifications/config.d.ts +34 -0
  85. package/dist/types/notifications/daemon-paths.d.ts +7 -0
  86. package/dist/types/notifications/index.d.ts +27 -0
  87. package/dist/types/notifications/lifecycle-control-runtime.d.ts +12 -4
  88. package/dist/types/notifications/notification-service.d.ts +144 -0
  89. package/dist/types/notifications/rate-limit-pool.d.ts +27 -9
  90. package/dist/types/notifications/telegram-daemon-control.d.ts +6 -0
  91. package/dist/types/notifications/telegram-daemon.d.ts +130 -3
  92. package/dist/types/notifications/telegram-reference.d.ts +19 -2
  93. package/dist/types/notifications/topic-registry.d.ts +24 -4
  94. package/dist/types/registry/agent-registry.d.ts +2 -0
  95. package/dist/types/rlm/index.d.ts +1 -1
  96. package/dist/types/sdk.d.ts +2 -0
  97. package/dist/types/secrets/obfuscator.d.ts +1 -1
  98. package/dist/types/session/agent-session.d.ts +40 -3
  99. package/dist/types/session/cache-economics.d.ts +79 -0
  100. package/dist/types/session/context-estimation.d.ts +31 -0
  101. package/dist/types/session/provider-safety-stop.d.ts +5 -0
  102. package/dist/types/session/session-manager.d.ts +124 -4
  103. package/dist/types/session/session-storage.d.ts +155 -13
  104. package/dist/types/setup/credential-auto-import.d.ts +2 -2
  105. package/dist/types/skc-runtime/session-state-sidecar.d.ts +34 -3
  106. package/dist/types/skc-runtime/state-schema.d.ts +19 -19
  107. package/dist/types/skc-runtime/tmux-common.d.ts +6 -0
  108. package/dist/types/skc-runtime/tmux-owner-isolation-cli.d.ts +5 -0
  109. package/dist/types/skc-runtime/tmux-owner-isolation.d.ts +309 -0
  110. package/dist/types/skc-runtime/tmux-sessions.d.ts +49 -17
  111. package/dist/types/task/agents.d.ts +2 -2
  112. package/dist/types/task/executor.d.ts +3 -0
  113. package/dist/types/task/receipt.d.ts +3 -1
  114. package/dist/types/task/types.d.ts +35 -27
  115. package/dist/types/tools/ask-answer-registry.d.ts +5 -0
  116. package/dist/types/tools/bash.d.ts +1 -1
  117. package/dist/types/tools/browser/tab-supervisor.d.ts +6 -0
  118. package/dist/types/tools/browser.d.ts +28 -28
  119. package/dist/types/tools/computer.d.ts +20 -20
  120. package/dist/types/tools/cron.d.ts +2 -2
  121. package/dist/types/tools/debug.d.ts +40 -40
  122. package/dist/types/tools/gh.d.ts +10 -10
  123. package/dist/types/tools/image-gen.d.ts +2 -2
  124. package/dist/types/tools/index.d.ts +58 -6
  125. package/dist/types/tools/monitor.d.ts +6 -6
  126. package/dist/types/tools/skill-discovery.d.ts +4 -4
  127. package/dist/types/tools/subagent.d.ts +12 -12
  128. package/dist/types/tools/telegram-send.d.ts +7 -0
  129. package/dist/types/tools/todo-write.d.ts +8 -8
  130. package/dist/types/utils/external-editor.d.ts +2 -0
  131. package/dist/types/web/insane/bridge.d.ts +9 -9
  132. package/dist/types/web/search/index.d.ts +4 -4
  133. package/package.json +8 -8
  134. package/scripts/g004-tmux-smoke.ts +196 -36
  135. package/src/cli/daemon-cli.ts +19 -29
  136. package/src/cli/malloc-env-guard.test.ts +104 -0
  137. package/src/cli/malloc-env-guard.ts +149 -0
  138. package/src/cli/notify-cli.ts +78 -14
  139. package/src/cli/session-picker.ts +23 -40
  140. package/src/cli/update-cli.ts +56 -37
  141. package/src/cli.ts +51 -10
  142. package/src/commands/daemon.ts +19 -5
  143. package/src/commands/harness.ts +515 -47
  144. package/src/commands/launch.ts +11 -3
  145. package/src/commands/notify.ts +5 -1
  146. package/src/commands/session.ts +6 -1
  147. package/src/config/keybindings.ts +5 -0
  148. package/src/config/model-profile-activation.ts +1 -1
  149. package/src/config/model-profiles.ts +14 -14
  150. package/src/config/settings-schema.ts +41 -1
  151. package/src/config/settings.ts +96 -45
  152. package/src/coordinator/contract.ts +1 -0
  153. package/src/coordinator-mcp/model-preset.ts +100 -0
  154. package/src/coordinator-mcp/policy.ts +30 -0
  155. package/src/coordinator-mcp/server.ts +2958 -430
  156. package/src/coordinator-mcp/session-reaper.ts +129 -0
  157. package/src/daemon/control-types.ts +11 -0
  158. package/src/daemon/operator-contract.ts +121 -0
  159. package/src/defaults/skc/extensions/grok-cli-vendor/src/models/catalog.ts +57 -17
  160. package/src/defaults/skc/extensions/grok-cli-vendor/src/payload/sanitize.ts +15 -2
  161. package/src/defaults/skc/extensions/grok-cli-vendor/src/provider/register.ts +5 -1
  162. package/src/extensibility/extensions/runner.ts +1 -1
  163. package/src/extensibility/extensions/types.ts +3 -1
  164. package/src/internal-urls/docs-index.generated.ts +10 -9
  165. package/src/internal-urls/local-protocol.ts +29 -10
  166. package/src/main.ts +301 -80
  167. package/src/modes/acp/acp-agent.ts +730 -100
  168. package/src/modes/acp/acp-mode.ts +29 -2
  169. package/src/modes/components/assistant-message.ts +32 -3
  170. package/src/modes/components/bash-execution.ts +43 -13
  171. package/src/modes/components/custom-editor.ts +5 -0
  172. package/src/modes/components/footer.ts +3 -3
  173. package/src/modes/components/hook-selector.ts +12 -0
  174. package/src/modes/components/irc-sidebar.ts +206 -0
  175. package/src/modes/components/pet-selector.ts +55 -0
  176. package/src/modes/components/sayknow-pet-widget.ts +360 -0
  177. package/src/modes/components/session-selector.ts +222 -221
  178. package/src/modes/components/settings-selector.ts +10 -0
  179. package/src/modes/components/status-line/segments.ts +14 -7
  180. package/src/modes/components/status-line/types.ts +1 -1
  181. package/src/modes/components/status-line.ts +4 -171
  182. package/src/modes/components/tool-execution.ts +47 -6
  183. package/src/modes/components/user-message.ts +26 -9
  184. package/src/modes/controllers/command-controller.ts +28 -2
  185. package/src/modes/controllers/event-controller.ts +159 -36
  186. package/src/modes/controllers/extension-ui-controller.ts +37 -18
  187. package/src/modes/controllers/input-controller.ts +8 -2
  188. package/src/modes/controllers/selector-controller.ts +62 -15
  189. package/src/modes/interactive-mode.ts +199 -7
  190. package/src/modes/irc-observation-ledger.ts +127 -0
  191. package/src/modes/print-mode.ts +228 -92
  192. package/src/modes/rpc/rpc-client.ts +58 -1
  193. package/src/modes/rpc/rpc-mode.ts +286 -85
  194. package/src/modes/rpc/rpc-types.ts +15 -1
  195. package/src/modes/shared/agent-wire/command-dispatch.ts +33 -1
  196. package/src/modes/shared/agent-wire/command-validation.ts +10 -0
  197. package/src/modes/shared/agent-wire/deep-interview-gate.ts +45 -2
  198. package/src/modes/shared/agent-wire/scopes.ts +1 -0
  199. package/src/modes/shared/agent-wire/unattended-session.ts +288 -1
  200. package/src/modes/shared/agent-wire/workflow-gate-broker.ts +130 -20
  201. package/src/modes/types.ts +34 -2
  202. package/src/modes/utils/context-usage.ts +56 -92
  203. package/src/modes/utils/hotkeys-markdown.ts +1 -0
  204. package/src/modes/utils/irc-message.ts +138 -0
  205. package/src/modes/utils/ui-helpers.ts +216 -50
  206. package/src/notifications/config.ts +44 -0
  207. package/src/notifications/daemon-paths.ts +8 -0
  208. package/src/notifications/index.ts +535 -75
  209. package/src/notifications/lifecycle-control-runtime.ts +930 -77
  210. package/src/notifications/notification-service.ts +710 -0
  211. package/src/notifications/rate-limit-pool.ts +46 -12
  212. package/src/notifications/telegram-daemon-control.ts +27 -2
  213. package/src/notifications/telegram-daemon.ts +635 -94
  214. package/src/notifications/telegram-reference.ts +73 -13
  215. package/src/notifications/topic-registry.ts +86 -9
  216. package/src/prompts/system/irc-peer-roster.md +1 -0
  217. package/src/prompts/system/subagent-system-prompt.md +1 -0
  218. package/src/prompts/system/system-prompt.md +1 -0
  219. package/src/prompts/tools/irc.md +1 -1
  220. package/src/registry/agent-registry.ts +3 -0
  221. package/src/sdk.ts +52 -1
  222. package/src/secrets/obfuscator.ts +4 -2
  223. package/src/session/agent-session.ts +670 -217
  224. package/src/session/cache-economics.ts +275 -0
  225. package/src/session/context-estimation.ts +91 -0
  226. package/src/session/provider-safety-stop.ts +9 -0
  227. package/src/session/session-dump-format.ts +6 -0
  228. package/src/session/session-manager.ts +809 -39
  229. package/src/session/session-storage.ts +582 -38
  230. package/src/skc-runtime/session-state-sidecar.ts +756 -135
  231. package/src/skc-runtime/tmux-common.ts +26 -7
  232. package/src/skc-runtime/tmux-gc.ts +52 -9
  233. package/src/skc-runtime/tmux-owner-isolation-cli.ts +185 -0
  234. package/src/skc-runtime/tmux-owner-isolation.ts +2060 -0
  235. package/src/skc-runtime/tmux-sessions.ts +871 -68
  236. package/src/slash-commands/builtin-registry.ts +153 -9
  237. package/src/slash-commands/helpers/context-report.ts +26 -4
  238. package/src/task/executor.ts +12 -3
  239. package/src/task/index.ts +8 -0
  240. package/src/task/receipt.ts +7 -2
  241. package/src/task/types.ts +26 -0
  242. package/src/tools/ask-answer-registry.ts +25 -0
  243. package/src/tools/browser/tab-supervisor.ts +63 -6
  244. package/src/tools/browser.ts +8 -3
  245. package/src/tools/index.ts +81 -6
  246. package/src/tools/sqlite-reader.ts +8 -4
  247. package/src/tui/output-block.ts +18 -6
  248. package/src/utils/external-editor.ts +6 -1
@@ -21,10 +21,14 @@ import type { EvalExecutionComponent } from "./components/eval-execution";
21
21
  import type { HookEditorComponent } from "./components/hook-editor";
22
22
  import type { HookInputComponent } from "./components/hook-input";
23
23
  import type { HookSelectorComponent } from "./components/hook-selector";
24
+ import type { PetMode } from "./components/sayknow-pet-widget";
24
25
  import type { StatusLineComponent } from "./components/status-line";
25
26
  import type { ToolExecutionHandle } from "./components/tool-execution";
27
+ import type { IrcObservationLedger } from "./irc-observation-ledger";
26
28
  import type { OAuthManualInputManager } from "./oauth-manual-input";
27
29
  import type { Theme } from "./theme/theme";
30
+ import type { ParsedIrcMessage } from "./utils/irc-message";
31
+ export type TranscriptRebuildPolicy = "replace-identity" | "reconcile-same-transcript";
28
32
  export type CompactionQueuedMessage = {
29
33
  text: string;
30
34
  mode: "steer" | "followUp";
@@ -49,6 +53,13 @@ export type TodoPhase = {
49
53
  name: string;
50
54
  tasks: TodoItem[];
51
55
  };
56
+ export type IrcArrivalSnapshot = Readonly<{
57
+ panelVisible: boolean;
58
+ /** User-requested open state; may be true while the panel yields at narrow widths. */
59
+ panelRequestedVisible: boolean;
60
+ sidebarAvailable: boolean;
61
+ resolvedToggleKey: string | null;
62
+ }>;
52
63
  export interface InteractiveModeContext {
53
64
  ui: TUI;
54
65
  chatContainer: Container;
@@ -69,6 +80,7 @@ export interface InteractiveModeContext {
69
80
  historyStorage?: HistoryStorage;
70
81
  mcpManager?: MCPManager;
71
82
  lspServers?: LspStartupServerInfo[];
83
+ readonly ircLedger: IrcObservationLedger;
72
84
  isInitialized: boolean;
73
85
  isBackgrounded: boolean;
74
86
  isBashMode: boolean;
@@ -141,6 +153,11 @@ export interface InteractiveModeContext {
141
153
  setWorkingMessage(message?: string): void;
142
154
  applyPendingWorkingMessage(): void;
143
155
  ensureLoadingAnimation(): void;
156
+ setPetMode(mode: PetMode): void;
157
+ /** Live-preview a pet skin during a selector without persisting. */
158
+ previewPetMode(mode: PetMode): void;
159
+ /** Re-mount the composer (pet-aware) after an overlay/selector closes. */
160
+ restoreComposer(): void;
144
161
  startPendingSubmission(input: {
145
162
  text: string;
146
163
  images?: ImageContent[];
@@ -169,22 +186,30 @@ export interface InteractiveModeContext {
169
186
  addMessageToChat(message: AgentMessage, options?: {
170
187
  populateHistory?: boolean;
171
188
  }): Component[];
189
+ addLiveIrcObservationToChat(message: ParsedIrcMessage, arrival: IrcArrivalSnapshot): Component[];
190
+ removeRenderedIrcInlineComponents(observationId: string): readonly Component[] | undefined;
191
+ resetRenderedIrcInlineComponents(): readonly (readonly Component[])[];
172
192
  renderSessionContext(sessionContext: SessionContext, options?: {
173
193
  updateFooter?: boolean;
174
194
  populateHistory?: boolean;
175
195
  }): void;
176
- renderInitialMessages(prebuiltContext?: SessionContext, options?: {
196
+ rebuildInitialMessages(policy: TranscriptRebuildPolicy, prebuiltContext?: SessionContext, options?: {
177
197
  preserveExistingChat?: boolean;
178
198
  }): void;
179
199
  getUserMessageText(message: Message): string;
200
+ getAssistantViewportAnchorId?(message: AssistantMessage): string;
180
201
  findLastAssistantMessage(): AssistantMessage | undefined;
181
202
  extractAssistantText(message: AssistantMessage): string;
182
203
  updateEditorTopBorder(): void;
183
204
  updateEditorBorderColor(): void;
184
- rebuildChatFromMessages(): void;
205
+ rebuildChatFromMessages(policy: TranscriptRebuildPolicy): void;
185
206
  setTodos(todos: TodoItem[] | TodoPhase[]): void;
186
207
  reloadTodos(): Promise<void>;
187
208
  toggleTodoExpansion(): void;
209
+ toggleIrcSidebar(): void;
210
+ captureIrcArrivalSnapshot(): IrcArrivalSnapshot;
211
+ applyIrcSidebarAvailability(enabled: boolean): void;
212
+ resetIrcSidebarSession(): void;
188
213
  handleExportCommand(text: string): Promise<void>;
189
214
  handleShareCommand(): Promise<void>;
190
215
  handleCopyCommand(sub?: string): void;
@@ -219,6 +244,7 @@ export interface InteractiveModeContext {
219
244
  refreshSlashCommandState(cwd?: string): Promise<void>;
220
245
  showSettingsSelector(): void;
221
246
  showThemeSelector(): void;
247
+ showPetSelector(): void;
222
248
  showHistorySearch(): void;
223
249
  showExtensionsDashboard(): void;
224
250
  showAgentsDashboard(): void;
@@ -1,4 +1,4 @@
1
- export declare function buildAbortDisplayMessage({ errorMessage, retryAttempt }: {
1
+ export declare function buildAbortDisplayMessage({ errorMessage, retryAttempt, }: {
2
2
  errorMessage?: string;
3
3
  retryAttempt: number;
4
4
  }): string;
@@ -1,9 +1,8 @@
1
1
  import type { AgentMessage } from "@sayknow-cli/agent-core";
2
2
  import type { Model } from "@sayknow-cli/ai";
3
- import type { Skill } from "../../extensibility/skills";
4
3
  import type { AgentSession } from "../../session/agent-session";
5
- import type { Tool } from "../../tools";
6
4
  import type { theme as Theme } from "../theme/theme";
5
+ export { computeNonMessageTokens, estimateSkillsTokens, estimateToolSchemaTokens, } from "../../session/context-estimation";
7
6
  type CategoryId = "systemPrompt" | "systemContext" | "rules" | "tools" | "skills" | "messages" | "lastUserTurn";
8
7
  interface CategoryInfo {
9
8
  id: CategoryId;
@@ -17,25 +16,12 @@ export interface ContextBreakdown {
17
16
  contextWindow: number;
18
17
  categories: CategoryInfo[];
19
18
  lastUserTurnTokens: number;
20
- usedTokens: number;
19
+ estimatedCategoryTotal: number;
20
+ usedTokens: number | null;
21
+ source: "provider_anchor" | "heuristic" | "unknown";
21
22
  autoCompactBufferTokens: number;
22
23
  freeTokens: number;
23
24
  }
24
- export declare function estimateSkillsTokens(skills: readonly Skill[]): number;
25
- export declare function estimateToolSchemaTokens(tools: ReadonlyArray<Pick<Tool, "name" | "description" | "parameters">>): number;
26
- /**
27
- * Compute just the NON-MESSAGE token total: system prompt (with its skills
28
- * section subtracted, since skills are tokenized separately) + system context
29
- * (the rest of the system-prompt array) + tools + skills.
30
- *
31
- * Exposed so callers like `StatusLineComponent` can cache the non-message
32
- * total separately from the message total. Non-message inputs (skills,
33
- * tools, system prompt) change rarely; the message list grows on every
34
- * streaming turn. Splitting the two lets the caller refresh each on its own
35
- * cadence — non-message recomputed only when the inputs identity changes,
36
- * messages walked incrementally as new entries append.
37
- */
38
- export declare function computeNonMessageTokens(session: AgentSession): number;
39
25
  /**
40
26
  * Compute a breakdown of estimated context usage by category for the active
41
27
  * session and model.
@@ -48,4 +34,3 @@ export declare function computeContextBreakdown(session: AgentSession, options?:
48
34
  * lines pairing the grid (left) with the legend (right).
49
35
  */
50
36
  export declare function renderContextUsage(breakdown: ContextBreakdown, theme: typeof Theme): string;
51
- export {};
@@ -0,0 +1,38 @@
1
+ import type { AgentMessage } from "@sayknow-cli/agent-core";
2
+ export type IrcMessageKind = "incoming" | "autoreply" | "relay";
3
+ export type ParsedIrcMessage = {
4
+ observationId: string;
5
+ from: string;
6
+ to: string;
7
+ text: string;
8
+ timestamp: number;
9
+ kind: IrcMessageKind;
10
+ };
11
+ export interface IrcMessageBlock {
12
+ readonly sender: string;
13
+ readonly recipient: string;
14
+ readonly kind: "incoming" | "autoreply" | "outgoing";
15
+ readonly time: string;
16
+ readonly bodyLines: readonly string[];
17
+ }
18
+ export declare function projectIrcText(text: string, maxUtf8Bytes: number): {
19
+ text: string;
20
+ truncated: boolean;
21
+ utf8Bytes: number;
22
+ };
23
+ /** Normalizes and bounds untrusted IRC identity fields without splitting visible graphemes. */
24
+ export declare function normalizeIrcIdentity(identity: string): string;
25
+ /** Formats IRC observations into display-neutral semantic blocks for both IRC surfaces. */
26
+ export declare function formatIrcMessageBlock(message: ParsedIrcMessage & {
27
+ timestamp: number;
28
+ }): IrcMessageBlock;
29
+ type IrcCustomMessage = Extract<AgentMessage, {
30
+ role: "custom";
31
+ }>;
32
+ export declare function isIrcCustomType(customType: string | undefined): customType is `irc:${IrcMessageKind}`;
33
+ /**
34
+ * Normalizes every supported IRC custom message into its UI observation shape.
35
+ * Legacy records have no UUID, so their immutable payload is used as a stable key.
36
+ */
37
+ export declare function parseIrcMessage(message: IrcCustomMessage): ParsedIrcMessage | undefined;
38
+ export {};
@@ -1,8 +1,11 @@
1
1
  import type { AgentMessage } from "@sayknow-cli/agent-core";
2
2
  import type { AssistantMessage, Message } from "@sayknow-cli/ai";
3
- import { type Component } from "@sayknow-cli/tui";
4
- import type { InteractiveModeContext } from "../../modes/types";
5
- import type { SessionContext } from "../../session/session-manager";
3
+ import { type Component, type TUI } from "@sayknow-cli/tui";
4
+ import type { InteractiveModeContext, IrcArrivalSnapshot, TranscriptRebuildPolicy } from "../../modes/types";
5
+ import { type SessionContext } from "../../session/session-manager";
6
+ import { type ParsedIrcMessage } from "./irc-message";
7
+ export type { TranscriptRebuildPolicy } from "../../modes/types";
8
+ export declare function prepareTranscriptRebuild(ui: TUI, policy: TranscriptRebuildPolicy): void;
6
9
  interface RenderInitialMessagesOptions {
7
10
  preserveExistingChat?: boolean;
8
11
  }
@@ -23,6 +26,13 @@ export declare class UiHelpers {
23
26
  #private;
24
27
  private ctx;
25
28
  constructor(ctx: InteractiveModeContext);
29
+ assistantViewportAnchorId(message: AssistantMessage): string;
30
+ getRenderedIrcInlineComponents(): Map<string, readonly Component[]>;
31
+ removeRenderedIrcInlineComponents(observationId: string): readonly Component[] | undefined;
32
+ resetRenderedIrcInlineComponents(): readonly (readonly Component[])[];
33
+ addLiveIrcObservationToChat(message: ParsedIrcMessage, arrival: IrcArrivalSnapshot): Component[];
34
+ addRebuiltIrcObservationToChat(message: ParsedIrcMessage): Component[];
35
+ resetIrcSidebarHint(): void;
26
36
  /** Extract text content from a user message */
27
37
  getUserMessageText(message: Message): string;
28
38
  /**
@@ -63,4 +73,3 @@ export declare class UiHelpers {
63
73
  findLastAssistantMessage(): AssistantMessage | undefined;
64
74
  extractAssistantText(message: AssistantMessage): string;
65
75
  }
66
- export {};
@@ -1,4 +1,14 @@
1
1
  import type { Settings } from "../config/settings";
2
+ /**
3
+ * Env marker set by SKC's own programmatic separate-process child spawn sites
4
+ * (team workers, harness RPC owners) and carrying the spawning session id.
5
+ *
6
+ * Presence — not the value — marks a session as SKC-spawned. It is consumed
7
+ * (read once, then deleted from the child's own env) at startup so it is
8
+ * per-spawn rather than dynastic: a grandchild is marked only if its own spawn
9
+ * site marks it, never by inheriting a marked ancestor's environment.
10
+ */
11
+ export declare const SPAWN_PROVENANCE_ENV = "SKC_SPAWNED_BY_SESSION";
2
12
  export interface NotificationConfig {
3
13
  enabled: boolean;
4
14
  botToken?: string;
@@ -13,6 +23,12 @@ export interface NotificationConfig {
13
23
  };
14
24
  redact: boolean;
15
25
  verbosity: "lean" | "verbose";
26
+ /**
27
+ * Which sessions may register a notification endpoint. `all` (default)
28
+ * preserves historical behavior; `primary` suppresses SKC-spawned children
29
+ * (those carrying {@link SPAWN_PROVENANCE_ENV}) unless they explicitly opt in.
30
+ */
31
+ sessionScope: "all" | "primary";
16
32
  idleTimeoutMs: number;
17
33
  rich: {
18
34
  enabled: boolean;
@@ -20,6 +36,15 @@ export interface NotificationConfig {
20
36
  richDraft: {
21
37
  enabled: boolean;
22
38
  };
39
+ topics: {
40
+ /**
41
+ * Optional Telegram forum-topic name template with `{repo}`, `{branch}`,
42
+ * and `{title}` placeholders. Unset preserves the built-in
43
+ * `{repo}/{branch} - {title}` composition (with its title/repo/branch
44
+ * fallbacks).
45
+ */
46
+ nameTemplate?: string;
47
+ };
23
48
  }
24
49
  /** Read typed config from Settings. */
25
50
  export declare function getNotificationConfig(settings: Settings): NotificationConfig;
@@ -51,6 +76,15 @@ export declare function shouldRegisterNotificationsExtension(input: {
51
76
  parentTaskPrefix?: string;
52
77
  /** Role-agent type/name; present for task sessions even if depth metadata is lost. */
53
78
  currentAgentType?: string;
79
+ /**
80
+ * True when this session was launched by one of SKC's own programmatic
81
+ * separate-process child spawn sites (marked via {@link SPAWN_PROVENANCE_ENV}).
82
+ * Under `notifications.sessionScope = "primary"` such children are suppressed
83
+ * unless they explicitly opt in, so an interactive parent that fans out work
84
+ * does not flood the paired chat with topics for children the user never
85
+ * asked for. User-opened sessions (CLI/tmux/headless) never carry the marker.
86
+ */
87
+ spawnedBySkc?: boolean;
54
88
  }): boolean;
55
89
  /**
56
90
  * Resolve whether THIS session should run notifications.
@@ -1,3 +1,10 @@
1
+ /**
2
+ * Owner heartbeat freshness window. A daemon ownership record older than this
3
+ * (without a live pid) is considered stale. Lives here, in the lightweight
4
+ * paths module, so secret-safe consumers (e.g. the notification service) can
5
+ * reuse it without importing the heavy daemon runtime.
6
+ */
7
+ export declare const HEARTBEAT_TTL_MS = 20000;
1
8
  export interface DaemonPaths {
2
9
  dir: string;
3
10
  lock: string;
@@ -176,6 +176,33 @@ export interface SessionReadyFrame {
176
176
  */
177
177
  export declare function readGitRepoName(cwd: string): string | undefined;
178
178
  export declare function notificationsEnabled(): boolean;
179
+ /** Workflow-gate answer shape (unattended mode). */
180
+ interface GateAnswer {
181
+ selected: string[];
182
+ other?: boolean;
183
+ custom?: string;
184
+ }
185
+ /**
186
+ * Discriminated result of mapping a client answer to a workflow-gate answer.
187
+ * `ok: false` means the reply is invalid and the caller must close the exact
188
+ * claim/receipt and reissue the interaction rather than durably accepting it.
189
+ */
190
+ type GateAnswerResult = {
191
+ ok: true;
192
+ answer: GateAnswer;
193
+ } | {
194
+ ok: false;
195
+ reason: string;
196
+ };
197
+ /**
198
+ * Map a client answer to the workflow-gate answer shape (unattended mode).
199
+ *
200
+ * The protocol defines a numeric reply as an option index, so a number outside
201
+ * `options` is invalid (issue #2030): it must NOT be converted into a free-text
202
+ * `Other` that passes the ask schema and triggers a misleading success ack.
203
+ * Only JSON strings enter the free-text/Other path.
204
+ */
205
+ export declare function mapAnswerToGate(answerJson: string, options: string[]): GateAnswerResult;
179
206
  interface NotificationControlCommandPayload {
180
207
  name?: unknown;
181
208
  action?: unknown;
@@ -1,3 +1,5 @@
1
+ import { type OwnerIsolationProbe } from "../skc-runtime/tmux-owner-isolation";
2
+ import { type SkcTmuxSessionStatus } from "../skc-runtime/tmux-sessions";
1
3
  import type { ResumeCandidate, SessionCreateFrame, SessionLifecycleRequest, SessionLifecycleResponse } from "./index";
2
4
  import { type AuditEvent, type CreateEffectResult, type LedgerStore, type LifecycleOutcome, type OrchestratorDeps, type ResumeEffectResult } from "./lifecycle-orchestrator";
3
5
  /** Minimal view of the native control server this runtime depends on. */
@@ -24,7 +26,6 @@ export type LifecycleControlServerFactory = (input: {
24
26
  ownerId: string;
25
27
  agentDir: string;
26
28
  }) => LifecycleControlServer;
27
- /** Atomic + fsynced file-backed idempotency ledger store. */
28
29
  export declare function fileLedgerStore(idempotencyFile: string): LedgerStore;
29
30
  /** Append-only JSONL audit sink (0600). Never receives tokens or raw prompts. */
30
31
  export declare function fileAudit(auditPath: string): (e: AuditEvent) => void;
@@ -44,14 +45,19 @@ export declare function buildCreateArgv(frame: SessionCreateFrame, _ids: {
44
45
  cwd: string;
45
46
  args: string[];
46
47
  };
47
- /** Real daemon-safe tmux launcher: detached `tmux new-session -d` + SKC tags. */
48
- export declare function daemonSpawnCreate(env?: NodeJS.ProcessEnv): (frame: SessionCreateFrame, ids: {
48
+ /** Real daemon-safe tmux launcher: canonical owner-isolation plan + SKC tags. */
49
+ export declare function daemonSpawnCreate(env?: NodeJS.ProcessEnv, opts?: {
50
+ ownerIsolationProbe?: OwnerIsolationProbe;
51
+ }): (frame: SessionCreateFrame, ids: {
49
52
  lifecycleRequestId: string;
50
53
  intendedSessionId: string;
51
54
  startupPromptRef?: string;
52
55
  }) => Promise<CreateEffectResult>;
53
56
  /** Real force-close effect (SKC-managed only, id-matched). */
54
- export declare function daemonCloseSession(env?: NodeJS.ProcessEnv): (target: {
57
+ export declare function daemonCloseSession(env?: NodeJS.ProcessEnv, deps?: {
58
+ forceClose?: (name: string, env: NodeJS.ProcessEnv, expectedSessionId?: string, sessionStateFile?: string) => Promise<void>;
59
+ findSession?: (name: string, env: NodeJS.ProcessEnv) => SkcTmuxSessionStatus | undefined;
60
+ }): (target: {
55
61
  sessionId: string;
56
62
  tmuxSession?: string;
57
63
  sessionStateFile?: string;
@@ -63,6 +69,8 @@ export declare function daemonCloseSession(env?: NodeJS.ProcessEnv): (target: {
63
69
  * cold-restarting exactly one resolved session via the daemon-safe launcher. */
64
70
  export declare function daemonResumeSession(env?: NodeJS.ProcessEnv, opts?: {
65
71
  sessionsRoot?: string;
72
+ listSessions?: (env: NodeJS.ProcessEnv) => SkcTmuxSessionStatus[];
73
+ ownerIsolationProbe?: OwnerIsolationProbe;
66
74
  }): (target: {
67
75
  sessionIdOrPrefix: string;
68
76
  path?: string;
@@ -0,0 +1,144 @@
1
+ import type { Settings } from "../config/settings";
2
+ /**
3
+ * Strip secrets from a human-facing diagnostic string. Redacts the configured
4
+ * bot token (exact match) and any token-shaped substring so health/test details
5
+ * can never leak a credential regardless of where the string originated.
6
+ */
7
+ export declare function sanitizeDiagnostic(text: string, token?: string): string;
8
+ /** Minimal filesystem surface the service needs; injectable for tests. */
9
+ export interface NotificationServiceFs {
10
+ readdir(dir: string): Promise<string[]>;
11
+ readFile(file: string, encoding: "utf8"): Promise<string>;
12
+ unlink(file: string): Promise<void>;
13
+ /**
14
+ * Atomically create `file` with O_EXCL semantics: resolves `true` when this
15
+ * call created it, `false` when it already existed. Used to hold the daemon
16
+ * steal-mutex ({@link DaemonPaths.steal}) during owner-bound lock removal so
17
+ * recovery and a concurrent daemon takeover are mutually exclusive.
18
+ */
19
+ createExclusive(file: string): Promise<boolean>;
20
+ }
21
+ /** Injectable dependencies shared across service operations. */
22
+ export interface NotificationServiceDeps {
23
+ fs?: NotificationServiceFs;
24
+ now?: () => number;
25
+ pidAlive?: (pid: number) => boolean;
26
+ fetchImpl?: typeof fetch;
27
+ apiBase?: string;
28
+ }
29
+ export interface AdapterConfigView {
30
+ botTokenMasked: string;
31
+ channel: string | undefined;
32
+ configured: boolean;
33
+ }
34
+ export interface NotificationStatusReport {
35
+ enabled: boolean;
36
+ redact: boolean;
37
+ verbosity: "lean" | "verbose";
38
+ globallyConfigured: boolean;
39
+ telegram: AdapterConfigView & {
40
+ tokenFingerprint: string | undefined;
41
+ };
42
+ discord: AdapterConfigView;
43
+ slack: AdapterConfigView;
44
+ }
45
+ /** Build a secret-safe structured status snapshot from settings. */
46
+ export declare function buildNotificationStatusReport(settings: Settings): NotificationStatusReport;
47
+ /** Render a status report as human-readable lines (no secrets). */
48
+ export declare function formatNotificationStatusReport(report: NotificationStatusReport): string;
49
+ export type HealthLevel = "ok" | "warn" | "error";
50
+ export interface HealthCheck {
51
+ name: string;
52
+ level: HealthLevel;
53
+ detail: string;
54
+ }
55
+ export interface DaemonHealth {
56
+ present: boolean;
57
+ ownerId: string | undefined;
58
+ pid: number | undefined;
59
+ alive: boolean;
60
+ heartbeatFresh: boolean;
61
+ identityMatches: boolean;
62
+ stopped: boolean;
63
+ }
64
+ export interface EndpointHealth {
65
+ total: number;
66
+ live: number;
67
+ dead: number;
68
+ unknown: number;
69
+ unreadable: number;
70
+ }
71
+ export interface NotificationHealthReport {
72
+ overall: HealthLevel;
73
+ configured: boolean;
74
+ checks: HealthCheck[];
75
+ daemon: DaemonHealth;
76
+ endpoints: EndpointHealth;
77
+ reachability: {
78
+ probed: boolean;
79
+ ok: boolean;
80
+ detail: string;
81
+ };
82
+ }
83
+ export interface HealthOptions {
84
+ settings: Settings;
85
+ stateRoot?: string;
86
+ deps?: NotificationServiceDeps;
87
+ /** When true and Telegram is configured, probe the Bot API (getMe) for reachability. */
88
+ probe?: boolean;
89
+ }
90
+ /** Structural (offline-by-default) health of the notification subsystem. */
91
+ export declare function checkNotificationHealth(opts: HealthOptions): Promise<NotificationHealthReport>;
92
+ /** Render a health report as human-readable lines (no secrets). */
93
+ export declare function formatNotificationHealthReport(report: NotificationHealthReport): string;
94
+ export interface NotificationTestResult {
95
+ ok: boolean;
96
+ adapter: "telegram";
97
+ chatId: string | undefined;
98
+ detail: string;
99
+ }
100
+ export interface TestOptions {
101
+ settings: Settings;
102
+ deps?: NotificationServiceDeps;
103
+ text?: string;
104
+ }
105
+ /** Send a one-off test notification through the configured Telegram adapter. */
106
+ export declare function sendNotificationTest(opts: TestOptions): Promise<NotificationTestResult>;
107
+ /** Render a test result as a single human-readable line (no secrets). */
108
+ export declare function formatNotificationTestResult(result: NotificationTestResult): string;
109
+ export interface RecoveredEndpoint {
110
+ sessionId: string;
111
+ pid: number | undefined;
112
+ reason: "stale-flag" | "dead-pid";
113
+ }
114
+ export type DaemonRecoveryAction = "none" | "cleared-dead-owner-lock" | "left-active" | "left-contended" | "owner-superseded" | "orphan-lock-left";
115
+ export interface NotificationRecoveryReport {
116
+ endpointsScanned: number;
117
+ endpointsRemoved: RecoveredEndpoint[];
118
+ endpointsKept: number;
119
+ endpointsUnreadable: number;
120
+ daemon: {
121
+ action: DaemonRecoveryAction;
122
+ detail: string;
123
+ ownerId: string | undefined;
124
+ pid: number | undefined;
125
+ };
126
+ }
127
+ export interface RecoveryOptions {
128
+ settings: Settings;
129
+ stateRoot?: string;
130
+ deps?: NotificationServiceDeps;
131
+ }
132
+ /**
133
+ * Ownership-protected cleanup. Removes only DEAD-owner artifacts:
134
+ * per-session endpoint files with positive proof of death (a stale tombstone or
135
+ * a dead recorded pid), and a daemon lock whose recorded owner is confirmed
136
+ * dead. A PID-less endpoint is treated as unknown (not dead) and kept. The
137
+ * daemon lock is removed through {@link removeDeadOwnerLock}, an owner-bound
138
+ * primitive that re-checks ownership under the daemon steal-mutex so it can
139
+ * never race a concurrent takeover. Never removes a live owner's lock, never
140
+ * deletes unreadable files, and never kills a process.
141
+ */
142
+ export declare function recoverNotifications(opts: RecoveryOptions): Promise<NotificationRecoveryReport>;
143
+ /** Render a recovery report as human-readable lines (no secrets). */
144
+ export declare function formatNotificationRecoveryReport(report: NotificationRecoveryReport): string;
@@ -29,11 +29,15 @@ export interface RateLimitItem<T = unknown> {
29
29
  /** Priority lane. */
30
30
  lane: RateLimitLane;
31
31
  /**
32
- * Optional coalesce key. Submitting another item with the same
33
- * `(sessionId, lane, coalesceKey)` replaces the queued payload with the
34
- * newer one instead of enqueuing a duplicate (used for live edits).
32
+ * Optional coalesce key. Submitting another unidentifiable item with the
33
+ * same `(sessionId, lane, coalesceKey)` replaces the queued payload with
34
+ * the newer one instead of enqueuing a duplicate (used for live edits).
35
35
  */
36
36
  coalesceKey?: string;
37
+ /** Optional stable identifier for exact queued-item removal. Identified items never coalesce. */
38
+ itemId?: string;
39
+ /** Absolute Unix timestamp in ms. The item expires when `now >= deadlineAt`. */
40
+ deadlineAt?: number;
37
41
  /** Opaque payload the caller maps to an actual Telegram send. */
38
42
  payload: T;
39
43
  }
@@ -46,6 +50,13 @@ export interface RateLimitPoolOptions {
46
50
  /** Injectable clock in ms. Default `Date.now`. */
47
51
  now?: () => number;
48
52
  }
53
+ /** The deterministic result of draining queued work at a point in time. */
54
+ export interface RateLimitDrainResult<T = unknown> {
55
+ /** Items granted a token and ready to send. */
56
+ granted: RateLimitItem<T>[];
57
+ /** Items removed because their absolute deadline has elapsed. */
58
+ expired: RateLimitItem<T>[];
59
+ }
49
60
  /**
50
61
  * A deterministic, pull-based shared rate-limit scheduler.
51
62
  *
@@ -70,19 +81,26 @@ export declare class RateLimitPool<T = unknown> {
70
81
  /** Current available token count (after refill at `now`). */
71
82
  availableTokens(nowMs?: number): number;
72
83
  /**
73
- * Submit an item. If it carries a `coalesceKey` matching a queued item in
74
- * the same `(sessionId, lane)`, the queued payload is replaced (latest
75
- * wins) and FIFO position is preserved; otherwise it is appended.
84
+ * Submit an item. Unidentified items with a `coalesceKey` matching a queued
85
+ * item in the same `(sessionId, lane)` replace its payload (latest wins)
86
+ * while preserving FIFO position; identified items are always appended.
76
87
  */
77
88
  submit(item: RateLimitItem<T>): void;
78
89
  /**
79
- * Grant as many queued items as tokens allow at `nowMs`. Items are selected
80
- * by lane priority, then round-robin across sessions within a lane (so no
81
- * single session monopolises a lane), consuming one token each.
90
+ * Grant as many queued items as tokens allow at `nowMs`. This compatibility
91
+ * wrapper discards items that expired during the drain.
82
92
  */
83
93
  drain(nowMs?: number): RateLimitItem<T>[];
94
+ /**
95
+ * Deterministically expire elapsed-deadline items, then grant as many live
96
+ * items as tokens allow. Expired items never consume tokens or receive a
97
+ * grant; both result lists preserve their deterministic queue ordering.
98
+ */
99
+ drainWithExpired(nowMs?: number): RateLimitDrainResult<T>;
84
100
  /** Remove queued items matching `predicate` without consuming tokens. Returns removed items in lane/FIFO order. */
85
101
  removeWhere(predicate: (item: RateLimitItem<T>) => boolean): RateLimitItem<T>[];
102
+ /** Remove exactly one queued item by its stable id without consuming a token. */
103
+ removeById(itemId: string): RateLimitItem<T> | undefined;
86
104
  private refill;
87
105
  /** Pop the next item by lane priority + per-session round-robin fairness. */
88
106
  private takeNext;
@@ -55,10 +55,16 @@ export declare class TelegramDaemonController implements BuiltInDaemonController
55
55
  status(): Promise<DaemonStatus>;
56
56
  private spawnDeps;
57
57
  private sleep;
58
+ /**
59
+ * Wait until the captured pid is dead. Ownership-file movement is NOT treated
60
+ * as quiescence here: only actual process death proves the old poller stopped,
61
+ * which is what the no-409 invariant requires before spawning a fresh poller.
62
+ */
58
63
  private waitForPidDeath;
59
64
  private result;
60
65
  reload(opts?: DaemonOperationOptions): Promise<DaemonOperationResult>;
61
66
  stop(opts?: DaemonOperationOptions): Promise<DaemonOperationResult>;
62
67
  private stopOrReload;
68
+ /** Clear our own control request unless a newer owner-scoped request replaced it. */
63
69
  private clearOwnRequest;
64
70
  }