@sayknow-cli/coding-agent 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/types/cli/notify-cli.d.ts +2 -0
- package/dist/types/config/settings-schema.d.ts +98 -3
- package/dist/types/defaults/skc-defaults.d.ts +2 -13
- package/dist/types/extensibility/shared-events.d.ts +1 -0
- package/dist/types/i18n/messages/en.d.ts +1 -0
- package/dist/types/lsp/types.d.ts +2 -0
- package/dist/types/modes/components/settings-defs.d.ts +1 -0
- package/dist/types/modes/theme/theme.d.ts +1 -1
- package/dist/types/notifications/attachment-registry.d.ts +17 -0
- package/dist/types/notifications/chat-adapters.d.ts +9 -0
- package/dist/types/notifications/config.d.ts +9 -1
- package/dist/types/notifications/engine.d.ts +59 -0
- package/dist/types/notifications/managed-daemon.d.ts +48 -0
- package/dist/types/notifications/telegram-daemon.d.ts +19 -0
- package/dist/types/notifications/threaded-inbound.d.ts +19 -0
- package/dist/types/notifications/threaded-render.d.ts +6 -1
- package/dist/types/session/agent-session.d.ts +2 -0
- package/dist/types/skc-runtime/ralplan-runtime.d.ts +1 -1
- package/dist/types/tools/fetch.d.ts +23 -0
- package/dist/types/tools/index.d.ts +1 -0
- package/dist/types/tools/telegram-send.d.ts +32 -0
- package/dist/types/web/insane/bridge.d.ts +103 -0
- package/dist/types/web/insane/url-guard.d.ts +22 -0
- package/dist/types/web/search/provider.d.ts +18 -1
- package/dist/types/web/search/providers/insane.d.ts +53 -0
- package/dist/types/web/search/providers/text-citations.d.ts +23 -0
- package/dist/types/web/search/types.d.ts +12 -4
- package/package.json +10 -8
- package/scripts/verify-insane-vendor.ts +132 -0
- package/src/cli/args.ts +1 -1
- package/src/cli/fast-help.ts +1 -1
- package/src/cli/notify-cli.ts +152 -5
- package/src/commands/team.ts +1 -1
- package/src/config/settings-schema.ts +119 -5
- package/src/defaults/skc/skills/ralplan/SKILL.md +11 -4
- package/src/defaults/skc-defaults.ts +2 -27
- package/src/extensibility/shared-events.ts +1 -0
- 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.ts +1 -0
- package/src/i18n/messages/ko.ts +1 -0
- package/src/i18n/messages/zh.ts +1 -0
- package/src/internal-urls/docs-index.generated.ts +9 -9
- package/src/lsp/config.ts +16 -3
- package/src/lsp/defaults.json +7 -0
- package/src/lsp/types.ts +2 -0
- package/src/modes/components/settings-defs.ts +2 -1
- package/src/modes/components/settings-selector.ts +7 -2
- package/src/modes/controllers/event-controller.ts +15 -0
- package/src/modes/interactive-mode.ts +46 -2
- package/src/modes/theme/theme.ts +4 -0
- package/src/modes/utils/context-usage.ts +2 -2
- package/src/notifications/attachment-registry.ts +23 -0
- package/src/notifications/chat-adapters.ts +147 -0
- package/src/notifications/config.ts +23 -2
- package/src/notifications/engine.ts +100 -0
- package/src/notifications/index.ts +180 -38
- package/src/notifications/managed-daemon.ts +163 -0
- package/src/notifications/telegram-daemon.ts +235 -14
- package/src/notifications/threaded-inbound.ts +60 -4
- package/src/notifications/threaded-render.ts +20 -2
- package/src/session/agent-session.ts +82 -51
- package/src/skc-runtime/launch-tmux.ts +17 -3
- package/src/skc-runtime/ledger-event-renderer.ts +1 -0
- package/src/skc-runtime/ralplan-runtime.ts +2 -2
- package/src/skc-runtime/workflow-manifest.generated.json +29 -0
- package/src/skc-runtime/workflow-manifest.ts +7 -2
- package/src/tools/fetch.ts +78 -1
- package/src/tools/index.ts +3 -0
- package/src/tools/telegram-send.ts +137 -0
- package/src/web/insane/bridge.ts +350 -0
- package/src/web/insane/url-guard.ts +155 -0
- package/src/web/search/provider.ts +77 -18
- package/src/web/search/providers/anthropic.ts +70 -3
- package/src/web/search/providers/codex.ts +1 -119
- package/src/web/search/providers/gemini.ts +99 -0
- package/src/web/search/providers/insane.ts +551 -0
- package/src/web/search/providers/openai-compatible.ts +66 -32
- package/src/web/search/providers/text-citations.ts +111 -0
- package/src/web/search/types.ts +13 -2
- package/vendor/insane-search/LICENSE +21 -0
- package/vendor/insane-search/MANIFEST.json +24 -0
- package/vendor/insane-search/engine/__init__.py +23 -0
- package/vendor/insane-search/engine/__main__.py +128 -0
- package/vendor/insane-search/engine/bias_check.py +183 -0
- package/vendor/insane-search/engine/executor.py +254 -0
- package/vendor/insane-search/engine/fetch_chain.py +725 -0
- package/vendor/insane-search/engine/learning.py +175 -0
- package/vendor/insane-search/engine/phase0.py +214 -0
- package/vendor/insane-search/engine/safety.py +91 -0
- package/vendor/insane-search/engine/templates/package.json +11 -0
- package/vendor/insane-search/engine/templates/playwright_mobile_chrome.js +188 -0
- package/vendor/insane-search/engine/templates/playwright_real_chrome.js +243 -0
- package/vendor/insane-search/engine/tests/test_hardening.py +57 -0
- package/vendor/insane-search/engine/tests/test_smoke.py +152 -0
- package/vendor/insane-search/engine/tests/test_u1.py +200 -0
- package/vendor/insane-search/engine/tests/test_u4.py +131 -0
- package/vendor/insane-search/engine/tests/test_u5.py +163 -0
- package/vendor/insane-search/engine/tests/test_u7.py +124 -0
- package/vendor/insane-search/engine/transport.py +211 -0
- package/vendor/insane-search/engine/url_transforms.py +98 -0
- package/vendor/insane-search/engine/validators.py +331 -0
- package/vendor/insane-search/engine/waf_detector.py +214 -0
- package/vendor/insane-search/engine/waf_profiles.yaml +162 -0
package/src/cli/notify-cli.ts
CHANGED
|
@@ -29,6 +29,8 @@ export interface NotifyCommandDeps {
|
|
|
29
29
|
pollIntervalMs?: number;
|
|
30
30
|
setupChatId?: string;
|
|
31
31
|
setupRedact?: boolean;
|
|
32
|
+
setupInteractive?: boolean;
|
|
33
|
+
threadedModePrompt?: (message: string) => Promise<string>;
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
interface TelegramApiResponse<T> {
|
|
@@ -47,6 +49,18 @@ interface TelegramUpdate {
|
|
|
47
49
|
};
|
|
48
50
|
}
|
|
49
51
|
|
|
52
|
+
interface TelegramUser {
|
|
53
|
+
id: number;
|
|
54
|
+
is_bot?: boolean;
|
|
55
|
+
first_name?: string;
|
|
56
|
+
username?: string;
|
|
57
|
+
has_topics_enabled?: boolean;
|
|
58
|
+
allows_users_to_create_topics?: boolean;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
type ThreadedModeState = "enabled" | "disabled" | "unknown";
|
|
62
|
+
type ThreadedModeFinalLabel = "verified" | "unverified" | "unknown";
|
|
63
|
+
|
|
50
64
|
const DEFAULT_API_BASE = "https://api.telegram.org";
|
|
51
65
|
const DEFAULT_POLL_TIMEOUT_MS = 60_000;
|
|
52
66
|
const DEFAULT_POLL_INTERVAL_MS = 1_000;
|
|
@@ -120,7 +134,11 @@ async function runSetup(deps: NotifyCommandDeps): Promise<void> {
|
|
|
120
134
|
throw new Error("Telegram bot token is required.");
|
|
121
135
|
}
|
|
122
136
|
|
|
123
|
-
await
|
|
137
|
+
const user = await getMe(fetchImpl, apiBase, token);
|
|
138
|
+
const threadedState = await verifyThreadedMode(fetchImpl, apiBase, token, user, {
|
|
139
|
+
interactive: resolveSetupInteractive(deps),
|
|
140
|
+
prompt: deps.threadedModePrompt ?? promptForThreadedMode,
|
|
141
|
+
});
|
|
124
142
|
process.stdout.write(
|
|
125
143
|
"Token validated. Message your bot now from the private Telegram chat to pair notifications.\n",
|
|
126
144
|
);
|
|
@@ -145,7 +163,9 @@ async function runSetup(deps: NotifyCommandDeps): Promise<void> {
|
|
|
145
163
|
if (deps.setupRedact) settings.set("notifications.redact", true);
|
|
146
164
|
await settings.flush();
|
|
147
165
|
|
|
148
|
-
process.stdout.write(
|
|
166
|
+
process.stdout.write(
|
|
167
|
+
`Notifications enabled. botToken=${maskToken(token)} chatId=${chatId} threaded=${threadedLabel(threadedState)}\n`,
|
|
168
|
+
);
|
|
149
169
|
}
|
|
150
170
|
|
|
151
171
|
async function promptForToken(): Promise<string> {
|
|
@@ -160,14 +180,135 @@ async function promptForToken(): Promise<string> {
|
|
|
160
180
|
}
|
|
161
181
|
}
|
|
162
182
|
|
|
183
|
+
const THREADED_ENABLED_SUCCESS =
|
|
184
|
+
"Telegram Threaded Mode capability verified for this bot. SKC will request a private-chat topic per session; if Telegram ever refuses topic creation, notifications fall back to this flat chat with a one-time nudge.\n";
|
|
185
|
+
|
|
186
|
+
const THREADED_MISSING_WARNING =
|
|
187
|
+
"Warning: Telegram getMe did not include has_topics_enabled, so SKC cannot verify private-chat Threaded Mode capability for this bot. Setup will continue; update Telegram/Bot API support or re-run setup if per-session topics fail.\n";
|
|
188
|
+
|
|
189
|
+
const THREADED_NONINTERACTIVE_WARNING =
|
|
190
|
+
"Warning: Telegram Threaded Mode capability is OFF for this bot. Setup will be saved because this run is non-interactive, but per-session Telegram delivery may fail closed until the bot owner enables Threaded Mode in @BotFather. SKC cannot enable it through the Bot API.\n";
|
|
191
|
+
|
|
192
|
+
const THREADED_DISABLED_GUIDANCE =
|
|
193
|
+
"Telegram Threaded Mode is OFF for this bot. SKC needs Telegram private-chat topics so each session can use its own thread.\n" +
|
|
194
|
+
"SKC cannot enable this through the Bot API. Open @BotFather, select this bot, enable Threaded Mode / forum topics for private chats, then return here.\n" +
|
|
195
|
+
"Telegram may require an additional Stars purchase fee for private-chat topics.\n";
|
|
196
|
+
|
|
197
|
+
const THREADED_DISABLED_PROMPT =
|
|
198
|
+
"Press Enter after enabling Threaded Mode, or type skip to finish setup with a warning: ";
|
|
199
|
+
|
|
200
|
+
const THREADED_STILL_OFF = "Telegram still reports Threaded Mode OFF for this bot.\n";
|
|
201
|
+
|
|
202
|
+
const THREADED_RETRY_PROMPT = "Press Enter to check again, or type skip to finish setup with a warning: ";
|
|
203
|
+
|
|
204
|
+
const THREADED_SKIP_WARNING =
|
|
205
|
+
"Warning: continuing without verified Telegram Threaded Mode capability. Setup will be saved, but per-session Telegram delivery may fail closed until Threaded Mode is enabled in BotFather.\n";
|
|
206
|
+
|
|
207
|
+
const THREADED_INVALID_INPUT = "Type Enter to retry or skip to continue with a warning.\n";
|
|
208
|
+
|
|
209
|
+
const THREADED_RETRY_INPUTS = new Set(["", "y", "yes", "r", "retry"]);
|
|
210
|
+
const THREADED_SKIP_INPUTS = new Set(["s", "skip", "n", "no"]);
|
|
211
|
+
|
|
212
|
+
function isTelegramUser(value: unknown): value is TelegramUser {
|
|
213
|
+
return Boolean(value) && typeof value === "object" && typeof (value as { id?: unknown }).id === "number";
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
async function getMe(fetchImpl: typeof fetch, apiBase: string, token: string): Promise<TelegramUser> {
|
|
217
|
+
const user = await callTelegram<unknown>(fetchImpl, apiBase, token, "getMe", {});
|
|
218
|
+
if (!isTelegramUser(user)) {
|
|
219
|
+
throw new Error("Telegram getMe returned invalid Telegram response: missing valid User result.");
|
|
220
|
+
}
|
|
221
|
+
return user;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function threadedModeState(user: TelegramUser): ThreadedModeState {
|
|
225
|
+
if (user.has_topics_enabled === true) return "enabled";
|
|
226
|
+
if (user.has_topics_enabled === false) return "disabled";
|
|
227
|
+
return "unknown";
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function threadedLabel(state: ThreadedModeState): ThreadedModeFinalLabel {
|
|
231
|
+
if (state === "enabled") return "verified";
|
|
232
|
+
if (state === "disabled") return "unverified";
|
|
233
|
+
return "unknown";
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function resolveSetupInteractive(deps: NotifyCommandDeps): boolean {
|
|
237
|
+
if (deps.setupInteractive !== undefined) return deps.setupInteractive;
|
|
238
|
+
return Boolean(process.stdin.isTTY) && !deps.setupChatId?.trim();
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
async function promptForThreadedMode(message: string): Promise<string> {
|
|
242
|
+
if (!process.stdin.isTTY) return "skip";
|
|
243
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout, terminal: true });
|
|
244
|
+
try {
|
|
245
|
+
return (await rl.question(message)).trim();
|
|
246
|
+
} finally {
|
|
247
|
+
rl.close();
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
async function verifyThreadedMode(
|
|
252
|
+
fetchImpl: typeof fetch,
|
|
253
|
+
apiBase: string,
|
|
254
|
+
token: string,
|
|
255
|
+
initialUser: TelegramUser,
|
|
256
|
+
opts: { interactive: boolean; prompt: (message: string) => Promise<string> },
|
|
257
|
+
): Promise<ThreadedModeState> {
|
|
258
|
+
const classify = (user: TelegramUser): ThreadedModeState | undefined => {
|
|
259
|
+
const state = threadedModeState(user);
|
|
260
|
+
if (state === "enabled") {
|
|
261
|
+
process.stdout.write(THREADED_ENABLED_SUCCESS);
|
|
262
|
+
return "enabled";
|
|
263
|
+
}
|
|
264
|
+
if (state === "unknown") {
|
|
265
|
+
process.stdout.write(THREADED_MISSING_WARNING);
|
|
266
|
+
return "unknown";
|
|
267
|
+
}
|
|
268
|
+
return undefined;
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
const initial = classify(initialUser);
|
|
272
|
+
if (initial) return initial;
|
|
273
|
+
|
|
274
|
+
if (!opts.interactive) {
|
|
275
|
+
process.stdout.write(THREADED_NONINTERACTIVE_WARNING);
|
|
276
|
+
return "disabled";
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
process.stdout.write(THREADED_DISABLED_GUIDANCE);
|
|
280
|
+
let firstPrompt = true;
|
|
281
|
+
for (;;) {
|
|
282
|
+
const answer = (await opts.prompt(firstPrompt ? THREADED_DISABLED_PROMPT : THREADED_RETRY_PROMPT))
|
|
283
|
+
.trim()
|
|
284
|
+
.toLowerCase();
|
|
285
|
+
firstPrompt = false;
|
|
286
|
+
if (THREADED_SKIP_INPUTS.has(answer)) {
|
|
287
|
+
process.stdout.write(THREADED_SKIP_WARNING);
|
|
288
|
+
return "disabled";
|
|
289
|
+
}
|
|
290
|
+
if (!THREADED_RETRY_INPUTS.has(answer)) {
|
|
291
|
+
process.stdout.write(THREADED_INVALID_INPUT);
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
const resolved = classify(await getMe(fetchImpl, apiBase, token));
|
|
295
|
+
if (resolved) return resolved;
|
|
296
|
+
process.stdout.write(THREADED_STILL_OFF);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
163
300
|
async function runStatus(deps: NotifyCommandDeps): Promise<void> {
|
|
164
301
|
const settings = await getSettings(deps);
|
|
165
302
|
const cfg = getNotificationConfig(settings);
|
|
166
303
|
process.stdout.write(
|
|
167
304
|
`${chalk.bold("Notifications")}\n` +
|
|
168
305
|
` enabled: ${cfg.enabled}\n` +
|
|
169
|
-
` botToken: ${maskToken(cfg.botToken)}\n` +
|
|
170
|
-
` chatId: ${cfg.chatId ?? "(unset)"}\n` +
|
|
306
|
+
` telegram.botToken: ${maskToken(cfg.botToken)}\n` +
|
|
307
|
+
` telegram.chatId: ${cfg.chatId ?? "(unset)"}\n` +
|
|
308
|
+
` discord.botToken: ${maskToken(cfg.discord.botToken)}\n` +
|
|
309
|
+
` discord.channelId: ${cfg.discord.channelId ?? "(unset)"}\n` +
|
|
310
|
+
` slack.botToken: ${maskToken(cfg.slack.botToken)}\n` +
|
|
311
|
+
` slack.channelId: ${cfg.slack.channelId ?? "(unset)"}\n` +
|
|
171
312
|
` redact: ${cfg.redact}\n`,
|
|
172
313
|
);
|
|
173
314
|
}
|
|
@@ -263,12 +404,18 @@ ${chalk.bold("Usage:")}
|
|
|
263
404
|
${APP_NAME} notify status
|
|
264
405
|
|
|
265
406
|
${chalk.bold("Subcommands:")}
|
|
266
|
-
setup Pair a Telegram bot token with a private chat
|
|
407
|
+
setup Pair a Telegram bot token with a private chat and verify Threaded Mode capability
|
|
267
408
|
status Show notification configuration without secrets
|
|
268
409
|
|
|
269
410
|
${chalk.bold("Examples:")}
|
|
270
411
|
${APP_NAME} notify setup
|
|
271
412
|
${APP_NAME} notify setup --token <botToken> --chat-id <chatId> [--redact]
|
|
272
413
|
${APP_NAME} notify status
|
|
414
|
+
|
|
415
|
+
${chalk.bold("Threaded Mode:")}
|
|
416
|
+
SKC uses Telegram private-chat topics for per-session threads. Setup verifies the bot
|
|
417
|
+
capability via getMe.has_topics_enabled. If it is off, enable Threaded Mode in @BotFather;
|
|
418
|
+
bots cannot toggle it through the Bot API. If Telegram refuses topic creation at runtime,
|
|
419
|
+
SKC delivers flat to the paired private chat and nudges you to enable Threaded Mode.
|
|
273
420
|
`);
|
|
274
421
|
}
|
package/src/commands/team.ts
CHANGED
|
@@ -95,7 +95,7 @@ export default class Team extends Command {
|
|
|
95
95
|
};
|
|
96
96
|
|
|
97
97
|
static examples = [
|
|
98
|
-
"skc --tmux # start
|
|
98
|
+
"skc --tmux # start the required tmux-backed leader session first",
|
|
99
99
|
'skc team 3:executor "Implement the approved plan"',
|
|
100
100
|
"skc team status <team-name> --json",
|
|
101
101
|
"skc team monitor <team-name> --json",
|
|
@@ -31,6 +31,7 @@ export type SettingTab =
|
|
|
31
31
|
| "appearance"
|
|
32
32
|
| "model"
|
|
33
33
|
| "interaction"
|
|
34
|
+
| "notifications"
|
|
34
35
|
| "context"
|
|
35
36
|
| "memory"
|
|
36
37
|
| "editing"
|
|
@@ -46,6 +47,7 @@ export const SETTING_TABS: SettingTab[] = [
|
|
|
46
47
|
"appearance",
|
|
47
48
|
"model",
|
|
48
49
|
"interaction",
|
|
50
|
+
"notifications",
|
|
49
51
|
"context",
|
|
50
52
|
"memory",
|
|
51
53
|
"editing",
|
|
@@ -59,6 +61,7 @@ export const TAB_METADATA: Record<SettingTab, { label: string; icon: `tab.${stri
|
|
|
59
61
|
appearance: { label: "Appearance", icon: "tab.appearance" },
|
|
60
62
|
model: { label: "Model", icon: "tab.model" },
|
|
61
63
|
interaction: { label: "Interaction", icon: "tab.interaction" },
|
|
64
|
+
notifications: { label: "Notifications", icon: "tab.notifications" },
|
|
62
65
|
context: { label: "Context", icon: "tab.context" },
|
|
63
66
|
memory: { label: "Memory", icon: "tab.memory" },
|
|
64
67
|
editing: { label: "Editing", icon: "tab.editing" },
|
|
@@ -130,11 +133,14 @@ interface UiString extends UiBase {
|
|
|
130
133
|
* - Omitted → renders as a free text input.
|
|
131
134
|
*/
|
|
132
135
|
options?: ReadonlyArray<SubmenuOption> | "runtime";
|
|
136
|
+
/** When true, the value is masked in the settings list display but edited in full. */
|
|
137
|
+
secret?: boolean;
|
|
133
138
|
}
|
|
134
139
|
|
|
135
140
|
/** Wide ui shape exposed to consumers that walk the schema generically. */
|
|
136
141
|
export type AnyUiMetadata = UiBase & {
|
|
137
142
|
options?: ReadonlyArray<SubmenuOption> | "runtime";
|
|
143
|
+
secret?: boolean;
|
|
138
144
|
};
|
|
139
145
|
|
|
140
146
|
interface BooleanDef {
|
|
@@ -255,15 +261,98 @@ export const SETTINGS_SCHEMA = {
|
|
|
255
261
|
"auth.broker.url": { type: "string", default: undefined },
|
|
256
262
|
"auth.broker.token": { type: "string", default: undefined },
|
|
257
263
|
|
|
258
|
-
// Notifications (Telegram
|
|
259
|
-
"notifications.enabled": {
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
264
|
+
// Notifications (shared daemon with Telegram/Discord/Slack presentation adapters)
|
|
265
|
+
"notifications.enabled": {
|
|
266
|
+
type: "boolean",
|
|
267
|
+
default: false,
|
|
268
|
+
ui: {
|
|
269
|
+
tab: "notifications",
|
|
270
|
+
label: "Enabled",
|
|
271
|
+
description:
|
|
272
|
+
"Master switch for notifications. Requires at least one adapter (Telegram/Discord/Slack) configured with a token and a chat/channel to actually deliver.",
|
|
273
|
+
},
|
|
274
|
+
},
|
|
275
|
+
"notifications.telegram.botToken": {
|
|
276
|
+
type: "string",
|
|
277
|
+
default: undefined,
|
|
278
|
+
ui: {
|
|
279
|
+
tab: "notifications",
|
|
280
|
+
label: "Telegram bot token",
|
|
281
|
+
description: "Telegram BotFather HTTP API token. Stored locally; shown masked.",
|
|
282
|
+
secret: true,
|
|
283
|
+
},
|
|
284
|
+
},
|
|
285
|
+
"notifications.telegram.chatId": {
|
|
286
|
+
type: "string",
|
|
287
|
+
default: undefined,
|
|
288
|
+
ui: {
|
|
289
|
+
tab: "notifications",
|
|
290
|
+
label: "Telegram chat ID",
|
|
291
|
+
description:
|
|
292
|
+
"Target chat. For end-to-end per-session delivery this must be a forum-enabled supergroup the bot can manage; a private chat id is only enough for setup/status.",
|
|
293
|
+
},
|
|
294
|
+
},
|
|
295
|
+
"notifications.discord.botToken": {
|
|
296
|
+
type: "string",
|
|
297
|
+
default: undefined,
|
|
298
|
+
ui: {
|
|
299
|
+
tab: "notifications",
|
|
300
|
+
label: "Discord bot token",
|
|
301
|
+
description: "Discord bot token for the notifications adapter. Stored locally; shown masked.",
|
|
302
|
+
secret: true,
|
|
303
|
+
},
|
|
304
|
+
},
|
|
305
|
+
"notifications.discord.channelId": {
|
|
306
|
+
type: "string",
|
|
307
|
+
default: undefined,
|
|
308
|
+
ui: {
|
|
309
|
+
tab: "notifications",
|
|
310
|
+
label: "Discord channel ID",
|
|
311
|
+
description: "Discord channel that receives notifications and routes replies back.",
|
|
312
|
+
},
|
|
313
|
+
},
|
|
314
|
+
"notifications.slack.botToken": {
|
|
315
|
+
type: "string",
|
|
316
|
+
default: undefined,
|
|
317
|
+
ui: {
|
|
318
|
+
tab: "notifications",
|
|
319
|
+
label: "Slack bot token",
|
|
320
|
+
description: "Slack bot token for the notifications adapter. Stored locally; shown masked.",
|
|
321
|
+
secret: true,
|
|
322
|
+
},
|
|
323
|
+
},
|
|
324
|
+
"notifications.slack.channelId": {
|
|
325
|
+
type: "string",
|
|
326
|
+
default: undefined,
|
|
327
|
+
ui: {
|
|
328
|
+
tab: "notifications",
|
|
329
|
+
label: "Slack channel ID",
|
|
330
|
+
description: "Slack channel that receives notifications and routes replies back.",
|
|
331
|
+
},
|
|
332
|
+
},
|
|
333
|
+
"notifications.redact": {
|
|
334
|
+
type: "boolean",
|
|
335
|
+
default: false,
|
|
336
|
+
ui: {
|
|
337
|
+
tab: "notifications",
|
|
338
|
+
label: "Redact",
|
|
339
|
+
description:
|
|
340
|
+
"Strip idle summaries and streamed content before remote delivery. Ask prompts are always sent so they remain answerable.",
|
|
341
|
+
},
|
|
342
|
+
},
|
|
263
343
|
"notifications.verbosity": {
|
|
264
344
|
type: "string",
|
|
265
345
|
default: "lean",
|
|
266
346
|
validate: (value: string) => value === "lean" || value === "verbose",
|
|
347
|
+
ui: {
|
|
348
|
+
tab: "notifications",
|
|
349
|
+
label: "Verbosity",
|
|
350
|
+
description: "How much session context to stream to Telegram.",
|
|
351
|
+
options: [
|
|
352
|
+
{ value: "lean", label: "Lean" },
|
|
353
|
+
{ value: "verbose", label: "Verbose" },
|
|
354
|
+
],
|
|
355
|
+
},
|
|
267
356
|
},
|
|
268
357
|
"notifications.daemon.idleTimeoutMs": {
|
|
269
358
|
type: "number",
|
|
@@ -2126,6 +2215,17 @@ export const SETTINGS_SCHEMA = {
|
|
|
2126
2215
|
ui: { tab: "tools", label: "Read URLs", description: "Allow the read tool to fetch and process URLs" },
|
|
2127
2216
|
},
|
|
2128
2217
|
|
|
2218
|
+
"web.insaneFallback": {
|
|
2219
|
+
type: "boolean",
|
|
2220
|
+
default: false,
|
|
2221
|
+
ui: {
|
|
2222
|
+
tab: "tools",
|
|
2223
|
+
label: "Insane Search Fallback",
|
|
2224
|
+
description:
|
|
2225
|
+
"Opt in to the vendored insane-search escalation for blocked public URL reads (403/WAF/JS-gated). Off by default. Requires preinstalled python3 + curl_cffi (and node + playwright/stealth for the browser phase); changes network posture by enabling TLS/browser impersonation for public pages.",
|
|
2226
|
+
},
|
|
2227
|
+
},
|
|
2228
|
+
|
|
2129
2229
|
"github.enabled": {
|
|
2130
2230
|
type: "boolean",
|
|
2131
2231
|
default: false,
|
|
@@ -2753,6 +2853,7 @@ export const SETTINGS_SCHEMA = {
|
|
|
2753
2853
|
values: [
|
|
2754
2854
|
"auto",
|
|
2755
2855
|
"duckduckgo",
|
|
2856
|
+
"insane",
|
|
2756
2857
|
"exa",
|
|
2757
2858
|
"brave",
|
|
2758
2859
|
"jina",
|
|
@@ -2785,6 +2886,11 @@ export const SETTINGS_SCHEMA = {
|
|
|
2785
2886
|
label: "DuckDuckGo",
|
|
2786
2887
|
description: "Keyless default — no API key or OAuth required",
|
|
2787
2888
|
},
|
|
2889
|
+
{
|
|
2890
|
+
value: "insane",
|
|
2891
|
+
label: "Insane",
|
|
2892
|
+
description: "Keyless safe public-route fallback inspired by upstream insane-search",
|
|
2893
|
+
},
|
|
2788
2894
|
{ value: "exa", label: "Exa", description: "Uses Exa API when EXA_API_KEY is set" },
|
|
2789
2895
|
{ value: "brave", label: "Brave", description: "Requires BRAVE_API_KEY" },
|
|
2790
2896
|
{ value: "jina", label: "Jina", description: "Requires JINA_API_KEY" },
|
|
@@ -3231,6 +3337,14 @@ export interface NotificationsSettings {
|
|
|
3231
3337
|
botToken: string | undefined;
|
|
3232
3338
|
chatId: string | undefined;
|
|
3233
3339
|
};
|
|
3340
|
+
discord: {
|
|
3341
|
+
botToken: string | undefined;
|
|
3342
|
+
channelId: string | undefined;
|
|
3343
|
+
};
|
|
3344
|
+
slack: {
|
|
3345
|
+
botToken: string | undefined;
|
|
3346
|
+
channelId: string | undefined;
|
|
3347
|
+
};
|
|
3234
3348
|
redact: boolean;
|
|
3235
3349
|
daemon: {
|
|
3236
3350
|
idleTimeoutMs: number;
|
|
@@ -47,7 +47,7 @@ Planning artifacts and stage handoffs MUST be persisted through the ralplan CLI
|
|
|
47
47
|
skc ralplan --write --stage <type> --stage_n <N> --artifact "markdown file path or markdown string"
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
Use stage values that match the producer or artifact kind, such as `planner`, `architect`, `critic`, `revision`, `adr`, or `final`. Increment `--stage_n` for each consensus-loop pass. The `--artifact` value may be either a markdown file path prepared outside `.skc/` for ingestion or the markdown content string itself. The native `--write` handler persists markdown under `.skc/_session-{sessionid}/plans/ralplan/<run-id>/stage-<NN>-<stage>.md`, maintains an `index.jsonl` audit log, and for `final` stages additionally writes a `pending-approval.md` copy. Direct `write`, `edit`, or `ast_edit` calls against `.skc/_session-{sessionid}/specs`, `.skc/_session-{sessionid}/plans`, `.skc/_session-{sessionid}/state`, or any other `.skc/` path are forbidden unless an explicit force override is active.
|
|
50
|
+
Use stage values that match the producer or artifact kind, such as `planner`, `architect`, `critic`, `revision`, `post-interview`, `adr`, or `final`. Increment `--stage_n` for each consensus-loop pass. The `--artifact` value may be either a markdown file path prepared outside `.skc/` for ingestion or the markdown content string itself. The native `--write` handler persists markdown under `.skc/_session-{sessionid}/plans/ralplan/<run-id>/stage-<NN>-<stage>.md`, maintains an `index.jsonl` audit log, and for `final` stages additionally writes a `pending-approval.md` copy. Direct `write`, `edit`, or `ast_edit` calls against `.skc/_session-{sessionid}/specs`, `.skc/_session-{sessionid}/plans`, `.skc/_session-{sessionid}/state`, or any other `.skc/` path are forbidden unless an explicit force override is active.
|
|
51
51
|
|
|
52
52
|
While ralplan is active it is a pre-approval planning phase: product-code mutation tools (`write`/`edit`/`ast_edit`) and product-mutating `bash` (e.g. `tee src/...`, redirects into the project tree) are blocked, exactly like deep-interview. Prefer passing the `--artifact` markdown **inline** (the content string) so no scratch file is needed; this is mandatory for restricted role agents (see below). Only the leader, and only when an artifact is too large to pass inline, may stage it as a file in a system temp directory (`os.tmpdir()`/`$TMPDIR`, `/tmp`, `/var/tmp`) outside the project tree and pass that path — never write scratch files into the repo or `.skc/`. Product code is mutated only after the plan is approved and execution begins.
|
|
53
53
|
|
|
@@ -80,9 +80,16 @@ The consensus workflow:
|
|
|
80
80
|
d. Return to Critic evaluation
|
|
81
81
|
e. Repeat this loop until Critic returns `APPROVE` or 5 iterations are reached
|
|
82
82
|
f. If 5 iterations are reached without `APPROVE`, present the best version to the user
|
|
83
|
-
6.
|
|
84
|
-
|
|
85
|
-
|
|
83
|
+
6. **Post-ralplan interview** (intent reconciliation gate): After Critic returns `APPROVE` and before the plan is finalized, reconcile the consensus plan against the user's actual intent. The goal is to make sure ralplan did not silently bake in assumptions that conflict with what the user wants.
|
|
84
|
+
a. **Collect open items** from the run: every assumption the Planner/Architect/Critic resolved by assumption rather than by stated fact, every ambiguity flagged during review, and every decision the loop made without explicit user input. Source these from the persisted `planner`/`architect`/`critic`/`revision` stage artifacts, not from memory.
|
|
85
|
+
b. **Cross-check prior context for conflicts**: glob `.skc/_session-{sessionid}/specs/deep-interview-*.md` and other prior specs/plans/context relevant by topic. For each, list points where the consensus plan contradicts, weakens, or expands beyond a previously crystallized decision, constraint, or non-goal. Cite the conflicting artifact and line/section.
|
|
86
|
+
c. **Reconcile with the user**:
|
|
87
|
+
- *(--interactive only)* Use the `ask` tool to confirm the open assumptions and conflicts **one at a time**, weakest/highest-impact first, polishing intent. If any confirmation reveals that the plan diverges from user intent, route the consolidated correction back into the re-review loop (step 5b Planner revision) and re-run Architect + Critic before returning here. Cap at the same 5-iteration ceiling.
|
|
88
|
+
- *(automated mode)* Do not ask. Embed every unconfirmed assumption and every detected prior-context conflict into the final plan under an **## Intent Reconciliation** section as explicit open confirmations the user must review at the `pending approval` gate, so nothing is silently assumed.
|
|
89
|
+
d. Persist the reconciliation with `skc ralplan --write --stage post-interview --stage_n <N> --artifact "..." --json`, then return the receipt/path plus a compact status (reconciled-clean / reconciled-with-revision / open-confirmations-pending) instead of pasting the full body.
|
|
90
|
+
7. On reconciliation completion, mark the plan `pending approval` unless explicit execution approval has already been captured, persist the ADR/final plan via `skc ralplan --write --stage final --stage_n <N> --artifact "..."`, and do not directly edit `.skc/_session-{sessionid}/plans`. *(--interactive only)* If `--interactive` is set, use the `ask` tool to present the plan with approval options (Approve execution via ultragoal (Recommended) / Approve execution via team (only when tmux-based interactive worker parallelization is required) / Compact then return for execution approval / Request changes / Reject). Final plan must include ADR (Decision, Drivers, Alternatives considered, Why chosen, Consequences, Follow-ups) and, when present, the **## Intent Reconciliation** section. Otherwise, output the final plan and stop before any mutation or delegation.
|
|
91
|
+
8. *(--interactive only)* User chooses: Approve ultragoal execution (recommended), Approve team execution (tmux parallelization only), Request changes, or Reject
|
|
92
|
+
9. *(--interactive only)* On approval: invoke `/skill:ultragoal` for execution by default; invoke `/skill:team` only when the user explicitly needs tmux-based interactive worker parallelization -- never implement directly
|
|
86
93
|
|
|
87
94
|
Before invoking `/skill:team` or `/skill:ultragoal`, mark ralplan ready for handoff so the skill tool's chain guard permits the transition:
|
|
88
95
|
|
|
@@ -10,11 +10,10 @@ import ralplanSkill from "./skc/skills/ralplan/SKILL.md" with { type: "text" };
|
|
|
10
10
|
import teamSkill from "./skc/skills/team/SKILL.md" with { type: "text" };
|
|
11
11
|
import aiSlopCleanerFragment from "./skc/skills/ultragoal/ai-slop-cleaner.md" with { type: "text" };
|
|
12
12
|
import ultragoalSkill from "./skc/skills/ultragoal/SKILL.md" with { type: "text" };
|
|
13
|
-
import ponytailRule from "./skc/rules/ponytail.md" with { type: "text" };
|
|
14
13
|
|
|
15
14
|
export const DEFAULT_SKC_DEFINITION_NAMES = ["deep-interview", "ralplan", "team", "ultragoal"] as const;
|
|
16
15
|
export type DefaultSkcDefinitionName = (typeof DEFAULT_SKC_DEFINITION_NAMES)[number];
|
|
17
|
-
export type DefaultSkcDefinitionKind = "skill" | "skill-fragment"
|
|
16
|
+
export type DefaultSkcDefinitionKind = "skill" | "skill-fragment";
|
|
18
17
|
export type EmbeddedDefaultSkcSkill = {
|
|
19
18
|
name: DefaultSkcDefinitionName;
|
|
20
19
|
description: string;
|
|
@@ -40,17 +39,7 @@ export interface DefaultSkcSkillFragmentDefinition {
|
|
|
40
39
|
content: string;
|
|
41
40
|
}
|
|
42
41
|
|
|
43
|
-
export
|
|
44
|
-
kind: "rule";
|
|
45
|
-
name: string;
|
|
46
|
-
relativePath: string;
|
|
47
|
-
content: string;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export type DefaultSkcDefinition =
|
|
51
|
-
| DefaultSkcSkillDefinition
|
|
52
|
-
| DefaultSkcSkillFragmentDefinition
|
|
53
|
-
| DefaultSkcRuleDefinition;
|
|
42
|
+
export type DefaultSkcDefinition = DefaultSkcSkillDefinition | DefaultSkcSkillFragmentDefinition;
|
|
54
43
|
|
|
55
44
|
export interface InstallDefaultSkcDefinitionsOptions {
|
|
56
45
|
check?: boolean;
|
|
@@ -70,12 +59,6 @@ export type DefaultSkcDefinitionInstallFile =
|
|
|
70
59
|
parentSkillName: DefaultSkcDefinitionName;
|
|
71
60
|
path: string;
|
|
72
61
|
status: DefaultSkcInstallStatus;
|
|
73
|
-
}
|
|
74
|
-
| {
|
|
75
|
-
kind: "rule";
|
|
76
|
-
name: string;
|
|
77
|
-
path: string;
|
|
78
|
-
status: DefaultSkcInstallStatus;
|
|
79
62
|
};
|
|
80
63
|
|
|
81
64
|
export interface DefaultSkcDefinitionInstallResult {
|
|
@@ -123,7 +106,6 @@ const DEFAULT_SKC_DEFINITIONS: readonly DefaultSkcDefinition[] = [
|
|
|
123
106
|
relativePath: "skill-fragments/ultragoal/ai-slop-cleaner.md",
|
|
124
107
|
content: aiSlopCleanerFragment,
|
|
125
108
|
},
|
|
126
|
-
{ kind: "rule", name: "ponytail", relativePath: "rules/ponytail.md", content: ponytailRule },
|
|
127
109
|
];
|
|
128
110
|
|
|
129
111
|
export function getDefaultSkcDefinitions(): readonly DefaultSkcDefinition[] {
|
|
@@ -192,13 +174,6 @@ export async function installDefaultSkcDefinitions(
|
|
|
192
174
|
path: destination,
|
|
193
175
|
status,
|
|
194
176
|
});
|
|
195
|
-
} else if (definition.kind === "rule") {
|
|
196
|
-
files.push({
|
|
197
|
-
kind: definition.kind,
|
|
198
|
-
name: definition.name,
|
|
199
|
-
path: destination,
|
|
200
|
-
status,
|
|
201
|
-
});
|
|
202
177
|
} else {
|
|
203
178
|
files.push({
|
|
204
179
|
kind: definition.kind,
|
|
@@ -217,6 +217,7 @@ export interface AutoCompactionEndEvent {
|
|
|
217
217
|
errorMessage?: string;
|
|
218
218
|
/** True when compaction was skipped for a benign reason (no model, no candidates, nothing to compact). */
|
|
219
219
|
skipped?: boolean;
|
|
220
|
+
continuationSkipReason?: "auto_continue_disabled_non_resumable_tail";
|
|
220
221
|
}
|
|
221
222
|
|
|
222
223
|
/** Fired when auto-retry starts */
|
package/src/i18n/messages/de.ts
CHANGED
|
@@ -30,6 +30,7 @@ export const de: Partial<Record<MsgKey, string>> = {
|
|
|
30
30
|
"settings.tab.appearance": "Darstellung",
|
|
31
31
|
"settings.tab.model": "Modell",
|
|
32
32
|
"settings.tab.interaction": "Interaktion",
|
|
33
|
+
"settings.tab.notifications": "Benachrichtigungen",
|
|
33
34
|
"settings.tab.context": "Kontext",
|
|
34
35
|
"settings.tab.memory": "Speicher",
|
|
35
36
|
"settings.tab.editing": "Bearbeitung",
|
package/src/i18n/messages/en.ts
CHANGED
|
@@ -37,6 +37,7 @@ export const en = {
|
|
|
37
37
|
"settings.tab.appearance": "Appearance",
|
|
38
38
|
"settings.tab.model": "Model",
|
|
39
39
|
"settings.tab.interaction": "Interaction",
|
|
40
|
+
"settings.tab.notifications": "Notifications",
|
|
40
41
|
"settings.tab.context": "Context",
|
|
41
42
|
"settings.tab.memory": "Memory",
|
|
42
43
|
"settings.tab.editing": "Editing",
|
package/src/i18n/messages/es.ts
CHANGED
|
@@ -30,6 +30,7 @@ export const es: Partial<Record<MsgKey, string>> = {
|
|
|
30
30
|
"settings.tab.appearance": "Apariencia",
|
|
31
31
|
"settings.tab.model": "Modelo",
|
|
32
32
|
"settings.tab.interaction": "Interacción",
|
|
33
|
+
"settings.tab.notifications": "Notificaciones",
|
|
33
34
|
"settings.tab.context": "Contexto",
|
|
34
35
|
"settings.tab.memory": "Memoria",
|
|
35
36
|
"settings.tab.editing": "Edición",
|
package/src/i18n/messages/fr.ts
CHANGED
|
@@ -30,6 +30,7 @@ export const fr: Partial<Record<MsgKey, string>> = {
|
|
|
30
30
|
"settings.tab.appearance": "Apparence",
|
|
31
31
|
"settings.tab.model": "Modèle",
|
|
32
32
|
"settings.tab.interaction": "Interaction",
|
|
33
|
+
"settings.tab.notifications": "Notifications",
|
|
33
34
|
"settings.tab.context": "Contexte",
|
|
34
35
|
"settings.tab.memory": "Mémoire",
|
|
35
36
|
"settings.tab.editing": "Édition",
|
package/src/i18n/messages/ja.ts
CHANGED
|
@@ -28,6 +28,7 @@ export const ja: Partial<Record<MsgKey, string>> = {
|
|
|
28
28
|
"settings.tab.appearance": "外観",
|
|
29
29
|
"settings.tab.model": "モデル",
|
|
30
30
|
"settings.tab.interaction": "操作",
|
|
31
|
+
"settings.tab.notifications": "通知",
|
|
31
32
|
"settings.tab.context": "コンテキスト",
|
|
32
33
|
"settings.tab.memory": "メモリ",
|
|
33
34
|
"settings.tab.editing": "編集",
|
package/src/i18n/messages/ko.ts
CHANGED
|
@@ -28,6 +28,7 @@ export const ko: Partial<Record<MsgKey, string>> = {
|
|
|
28
28
|
"settings.tab.appearance": "외관",
|
|
29
29
|
"settings.tab.model": "모델",
|
|
30
30
|
"settings.tab.interaction": "상호작용",
|
|
31
|
+
"settings.tab.notifications": "알림",
|
|
31
32
|
"settings.tab.context": "컨텍스트",
|
|
32
33
|
"settings.tab.memory": "메모리",
|
|
33
34
|
"settings.tab.editing": "편집",
|
package/src/i18n/messages/zh.ts
CHANGED
|
@@ -28,6 +28,7 @@ export const zh: Partial<Record<MsgKey, string>> = {
|
|
|
28
28
|
"settings.tab.appearance": "外观",
|
|
29
29
|
"settings.tab.model": "模型",
|
|
30
30
|
"settings.tab.interaction": "交互",
|
|
31
|
+
"settings.tab.notifications": "通知",
|
|
31
32
|
"settings.tab.context": "上下文",
|
|
32
33
|
"settings.tab.memory": "记忆",
|
|
33
34
|
"settings.tab.editing": "编辑",
|