@sayknow-cli/coding-agent 0.3.2 → 0.3.5
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/config/model-profiles.d.ts +2 -2
- package/dist/types/config/model-registry.d.ts +3 -3
- package/dist/types/config/models-config-schema.d.ts +0 -5
- package/dist/types/config/settings-schema.d.ts +78 -9
- package/dist/types/export/html/template.generated.d.ts +1 -1
- package/dist/types/skc-runtime/state-renderer.d.ts +5 -0
- package/dist/types/skc-runtime/ultragoal-guard.d.ts +37 -1
- package/dist/types/skc-runtime/ultragoal-runtime.d.ts +80 -0
- package/dist/types/tools/browser/tab-supervisor.d.ts +31 -0
- package/dist/types/tools/computer-gc.d.ts +23 -0
- package/dist/types/tools/cron.d.ts +36 -61
- package/dist/types/tools/index.d.ts +0 -1
- package/dist/types/tools/resource-gc.d.ts +54 -0
- package/dist/types/web/search/index.d.ts +1 -0
- package/dist/types/web/search/providers/utils.d.ts +11 -4
- package/package.json +7 -7
- package/scripts/verify-insane-vendor.ts +5 -0
- package/src/cli/args.ts +0 -1
- package/src/cli/fast-help.ts +0 -1
- package/src/cli/plugin-cli.ts +1 -1
- package/src/cli/web-search-cli.ts +5 -0
- package/src/config/model-profile-activation.ts +7 -1
- package/src/config/model-profiles.ts +3 -4
- package/src/config/model-registry.ts +3 -6
- package/src/config/models-config-schema.ts +1 -1
- package/src/config/settings-schema.ts +80 -10
- package/src/export/html/template.generated.ts +1 -1
- package/src/export/html/template.js +0 -12
- package/src/goals/tools/goal-tool.ts +14 -1
- package/src/internal-urls/docs-index.generated.ts +3 -4
- package/src/modes/components/model-selector.ts +9 -1
- package/src/modes/controllers/selector-controller.ts +6 -0
- package/src/prompts/system/system-prompt.md +16 -20
- package/src/prompts/tools/cron.md +5 -3
- package/src/prompts/tools/read.md +1 -1
- package/src/sdk.ts +5 -0
- package/src/session/agent-session.ts +44 -0
- package/src/skc-runtime/state-renderer.ts +13 -0
- package/src/skc-runtime/ultragoal-guard.ts +167 -0
- package/src/skc-runtime/ultragoal-runtime.ts +221 -1
- package/src/tools/browser/tab-supervisor.ts +86 -2
- package/src/tools/computer-gc.ts +66 -0
- package/src/tools/computer.ts +2 -0
- package/src/tools/cron.ts +75 -112
- package/src/tools/index.ts +2 -8
- package/src/tools/read.ts +25 -55
- package/src/tools/renderers.ts +0 -2
- package/src/tools/resource-gc.ts +291 -0
- package/src/tools/ultragoal-ask-guard.ts +7 -1
- package/src/web/search/index.ts +1 -0
- package/src/web/search/providers/utils.ts +22 -5
- package/vendor/insane-search/MANIFEST.json +5 -1
- package/vendor/insane-search/engine/__init__.py +14 -0
- package/vendor/insane-search/engine/content_safety.py +151 -0
- package/vendor/insane-search/engine/fetch_chain.py +32 -0
- package/vendor/insane-search/engine/tests/test_u1.py +28 -0
- package/vendor/insane-search/engine/tests/test_u8.py +216 -0
- package/vendor/insane-search/engine/transport.py +3 -2
- package/vendor/insane-search/engine/validators.py +14 -0
- package/dist/types/tools/inspect-image-renderer.d.ts +0 -26
- package/dist/types/tools/inspect-image.d.ts +0 -31
- package/src/prompts/tools/inspect-image-system.md +0 -20
- package/src/prompts/tools/inspect-image.md +0 -32
- package/src/tools/inspect-image-renderer.ts +0 -103
- package/src/tools/inspect-image.ts +0 -172
|
@@ -39,6 +39,11 @@ export declare function renderUltragoalStatusMarkdown(summary: {
|
|
|
39
39
|
};
|
|
40
40
|
counts: Record<string, number>;
|
|
41
41
|
goals: unknown[];
|
|
42
|
+
nudgeBudget?: number;
|
|
43
|
+
nudgeCount?: number;
|
|
44
|
+
nudgeRemaining?: number;
|
|
45
|
+
nudgeGoalId?: string;
|
|
46
|
+
nudgeTargetKind?: string;
|
|
42
47
|
}): string;
|
|
43
48
|
export declare function renderTeamStatusMarkdown(snapshot: {
|
|
44
49
|
team_name: string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type UltragoalGoal, type UltragoalLedgerEvent, type UltragoalPlan, type UltragoalReceiptKind } from "./ultragoal-runtime";
|
|
1
|
+
import { type UltragoalGoal, type UltragoalLedgerEvent, type UltragoalNudgeSurface, type UltragoalPlan, type UltragoalReceiptKind } from "./ultragoal-runtime";
|
|
2
2
|
export type UltragoalGuardState = "inactive" | "unrelated_goal" | "active_verified_complete" | "active_missing_receipt" | "active_stale_receipt" | "active_missing_final_receipt" | "active_dirty_quality_gate" | "active_review_blocked_unrecorded" | "active_review_blocked_recorded" | "unreadable_fail_closed";
|
|
3
3
|
export interface UltragoalGuardDiagnostic {
|
|
4
4
|
state: UltragoalGuardState;
|
|
@@ -30,6 +30,29 @@ export declare function readUltragoalVerificationState(input: {
|
|
|
30
30
|
sessionId?: string | null;
|
|
31
31
|
}): Promise<UltragoalGuardDiagnostic>;
|
|
32
32
|
export declare function isUltragoalAskBlocked(cwd: string): Promise<UltragoalAskBlockDiagnostic>;
|
|
33
|
+
/**
|
|
34
|
+
* Escalating per-attempt refusal text. Deliberately avoids every
|
|
35
|
+
* `isUltragoalBypassPrompt` trigger: no `update_goal(`, no "skip/weaken verification",
|
|
36
|
+
* no "mark ... complete", no "--status complete", and the word "complete" never
|
|
37
|
+
* appears (so the `goal` ... `complete` proximity rule cannot match).
|
|
38
|
+
*/
|
|
39
|
+
export declare function formatUltragoalNudgeMessage(input: {
|
|
40
|
+
surface: UltragoalNudgeSurface;
|
|
41
|
+
attempt: number;
|
|
42
|
+
budget: number;
|
|
43
|
+
goalId: string;
|
|
44
|
+
}): string;
|
|
45
|
+
/**
|
|
46
|
+
* Assert-path entry for the `ask` surface (the ask guard lives in another module).
|
|
47
|
+
* Resolves the active (leader) Ultragoal session so subagent/headless asks consume
|
|
48
|
+
* the leader run's budget rather than a fresh child ledger.
|
|
49
|
+
*/
|
|
50
|
+
export declare function consumeUltragoalAskNudge(cwd: string, sessionId?: string | null): Promise<{
|
|
51
|
+
nudged: true;
|
|
52
|
+
message: string;
|
|
53
|
+
} | {
|
|
54
|
+
nudged: false;
|
|
55
|
+
}>;
|
|
33
56
|
export declare function assertCanCompleteCurrentGoal(input: {
|
|
34
57
|
cwd: string;
|
|
35
58
|
currentGoal?: CurrentGoalLike | null;
|
|
@@ -49,3 +72,16 @@ export interface UltragoalPauseBlockDiagnostic {
|
|
|
49
72
|
*/
|
|
50
73
|
export declare function isUltragoalPauseBlocked(cwd: string): Promise<UltragoalPauseBlockDiagnostic>;
|
|
51
74
|
export declare function assertUltragoalPauseAllowed(cwd: string): Promise<void>;
|
|
75
|
+
/**
|
|
76
|
+
* Guard `goal({"op":"drop"})` during an active Ultragoal run. A *real give-up* (an
|
|
77
|
+
* aggregate run still mid-flight with incomplete required stories) is nudged while
|
|
78
|
+
* budget remains; once exhausted it falls through to today's drop behavior. Legitimate
|
|
79
|
+
* aggregate-reset drops — no durable run, unrelated goal, an already dropped/stale
|
|
80
|
+
* aggregate, or an all-stories-complete run — are never nudged. If durable state
|
|
81
|
+
* exists but cannot be read to classify the drop, fail closed.
|
|
82
|
+
*/
|
|
83
|
+
export declare function assertUltragoalDropAllowed(input: {
|
|
84
|
+
cwd: string;
|
|
85
|
+
currentGoal?: CurrentGoalLike | null;
|
|
86
|
+
sessionId?: string | null;
|
|
87
|
+
}): Promise<void>;
|
|
@@ -53,6 +53,41 @@ export interface UltragoalLedgerEvent extends JsonObject {
|
|
|
53
53
|
goalId?: string;
|
|
54
54
|
timestamp?: string;
|
|
55
55
|
}
|
|
56
|
+
export type UltragoalNudgeSurface = "pause" | "drop" | "ask" | "premature_complete";
|
|
57
|
+
export type UltragoalNudgeTargetKind = "story" | "final_aggregate_receipt";
|
|
58
|
+
export interface UltragoalNudgeLedgerEvent extends UltragoalLedgerEvent {
|
|
59
|
+
event: "nudge";
|
|
60
|
+
goalId: string;
|
|
61
|
+
targetKind: UltragoalNudgeTargetKind;
|
|
62
|
+
surface: UltragoalNudgeSurface;
|
|
63
|
+
attempt: number;
|
|
64
|
+
budget: number;
|
|
65
|
+
reason: string;
|
|
66
|
+
currentGoalObjective?: string;
|
|
67
|
+
}
|
|
68
|
+
export interface UltragoalNudgeTarget {
|
|
69
|
+
goalId: string;
|
|
70
|
+
targetKind: UltragoalNudgeTargetKind;
|
|
71
|
+
}
|
|
72
|
+
export type UltragoalNudgeOutcome = {
|
|
73
|
+
nudged: true;
|
|
74
|
+
attempt: number;
|
|
75
|
+
budget: number;
|
|
76
|
+
goalId: string;
|
|
77
|
+
targetKind: UltragoalNudgeTargetKind;
|
|
78
|
+
event: UltragoalNudgeLedgerEvent;
|
|
79
|
+
} | {
|
|
80
|
+
nudged: false;
|
|
81
|
+
exhausted: true;
|
|
82
|
+
count: number;
|
|
83
|
+
budget: number;
|
|
84
|
+
goalId: string;
|
|
85
|
+
targetKind: UltragoalNudgeTargetKind;
|
|
86
|
+
} | {
|
|
87
|
+
nudged: false;
|
|
88
|
+
inactive: true;
|
|
89
|
+
reason: string;
|
|
90
|
+
};
|
|
56
91
|
export interface UltragoalPaths {
|
|
57
92
|
dir: string;
|
|
58
93
|
briefPath: string;
|
|
@@ -67,6 +102,11 @@ export interface UltragoalStatusSummary {
|
|
|
67
102
|
currentGoal?: UltragoalGoal;
|
|
68
103
|
counts: Record<UltragoalGoalStatus, number>;
|
|
69
104
|
goals: UltragoalGoal[];
|
|
105
|
+
nudgeBudget?: number;
|
|
106
|
+
nudgeCount?: number;
|
|
107
|
+
nudgeRemaining?: number;
|
|
108
|
+
nudgeGoalId?: string;
|
|
109
|
+
nudgeTargetKind?: UltragoalNudgeTargetKind;
|
|
70
110
|
}
|
|
71
111
|
export interface UltragoalCommandResult {
|
|
72
112
|
reviewBlockerGoalIds?: string[];
|
|
@@ -82,6 +122,46 @@ interface JsonObject {
|
|
|
82
122
|
export declare function hashStructuredValue(value: unknown): string;
|
|
83
123
|
export declare function getUltragoalPaths(cwd: string, sessionId?: string | null): UltragoalPaths;
|
|
84
124
|
export declare function readUltragoalLedger(cwd: string, sessionId?: string | null): Promise<UltragoalLedgerEvent[]>;
|
|
125
|
+
export declare const DEFAULT_ULTRAGOAL_NUDGE_BUDGET = 10;
|
|
126
|
+
/** Pure: count ledger `nudge` rows for an exact goalId. */
|
|
127
|
+
export declare function countUltragoalNudges(ledger: readonly UltragoalLedgerEvent[], goalId: string): number;
|
|
128
|
+
/**
|
|
129
|
+
* Resolve the per-story nudge budget. Project `./.skc/settings.json` overrides the
|
|
130
|
+
* user settings (`$SKC_CONFIG_DIR/settings.json` or `~/.skc/settings.json`), else the
|
|
131
|
+
* default. Mirrors the `skc.deepInterview.ambiguityThreshold` user+project precedence.
|
|
132
|
+
*/
|
|
133
|
+
export declare function resolveUltragoalNudgeBudget(cwd: string): Promise<{
|
|
134
|
+
budget: number;
|
|
135
|
+
source: string;
|
|
136
|
+
}>;
|
|
137
|
+
/**
|
|
138
|
+
* Pure canonical selector shared by guards and status so `nudgeGoalId` can never
|
|
139
|
+
* diverge between what a guard consumes and what status displays. Prefers the active
|
|
140
|
+
* current-goal objective, then active > pending > failed (matching `chooseNextGoal`),
|
|
141
|
+
* then the aggregate final-receipt target when all stories are complete but the
|
|
142
|
+
* aggregate run still needs a final receipt. Returns null for verified-complete or
|
|
143
|
+
* absent/unrelated plans.
|
|
144
|
+
*/
|
|
145
|
+
export declare function selectUltragoalNudgeTarget(plan: UltragoalPlan, options?: {
|
|
146
|
+
currentGoalObjective?: string;
|
|
147
|
+
retryFailed?: boolean;
|
|
148
|
+
}): UltragoalNudgeTarget | null;
|
|
149
|
+
/**
|
|
150
|
+
* Atomic consuming writer. Locks the ledger path, rereads + counts nudge rows for the
|
|
151
|
+
* target story, and appends exactly one `nudge` row inside the same critical section
|
|
152
|
+
* only while budget remains. Reuses the lockless `appendLedger` inside the lock (it
|
|
153
|
+
* does not acquire a conflicting lock), so concurrent guarded attempts cannot both
|
|
154
|
+
* observe `count = budget - 1` and overshoot the budget.
|
|
155
|
+
*/
|
|
156
|
+
export declare function recordUltragoalNudgeIfBudgetRemaining(input: {
|
|
157
|
+
cwd: string;
|
|
158
|
+
sessionId?: string | null;
|
|
159
|
+
target: UltragoalNudgeTarget;
|
|
160
|
+
surface: UltragoalNudgeSurface;
|
|
161
|
+
budget: number;
|
|
162
|
+
reason: string;
|
|
163
|
+
currentGoalObjective?: string;
|
|
164
|
+
}): Promise<UltragoalNudgeOutcome>;
|
|
85
165
|
export declare function computeUltragoalPlanGeneration(input: {
|
|
86
166
|
plan: UltragoalPlan;
|
|
87
167
|
ledger: readonly UltragoalLedgerEvent[];
|
|
@@ -28,6 +28,10 @@ export interface TabSession {
|
|
|
28
28
|
kindTag: BrowserKindTag;
|
|
29
29
|
/** Session that acquired this tab; used for session-scoped teardown (F13). */
|
|
30
30
|
ownerId?: string;
|
|
31
|
+
/** Unix-ms timestamp of the last acquire/run activity; drives GC idle + LRU ordering. */
|
|
32
|
+
lastUsedAt: number;
|
|
33
|
+
/** Set synchronously by the shared begin-release guard so concurrent release is a no-op. */
|
|
34
|
+
releasing?: boolean;
|
|
31
35
|
}
|
|
32
36
|
export interface AcquireTabOptions {
|
|
33
37
|
url?: string;
|
|
@@ -58,6 +62,33 @@ export interface ReleaseTabOptions {
|
|
|
58
62
|
kill?: boolean;
|
|
59
63
|
}
|
|
60
64
|
export declare function getTab(name: string): TabSession | undefined;
|
|
65
|
+
/** Read-only, GC-facing projection of a live tab. Never exposes the mutable `tabs` map. */
|
|
66
|
+
export interface TabGcSnapshot {
|
|
67
|
+
name: string;
|
|
68
|
+
ownerId?: string;
|
|
69
|
+
state: TabSession["state"];
|
|
70
|
+
pendingCount: number;
|
|
71
|
+
kindTag: BrowserKindTag;
|
|
72
|
+
lastUsedAt: number;
|
|
73
|
+
browserRefCount: number;
|
|
74
|
+
}
|
|
75
|
+
export interface BrowserGcEligibilityPolicy {
|
|
76
|
+
now: () => number;
|
|
77
|
+
idleMs: number;
|
|
78
|
+
}
|
|
79
|
+
/** Snapshot every live tab for GC ordering/diagnostics. */
|
|
80
|
+
export declare function listTabsForGc(): TabGcSnapshot[];
|
|
81
|
+
/**
|
|
82
|
+
* Evict a tab only if it is still GC-eligible against the LIVE supervisor state. The full
|
|
83
|
+
* predicate is checked synchronously and `releaseTab` is invoked with no intervening await,
|
|
84
|
+
* so a tab that became busy after a GC snapshot cannot be closed (its run rejected). Returns
|
|
85
|
+
* true only when the tab was actually released.
|
|
86
|
+
*/
|
|
87
|
+
export declare function releaseTabIfGcEligible(name: string, policy: BrowserGcEligibilityPolicy): Promise<boolean>;
|
|
88
|
+
/** Test-only: install a fabricated tab session. */
|
|
89
|
+
export declare function setTabForTest(tab: TabSession): void;
|
|
90
|
+
/** Test-only: clear the tab registry between cases. */
|
|
91
|
+
export declare function clearTabsForTest(): void;
|
|
61
92
|
export declare function acquireTab(name: string, browser: BrowserHandle, opts: AcquireTabOptions): Promise<AcquireTabResult>;
|
|
62
93
|
export declare function runInTab(name: string, opts: RunInTabOptions): Promise<RunResultOk>;
|
|
63
94
|
export declare function releaseTab(name: string, opts?: ReleaseTabOptions): Promise<boolean>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** Prefix of every computer-use screenshot fallback directory created under the OS temp dir. */
|
|
2
|
+
export declare const SCREENSHOT_FALLBACK_DIR_PREFIX = "skc-computer-screenshots-";
|
|
3
|
+
/** Lazy-arm marker: called when the first screenshot fallback dir is created this process. */
|
|
4
|
+
export declare function markScreenshotFallbackDirCreatedForGc(): void;
|
|
5
|
+
/** Whether any screenshot fallback dir has been created this process (GC lazy-arm gate). */
|
|
6
|
+
export declare function hasCreatedScreenshotFallbackDir(): boolean;
|
|
7
|
+
/** Test-only: reset the lazy-arm marker between cases. */
|
|
8
|
+
export declare function resetScreenshotFallbackGcForTest(): void;
|
|
9
|
+
export interface ScreenshotGcOptions {
|
|
10
|
+
now: () => number;
|
|
11
|
+
staleMs: number;
|
|
12
|
+
/** Base dir to scan; defaults to os.tmpdir(). Injectable for tests. */
|
|
13
|
+
tmpDir?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Disk-only GC for stale computer-use screenshot fallback directories. Scans the temp dir for
|
|
17
|
+
* `skc-computer-screenshots-*` directories and removes those whose mtime is older than `staleMs`.
|
|
18
|
+
* Never throws on a per-directory failure; the whole sweep is best-effort.
|
|
19
|
+
*/
|
|
20
|
+
export declare function cleanupStaleScreenshotFallbackDirs(options: ScreenshotGcOptions): Promise<{
|
|
21
|
+
scanned: number;
|
|
22
|
+
removed: number;
|
|
23
|
+
}>;
|
|
@@ -5,18 +5,18 @@ import type { ToolSession } from "./index";
|
|
|
5
5
|
export declare const MAX_CRON_TASKS_PER_OWNER = 50;
|
|
6
6
|
/** Recurring tasks auto-expire 7 days after creation (mirrors upstream). */
|
|
7
7
|
export declare const CRON_RECURRING_MAX_AGE_MS: number;
|
|
8
|
-
declare const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
id: z.ZodString
|
|
18
|
-
}, z.core.$
|
|
19
|
-
export type
|
|
8
|
+
declare const cronSchema: z.ZodObject<{
|
|
9
|
+
op: z.ZodEnum<{
|
|
10
|
+
create: "create";
|
|
11
|
+
delete: "delete";
|
|
12
|
+
list: "list";
|
|
13
|
+
}>;
|
|
14
|
+
cron_expression: z.ZodOptional<z.ZodString>;
|
|
15
|
+
prompt: z.ZodOptional<z.ZodString>;
|
|
16
|
+
recurring: z.ZodOptional<z.ZodBoolean>;
|
|
17
|
+
id: z.ZodOptional<z.ZodString>;
|
|
18
|
+
}, z.core.$strict>;
|
|
19
|
+
export type CronParams = z.infer<typeof cronSchema>;
|
|
20
20
|
export interface CronJobSnapshot {
|
|
21
21
|
id: string;
|
|
22
22
|
cron_expression: string;
|
|
@@ -35,18 +35,14 @@ export interface CronListJobDetails {
|
|
|
35
35
|
prompt: string;
|
|
36
36
|
humanSchedule: string;
|
|
37
37
|
}
|
|
38
|
-
export interface
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
export interface CronToolDetails {
|
|
39
|
+
op: "create" | "list" | "delete";
|
|
40
|
+
id?: string;
|
|
41
|
+
cron_expression?: string;
|
|
42
|
+
recurring?: boolean;
|
|
42
43
|
nextFireAt?: number;
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
jobs: CronListJobDetails[];
|
|
46
|
-
}
|
|
47
|
-
export interface CronDeleteToolDetails {
|
|
48
|
-
id: string;
|
|
49
|
-
deleted: boolean;
|
|
44
|
+
jobs?: CronListJobDetails[];
|
|
45
|
+
deleted?: boolean;
|
|
50
46
|
}
|
|
51
47
|
/** Clear every schedule for an owner. Exported for tests + lifecycle teardown. */
|
|
52
48
|
export declare function clearOwnerSchedules(ownerId: string | undefined): void;
|
|
@@ -68,49 +64,28 @@ export declare function calculateCronFireTimeMs(params: {
|
|
|
68
64
|
nowMs: number;
|
|
69
65
|
expiresAt?: number;
|
|
70
66
|
}): number;
|
|
71
|
-
export declare class
|
|
72
|
-
private
|
|
73
|
-
readonly name = "CronCreate";
|
|
74
|
-
readonly label = "CronCreate";
|
|
75
|
-
readonly summary = "Schedule a prompt on a 5-field cron expression";
|
|
76
|
-
readonly description: string;
|
|
77
|
-
readonly parameters: z.ZodObject<{
|
|
78
|
-
cron_expression: z.ZodString;
|
|
79
|
-
prompt: z.ZodString;
|
|
80
|
-
recurring: z.ZodDefault<z.ZodBoolean>;
|
|
81
|
-
}, z.core.$strip>;
|
|
82
|
-
readonly strict = true;
|
|
83
|
-
readonly loadMode = "discoverable";
|
|
84
|
-
constructor(session: ToolSession);
|
|
85
|
-
static createIf(session: ToolSession): CronCreateTool | null;
|
|
86
|
-
execute(_toolCallId: string, params: CronCreateParams, _signal?: AbortSignal, _onUpdate?: AgentToolUpdateCallback<CronCreateToolDetails>, _context?: AgentToolContext): Promise<AgentToolResult<CronCreateToolDetails>>;
|
|
87
|
-
}
|
|
88
|
-
export declare class CronListTool implements AgentTool<typeof cronListSchema, CronListToolDetails> {
|
|
89
|
-
private readonly session;
|
|
90
|
-
readonly name = "CronList";
|
|
91
|
-
readonly label = "CronList";
|
|
92
|
-
readonly summary = "List scheduled cron jobs";
|
|
93
|
-
readonly description: string;
|
|
94
|
-
readonly parameters: z.ZodObject<{}, z.core.$strip>;
|
|
95
|
-
readonly strict = true;
|
|
96
|
-
readonly loadMode = "discoverable";
|
|
97
|
-
constructor(session: ToolSession);
|
|
98
|
-
static createIf(session: ToolSession): CronListTool | null;
|
|
99
|
-
execute(_toolCallId: string, _params: CronListParams, _signal?: AbortSignal, _onUpdate?: AgentToolUpdateCallback<CronListToolDetails>, _context?: AgentToolContext): Promise<AgentToolResult<CronListToolDetails>>;
|
|
100
|
-
}
|
|
101
|
-
export declare class CronDeleteTool implements AgentTool<typeof cronDeleteSchema, CronDeleteToolDetails> {
|
|
67
|
+
export declare class CronTool implements AgentTool<typeof cronSchema, CronToolDetails> {
|
|
68
|
+
#private;
|
|
102
69
|
private readonly session;
|
|
103
|
-
readonly name = "
|
|
104
|
-
readonly label = "
|
|
105
|
-
readonly summary = "
|
|
70
|
+
readonly name = "cron";
|
|
71
|
+
readonly label = "Cron";
|
|
72
|
+
readonly summary = "Schedule, list, and cancel cron-style prompts (op: create | list | delete)";
|
|
106
73
|
readonly description: string;
|
|
107
74
|
readonly parameters: z.ZodObject<{
|
|
108
|
-
|
|
109
|
-
|
|
75
|
+
op: z.ZodEnum<{
|
|
76
|
+
create: "create";
|
|
77
|
+
delete: "delete";
|
|
78
|
+
list: "list";
|
|
79
|
+
}>;
|
|
80
|
+
cron_expression: z.ZodOptional<z.ZodString>;
|
|
81
|
+
prompt: z.ZodOptional<z.ZodString>;
|
|
82
|
+
recurring: z.ZodOptional<z.ZodBoolean>;
|
|
83
|
+
id: z.ZodOptional<z.ZodString>;
|
|
84
|
+
}, z.core.$strict>;
|
|
110
85
|
readonly strict = true;
|
|
111
86
|
readonly loadMode = "discoverable";
|
|
112
87
|
constructor(session: ToolSession);
|
|
113
|
-
static createIf(session: ToolSession):
|
|
114
|
-
execute(_toolCallId: string, params:
|
|
88
|
+
static createIf(session: ToolSession): CronTool | null;
|
|
89
|
+
execute(_toolCallId: string, params: CronParams, _signal?: AbortSignal, _onUpdate?: AgentToolUpdateCallback<CronToolDetails>, _context?: AgentToolContext): Promise<AgentToolResult<CronToolDetails>>;
|
|
115
90
|
}
|
|
116
91
|
export {};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { Settings } from "../config/settings";
|
|
2
|
+
import { type TabGcSnapshot } from "./browser/tab-supervisor";
|
|
3
|
+
export interface BrowserGcPolicy {
|
|
4
|
+
enabled: boolean;
|
|
5
|
+
idleMs: number;
|
|
6
|
+
rssLimitBytes: number;
|
|
7
|
+
}
|
|
8
|
+
export interface ComputerGcPolicy {
|
|
9
|
+
enabled: boolean;
|
|
10
|
+
staleMs: number;
|
|
11
|
+
scanIntervalMs: number;
|
|
12
|
+
}
|
|
13
|
+
export declare function resolveBrowserGcPolicy(settings: Settings): BrowserGcPolicy;
|
|
14
|
+
export declare function resolveComputerGcPolicy(settings: Settings): ComputerGcPolicy;
|
|
15
|
+
export declare function resolveSweepIntervalMs(settings: Settings): number;
|
|
16
|
+
/** Injectable seams so the controller is fully testable without real browsers/filesystem/RSS. */
|
|
17
|
+
export interface ResourceGcDeps {
|
|
18
|
+
now: () => number;
|
|
19
|
+
rssBytes: () => number;
|
|
20
|
+
logWarn: (msg: string, meta?: Record<string, unknown>) => void;
|
|
21
|
+
listTabs: () => TabGcSnapshot[];
|
|
22
|
+
releaseTab: (name: string, policy: {
|
|
23
|
+
now: () => number;
|
|
24
|
+
idleMs: number;
|
|
25
|
+
}) => Promise<boolean>;
|
|
26
|
+
cleanupScreenshots: (opts: {
|
|
27
|
+
now: () => number;
|
|
28
|
+
staleMs: number;
|
|
29
|
+
}) => Promise<{
|
|
30
|
+
scanned: number;
|
|
31
|
+
removed: number;
|
|
32
|
+
}>;
|
|
33
|
+
screenshotArmed: () => boolean;
|
|
34
|
+
}
|
|
35
|
+
export interface ResourceGcRegistration {
|
|
36
|
+
sessionId: string;
|
|
37
|
+
settings: Settings;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Register a session with the resource GC. Starts the single shared timer on the first
|
|
41
|
+
* registration. Returns an idempotent unregister function; the timer stops only when the last
|
|
42
|
+
* session unregisters.
|
|
43
|
+
*/
|
|
44
|
+
export declare function registerResourceGcSession(reg: ResourceGcRegistration): () => void;
|
|
45
|
+
export declare function sweepOnce(d?: ResourceGcDeps): Promise<void>;
|
|
46
|
+
export declare function __setResourceGcDepsForTest(overrides: Partial<ResourceGcDeps>): void;
|
|
47
|
+
export declare function __runResourceGcTickForTest(): Promise<void>;
|
|
48
|
+
export declare function __getResourceGcStateForTest(): {
|
|
49
|
+
timerActive: boolean;
|
|
50
|
+
sessionCount: number;
|
|
51
|
+
rssWarningActive: boolean;
|
|
52
|
+
inProgress: boolean;
|
|
53
|
+
};
|
|
54
|
+
export declare function __resetResourceGcForTest(): void;
|
|
@@ -111,5 +111,6 @@ export declare class WebSearchTool implements AgentTool<typeof webSearchSchema,
|
|
|
111
111
|
export declare const webSearchCustomTool: CustomTool<typeof webSearchSchema, SearchRenderDetails>;
|
|
112
112
|
export declare function getSearchTools(): CustomTool<any, any>[];
|
|
113
113
|
export { getSearchProvider, setPreferredSearchProvider, setSearchFallbackProviders } from "./provider";
|
|
114
|
+
export { setSearchHardTimeoutMs } from "./providers/utils";
|
|
114
115
|
export type { SearchProviderId as SearchProvider, SearchResponse } from "./types";
|
|
115
116
|
export { isConfigurableSearchProviderId, isSearchProviderPreference } from "./types";
|
|
@@ -15,16 +15,23 @@ import { SearchProviderError, type SearchProviderId, type SearchSource } from ".
|
|
|
15
15
|
*/
|
|
16
16
|
export declare function findCredential(storage: AgentStorage | null | undefined, envKey: string | null | undefined, ...storageProviders: string[]): string | null;
|
|
17
17
|
/**
|
|
18
|
-
* Default hard ceiling for a single web-search round-trip.
|
|
18
|
+
* Default hard ceiling for a single web-search round-trip. 300s tolerates
|
|
19
19
|
* legitimate slow LLM-mediated responses (anthropic web_search_20250305,
|
|
20
20
|
* perplexity, gemini, OpenAI code backend) while still guaranteeing the session unfreezes
|
|
21
|
-
*
|
|
21
|
+
* if Bun's `AbortSignal` fails to propagate on Windows.
|
|
22
22
|
*
|
|
23
23
|
* Pure search APIs (brave, exa, jina, tavily, searxng, synthetic, zai)
|
|
24
24
|
* settle far faster in practice; reusing the same ceiling keeps the wiring
|
|
25
25
|
* uniform without compromising correctness.
|
|
26
26
|
*/
|
|
27
|
-
export declare const SEARCH_HARD_TIMEOUT_MS =
|
|
27
|
+
export declare const SEARCH_HARD_TIMEOUT_MS = 300000;
|
|
28
|
+
/**
|
|
29
|
+
* Override the hard timeout applied to every web-search round-trip.
|
|
30
|
+
*
|
|
31
|
+
* @param ms - Hard timeout in milliseconds. Non-finite or non-positive
|
|
32
|
+
* values reset the timeout to {@link SEARCH_HARD_TIMEOUT_MS}.
|
|
33
|
+
*/
|
|
34
|
+
export declare function setSearchHardTimeoutMs(ms: number | undefined): void;
|
|
28
35
|
/**
|
|
29
36
|
* Compose a caller-supplied {@link AbortSignal} with a hard timeout so an
|
|
30
37
|
* outbound `fetch()` is guaranteed to settle within `ms` even when the
|
|
@@ -36,7 +43,7 @@ export declare const SEARCH_HARD_TIMEOUT_MS = 60000;
|
|
|
36
43
|
* because the user's Esc is never delivered to the native layer.
|
|
37
44
|
*
|
|
38
45
|
* @param signal - Caller cancellation signal, if any.
|
|
39
|
-
* @param ms - Hard timeout in milliseconds. Defaults to
|
|
46
|
+
* @param ms - Hard timeout in milliseconds. Defaults to the configured value.
|
|
40
47
|
*/
|
|
41
48
|
export declare function withHardTimeout(signal: AbortSignal | undefined, ms?: number): AbortSignal;
|
|
42
49
|
/**
|
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.5",
|
|
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",
|
|
@@ -52,12 +52,12 @@
|
|
|
52
52
|
"@agentclientprotocol/sdk": "0.21.0",
|
|
53
53
|
"@babel/parser": "^7.29.3",
|
|
54
54
|
"@mozilla/readability": "^0.6.0",
|
|
55
|
-
"@sayknow-cli/stats": "0.3.
|
|
56
|
-
"@sayknow-cli/agent-core": "0.3.
|
|
57
|
-
"@sayknow-cli/ai": "0.3.
|
|
58
|
-
"@sayknow-cli/natives": "0.3.
|
|
59
|
-
"@sayknow-cli/tui": "0.3.
|
|
60
|
-
"@sayknow-cli/utils": "0.3.
|
|
55
|
+
"@sayknow-cli/stats": "0.3.5",
|
|
56
|
+
"@sayknow-cli/agent-core": "0.3.5",
|
|
57
|
+
"@sayknow-cli/ai": "0.3.5",
|
|
58
|
+
"@sayknow-cli/natives": "0.3.5",
|
|
59
|
+
"@sayknow-cli/tui": "0.3.5",
|
|
60
|
+
"@sayknow-cli/utils": "0.3.5",
|
|
61
61
|
"@puppeteer/browsers": "^2.13.0",
|
|
62
62
|
"@types/turndown": "5.0.6",
|
|
63
63
|
"@xterm/headless": "^6.0.0",
|
|
@@ -28,6 +28,11 @@ function fail(msg: string): void {
|
|
|
28
28
|
function walk(dir: string): string[] {
|
|
29
29
|
const out: string[] = [];
|
|
30
30
|
for (const entry of readdirSync(dir)) {
|
|
31
|
+
// node_modules holds locally-installed Phase 3 runtime deps (gitignored,
|
|
32
|
+
// never vendored and never packed by npm). Vendor checks apply only to
|
|
33
|
+
// vendored upstream source; third-party package files legitimately carry
|
|
34
|
+
// paths like ".../references/" that would otherwise false-positive.
|
|
35
|
+
if (entry === "node_modules") continue;
|
|
31
36
|
const full = join(dir, entry);
|
|
32
37
|
if (statSync(full).isDirectory()) out.push(...walk(full));
|
|
33
38
|
else out.push(full);
|
package/src/cli/args.ts
CHANGED
|
@@ -284,7 +284,6 @@ ${chalk.bold("Available Tools (default-enabled unless noted):")}
|
|
|
284
284
|
lsp - Language server protocol (code intelligence)
|
|
285
285
|
python - Execute Python code (requires: ${APP_NAME} setup python)
|
|
286
286
|
notebook - Edit Jupyter notebooks
|
|
287
|
-
inspect_image - Analyze images with a vision model
|
|
288
287
|
browser - Browser automation (Puppeteer)
|
|
289
288
|
task - Launch sub-agents for parallel tasks
|
|
290
289
|
todo_write - Manage todo/task lists
|
package/src/cli/fast-help.ts
CHANGED
|
@@ -97,7 +97,6 @@ Available Tools (default-enabled unless noted):
|
|
|
97
97
|
lsp - Language server protocol (code intelligence)
|
|
98
98
|
python - Execute Python code (requires: ${APP_NAME} setup python)
|
|
99
99
|
notebook - Edit Jupyter notebooks
|
|
100
|
-
inspect_image - Analyze images with a vision model
|
|
101
100
|
browser - Browser automation (Puppeteer)
|
|
102
101
|
task - Launch sub-agents for parallel tasks
|
|
103
102
|
todo_write - Manage todo/task lists
|
package/src/cli/plugin-cli.ts
CHANGED
|
@@ -16,8 +16,8 @@ import {
|
|
|
16
16
|
getPluginsCacheDir,
|
|
17
17
|
MarketplaceManager,
|
|
18
18
|
} from "../extensibility/plugins/marketplace/index.js";
|
|
19
|
-
import { installSkcPluginBundle, isSkcPluginBundleSource, readRegistry } from "../extensibility/skc-plugins";
|
|
20
19
|
import { type ScanReport, scanPluginDir } from "../extensibility/plugins/security-scanner";
|
|
20
|
+
import { installSkcPluginBundle, isSkcPluginBundleSource, readRegistry } from "../extensibility/skc-plugins";
|
|
21
21
|
import { theme } from "../modes/theme/theme";
|
|
22
22
|
|
|
23
23
|
// =============================================================================
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
type SearchQueryParams,
|
|
16
16
|
} from "../web/search/index";
|
|
17
17
|
import { SEARCH_PROVIDER_ORDER, setPreferredSearchProvider, setSearchFallbackProviders } from "../web/search/provider";
|
|
18
|
+
import { setSearchHardTimeoutMs } from "../web/search/providers/utils";
|
|
18
19
|
import { renderSearchResult } from "../web/search/render";
|
|
19
20
|
import type { SearchProviderId } from "../web/search/types";
|
|
20
21
|
|
|
@@ -179,6 +180,10 @@ export async function runSearchCommand(cmd: SearchCommandArgs): Promise<void> {
|
|
|
179
180
|
configuredFallback.filter(value => typeof value === "string" && isConfigurableSearchProviderId(value)),
|
|
180
181
|
);
|
|
181
182
|
}
|
|
183
|
+
const configuredTimeout = settings.get("web_search.timeout");
|
|
184
|
+
if (typeof configuredTimeout === "number" && Number.isFinite(configuredTimeout) && configuredTimeout > 0) {
|
|
185
|
+
setSearchHardTimeoutMs(configuredTimeout * 1000);
|
|
186
|
+
}
|
|
182
187
|
|
|
183
188
|
const params: SearchQueryParams = {
|
|
184
189
|
query: cmd.query,
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
} from "./model-profiles";
|
|
10
10
|
import {
|
|
11
11
|
isAuthenticated,
|
|
12
|
+
kNoAuth,
|
|
12
13
|
type ModelRegistry,
|
|
13
14
|
SKC_MODEL_ASSIGNMENT_TARGETS,
|
|
14
15
|
type SkcModelAssignmentTargetId,
|
|
@@ -192,7 +193,12 @@ export async function prepareModelProfileActivation(
|
|
|
192
193
|
const authenticatedProviders: string[] = [];
|
|
193
194
|
for (const provider of allProviders) {
|
|
194
195
|
const apiKey = await options.modelRegistry.getApiKeyForProvider(provider, options.session.sessionId);
|
|
195
|
-
|
|
196
|
+
// kNoAuth ("N/A") is the sentinel for keyless/no-auth providers (local LLMs,
|
|
197
|
+
// `--auth none` custom providers): usable WITHOUT a key. isAuthenticated()
|
|
198
|
+
// rejects it, so gate on usability ("a credential resolved OR keyless")
|
|
199
|
+
// rather than "has a real key" — otherwise activating a keyless profile
|
|
200
|
+
// throws a spurious "requires credentials" error and the model never applies.
|
|
201
|
+
if (!isAuthenticated(apiKey) && apiKey !== kNoAuth) {
|
|
196
202
|
missingProviders.push(provider);
|
|
197
203
|
} else {
|
|
198
204
|
authenticatedProviders.push(provider);
|
|
@@ -23,8 +23,8 @@ export interface ModelProfileDefinition {
|
|
|
23
23
|
|
|
24
24
|
export interface ResolvedProfileBinding {
|
|
25
25
|
defaultSelector?: string;
|
|
26
|
-
modelRoles:
|
|
27
|
-
agentModelOverrides: Partial<Record<Exclude<ModelProfileRole, "default"
|
|
26
|
+
modelRoles: Record<string, string>;
|
|
27
|
+
agentModelOverrides: Partial<Record<Exclude<ModelProfileRole, "default">, string>>;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
function parseModelSelectorProvider(selector: string): string | undefined {
|
|
@@ -383,9 +383,8 @@ export function mergeModelProfiles(userProfiles?: ModelsConfig["profiles"]): Map
|
|
|
383
383
|
}
|
|
384
384
|
|
|
385
385
|
export function resolveProfileBindings(definition: ModelProfileDefinition): ResolvedProfileBinding {
|
|
386
|
-
const { default: defaultSelector,
|
|
386
|
+
const { default: defaultSelector, executor, architect, planner, critic } = definition.modelMapping;
|
|
387
387
|
const modelRoles: ResolvedProfileBinding["modelRoles"] = {};
|
|
388
|
-
if (vision !== undefined) modelRoles.vision = vision;
|
|
389
388
|
const agentModelOverrides: ResolvedProfileBinding["agentModelOverrides"] = {};
|
|
390
389
|
if (executor !== undefined) agentModelOverrides.executor = executor;
|
|
391
390
|
if (architect !== undefined) agentModelOverrides.architect = architect;
|
|
@@ -70,7 +70,7 @@ export function isAuthenticated(apiKey: string | undefined | null): apiKey is st
|
|
|
70
70
|
return Boolean(apiKey) && apiKey !== kNoAuth;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
export type ModelRole = "default"
|
|
73
|
+
export type ModelRole = "default";
|
|
74
74
|
|
|
75
75
|
export interface ModelRoleInfo {
|
|
76
76
|
tag?: string;
|
|
@@ -80,12 +80,11 @@ export interface ModelRoleInfo {
|
|
|
80
80
|
|
|
81
81
|
export const MODEL_ROLES: Record<ModelRole, ModelRoleInfo> = {
|
|
82
82
|
default: { tag: "DEFAULT", name: "Default", color: "success" },
|
|
83
|
-
vision: { tag: "VISION", name: "Vision", color: "accent" },
|
|
84
83
|
};
|
|
85
84
|
|
|
86
|
-
export const MODEL_ROLE_IDS: ModelRole[] = ["default"
|
|
85
|
+
export const MODEL_ROLE_IDS: ModelRole[] = ["default"];
|
|
87
86
|
|
|
88
|
-
export type SkcModelAssignmentTargetId = "default" | "
|
|
87
|
+
export type SkcModelAssignmentTargetId = "default" | "executor" | "architect" | "planner" | "critic";
|
|
89
88
|
|
|
90
89
|
export interface SkcModelAssignmentTargetInfo extends ModelRoleInfo {
|
|
91
90
|
id: SkcModelAssignmentTargetId;
|
|
@@ -94,7 +93,6 @@ export interface SkcModelAssignmentTargetInfo extends ModelRoleInfo {
|
|
|
94
93
|
|
|
95
94
|
export const SKC_MODEL_ASSIGNMENT_TARGET_IDS: SkcModelAssignmentTargetId[] = [
|
|
96
95
|
"default",
|
|
97
|
-
"vision",
|
|
98
96
|
"executor",
|
|
99
97
|
"architect",
|
|
100
98
|
"planner",
|
|
@@ -103,7 +101,6 @@ export const SKC_MODEL_ASSIGNMENT_TARGET_IDS: SkcModelAssignmentTargetId[] = [
|
|
|
103
101
|
|
|
104
102
|
export const SKC_MODEL_ASSIGNMENT_TARGETS: Record<SkcModelAssignmentTargetId, SkcModelAssignmentTargetInfo> = {
|
|
105
103
|
default: { id: "default", tag: "DEFAULT", name: "Default", color: "success", settingsPath: "modelRoles" },
|
|
106
|
-
vision: { id: "vision", tag: "VISION", name: "Vision", color: "accent", settingsPath: "modelRoles" },
|
|
107
104
|
executor: {
|
|
108
105
|
id: "executor",
|
|
109
106
|
tag: "EXECUTOR",
|
|
@@ -81,7 +81,7 @@ const ModelBindingsSchema = z.object({
|
|
|
81
81
|
modelRoles: z.record(z.string(), z.string().min(1)).optional(),
|
|
82
82
|
agentModelOverrides: z.record(z.string(), z.string().min(1)).optional(),
|
|
83
83
|
});
|
|
84
|
-
export const ProfileRoleSchema = z.enum(["default", "
|
|
84
|
+
export const ProfileRoleSchema = z.enum(["default", "executor", "architect", "planner", "critic"]);
|
|
85
85
|
|
|
86
86
|
function isValidProfileModelSelector(value: string): boolean {
|
|
87
87
|
if (value.includes(",")) return false;
|