@sayknow-cli/coding-agent 0.3.5 → 0.3.7
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/README.md +76 -0
- package/dist/types/capability/mcp.d.ts +2 -0
- package/dist/types/cli/local-provider-smoke.d.ts +59 -0
- package/dist/types/cli/notify-cli.d.ts +1 -1
- package/dist/types/cli/telegram-cli.d.ts +13 -0
- package/dist/types/cli/update-cli.d.ts +24 -1
- package/dist/types/commands/local-provider.d.ts +34 -0
- package/dist/types/commands/telegram.d.ts +22 -0
- package/dist/types/config/keybindings.d.ts +12 -6
- package/dist/types/config/model-registry.d.ts +5 -0
- package/dist/types/config/models-config-schema.d.ts +5 -0
- package/dist/types/config/settings-schema.d.ts +284 -4
- package/dist/types/config/settings.d.ts +7 -0
- package/dist/types/config/telegram-autostart.d.ts +14 -0
- package/dist/types/config/telegram-env-bridge.d.ts +18 -0
- package/dist/types/coordinator-mcp/server.d.ts +8 -0
- package/dist/types/defaults/skc-defaults.d.ts +7 -0
- package/dist/types/discovery/cursor.d.ts +4 -1
- package/dist/types/discovery/index.d.ts +0 -1
- package/dist/types/discovery/windsurf.d.ts +3 -1
- package/dist/types/extensibility/extensions/types.d.ts +11 -0
- package/dist/types/i18n/messages/en.d.ts +1 -0
- package/dist/types/modes/components/custom-editor.d.ts +6 -1
- package/dist/types/modes/components/custom-model-preset-wizard.d.ts +1 -1
- package/dist/types/modes/components/hook-selector.d.ts +5 -0
- package/dist/types/modes/components/model-selector.d.ts +2 -0
- package/dist/types/modes/components/settings-selector.d.ts +5 -1
- package/dist/types/modes/components/skill-message.d.ts +1 -0
- package/dist/types/modes/components/status-line/types.d.ts +5 -7
- package/dist/types/modes/components/status-line.d.ts +15 -3
- package/dist/types/modes/components/two-column-body.d.ts +9 -0
- package/dist/types/modes/components/welcome.d.ts +9 -1
- package/dist/types/modes/controllers/command-controller.d.ts +1 -0
- package/dist/types/modes/controllers/input-controller.d.ts +6 -1
- package/dist/types/modes/controllers/selector-controller.d.ts +10 -1
- package/dist/types/modes/interactive-mode.d.ts +2 -0
- package/dist/types/modes/prompt-action-autocomplete.d.ts +3 -0
- package/dist/types/modes/rpc/rpc-types.d.ts +1 -0
- package/dist/types/modes/shared/agent-wire/deep-interview-gate.d.ts +41 -7
- package/dist/types/modes/slash-command-visibility.d.ts +1 -0
- package/dist/types/modes/theme/theme.d.ts +1 -1
- package/dist/types/modes/tmux-scroll.d.ts +18 -0
- package/dist/types/modes/types.d.ts +5 -1
- package/dist/types/modes/utils/terminal-bell.d.ts +3 -0
- package/dist/types/notifications/daemon-paths.d.ts +9 -0
- package/dist/types/notifications/html-format.d.ts +2 -0
- package/dist/types/notifications/recent-activity.d.ts +5 -1
- package/dist/types/notifications/telegram-daemon-cli.d.ts +21 -4
- package/dist/types/notifications/telegram-daemon-control.d.ts +8 -0
- package/dist/types/notifications/telegram-daemon.d.ts +4 -10
- package/dist/types/notifications/telegram-reference.d.ts +4 -0
- package/dist/types/notifications/threaded-render.d.ts +1 -0
- package/dist/types/runtime-mcp/config-writer.d.ts +9 -0
- package/dist/types/runtime-mcp/config.d.ts +2 -0
- package/dist/types/runtime-mcp/loader.d.ts +2 -0
- package/dist/types/runtime-mcp/manager.d.ts +2 -0
- package/dist/types/runtime-mcp/types.d.ts +6 -0
- package/dist/types/sdk.d.ts +1 -1
- package/dist/types/skc-runtime/launch-tmux.d.ts +2 -2
- package/dist/types/skc-runtime/ralplan-runtime.d.ts +3 -2
- package/dist/types/skc-runtime/restricted-role-agent-bash.d.ts +1 -0
- package/dist/types/skc-runtime/session-state-sidecar.d.ts +5 -0
- package/dist/types/skc-runtime/state-writer.d.ts +1 -0
- package/dist/types/skc-runtime/team-runtime.d.ts +1 -1
- package/dist/types/skc-runtime/tmux-common.d.ts +12 -0
- package/dist/types/skc-runtime/ultragoal-guard.d.ts +7 -1
- package/dist/types/skc-runtime/ultragoal-runtime.d.ts +0 -4
- package/dist/types/slash-commands/types.d.ts +2 -0
- package/dist/types/system-prompt.d.ts +7 -0
- package/dist/types/tools/skill.d.ts +1 -1
- package/dist/types/utils/cmux-workspace.d.ts +52 -0
- package/dist/types/web/search/index.d.ts +4 -2
- package/dist/types/web/search/provider.d.ts +14 -0
- package/dist/types/web/search/providers/utils.d.ts +42 -13
- package/dist/types/web/search/render.d.ts +1 -0
- package/dist/types/workflow/workflow-intent-diff.d.ts +19 -0
- package/package.json +8 -8
- package/scripts/build-binary.ts +3 -0
- package/scripts/verify-insane-vendor.ts +0 -5
- package/src/capability/mcp.ts +2 -0
- package/src/cli/local-provider-smoke.ts +632 -0
- package/src/cli/notify-cli.ts +4 -2
- package/src/cli/telegram-cli.ts +156 -0
- package/src/cli/update-cli.ts +179 -13
- package/src/cli/web-search-cli.ts +5 -7
- package/src/cli.ts +20 -1
- package/src/commands/launch.ts +47 -2
- package/src/commands/local-provider.ts +62 -0
- package/src/commands/notify.ts +1 -1
- package/src/commands/ralplan.ts +1 -0
- package/src/commands/telegram.ts +47 -0
- package/src/config/keybindings.ts +15 -6
- package/src/config/mcp-schema.json +4 -0
- package/src/config/model-registry.ts +55 -1
- package/src/config/models-config-schema.ts +9 -0
- package/src/config/settings-schema.ts +256 -4
- package/src/config/settings.ts +11 -0
- package/src/config/telegram-autostart.ts +107 -0
- package/src/config/telegram-env-bridge.ts +92 -0
- package/src/coordinator-mcp/server.ts +276 -34
- package/src/defaults/skc/skills/ralplan/SKILL.md +29 -20
- package/src/defaults/skc/skills/ultragoal/SKILL.md +37 -112
- package/src/defaults/skc-defaults.ts +16 -0
- package/src/discovery/builtin.ts +12 -0
- package/src/discovery/cursor.ts +5 -84
- package/src/discovery/gemini.ts +3 -89
- package/src/discovery/index.ts +4 -1
- package/src/discovery/mcp-json.ts +11 -0
- package/src/discovery/opencode.ts +5 -122
- package/src/discovery/windsurf.ts +4 -82
- package/src/edit/read-file.ts +9 -1
- package/src/extensibility/extensions/runner.ts +2 -1
- package/src/extensibility/extensions/types.ts +11 -0
- package/src/extensibility/skills.ts +30 -2
- package/src/hooks/skill-state.ts +33 -73
- package/src/i18n/messages/de.ts +1 -0
- package/src/i18n/messages/en.ts +1 -0
- package/src/i18n/messages/es.ts +1 -0
- package/src/i18n/messages/fr.ts +1 -0
- package/src/i18n/messages/ja.settings.ts +28 -0
- package/src/i18n/messages/ja.ts +1 -0
- package/src/i18n/messages/ko.settings.ts +28 -0
- package/src/i18n/messages/ko.ts +1 -0
- package/src/i18n/messages/zh.settings.ts +28 -0
- package/src/i18n/messages/zh.ts +1 -0
- package/src/internal-urls/docs-index.generated.ts +12 -7
- package/src/main.ts +5 -2
- package/src/migrate/executor.ts +23 -6
- package/src/modes/acp/acp-agent.ts +7 -6
- package/src/modes/components/agent-dashboard.ts +3 -35
- package/src/modes/components/custom-editor.ts +26 -0
- package/src/modes/components/custom-model-preset-wizard.ts +30 -189
- package/src/modes/components/extensions/extension-dashboard.ts +2 -48
- package/src/modes/components/hook-selector.ts +21 -3
- package/src/modes/components/model-selector.ts +246 -11
- package/src/modes/components/session-observer-overlay.ts +3 -3
- package/src/modes/components/session-selector.ts +19 -9
- package/src/modes/components/settings-selector.ts +551 -11
- package/src/modes/components/skill-message.ts +18 -10
- package/src/modes/components/status-line/presets.ts +11 -0
- package/src/modes/components/status-line/segments.ts +16 -19
- package/src/modes/components/status-line/types.ts +6 -2
- package/src/modes/components/status-line.ts +281 -117
- package/src/modes/components/tree-selector.ts +28 -8
- package/src/modes/components/two-column-body.ts +36 -0
- package/src/modes/components/welcome.ts +165 -30
- package/src/modes/controllers/command-controller.ts +26 -2
- package/src/modes/controllers/event-controller.ts +102 -4
- package/src/modes/controllers/extension-ui-controller.ts +21 -2
- package/src/modes/controllers/input-controller.ts +214 -47
- package/src/modes/controllers/selector-controller.ts +35 -33
- package/src/modes/interactive-mode.ts +36 -30
- package/src/modes/prompt-action-autocomplete.ts +48 -8
- package/src/modes/rpc/rpc-types.ts +1 -0
- package/src/modes/shared/agent-wire/deep-interview-gate.ts +72 -15
- package/src/modes/shared/agent-wire/workflow-gate-schema.ts +20 -0
- package/src/modes/slash-command-visibility.ts +9 -0
- package/src/modes/theme/theme.ts +5 -1
- package/src/modes/tmux-scroll.ts +92 -0
- package/src/modes/types.ts +8 -1
- package/src/modes/utils/hotkeys-markdown.ts +5 -2
- package/src/modes/utils/terminal-bell.ts +48 -0
- package/src/modes/utils/ui-helpers.ts +76 -3
- package/src/notifications/daemon-paths.ts +22 -0
- package/src/notifications/html-format.ts +75 -1
- package/src/notifications/index.ts +32 -2
- package/src/notifications/lifecycle-control-runtime.ts +12 -3
- package/src/notifications/recent-activity.ts +32 -8
- package/src/notifications/telegram-daemon-cli.ts +125 -13
- package/src/notifications/telegram-daemon-control.ts +9 -0
- package/src/notifications/telegram-daemon.ts +88 -46
- package/src/notifications/telegram-reference.ts +36 -14
- package/src/notifications/threaded-render.ts +14 -6
- package/src/prompts/agents/architect.md +7 -4
- package/src/prompts/agents/critic.md +15 -6
- package/src/prompts/agents/planner.md +8 -3
- package/src/prompts/system/system-prompt.md +20 -2
- package/src/prompts/tools/bash.md +1 -1
- package/src/runtime-mcp/config-writer.ts +28 -0
- package/src/runtime-mcp/config.ts +7 -0
- package/src/runtime-mcp/loader.ts +3 -0
- package/src/runtime-mcp/manager.ts +3 -0
- package/src/runtime-mcp/types.ts +6 -0
- package/src/sdk.ts +10 -11
- package/src/session/agent-session.ts +103 -13
- package/src/session/session-manager.ts +58 -18
- package/src/skc-runtime/launch-tmux.ts +34 -8
- package/src/skc-runtime/launch-worktree.ts +16 -2
- package/src/skc-runtime/ralplan-runtime.ts +22 -6
- package/src/skc-runtime/restricted-role-agent-bash.ts +1 -0
- package/src/skc-runtime/session-state-sidecar.ts +167 -24
- package/src/skc-runtime/state-runtime.ts +8 -5
- package/src/skc-runtime/state-writer.ts +5 -5
- package/src/skc-runtime/team-runtime.ts +21 -6
- package/src/skc-runtime/tmux-common.ts +16 -0
- package/src/skc-runtime/tmux-sessions.ts +13 -9
- package/src/skc-runtime/ultragoal-guard.ts +150 -24
- package/src/skc-runtime/ultragoal-runtime.ts +16 -124
- package/src/skc-runtime/workflow-manifest.generated.json +0 -8
- package/src/skc-runtime/workflow-manifest.ts +0 -1
- package/src/slash-commands/builtin-registry.ts +136 -23
- package/src/slash-commands/types.ts +2 -0
- package/src/system-prompt.ts +8 -0
- package/src/task/executor.ts +5 -0
- package/src/tools/ast-grep.ts +10 -7
- package/src/tools/bash.ts +39 -14
- package/src/tools/index.ts +8 -1
- package/src/tools/skill.ts +1 -1
- package/src/utils/cmux-workspace.ts +233 -0
- package/src/utils/title-generator.ts +2 -0
- package/src/web/kagi.ts +1 -1
- package/src/web/parallel.ts +2 -2
- package/src/web/search/index.ts +137 -52
- package/src/web/search/provider.ts +80 -0
- package/src/web/search/providers/anthropic.ts +1 -1
- package/src/web/search/providers/brave.ts +1 -1
- package/src/web/search/providers/codex.ts +1 -1
- package/src/web/search/providers/duckduckgo.ts +1 -1
- package/src/web/search/providers/exa.ts +1 -1
- package/src/web/search/providers/gemini.ts +7 -3
- package/src/web/search/providers/jina.ts +1 -1
- package/src/web/search/providers/kimi.ts +6 -1
- package/src/web/search/providers/openai-compatible.ts +1 -1
- package/src/web/search/providers/parallel.ts +1 -1
- package/src/web/search/providers/perplexity.ts +2 -2
- package/src/web/search/providers/searxng.ts +1 -1
- package/src/web/search/providers/synthetic.ts +1 -1
- package/src/web/search/providers/tavily.ts +1 -1
- package/src/web/search/providers/utils.ts +67 -17
- package/src/web/search/providers/xai.ts +1 -1
- package/src/web/search/providers/zai.ts +1 -1
- package/src/web/search/render.ts +3 -0
- package/src/workflow/workflow-intent-diff.ts +199 -0
- package/vendor/insane-search/MANIFEST.json +1 -3
- package/vendor/insane-search/engine/tests/test_u1.py +0 -28
- package/vendor/insane-search/engine/transport.py +2 -3
- package/vendor/insane-search/engine/validators.py +0 -14
- package/src/defaults/skc/rules/ponytail.md +0 -68
|
@@ -10,7 +10,14 @@ import type { DaemonRuntimeInfo } from "../daemon/control-types";
|
|
|
10
10
|
import { resolveSkcRuntimeSpawnInfo } from "../daemon/runtime";
|
|
11
11
|
import { getNotificationConfig, isGloballyConfigured, tokenFingerprint } from "./config";
|
|
12
12
|
import { parseInThreadConfigCommand } from "./config-commands";
|
|
13
|
-
import {
|
|
13
|
+
import { daemonPaths } from "./daemon-paths";
|
|
14
|
+
import {
|
|
15
|
+
buildCompactChoiceGrid,
|
|
16
|
+
code,
|
|
17
|
+
splitTelegramHtml,
|
|
18
|
+
TELEGRAM_MESSAGE_LIMIT,
|
|
19
|
+
TELEGRAM_PARSE_MODE,
|
|
20
|
+
} from "./html-format";
|
|
14
21
|
import type {
|
|
15
22
|
SessionCloseTarget,
|
|
16
23
|
SessionCreateTarget,
|
|
@@ -62,16 +69,6 @@ export interface DaemonState {
|
|
|
62
69
|
version: 1;
|
|
63
70
|
stoppedAt?: number;
|
|
64
71
|
}
|
|
65
|
-
|
|
66
|
-
export interface DaemonPaths {
|
|
67
|
-
dir: string;
|
|
68
|
-
lock: string;
|
|
69
|
-
state: string;
|
|
70
|
-
roots: string;
|
|
71
|
-
steal: string;
|
|
72
|
-
aliases: string;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
72
|
export interface TelegramDaemonFs {
|
|
76
73
|
mkdir(path: string, opts?: fs.MakeDirectoryOptions): Promise<void>;
|
|
77
74
|
readFile(path: string, encoding: BufferEncoding): Promise<string>;
|
|
@@ -131,6 +128,24 @@ const QUEUED_REACTION = "👀";
|
|
|
131
128
|
const PENDING_TOPIC_FRAME_LIMIT = 20;
|
|
132
129
|
const CONSUMED_REACTION = "✅";
|
|
133
130
|
|
|
131
|
+
function splitTelegramPlainText(text: string, max = TELEGRAM_MESSAGE_LIMIT): string[] {
|
|
132
|
+
if (text.length <= max) return [text];
|
|
133
|
+
const chunks: string[] = [];
|
|
134
|
+
let out = "";
|
|
135
|
+
for (const ch of text) {
|
|
136
|
+
if (out.length + ch.length > max) {
|
|
137
|
+
chunks.push(out);
|
|
138
|
+
out = "";
|
|
139
|
+
}
|
|
140
|
+
out += ch;
|
|
141
|
+
}
|
|
142
|
+
if (out) chunks.push(out);
|
|
143
|
+
return chunks;
|
|
144
|
+
}
|
|
145
|
+
function endpointGenerationKey(url: string, token: string): string {
|
|
146
|
+
return `${url}\0${token}`;
|
|
147
|
+
}
|
|
148
|
+
|
|
134
149
|
/**
|
|
135
150
|
* Whether `err` is a transient network failure worth retrying. Telegram API
|
|
136
151
|
* calls over HTTP/2 occasionally surface mid-stream `ECONNRESET` (and similar)
|
|
@@ -184,17 +199,7 @@ async function fetchWithRetry(
|
|
|
184
199
|
throw lastErr;
|
|
185
200
|
}
|
|
186
201
|
|
|
187
|
-
export
|
|
188
|
-
const dir = path.join(agentDir, "notifications");
|
|
189
|
-
return {
|
|
190
|
-
dir,
|
|
191
|
-
lock: path.join(dir, "telegram-daemon.lock"),
|
|
192
|
-
state: path.join(dir, "telegram-daemon.state.json"),
|
|
193
|
-
roots: path.join(dir, "telegram-daemon.roots.json"),
|
|
194
|
-
steal: path.join(dir, "telegram-daemon.steal"),
|
|
195
|
-
aliases: path.join(dir, "telegram-callback-aliases.json"),
|
|
196
|
-
};
|
|
197
|
-
}
|
|
202
|
+
export { type DaemonPaths, daemonPaths } from "./daemon-paths";
|
|
198
203
|
|
|
199
204
|
/**
|
|
200
205
|
* Attach session-lifecycle control (create/close/resume) to the running daemon.
|
|
@@ -770,6 +775,7 @@ export interface TelegramDaemonOptions {
|
|
|
770
775
|
interface SessionSocket {
|
|
771
776
|
sessionId: string;
|
|
772
777
|
token: string;
|
|
778
|
+
endpointKey: string;
|
|
773
779
|
ws: WebSocket;
|
|
774
780
|
pending: Map<string, { sessionId: string; actionId: string }>;
|
|
775
781
|
/** True once the server advertised the `client_ping_pong` capability. */
|
|
@@ -806,6 +812,8 @@ export class TelegramNotificationDaemon {
|
|
|
806
812
|
private readonly topicOwnerByIdentity = new Map<string, string>();
|
|
807
813
|
/** Non-identity frames held until identity creates the correct thread. */
|
|
808
814
|
private readonly pendingThreadedFrames = new Map<string, PendingThreadedFrame[]>();
|
|
815
|
+
/** Endpoint generation tombstones for sessions that already sent session_closed. */
|
|
816
|
+
private readonly closedEndpointKeys = new Map<string, string>();
|
|
809
817
|
/** True once the daemon has nudged the user to enable Threaded Mode. */
|
|
810
818
|
private threadedFallbackNoticeSent = false;
|
|
811
819
|
/** Sessions whose identity header was already sent flat (Threaded Mode off). */
|
|
@@ -1072,14 +1080,29 @@ export class TelegramNotificationDaemon {
|
|
|
1072
1080
|
threadId: number | undefined,
|
|
1073
1081
|
): Promise<boolean> {
|
|
1074
1082
|
if (!isLifecycleCommandText(text)) return false;
|
|
1075
|
-
const reply = (body: string) =>
|
|
1076
|
-
|
|
1077
|
-
.
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
+
const reply = async (body: string): Promise<void> => {
|
|
1084
|
+
for (const text of splitTelegramPlainText(body)) {
|
|
1085
|
+
await this.botApi
|
|
1086
|
+
.call("sendMessage", {
|
|
1087
|
+
chat_id: this.opts.chatId,
|
|
1088
|
+
...(threadId !== undefined ? { message_thread_id: threadId } : {}),
|
|
1089
|
+
text,
|
|
1090
|
+
})
|
|
1091
|
+
.catch(() => undefined);
|
|
1092
|
+
}
|
|
1093
|
+
};
|
|
1094
|
+
const replyHtml = async (body: string): Promise<void> => {
|
|
1095
|
+
for (const text of splitTelegramHtml(body)) {
|
|
1096
|
+
await this.botApi
|
|
1097
|
+
.call("sendMessage", {
|
|
1098
|
+
chat_id: this.opts.chatId,
|
|
1099
|
+
...(threadId !== undefined ? { message_thread_id: threadId } : {}),
|
|
1100
|
+
text,
|
|
1101
|
+
parse_mode: TELEGRAM_PARSE_MODE,
|
|
1102
|
+
})
|
|
1103
|
+
.catch(() => undefined);
|
|
1104
|
+
}
|
|
1105
|
+
};
|
|
1083
1106
|
|
|
1084
1107
|
if (!this.lifecycleControlActive) {
|
|
1085
1108
|
await reply("Session lifecycle control is not available right now.");
|
|
@@ -1098,11 +1121,12 @@ export class TelegramNotificationDaemon {
|
|
|
1098
1121
|
const recent = listRecentSessions({
|
|
1099
1122
|
sessionsRoot: path.join(this.opts.settings.getAgentDir(), "sessions"),
|
|
1100
1123
|
limit: 10,
|
|
1124
|
+
includeInternal: false,
|
|
1101
1125
|
});
|
|
1102
|
-
const
|
|
1103
|
-
? recent.map(e =>
|
|
1126
|
+
const body = recent.length
|
|
1127
|
+
? recent.map(e => `• ${code(e.sessionId)}${e.path ? ` (${code(e.path)})` : ""}`).join("\n")
|
|
1104
1128
|
: "No recent sessions.";
|
|
1105
|
-
await
|
|
1129
|
+
await replyHtml(body);
|
|
1106
1130
|
return true;
|
|
1107
1131
|
}
|
|
1108
1132
|
|
|
@@ -1205,7 +1229,9 @@ export class TelegramNotificationDaemon {
|
|
|
1205
1229
|
matches: msg => msg.type === "session_closed",
|
|
1206
1230
|
handle: async session => {
|
|
1207
1231
|
this.busy.delete(session.sessionId);
|
|
1232
|
+
this.closedEndpointKeys.set(session.sessionId, session.endpointKey);
|
|
1208
1233
|
await this.deleteTopic(session.sessionId);
|
|
1234
|
+
this.dropSession(session, "session_closed");
|
|
1209
1235
|
},
|
|
1210
1236
|
});
|
|
1211
1237
|
}
|
|
@@ -1242,6 +1268,9 @@ export class TelegramNotificationDaemon {
|
|
|
1242
1268
|
// would chase a dead, token-bearing record forever.
|
|
1243
1269
|
const pidAlive = this.opts.pidAlive ?? defaultPidAlive;
|
|
1244
1270
|
if (endpoint.stale || (endpoint.pid !== undefined && !pidAlive(endpoint.pid))) continue;
|
|
1271
|
+
const endpointKey = endpointGenerationKey(endpoint.url, endpoint.token);
|
|
1272
|
+
if (this.closedEndpointKeys.get(sessionId) === endpointKey) continue;
|
|
1273
|
+
this.closedEndpointKeys.delete(sessionId);
|
|
1245
1274
|
this.connectSession(sessionId, endpoint.url, endpoint.token);
|
|
1246
1275
|
} catch {}
|
|
1247
1276
|
}
|
|
@@ -1251,9 +1280,12 @@ export class TelegramNotificationDaemon {
|
|
|
1251
1280
|
connectSession(sessionId: string, url: string, token: string): void {
|
|
1252
1281
|
const WS = this.opts.WebSocketImpl ?? WebSocket;
|
|
1253
1282
|
const ws = new WS(`${url}/?token=${encodeURIComponent(token)}`);
|
|
1283
|
+
const endpointKey = endpointGenerationKey(url, token);
|
|
1284
|
+
this.closedEndpointKeys.delete(sessionId);
|
|
1254
1285
|
const session: SessionSocket = {
|
|
1255
1286
|
sessionId,
|
|
1256
1287
|
token,
|
|
1288
|
+
endpointKey,
|
|
1257
1289
|
ws,
|
|
1258
1290
|
pending: new Map(),
|
|
1259
1291
|
capable: false,
|
|
@@ -1631,12 +1663,14 @@ export class TelegramNotificationDaemon {
|
|
|
1631
1663
|
parse_mode: TELEGRAM_PARSE_MODE,
|
|
1632
1664
|
});
|
|
1633
1665
|
} else if (send.text) {
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1666
|
+
for (const text of splitTelegramHtml(send.text)) {
|
|
1667
|
+
await this.botApi.call("sendMessage", {
|
|
1668
|
+
chat_id: this.opts.chatId,
|
|
1669
|
+
...threadField,
|
|
1670
|
+
text,
|
|
1671
|
+
parse_mode: TELEGRAM_PARSE_MODE,
|
|
1672
|
+
});
|
|
1673
|
+
}
|
|
1640
1674
|
}
|
|
1641
1675
|
} catch {
|
|
1642
1676
|
// Best-effort: a failed send must never stop the daemon.
|
|
@@ -1839,13 +1873,17 @@ export class TelegramNotificationDaemon {
|
|
|
1839
1873
|
const inline_keyboard = buildCompactChoiceGrid(options, (i: number) =>
|
|
1840
1874
|
this.aliasTable.put({ sessionId: session.sessionId, actionId: msg.id, answer: i }),
|
|
1841
1875
|
);
|
|
1842
|
-
const
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1876
|
+
const chunks = splitTelegramHtml(rendered.text);
|
|
1877
|
+
let result: { result?: { message_id?: number } } = {};
|
|
1878
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
1879
|
+
result = (await this.botApi.call("sendMessage", {
|
|
1880
|
+
chat_id: this.opts.chatId,
|
|
1881
|
+
...threadField,
|
|
1882
|
+
text: chunks[i]!,
|
|
1883
|
+
parse_mode: TELEGRAM_PARSE_MODE,
|
|
1884
|
+
...(i === chunks.length - 1 && inline_keyboard.length ? { reply_markup: { inline_keyboard } } : {}),
|
|
1885
|
+
})) as { result?: { message_id?: number } };
|
|
1886
|
+
}
|
|
1849
1887
|
const messageId = result.result?.message_id;
|
|
1850
1888
|
if (messageId !== undefined)
|
|
1851
1889
|
this.messageRoutes.set(String(messageId), { sessionId: session.sessionId, actionId: msg.id });
|
|
@@ -2005,6 +2043,10 @@ export class TelegramNotificationDaemon {
|
|
|
2005
2043
|
{ command: "verbose", description: "Mirror full tool output + reasoning in this thread" },
|
|
2006
2044
|
{ command: "lean", description: "Mirror assistant text + tool names only (default)" },
|
|
2007
2045
|
{ command: "redact", description: "Toggle redaction of streamed content: /redact <on|off>" },
|
|
2046
|
+
{ command: "session_create", description: "Create a SKC session: path, worktree, or dir" },
|
|
2047
|
+
{ command: "session_recent", description: "List recent SKC sessions" },
|
|
2048
|
+
{ command: "session_close", description: "Close a SKC-managed session" },
|
|
2049
|
+
{ command: "session_resume", description: "Resume or reattach a session" },
|
|
2008
2050
|
],
|
|
2009
2051
|
});
|
|
2010
2052
|
} catch {
|
|
@@ -20,8 +20,8 @@ import {
|
|
|
20
20
|
buildCompactChoiceGrid,
|
|
21
21
|
escapeHtml,
|
|
22
22
|
numberedOptionList,
|
|
23
|
+
splitTelegramHtml,
|
|
23
24
|
TELEGRAM_PARSE_MODE,
|
|
24
|
-
truncateTelegramHtml,
|
|
25
25
|
} from "./html-format";
|
|
26
26
|
import { renderThreadedFrame } from "./threaded-render";
|
|
27
27
|
|
|
@@ -37,6 +37,8 @@ export interface RenderedMessage {
|
|
|
37
37
|
inline_keyboard?: InlineButton[][];
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
type TelegramSend = (method: string, body: unknown) => Promise<Response>;
|
|
41
|
+
|
|
40
42
|
/** Encode `actionId` + option `index` into Telegram callback_data (<=64 bytes). */
|
|
41
43
|
export function encodeCallbackData(actionId: string, index: number): string {
|
|
42
44
|
return `r:${index}:${actionId}`.slice(0, 64);
|
|
@@ -131,14 +133,32 @@ export function buildActionMessage(action: {
|
|
|
131
133
|
}): RenderedMessage {
|
|
132
134
|
if (action.kind === "idle") {
|
|
133
135
|
const text = action.summary ? `🟢 Agent idle\n${escapeHtml(action.summary)}` : "🟢 Agent idle";
|
|
134
|
-
return { text
|
|
136
|
+
return { text };
|
|
135
137
|
}
|
|
136
138
|
const text = `❓ ${bold(action.question ?? "Question")}`;
|
|
137
139
|
const options = action.options ?? [];
|
|
138
|
-
if (options.length === 0) return { text:
|
|
140
|
+
if (options.length === 0) return { text: `${text}\n\n(reply with text)` };
|
|
139
141
|
const body = `${text}\n\n${numberedOptionList(options)}`;
|
|
140
142
|
const inline_keyboard = buildCompactChoiceGrid(options, i => encodeCallbackData(action.id, i));
|
|
141
|
-
return { text:
|
|
143
|
+
return { text: body, inline_keyboard };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** Send Telegram HTML text chunks sequentially so long messages preserve order. */
|
|
147
|
+
export async function sendTelegramHtmlChunks(
|
|
148
|
+
send: TelegramSend,
|
|
149
|
+
chatId: string,
|
|
150
|
+
text: string,
|
|
151
|
+
inlineKeyboard?: InlineButton[][],
|
|
152
|
+
): Promise<void> {
|
|
153
|
+
const chunks = splitTelegramHtml(text);
|
|
154
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
155
|
+
await send("sendMessage", {
|
|
156
|
+
chat_id: chatId,
|
|
157
|
+
text: chunks[i]!,
|
|
158
|
+
parse_mode: TELEGRAM_PARSE_MODE,
|
|
159
|
+
...(i === chunks.length - 1 && inlineKeyboard ? { reply_markup: { inline_keyboard: inlineKeyboard } } : {}),
|
|
160
|
+
});
|
|
161
|
+
}
|
|
142
162
|
}
|
|
143
163
|
|
|
144
164
|
/** A protocol `reply` frame the client should send to the server. */
|
|
@@ -285,15 +305,15 @@ export async function runTelegramReferenceClient(opts: TelegramReferenceOptions)
|
|
|
285
305
|
const ws = new WebSocket(`${url}/?token=${encodeURIComponent(token)}`);
|
|
286
306
|
let latestPendingAskId: string | undefined;
|
|
287
307
|
|
|
288
|
-
const send = (method
|
|
308
|
+
const send: TelegramSend = (method, body) =>
|
|
289
309
|
fetchImpl(`${api}/${method}`, {
|
|
290
310
|
method: "POST",
|
|
291
311
|
headers: { "content-type": "application/json" },
|
|
292
312
|
body: JSON.stringify(body),
|
|
293
313
|
});
|
|
294
314
|
|
|
295
|
-
|
|
296
|
-
const msg = JSON.parse(
|
|
315
|
+
const handleServerMessage = async (data: string): Promise<void> => {
|
|
316
|
+
const msg = JSON.parse(data) as {
|
|
297
317
|
type: string;
|
|
298
318
|
kind?: "ask" | "idle";
|
|
299
319
|
id?: string;
|
|
@@ -311,12 +331,7 @@ export async function runTelegramReferenceClient(opts: TelegramReferenceOptions)
|
|
|
311
331
|
options: msg.options,
|
|
312
332
|
summary: msg.summary,
|
|
313
333
|
});
|
|
314
|
-
|
|
315
|
-
chat_id: opts.chatId,
|
|
316
|
-
text: rendered.text,
|
|
317
|
-
parse_mode: TELEGRAM_PARSE_MODE,
|
|
318
|
-
...(rendered.inline_keyboard ? { reply_markup: { inline_keyboard: rendered.inline_keyboard } } : {}),
|
|
319
|
-
});
|
|
334
|
+
await sendTelegramHtmlChunks(send, opts.chatId, rendered.text, rendered.inline_keyboard);
|
|
320
335
|
} else if (msg.type === "action_resolved" && msg.id === latestPendingAskId) {
|
|
321
336
|
latestPendingAskId = undefined;
|
|
322
337
|
} else {
|
|
@@ -325,9 +340,16 @@ export async function runTelegramReferenceClient(opts: TelegramReferenceOptions)
|
|
|
325
340
|
// session's forum topic; this reference shows the minimal handling.
|
|
326
341
|
const threaded = renderThreadedFrame(msg as never);
|
|
327
342
|
if (threaded?.text) {
|
|
328
|
-
|
|
343
|
+
await sendTelegramHtmlChunks(send, opts.chatId, threaded.text);
|
|
329
344
|
}
|
|
330
345
|
}
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
let messageQueue = Promise.resolve();
|
|
349
|
+
ws.addEventListener("message", ev => {
|
|
350
|
+
const data = String(ev.data);
|
|
351
|
+
messageQueue = messageQueue.catch(() => undefined).then(() => handleServerMessage(data));
|
|
352
|
+
void messageQueue.catch(() => undefined);
|
|
331
353
|
});
|
|
332
354
|
|
|
333
355
|
// Telegram long-poll loop.
|
|
@@ -49,6 +49,7 @@ interface ThreadedFrame {
|
|
|
49
49
|
tokenUsage?: unknown;
|
|
50
50
|
model?: unknown;
|
|
51
51
|
diff?: unknown;
|
|
52
|
+
cwd?: unknown;
|
|
52
53
|
// turn_stream
|
|
53
54
|
phase?: unknown;
|
|
54
55
|
text?: unknown;
|
|
@@ -89,18 +90,25 @@ export function formatIdentityHeader(frame: {
|
|
|
89
90
|
/** Format a streamed context update into a compact block (omitting empty fields). */
|
|
90
91
|
export function formatContextUpdate(frame: ThreadedFrame): string | undefined {
|
|
91
92
|
const lines: string[] = [];
|
|
93
|
+
const session = str(frame.sessionId);
|
|
94
|
+
const cwd = str(frame.cwd);
|
|
92
95
|
const last = str(frame.lastMessage);
|
|
93
|
-
if (last) lines.push(escapeHtml(truncate(last, 600)));
|
|
94
96
|
const task = str(frame.task);
|
|
95
|
-
if (task) lines.push(`${italic("task:")} ${escapeHtml(task)}`);
|
|
96
97
|
const goal = str(frame.goal);
|
|
97
|
-
if (goal) lines.push(`${italic("goal:")} ${escapeHtml(goal)}`);
|
|
98
98
|
const usage = str(frame.tokenUsage);
|
|
99
99
|
const model = str(frame.model);
|
|
100
|
-
if (usage || model) lines.push(`ctx: ${code([usage, model].filter(Boolean).join(" · "))}`);
|
|
101
100
|
const diff = str(frame.diff);
|
|
101
|
+
if (!cwd && !last && !task && !goal && !usage && !model && !diff) return undefined;
|
|
102
|
+
const meta = [session ? `session: ${code(session)}` : undefined, cwd ? `cwd: ${code(cwd)}` : undefined].filter(
|
|
103
|
+
Boolean,
|
|
104
|
+
);
|
|
105
|
+
if (meta.length > 0) lines.push(meta.join(" · "));
|
|
106
|
+
if (last) lines.push(escapeHtml(truncate(last, 600)));
|
|
107
|
+
if (task) lines.push(`${italic("task:")} ${escapeHtml(task)}`);
|
|
108
|
+
if (goal) lines.push(`${italic("goal:")} ${escapeHtml(goal)}`);
|
|
109
|
+
if (usage || model) lines.push(`ctx: ${code([usage, model].filter(Boolean).join(" · "))}`);
|
|
102
110
|
if (diff) lines.push(`diff:\n${pre(truncate(diff, 1200))}`);
|
|
103
|
-
return lines.
|
|
111
|
+
return lines.join("\n");
|
|
104
112
|
}
|
|
105
113
|
|
|
106
114
|
/**
|
|
@@ -125,7 +133,7 @@ export function renderThreadedFrame(frame: ThreadedFrame): ThreadedSend | undefi
|
|
|
125
133
|
case "turn_stream": {
|
|
126
134
|
const raw = str(frame.text);
|
|
127
135
|
if (!raw) return undefined;
|
|
128
|
-
const text =
|
|
136
|
+
const text = markdownToTelegramHtml(raw);
|
|
129
137
|
const finalized = frame.phase === "finalized";
|
|
130
138
|
return {
|
|
131
139
|
method: "sendMessage",
|
|
@@ -25,7 +25,7 @@ You may receive a forked parent-conversation snapshot as background. Your read-o
|
|
|
25
25
|
|
|
26
26
|
<constraints>
|
|
27
27
|
- Read-only: never write, edit, format, commit, push, or mutate files.
|
|
28
|
-
- Exception: you may use the restricted `bash` tool only for sanctioned SKC workflow CLI persistence (`skc ralplan --write ...`) and SKC workflow state read/write/contract commands (`skc state ...`). For `skc ralplan --write`, pass the verdict markdown
|
|
28
|
+
- Exception: you may use the restricted `bash` tool only for sanctioned SKC workflow CLI persistence (`skc ralplan --write ...`) and SKC workflow state read/write/contract commands (`skc state ...`). For `skc ralplan --write`, pass the verdict markdown through the `SKC_RALPLAN_ARTIFACT` env override and `--artifact-env SKC_RALPLAN_ARTIFACT`, not as a file path. Do not use bash for product-source writes, direct handoffs, state clears, or general shell work.
|
|
29
29
|
- Never approve code or plans you have not grounded in inspected files.
|
|
30
30
|
- Never give generic advice detached from this codebase.
|
|
31
31
|
- Never approve CRITICAL or HIGH severity issues.
|
|
@@ -63,6 +63,9 @@ A narrow compatibility fallback can be acceptable only when it is scoped to a kn
|
|
|
63
63
|
## Summary
|
|
64
64
|
2-3 sentences with result and main recommendation.
|
|
65
65
|
|
|
66
|
+
## Claims
|
|
67
|
+
Evidence-backed claims being reviewed or introduced.
|
|
68
|
+
|
|
66
69
|
## Analysis
|
|
67
70
|
Evidence-backed findings.
|
|
68
71
|
|
|
@@ -81,12 +84,12 @@ Prioritized concrete actions.
|
|
|
81
84
|
## Code Review Recommendation
|
|
82
85
|
`APPROVE` / `COMMENT` / `REQUEST CHANGES`
|
|
83
86
|
|
|
84
|
-
##
|
|
87
|
+
## Tradeoffs
|
|
85
88
|
Table or bullets comparing viable options when relevant.
|
|
86
89
|
|
|
87
|
-
Persist this full review as the durable artifact via the restricted bash CLI, passing the markdown
|
|
90
|
+
Persist this full review as the durable artifact via the restricted bash CLI, passing the markdown through the `SKC_RALPLAN_ARTIFACT` env override (never a file path, never `/tmp`):
|
|
88
91
|
|
|
89
|
-
skc ralplan --write --stage architect --stage_n <N> --artifact
|
|
92
|
+
skc ralplan --write --stage architect --stage_n <N> --artifact-env SKC_RALPLAN_ARTIFACT --json
|
|
90
93
|
|
|
91
94
|
Then return to the caller ONLY the write receipt (`run_id`, `path`, `sha256`, `stage`, `stage_n`) plus the compact verdict (Architectural Status + Code Review Recommendation). Never paste the full review body back into your response — the caller reads the persisted artifact when it needs the full text.
|
|
92
95
|
</output_contract>
|
|
@@ -17,7 +17,7 @@ Review plan clarity, completeness, verification, big-picture fit, referenced fil
|
|
|
17
17
|
|
|
18
18
|
<constraints>
|
|
19
19
|
- Read-only: do not write, edit, format, commit, push, or mutate files.
|
|
20
|
-
- Exception: you may use the restricted `bash` tool only for sanctioned SKC workflow CLI persistence (`skc ralplan --write ...`) and SKC workflow state read/write/contract commands (`skc state ...`). For `skc ralplan --write`, pass the evaluation markdown
|
|
20
|
+
- Exception: you may use the restricted `bash` tool only for sanctioned SKC workflow CLI persistence (`skc ralplan --write ...`) and SKC workflow state read/write/contract commands (`skc state ...`). For `skc ralplan --write`, pass the evaluation markdown through the `SKC_RALPLAN_ARTIFACT` env override and `--artifact-env SKC_RALPLAN_ARTIFACT`, not as a file path. Do not use bash for product-source writes, direct handoffs, state clears, or general shell work.
|
|
21
21
|
- A lone file path is valid input; read and evaluate it.
|
|
22
22
|
- Reject YAML-only plans as invalid plan format when a human-readable plan is required.
|
|
23
23
|
- Do not invent problems; report no issues found when the plan passes.
|
|
@@ -42,11 +42,19 @@ Review plan clarity, completeness, verification, big-picture fit, referenced fil
|
|
|
42
42
|
</success_criteria>
|
|
43
43
|
|
|
44
44
|
<output_contract>
|
|
45
|
+
## Verdict
|
|
45
46
|
**[OKAY / ITERATE / REJECT]**
|
|
46
47
|
|
|
47
|
-
|
|
48
|
+
## Claim Checks
|
|
49
|
+
Concise evidence-backed explanation of verified claims.
|
|
48
50
|
|
|
49
|
-
|
|
51
|
+
## Missing Evidence
|
|
52
|
+
Definitely missing or unverified evidence, or `None`.
|
|
53
|
+
|
|
54
|
+
## Approval Boundary
|
|
55
|
+
What execution may proceed with, and what remains outside approval.
|
|
56
|
+
|
|
57
|
+
## Summary
|
|
50
58
|
- Clarity
|
|
51
59
|
- Verifiability
|
|
52
60
|
- Completeness
|
|
@@ -55,11 +63,12 @@ Review plan clarity, completeness, verification, big-picture fit, referenced fil
|
|
|
55
63
|
- Alternatives Depth
|
|
56
64
|
- Risk/Verification Rigor
|
|
57
65
|
|
|
58
|
-
|
|
66
|
+
## Required Changes
|
|
67
|
+
If not OKAY, list concrete required fixes; otherwise write `None`.
|
|
59
68
|
|
|
60
|
-
Persist this full evaluation as the durable artifact via the restricted bash CLI, passing the markdown
|
|
69
|
+
Persist this full evaluation as the durable artifact via the restricted bash CLI, passing the markdown through the `SKC_RALPLAN_ARTIFACT` env override (never a file path, never `/tmp`):
|
|
61
70
|
|
|
62
|
-
skc ralplan --write --stage critic --stage_n <N> --artifact
|
|
71
|
+
skc ralplan --write --stage critic --stage_n <N> --artifact-env SKC_RALPLAN_ARTIFACT --json
|
|
63
72
|
|
|
64
73
|
Then return to the caller ONLY the write receipt (`run_id`, `path`, `sha256`, `stage`, `stage_n`) plus the compact verdict (OKAY / ITERATE / REJECT). Never paste the full evaluation body back into your response — the caller reads the persisted artifact when it needs the full text.
|
|
65
74
|
</output_contract>
|
|
@@ -17,7 +17,7 @@ Leave execution with a right-sized, evidence-grounded plan: scope, steps, accept
|
|
|
17
17
|
|
|
18
18
|
<constraints>
|
|
19
19
|
- Read-only: never write, edit, format, commit, push, or mutate files.
|
|
20
|
-
- Exception: you may use the restricted `bash` tool only for sanctioned SKC workflow CLI persistence (`skc ralplan --write ...`) and SKC workflow state read/write/contract commands (`skc state ...`). For `skc ralplan --write`, pass the plan markdown
|
|
20
|
+
- Exception: you may use the restricted `bash` tool only for sanctioned SKC workflow CLI persistence (`skc ralplan --write ...`) and SKC workflow state read/write/contract commands (`skc state ...`). For `skc ralplan --write`, pass the plan markdown through the `SKC_RALPLAN_ARTIFACT` env override and `--artifact-env SKC_RALPLAN_ARTIFACT`, not as a file path. Do not use bash for product-source writes, direct handoffs, state clears, or general shell work.
|
|
21
21
|
- Persist durable plans only through `skc ralplan --write`. Never write plan files to `/tmp`, the repository, or any other path, and never rely on a file the caller must read back. The CLI is your only persistence channel.
|
|
22
22
|
- Inspect the repository before asking about code facts.
|
|
23
23
|
- Ask only about priorities, tradeoffs, scope decisions, timelines, or preferences that repository inspection cannot resolve.
|
|
@@ -45,16 +45,21 @@ Leave execution with a right-sized, evidence-grounded plan: scope, steps, accept
|
|
|
45
45
|
<output_contract>
|
|
46
46
|
Build the full plan as a single markdown document containing:
|
|
47
47
|
- Summary
|
|
48
|
+
- Intent Diff
|
|
49
|
+
- Decision Drivers
|
|
50
|
+
- Options
|
|
48
51
|
- In scope / out of scope
|
|
49
52
|
- File-level changes
|
|
50
53
|
- Sequencing and dependencies
|
|
51
54
|
- Acceptance criteria
|
|
52
55
|
- Verification
|
|
56
|
+
- Escalation/Risk Gate
|
|
57
|
+
- Verification Plan
|
|
53
58
|
- Risks and mitigations
|
|
54
59
|
|
|
55
|
-
Persist that markdown as the durable artifact via the restricted bash CLI, passing the plan
|
|
60
|
+
Persist that markdown as the durable artifact via the restricted bash CLI, passing the plan through the `SKC_RALPLAN_ARTIFACT` env override (never a file path, never `/tmp`):
|
|
56
61
|
|
|
57
|
-
skc ralplan --write --stage planner --stage_n <N> --artifact
|
|
62
|
+
skc ralplan --write --stage planner --stage_n <N> --artifact-env SKC_RALPLAN_ARTIFACT --json
|
|
58
63
|
|
|
59
64
|
Then return to the caller ONLY the write receipt (`run_id`, `path`, `sha256`, `stage`, `stage_n`) plus a compact plan summary (<=10 lines). Never paste the full plan body back into your response — the caller reads the persisted artifact when it needs the full text.
|
|
60
65
|
</output_contract>
|
|
@@ -60,8 +60,13 @@ Use for read-only plan critique. It approves only when execution can proceed wit
|
|
|
60
60
|
|
|
61
61
|
<routing>
|
|
62
62
|
- Clear, low-risk implementation request → implement directly with focused verification.
|
|
63
|
+
- Do not invoke `deep-interview`, `ralplan`, `ultragoal`, `team`, or role agents for simple clear implementation requests; direct tools and the default launch path are enough.
|
|
64
|
+
- The runtime records a `workflow-intent-diff` CustomEntry for direct-path traceability; it does not participate in LLM context and is not a reason to slow down direct execution.
|
|
65
|
+
- When a task is clear, bounded, and low-risk, the default action is to make the smallest correct change and verify it, not to interview, plan, open a durable ledger, or delegate.
|
|
66
|
+
- Small verification needs do not make a task a planning workflow. Escalate only for real ambiguity, non-trivial architecture/sequence risk, durable multi-goal tracking, or useful coordinated workers.
|
|
67
|
+
- Root-cause phase schema is active only for contradiction, regression, or high-risk transition work; otherwise keep ordinary verification and do not add root-cause ceremony.
|
|
63
68
|
- Vague requirements → use `deep-interview` before planning or execution.
|
|
64
|
-
- Clear requirements but non-trivial architecture/sequence risk → use `ralplan` and stop at pending approval.
|
|
69
|
+
- Clear requirements but non-trivial architecture/sequence risk → use `ralplan --deliberate` and stop at pending approval.
|
|
65
70
|
- Durable goal ledger needed → use `ultragoal`; if no approved plan exists, run `ralplan` first.
|
|
66
71
|
- Approved work benefits from coordinated persistent workers → use `team`.
|
|
67
72
|
- Large enough implementation work → delegate bounded slices to `executor` through the task/sub-agent tool when it improves quality or throughput.
|
|
@@ -131,6 +136,18 @@ Use tools whenever they materially improve correctness, completeness, or groundi
|
|
|
131
136
|
</inventory>
|
|
132
137
|
{{/if}}
|
|
133
138
|
|
|
139
|
+
{{#if toolDiscoveryActive}}
|
|
140
|
+
<tool-discovery>
|
|
141
|
+
Only essential tools are loaded up front; other tools are hidden to save context and are available on demand. Call `{{toolRefs.search_tool_bm25}}` with a short natural-language query to find and activate the tool you need, then call it. Prefer discovering a purpose-built tool over forcing the task onto a resident one.
|
|
142
|
+
{{#if discoverableTools.length}}
|
|
143
|
+
Discoverable tools:
|
|
144
|
+
{{#each discoverableTools}}
|
|
145
|
+
- `{{name}}`{{#if summary}} — {{summary}}{{/if}}
|
|
146
|
+
{{/each}}
|
|
147
|
+
{{/if}}
|
|
148
|
+
</tool-discovery>
|
|
149
|
+
{{/if}}
|
|
150
|
+
|
|
134
151
|
<inputs>
|
|
135
152
|
- Keep tool inputs concise where possible.
|
|
136
153
|
- For `path` or path-like fields, prefer relative paths.
|
|
@@ -233,9 +250,10 @@ For image understanding, call `{{toolRefs.read}}` on the image path; the image i
|
|
|
233
250
|
</before-editing>
|
|
234
251
|
|
|
235
252
|
<decomposition>
|
|
236
|
-
- Use todo tracking for tasks with three or more distinct steps.
|
|
253
|
+
- Use todo tracking for tasks with three or more distinct steps; skip it for one-step or obvious two-step fixes where the next action is already clear.
|
|
237
254
|
- Mark completed tasks immediately and continue to the next task without yielding.
|
|
238
255
|
- Delegate rather than silently shrinking scope. Prefer `executor` for bounded implementation slices, `planner` for sequencing, `architect` for architecture/code-review lanes, and `critic` for plan critique.
|
|
256
|
+
- Do not delegate for single-line typos, obvious syntax errors, single-file known-location fixes, or direct answers.
|
|
239
257
|
</decomposition>
|
|
240
258
|
|
|
241
259
|
<verification>
|
|
@@ -33,7 +33,7 @@ This session's bash tool is restricted. It only accepts commands beginning with:
|
|
|
33
33
|
{{#each restrictedAllowedPrefixes}}
|
|
34
34
|
- `{{this}}`
|
|
35
35
|
{{/each}}
|
|
36
|
-
Use it only for sanctioned SKC workflow CLI persistence or state read/write/contract operations; per-command env
|
|
36
|
+
Use it only for sanctioned SKC workflow CLI persistence or state read/write/contract operations; the only per-command env override allowed is `SKC_RALPLAN_ARTIFACT` when paired with `skc ralplan --write ... --artifact-env SKC_RALPLAN_ARTIFACT`, and all other shell command shapes are blocked before execution.
|
|
37
37
|
{{/when}}
|
|
38
38
|
</restricted-bash-mode>
|
|
39
39
|
{{/if}}
|
|
@@ -237,6 +237,34 @@ export async function listMCPServers(filePath: string): Promise<string[]> {
|
|
|
237
237
|
return Object.keys(config.mcpServers ?? {});
|
|
238
238
|
}
|
|
239
239
|
|
|
240
|
+
/**
|
|
241
|
+
* Set a server's autoload flag (connect automatically at session startup).
|
|
242
|
+
*
|
|
243
|
+
* Autoload defaults to true, so turning it on removes the key to keep the
|
|
244
|
+
* config file free of redundant fields; turning it off writes `autoload: false`.
|
|
245
|
+
*
|
|
246
|
+
* @throws Error if the server doesn't exist
|
|
247
|
+
*/
|
|
248
|
+
export async function setServerAutoload(filePath: string, name: string, autoload: boolean): Promise<void> {
|
|
249
|
+
const existing = await readMCPConfigFile(filePath);
|
|
250
|
+
const server = existing.mcpServers?.[name];
|
|
251
|
+
if (!server) {
|
|
252
|
+
throw new Error(`Server "${name}" not found in ${filePath}`);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const { autoload: _removed, ...rest } = server;
|
|
256
|
+
const updatedServer = (autoload ? rest : { ...rest, autoload: false }) as MCPServerConfig;
|
|
257
|
+
const updated: MCPConfigFile = {
|
|
258
|
+
...existing,
|
|
259
|
+
mcpServers: {
|
|
260
|
+
...existing.mcpServers,
|
|
261
|
+
[name]: updatedServer,
|
|
262
|
+
},
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
await writeMCPConfigFile(filePath, updated);
|
|
266
|
+
}
|
|
267
|
+
|
|
240
268
|
/**
|
|
241
269
|
* Read the disabled servers list from a config file.
|
|
242
270
|
*/
|
|
@@ -20,6 +20,8 @@ export interface LoadMCPConfigsOptions {
|
|
|
20
20
|
filterExa?: boolean;
|
|
21
21
|
/** Whether to filter out browser MCP servers when builtin browser tool is enabled (default: false) */
|
|
22
22
|
filterBrowser?: boolean;
|
|
23
|
+
/** Only include servers eligible for startup connection, i.e. autoload !== false (default: false) */
|
|
24
|
+
autoloadOnly?: boolean;
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
/** Result of loading MCP configs */
|
|
@@ -40,6 +42,7 @@ function convertToLegacyConfig(server: MCPServer): MCPServerConfig {
|
|
|
40
42
|
const transport = server.transport ?? (server.command ? "stdio" : server.url ? "http" : "stdio");
|
|
41
43
|
const shared = {
|
|
42
44
|
enabled: server.enabled,
|
|
45
|
+
autoload: server.autoload,
|
|
43
46
|
timeout: server.timeout,
|
|
44
47
|
auth: server.auth,
|
|
45
48
|
oauth: server.oauth,
|
|
@@ -96,6 +99,7 @@ export async function loadAllMCPConfigs(cwd: string, options?: LoadMCPConfigsOpt
|
|
|
96
99
|
const enableProjectConfig = options?.enableProjectConfig ?? true;
|
|
97
100
|
const filterExa = options?.filterExa ?? true;
|
|
98
101
|
const filterBrowser = options?.filterBrowser ?? false;
|
|
102
|
+
const autoloadOnly = options?.autoloadOnly ?? false;
|
|
99
103
|
|
|
100
104
|
// Load MCP servers via capability system
|
|
101
105
|
const result = await loadCapability<MCPServer>(mcpCapability.id, { cwd });
|
|
@@ -115,6 +119,9 @@ export async function loadAllMCPConfigs(cwd: string, options?: LoadMCPConfigsOpt
|
|
|
115
119
|
if (config.enabled === false || disabledServers.has(server.name)) {
|
|
116
120
|
continue;
|
|
117
121
|
}
|
|
122
|
+
if (autoloadOnly && config.autoload === false) {
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
118
125
|
configs[server.name] = config;
|
|
119
126
|
sources[server.name] = server._source;
|
|
120
127
|
}
|
|
@@ -34,6 +34,8 @@ export interface MCPToolsLoadOptions {
|
|
|
34
34
|
filterExa?: boolean;
|
|
35
35
|
/** Whether to filter out browser MCP servers when builtin browser tool is enabled (default: false) */
|
|
36
36
|
filterBrowser?: boolean;
|
|
37
|
+
/** Only connect servers with autoload !== false (default: false) */
|
|
38
|
+
autoloadOnly?: boolean;
|
|
37
39
|
/** SQLite storage for MCP tool cache (null disables cache) */
|
|
38
40
|
cacheStorage?: AgentStorage | null;
|
|
39
41
|
/** Auth storage used to resolve OAuth credentials before initial MCP connect */
|
|
@@ -72,6 +74,7 @@ export async function discoverAndLoadMCPTools(cwd: string, options?: MCPToolsLoa
|
|
|
72
74
|
enableProjectConfig: options?.enableProjectConfig,
|
|
73
75
|
filterExa: options?.filterExa,
|
|
74
76
|
filterBrowser: options?.filterBrowser,
|
|
77
|
+
autoloadOnly: options?.autoloadOnly,
|
|
75
78
|
});
|
|
76
79
|
} catch (error) {
|
|
77
80
|
// If discovery fails entirely, return empty result
|