@sayknow-cli/coding-agent 0.3.0 → 0.3.2
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/bin/skc.js +4 -0
- package/dist/types/cli/mcp-cli.d.ts +25 -0
- package/dist/types/cli/notify-cli.d.ts +2 -0
- package/dist/types/cli/plugin-cli.d.ts +2 -0
- package/dist/types/cli.d.ts +6 -0
- package/dist/types/commands/mcp.d.ts +70 -0
- package/dist/types/commands/plugin.d.ts +6 -0
- package/dist/types/commands/session.d.ts +6 -0
- package/dist/types/config/keybindings.d.ts +2 -2
- package/dist/types/config/model-profile-activation.d.ts +8 -1
- package/dist/types/config/settings-schema.d.ts +39 -2
- package/dist/types/deep-interview/plaintext-gate-guard.d.ts +11 -0
- package/dist/types/defaults/skc-defaults.d.ts +2 -13
- package/dist/types/extensibility/shared-events.d.ts +1 -0
- package/dist/types/extensibility/skc-plugins/compiler.d.ts +19 -0
- package/dist/types/extensibility/skc-plugins/constrained-hooks.d.ts +29 -0
- package/dist/types/extensibility/skc-plugins/index.d.ts +9 -0
- package/dist/types/extensibility/skc-plugins/injection.d.ts +9 -0
- package/dist/types/extensibility/skc-plugins/installer.d.ts +13 -0
- package/dist/types/extensibility/skc-plugins/mcp-policy.d.ts +26 -0
- package/dist/types/extensibility/skc-plugins/observability.d.ts +27 -0
- package/dist/types/extensibility/skc-plugins/prompt-appendix.d.ts +16 -0
- package/dist/types/extensibility/skc-plugins/registry.d.ts +32 -0
- package/dist/types/extensibility/skc-plugins/runtime-adapters.d.ts +64 -0
- package/dist/types/extensibility/skc-plugins/session-validation.d.ts +42 -0
- package/dist/types/extensibility/skc-plugins/types.d.ts +158 -2
- package/dist/types/extensibility/skc-plugins/validation.d.ts +8 -1
- package/dist/types/lsp/types.d.ts +2 -0
- package/dist/types/main.d.ts +2 -0
- package/dist/types/modes/components/custom-editor.d.ts +1 -1
- package/dist/types/modes/components/model-selector.d.ts +8 -0
- package/dist/types/modes/components/status-line/git-utils.d.ts +6 -0
- package/dist/types/modes/theme/defaults/index.d.ts +99 -0
- 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/html-format.d.ts +11 -0
- package/dist/types/notifications/index.d.ts +149 -1
- package/dist/types/notifications/lifecycle-commands.d.ts +60 -0
- package/dist/types/notifications/lifecycle-control-runtime.d.ts +98 -0
- package/dist/types/notifications/lifecycle-orchestrator.d.ts +144 -0
- package/dist/types/notifications/managed-daemon.d.ts +48 -0
- package/dist/types/notifications/operator-runtime.d.ts +52 -0
- package/dist/types/notifications/rate-limit-pool.d.ts +2 -0
- package/dist/types/notifications/recent-activity.d.ts +35 -0
- package/dist/types/notifications/telegram-daemon.d.ts +133 -16
- package/dist/types/notifications/telegram-reference.d.ts +3 -1
- package/dist/types/notifications/threaded-inbound.d.ts +19 -0
- package/dist/types/notifications/threaded-render.d.ts +6 -1
- package/dist/types/notifications/topic-registry.d.ts +12 -9
- package/dist/types/runtime-mcp/types.d.ts +7 -0
- package/dist/types/sdk.d.ts +2 -0
- package/dist/types/session/agent-session.d.ts +16 -4
- package/dist/types/session/blob-store.d.ts +25 -0
- package/dist/types/session/session-manager.d.ts +57 -0
- package/dist/types/skc-runtime/launch-tmux.d.ts +1 -0
- package/dist/types/skc-runtime/psmux-detect.d.ts +78 -0
- package/dist/types/skc-runtime/ralplan-runtime.d.ts +1 -1
- package/dist/types/skc-runtime/team-runtime.d.ts +2 -0
- package/dist/types/skc-runtime/tmux-common.d.ts +30 -2
- package/dist/types/skc-runtime/tmux-sessions.d.ts +18 -0
- package/dist/types/slash-commands/helpers/fast-status-report.d.ts +6 -0
- package/dist/types/system-prompt.d.ts +2 -0
- package/dist/types/task/executor.d.ts +9 -1
- package/dist/types/tools/composer-bash-policy.d.ts +14 -0
- package/dist/types/tools/fetch.d.ts +23 -0
- package/dist/types/tools/index.d.ts +4 -1
- package/dist/types/tools/telegram-send.d.ts +32 -0
- package/dist/types/utils/changelog.d.ts +1 -0
- package/dist/types/web/insane/bridge.d.ts +103 -0
- package/dist/types/web/insane/url-guard.d.ts +25 -0
- package/dist/types/web/scrapers/types.d.ts +5 -0
- package/dist/types/web/scrapers/utils.d.ts +7 -1
- 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 +14 -10
- package/scripts/g004-tmux-smoke.ts +100 -0
- package/scripts/g005-daemon-smoke.ts +180 -0
- package/scripts/g011-daemon-path-smoke.ts +153 -0
- package/scripts/verify-insane-vendor.ts +132 -0
- package/src/cli/args.ts +1 -1
- package/src/cli/fast-help.ts +30 -2
- package/src/cli/mcp-cli.ts +272 -0
- package/src/cli/notify-cli.ts +152 -5
- package/src/cli/plugin-cli.ts +66 -3
- package/src/cli.ts +30 -12
- package/src/commands/mcp.ts +117 -0
- package/src/commands/plugin.ts +4 -0
- package/src/commands/session.ts +18 -0
- package/src/commands/team.ts +1 -1
- package/src/config/keybindings.ts +2 -2
- package/src/config/model-profile-activation.ts +55 -7
- package/src/config/settings-schema.ts +30 -1
- package/src/deep-interview/plaintext-gate-guard.ts +94 -0
- package/src/defaults/skc/extensions/grok-cli-vendor/biome.json +1 -1
- package/src/defaults/skc/skills/deep-interview/SKILL.md +7 -6
- package/src/defaults/skc/skills/ralplan/SKILL.md +11 -4
- package/src/defaults/skc/skills/team/SKILL.md +5 -3
- package/src/defaults/skc/skills/ultragoal/SKILL.md +41 -13
- package/src/defaults/skc-defaults.ts +2 -27
- package/src/export/html/index.ts +2 -2
- package/src/extensibility/extensions/runner.ts +1 -0
- package/src/extensibility/shared-events.ts +1 -0
- package/src/extensibility/skc-plugins/compiler.ts +351 -0
- package/src/extensibility/skc-plugins/constrained-hooks.ts +170 -0
- package/src/extensibility/skc-plugins/index.ts +9 -0
- package/src/extensibility/skc-plugins/injection.ts +109 -0
- package/src/extensibility/skc-plugins/installer.ts +434 -0
- package/src/extensibility/skc-plugins/loader.ts +3 -1
- package/src/extensibility/skc-plugins/mcp-policy.ts +239 -0
- package/src/extensibility/skc-plugins/observability.ts +84 -0
- package/src/extensibility/skc-plugins/paths.ts +1 -1
- package/src/extensibility/skc-plugins/prompt-appendix.ts +109 -0
- package/src/extensibility/skc-plugins/registry.ts +180 -0
- package/src/extensibility/skc-plugins/runtime-adapters.ts +234 -0
- package/src/extensibility/skc-plugins/schema.ts +250 -20
- package/src/extensibility/skc-plugins/session-validation.ts +147 -0
- package/src/extensibility/skc-plugins/types.ts +199 -3
- package/src/extensibility/skc-plugins/validation.ts +80 -0
- package/src/extensibility/skills.ts +15 -0
- package/src/hooks/skill-state.ts +57 -0
- package/src/internal-urls/docs-index.generated.ts +17 -13
- package/src/lsp/config.ts +16 -3
- package/src/lsp/defaults.json +7 -0
- package/src/lsp/types.ts +2 -0
- package/src/main.ts +14 -3
- package/src/modes/bridge/bridge-mode.ts +11 -0
- package/src/modes/components/assistant-message.ts +49 -1
- package/src/modes/components/custom-editor.ts +2 -0
- package/src/modes/components/footer.ts +2 -3
- package/src/modes/components/hook-editor.ts +1 -1
- package/src/modes/components/hook-selector.ts +67 -43
- package/src/modes/components/model-selector.ts +56 -11
- package/src/modes/components/status-line/git-utils.ts +25 -0
- package/src/modes/components/status-line.ts +10 -11
- package/src/modes/components/welcome.ts +2 -3
- package/src/modes/controllers/event-controller.ts +15 -0
- package/src/modes/controllers/extension-ui-controller.ts +0 -27
- package/src/modes/controllers/selector-controller.ts +53 -11
- package/src/modes/interactive-mode.ts +50 -3
- package/src/modes/shared/agent-wire/scopes.ts +1 -1
- package/src/modes/theme/defaults/gruvbox-dark.json +99 -0
- package/src/modes/theme/defaults/index.ts +2 -0
- package/src/modes/utils/context-usage.ts +2 -2
- package/src/modes/utils/hotkeys-markdown.ts +1 -1
- 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/html-format.ts +38 -0
- package/src/notifications/index.ts +417 -45
- package/src/notifications/lifecycle-commands.ts +238 -0
- package/src/notifications/lifecycle-control-runtime.ts +405 -0
- package/src/notifications/lifecycle-orchestrator.ts +358 -0
- package/src/notifications/managed-daemon.ts +163 -0
- package/src/notifications/operator-runtime.ts +171 -0
- package/src/notifications/rate-limit-pool.ts +19 -0
- package/src/notifications/recent-activity.ts +132 -0
- package/src/notifications/telegram-daemon.ts +984 -242
- package/src/notifications/telegram-reference.ts +25 -7
- package/src/notifications/threaded-inbound.ts +60 -4
- package/src/notifications/threaded-render.ts +20 -2
- package/src/notifications/topic-registry.ts +23 -9
- package/src/prompts/agents/executor.md +2 -2
- package/src/runtime-mcp/transports/stdio.ts +38 -4
- package/src/runtime-mcp/types.ts +7 -0
- package/src/sdk.ts +157 -10
- package/src/session/agent-session.ts +248 -125
- package/src/session/blob-store.ts +196 -8
- package/src/session/session-manager.ts +762 -12
- package/src/skc-runtime/launch-tmux.ts +85 -22
- package/src/skc-runtime/ledger-event-renderer.ts +1 -0
- package/src/skc-runtime/psmux-detect.ts +239 -0
- package/src/skc-runtime/ralplan-runtime.ts +2 -2
- package/src/skc-runtime/team-runtime.ts +56 -23
- package/src/skc-runtime/tmux-common.ts +88 -4
- package/src/skc-runtime/tmux-sessions.ts +111 -9
- package/src/skc-runtime/ultragoal-guard.ts +25 -8
- package/src/skc-runtime/ultragoal-runtime.ts +75 -15
- package/src/skc-runtime/workflow-manifest.generated.json +29 -0
- package/src/skc-runtime/workflow-manifest.ts +7 -2
- package/src/slash-commands/builtin-registry.ts +23 -3
- package/src/slash-commands/helpers/fast-status-report.ts +13 -3
- package/src/slash-commands/helpers/parse.ts +2 -1
- package/src/system-prompt.ts +9 -0
- package/src/task/executor.ts +31 -7
- package/src/task/index.ts +2 -0
- package/src/tools/ask.ts +5 -1
- package/src/tools/bash.ts +9 -0
- package/src/tools/composer-bash-policy.ts +96 -0
- package/src/tools/fetch.ts +94 -1
- package/src/tools/index.ts +6 -1
- package/src/tools/telegram-send.ts +137 -0
- package/src/utils/changelog.ts +8 -0
- package/src/web/insane/bridge.ts +350 -0
- package/src/web/insane/url-guard.ts +159 -0
- package/src/web/scrapers/types.ts +143 -45
- package/src/web/scrapers/utils.ts +70 -19
- 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
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { AgentToolResult } from "@sayknow-cli/agent-core";
|
|
2
2
|
import { type Component } from "@sayknow-cli/tui";
|
|
3
|
+
import type { Settings } from "../config/settings";
|
|
3
4
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
4
5
|
import { type Theme } from "../modes/theme/theme";
|
|
5
6
|
import type { ToolSession } from "../sdk";
|
|
7
|
+
import type { RenderResult } from "../web/scrapers/types";
|
|
6
8
|
import { type OutputMeta } from "./output-meta";
|
|
7
9
|
export declare function isReadableUrlPath(value: string): boolean;
|
|
8
10
|
export interface ParsedReadUrlTarget {
|
|
@@ -16,6 +18,27 @@ interface FetchImagePayload {
|
|
|
16
18
|
data: string;
|
|
17
19
|
mimeType: string;
|
|
18
20
|
}
|
|
21
|
+
type FetchRenderResult = RenderResult & {
|
|
22
|
+
image?: FetchImagePayload;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Opt-in insane-search fallback for blocked / degraded public URL reads.
|
|
26
|
+
*
|
|
27
|
+
* Returns a finalized `method: "insane"` result on success, or null (so the
|
|
28
|
+
* caller continues with its normal degraded behavior). Fail-closed: no note,
|
|
29
|
+
* guard DNS, dependency probe, or subprocess when raw mode or the opt-in
|
|
30
|
+
* setting is off. The public-URL guard runs BEFORE any probe/spawn.
|
|
31
|
+
*/
|
|
32
|
+
export declare function tryInsaneFallback(args: {
|
|
33
|
+
url: string;
|
|
34
|
+
finalUrl: string;
|
|
35
|
+
timeout: number;
|
|
36
|
+
raw: boolean;
|
|
37
|
+
settings: Settings;
|
|
38
|
+
signal: AbortSignal | undefined;
|
|
39
|
+
fetchedAt: string;
|
|
40
|
+
notes: string[];
|
|
41
|
+
}): Promise<FetchRenderResult | null>;
|
|
19
42
|
export interface ReadUrlToolDetails {
|
|
20
43
|
kind: "url";
|
|
21
44
|
url: string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AgentTelemetryConfig, AgentTool } from "@sayknow-cli/agent-core";
|
|
2
|
-
import type { Model, ToolChoice } from "@sayknow-cli/ai";
|
|
2
|
+
import type { Model, ServiceTier, ToolChoice } from "@sayknow-cli/ai";
|
|
3
3
|
import type { PromptTemplate } from "../config/prompt-templates";
|
|
4
4
|
import type { Settings } from "../config/settings";
|
|
5
5
|
import type { Skill } from "../extensibility/skills";
|
|
@@ -55,6 +55,7 @@ export * from "./search-tool-bm25";
|
|
|
55
55
|
export * from "./skill";
|
|
56
56
|
export * from "./ssh";
|
|
57
57
|
export * from "./subagent";
|
|
58
|
+
export * from "./telegram-send";
|
|
58
59
|
export * from "./todo-write";
|
|
59
60
|
export * from "./vim";
|
|
60
61
|
export * from "./write";
|
|
@@ -172,6 +173,8 @@ export interface ToolSession {
|
|
|
172
173
|
agentOutputManager?: AgentOutputManager;
|
|
173
174
|
/** Settings instance for passing to subagents */
|
|
174
175
|
settings: Settings;
|
|
176
|
+
/** Live service-tier intent of the parent session, inherited by `inherit` subagents. */
|
|
177
|
+
serviceTier?: ServiceTier;
|
|
175
178
|
/** Plan mode state (if active) */
|
|
176
179
|
getPlanModeState?: () => PlanModeState | undefined;
|
|
177
180
|
/** Goal mode state (if active or paused) */
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@sayknow-cli/agent-core";
|
|
2
|
+
import { z } from "zod/v4";
|
|
3
|
+
import type { ToolSession } from "./index";
|
|
4
|
+
declare const telegramSendSchema: z.ZodObject<{
|
|
5
|
+
path: z.ZodString;
|
|
6
|
+
caption: z.ZodOptional<z.ZodString>;
|
|
7
|
+
}, z.core.$strip>;
|
|
8
|
+
type TelegramSendParams = z.infer<typeof telegramSendSchema>;
|
|
9
|
+
interface TelegramSendDetails {
|
|
10
|
+
path: string;
|
|
11
|
+
caption?: string;
|
|
12
|
+
ok: boolean;
|
|
13
|
+
error?: string;
|
|
14
|
+
}
|
|
15
|
+
export declare class TelegramSendTool implements AgentTool<typeof telegramSendSchema, TelegramSendDetails> {
|
|
16
|
+
private readonly session;
|
|
17
|
+
readonly name = "telegram_send";
|
|
18
|
+
readonly label = "TelegramSend";
|
|
19
|
+
readonly summary = "Send a workspace file to Telegram";
|
|
20
|
+
readonly loadMode = "discoverable";
|
|
21
|
+
readonly description: string;
|
|
22
|
+
readonly parameters: z.ZodObject<{
|
|
23
|
+
path: z.ZodString;
|
|
24
|
+
caption: z.ZodOptional<z.ZodString>;
|
|
25
|
+
}, z.core.$strip>;
|
|
26
|
+
readonly strict = true;
|
|
27
|
+
constructor(session: ToolSession);
|
|
28
|
+
static createIf(session: ToolSession): TelegramSendTool | null;
|
|
29
|
+
private resolveContainedFile;
|
|
30
|
+
execute(_toolCallId: string, params: TelegramSendParams, _signal?: AbortSignal, _onUpdate?: AgentToolUpdateCallback<TelegramSendDetails>, _context?: AgentToolContext): Promise<AgentToolResult<TelegramSendDetails>>;
|
|
31
|
+
}
|
|
32
|
+
export {};
|
|
@@ -25,6 +25,7 @@ export declare function parseChangelog(changelogPath: string): Promise<Changelog
|
|
|
25
25
|
* binary-identity changelog).
|
|
26
26
|
*/
|
|
27
27
|
export declare function getDisplayChangelogEntries(): ChangelogEntry[];
|
|
28
|
+
export declare function getInstalledVersionChangelogEntry(entries: readonly ChangelogEntry[], installedVersion: string): ChangelogEntry | undefined;
|
|
28
29
|
/**
|
|
29
30
|
* Compare versions. Returns: -1 if v1 < v2, 0 if v1 === v2, 1 if v1 > v2
|
|
30
31
|
*/
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bridge from TypeScript to the vendored insane-search Python engine.
|
|
3
|
+
*
|
|
4
|
+
* Invokes `python3 -m engine "<url>" --json` per fallback attempt (cwd + PYTHONPATH
|
|
5
|
+
* pointed at the vendored engine), validates the JSON envelope, and maps it onto a
|
|
6
|
+
* discriminated result. Hardened: clamped timeout, AbortSignal propagation that
|
|
7
|
+
* kills+reaps the child, bounded stdout/stderr capture, and a per-process
|
|
8
|
+
* concurrency cap so blocked reads cannot fork-storm.
|
|
9
|
+
*
|
|
10
|
+
* Fail-closed: missing dependencies / bad output / auth-required never throw past
|
|
11
|
+
* the caller and never auto-install anything; they return ok:false with a stable,
|
|
12
|
+
* bounded note so `read` can continue with its normal degraded result.
|
|
13
|
+
*/
|
|
14
|
+
import { spawn as nodeSpawn } from "node:child_process";
|
|
15
|
+
/** packages/coding-agent/vendor/insane-search */
|
|
16
|
+
export declare const INSANE_VENDOR_DIR: string;
|
|
17
|
+
/** Stable note prefixes — tests assert on these without depending on full stderr. */
|
|
18
|
+
export declare const INSANE_NOTES: {
|
|
19
|
+
readonly guardBlocked: (reason: string) => string;
|
|
20
|
+
readonly vendorMissing: `insane fallback unavailable: vendor engine missing at packages/coding-agent/vendor/insane-search`;
|
|
21
|
+
readonly noPython: `insane fallback unavailable: python3 not found; install python3 and curl_cffi, then retry with web.insaneFallback=true`;
|
|
22
|
+
readonly noCurlCffi: `insane fallback unavailable: python3 cannot import curl_cffi; install curl_cffi for Phase 0-2`;
|
|
23
|
+
readonly noBrowser: `insane fallback unavailable: node/playwright/stealth dependencies missing for Phase 3; install dependencies under packages/coding-agent/vendor/insane-search/engine/templates`;
|
|
24
|
+
readonly timeout: (seconds: number) => string;
|
|
25
|
+
readonly invalidJson: `insane fallback failed: engine returned invalid JSON`;
|
|
26
|
+
readonly authRequired: `insane fallback stopped: authentication required`;
|
|
27
|
+
readonly verdict: (verdict: string) => string;
|
|
28
|
+
readonly untried: (routes: string) => string;
|
|
29
|
+
readonly mustBrowserMcp: `insane fallback requires browser MCP/manual phase: must_invoke_playwright_mcp=true`;
|
|
30
|
+
readonly concurrency: `insane fallback skipped: max concurrent engine attempts reached`;
|
|
31
|
+
readonly emptyContent: `insane fallback failed: engine reported ok but returned no content`;
|
|
32
|
+
};
|
|
33
|
+
/** Raw JSON envelope produced by `python3 -m engine --json`. */
|
|
34
|
+
export interface InsaneFetchResultRaw {
|
|
35
|
+
ok?: boolean;
|
|
36
|
+
verdict?: string;
|
|
37
|
+
content?: string;
|
|
38
|
+
profile_used?: string;
|
|
39
|
+
trace?: unknown;
|
|
40
|
+
untried_routes?: string[];
|
|
41
|
+
must_invoke_playwright_mcp?: boolean;
|
|
42
|
+
}
|
|
43
|
+
export interface InsaneSuccess {
|
|
44
|
+
ok: true;
|
|
45
|
+
content: string;
|
|
46
|
+
profileUsed?: string;
|
|
47
|
+
notes: string[];
|
|
48
|
+
}
|
|
49
|
+
export interface InsaneFailure {
|
|
50
|
+
ok: false;
|
|
51
|
+
reason: string;
|
|
52
|
+
verdict?: string;
|
|
53
|
+
notes: string[];
|
|
54
|
+
}
|
|
55
|
+
export type InsaneBridgeResult = InsaneSuccess | InsaneFailure;
|
|
56
|
+
export interface EngineInvocation {
|
|
57
|
+
url: string;
|
|
58
|
+
timeoutMs: number;
|
|
59
|
+
signal?: AbortSignal;
|
|
60
|
+
}
|
|
61
|
+
export interface EngineRawOutput {
|
|
62
|
+
code: number | null;
|
|
63
|
+
stdout: string;
|
|
64
|
+
stderr: string;
|
|
65
|
+
timedOut: boolean;
|
|
66
|
+
aborted: boolean;
|
|
67
|
+
}
|
|
68
|
+
/** Seam: run the engine subprocess. Default spawns python3. */
|
|
69
|
+
export type EngineRunner = (inv: EngineInvocation) => Promise<EngineRawOutput>;
|
|
70
|
+
export interface InsaneDependencyStatus {
|
|
71
|
+
vendorPresent: boolean;
|
|
72
|
+
python: boolean;
|
|
73
|
+
curlCffi: boolean;
|
|
74
|
+
browser: boolean;
|
|
75
|
+
}
|
|
76
|
+
/** Seam: probe dependencies. Default probes the real environment (cached). */
|
|
77
|
+
export type DependencyProber = () => Promise<InsaneDependencyStatus>;
|
|
78
|
+
type SpawnImpl = typeof nodeSpawn;
|
|
79
|
+
/** Real engine runner: `python3 -m engine "<url>" --json`. */
|
|
80
|
+
export declare function runEngineSubprocess(inv: EngineInvocation, options?: {
|
|
81
|
+
spawnImpl?: SpawnImpl;
|
|
82
|
+
}): Promise<EngineRawOutput>;
|
|
83
|
+
/** Reset the probe cache between tests so probe state never leaks. */
|
|
84
|
+
export declare function resetInsaneProbeCacheForTest(): void;
|
|
85
|
+
/** Probe (and cache) the insane-search runtime dependencies. */
|
|
86
|
+
export declare function probeInsaneDependencies(): Promise<InsaneDependencyStatus>;
|
|
87
|
+
export declare function resetInsaneConcurrencyForTest(): void;
|
|
88
|
+
export interface TryInsaneFetchOptions {
|
|
89
|
+
timeoutMs?: number;
|
|
90
|
+
signal?: AbortSignal;
|
|
91
|
+
concurrencyLimit?: number;
|
|
92
|
+
/** Seam: dependency prober (default real, cached). */
|
|
93
|
+
prober?: DependencyProber;
|
|
94
|
+
/** Seam: engine runner (default real subprocess). */
|
|
95
|
+
runner?: EngineRunner;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Attempt to read `url` through the insane-search engine. The caller is
|
|
99
|
+
* responsible for the opt-in gate, raw-mode skip, and the public-URL guard
|
|
100
|
+
* (which MUST run before this is called). Never throws; always returns a result.
|
|
101
|
+
*/
|
|
102
|
+
export declare function tryInsaneFetch(url: string, options?: TryInsaneFetchOptions): Promise<InsaneBridgeResult>;
|
|
103
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface PublicUrlAccepted {
|
|
2
|
+
ok: true;
|
|
3
|
+
url: URL;
|
|
4
|
+
addresses: string[];
|
|
5
|
+
}
|
|
6
|
+
export interface PublicUrlRejected {
|
|
7
|
+
ok: false;
|
|
8
|
+
reason: string;
|
|
9
|
+
}
|
|
10
|
+
export type PublicUrlResult = PublicUrlAccepted | PublicUrlRejected;
|
|
11
|
+
/** Resolver seam so tests can inject DNS results without real lookups. */
|
|
12
|
+
export type AddressResolver = (hostname: string) => Promise<string[]>;
|
|
13
|
+
/** True for any address that is not a routable public unicast address. */
|
|
14
|
+
export declare function isPrivateOrSpecialAddress(address: string): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Validate that `rawUrl` is a public http/https target. Resolves DNS names and
|
|
17
|
+
* rejects any that map to a private/special address. Never throws; returns a
|
|
18
|
+
* discriminated result.
|
|
19
|
+
*/
|
|
20
|
+
export declare function validatePublicHttpUrl(rawUrl: string, options?: {
|
|
21
|
+
resolver?: AddressResolver;
|
|
22
|
+
}): Promise<PublicUrlResult>;
|
|
23
|
+
export declare function validatePublicHttpUrlForInsane(rawUrl: string, options?: {
|
|
24
|
+
resolver?: AddressResolver;
|
|
25
|
+
}): Promise<PublicUrlResult>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AgentStorage } from "../../session/agent-storage";
|
|
2
|
+
import type { AddressResolver } from "../insane/url-guard";
|
|
2
3
|
export { formatNumber } from "@sayknow-cli/utils";
|
|
3
4
|
export interface RenderResult {
|
|
4
5
|
url: string;
|
|
@@ -27,6 +28,9 @@ export interface LoadPageOptions {
|
|
|
27
28
|
body?: string;
|
|
28
29
|
maxBytes?: number;
|
|
29
30
|
signal?: AbortSignal;
|
|
31
|
+
publicUrlGuard?: boolean;
|
|
32
|
+
resolver?: AddressResolver;
|
|
33
|
+
maxRedirects?: number;
|
|
30
34
|
}
|
|
31
35
|
export interface LoadPageResult {
|
|
32
36
|
content: string;
|
|
@@ -34,6 +38,7 @@ export interface LoadPageResult {
|
|
|
34
38
|
finalUrl: string;
|
|
35
39
|
ok: boolean;
|
|
36
40
|
status?: number;
|
|
41
|
+
error?: string;
|
|
37
42
|
}
|
|
38
43
|
/**
|
|
39
44
|
* Fetch a page with timeout and size limit
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { isRecord } from "@sayknow-cli/utils";
|
|
2
2
|
export { isRecord };
|
|
3
|
+
import type { AddressResolver } from "../insane/url-guard";
|
|
3
4
|
export declare function asRecord(value: unknown): Record<string, unknown> | null;
|
|
4
5
|
export declare function asString(value: unknown): string | null;
|
|
5
6
|
export declare function asNumber(value: unknown): number | null;
|
|
@@ -12,10 +13,15 @@ export type BinaryFetchResult = BinaryFetchSuccess | {
|
|
|
12
13
|
ok: false;
|
|
13
14
|
error?: string;
|
|
14
15
|
};
|
|
16
|
+
export interface FetchBinaryOptions {
|
|
17
|
+
publicUrlGuard?: boolean;
|
|
18
|
+
resolver?: AddressResolver;
|
|
19
|
+
maxRedirects?: number;
|
|
20
|
+
}
|
|
15
21
|
/**
|
|
16
22
|
* Fetch binary content from a URL
|
|
17
23
|
*/
|
|
18
|
-
export declare function fetchBinary(url: string, timeout?: number, signal?: AbortSignal): Promise<BinaryFetchResult>;
|
|
24
|
+
export declare function fetchBinary(url: string, timeout?: number, signal?: AbortSignal, options?: FetchBinaryOptions): Promise<BinaryFetchResult>;
|
|
19
25
|
/**
|
|
20
26
|
* Convert binary content to markdown using markit.
|
|
21
27
|
*/
|
|
@@ -20,5 +20,22 @@ export declare function looksHostedModelId(modelId: string | undefined): boolean
|
|
|
20
20
|
export declare function isLocalBaseUrl(baseUrl: string | undefined): boolean;
|
|
21
21
|
export declare function inferNativeProviderFromModel(ctx: ActiveSearchModelContext | undefined): SearchProviderId | undefined;
|
|
22
22
|
export declare function canUseGenericCredentials(authStorage: AuthStorage, ctx: ActiveSearchModelContext | undefined, sessionId?: string, signal?: AbortSignal): Promise<boolean>;
|
|
23
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Native web-search provider to attempt by reusing the ACTIVE model's own
|
|
25
|
+
* credentials + baseUrl, dispatched by the model's wire protocol.
|
|
26
|
+
*
|
|
27
|
+
* This is the "native search over a proxy" path: when a model is served through
|
|
28
|
+
* a proxy/custom endpoint, its canonical search credentials (e.g. a dedicated
|
|
29
|
+
* `anthropic` key, or ChatGPT OAuth for `codex`) are usually absent, but the
|
|
30
|
+
* credential that authenticates the model itself — stored under the active
|
|
31
|
+
* provider id and aimed at `ctx.baseUrl` — can drive native web search just as
|
|
32
|
+
* well. Each provider's `search()` falls back to those active credentials when
|
|
33
|
+
* its canonical ones are missing.
|
|
34
|
+
*
|
|
35
|
+
* Returned ids are matched purely from the wire `api` (+ model-id family where a
|
|
36
|
+
* native tool only makes sense for that family); the providers themselves fail
|
|
37
|
+
* closed (and the chain falls through to DuckDuckGo) if the endpoint does not
|
|
38
|
+
* actually support web search.
|
|
39
|
+
*/
|
|
40
|
+
export declare function activeContextNativeId(ctx: ActiveSearchModelContext | undefined): SearchProviderId | undefined;
|
|
24
41
|
export declare function resolveProviderChain(options: ResolveProviderChainOptions): Promise<SearchProvider[]>;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Insane Search Provider
|
|
3
|
+
*
|
|
4
|
+
* Native TypeScript, fail-closed adaptation of the MIT-licensed upstream
|
|
5
|
+
* fivetaku/insane-search public-route strategy. This ports only safe Phase 0
|
|
6
|
+
* concepts: deterministic no-auth public endpoints plus route-attempt tracing.
|
|
7
|
+
*
|
|
8
|
+
* Deliberately excluded from upstream: TLS impersonation, browser/cookie warming,
|
|
9
|
+
* CAPTCHA/paywall/login bypasses, credential storage, Playwright automation, and
|
|
10
|
+
* auto dependency installation. Unsupported or terminal auth/paywall/block states
|
|
11
|
+
* throw instead of pretending a shallow fetch succeeded.
|
|
12
|
+
*/
|
|
13
|
+
import type { AuthStorage } from "@sayknow-cli/ai";
|
|
14
|
+
import type { SearchResponse, SearchSource } from "../../../web/search/types";
|
|
15
|
+
import type { SearchParams } from "./base";
|
|
16
|
+
import { SearchProvider } from "./base";
|
|
17
|
+
export interface InsaneRouteAttempt {
|
|
18
|
+
platform: InsanePlatform;
|
|
19
|
+
route: string;
|
|
20
|
+
ok: boolean;
|
|
21
|
+
status: number;
|
|
22
|
+
bytes: number;
|
|
23
|
+
note?: string;
|
|
24
|
+
}
|
|
25
|
+
type InsanePlatform = "reddit" | "x" | "youtube" | "hackernews";
|
|
26
|
+
interface RouteSuccess {
|
|
27
|
+
platform: InsanePlatform;
|
|
28
|
+
route: string;
|
|
29
|
+
finalUrl: string;
|
|
30
|
+
sources: SearchSource[];
|
|
31
|
+
attempts: InsaneRouteAttempt[];
|
|
32
|
+
}
|
|
33
|
+
interface RouteFailure {
|
|
34
|
+
platform: InsanePlatform;
|
|
35
|
+
attempts: InsaneRouteAttempt[];
|
|
36
|
+
}
|
|
37
|
+
type RouteResult = RouteSuccess | RouteFailure | null;
|
|
38
|
+
export declare function routeInsanePublicUrl(rawUrl: string, signal?: AbortSignal): Promise<RouteResult>;
|
|
39
|
+
/** Execute safe Insane Search public-route discovery. */
|
|
40
|
+
export declare function searchInsane(params: {
|
|
41
|
+
query: string;
|
|
42
|
+
num_results?: number;
|
|
43
|
+
recency?: "day" | "week" | "month" | "year";
|
|
44
|
+
signal?: AbortSignal;
|
|
45
|
+
}): Promise<SearchResponse>;
|
|
46
|
+
/** Keyless provider that ports safe upstream public-route fallbacks only. */
|
|
47
|
+
export declare class InsaneProvider extends SearchProvider {
|
|
48
|
+
readonly id = "insane";
|
|
49
|
+
readonly label = "Insane";
|
|
50
|
+
isAvailable(_authStorage: AuthStorage): boolean;
|
|
51
|
+
search(params: SearchParams): Promise<SearchResponse>;
|
|
52
|
+
}
|
|
53
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inline citation extraction shared by native web-search providers.
|
|
3
|
+
*
|
|
4
|
+
* Web-search-capable models sometimes return a genuinely grounded answer whose
|
|
5
|
+
* sources are written inline (markdown links or bare URLs) instead of as
|
|
6
|
+
* structured citation annotations. When a provider has independent proof that a
|
|
7
|
+
* web search actually ran, these helpers recover sources from the answer text so
|
|
8
|
+
* the result is not discarded.
|
|
9
|
+
*/
|
|
10
|
+
import type { SearchSource } from "../types";
|
|
11
|
+
/** Append a source, de-duplicating by URL. */
|
|
12
|
+
export declare function addSource(sources: SearchSource[], source: SearchSource): void;
|
|
13
|
+
/**
|
|
14
|
+
* Strips prose punctuation and unmatched closing delimiters from extracted URLs.
|
|
15
|
+
* Models often return links embedded in markdown or sentence text.
|
|
16
|
+
*/
|
|
17
|
+
export declare function normalizeExtractedUrl(candidate: string): string | null;
|
|
18
|
+
/**
|
|
19
|
+
* Extracts citation sources from markdown links and bare URLs in answer text.
|
|
20
|
+
* Used only as a fallback when a provider confirms a search ran but omits
|
|
21
|
+
* structured citation annotations.
|
|
22
|
+
*/
|
|
23
|
+
export declare function extractTextSources(text: string): SearchSource[];
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Unified types for web search responses across supported providers.
|
|
5
5
|
*/
|
|
6
6
|
/** Supported web search providers */
|
|
7
|
-
export type SearchProviderId = "duckduckgo" | "exa" | "brave" | "jina" | "kimi" | "zai" | "anthropic" | "perplexity" | "gemini" | "codex" | "xai" | "tavily" | "parallel" | "kagi" | "synthetic" | "searxng" | "openai-compatible";
|
|
7
|
+
export type SearchProviderId = "duckduckgo" | "insane" | "exa" | "brave" | "jina" | "kimi" | "zai" | "anthropic" | "perplexity" | "gemini" | "codex" | "xai" | "tavily" | "parallel" | "kagi" | "synthetic" | "searxng" | "openai-compatible";
|
|
8
8
|
export type WebSearchMode = "on" | "off" | "auto";
|
|
9
9
|
export interface ActiveSearchModelContext {
|
|
10
10
|
provider: string;
|
|
@@ -15,7 +15,7 @@ export interface ActiveSearchModelContext {
|
|
|
15
15
|
headers?: Record<string, string>;
|
|
16
16
|
webSearch?: WebSearchMode;
|
|
17
17
|
}
|
|
18
|
-
export declare const CONFIGURABLE_SEARCH_PROVIDER_IDS: readonly ["duckduckgo", "exa", "brave", "jina", "kimi", "zai", "anthropic", "perplexity", "gemini", "codex", "xai", "tavily", "parallel", "kagi", "synthetic", "searxng"];
|
|
18
|
+
export declare const CONFIGURABLE_SEARCH_PROVIDER_IDS: readonly ["duckduckgo", "insane", "exa", "brave", "jina", "kimi", "zai", "anthropic", "perplexity", "gemini", "codex", "xai", "tavily", "parallel", "kagi", "synthetic", "searxng"];
|
|
19
19
|
export declare function isSearchProviderId(value: string): value is SearchProviderId;
|
|
20
20
|
export declare function isConfigurableSearchProviderId(value: string): value is (typeof CONFIGURABLE_SEARCH_PROVIDER_IDS)[number];
|
|
21
21
|
export declare function isSearchProviderPreference(value: string): value is (typeof CONFIGURABLE_SEARCH_PROVIDER_IDS)[number] | "auto";
|
|
@@ -96,6 +96,14 @@ export interface AnthropicCitation {
|
|
|
96
96
|
cited_text: string;
|
|
97
97
|
encrypted_index: string;
|
|
98
98
|
}
|
|
99
|
+
/**
|
|
100
|
+
* Error payload returned in `web_search_tool_result.content` when a server-side
|
|
101
|
+
* web search fails. Unlike the success case, this is an object, not an array.
|
|
102
|
+
*/
|
|
103
|
+
export interface AnthropicWebSearchToolResultError {
|
|
104
|
+
type: "web_search_tool_result_error";
|
|
105
|
+
error_code?: string;
|
|
106
|
+
}
|
|
99
107
|
export interface AnthropicContentBlock {
|
|
100
108
|
type: string;
|
|
101
109
|
/** Text content (for type="text") */
|
|
@@ -108,8 +116,8 @@ export interface AnthropicContentBlock {
|
|
|
108
116
|
input?: {
|
|
109
117
|
query: string;
|
|
110
118
|
};
|
|
111
|
-
/** Search results (
|
|
112
|
-
content?: AnthropicSearchResult[];
|
|
119
|
+
/** Search results array on success, or an error object on failure (type="web_search_tool_result") */
|
|
120
|
+
content?: AnthropicSearchResult[] | AnthropicWebSearchToolResultError;
|
|
113
121
|
}
|
|
114
122
|
export interface AnthropicApiResponse {
|
|
115
123
|
id: string;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@sayknow-cli/coding-agent",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.2",
|
|
5
5
|
"description": "Sayknow-CLI CLI with read, bash, edit, write tools and session management",
|
|
6
6
|
"homepage": "https://github.com/jaybeyond/Sayknow_CLI",
|
|
7
7
|
"author": "jaybeyond",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"main": "./src/index.ts",
|
|
29
29
|
"types": "./dist/types/index.d.ts",
|
|
30
30
|
"bin": {
|
|
31
|
-
"skc": "
|
|
31
|
+
"skc": "bin/skc.js"
|
|
32
32
|
},
|
|
33
33
|
"scripts": {
|
|
34
34
|
"build": "bun scripts/build-binary.ts",
|
|
@@ -45,18 +45,19 @@
|
|
|
45
45
|
"generate-docs-index": "bun scripts/generate-docs-index.ts",
|
|
46
46
|
"prepack": "bun scripts/generate-docs-index.ts",
|
|
47
47
|
"generate-template": "bun scripts/generate-template.ts",
|
|
48
|
-
"install:defaults": "bun src/cli.ts setup defaults"
|
|
48
|
+
"install:defaults": "bun src/cli.ts setup defaults",
|
|
49
|
+
"verify:insane-vendor": "bun scripts/verify-insane-vendor.ts"
|
|
49
50
|
},
|
|
50
51
|
"dependencies": {
|
|
51
52
|
"@agentclientprotocol/sdk": "0.21.0",
|
|
52
53
|
"@babel/parser": "^7.29.3",
|
|
53
54
|
"@mozilla/readability": "^0.6.0",
|
|
54
|
-
"@sayknow-cli/stats": "0.3.
|
|
55
|
-
"@sayknow-cli/agent-core": "0.3.
|
|
56
|
-
"@sayknow-cli/ai": "0.3.
|
|
57
|
-
"@sayknow-cli/natives": "0.3.
|
|
58
|
-
"@sayknow-cli/tui": "0.3.
|
|
59
|
-
"@sayknow-cli/utils": "0.3.
|
|
55
|
+
"@sayknow-cli/stats": "0.3.2",
|
|
56
|
+
"@sayknow-cli/agent-core": "0.3.2",
|
|
57
|
+
"@sayknow-cli/ai": "0.3.2",
|
|
58
|
+
"@sayknow-cli/natives": "0.3.2",
|
|
59
|
+
"@sayknow-cli/tui": "0.3.2",
|
|
60
|
+
"@sayknow-cli/utils": "0.3.2",
|
|
60
61
|
"@puppeteer/browsers": "^2.13.0",
|
|
61
62
|
"@types/turndown": "5.0.6",
|
|
62
63
|
"@xterm/headless": "^6.0.0",
|
|
@@ -73,17 +74,20 @@
|
|
|
73
74
|
"zod": "4.4.3"
|
|
74
75
|
},
|
|
75
76
|
"devDependencies": {
|
|
76
|
-
"@types/bun": "^1.3.14"
|
|
77
|
+
"@types/bun": "^1.3.14",
|
|
78
|
+
"@types/ws": "^8.5.13"
|
|
77
79
|
},
|
|
78
80
|
"engines": {
|
|
79
81
|
"bun": ">=1.3.14"
|
|
80
82
|
},
|
|
81
83
|
"files": [
|
|
82
84
|
"src",
|
|
85
|
+
"bin",
|
|
83
86
|
"scripts",
|
|
84
87
|
"examples",
|
|
85
88
|
"README.md",
|
|
86
89
|
"CHANGELOG.md",
|
|
90
|
+
"vendor",
|
|
87
91
|
"dist/types"
|
|
88
92
|
],
|
|
89
93
|
"exports": {
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// G004 real-tmux smoke: exercises forceCloseSkcTmuxSession against LIVE tmux
|
|
2
|
+
// sessions (tmux 3.6a). Proves the wrapper hard-kills SKC-managed live panes
|
|
3
|
+
// (where remove refuses), refuses non-SKC sessions, and enforces session-id
|
|
4
|
+
// matching. Produces durable evidence; not part of the unit suite.
|
|
5
|
+
import assert from "node:assert";
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
buildSkcTmuxExactOptionTarget,
|
|
9
|
+
buildSkcTmuxProfileCommands,
|
|
10
|
+
resolveSkcTmuxCommand,
|
|
11
|
+
} from "../src/skc-runtime/tmux-common";
|
|
12
|
+
import { forceCloseSkcTmuxSession, removeSkcTmuxSession, statusSkcTmuxSession } from "../src/skc-runtime/tmux-sessions";
|
|
13
|
+
|
|
14
|
+
const tmux = resolveSkcTmuxCommand(process.env);
|
|
15
|
+
|
|
16
|
+
function sh(args: string[]): { code: number; err: string } {
|
|
17
|
+
const r = Bun.spawnSync([tmux, ...args], { stdout: "pipe", stderr: "pipe" });
|
|
18
|
+
return { code: r.exitCode, err: r.stderr.toString().trim() };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function makeRawSession(name: string): void {
|
|
22
|
+
const r = sh(["new-session", "-d", "-s", name, "sleep 600"]);
|
|
23
|
+
if (r.code !== 0) throw new Error(`failed to create tmux session ${name}: ${r.err}`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function tagAsSkc(name: string, sessionId?: string): void {
|
|
27
|
+
const target = buildSkcTmuxExactOptionTarget(name);
|
|
28
|
+
for (const cmd of buildSkcTmuxProfileCommands(target, process.env, { sessionId })) {
|
|
29
|
+
const r = sh(cmd.args);
|
|
30
|
+
if (r.code !== 0) throw new Error(`failed to tag ${name} (${cmd.description}): ${r.err}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function exists(name: string): boolean {
|
|
35
|
+
return sh(["has-session", "-t", `=${name}`]).code === 0;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function killQuiet(name: string): void {
|
|
39
|
+
sh(["kill-session", "-t", `=${name}`]);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const suffix = `${process.pid}-${Date.now()}`;
|
|
43
|
+
const live = `skc_g004live_${suffix}`;
|
|
44
|
+
const raw = `g004raw_${suffix}`;
|
|
45
|
+
const mism = `skc_g004mism_${suffix}`;
|
|
46
|
+
const cleanup: string[] = [live, raw, mism];
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
// 1. SKC-managed LIVE session: remove refuses, force-close hard-kills.
|
|
50
|
+
makeRawSession(live);
|
|
51
|
+
tagAsSkc(live, "sess-g004");
|
|
52
|
+
const status = statusSkcTmuxSession(live);
|
|
53
|
+
assert.equal(status.profile, "1", "session must be recognized as SKC-managed");
|
|
54
|
+
assert.ok(status.panePids.length > 0, "session must have a live pane (sleep)");
|
|
55
|
+
console.log(`[g004] live SKC session up: ${live} panePids=${status.panePids.length}`);
|
|
56
|
+
|
|
57
|
+
let removeRefused = false;
|
|
58
|
+
try {
|
|
59
|
+
removeSkcTmuxSession(live);
|
|
60
|
+
} catch (e) {
|
|
61
|
+
removeRefused = /skc_tmux_session_live/.test(String(e));
|
|
62
|
+
}
|
|
63
|
+
assert.ok(removeRefused, "removeSkcTmuxSession must REFUSE a live pane");
|
|
64
|
+
console.log("[g004] remove refused live session (expected)");
|
|
65
|
+
|
|
66
|
+
const closed = forceCloseSkcTmuxSession(live, process.env, "sess-g004");
|
|
67
|
+
assert.equal(closed.name, live);
|
|
68
|
+
assert.ok(!exists(live), "force-close must hard-kill the live SKC session");
|
|
69
|
+
console.log("[g004] force-close hard-killed the live SKC session (id-matched)");
|
|
70
|
+
|
|
71
|
+
// 2. Non-SKC (untagged) session: force-close must refuse.
|
|
72
|
+
makeRawSession(raw);
|
|
73
|
+
let notManaged = false;
|
|
74
|
+
try {
|
|
75
|
+
forceCloseSkcTmuxSession(raw, process.env);
|
|
76
|
+
} catch (e) {
|
|
77
|
+
notManaged = /skc_tmux_session_(not_managed|not_found)/.test(String(e));
|
|
78
|
+
}
|
|
79
|
+
assert.ok(notManaged, "force-close must refuse a non-SKC tmux session");
|
|
80
|
+
assert.ok(exists(raw), "non-SKC session must be left untouched");
|
|
81
|
+
killQuiet(raw);
|
|
82
|
+
console.log("[g004] force-close refused + preserved non-SKC session (expected)");
|
|
83
|
+
|
|
84
|
+
// 3. SKC session with a MISMATCHED expected session id: must refuse.
|
|
85
|
+
makeRawSession(mism);
|
|
86
|
+
tagAsSkc(mism, "sess-real");
|
|
87
|
+
let idMismatch = false;
|
|
88
|
+
try {
|
|
89
|
+
forceCloseSkcTmuxSession(mism, process.env, "sess-WRONG");
|
|
90
|
+
} catch (e) {
|
|
91
|
+
idMismatch = /skc_tmux_session_id_mismatch/.test(String(e));
|
|
92
|
+
}
|
|
93
|
+
assert.ok(idMismatch, "force-close must refuse on session-id mismatch");
|
|
94
|
+
assert.ok(exists(mism), "mismatched session must be left untouched");
|
|
95
|
+
console.log("[g004] force-close refused on session-id mismatch (expected)");
|
|
96
|
+
|
|
97
|
+
console.log("[g004] PASS: forceCloseSkcTmuxSession verified against live tmux");
|
|
98
|
+
} finally {
|
|
99
|
+
for (const name of cleanup) killQuiet(name);
|
|
100
|
+
}
|