@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
|
@@ -286,6 +286,8 @@ import { ToolChoiceQueue } from "./tool-choice-queue";
|
|
|
286
286
|
import { YieldQueue } from "./yield-queue";
|
|
287
287
|
|
|
288
288
|
/** Session-specific events that extend the core AgentEvent */
|
|
289
|
+
export type AutoCompactionContinuationSkipReason = "auto_continue_disabled_non_resumable_tail";
|
|
290
|
+
|
|
289
291
|
export type AgentSessionEvent =
|
|
290
292
|
| AgentEvent
|
|
291
293
|
| { type: "auto_compaction_start"; reason: "threshold" | "overflow" | "idle"; action: "context-full" | "handoff" }
|
|
@@ -298,6 +300,7 @@ export type AgentSessionEvent =
|
|
|
298
300
|
errorMessage?: string;
|
|
299
301
|
/** True when compaction was skipped for a benign reason (no model, no candidates, nothing to compact). */
|
|
300
302
|
skipped?: boolean;
|
|
303
|
+
continuationSkipReason?: AutoCompactionContinuationSkipReason;
|
|
301
304
|
}
|
|
302
305
|
| {
|
|
303
306
|
type: "auto_retry_start";
|
|
@@ -1869,7 +1872,6 @@ export class AgentSession {
|
|
|
1869
1872
|
this.#resolveTtsrResume();
|
|
1870
1873
|
return;
|
|
1871
1874
|
}
|
|
1872
|
-
this.#ttsrAbortPending = false;
|
|
1873
1875
|
this.#perToolTtsrInjections.clear();
|
|
1874
1876
|
const ttsrSettings = this.#ttsrManager?.getSettings();
|
|
1875
1877
|
if (ttsrSettings?.contextMode === "discard" && targetAssistantIndex !== -1) {
|
|
@@ -1898,11 +1900,22 @@ export class AgentSession {
|
|
|
1898
1900
|
);
|
|
1899
1901
|
this.#markTtsrInjected(details.rules);
|
|
1900
1902
|
}
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1903
|
+
await this.#scheduleAgentContinue({
|
|
1904
|
+
delayMs: 0,
|
|
1905
|
+
generation,
|
|
1906
|
+
shouldContinue: () => {
|
|
1907
|
+
this.#ttsrAbortPending = false;
|
|
1908
|
+
return true;
|
|
1909
|
+
},
|
|
1910
|
+
onSkip: () => {
|
|
1911
|
+
this.#ttsrAbortPending = false;
|
|
1912
|
+
this.#resolveTtsrResume();
|
|
1913
|
+
},
|
|
1914
|
+
onError: () => {
|
|
1915
|
+
this.#ttsrAbortPending = false;
|
|
1916
|
+
this.#resolveTtsrResume();
|
|
1917
|
+
},
|
|
1918
|
+
});
|
|
1906
1919
|
},
|
|
1907
1920
|
{ delayMs: 50 },
|
|
1908
1921
|
);
|
|
@@ -2214,7 +2227,7 @@ export class AgentSession {
|
|
|
2214
2227
|
#schedulePostPromptTask(
|
|
2215
2228
|
task: (signal: AbortSignal) => Promise<void>,
|
|
2216
2229
|
options?: { delayMs?: number; generation?: number; onSkip?: () => void },
|
|
2217
|
-
): void {
|
|
2230
|
+
): Promise<void> {
|
|
2218
2231
|
const delayMs = options?.delayMs ?? 0;
|
|
2219
2232
|
const signal = this.#postPromptTasksAbortController.signal;
|
|
2220
2233
|
const scheduled = (async () => {
|
|
@@ -2236,18 +2249,20 @@ export class AgentSession {
|
|
|
2236
2249
|
await task(signal);
|
|
2237
2250
|
})();
|
|
2238
2251
|
this.#trackPostPromptTask(scheduled);
|
|
2252
|
+
return scheduled;
|
|
2239
2253
|
}
|
|
2240
2254
|
|
|
2241
2255
|
#scheduleAgentContinue(options?: {
|
|
2242
2256
|
delayMs?: number;
|
|
2243
2257
|
generation?: number;
|
|
2258
|
+
skipCompactionCheck?: boolean;
|
|
2244
2259
|
shouldContinue?: () => boolean;
|
|
2245
2260
|
onSkip?: (reason: "generation_changed" | "aborted_signal" | "queue_drained") => void;
|
|
2246
2261
|
onError?: (error: unknown) => void;
|
|
2247
|
-
}): void {
|
|
2262
|
+
}): Promise<void> {
|
|
2248
2263
|
const scheduledGeneration = options?.generation;
|
|
2249
2264
|
const signal = this.#postPromptTasksAbortController.signal;
|
|
2250
|
-
this.#schedulePostPromptTask(
|
|
2265
|
+
return this.#schedulePostPromptTask(
|
|
2251
2266
|
async () => {
|
|
2252
2267
|
if (signal.aborted) {
|
|
2253
2268
|
options?.onSkip?.("aborted_signal");
|
|
@@ -2263,6 +2278,9 @@ export class AgentSession {
|
|
|
2263
2278
|
}
|
|
2264
2279
|
try {
|
|
2265
2280
|
await this.#maybeRestoreRetryFallbackPrimary();
|
|
2281
|
+
if (!options?.skipCompactionCheck) {
|
|
2282
|
+
await this.#checkEstimatedContextBeforePrompt();
|
|
2283
|
+
}
|
|
2266
2284
|
await this.agent.continue();
|
|
2267
2285
|
} catch (error) {
|
|
2268
2286
|
logger.warn("agent.continue failed after scheduling", {
|
|
@@ -2307,6 +2325,34 @@ export class AgentSession {
|
|
|
2307
2325
|
}
|
|
2308
2326
|
}
|
|
2309
2327
|
|
|
2328
|
+
#detectOverflowRetryContinuationSkip(): AutoCompactionContinuationSkipReason | undefined {
|
|
2329
|
+
this.#stripOverflowFailedTurnForRetry();
|
|
2330
|
+
if (this.#isResumableAgentTail()) return undefined;
|
|
2331
|
+
const compactionSettings = this.settings.getGroup("compaction");
|
|
2332
|
+
return compactionSettings.autoContinue === false ? "auto_continue_disabled_non_resumable_tail" : undefined;
|
|
2333
|
+
}
|
|
2334
|
+
|
|
2335
|
+
#scheduleOverflowRetryContinuation(generation: number): void {
|
|
2336
|
+
this.#stripOverflowFailedTurnForRetry();
|
|
2337
|
+
if (this.#isResumableAgentTail()) {
|
|
2338
|
+
this.#scheduleAgentContinue({
|
|
2339
|
+
delayMs: 100,
|
|
2340
|
+
generation,
|
|
2341
|
+
onSkip: reason => this.#logCompactionContinuationSkipped("overflow_retry", reason),
|
|
2342
|
+
onError: error => this.#logCompactionContinuationError("overflow_retry", error),
|
|
2343
|
+
});
|
|
2344
|
+
return;
|
|
2345
|
+
}
|
|
2346
|
+
|
|
2347
|
+
const compactionSettings = this.settings.getGroup("compaction");
|
|
2348
|
+
if (compactionSettings.autoContinue !== false) {
|
|
2349
|
+
this.#scheduleAutoContinuePrompt(generation);
|
|
2350
|
+
return;
|
|
2351
|
+
}
|
|
2352
|
+
|
|
2353
|
+
this.#logCompactionContinuationSkipped("overflow_retry", "auto_continue_disabled_non_resumable_tail");
|
|
2354
|
+
}
|
|
2355
|
+
|
|
2310
2356
|
#scheduleAutoContinuePrompt(generation: number): void {
|
|
2311
2357
|
const continuePrompt = async () => {
|
|
2312
2358
|
await this.#promptWithMessage(
|
|
@@ -3052,6 +3098,7 @@ export class AgentSession {
|
|
|
3052
3098
|
willRetry: event.willRetry,
|
|
3053
3099
|
errorMessage: event.errorMessage,
|
|
3054
3100
|
skipped: event.skipped,
|
|
3101
|
+
continuationSkipReason: event.continuationSkipReason,
|
|
3055
3102
|
});
|
|
3056
3103
|
} else if (event.type === "auto_retry_start") {
|
|
3057
3104
|
await this.#extensionRunner.emit({
|
|
@@ -6722,6 +6769,8 @@ export class AgentSession {
|
|
|
6722
6769
|
const compactionSettings = this.settings.getGroup("compaction");
|
|
6723
6770
|
if (compactionSettings.enabled && compactionSettings.strategy !== "off") {
|
|
6724
6771
|
await this.#runAutoCompaction("overflow", true);
|
|
6772
|
+
} else {
|
|
6773
|
+
this.#scheduleOverflowRetryContinuation(generation);
|
|
6725
6774
|
}
|
|
6726
6775
|
return;
|
|
6727
6776
|
}
|
|
@@ -6732,17 +6781,19 @@ export class AgentSession {
|
|
|
6732
6781
|
// Skip if this was an error (non-overflow errors don't have usage data)
|
|
6733
6782
|
if (assistantMessage.stopReason === "error") return;
|
|
6734
6783
|
let contextTokens = calculateContextTokens(assistantMessage.usage);
|
|
6735
|
-
|
|
6784
|
+
// Model maxTokens is a capability ceiling, not a per-turn reservation.
|
|
6785
|
+
// Auto maintenance should track actual context fullness.
|
|
6786
|
+
const autoCompactionOutputReserveTokens = 0;
|
|
6736
6787
|
// Cache-epoch invariant: pruning rewrites already-sent toolResult history,
|
|
6737
6788
|
// which breaks the provider prompt-cache prefix mid-epoch. Only prune at a
|
|
6738
6789
|
// sanctioned maintenance boundary, i.e. when the un-pruned context already
|
|
6739
6790
|
// crosses the compaction threshold. Pruning may then avert full compaction.
|
|
6740
|
-
if (!shouldCompact(contextTokens, contextWindow, compactionSettings,
|
|
6791
|
+
if (!shouldCompact(contextTokens, contextWindow, compactionSettings, autoCompactionOutputReserveTokens)) return;
|
|
6741
6792
|
const pruneResult = await this.#pruneToolOutputs();
|
|
6742
6793
|
if (pruneResult) {
|
|
6743
6794
|
contextTokens = Math.max(0, contextTokens - pruneResult.tokensSaved);
|
|
6744
6795
|
}
|
|
6745
|
-
if (shouldCompact(contextTokens, contextWindow, compactionSettings,
|
|
6796
|
+
if (shouldCompact(contextTokens, contextWindow, compactionSettings, autoCompactionOutputReserveTokens)) {
|
|
6746
6797
|
// Try promotion first — if a larger model is available, switch instead of compacting
|
|
6747
6798
|
const promoted = await this.#tryContextPromotion(assistantMessage);
|
|
6748
6799
|
if (!promoted) {
|
|
@@ -6820,14 +6871,16 @@ export class AgentSession {
|
|
|
6820
6871
|
if (!compactionSettings.enabled || compactionSettings.strategy === "off") return;
|
|
6821
6872
|
|
|
6822
6873
|
let contextTokens = this.#estimateContextTokensForCompaction(pendingMessages).tokens;
|
|
6823
|
-
|
|
6824
|
-
|
|
6874
|
+
// Model maxTokens is a capability ceiling, not a per-turn reservation.
|
|
6875
|
+
// Auto maintenance should track actual context fullness.
|
|
6876
|
+
const autoCompactionOutputReserveTokens = 0;
|
|
6877
|
+
if (!shouldCompact(contextTokens, contextWindow, compactionSettings, autoCompactionOutputReserveTokens)) return;
|
|
6825
6878
|
|
|
6826
6879
|
const pruneResult = await this.#pruneToolOutputs();
|
|
6827
6880
|
if (pruneResult) {
|
|
6828
6881
|
contextTokens = Math.max(0, contextTokens - pruneResult.tokensSaved);
|
|
6829
6882
|
}
|
|
6830
|
-
if (shouldCompact(contextTokens, contextWindow, compactionSettings,
|
|
6883
|
+
if (shouldCompact(contextTokens, contextWindow, compactionSettings, autoCompactionOutputReserveTokens)) {
|
|
6831
6884
|
await this.#runAutoCompaction("threshold", false, false, {
|
|
6832
6885
|
continueAfterMaintenance: false,
|
|
6833
6886
|
deferHandoffMaintenance: false,
|
|
@@ -7730,32 +7783,18 @@ export class AgentSession {
|
|
|
7730
7783
|
|
|
7731
7784
|
const preparation = prepareCompaction(pathEntries, compactionSettings);
|
|
7732
7785
|
if (!preparation) {
|
|
7786
|
+
const continuationSkipReason = willRetry ? this.#detectOverflowRetryContinuationSkip() : undefined;
|
|
7733
7787
|
await this.#emitSessionEvent({
|
|
7734
7788
|
type: "auto_compaction_end",
|
|
7735
7789
|
action,
|
|
7736
7790
|
result: undefined,
|
|
7737
7791
|
aborted: false,
|
|
7738
|
-
willRetry:
|
|
7792
|
+
willRetry: willRetry && !continuationSkipReason,
|
|
7739
7793
|
skipped: true,
|
|
7794
|
+
continuationSkipReason,
|
|
7740
7795
|
});
|
|
7741
7796
|
if (willRetry) {
|
|
7742
|
-
this.#
|
|
7743
|
-
if (this.#isResumableAgentTail()) {
|
|
7744
|
-
this.#scheduleAgentContinue({
|
|
7745
|
-
delayMs: 100,
|
|
7746
|
-
generation,
|
|
7747
|
-
onSkip: skipReason => this.#logCompactionContinuationSkipped("overflow_retry", skipReason),
|
|
7748
|
-
onError: error => this.#logCompactionContinuationError("overflow_retry", error),
|
|
7749
|
-
});
|
|
7750
|
-
} else {
|
|
7751
|
-
const tail = this.agent.state.messages.at(-1) as AssistantMessage | undefined;
|
|
7752
|
-
logger.warn("Auto-compaction continuation skipped", {
|
|
7753
|
-
source: "overflow_retry",
|
|
7754
|
-
reason: "not_resumable_tail",
|
|
7755
|
-
role: tail?.role,
|
|
7756
|
-
stopReason: tail?.stopReason,
|
|
7757
|
-
});
|
|
7758
|
-
}
|
|
7797
|
+
this.#scheduleOverflowRetryContinuation(generation);
|
|
7759
7798
|
} else if (continueAfterMaintenance && reason !== "idle" && this.agent.hasQueuedMessages()) {
|
|
7760
7799
|
this.#scheduleAgentContinue({
|
|
7761
7800
|
delayMs: 100,
|
|
@@ -7966,26 +8005,18 @@ export class AgentSession {
|
|
|
7966
8005
|
details,
|
|
7967
8006
|
preserveData,
|
|
7968
8007
|
};
|
|
7969
|
-
|
|
8008
|
+
const continuationSkipReason = willRetry ? this.#detectOverflowRetryContinuationSkip() : undefined;
|
|
8009
|
+
await this.#emitSessionEvent({
|
|
8010
|
+
type: "auto_compaction_end",
|
|
8011
|
+
action,
|
|
8012
|
+
result,
|
|
8013
|
+
aborted: false,
|
|
8014
|
+
willRetry: willRetry && !continuationSkipReason,
|
|
8015
|
+
continuationSkipReason,
|
|
8016
|
+
});
|
|
7970
8017
|
|
|
7971
8018
|
if (willRetry) {
|
|
7972
|
-
this.#
|
|
7973
|
-
if (!this.#isResumableAgentTail()) {
|
|
7974
|
-
const tail = this.agent.state.messages.at(-1) as AssistantMessage | undefined;
|
|
7975
|
-
logger.warn("Auto-compaction continuation skipped", {
|
|
7976
|
-
source: "overflow_retry",
|
|
7977
|
-
reason: "not_resumable_tail",
|
|
7978
|
-
role: tail?.role,
|
|
7979
|
-
stopReason: tail?.stopReason,
|
|
7980
|
-
});
|
|
7981
|
-
} else {
|
|
7982
|
-
this.#scheduleAgentContinue({
|
|
7983
|
-
delayMs: 100,
|
|
7984
|
-
generation,
|
|
7985
|
-
onSkip: reason => this.#logCompactionContinuationSkipped("overflow_retry", reason),
|
|
7986
|
-
onError: error => this.#logCompactionContinuationError("overflow_retry", error),
|
|
7987
|
-
});
|
|
7988
|
-
}
|
|
8019
|
+
this.#scheduleOverflowRetryContinuation(generation);
|
|
7989
8020
|
} else if (continueAfterMaintenance && reason !== "idle" && this.agent.hasQueuedMessages()) {
|
|
7990
8021
|
// Auto-compaction can complete while follow-up/steering/custom messages are waiting.
|
|
7991
8022
|
// Kick the loop so queued messages are actually delivered.
|
|
@@ -93,6 +93,10 @@ function hasCurrentSkcVersion(session: SkcTmuxSessionStatus | undefined): boolea
|
|
|
93
93
|
return session?.version === VERSION;
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
+
function allowsExistingTmuxAttach(parsed: Args, env: NodeJS.ProcessEnv): boolean {
|
|
97
|
+
return Boolean(parsed.continue || parsed.resume || explicitTmuxSessionName(env));
|
|
98
|
+
}
|
|
99
|
+
|
|
96
100
|
function findExistingSessionForLaunch(context: {
|
|
97
101
|
env: NodeJS.ProcessEnv;
|
|
98
102
|
project: string;
|
|
@@ -352,6 +356,11 @@ function readCurrentBranch(cwd: string): string | null {
|
|
|
352
356
|
function cleanupCreatedTmuxSession(plan: TmuxLaunchPlan, spawnSync: TmuxSpawnSync, options: TmuxSpawnOptions): void {
|
|
353
357
|
spawnSync(plan.tmuxCommand, ["kill-session", "-t", `=${plan.sessionName}`], options);
|
|
354
358
|
}
|
|
359
|
+
function isTmuxAttachDisconnectError(result: TmuxSpawnResult): boolean {
|
|
360
|
+
if (result.signalCode === "SIGHUP") return true;
|
|
361
|
+
const stderr = result.stderr?.toLowerCase() ?? "";
|
|
362
|
+
return stderr.includes("eio") || stderr.includes("input/output error");
|
|
363
|
+
}
|
|
355
364
|
|
|
356
365
|
export function buildDefaultTmuxLaunchPlan(context: TmuxLaunchContext): TmuxLaunchPlan | undefined {
|
|
357
366
|
const env = context.env ?? process.env;
|
|
@@ -377,14 +386,15 @@ export function buildDefaultTmuxLaunchPlan(context: TmuxLaunchContext): TmuxLaun
|
|
|
377
386
|
tmuxRuntimeSessionPath(cwd, skcSessionId, buildSkcTmuxSessionSlug(sessionName));
|
|
378
387
|
const tmuxAvailable = context.tmuxAvailable ?? Bun.which(tmuxCommand) !== null;
|
|
379
388
|
if (!tmuxAvailable) return undefined;
|
|
380
|
-
const existingSessionName =
|
|
381
|
-
"existingBranchSessionName" in context
|
|
389
|
+
const existingSessionName = allowsExistingTmuxAttach(context.parsed, env)
|
|
390
|
+
? "existingBranchSessionName" in context
|
|
382
391
|
? (context.existingBranchSessionName ?? undefined)
|
|
383
392
|
: findExistingSessionForLaunch({
|
|
384
393
|
env,
|
|
385
394
|
project,
|
|
386
395
|
branch,
|
|
387
|
-
})
|
|
396
|
+
})
|
|
397
|
+
: undefined;
|
|
388
398
|
const innerCommand = buildInnerCommand(
|
|
389
399
|
{
|
|
390
400
|
cwd,
|
|
@@ -478,6 +488,10 @@ export function launchDefaultTmuxIfNeeded(context: TmuxLaunchContext): boolean {
|
|
|
478
488
|
if (created.exitCode !== 0) return false;
|
|
479
489
|
const attached = spawnSync(plan.tmuxCommand, ["attach-session", "-t", `=${plan.sessionName}`], options);
|
|
480
490
|
if (attached.exitCode === 0) return true;
|
|
491
|
+
if (isTmuxAttachDisconnectError(attached)) {
|
|
492
|
+
(context.diagnosticWriter ?? safeStderrWrite)(formatTmuxLaunchDiagnostic("attach disconnected", attached.stderr));
|
|
493
|
+
return true;
|
|
494
|
+
}
|
|
481
495
|
cleanupCreatedTmuxSession(plan, spawnSync, options);
|
|
482
496
|
(context.diagnosticWriter ?? safeStderrWrite)(formatTmuxLaunchDiagnostic("attach failed", attached.stderr));
|
|
483
497
|
return true;
|
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
*
|
|
38
38
|
* 2. **Artifact write**: `skc ralplan --write --stage <type> --stage_n <N> --artifact
|
|
39
39
|
* <path-or-string> [--run-id <id>] [--session-id <id>] [--json]` persists Planner / Architect
|
|
40
|
-
* / Critic / revision / ADR / final markdown under `.skc/plans/ralplan/<run-id>/`, maintains
|
|
40
|
+
* / Critic / revision / post-interview / ADR / final markdown under `.skc/plans/ralplan/<run-id>/`, maintains
|
|
41
41
|
* an `index.jsonl` audit log, copies `final` stages to `pending-approval.md`, and advances
|
|
42
42
|
* the HUD chip to reflect the latest persisted stage.
|
|
43
43
|
*/
|
|
@@ -48,7 +48,7 @@ export interface RalplanCommandResult {
|
|
|
48
48
|
stderr?: string;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
const KNOWN_STAGES = ["planner", "architect", "critic", "revision", "adr", "final"] as const;
|
|
51
|
+
const KNOWN_STAGES = ["planner", "architect", "critic", "revision", "post-interview", "adr", "final"] as const;
|
|
52
52
|
type RalplanStage = (typeof KNOWN_STAGES)[number];
|
|
53
53
|
|
|
54
54
|
const KNOWN_ARCHITECT_KINDS = new Set(["openai-code"]);
|
|
@@ -369,6 +369,9 @@
|
|
|
369
369
|
{
|
|
370
370
|
"id": "revision"
|
|
371
371
|
},
|
|
372
|
+
{
|
|
373
|
+
"id": "post-interview"
|
|
374
|
+
},
|
|
372
375
|
{
|
|
373
376
|
"id": "adr"
|
|
374
377
|
},
|
|
@@ -401,6 +404,26 @@
|
|
|
401
404
|
"to": "revision",
|
|
402
405
|
"verb": "write-artifact"
|
|
403
406
|
},
|
|
407
|
+
{
|
|
408
|
+
"from": "revision",
|
|
409
|
+
"to": "post-interview",
|
|
410
|
+
"verb": "write-artifact"
|
|
411
|
+
},
|
|
412
|
+
{
|
|
413
|
+
"from": "critic",
|
|
414
|
+
"to": "post-interview",
|
|
415
|
+
"verb": "write-artifact"
|
|
416
|
+
},
|
|
417
|
+
{
|
|
418
|
+
"from": "post-interview",
|
|
419
|
+
"to": "revision",
|
|
420
|
+
"verb": "write-artifact"
|
|
421
|
+
},
|
|
422
|
+
{
|
|
423
|
+
"from": "post-interview",
|
|
424
|
+
"to": "adr",
|
|
425
|
+
"verb": "write-artifact"
|
|
426
|
+
},
|
|
404
427
|
{
|
|
405
428
|
"from": "revision",
|
|
406
429
|
"to": "adr",
|
|
@@ -435,6 +458,11 @@
|
|
|
435
458
|
"from": "adr",
|
|
436
459
|
"to": "handoff",
|
|
437
460
|
"verb": "handoff"
|
|
461
|
+
},
|
|
462
|
+
{
|
|
463
|
+
"from": "post-interview",
|
|
464
|
+
"to": "handoff",
|
|
465
|
+
"verb": "handoff"
|
|
438
466
|
}
|
|
439
467
|
],
|
|
440
468
|
"typedArgs": [
|
|
@@ -592,6 +620,7 @@
|
|
|
592
620
|
"architect",
|
|
593
621
|
"critic",
|
|
594
622
|
"revision",
|
|
623
|
+
"post-interview",
|
|
595
624
|
"adr",
|
|
596
625
|
"final"
|
|
597
626
|
],
|
|
@@ -172,12 +172,16 @@ export const WORKFLOW_MANIFEST: Record<CanonicalSkcWorkflowSkill, SkillManifest>
|
|
|
172
172
|
}),
|
|
173
173
|
ralplan: manifest({
|
|
174
174
|
skill: "ralplan",
|
|
175
|
-
states: ["planner", "architect", "critic", "revision", "adr", "final", "handoff"],
|
|
175
|
+
states: ["planner", "architect", "critic", "revision", "post-interview", "adr", "final", "handoff"],
|
|
176
176
|
terminalStates: ["final", "handoff"],
|
|
177
177
|
transitions: [
|
|
178
178
|
{ from: "planner", to: "architect", verb: "write-artifact" },
|
|
179
179
|
{ from: "architect", to: "critic", verb: "write-artifact" },
|
|
180
180
|
{ from: "critic", to: "revision", verb: "write-artifact" },
|
|
181
|
+
{ from: "revision", to: "post-interview", verb: "write-artifact" },
|
|
182
|
+
{ from: "critic", to: "post-interview", verb: "write-artifact" },
|
|
183
|
+
{ from: "post-interview", to: "revision", verb: "write-artifact" },
|
|
184
|
+
{ from: "post-interview", to: "adr", verb: "write-artifact" },
|
|
181
185
|
{ from: "revision", to: "adr", verb: "write-artifact" },
|
|
182
186
|
{ from: "adr", to: "final", verb: "write-artifact" },
|
|
183
187
|
{ from: "planner", to: "handoff", verb: "handoff" },
|
|
@@ -185,6 +189,7 @@ export const WORKFLOW_MANIFEST: Record<CanonicalSkcWorkflowSkill, SkillManifest>
|
|
|
185
189
|
{ from: "critic", to: "handoff", verb: "handoff" },
|
|
186
190
|
{ from: "revision", to: "handoff", verb: "handoff" },
|
|
187
191
|
{ from: "adr", to: "handoff", verb: "handoff" },
|
|
192
|
+
{ from: "post-interview", to: "handoff", verb: "handoff" },
|
|
188
193
|
],
|
|
189
194
|
verbs: [...stateVerbs(), ...flagVerbs(["kickoff", "write-artifact"]), ...plannedVerbs(PLANNED_ADMIN_VERBS)],
|
|
190
195
|
typedArgs: [
|
|
@@ -196,7 +201,7 @@ export const WORKFLOW_MANIFEST: Record<CanonicalSkcWorkflowSkill, SkillManifest>
|
|
|
196
201
|
{
|
|
197
202
|
name: "stage",
|
|
198
203
|
type: "enum",
|
|
199
|
-
enumValues: ["planner", "architect", "critic", "revision", "adr", "final"],
|
|
204
|
+
enumValues: ["planner", "architect", "critic", "revision", "post-interview", "adr", "final"],
|
|
200
205
|
appliesToVerbs: ["write-artifact"],
|
|
201
206
|
},
|
|
202
207
|
{ name: "stage_n", type: "number", appliesToVerbs: ["write-artifact"] },
|
package/src/tools/fetch.ts
CHANGED
|
@@ -16,6 +16,8 @@ import { renderStatusLine } from "../tui";
|
|
|
16
16
|
import { CachedOutputBlock } from "../tui/output-block";
|
|
17
17
|
import { formatDimensionNote, resizeImage } from "../utils/image-resize";
|
|
18
18
|
import { ensureTool } from "../utils/tools-manager";
|
|
19
|
+
import { INSANE_NOTES, tryInsaneFetch } from "../web/insane/bridge";
|
|
20
|
+
import { validatePublicHttpUrlForInsane } from "../web/insane/url-guard";
|
|
19
21
|
import { extractWithParallel, findParallelApiKey, getParallelExtractContent } from "../web/parallel";
|
|
20
22
|
import { specialHandlers } from "../web/scrapers";
|
|
21
23
|
import type { RenderResult } from "../web/scrapers/types";
|
|
@@ -705,6 +707,55 @@ async function handleSpecialUrls(
|
|
|
705
707
|
// Main Render Function
|
|
706
708
|
// =============================================================================
|
|
707
709
|
|
|
710
|
+
/**
|
|
711
|
+
* Opt-in insane-search fallback for blocked / degraded public URL reads.
|
|
712
|
+
*
|
|
713
|
+
* Returns a finalized `method: "insane"` result on success, or null (so the
|
|
714
|
+
* caller continues with its normal degraded behavior). Fail-closed: no note,
|
|
715
|
+
* guard DNS, dependency probe, or subprocess when raw mode or the opt-in
|
|
716
|
+
* setting is off. The public-URL guard runs BEFORE any probe/spawn.
|
|
717
|
+
*/
|
|
718
|
+
export async function tryInsaneFallback(args: {
|
|
719
|
+
url: string;
|
|
720
|
+
finalUrl: string;
|
|
721
|
+
timeout: number;
|
|
722
|
+
raw: boolean;
|
|
723
|
+
settings: Settings;
|
|
724
|
+
signal: AbortSignal | undefined;
|
|
725
|
+
fetchedAt: string;
|
|
726
|
+
notes: string[];
|
|
727
|
+
}): Promise<FetchRenderResult | null> {
|
|
728
|
+
if (args.raw) return null;
|
|
729
|
+
if (args.settings.get("web.insaneFallback") !== true) return null;
|
|
730
|
+
|
|
731
|
+
const target = args.finalUrl || args.url;
|
|
732
|
+
const guard = await validatePublicHttpUrlForInsane(target);
|
|
733
|
+
if (!guard.ok) {
|
|
734
|
+
args.notes.push(INSANE_NOTES.guardBlocked(guard.reason));
|
|
735
|
+
return null;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
const result = await tryInsaneFetch(guard.url.toString(), {
|
|
739
|
+
timeoutMs: args.timeout * 1000,
|
|
740
|
+
signal: args.signal,
|
|
741
|
+
});
|
|
742
|
+
if (result.ok) {
|
|
743
|
+
const output = finalizeOutput(result.content);
|
|
744
|
+
return {
|
|
745
|
+
url: args.url,
|
|
746
|
+
finalUrl: target,
|
|
747
|
+
contentType: "text/markdown",
|
|
748
|
+
method: "insane",
|
|
749
|
+
content: output.content,
|
|
750
|
+
fetchedAt: args.fetchedAt,
|
|
751
|
+
truncated: output.truncated,
|
|
752
|
+
notes: [...args.notes, ...result.notes],
|
|
753
|
+
};
|
|
754
|
+
}
|
|
755
|
+
for (const note of result.notes) args.notes.push(note);
|
|
756
|
+
return null;
|
|
757
|
+
}
|
|
758
|
+
|
|
708
759
|
/**
|
|
709
760
|
* Main render function implementing the full pipeline
|
|
710
761
|
*/
|
|
@@ -751,6 +802,19 @@ async function renderUrl(
|
|
|
751
802
|
throw new ToolAbortError();
|
|
752
803
|
}
|
|
753
804
|
if (!response.ok) {
|
|
805
|
+
const failureNote = response.status ? `Failed to fetch URL (HTTP ${response.status})` : "Failed to fetch URL";
|
|
806
|
+
notes.push(failureNote);
|
|
807
|
+
const insane = await tryInsaneFallback({
|
|
808
|
+
url,
|
|
809
|
+
finalUrl: response.finalUrl || url,
|
|
810
|
+
timeout,
|
|
811
|
+
raw,
|
|
812
|
+
settings,
|
|
813
|
+
signal,
|
|
814
|
+
fetchedAt,
|
|
815
|
+
notes,
|
|
816
|
+
});
|
|
817
|
+
if (insane) return insane;
|
|
754
818
|
return {
|
|
755
819
|
url,
|
|
756
820
|
finalUrl: response.finalUrl || url,
|
|
@@ -759,7 +823,7 @@ async function renderUrl(
|
|
|
759
823
|
content: "",
|
|
760
824
|
fetchedAt,
|
|
761
825
|
truncated: false,
|
|
762
|
-
notes
|
|
826
|
+
notes,
|
|
763
827
|
};
|
|
764
828
|
}
|
|
765
829
|
|
|
@@ -1062,6 +1126,8 @@ async function renderUrl(
|
|
|
1062
1126
|
const htmlResult = await renderHtmlToText(finalUrl, rawContent, timeout, settings, signal, storage);
|
|
1063
1127
|
if (!htmlResult.ok) {
|
|
1064
1128
|
notes.push("html rendering failed (lynx/html2text unavailable)");
|
|
1129
|
+
const insane = await tryInsaneFallback({ url, finalUrl, timeout, raw, settings, signal, fetchedAt, notes });
|
|
1130
|
+
if (insane) return insane;
|
|
1065
1131
|
const output = finalizeOutput(rawContent);
|
|
1066
1132
|
return {
|
|
1067
1133
|
url,
|
|
@@ -1122,6 +1188,17 @@ async function renderUrl(
|
|
|
1122
1188
|
};
|
|
1123
1189
|
}
|
|
1124
1190
|
|
|
1191
|
+
const insaneLowQuality = await tryInsaneFallback({
|
|
1192
|
+
url,
|
|
1193
|
+
finalUrl,
|
|
1194
|
+
timeout,
|
|
1195
|
+
raw,
|
|
1196
|
+
settings,
|
|
1197
|
+
signal,
|
|
1198
|
+
fetchedAt,
|
|
1199
|
+
notes,
|
|
1200
|
+
});
|
|
1201
|
+
if (insaneLowQuality) return insaneLowQuality;
|
|
1125
1202
|
notes.push("Page appears to require JavaScript or is mostly navigation");
|
|
1126
1203
|
}
|
|
1127
1204
|
|
package/src/tools/index.ts
CHANGED
|
@@ -58,6 +58,7 @@ import { SearchToolBm25Tool } from "./search-tool-bm25";
|
|
|
58
58
|
import { SkillTool } from "./skill";
|
|
59
59
|
import { loadSshTool } from "./ssh";
|
|
60
60
|
import { SubagentTool } from "./subagent";
|
|
61
|
+
import { TelegramSendTool } from "./telegram-send";
|
|
61
62
|
import { type TodoPhase, TodoWriteTool } from "./todo-write";
|
|
62
63
|
import { WriteTool } from "./write";
|
|
63
64
|
import { YieldTool } from "./yield";
|
|
@@ -96,6 +97,7 @@ export * from "./search-tool-bm25";
|
|
|
96
97
|
export * from "./skill";
|
|
97
98
|
export * from "./ssh";
|
|
98
99
|
export * from "./subagent";
|
|
100
|
+
export * from "./telegram-send";
|
|
99
101
|
export * from "./todo-write";
|
|
100
102
|
export * from "./vim";
|
|
101
103
|
export * from "./write";
|
|
@@ -402,6 +404,7 @@ export const BUILTIN_TOOLS: Record<string, ToolFactory> = {
|
|
|
402
404
|
todo_write: s => new TodoWriteTool(s),
|
|
403
405
|
web_search: s => new WebSearchTool(s),
|
|
404
406
|
search_tool_bm25: SearchToolBm25Tool.createIf,
|
|
407
|
+
telegram_send: TelegramSendTool.createIf,
|
|
405
408
|
write: s => new WriteTool(s),
|
|
406
409
|
skill: SkillTool.createIf,
|
|
407
410
|
goal: s => new GoalTool(s),
|