@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.
- package/dist/types/autoresearch/tools/init-experiment.d.ts +1 -1
- package/dist/types/autoresearch/tools/log-experiment.d.ts +2 -2
- package/dist/types/cli/classify-install-target.d.ts +0 -10
- package/dist/types/cli/daemon-cli.d.ts +2 -0
- package/dist/types/cli/initial-message.d.ts +1 -1
- package/dist/types/cli/malloc-env-guard.d.ts +65 -0
- package/dist/types/cli/malloc-env-guard.test.d.ts +1 -0
- package/dist/types/cli/notify-cli.d.ts +3 -1
- package/dist/types/cli/session-picker.d.ts +4 -3
- package/dist/types/cli/update-cli.d.ts +22 -3
- package/dist/types/cli.d.ts +5 -0
- package/dist/types/commands/daemon.d.ts +6 -2
- package/dist/types/commands/launch.d.ts +1 -0
- package/dist/types/commands/notify.d.ts +6 -0
- package/dist/types/commands/web-search.d.ts +1 -1
- package/dist/types/commit/agentic/tools/propose-changelog.d.ts +4 -4
- package/dist/types/commit/agentic/tools/propose-commit.d.ts +9 -9
- package/dist/types/commit/agentic/tools/schemas.d.ts +9 -9
- package/dist/types/commit/agentic/tools/split-commit.d.ts +9 -9
- package/dist/types/commit/analysis/conventional.d.ts +1 -1
- package/dist/types/commit/analysis/summary.d.ts +1 -1
- package/dist/types/commit/changelog/generate.d.ts +3 -3
- package/dist/types/commit/changelog/index.d.ts +2 -2
- package/dist/types/commit/map-reduce/map-phase.d.ts +1 -1
- package/dist/types/commit/map-reduce/reduce-phase.d.ts +1 -1
- package/dist/types/commit/shared-llm.d.ts +9 -9
- package/dist/types/config/keybindings.d.ts +134 -129
- package/dist/types/config/model-profile-activation.d.ts +1 -0
- package/dist/types/config/model-registry.d.ts +35 -35
- package/dist/types/config/models-config-schema.d.ts +111 -111
- package/dist/types/config/settings-schema.d.ts +56 -16
- package/dist/types/config/settings.d.ts +2 -0
- package/dist/types/coordinator/contract.d.ts +1 -1
- package/dist/types/coordinator-mcp/model-preset.d.ts +45 -0
- package/dist/types/coordinator-mcp/policy.d.ts +3 -0
- package/dist/types/coordinator-mcp/server.d.ts +79 -6
- package/dist/types/coordinator-mcp/session-reaper.d.ts +53 -0
- package/dist/types/daemon/control-types.d.ts +10 -0
- package/dist/types/daemon/operator-contract.d.ts +47 -0
- package/dist/types/defaults/skc/extensions/grok-cli-vendor/src/models/catalog.d.ts +4 -0
- package/dist/types/defaults/skc/extensions/grok-cli-vendor/src/shared/errors.d.ts +12 -12
- package/dist/types/discovery/helpers.d.ts +1 -1
- package/dist/types/discovery/substitute-plugin-root.d.ts +0 -4
- package/dist/types/edit/modes/patch.d.ts +2 -2
- package/dist/types/extensibility/extensions/types.d.ts +3 -1
- package/dist/types/goals/tools/goal-tool.d.ts +6 -6
- package/dist/types/i18n/messages/en.d.ts +8 -8
- package/dist/types/internal-urls/local-protocol.d.ts +13 -9
- package/dist/types/lsp/index.d.ts +9 -9
- package/dist/types/lsp/types.d.ts +9 -9
- package/dist/types/main.d.ts +66 -2
- package/dist/types/modes/acp/acp-agent.d.ts +19 -2
- package/dist/types/modes/acp/acp-mode.d.ts +12 -0
- package/dist/types/modes/components/assistant-message.d.ts +1 -1
- package/dist/types/modes/components/custom-editor.d.ts +2 -0
- package/dist/types/modes/components/hook-selector.d.ts +5 -0
- package/dist/types/modes/components/irc-sidebar.d.ts +32 -0
- package/dist/types/modes/components/pet-selector.d.ts +11 -0
- package/dist/types/modes/components/sayknow-pet-widget.d.ts +59 -0
- package/dist/types/modes/components/session-selector.d.ts +22 -9
- package/dist/types/modes/components/settings-selector.d.ts +2 -0
- package/dist/types/modes/components/status-line/types.d.ts +1 -1
- package/dist/types/modes/components/status-line.d.ts +0 -10
- package/dist/types/modes/components/tool-execution.d.ts +1 -0
- package/dist/types/modes/components/user-message.d.ts +2 -1
- package/dist/types/modes/controllers/event-controller.d.ts +4 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +24 -2
- package/dist/types/modes/irc-observation-ledger.d.ts +22 -0
- package/dist/types/modes/print-mode.d.ts +3 -4
- package/dist/types/modes/rpc/rpc-client.d.ts +7 -1
- package/dist/types/modes/rpc/rpc-mode.d.ts +2 -1
- package/dist/types/modes/rpc/rpc-types.d.ts +17 -1
- package/dist/types/modes/shared/agent-wire/deep-interview-gate.d.ts +8 -0
- package/dist/types/modes/shared/agent-wire/unattended-run-controller.d.ts +5 -0
- package/dist/types/modes/shared/agent-wire/unattended-session.d.ts +45 -0
- package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +42 -2
- package/dist/types/modes/theme/defaults/index.d.ts +568 -568
- package/dist/types/modes/types.d.ts +28 -2
- package/dist/types/modes/utils/abort-message.d.ts +1 -1
- package/dist/types/modes/utils/context-usage.d.ts +4 -19
- package/dist/types/modes/utils/irc-message.d.ts +38 -0
- package/dist/types/modes/utils/ui-helpers.d.ts +13 -4
- package/dist/types/notifications/config.d.ts +34 -0
- package/dist/types/notifications/daemon-paths.d.ts +7 -0
- package/dist/types/notifications/index.d.ts +27 -0
- package/dist/types/notifications/lifecycle-control-runtime.d.ts +12 -4
- package/dist/types/notifications/notification-service.d.ts +144 -0
- package/dist/types/notifications/rate-limit-pool.d.ts +27 -9
- package/dist/types/notifications/telegram-daemon-control.d.ts +6 -0
- package/dist/types/notifications/telegram-daemon.d.ts +130 -3
- package/dist/types/notifications/telegram-reference.d.ts +19 -2
- package/dist/types/notifications/topic-registry.d.ts +24 -4
- package/dist/types/registry/agent-registry.d.ts +2 -0
- package/dist/types/rlm/index.d.ts +1 -1
- package/dist/types/sdk.d.ts +2 -0
- package/dist/types/secrets/obfuscator.d.ts +1 -1
- package/dist/types/session/agent-session.d.ts +40 -3
- package/dist/types/session/cache-economics.d.ts +79 -0
- package/dist/types/session/context-estimation.d.ts +31 -0
- package/dist/types/session/provider-safety-stop.d.ts +5 -0
- package/dist/types/session/session-manager.d.ts +124 -4
- package/dist/types/session/session-storage.d.ts +155 -13
- package/dist/types/setup/credential-auto-import.d.ts +2 -2
- package/dist/types/skc-runtime/session-state-sidecar.d.ts +34 -3
- package/dist/types/skc-runtime/state-schema.d.ts +19 -19
- package/dist/types/skc-runtime/tmux-common.d.ts +6 -0
- package/dist/types/skc-runtime/tmux-owner-isolation-cli.d.ts +5 -0
- package/dist/types/skc-runtime/tmux-owner-isolation.d.ts +309 -0
- package/dist/types/skc-runtime/tmux-sessions.d.ts +49 -17
- package/dist/types/task/agents.d.ts +2 -2
- package/dist/types/task/executor.d.ts +3 -0
- package/dist/types/task/receipt.d.ts +3 -1
- package/dist/types/task/types.d.ts +35 -27
- package/dist/types/tools/ask-answer-registry.d.ts +5 -0
- package/dist/types/tools/bash.d.ts +1 -1
- package/dist/types/tools/browser/tab-supervisor.d.ts +6 -0
- package/dist/types/tools/browser.d.ts +28 -28
- package/dist/types/tools/computer.d.ts +20 -20
- package/dist/types/tools/cron.d.ts +2 -2
- package/dist/types/tools/debug.d.ts +40 -40
- package/dist/types/tools/gh.d.ts +10 -10
- package/dist/types/tools/image-gen.d.ts +2 -2
- package/dist/types/tools/index.d.ts +58 -6
- package/dist/types/tools/monitor.d.ts +6 -6
- package/dist/types/tools/skill-discovery.d.ts +4 -4
- package/dist/types/tools/subagent.d.ts +12 -12
- package/dist/types/tools/telegram-send.d.ts +7 -0
- package/dist/types/tools/todo-write.d.ts +8 -8
- package/dist/types/utils/external-editor.d.ts +2 -0
- package/dist/types/web/insane/bridge.d.ts +9 -9
- package/dist/types/web/search/index.d.ts +4 -4
- package/package.json +8 -8
- package/scripts/g004-tmux-smoke.ts +196 -36
- package/src/cli/daemon-cli.ts +19 -29
- package/src/cli/malloc-env-guard.test.ts +104 -0
- package/src/cli/malloc-env-guard.ts +149 -0
- package/src/cli/notify-cli.ts +78 -14
- package/src/cli/session-picker.ts +23 -40
- package/src/cli/update-cli.ts +56 -37
- package/src/cli.ts +51 -10
- package/src/commands/daemon.ts +19 -5
- package/src/commands/harness.ts +515 -47
- package/src/commands/launch.ts +11 -3
- package/src/commands/notify.ts +5 -1
- package/src/commands/session.ts +6 -1
- package/src/config/keybindings.ts +5 -0
- package/src/config/model-profile-activation.ts +1 -1
- package/src/config/model-profiles.ts +14 -14
- package/src/config/settings-schema.ts +41 -1
- package/src/config/settings.ts +96 -45
- package/src/coordinator/contract.ts +1 -0
- package/src/coordinator-mcp/model-preset.ts +100 -0
- package/src/coordinator-mcp/policy.ts +30 -0
- package/src/coordinator-mcp/server.ts +2958 -430
- package/src/coordinator-mcp/session-reaper.ts +129 -0
- package/src/daemon/control-types.ts +11 -0
- package/src/daemon/operator-contract.ts +121 -0
- package/src/defaults/skc/extensions/grok-cli-vendor/src/models/catalog.ts +57 -17
- package/src/defaults/skc/extensions/grok-cli-vendor/src/payload/sanitize.ts +15 -2
- package/src/defaults/skc/extensions/grok-cli-vendor/src/provider/register.ts +5 -1
- package/src/extensibility/extensions/runner.ts +1 -1
- package/src/extensibility/extensions/types.ts +3 -1
- package/src/internal-urls/docs-index.generated.ts +10 -9
- package/src/internal-urls/local-protocol.ts +29 -10
- package/src/main.ts +301 -80
- package/src/modes/acp/acp-agent.ts +730 -100
- package/src/modes/acp/acp-mode.ts +29 -2
- package/src/modes/components/assistant-message.ts +32 -3
- package/src/modes/components/bash-execution.ts +43 -13
- package/src/modes/components/custom-editor.ts +5 -0
- package/src/modes/components/footer.ts +3 -3
- package/src/modes/components/hook-selector.ts +12 -0
- package/src/modes/components/irc-sidebar.ts +206 -0
- package/src/modes/components/pet-selector.ts +55 -0
- package/src/modes/components/sayknow-pet-widget.ts +360 -0
- package/src/modes/components/session-selector.ts +222 -221
- package/src/modes/components/settings-selector.ts +10 -0
- package/src/modes/components/status-line/segments.ts +14 -7
- package/src/modes/components/status-line/types.ts +1 -1
- package/src/modes/components/status-line.ts +4 -171
- package/src/modes/components/tool-execution.ts +47 -6
- package/src/modes/components/user-message.ts +26 -9
- package/src/modes/controllers/command-controller.ts +28 -2
- package/src/modes/controllers/event-controller.ts +159 -36
- package/src/modes/controllers/extension-ui-controller.ts +37 -18
- package/src/modes/controllers/input-controller.ts +8 -2
- package/src/modes/controllers/selector-controller.ts +62 -15
- package/src/modes/interactive-mode.ts +199 -7
- package/src/modes/irc-observation-ledger.ts +127 -0
- package/src/modes/print-mode.ts +228 -92
- package/src/modes/rpc/rpc-client.ts +58 -1
- package/src/modes/rpc/rpc-mode.ts +286 -85
- package/src/modes/rpc/rpc-types.ts +15 -1
- package/src/modes/shared/agent-wire/command-dispatch.ts +33 -1
- package/src/modes/shared/agent-wire/command-validation.ts +10 -0
- package/src/modes/shared/agent-wire/deep-interview-gate.ts +45 -2
- package/src/modes/shared/agent-wire/scopes.ts +1 -0
- package/src/modes/shared/agent-wire/unattended-session.ts +288 -1
- package/src/modes/shared/agent-wire/workflow-gate-broker.ts +130 -20
- package/src/modes/types.ts +34 -2
- package/src/modes/utils/context-usage.ts +56 -92
- package/src/modes/utils/hotkeys-markdown.ts +1 -0
- package/src/modes/utils/irc-message.ts +138 -0
- package/src/modes/utils/ui-helpers.ts +216 -50
- package/src/notifications/config.ts +44 -0
- package/src/notifications/daemon-paths.ts +8 -0
- package/src/notifications/index.ts +535 -75
- package/src/notifications/lifecycle-control-runtime.ts +930 -77
- package/src/notifications/notification-service.ts +710 -0
- package/src/notifications/rate-limit-pool.ts +46 -12
- package/src/notifications/telegram-daemon-control.ts +27 -2
- package/src/notifications/telegram-daemon.ts +635 -94
- package/src/notifications/telegram-reference.ts +73 -13
- package/src/notifications/topic-registry.ts +86 -9
- package/src/prompts/system/irc-peer-roster.md +1 -0
- package/src/prompts/system/subagent-system-prompt.md +1 -0
- package/src/prompts/system/system-prompt.md +1 -0
- package/src/prompts/tools/irc.md +1 -1
- package/src/registry/agent-registry.ts +3 -0
- package/src/sdk.ts +52 -1
- package/src/secrets/obfuscator.ts +4 -2
- package/src/session/agent-session.ts +670 -217
- package/src/session/cache-economics.ts +275 -0
- package/src/session/context-estimation.ts +91 -0
- package/src/session/provider-safety-stop.ts +9 -0
- package/src/session/session-dump-format.ts +6 -0
- package/src/session/session-manager.ts +809 -39
- package/src/session/session-storage.ts +582 -38
- package/src/skc-runtime/session-state-sidecar.ts +756 -135
- package/src/skc-runtime/tmux-common.ts +26 -7
- package/src/skc-runtime/tmux-gc.ts +52 -9
- package/src/skc-runtime/tmux-owner-isolation-cli.ts +185 -0
- package/src/skc-runtime/tmux-owner-isolation.ts +2060 -0
- package/src/skc-runtime/tmux-sessions.ts +871 -68
- package/src/slash-commands/builtin-registry.ts +153 -9
- package/src/slash-commands/helpers/context-report.ts +26 -4
- package/src/task/executor.ts +12 -3
- package/src/task/index.ts +8 -0
- package/src/task/receipt.ts +7 -2
- package/src/task/types.ts +26 -0
- package/src/tools/ask-answer-registry.ts +25 -0
- package/src/tools/browser/tab-supervisor.ts +63 -6
- package/src/tools/browser.ts +8 -3
- package/src/tools/index.ts +81 -6
- package/src/tools/sqlite-reader.ts +8 -4
- package/src/tui/output-block.ts +18 -6
- package/src/utils/external-editor.ts +6 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { AgentMessage } from "@sayknow-cli/agent-core";
|
|
2
2
|
import type { AssistantMessage, ImageContent, Message } from "@sayknow-cli/ai";
|
|
3
|
-
import { type Component, Spacer, Text, TruncatedText } from "@sayknow-cli/tui";
|
|
3
|
+
import { type Component, Spacer, Text, TruncatedText, type TUI, truncateToWidth } from "@sayknow-cli/tui";
|
|
4
4
|
import { settings } from "../../config/settings";
|
|
5
5
|
import { resolveSubskillActivationForSkillInvocation } from "../../extensibility/skc-plugins";
|
|
6
6
|
import { buildSkillPromptMessage, parseSkillInvocations } from "../../extensibility/skills";
|
|
@@ -21,17 +21,69 @@ import { SkillMessageComponent } from "../../modes/components/skill-message";
|
|
|
21
21
|
import { ToolExecutionComponent } from "../../modes/components/tool-execution";
|
|
22
22
|
import { UserMessageComponent } from "../../modes/components/user-message";
|
|
23
23
|
import { theme } from "../../modes/theme/theme";
|
|
24
|
-
import type {
|
|
24
|
+
import type {
|
|
25
|
+
CompactionQueuedMessage,
|
|
26
|
+
InteractiveModeContext,
|
|
27
|
+
IrcArrivalSnapshot,
|
|
28
|
+
TranscriptRebuildPolicy,
|
|
29
|
+
} from "../../modes/types";
|
|
25
30
|
import {
|
|
26
31
|
type CustomMessage,
|
|
27
32
|
isSilentAbort,
|
|
28
33
|
SKILL_PROMPT_MESSAGE_TYPE,
|
|
29
34
|
type SkillPromptDetails,
|
|
30
35
|
} from "../../session/messages";
|
|
31
|
-
import
|
|
36
|
+
import {
|
|
37
|
+
associateSessionMessageViewportAnchorId,
|
|
38
|
+
getSessionMessageEntryId,
|
|
39
|
+
getSessionMessageViewportAnchorId,
|
|
40
|
+
type SessionContext,
|
|
41
|
+
} from "../../session/session-manager";
|
|
32
42
|
import { formatBytes, formatDuration } from "../../tools/render-utils";
|
|
33
43
|
import { buildAbortDisplayMessage } from "./abort-message";
|
|
44
|
+
import {
|
|
45
|
+
formatIrcMessageBlock,
|
|
46
|
+
isIrcCustomType,
|
|
47
|
+
type ParsedIrcMessage,
|
|
48
|
+
parseIrcMessage,
|
|
49
|
+
projectIrcText,
|
|
50
|
+
} from "./irc-message";
|
|
51
|
+
|
|
52
|
+
export type { TranscriptRebuildPolicy } from "../../modes/types";
|
|
53
|
+
|
|
54
|
+
const IRC_INLINE_MAX_RENDER_ROWS = 2_048;
|
|
55
|
+
const IRC_INLINE_MAX_SOURCE_UTF8_BYTES = 64 * 1_024;
|
|
56
|
+
const IRC_INLINE_ELISION = " … message elided …";
|
|
57
|
+
|
|
58
|
+
class BoundedIrcTextComponent implements Component {
|
|
59
|
+
#text: Text;
|
|
60
|
+
#sourceTruncated: boolean;
|
|
61
|
+
|
|
62
|
+
constructor(text: string, sourceTruncated: boolean) {
|
|
63
|
+
this.#text = new Text(text, 0, 0);
|
|
64
|
+
this.#sourceTruncated = sourceTruncated;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
render(width: number): string[] {
|
|
68
|
+
const rendered = this.#text.render(width);
|
|
69
|
+
if (!this.#sourceTruncated && rendered.length <= IRC_INLINE_MAX_RENDER_ROWS) return rendered;
|
|
70
|
+
const lines = rendered.slice(0, IRC_INLINE_MAX_RENDER_ROWS);
|
|
71
|
+
const marker = truncateToWidth(theme.fg("dim", IRC_INLINE_ELISION), width);
|
|
72
|
+
if (lines.length === 0) return [marker];
|
|
73
|
+
if (this.#sourceTruncated && rendered.length < IRC_INLINE_MAX_RENDER_ROWS) lines.push(marker);
|
|
74
|
+
else lines[lines.length - 1] = marker;
|
|
75
|
+
return lines;
|
|
76
|
+
}
|
|
34
77
|
|
|
78
|
+
invalidate(): void {
|
|
79
|
+
this.#text.invalidate();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function prepareTranscriptRebuild(ui: TUI, policy: TranscriptRebuildPolicy): void {
|
|
84
|
+
if (policy === "replace-identity") ui.resetViewportAnchorIntent();
|
|
85
|
+
else ui.prepareViewportAnchorForTranscriptRebuild();
|
|
86
|
+
}
|
|
35
87
|
type TextBlock = { type: "text"; text: string };
|
|
36
88
|
interface RenderInitialMessagesOptions {
|
|
37
89
|
preserveExistingChat?: boolean;
|
|
@@ -119,8 +171,18 @@ function getChatChildTime(component: Component): number {
|
|
|
119
171
|
return chatChildAddedAt.get(component) ?? Date.now();
|
|
120
172
|
}
|
|
121
173
|
|
|
174
|
+
function stableSemanticIdPart(value: string): string {
|
|
175
|
+
let hash = 0xcbf29ce484222325n;
|
|
176
|
+
for (const char of value) {
|
|
177
|
+
hash ^= BigInt(char.codePointAt(0)!);
|
|
178
|
+
hash = BigInt.asUintN(64, hash * 0x100000001b3n);
|
|
179
|
+
}
|
|
180
|
+
return hash.toString(36);
|
|
181
|
+
}
|
|
182
|
+
|
|
122
183
|
export function addChatChild(ctx: InteractiveModeContext, component: Component): void {
|
|
123
184
|
ctx.chatContainer.addChild(component);
|
|
185
|
+
|
|
124
186
|
chatChildAddedAt.set(component, Date.now());
|
|
125
187
|
trimChatChildren(ctx);
|
|
126
188
|
}
|
|
@@ -187,8 +249,114 @@ export function trimChatChildren(ctx: InteractiveModeContext): void {
|
|
|
187
249
|
}
|
|
188
250
|
|
|
189
251
|
export class UiHelpers {
|
|
252
|
+
#renderedIrcInlineComponents = new Map<string, readonly Component[]>();
|
|
253
|
+
#viewportAnchorOccurrences = new WeakMap<object, { base: string; epoch: number; id: string }>();
|
|
254
|
+
#nextViewportAnchorOccurrence = new Map<string, number>();
|
|
255
|
+
#viewportAnchorOccurrenceEpoch = 0;
|
|
256
|
+
#ircSidebarHintShown = false;
|
|
257
|
+
|
|
190
258
|
constructor(private ctx: InteractiveModeContext) {}
|
|
191
259
|
|
|
260
|
+
#resetViewportAnchorOccurrencePass(): void {
|
|
261
|
+
this.#viewportAnchorOccurrenceEpoch += 1;
|
|
262
|
+
this.#nextViewportAnchorOccurrence.clear();
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
#viewportAnchorOccurrenceId(message: object, base: string): string {
|
|
266
|
+
const existing = this.#viewportAnchorOccurrences.get(message);
|
|
267
|
+
if (existing?.base === base && existing.epoch === this.#viewportAnchorOccurrenceEpoch) return existing.id;
|
|
268
|
+
const occurrence = this.#nextViewportAnchorOccurrence.get(base) ?? 0;
|
|
269
|
+
this.#nextViewportAnchorOccurrence.set(base, occurrence + 1);
|
|
270
|
+
const id = `${base}:occurrence:${occurrence}`;
|
|
271
|
+
this.#viewportAnchorOccurrences.set(message, {
|
|
272
|
+
base,
|
|
273
|
+
epoch: this.#viewportAnchorOccurrenceEpoch,
|
|
274
|
+
id,
|
|
275
|
+
});
|
|
276
|
+
return id;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
assistantViewportAnchorId(message: AssistantMessage): string {
|
|
280
|
+
const semanticId = getSessionMessageViewportAnchorId(message);
|
|
281
|
+
if (semanticId) return semanticId;
|
|
282
|
+
const entryId = getSessionMessageEntryId(message);
|
|
283
|
+
if (entryId) return `assistant:entry:${entryId}`;
|
|
284
|
+
const base = `assistant:${message.api}:${message.provider}:${message.model}:${message.timestamp}`;
|
|
285
|
+
const id = this.#viewportAnchorOccurrenceId(message, base);
|
|
286
|
+
associateSessionMessageViewportAnchorId(message, id);
|
|
287
|
+
return id;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
#userViewportAnchorId(message: Extract<Message, { role: "user" }>): string {
|
|
291
|
+
const semanticId = getSessionMessageViewportAnchorId(message);
|
|
292
|
+
if (semanticId) return semanticId;
|
|
293
|
+
const entryId = getSessionMessageEntryId(message);
|
|
294
|
+
if (entryId) return `user:entry:${entryId}`;
|
|
295
|
+
const base = `user:local:${message.timestamp}:${stableSemanticIdPart(JSON.stringify(message.content))}`;
|
|
296
|
+
const id = this.#viewportAnchorOccurrenceId(message, base);
|
|
297
|
+
associateSessionMessageViewportAnchorId(message, id);
|
|
298
|
+
return id;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
getRenderedIrcInlineComponents(): Map<string, readonly Component[]> {
|
|
302
|
+
return this.#renderedIrcInlineComponents;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
removeRenderedIrcInlineComponents(observationId: string): readonly Component[] | undefined {
|
|
306
|
+
const components = this.#renderedIrcInlineComponents.get(observationId);
|
|
307
|
+
this.#renderedIrcInlineComponents.delete(observationId);
|
|
308
|
+
return components;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
resetRenderedIrcInlineComponents(): readonly (readonly Component[])[] {
|
|
312
|
+
const components = [...this.#renderedIrcInlineComponents.values()];
|
|
313
|
+
this.#renderedIrcInlineComponents.clear();
|
|
314
|
+
return components;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
#addIrcObservationToChat(message: ParsedIrcMessage, sidebarHint?: string): Component[] {
|
|
318
|
+
const bodyProjection = projectIrcText(message.text, IRC_INLINE_MAX_SOURCE_UTF8_BYTES);
|
|
319
|
+
const block = formatIrcMessageBlock({ ...message, text: bodyProjection.text });
|
|
320
|
+
const components: Component[] = [];
|
|
321
|
+
const header = `${theme.fg("accent", `[IRC] ${block.sender} → ${block.recipient} · ${block.time}`)}${sidebarHint ? theme.fg("dim", sidebarHint) : ""}`;
|
|
322
|
+
const headerComponent = new Text(header, 1, 0);
|
|
323
|
+
addChatChild(this.ctx, headerComponent);
|
|
324
|
+
components.push(headerComponent);
|
|
325
|
+
if (block.bodyLines.length > 0 || bodyProjection.truncated) {
|
|
326
|
+
const bodyComponent = new BoundedIrcTextComponent(
|
|
327
|
+
theme.fg("muted", ` ${block.bodyLines.join("\n ")}`),
|
|
328
|
+
bodyProjection.truncated,
|
|
329
|
+
);
|
|
330
|
+
addChatChild(this.ctx, bodyComponent);
|
|
331
|
+
components.push(bodyComponent);
|
|
332
|
+
}
|
|
333
|
+
return components;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
addLiveIrcObservationToChat(message: ParsedIrcMessage, arrival: IrcArrivalSnapshot): Component[] {
|
|
337
|
+
// Requested-open panels that merely yielded at narrow widths must not
|
|
338
|
+
// advertise the toggle key: pressing it would close the pending request.
|
|
339
|
+
const showSidebarHint =
|
|
340
|
+
!arrival.panelVisible &&
|
|
341
|
+
!arrival.panelRequestedVisible &&
|
|
342
|
+
arrival.sidebarAvailable &&
|
|
343
|
+
Boolean(arrival.resolvedToggleKey) &&
|
|
344
|
+
!this.#ircSidebarHintShown;
|
|
345
|
+
if (showSidebarHint) this.#ircSidebarHintShown = true;
|
|
346
|
+
return this.#addIrcObservationToChat(
|
|
347
|
+
message,
|
|
348
|
+
showSidebarHint ? ` · ${arrival.resolvedToggleKey} opens sidebar` : undefined,
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
addRebuiltIrcObservationToChat(message: ParsedIrcMessage): Component[] {
|
|
353
|
+
return this.#addIrcObservationToChat(message);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
resetIrcSidebarHint(): void {
|
|
357
|
+
this.#ircSidebarHintShown = false;
|
|
358
|
+
}
|
|
359
|
+
|
|
192
360
|
/** Extract text content from a user message */
|
|
193
361
|
getUserMessageText(message: Message): string {
|
|
194
362
|
if (message.role !== "user") return "";
|
|
@@ -306,50 +474,9 @@ export class UiHelpers {
|
|
|
306
474
|
addChatChild(this.ctx, component);
|
|
307
475
|
break;
|
|
308
476
|
}
|
|
309
|
-
if (
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
message.customType === "irc:relay"
|
|
313
|
-
) {
|
|
314
|
-
const details = (
|
|
315
|
-
message as CustomMessage<{
|
|
316
|
-
from?: string;
|
|
317
|
-
to?: string;
|
|
318
|
-
message?: string;
|
|
319
|
-
reply?: string;
|
|
320
|
-
body?: string;
|
|
321
|
-
kind?: "message" | "reply";
|
|
322
|
-
}>
|
|
323
|
-
).details;
|
|
324
|
-
let arrow: string;
|
|
325
|
-
let body: string;
|
|
326
|
-
if (message.customType === "irc:incoming") {
|
|
327
|
-
const peer = details?.from ?? "?";
|
|
328
|
-
body = details?.message ?? "";
|
|
329
|
-
arrow = `⇦ ${peer}`;
|
|
330
|
-
} else if (message.customType === "irc:autoreply") {
|
|
331
|
-
const peer = details?.to ?? "?";
|
|
332
|
-
body = details?.reply ?? "";
|
|
333
|
-
arrow = `⇨ ${peer}`;
|
|
334
|
-
} else {
|
|
335
|
-
const from = details?.from ?? "?";
|
|
336
|
-
const to = details?.to ?? "?";
|
|
337
|
-
body = details?.body ?? "";
|
|
338
|
-
arrow = `${from} ⇨ ${to}`;
|
|
339
|
-
}
|
|
340
|
-
const components: Component[] = [];
|
|
341
|
-
const header = `${theme.fg("accent", `[IRC] ${arrow}`)}`;
|
|
342
|
-
const headerComponent = new Text(header, 1, 0);
|
|
343
|
-
addChatChild(this.ctx, headerComponent);
|
|
344
|
-
components.push(headerComponent);
|
|
345
|
-
if (body) {
|
|
346
|
-
for (const line of body.split("\n")) {
|
|
347
|
-
const lineComponent = new Text(theme.fg("muted", ` ${line}`), 0, 0);
|
|
348
|
-
addChatChild(this.ctx, lineComponent);
|
|
349
|
-
components.push(lineComponent);
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
return components;
|
|
477
|
+
if (message.role === "custom" && isIrcCustomType(message.customType)) {
|
|
478
|
+
const parsed = parseIrcMessage(message);
|
|
479
|
+
if (parsed) return this.addRebuiltIrcObservationToChat(parsed);
|
|
353
480
|
}
|
|
354
481
|
if (message.customType === "subagent:steer" || message.customType === "subagent:steer:relay") {
|
|
355
482
|
const details = (
|
|
@@ -423,7 +550,11 @@ export class UiHelpers {
|
|
|
423
550
|
const textContent = this.ctx.getUserMessageText(message);
|
|
424
551
|
if (textContent) {
|
|
425
552
|
const isSynthetic = message.role === "developer" ? true : (message.synthetic ?? false);
|
|
426
|
-
const userComponent = new UserMessageComponent(
|
|
553
|
+
const userComponent = new UserMessageComponent(
|
|
554
|
+
textContent,
|
|
555
|
+
isSynthetic,
|
|
556
|
+
message.role === "user" && !isSynthetic ? this.#userViewportAnchorId(message) : undefined,
|
|
557
|
+
);
|
|
427
558
|
addChatChild(this.ctx, userComponent);
|
|
428
559
|
if (options?.populateHistory && message.role === "user" && !isSynthetic) {
|
|
429
560
|
this.ctx.editor.addToHistory(textContent);
|
|
@@ -432,8 +563,11 @@ export class UiHelpers {
|
|
|
432
563
|
break;
|
|
433
564
|
}
|
|
434
565
|
case "assistant": {
|
|
435
|
-
const assistantComponent = new AssistantMessageComponent(
|
|
436
|
-
|
|
566
|
+
const assistantComponent = new AssistantMessageComponent(
|
|
567
|
+
message,
|
|
568
|
+
this.ctx.hideThinkingBlock,
|
|
569
|
+
() => this.ctx.ui.requestRender(),
|
|
570
|
+
this.assistantViewportAnchorId(message),
|
|
437
571
|
);
|
|
438
572
|
addChatChild(this.ctx, assistantComponent);
|
|
439
573
|
break;
|
|
@@ -471,12 +605,35 @@ export class UiHelpers {
|
|
|
471
605
|
const readToolCallArgs = new Map<string, Record<string, unknown>>();
|
|
472
606
|
const readToolCallAssistantComponents = new Map<string, AssistantMessageComponent>();
|
|
473
607
|
const deferredMessages: AgentMessage[] = [];
|
|
608
|
+
const persistedIrcObservationIds = new Set<string>();
|
|
609
|
+
const now = Date.now();
|
|
610
|
+
this.#resetViewportAnchorOccurrencePass();
|
|
611
|
+
this.#renderedIrcInlineComponents.clear();
|
|
474
612
|
for (const message of sessionContext.messages) {
|
|
475
613
|
// Defer compaction summaries so they render at the bottom (visible after scroll)
|
|
476
614
|
if (message.role === "compactionSummary") {
|
|
477
615
|
deferredMessages.push(message);
|
|
478
616
|
continue;
|
|
479
617
|
}
|
|
618
|
+
if (message.role === "custom" && isIrcCustomType(message.customType)) {
|
|
619
|
+
const parsed = parseIrcMessage(message);
|
|
620
|
+
if (parsed) {
|
|
621
|
+
persistedIrcObservationIds.add(parsed.observationId);
|
|
622
|
+
const record = this.ctx.ircLedger.getRecord(parsed.observationId);
|
|
623
|
+
if (
|
|
624
|
+
record &&
|
|
625
|
+
(record.mode === "persistent" || now < record.expiresAt!) &&
|
|
626
|
+
!this.#renderedIrcInlineComponents.has(record.observationId)
|
|
627
|
+
) {
|
|
628
|
+
this.#renderedIrcInlineComponents.set(
|
|
629
|
+
record.observationId,
|
|
630
|
+
this.addRebuiltIrcObservationToChat(record),
|
|
631
|
+
);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
continue;
|
|
635
|
+
}
|
|
636
|
+
|
|
480
637
|
// Assistant messages need special handling for tool calls
|
|
481
638
|
if (message.role === "assistant") {
|
|
482
639
|
this.ctx.addMessageToChat(message);
|
|
@@ -627,6 +784,15 @@ export class UiHelpers {
|
|
|
627
784
|
for (const message of deferredMessages) {
|
|
628
785
|
this.ctx.addMessageToChat(message, options);
|
|
629
786
|
}
|
|
787
|
+
for (const record of this.ctx.ircLedger.getInlineProjection(now)) {
|
|
788
|
+
if (
|
|
789
|
+
persistedIrcObservationIds.has(record.observationId) ||
|
|
790
|
+
this.#renderedIrcInlineComponents.has(record.observationId)
|
|
791
|
+
) {
|
|
792
|
+
continue;
|
|
793
|
+
}
|
|
794
|
+
this.#renderedIrcInlineComponents.set(record.observationId, this.addRebuiltIrcObservationToChat(record));
|
|
795
|
+
}
|
|
630
796
|
|
|
631
797
|
this.ctx.pendingTools.clear();
|
|
632
798
|
this.ctx.ui.requestRender();
|
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
import * as crypto from "node:crypto";
|
|
2
2
|
import type { Settings } from "../config/settings";
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Env marker set by SKC's own programmatic separate-process child spawn sites
|
|
6
|
+
* (team workers, harness RPC owners) and carrying the spawning session id.
|
|
7
|
+
*
|
|
8
|
+
* Presence — not the value — marks a session as SKC-spawned. It is consumed
|
|
9
|
+
* (read once, then deleted from the child's own env) at startup so it is
|
|
10
|
+
* per-spawn rather than dynastic: a grandchild is marked only if its own spawn
|
|
11
|
+
* site marks it, never by inheriting a marked ancestor's environment.
|
|
12
|
+
*/
|
|
13
|
+
export const SPAWN_PROVENANCE_ENV = "SKC_SPAWNED_BY_SESSION";
|
|
14
|
+
|
|
4
15
|
export interface NotificationConfig {
|
|
5
16
|
enabled: boolean;
|
|
6
17
|
botToken?: string;
|
|
@@ -15,6 +26,12 @@ export interface NotificationConfig {
|
|
|
15
26
|
};
|
|
16
27
|
redact: boolean;
|
|
17
28
|
verbosity: "lean" | "verbose";
|
|
29
|
+
/**
|
|
30
|
+
* Which sessions may register a notification endpoint. `all` (default)
|
|
31
|
+
* preserves historical behavior; `primary` suppresses SKC-spawned children
|
|
32
|
+
* (those carrying {@link SPAWN_PROVENANCE_ENV}) unless they explicitly opt in.
|
|
33
|
+
*/
|
|
34
|
+
sessionScope: "all" | "primary";
|
|
18
35
|
idleTimeoutMs: number;
|
|
19
36
|
rich: {
|
|
20
37
|
enabled: boolean;
|
|
@@ -22,6 +39,15 @@ export interface NotificationConfig {
|
|
|
22
39
|
richDraft: {
|
|
23
40
|
enabled: boolean;
|
|
24
41
|
};
|
|
42
|
+
topics: {
|
|
43
|
+
/**
|
|
44
|
+
* Optional Telegram forum-topic name template with `{repo}`, `{branch}`,
|
|
45
|
+
* and `{title}` placeholders. Unset preserves the built-in
|
|
46
|
+
* `{repo}/{branch} - {title}` composition (with its title/repo/branch
|
|
47
|
+
* fallbacks).
|
|
48
|
+
*/
|
|
49
|
+
nameTemplate?: string;
|
|
50
|
+
};
|
|
25
51
|
}
|
|
26
52
|
|
|
27
53
|
/** Read typed config from Settings. */
|
|
@@ -40,6 +66,7 @@ export function getNotificationConfig(settings: Settings): NotificationConfig {
|
|
|
40
66
|
},
|
|
41
67
|
redact: settings.get("notifications.redact"),
|
|
42
68
|
verbosity: settings.get("notifications.verbosity") === "verbose" ? "verbose" : "lean",
|
|
69
|
+
sessionScope: settings.get("notifications.sessionScope") === "primary" ? "primary" : "all",
|
|
43
70
|
idleTimeoutMs: settings.get("notifications.daemon.idleTimeoutMs"),
|
|
44
71
|
rich: {
|
|
45
72
|
enabled: settings.get("notifications.telegram.rich.enabled"),
|
|
@@ -47,6 +74,9 @@ export function getNotificationConfig(settings: Settings): NotificationConfig {
|
|
|
47
74
|
richDraft: {
|
|
48
75
|
enabled: settings.get("notifications.telegram.richDraft.enabled"),
|
|
49
76
|
},
|
|
77
|
+
topics: {
|
|
78
|
+
nameTemplate: settings.get("notifications.telegram.topics.nameTemplate"),
|
|
79
|
+
},
|
|
50
80
|
};
|
|
51
81
|
}
|
|
52
82
|
|
|
@@ -95,11 +125,25 @@ export function shouldRegisterNotificationsExtension(input: {
|
|
|
95
125
|
parentTaskPrefix?: string;
|
|
96
126
|
/** Role-agent type/name; present for task sessions even if depth metadata is lost. */
|
|
97
127
|
currentAgentType?: string;
|
|
128
|
+
/**
|
|
129
|
+
* True when this session was launched by one of SKC's own programmatic
|
|
130
|
+
* separate-process child spawn sites (marked via {@link SPAWN_PROVENANCE_ENV}).
|
|
131
|
+
* Under `notifications.sessionScope = "primary"` such children are suppressed
|
|
132
|
+
* unless they explicitly opt in, so an interactive parent that fans out work
|
|
133
|
+
* does not flood the paired chat with topics for children the user never
|
|
134
|
+
* asked for. User-opened sessions (CLI/tmux/headless) never carry the marker.
|
|
135
|
+
*/
|
|
136
|
+
spawnedBySkc?: boolean;
|
|
98
137
|
}): boolean {
|
|
99
138
|
if ((input.taskDepth ?? 0) > 0 || input.parentTaskPrefix || input.currentAgentType) return false;
|
|
100
139
|
if (completionNotifyDisabledByEnv(input.env)) return false;
|
|
101
140
|
if (input.env.SKC_NOTIFICATIONS === "0") return false;
|
|
102
141
|
if (input.env.SKC_NOTIFICATIONS === "1" || input.env.SKC_NOTIFICATIONS_TOKEN) return true;
|
|
142
|
+
// Spawned-child suppression sits below explicit opt-in (so Telegram
|
|
143
|
+
// `/session_create` and cold `/session_resume`, which launch with
|
|
144
|
+
// SKC_NOTIFICATIONS=1, keep their fully bidirectional topic) and above global
|
|
145
|
+
// auto-on (so their children stay silent under `primary`).
|
|
146
|
+
if (input.spawnedBySkc && input.cfg?.sessionScope === "primary") return false;
|
|
103
147
|
return input.cfg ? isGloballyConfigured(input.cfg) : false;
|
|
104
148
|
}
|
|
105
149
|
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Owner heartbeat freshness window. A daemon ownership record older than this
|
|
5
|
+
* (without a live pid) is considered stale. Lives here, in the lightweight
|
|
6
|
+
* paths module, so secret-safe consumers (e.g. the notification service) can
|
|
7
|
+
* reuse it without importing the heavy daemon runtime.
|
|
8
|
+
*/
|
|
9
|
+
export const HEARTBEAT_TTL_MS = 20_000;
|
|
10
|
+
|
|
3
11
|
export interface DaemonPaths {
|
|
4
12
|
dir: string;
|
|
5
13
|
lock: string;
|