comisai 1.0.14 → 1.0.16
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/node_modules/@comis/agent/dist/bootstrap/index.d.ts +1 -1
- package/node_modules/@comis/agent/dist/bootstrap/index.js +1 -1
- package/node_modules/@comis/agent/dist/bootstrap/system-prompt-assembler.d.ts +26 -0
- package/node_modules/@comis/agent/dist/bootstrap/system-prompt-assembler.js +86 -54
- package/node_modules/@comis/agent/dist/bootstrap/types.d.ts +7 -1
- package/node_modules/@comis/agent/dist/bootstrap/workspace-loader.d.ts +12 -0
- package/node_modules/@comis/agent/dist/bootstrap/workspace-loader.js +21 -0
- package/node_modules/@comis/agent/dist/bridge/pi-event-bridge.js +12 -11
- package/node_modules/@comis/agent/dist/context-engine/constants.d.ts +11 -0
- package/node_modules/@comis/agent/dist/context-engine/constants.js +11 -0
- package/node_modules/@comis/agent/dist/context-engine/index.d.ts +1 -1
- package/node_modules/@comis/agent/dist/context-engine/index.js +1 -1
- package/node_modules/@comis/agent/dist/context-engine/llm-compaction.js +32 -18
- package/node_modules/@comis/agent/dist/executor/cache-break-diff-writer.d.ts +2 -0
- package/node_modules/@comis/agent/dist/executor/cache-break-diff-writer.js +36 -0
- package/node_modules/@comis/agent/dist/executor/executor-post-execution.d.ts +12 -0
- package/node_modules/@comis/agent/dist/executor/executor-post-execution.js +117 -27
- package/node_modules/@comis/agent/dist/executor/executor-prompt-runner.d.ts +1 -0
- package/node_modules/@comis/agent/dist/executor/executor-prompt-runner.js +16 -4
- package/node_modules/@comis/agent/dist/executor/executor-stream-setup.js +17 -1
- package/node_modules/@comis/agent/dist/executor/executor-tool-assembly.js +29 -1
- package/node_modules/@comis/agent/dist/executor/prompt-assembly.d.ts +8 -1
- package/node_modules/@comis/agent/dist/executor/prompt-assembly.js +22 -5
- package/node_modules/@comis/agent/dist/executor/session-snapshot-cleanup.js +3 -1
- package/node_modules/@comis/agent/dist/executor/stream-wrappers/index.d.ts +2 -0
- package/node_modules/@comis/agent/dist/executor/stream-wrappers/index.js +2 -0
- package/node_modules/@comis/agent/dist/executor/stream-wrappers/request-body-injector.d.ts +9 -0
- package/node_modules/@comis/agent/dist/executor/stream-wrappers/request-body-injector.js +65 -3
- package/node_modules/@comis/agent/dist/executor/stream-wrappers/stub-filter-injector.d.ts +28 -0
- package/node_modules/@comis/agent/dist/executor/stream-wrappers/stub-filter-injector.js +63 -0
- package/node_modules/@comis/agent/dist/executor/tool-deferral.d.ts +18 -1
- package/node_modules/@comis/agent/dist/executor/tool-deferral.js +275 -133
- package/node_modules/@comis/agent/dist/executor/ttl-guard.d.ts +6 -0
- package/node_modules/@comis/agent/dist/executor/ttl-guard.js +8 -0
- package/node_modules/@comis/agent/dist/session/comis-session-manager.js +7 -2
- package/node_modules/@comis/agent/package.json +1 -1
- package/node_modules/@comis/channels/dist/shared/typing-controller.d.ts +7 -0
- package/node_modules/@comis/channels/dist/shared/typing-controller.js +33 -0
- package/node_modules/@comis/channels/package.json +1 -1
- package/node_modules/@comis/cli/dist/commands/daemon.js +116 -28
- package/node_modules/@comis/cli/dist/doctor/checks/channel-health.js +2 -2
- package/node_modules/@comis/cli/dist/wizard/index.d.ts +2 -3
- package/node_modules/@comis/cli/dist/wizard/index.js +2 -3
- package/node_modules/@comis/cli/dist/wizard/steps/10-write-config.js +1 -1
- package/node_modules/@comis/cli/dist/wizard/types.d.ts +1 -2
- package/node_modules/@comis/cli/dist/wizard/types.js +1 -2
- package/node_modules/@comis/cli/package.json +1 -1
- package/node_modules/@comis/core/dist/config/schema-agent.d.ts +64 -10
- package/node_modules/@comis/core/dist/config/schema-agent.js +19 -0
- package/node_modules/@comis/core/dist/config/schema-skills.d.ts +16 -2
- package/node_modules/@comis/core/dist/config/schema-skills.js +9 -2
- package/node_modules/@comis/core/dist/config/schema.d.ts +19 -11
- package/node_modules/@comis/core/dist/event-bus/events-infra.d.ts +8 -0
- package/node_modules/@comis/core/package.json +1 -1
- package/node_modules/@comis/daemon/dist/daemon.js +2 -1
- package/node_modules/@comis/daemon/dist/wiring/setup-channels.js +19 -2
- package/node_modules/@comis/daemon/dist/wiring/setup-heartbeat.js +10 -0
- package/node_modules/@comis/daemon/dist/wiring/setup-memory.d.ts +2 -0
- package/node_modules/@comis/daemon/dist/wiring/setup-memory.js +15 -0
- package/node_modules/@comis/daemon/dist/wiring/setup-schedulers.js +1 -0
- package/node_modules/@comis/daemon/dist/wiring/setup-shutdown.d.ts +1 -0
- package/node_modules/@comis/daemon/dist/wiring/setup-shutdown.js +4 -0
- package/node_modules/@comis/daemon/package.json +1 -1
- package/node_modules/@comis/gateway/package.json +1 -1
- package/node_modules/@comis/infra/package.json +1 -1
- package/node_modules/@comis/memory/package.json +1 -1
- package/node_modules/@comis/scheduler/dist/cron/cron-types.d.ts +11 -0
- package/node_modules/@comis/scheduler/dist/cron/cron-types.js +11 -0
- package/node_modules/@comis/scheduler/dist/heartbeat/agent-heartbeat-source.d.ts +26 -1
- package/node_modules/@comis/scheduler/dist/heartbeat/agent-heartbeat-source.js +23 -2
- package/node_modules/@comis/scheduler/dist/heartbeat/heartbeat-config.d.ts +7 -0
- package/node_modules/@comis/scheduler/dist/heartbeat/heartbeat-config.js +2 -0
- package/node_modules/@comis/scheduler/package.json +1 -1
- package/node_modules/@comis/shared/package.json +1 -1
- package/node_modules/@comis/skills/dist/index.d.ts +1 -1
- package/node_modules/@comis/skills/dist/index.js +1 -1
- package/node_modules/@comis/skills/dist/policy/tool-policy.js +25 -0
- package/node_modules/@comis/skills/package.json +1 -1
- package/package.json +12 -12
|
@@ -33,6 +33,13 @@
|
|
|
33
33
|
* text deltas from the model). Active generation keeps the indicator
|
|
34
34
|
* alive beyond the initial TTL window.
|
|
35
35
|
*
|
|
36
|
+
* 6. **Internal TTL refresh (liveness watchdog)**: While active and not
|
|
37
|
+
* sealed, an internal 30s interval calls resetTtl() unconditionally.
|
|
38
|
+
* This is a belt-and-braces mechanism for long silences (extended
|
|
39
|
+
* thinking, slow tools, inter-turn gaps). External refreshTtl() calls
|
|
40
|
+
* from content signals remain the primary mechanism; this timer is
|
|
41
|
+
* additive, not a replacement.
|
|
42
|
+
*
|
|
36
43
|
* Typing failures are non-fatal: errors from `sendTyping` are caught
|
|
37
44
|
* and logged but never propagated to the caller.
|
|
38
45
|
*
|
|
@@ -34,6 +34,13 @@
|
|
|
34
34
|
* text deltas from the model). Active generation keeps the indicator
|
|
35
35
|
* alive beyond the initial TTL window.
|
|
36
36
|
*
|
|
37
|
+
* 6. **Internal TTL refresh (liveness watchdog)**: While active and not
|
|
38
|
+
* sealed, an internal 30s interval calls resetTtl() unconditionally.
|
|
39
|
+
* This is a belt-and-braces mechanism for long silences (extended
|
|
40
|
+
* thinking, slow tools, inter-turn gaps). External refreshTtl() calls
|
|
41
|
+
* from content signals remain the primary mechanism; this timer is
|
|
42
|
+
* additive, not a replacement.
|
|
43
|
+
*
|
|
37
44
|
* Typing failures are non-fatal: errors from `sendTyping` are caught
|
|
38
45
|
* and logged but never propagated to the caller.
|
|
39
46
|
*
|
|
@@ -42,6 +49,11 @@
|
|
|
42
49
|
// ---------------------------------------------------------------------------
|
|
43
50
|
// Factory
|
|
44
51
|
// ---------------------------------------------------------------------------
|
|
52
|
+
/** Internal liveness refresh interval. MUST be strictly less than default
|
|
53
|
+
* ttlMs (60_000) so an active, non-sealed controller cannot have its TTL
|
|
54
|
+
* expire without an explicit stop() or circuit-breaker trip. Matches the
|
|
55
|
+
* tool-active refresh cadence in execution-execute.ts for consistency. */
|
|
56
|
+
const INTERNAL_TTL_REFRESH_MS = 30_000;
|
|
45
57
|
/**
|
|
46
58
|
* Create a typing controller that sends platform typing indicators at a
|
|
47
59
|
* configurable refresh interval.
|
|
@@ -59,6 +71,7 @@ export function createTypingController(config, sendTyping, logger) {
|
|
|
59
71
|
let consecutiveFailures = 0;
|
|
60
72
|
let tickInFlight = false;
|
|
61
73
|
let ttlTimer = null;
|
|
74
|
+
let ttlRefreshTimer = null;
|
|
62
75
|
const threshold = config.circuitBreakerThreshold ?? 3;
|
|
63
76
|
/** Fire-and-forget typing send with tick serialization and circuit breaker. */
|
|
64
77
|
function doSendTyping(chatId) {
|
|
@@ -83,6 +96,10 @@ export function createTypingController(config, sendTyping, logger) {
|
|
|
83
96
|
clearTimeout(ttlTimer);
|
|
84
97
|
ttlTimer = null;
|
|
85
98
|
}
|
|
99
|
+
if (ttlRefreshTimer !== null) {
|
|
100
|
+
clearInterval(ttlRefreshTimer);
|
|
101
|
+
ttlRefreshTimer = null;
|
|
102
|
+
}
|
|
86
103
|
}
|
|
87
104
|
else {
|
|
88
105
|
logger?.warn({ err, chatId, hint: "Typing indicator delivery failed; non-blocking", errorKind: "platform" }, "Typing indicator send failed");
|
|
@@ -106,6 +123,10 @@ export function createTypingController(config, sendTyping, logger) {
|
|
|
106
123
|
clearInterval(timer);
|
|
107
124
|
timer = null;
|
|
108
125
|
}
|
|
126
|
+
if (ttlRefreshTimer !== null) {
|
|
127
|
+
clearInterval(ttlRefreshTimer);
|
|
128
|
+
ttlRefreshTimer = null;
|
|
129
|
+
}
|
|
109
130
|
}
|
|
110
131
|
}, config.ttlMs ?? 60_000);
|
|
111
132
|
}
|
|
@@ -125,6 +146,14 @@ export function createTypingController(config, sendTyping, logger) {
|
|
|
125
146
|
}, config.refreshMs);
|
|
126
147
|
// Arm the TTL timer.
|
|
127
148
|
resetTtl();
|
|
149
|
+
// Belt-and-braces liveness watchdog: refresh the TTL on a fixed cadence so
|
|
150
|
+
// the indicator survives long silences (extended thinking, slow tools,
|
|
151
|
+
// inter-turn gaps) even when no external signal calls refreshTtl().
|
|
152
|
+
ttlRefreshTimer = setInterval(() => {
|
|
153
|
+
if (active && !sealed) {
|
|
154
|
+
resetTtl();
|
|
155
|
+
}
|
|
156
|
+
}, INTERNAL_TTL_REFRESH_MS);
|
|
128
157
|
},
|
|
129
158
|
stop() {
|
|
130
159
|
active = false;
|
|
@@ -137,6 +166,10 @@ export function createTypingController(config, sendTyping, logger) {
|
|
|
137
166
|
clearTimeout(ttlTimer);
|
|
138
167
|
ttlTimer = null;
|
|
139
168
|
}
|
|
169
|
+
if (ttlRefreshTimer !== null) {
|
|
170
|
+
clearInterval(ttlRefreshTimer);
|
|
171
|
+
ttlRefreshTimer = null;
|
|
172
|
+
}
|
|
140
173
|
},
|
|
141
174
|
refreshTtl() {
|
|
142
175
|
if (!active || sealed)
|
|
@@ -12,7 +12,9 @@
|
|
|
12
12
|
import { execFile, spawn } from "node:child_process";
|
|
13
13
|
import { existsSync, readFileSync, writeFileSync, unlinkSync, mkdirSync, openSync } from "node:fs";
|
|
14
14
|
import * as os from "node:os";
|
|
15
|
+
import { createInterface } from "node:readline";
|
|
15
16
|
import { promisify } from "node:util";
|
|
17
|
+
import chalk from "chalk";
|
|
16
18
|
import { safePath } from "@comis/core";
|
|
17
19
|
import { withClient } from "../client/rpc-client.js";
|
|
18
20
|
import { success, error, info, warn } from "../output/format.js";
|
|
@@ -23,6 +25,63 @@ const PID_FILE = safePath(COMIS_DIR, "daemon.pid");
|
|
|
23
25
|
const LOG_FILE = safePath(COMIS_DIR, "daemon.log");
|
|
24
26
|
/** Max time to wait for daemon gateway to become ready (ms). */
|
|
25
27
|
const READY_TIMEOUT_MS = 15_000;
|
|
28
|
+
// ---------- Log formatting ----------
|
|
29
|
+
const PINO_LEVELS = {
|
|
30
|
+
10: { label: "TRACE", color: chalk.gray },
|
|
31
|
+
20: { label: "DEBUG", color: chalk.gray },
|
|
32
|
+
30: { label: "INFO", color: chalk.cyan },
|
|
33
|
+
40: { label: "WARN", color: chalk.yellow },
|
|
34
|
+
50: { label: "ERROR", color: chalk.red },
|
|
35
|
+
60: { label: "FATAL", color: chalk.bgRed.white },
|
|
36
|
+
};
|
|
37
|
+
const PINO_META_KEYS = new Set([
|
|
38
|
+
"level", "time", "pid", "hostname", "name", "msg", "module",
|
|
39
|
+
"levelValue", "instanceId",
|
|
40
|
+
]);
|
|
41
|
+
function formatPinoLine(line) {
|
|
42
|
+
const trimmed = line.trim();
|
|
43
|
+
if (!trimmed.startsWith("{"))
|
|
44
|
+
return line;
|
|
45
|
+
let record;
|
|
46
|
+
try {
|
|
47
|
+
record = JSON.parse(trimmed);
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return line;
|
|
51
|
+
}
|
|
52
|
+
if (typeof record["level"] !== "number" && typeof record["level"] !== "string")
|
|
53
|
+
return line;
|
|
54
|
+
const levelNum = typeof record["level"] === "number"
|
|
55
|
+
? record["level"]
|
|
56
|
+
: ({ trace: 10, debug: 20, info: 30, warn: 40, error: 50, fatal: 60 }[record["level"]] ?? 30);
|
|
57
|
+
const levelInfo = PINO_LEVELS[levelNum] ?? { label: String(record["level"]), color: chalk.white };
|
|
58
|
+
const time = record["time"]
|
|
59
|
+
? new Date(record["time"]).toLocaleTimeString()
|
|
60
|
+
: "";
|
|
61
|
+
const mod = record["module"] ? `[${record["module"]}]` : "";
|
|
62
|
+
const msg = record["msg"] ?? "";
|
|
63
|
+
const extra = {};
|
|
64
|
+
for (const [k, v] of Object.entries(record)) {
|
|
65
|
+
if (!PINO_META_KEYS.has(k))
|
|
66
|
+
extra[k] = v;
|
|
67
|
+
}
|
|
68
|
+
const extraStr = Object.keys(extra).length > 0
|
|
69
|
+
? chalk.gray(` ${JSON.stringify(extra)}`)
|
|
70
|
+
: "";
|
|
71
|
+
return `${chalk.dim(time)} ${levelInfo.color(levelInfo.label.padEnd(5))} ${chalk.blue(mod)} ${msg}${extraStr}`;
|
|
72
|
+
}
|
|
73
|
+
function formatLogOutput(raw) {
|
|
74
|
+
return raw
|
|
75
|
+
.split("\n")
|
|
76
|
+
.map((line) => (line.trim() ? formatPinoLine(line) : line))
|
|
77
|
+
.join("\n");
|
|
78
|
+
}
|
|
79
|
+
function pipeFormatted(child) {
|
|
80
|
+
if (!child.stdout)
|
|
81
|
+
return;
|
|
82
|
+
const rl = createInterface({ input: child.stdout });
|
|
83
|
+
rl.on("line", (line) => console.log(formatPinoLine(line)));
|
|
84
|
+
}
|
|
26
85
|
/** Interval between readiness polls (ms). */
|
|
27
86
|
const READY_POLL_MS = 500;
|
|
28
87
|
/**
|
|
@@ -533,40 +592,48 @@ async function handleDaemonLogs(options) {
|
|
|
533
592
|
}
|
|
534
593
|
}
|
|
535
594
|
async function streamSystemdLogs(scope, options) {
|
|
536
|
-
const jArgs = ["--unit=comis", "--no-pager", `-n${options.lines}
|
|
595
|
+
const jArgs = ["--unit=comis", "--no-pager", `-n${options.lines}`, "--output=cat"];
|
|
537
596
|
if (scope === "systemd-user")
|
|
538
597
|
jArgs.push("--user");
|
|
539
598
|
if (options.follow)
|
|
540
599
|
jArgs.push("--follow");
|
|
541
600
|
if (options.follow) {
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
error(
|
|
545
|
-
|
|
546
|
-
|
|
601
|
+
if (options.raw) {
|
|
602
|
+
const child = spawn("journalctl", jArgs, { stdio: "inherit" });
|
|
603
|
+
child.on("error", (err) => {
|
|
604
|
+
error(`Failed to read logs: ${err.message}`);
|
|
605
|
+
process.exit(1);
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
else {
|
|
609
|
+
const child = spawn("journalctl", jArgs, { stdio: ["ignore", "pipe", "inherit"] });
|
|
610
|
+
child.on("error", (err) => {
|
|
611
|
+
error(`Failed to read logs: ${err.message}`);
|
|
612
|
+
process.exit(1);
|
|
613
|
+
});
|
|
614
|
+
pipeFormatted(child);
|
|
615
|
+
}
|
|
547
616
|
return;
|
|
548
617
|
}
|
|
549
|
-
|
|
550
|
-
// then fall back to sudo for system-scope journals.
|
|
551
|
-
try {
|
|
552
|
-
const { stdout } = await exec("journalctl", jArgs, { timeout: 10_000 });
|
|
618
|
+
const printOutput = (stdout) => {
|
|
553
619
|
if (stdout.trim()) {
|
|
554
|
-
console.log(stdout);
|
|
620
|
+
console.log(options.raw ? stdout : formatLogOutput(stdout));
|
|
555
621
|
}
|
|
556
622
|
else {
|
|
557
623
|
info("No logs found");
|
|
558
624
|
}
|
|
625
|
+
};
|
|
626
|
+
// Try journalctl directly first (works when user is in systemd-journal group),
|
|
627
|
+
// then fall back to sudo for system-scope journals.
|
|
628
|
+
try {
|
|
629
|
+
const { stdout } = await exec("journalctl", jArgs, { timeout: 10_000 });
|
|
630
|
+
printOutput(stdout);
|
|
559
631
|
}
|
|
560
632
|
catch {
|
|
561
633
|
// eslint-disable-next-line no-restricted-syntax -- CLI bootstrap before SecretManager
|
|
562
634
|
if (scope === "systemd" && process.getuid?.() !== 0) {
|
|
563
635
|
const { stdout } = await exec("sudo", ["journalctl", ...jArgs], { timeout: 10_000 });
|
|
564
|
-
|
|
565
|
-
console.log(stdout);
|
|
566
|
-
}
|
|
567
|
-
else {
|
|
568
|
-
info("No logs found");
|
|
569
|
-
}
|
|
636
|
+
printOutput(stdout);
|
|
570
637
|
}
|
|
571
638
|
else {
|
|
572
639
|
throw new Error("Failed to read journal logs");
|
|
@@ -577,11 +644,21 @@ function streamPm2Logs(options) {
|
|
|
577
644
|
const args = ["logs", "comis", "--lines", options.lines];
|
|
578
645
|
if (!options.follow)
|
|
579
646
|
args.push("--nostream");
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
error(
|
|
583
|
-
|
|
584
|
-
|
|
647
|
+
if (options.raw) {
|
|
648
|
+
const child = spawn("pm2", args, { stdio: "inherit" });
|
|
649
|
+
child.on("error", (err) => {
|
|
650
|
+
error(`Failed to read logs: ${err.message}`);
|
|
651
|
+
process.exit(1);
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
else {
|
|
655
|
+
const child = spawn("pm2", args, { stdio: ["ignore", "pipe", "inherit"] });
|
|
656
|
+
child.on("error", (err) => {
|
|
657
|
+
error(`Failed to read logs: ${err.message}`);
|
|
658
|
+
process.exit(1);
|
|
659
|
+
});
|
|
660
|
+
pipeFormatted(child);
|
|
661
|
+
}
|
|
585
662
|
}
|
|
586
663
|
async function streamDirectLogs(options) {
|
|
587
664
|
// eslint-disable-next-line no-restricted-syntax -- CLI bootstrap before SecretManager
|
|
@@ -594,16 +671,26 @@ async function streamDirectLogs(options) {
|
|
|
594
671
|
const args = ["-n", options.lines, logPath];
|
|
595
672
|
if (options.follow) {
|
|
596
673
|
args.unshift("-f");
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
error(
|
|
600
|
-
|
|
601
|
-
|
|
674
|
+
if (options.raw) {
|
|
675
|
+
const child = spawn("tail", args, { stdio: "inherit" });
|
|
676
|
+
child.on("error", (err) => {
|
|
677
|
+
error(`Failed to read logs: ${err.message}`);
|
|
678
|
+
process.exit(1);
|
|
679
|
+
});
|
|
680
|
+
}
|
|
681
|
+
else {
|
|
682
|
+
const child = spawn("tail", args, { stdio: ["ignore", "pipe", "inherit"] });
|
|
683
|
+
child.on("error", (err) => {
|
|
684
|
+
error(`Failed to read logs: ${err.message}`);
|
|
685
|
+
process.exit(1);
|
|
686
|
+
});
|
|
687
|
+
pipeFormatted(child);
|
|
688
|
+
}
|
|
602
689
|
return;
|
|
603
690
|
}
|
|
604
691
|
const { stdout } = await exec("tail", args, { timeout: 5_000 });
|
|
605
692
|
if (stdout.trim()) {
|
|
606
|
-
console.log(stdout);
|
|
693
|
+
console.log(options.raw ? stdout : formatLogOutput(stdout));
|
|
607
694
|
}
|
|
608
695
|
else {
|
|
609
696
|
info("No logs found");
|
|
@@ -634,5 +721,6 @@ export function registerDaemonCommand(program) {
|
|
|
634
721
|
.description("Show daemon logs")
|
|
635
722
|
.option("-f, --follow", "Follow log output")
|
|
636
723
|
.option("-n, --lines <n>", "Number of lines to show", "50")
|
|
724
|
+
.option("--raw", "Show raw JSON output without formatting")
|
|
637
725
|
.action(handleDaemonLogs);
|
|
638
726
|
}
|
|
@@ -8,12 +8,12 @@
|
|
|
8
8
|
*
|
|
9
9
|
* @module
|
|
10
10
|
*/
|
|
11
|
-
import { CHANNEL_ENV_KEYS } from "../../wizard/
|
|
11
|
+
import { CHANNEL_ENV_KEYS } from "../../wizard/index.js";
|
|
12
12
|
const CATEGORY = "channels";
|
|
13
13
|
/**
|
|
14
14
|
* Map of channel types to their required credential environment variable names.
|
|
15
15
|
*
|
|
16
|
-
* Sourced from the shared CHANNEL_ENV_KEYS constant in
|
|
16
|
+
* Sourced from the shared CHANNEL_ENV_KEYS constant in the wizard module.
|
|
17
17
|
* These are format checks only -- we verify the credential appears to be
|
|
18
18
|
* a non-empty string, not that it's a valid API key.
|
|
19
19
|
*/
|
|
@@ -6,9 +6,8 @@
|
|
|
6
6
|
* init command, tests, future phases) imports from this single entry
|
|
7
7
|
* point rather than reaching into individual wizard files.
|
|
8
8
|
*
|
|
9
|
-
* Legacy wizard files (flow-types.ts, quickstart-flow.ts, manual-flow.ts
|
|
10
|
-
*
|
|
11
|
-
* but are not part of the new wizard architecture.
|
|
9
|
+
* Legacy wizard files (flow-types.ts, quickstart-flow.ts, manual-flow.ts,
|
|
10
|
+
* config-writer.ts) were removed -- superseded by the step-based architecture.
|
|
12
11
|
*
|
|
13
12
|
* @module
|
|
14
13
|
*/
|
|
@@ -7,9 +7,8 @@
|
|
|
7
7
|
* init command, tests, future phases) imports from this single entry
|
|
8
8
|
* point rather than reaching into individual wizard files.
|
|
9
9
|
*
|
|
10
|
-
* Legacy wizard files (flow-types.ts, quickstart-flow.ts, manual-flow.ts
|
|
11
|
-
*
|
|
12
|
-
* but are not part of the new wizard architecture.
|
|
10
|
+
* Legacy wizard files (flow-types.ts, quickstart-flow.ts, manual-flow.ts,
|
|
11
|
+
* config-writer.ts) were removed -- superseded by the step-based architecture.
|
|
13
12
|
*
|
|
14
13
|
* @module
|
|
15
14
|
*/
|
|
@@ -17,7 +17,7 @@ import { updateState, heading, success as themeSuccess, PROVIDER_ENV_KEYS, CHANN
|
|
|
17
17
|
/**
|
|
18
18
|
* Get a sensible default model for a provider.
|
|
19
19
|
*
|
|
20
|
-
* Matches the defaults
|
|
20
|
+
* Matches the defaults used by the wizard.
|
|
21
21
|
*/
|
|
22
22
|
function getDefaultModel(provider) {
|
|
23
23
|
if (!provider)
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Foundational types and constants for the init wizard redesign.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* that supports the full wizard architecture: immutable state
|
|
4
|
+
* Core type system for the wizard architecture: immutable state
|
|
6
5
|
* accumulation, structured validation, and multi-flow support.
|
|
7
6
|
*
|
|
8
7
|
* @module
|
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Foundational types and constants for the init wizard redesign.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
* that supports the full wizard architecture: immutable state
|
|
5
|
+
* Core type system for the wizard architecture: immutable state
|
|
7
6
|
* accumulation, structured validation, and multi-flow support.
|
|
8
7
|
*
|
|
9
8
|
* @module
|
|
@@ -550,9 +550,9 @@ export declare const BootstrapConfigSchema: z.ZodObject<{
|
|
|
550
550
|
maxChars: z.ZodDefault<z.ZodNumber>;
|
|
551
551
|
/** System prompt verbosity mode: full (all sections), minimal (sub-agents), none (identity only) */
|
|
552
552
|
promptMode: z.ZodDefault<z.ZodEnum<{
|
|
553
|
-
minimal: "minimal";
|
|
554
|
-
full: "full";
|
|
555
553
|
none: "none";
|
|
554
|
+
full: "full";
|
|
555
|
+
minimal: "minimal";
|
|
556
556
|
}>>;
|
|
557
557
|
/** When true, USER.md is excluded from bootstrap context in group chat sessions (privacy). Default: true. */
|
|
558
558
|
groupChatFiltering: z.ZodDefault<z.ZodBoolean>;
|
|
@@ -737,6 +737,10 @@ export declare const AgentConfigSchema: z.ZodObject<{
|
|
|
737
737
|
"multi-zone": "multi-zone";
|
|
738
738
|
single: "single";
|
|
739
739
|
}>>;
|
|
740
|
+
/** Advanced cache optimization options for interactive sessions. */
|
|
741
|
+
advancedCacheOptimization: z.ZodDefault<z.ZodObject<{
|
|
742
|
+
enableRecentZonePromotion: z.ZodDefault<z.ZodBoolean>;
|
|
743
|
+
}, z.core.$strip>>;
|
|
740
744
|
/** Gemini explicit cache configuration (CachedContent lifecycle). */
|
|
741
745
|
geminiCache: z.ZodDefault<z.ZodObject<{
|
|
742
746
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
@@ -822,9 +826,9 @@ export declare const AgentConfigSchema: z.ZodObject<{
|
|
|
822
826
|
maxChars: z.ZodDefault<z.ZodNumber>;
|
|
823
827
|
/** System prompt verbosity mode: full (all sections), minimal (sub-agents), none (identity only) */
|
|
824
828
|
promptMode: z.ZodDefault<z.ZodEnum<{
|
|
825
|
-
minimal: "minimal";
|
|
826
|
-
full: "full";
|
|
827
829
|
none: "none";
|
|
830
|
+
full: "full";
|
|
831
|
+
minimal: "minimal";
|
|
828
832
|
}>>;
|
|
829
833
|
/** When true, USER.md is excluded from bootstrap context in group chat sessions (privacy). Default: true. */
|
|
830
834
|
groupChatFiltering: z.ZodDefault<z.ZodBoolean>;
|
|
@@ -987,6 +991,17 @@ export declare const PerAgentHeartbeatConfigSchema: z.ZodObject<{
|
|
|
987
991
|
alertCooldownMs: z.ZodOptional<z.ZodNumber>;
|
|
988
992
|
/** Override stuck detection timeout in ms (per-agent) */
|
|
989
993
|
staleMs: z.ZodOptional<z.ZodNumber>;
|
|
994
|
+
/** Per-agent heartbeat tool policy override -- matches AgentConfig.toolPolicy shape.
|
|
995
|
+
*
|
|
996
|
+
* Resolution order: heartbeat.toolPolicy > agentConfig.toolPolicy > passthrough.
|
|
997
|
+
* Opt-in: omitting this field preserves the agent's interactive tool set for
|
|
998
|
+
* heartbeat ticks. Use `{ profile: "heartbeat-minimal" }` for the conservative
|
|
999
|
+
* preset in `packages/skills/src/policy/tool-policy.ts`. */
|
|
1000
|
+
toolPolicy: z.ZodOptional<z.ZodObject<{
|
|
1001
|
+
profile: z.ZodDefault<z.ZodString>;
|
|
1002
|
+
allow: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
1003
|
+
deny: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
1004
|
+
}, z.core.$strip>>;
|
|
990
1005
|
}, z.core.$strict>;
|
|
991
1006
|
export type HeartbeatTarget = z.infer<typeof HeartbeatTargetSchema>;
|
|
992
1007
|
export type PerAgentHeartbeatConfig = z.infer<typeof PerAgentHeartbeatConfigSchema>;
|
|
@@ -1046,6 +1061,17 @@ export declare const PerAgentSchedulerConfigSchema: z.ZodObject<{
|
|
|
1046
1061
|
alertCooldownMs: z.ZodOptional<z.ZodNumber>;
|
|
1047
1062
|
/** Override stuck detection timeout in ms (per-agent) */
|
|
1048
1063
|
staleMs: z.ZodOptional<z.ZodNumber>;
|
|
1064
|
+
/** Per-agent heartbeat tool policy override -- matches AgentConfig.toolPolicy shape.
|
|
1065
|
+
*
|
|
1066
|
+
* Resolution order: heartbeat.toolPolicy > agentConfig.toolPolicy > passthrough.
|
|
1067
|
+
* Opt-in: omitting this field preserves the agent's interactive tool set for
|
|
1068
|
+
* heartbeat ticks. Use `{ profile: "heartbeat-minimal" }` for the conservative
|
|
1069
|
+
* preset in `packages/skills/src/policy/tool-policy.ts`. */
|
|
1070
|
+
toolPolicy: z.ZodOptional<z.ZodObject<{
|
|
1071
|
+
profile: z.ZodDefault<z.ZodString>;
|
|
1072
|
+
allow: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
1073
|
+
deny: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
1074
|
+
}, z.core.$strip>>;
|
|
1049
1075
|
}, z.core.$strict>>;
|
|
1050
1076
|
}, z.core.$strict>;
|
|
1051
1077
|
/** Per-agent configuration: extends AgentConfigSchema with skills, scheduler, session, concurrency. */
|
|
@@ -1080,6 +1106,9 @@ export declare const PerAgentConfigSchema: z.ZodObject<{
|
|
|
1080
1106
|
"multi-zone": "multi-zone";
|
|
1081
1107
|
single: "single";
|
|
1082
1108
|
}>>;
|
|
1109
|
+
advancedCacheOptimization: z.ZodDefault<z.ZodObject<{
|
|
1110
|
+
enableRecentZonePromotion: z.ZodDefault<z.ZodBoolean>;
|
|
1111
|
+
}, z.core.$strip>>;
|
|
1083
1112
|
geminiCache: z.ZodDefault<z.ZodObject<{
|
|
1084
1113
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
1085
1114
|
maxActiveCaches: z.ZodDefault<z.ZodNumber>;
|
|
@@ -1150,9 +1179,9 @@ export declare const PerAgentConfigSchema: z.ZodObject<{
|
|
|
1150
1179
|
maxChars: z.ZodDefault<z.ZodNumber>;
|
|
1151
1180
|
/** System prompt verbosity mode: full (all sections), minimal (sub-agents), none (identity only) */
|
|
1152
1181
|
promptMode: z.ZodDefault<z.ZodEnum<{
|
|
1153
|
-
minimal: "minimal";
|
|
1154
|
-
full: "full";
|
|
1155
1182
|
none: "none";
|
|
1183
|
+
full: "full";
|
|
1184
|
+
minimal: "minimal";
|
|
1156
1185
|
}>>;
|
|
1157
1186
|
/** When true, USER.md is excluded from bootstrap context in group chat sessions (privacy). Default: true. */
|
|
1158
1187
|
groupChatFiltering: z.ZodDefault<z.ZodBoolean>;
|
|
@@ -1259,11 +1288,11 @@ export declare const PerAgentConfigSchema: z.ZodObject<{
|
|
|
1259
1288
|
}, z.core.$strict>>;
|
|
1260
1289
|
toolPolicy: z.ZodDefault<z.ZodObject<{
|
|
1261
1290
|
profile: z.ZodDefault<z.ZodEnum<{
|
|
1291
|
+
full: "full";
|
|
1262
1292
|
minimal: "minimal";
|
|
1263
1293
|
coding: "coding";
|
|
1264
1294
|
messaging: "messaging";
|
|
1265
1295
|
supervisor: "supervisor";
|
|
1266
|
-
full: "full";
|
|
1267
1296
|
}>>;
|
|
1268
1297
|
allow: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
1269
1298
|
deny: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
@@ -1351,6 +1380,17 @@ export declare const PerAgentConfigSchema: z.ZodObject<{
|
|
|
1351
1380
|
alertCooldownMs: z.ZodOptional<z.ZodNumber>;
|
|
1352
1381
|
/** Override stuck detection timeout in ms (per-agent) */
|
|
1353
1382
|
staleMs: z.ZodOptional<z.ZodNumber>;
|
|
1383
|
+
/** Per-agent heartbeat tool policy override -- matches AgentConfig.toolPolicy shape.
|
|
1384
|
+
*
|
|
1385
|
+
* Resolution order: heartbeat.toolPolicy > agentConfig.toolPolicy > passthrough.
|
|
1386
|
+
* Opt-in: omitting this field preserves the agent's interactive tool set for
|
|
1387
|
+
* heartbeat ticks. Use `{ profile: "heartbeat-minimal" }` for the conservative
|
|
1388
|
+
* preset in `packages/skills/src/policy/tool-policy.ts`. */
|
|
1389
|
+
toolPolicy: z.ZodOptional<z.ZodObject<{
|
|
1390
|
+
profile: z.ZodDefault<z.ZodString>;
|
|
1391
|
+
allow: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
1392
|
+
deny: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
1393
|
+
}, z.core.$strip>>;
|
|
1354
1394
|
}, z.core.$strict>>;
|
|
1355
1395
|
}, z.core.$strict>>;
|
|
1356
1396
|
session: z.ZodOptional<z.ZodObject<{
|
|
@@ -1740,6 +1780,9 @@ export declare const AgentsMapSchema: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
|
1740
1780
|
"multi-zone": "multi-zone";
|
|
1741
1781
|
single: "single";
|
|
1742
1782
|
}>>;
|
|
1783
|
+
advancedCacheOptimization: z.ZodDefault<z.ZodObject<{
|
|
1784
|
+
enableRecentZonePromotion: z.ZodDefault<z.ZodBoolean>;
|
|
1785
|
+
}, z.core.$strip>>;
|
|
1743
1786
|
geminiCache: z.ZodDefault<z.ZodObject<{
|
|
1744
1787
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
1745
1788
|
maxActiveCaches: z.ZodDefault<z.ZodNumber>;
|
|
@@ -1810,9 +1853,9 @@ export declare const AgentsMapSchema: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
|
1810
1853
|
maxChars: z.ZodDefault<z.ZodNumber>;
|
|
1811
1854
|
/** System prompt verbosity mode: full (all sections), minimal (sub-agents), none (identity only) */
|
|
1812
1855
|
promptMode: z.ZodDefault<z.ZodEnum<{
|
|
1813
|
-
minimal: "minimal";
|
|
1814
|
-
full: "full";
|
|
1815
1856
|
none: "none";
|
|
1857
|
+
full: "full";
|
|
1858
|
+
minimal: "minimal";
|
|
1816
1859
|
}>>;
|
|
1817
1860
|
/** When true, USER.md is excluded from bootstrap context in group chat sessions (privacy). Default: true. */
|
|
1818
1861
|
groupChatFiltering: z.ZodDefault<z.ZodBoolean>;
|
|
@@ -1919,11 +1962,11 @@ export declare const AgentsMapSchema: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
|
1919
1962
|
}, z.core.$strict>>;
|
|
1920
1963
|
toolPolicy: z.ZodDefault<z.ZodObject<{
|
|
1921
1964
|
profile: z.ZodDefault<z.ZodEnum<{
|
|
1965
|
+
full: "full";
|
|
1922
1966
|
minimal: "minimal";
|
|
1923
1967
|
coding: "coding";
|
|
1924
1968
|
messaging: "messaging";
|
|
1925
1969
|
supervisor: "supervisor";
|
|
1926
|
-
full: "full";
|
|
1927
1970
|
}>>;
|
|
1928
1971
|
allow: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
1929
1972
|
deny: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
@@ -2011,6 +2054,17 @@ export declare const AgentsMapSchema: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
|
2011
2054
|
alertCooldownMs: z.ZodOptional<z.ZodNumber>;
|
|
2012
2055
|
/** Override stuck detection timeout in ms (per-agent) */
|
|
2013
2056
|
staleMs: z.ZodOptional<z.ZodNumber>;
|
|
2057
|
+
/** Per-agent heartbeat tool policy override -- matches AgentConfig.toolPolicy shape.
|
|
2058
|
+
*
|
|
2059
|
+
* Resolution order: heartbeat.toolPolicy > agentConfig.toolPolicy > passthrough.
|
|
2060
|
+
* Opt-in: omitting this field preserves the agent's interactive tool set for
|
|
2061
|
+
* heartbeat ticks. Use `{ profile: "heartbeat-minimal" }` for the conservative
|
|
2062
|
+
* preset in `packages/skills/src/policy/tool-policy.ts`. */
|
|
2063
|
+
toolPolicy: z.ZodOptional<z.ZodObject<{
|
|
2064
|
+
profile: z.ZodDefault<z.ZodString>;
|
|
2065
|
+
allow: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
2066
|
+
deny: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
2067
|
+
}, z.core.$strip>>;
|
|
2014
2068
|
}, z.core.$strict>>;
|
|
2015
2069
|
}, z.core.$strict>>;
|
|
2016
2070
|
session: z.ZodOptional<z.ZodObject<{
|
|
@@ -574,6 +574,14 @@ export const AgentConfigSchema = z.strictObject({
|
|
|
574
574
|
* 'auto' resolves to 'single' for direct Anthropic and 'multi-zone' for Bedrock/Vertex.
|
|
575
575
|
* 'multi-zone' places breakpoints across system, tools, and messages. */
|
|
576
576
|
cacheBreakpointStrategy: z.enum(["auto", "multi-zone", "single"]).default("single"),
|
|
577
|
+
/** Advanced cache optimization options for interactive sessions. */
|
|
578
|
+
advancedCacheOptimization: z.object({
|
|
579
|
+
/** When true, the recent-zone message breakpoint may be promoted from
|
|
580
|
+
* "short" (5m) to "long" (1h) TTL when observed inter-turn gaps
|
|
581
|
+
* consistently exceed 5 minutes. Prevents repeated cache rewrite costs
|
|
582
|
+
* in slow-cadence channels like Telegram. Default: true. */
|
|
583
|
+
enableRecentZonePromotion: z.boolean().default(true),
|
|
584
|
+
}).default(() => ({ enableRecentZonePromotion: true })),
|
|
577
585
|
/** Gemini explicit cache configuration (CachedContent lifecycle). */
|
|
578
586
|
geminiCache: GeminiCacheConfigSchema.default(() => GeminiCacheConfigSchema.parse({})),
|
|
579
587
|
/** When true, only content inside <final> blocks reaches users (streaming + non-streaming). Default: false. */
|
|
@@ -674,6 +682,17 @@ export const PerAgentHeartbeatConfigSchema = z.strictObject({
|
|
|
674
682
|
alertCooldownMs: z.number().int().positive().optional(),
|
|
675
683
|
/** Override stuck detection timeout in ms (per-agent) */
|
|
676
684
|
staleMs: z.number().int().positive().optional(),
|
|
685
|
+
/** Per-agent heartbeat tool policy override -- matches AgentConfig.toolPolicy shape.
|
|
686
|
+
*
|
|
687
|
+
* Resolution order: heartbeat.toolPolicy > agentConfig.toolPolicy > passthrough.
|
|
688
|
+
* Opt-in: omitting this field preserves the agent's interactive tool set for
|
|
689
|
+
* heartbeat ticks. Use `{ profile: "heartbeat-minimal" }` for the conservative
|
|
690
|
+
* preset in `packages/skills/src/policy/tool-policy.ts`. */
|
|
691
|
+
toolPolicy: z.object({
|
|
692
|
+
profile: z.string().default("full"),
|
|
693
|
+
allow: z.array(z.string()).default([]),
|
|
694
|
+
deny: z.array(z.string()).default([]),
|
|
695
|
+
}).optional(),
|
|
677
696
|
});
|
|
678
697
|
/** Per-agent scheduler configuration (wraps cron and heartbeat settings). */
|
|
679
698
|
export const PerAgentSchedulerConfigSchema = z.strictObject({
|
|
@@ -23,7 +23,14 @@ export declare const PromptSkillsConfigSchema: z.ZodObject<{
|
|
|
23
23
|
* per-deployment without a rebuild.
|
|
24
24
|
*/
|
|
25
25
|
declare const ToolDiscoverySchema: z.ZodObject<{
|
|
26
|
-
/** Minimum BM25 score
|
|
26
|
+
/** Minimum BM25 score as FRACTION OF TOP MATCH (0..1). Default 0.8.
|
|
27
|
+
* As of 2026-04-23, BM25 scores are normalized to [0, 1] before this floor
|
|
28
|
+
* applies, matching the semantics of minHybridScore. A value of 0.8 means
|
|
29
|
+
* "return only tools scoring >= 80% of the top match". Values > 1.0 fail
|
|
30
|
+
* validation at config load (stale raw-score overrides would produce zero
|
|
31
|
+
* matches under the new normalized semantics; fail-fast surfaces the
|
|
32
|
+
* error immediately per AGENTS.md §3.4). See design §5.6:
|
|
33
|
+
* .planning/design/discover-tools-bm25-fallback-fix.md */
|
|
27
34
|
minBm25Score: z.ZodDefault<z.ZodNumber>;
|
|
28
35
|
/** Minimum combined score (0..1 normalized) for hybrid mode. Default 0.35. */
|
|
29
36
|
minHybridScore: z.ZodDefault<z.ZodNumber>;
|
|
@@ -115,7 +122,14 @@ export declare const SkillsConfigSchema: z.ZodObject<{
|
|
|
115
122
|
}, z.core.$strict>>;
|
|
116
123
|
/** discover_tools score-floor thresholds (BM25 + hybrid). */
|
|
117
124
|
toolDiscovery: z.ZodDefault<z.ZodObject<{
|
|
118
|
-
/** Minimum BM25 score
|
|
125
|
+
/** Minimum BM25 score as FRACTION OF TOP MATCH (0..1). Default 0.8.
|
|
126
|
+
* As of 2026-04-23, BM25 scores are normalized to [0, 1] before this floor
|
|
127
|
+
* applies, matching the semantics of minHybridScore. A value of 0.8 means
|
|
128
|
+
* "return only tools scoring >= 80% of the top match". Values > 1.0 fail
|
|
129
|
+
* validation at config load (stale raw-score overrides would produce zero
|
|
130
|
+
* matches under the new normalized semantics; fail-fast surfaces the
|
|
131
|
+
* error immediately per AGENTS.md §3.4). See design §5.6:
|
|
132
|
+
* .planning/design/discover-tools-bm25-fallback-fix.md */
|
|
119
133
|
minBm25Score: z.ZodDefault<z.ZodNumber>;
|
|
120
134
|
/** Minimum combined score (0..1 normalized) for hybrid mode. Default 0.35. */
|
|
121
135
|
minHybridScore: z.ZodDefault<z.ZodNumber>;
|
|
@@ -93,8 +93,15 @@ const ExecSandboxSchema = z.strictObject({
|
|
|
93
93
|
* per-deployment without a rebuild.
|
|
94
94
|
*/
|
|
95
95
|
const ToolDiscoverySchema = z.strictObject({
|
|
96
|
-
/** Minimum BM25 score
|
|
97
|
-
|
|
96
|
+
/** Minimum BM25 score as FRACTION OF TOP MATCH (0..1). Default 0.8.
|
|
97
|
+
* As of 2026-04-23, BM25 scores are normalized to [0, 1] before this floor
|
|
98
|
+
* applies, matching the semantics of minHybridScore. A value of 0.8 means
|
|
99
|
+
* "return only tools scoring >= 80% of the top match". Values > 1.0 fail
|
|
100
|
+
* validation at config load (stale raw-score overrides would produce zero
|
|
101
|
+
* matches under the new normalized semantics; fail-fast surfaces the
|
|
102
|
+
* error immediately per AGENTS.md §3.4). See design §5.6:
|
|
103
|
+
* .planning/design/discover-tools-bm25-fallback-fix.md */
|
|
104
|
+
minBm25Score: z.number().min(0).max(1).default(0.8),
|
|
98
105
|
/** Minimum combined score (0..1 normalized) for hybrid mode. Default 0.35. */
|
|
99
106
|
minHybridScore: z.number().min(0).max(1).default(0.35),
|
|
100
107
|
});
|