talon-agent 3.0.5 → 3.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +8 -5
- package/src/backend/kilo/models/index.ts +4 -1
- package/src/backend/kilo/server.ts +17 -4
- package/src/backend/openai-agents/session.ts +1 -1
- package/src/backend/opencode/models/index.ts +4 -1
- package/src/backend/opencode/server.ts +17 -4
- package/src/backend/shared/system-prompt.ts +5 -0
- package/src/cli/events.ts +1 -1
- package/src/cli/index.ts +1 -1
- package/src/cli/tasks.ts +5 -1
- package/src/core/background/triggers/pid.ts +28 -0
- package/src/core/background/triggers/resume.ts +4 -24
- package/src/core/background/triggers/spawn.ts +1 -1
- package/src/core/engine/gateway-actions/plugins.ts +3 -3
- package/src/core/prompt/invalidation.ts +23 -0
- package/src/core/types.ts +2 -2
- package/src/frontend/discord/callbacks/components.ts +1 -1
- package/src/frontend/discord/commands/settings.ts +1 -1
- package/src/frontend/native/auth.ts +52 -0
- package/src/frontend/native/extensions.ts +2 -2
- package/src/frontend/native/index.ts +8 -2
- package/src/frontend/native/server.ts +96 -15
- package/src/frontend/telegram/callbacks/model.ts +1 -1
- package/src/frontend/telegram/commands/admin.ts +1 -1
- package/src/frontend/telegram/commands/settings.ts +1 -1
- package/src/frontend/telegram/formatting.ts +1 -1
- package/src/frontend/terminal/commands.ts +1 -1
- package/src/native/htmlents-wasm-bytes.ts +4 -4
- package/src/native/htmlents.ts +4 -4
- package/src/native/registry.ts +9 -9
- package/src/native/runtime.ts +2 -2
- package/src/native/sqlguard-wasm-bytes.ts +4 -4
- package/src/native/sqlguard.ts +2 -2
- package/src/native/strsim-wasm-bytes.ts +4 -4
- package/src/native/strsim.ts +3 -3
- package/src/plugins/github/index.ts +1 -1
- package/src/plugins/mem0/index.ts +1 -1
- package/src/plugins/mempalace/index.ts +1 -1
- package/src/plugins/playwright/index.ts +1 -1
- package/src/storage/chat-settings.ts +3 -60
- package/src/storage/cron-store.ts +6 -80
- package/src/storage/goal-store.ts +10 -19
- package/src/storage/history.ts +3 -14
- package/src/storage/journal.ts +20 -9
- package/src/storage/media-index.ts +2 -22
- package/src/storage/repositories/chat-settings-repo.ts +55 -1
- package/src/storage/repositories/cron-repo.ts +82 -6
- package/src/storage/repositories/goals-repo.ts +20 -1
- package/src/storage/repositories/history-repo.ts +14 -1
- package/src/storage/repositories/media-index-repo.ts +23 -1
- package/src/storage/repositories/scripts-repo.ts +16 -1
- package/src/storage/repositories/sessions-repo.ts +1 -1
- package/src/storage/repositories/triggers-repo.ts +60 -5
- package/src/storage/script-store.ts +2 -15
- package/src/storage/session-record.ts +220 -0
- package/src/storage/sessions.ts +22 -210
- package/src/storage/trigger-store.ts +6 -60
- package/src/types/assets.d.ts +9 -0
- package/src/types/effort.ts +12 -0
- package/src/util/config.ts +9 -2
- package/src/util/harden.ts +50 -0
- package/src/util/log.ts +2 -1
- package/tsconfig.json +6 -0
|
@@ -25,7 +25,7 @@ import { createHash, timingSafeEqual } from "node:crypto";
|
|
|
25
25
|
import { createReadStream } from "node:fs";
|
|
26
26
|
import { stat } from "node:fs/promises";
|
|
27
27
|
import { extname } from "node:path";
|
|
28
|
-
import { log, logError, logDebug } from "../../util/log.js";
|
|
28
|
+
import { log, logError, logDebug, logWarn } from "../../util/log.js";
|
|
29
29
|
import { formatFingerprint, type BridgeTlsIdentity } from "./tls.js";
|
|
30
30
|
import {
|
|
31
31
|
BRIDGE_PROTOCOL_VERSION,
|
|
@@ -149,12 +149,32 @@ const MAX_BODY_BYTES = 256 * 1024;
|
|
|
149
149
|
const MAX_UPLOAD_BYTES = 25 * 1024 * 1024;
|
|
150
150
|
const PORT_FALLBACKS = 5;
|
|
151
151
|
|
|
152
|
+
// Failed-auth lockout: after this many wrong tokens from one address inside
|
|
153
|
+
// the window, that address gets 429s until the window lapses. The token's
|
|
154
|
+
// 256 bits make brute force hopeless anyway — this is about not letting an
|
|
155
|
+
// internet-facing bridge be hammered for free (and giving fail2ban-style
|
|
156
|
+
// tooling a clean signal in the log). Only *presented-and-wrong* secrets
|
|
157
|
+
// count: tokenless probes are just scanners finding a locked door.
|
|
158
|
+
const AUTH_LOCKOUT_MAX_FAILURES = 20;
|
|
159
|
+
const AUTH_LOCKOUT_WINDOW_MS = 15 * 60 * 1000;
|
|
160
|
+
/** Hard cap on tracked addresses so the map can't become a memory lever. */
|
|
161
|
+
const AUTH_LOCKOUT_MAX_TRACKED = 10_000;
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* ok: request carries the right token (or none is required).
|
|
165
|
+
* anonymous: no credential presented — a pre-pairing probe, not an attack.
|
|
166
|
+
* bad: a credential was presented and it is wrong.
|
|
167
|
+
*/
|
|
168
|
+
type AuthState = "ok" | "anonymous" | "bad";
|
|
169
|
+
|
|
152
170
|
export class BridgeServer {
|
|
153
171
|
private server: Server | null = null;
|
|
154
172
|
private clients = new Set<ServerResponse>();
|
|
155
173
|
private pingTimer: ReturnType<typeof setInterval> | undefined;
|
|
156
174
|
private port = 0;
|
|
157
175
|
private tlsIdentity: BridgeTlsIdentity | null = null;
|
|
176
|
+
/** Wrong-token counts per remote address (behind a proxy: per proxy). */
|
|
177
|
+
private authFailures = new Map<string, { count: number; resetAt: number }>();
|
|
158
178
|
|
|
159
179
|
constructor(
|
|
160
180
|
private readonly opts: {
|
|
@@ -311,21 +331,43 @@ export class BridgeServer {
|
|
|
311
331
|
return;
|
|
312
332
|
}
|
|
313
333
|
|
|
334
|
+
const remote = req.socket.remoteAddress ?? "unknown";
|
|
335
|
+
if (this.authLockedOut(remote)) {
|
|
336
|
+
res.writeHead(429, {
|
|
337
|
+
...this.jsonHeaders(),
|
|
338
|
+
"Retry-After": String(Math.ceil(AUTH_LOCKOUT_WINDOW_MS / 1000)),
|
|
339
|
+
});
|
|
340
|
+
res.end(
|
|
341
|
+
JSON.stringify({ ok: false, error: "Too many failed auth attempts" }),
|
|
342
|
+
);
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
const auth = this.authState(req, url);
|
|
347
|
+
if (auth === "bad") this.recordAuthFailure(remote);
|
|
348
|
+
else if (auth === "ok") this.authFailures.delete(remote);
|
|
349
|
+
|
|
314
350
|
// /health is unauthenticated so clients can discover/ping the bridge
|
|
315
|
-
// before they hold a token.
|
|
351
|
+
// before they hold a token. Pre-auth it serves only what pairing needs
|
|
352
|
+
// (identity, protocol, fingerprint) — operational details like bot name,
|
|
353
|
+
// backend, and chat count are not for internet scanners to enumerate.
|
|
316
354
|
if (method === "GET" && path === "/health") {
|
|
317
|
-
const
|
|
318
|
-
return this.json(res, 200, {
|
|
355
|
+
const base = {
|
|
319
356
|
app: "talon-bridge",
|
|
320
357
|
ok: true,
|
|
321
358
|
protocol: BRIDGE_PROTOCOL_VERSION,
|
|
322
|
-
host: this.opts.host,
|
|
323
359
|
port: this.port,
|
|
324
360
|
scheme: this.getScheme(),
|
|
325
361
|
// The certificate's own hash — public by definition (any TLS client
|
|
326
362
|
// sees the certificate), surfaced so pairing UIs can display it.
|
|
327
363
|
fingerprint: this.getFingerprint(),
|
|
328
364
|
authRequired: Boolean(this.opts.token),
|
|
365
|
+
};
|
|
366
|
+
if (auth !== "ok") return this.json(res, 200, base);
|
|
367
|
+
const s = this.handlers.status();
|
|
368
|
+
return this.json(res, 200, {
|
|
369
|
+
...base,
|
|
370
|
+
host: this.opts.host,
|
|
329
371
|
startedAt: this.opts.startedAt,
|
|
330
372
|
botName: s.botName,
|
|
331
373
|
backend: s.backend,
|
|
@@ -335,7 +377,7 @@ export class BridgeServer {
|
|
|
335
377
|
});
|
|
336
378
|
}
|
|
337
379
|
|
|
338
|
-
if (
|
|
380
|
+
if (auth !== "ok") {
|
|
339
381
|
return this.json(res, 401, { ok: false, error: "Unauthorized" });
|
|
340
382
|
}
|
|
341
383
|
|
|
@@ -677,17 +719,54 @@ export class BridgeServer {
|
|
|
677
719
|
|
|
678
720
|
// ── Helpers ──────────────────────────────────────────────────────────────
|
|
679
721
|
|
|
680
|
-
private
|
|
681
|
-
if (!this.opts.token) return
|
|
722
|
+
private authState(req: IncomingMessage, url: URL): AuthState {
|
|
723
|
+
if (!this.opts.token) return "ok";
|
|
682
724
|
const header = req.headers["authorization"];
|
|
683
|
-
|
|
684
|
-
typeof header === "string" &&
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
)
|
|
688
|
-
return true;
|
|
725
|
+
const fromHeader =
|
|
726
|
+
typeof header === "string" && header.startsWith("Bearer ")
|
|
727
|
+
? header.slice("Bearer ".length)
|
|
728
|
+
: null;
|
|
689
729
|
// EventSource can't set headers, so SSE clients pass ?token=… instead.
|
|
690
|
-
|
|
730
|
+
const candidate = fromHeader ?? url.searchParams.get("token");
|
|
731
|
+
if (candidate === null) return "anonymous";
|
|
732
|
+
return this.tokenMatches(candidate) ? "ok" : "bad";
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
private authLockedOut(remote: string): boolean {
|
|
736
|
+
const entry = this.authFailures.get(remote);
|
|
737
|
+
if (!entry) return false;
|
|
738
|
+
if (Date.now() >= entry.resetAt) {
|
|
739
|
+
this.authFailures.delete(remote);
|
|
740
|
+
return false;
|
|
741
|
+
}
|
|
742
|
+
return entry.count >= AUTH_LOCKOUT_MAX_FAILURES;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
private recordAuthFailure(remote: string): void {
|
|
746
|
+
const now = Date.now();
|
|
747
|
+
const entry = this.authFailures.get(remote);
|
|
748
|
+
if (!entry || now >= entry.resetAt) {
|
|
749
|
+
if (this.authFailures.size >= AUTH_LOCKOUT_MAX_TRACKED) {
|
|
750
|
+
for (const [ip, e] of this.authFailures) {
|
|
751
|
+
if (now >= e.resetAt) this.authFailures.delete(ip);
|
|
752
|
+
}
|
|
753
|
+
// Still saturated after pruning live entries — under that much churn
|
|
754
|
+
// dropping the newest attacker beats unbounded growth.
|
|
755
|
+
if (this.authFailures.size >= AUTH_LOCKOUT_MAX_TRACKED) return;
|
|
756
|
+
}
|
|
757
|
+
this.authFailures.set(remote, {
|
|
758
|
+
count: 1,
|
|
759
|
+
resetAt: now + AUTH_LOCKOUT_WINDOW_MS,
|
|
760
|
+
});
|
|
761
|
+
return;
|
|
762
|
+
}
|
|
763
|
+
entry.count++;
|
|
764
|
+
if (entry.count === AUTH_LOCKOUT_MAX_FAILURES) {
|
|
765
|
+
logWarn(
|
|
766
|
+
"native",
|
|
767
|
+
`Bridge auth lockout for ${remote} (${AUTH_LOCKOUT_MAX_FAILURES} wrong tokens in ${AUTH_LOCKOUT_WINDOW_MS / 60_000}m)`,
|
|
768
|
+
);
|
|
769
|
+
}
|
|
691
770
|
}
|
|
692
771
|
|
|
693
772
|
/**
|
|
@@ -708,6 +787,8 @@ export class BridgeServer {
|
|
|
708
787
|
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
|
|
709
788
|
"Access-Control-Allow-Headers": "Authorization, Content-Type",
|
|
710
789
|
"Access-Control-Max-Age": "86400",
|
|
790
|
+
// Every response states its type; never let a browser guess one.
|
|
791
|
+
"X-Content-Type-Options": "nosniff",
|
|
711
792
|
};
|
|
712
793
|
}
|
|
713
794
|
|
|
@@ -11,8 +11,8 @@ import type { Context } from "grammy";
|
|
|
11
11
|
import {
|
|
12
12
|
setChatModelForBackend,
|
|
13
13
|
setChatBackend,
|
|
14
|
-
resolveModelName,
|
|
15
14
|
} from "../../../storage/chat-settings.js";
|
|
15
|
+
import { resolveModelId as resolveModelName } from "../../../core/models/catalog.js";
|
|
16
16
|
import { resetSession } from "../../../storage/sessions.js";
|
|
17
17
|
import { clearHistory } from "../../../storage/history.js";
|
|
18
18
|
import {
|
|
@@ -211,7 +211,7 @@ export function registerAdminCommands(
|
|
|
211
211
|
}
|
|
212
212
|
|
|
213
213
|
// Unknown /command → "did you mean ...?" via the C similarity core
|
|
214
|
-
// (native/strsim-
|
|
214
|
+
// (native/strsim-wasm). Registered after every real command, so grammY
|
|
215
215
|
// only reaches this when nothing above matched. Only bare commands
|
|
216
216
|
// are intercepted — a close miss gets a suggestion, anything else
|
|
217
217
|
// keeps flowing to the agent as a normal message.
|
|
@@ -9,9 +9,9 @@ import {
|
|
|
9
9
|
setChatBackend,
|
|
10
10
|
setChatEffort,
|
|
11
11
|
setChatPulseInterval,
|
|
12
|
-
resolveModelName,
|
|
13
12
|
type EffortLevel,
|
|
14
13
|
} from "../../../storage/chat-settings.js";
|
|
14
|
+
import { resolveModelId as resolveModelName } from "../../../core/models/catalog.js";
|
|
15
15
|
import {
|
|
16
16
|
registerChat,
|
|
17
17
|
disablePulse,
|
|
@@ -18,7 +18,7 @@ export function splitMessage(text: string, max: number): string[] {
|
|
|
18
18
|
/**
|
|
19
19
|
* Escape HTML special characters for Telegram HTML parse mode.
|
|
20
20
|
* Must be applied to all text that is NOT inside an HTML tag.
|
|
21
|
-
* Delegates to the
|
|
21
|
+
* Delegates to the Rust core (native/htmlents-wasm): one pass over the
|
|
22
22
|
* bytes instead of the five chained regex passes this replaces.
|
|
23
23
|
*/
|
|
24
24
|
export function escapeHtml(text: string): string {
|
|
@@ -20,8 +20,8 @@ import {
|
|
|
20
20
|
getChatSettings,
|
|
21
21
|
setChatModel,
|
|
22
22
|
setChatEffort,
|
|
23
|
-
resolveModelName,
|
|
24
23
|
} from "../../storage/chat-settings.js";
|
|
24
|
+
import { resolveModelId as resolveModelName } from "../../core/models/catalog.js";
|
|
25
25
|
import {
|
|
26
26
|
getAllSessions,
|
|
27
27
|
getSession,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Generated by native/shared/build-lib.mjs — do not edit by hand.
|
|
2
|
-
// Rebuild with `npm run build:
|
|
3
|
-
// Source: native/htmlents-
|
|
2
|
+
// Rebuild with `npm run build:wasm` after changing the source.
|
|
3
|
+
// Source: native/htmlents-wasm (1195 bytes of wasm32-unknown-unknown).
|
|
4
4
|
|
|
5
|
-
/** native/htmlents-
|
|
5
|
+
/** native/htmlents-wasm artifact, base64-encoded. Decoded + instantiated lazily by src/native/htmlents.ts. */
|
|
6
6
|
export const HTMLENTS_WASM_BASE64 =
|
|
7
|
-
"
|
|
7
|
+
"AGFzbQEAAAABKQdgBX9/f39/AGADf39/AGACf38AYAR/f39/AGABfwF/YAJ/fwF/YAAAAwsKAAECAwIEAgUCBgUDAQARBhkDfwFBgIDAAAt/AEGggMAAC38AQaCAwAALB0UGBm1lbW9yeQIABWFsbG9jAAULX19oZWFwX2Jhc2UDAQdkZWFsbG9jAAYLZXNjYXBlX2h0bWwABwpfX2RhdGFfZW5kAwIK3gcKNQACQCACIAFJDQAgAiAESw0AIAAgAiABazYCBCAAIAMgAWo2AgAPCyABIAIgBBCBgICAAAALCQAQiYCAgAAAC40BAQJ/QQAhAgJAAkACQAJAAkACQAJAAkAgAUH/AXEiAUFeag4GBAcHBwEFAAsgAUFEag4DAQYCBgtBgIDAgAAhAgwEC0GFgMCAACECQQQhAwwEC0GJgMCAACECQQQhAwwDC0GNgMCAACECQQYhAwwCC0GTgMCAACECC0EFIQMLIAAgAzYCBCAAIAI2AgALKgACQCABIANHDQACQCABRQ0AIAAgAiAB/AoAAAsPCyABIAMQhICAgAAACwkAEImAgIAAAAuiAQEDfwJAIAANAEEADwtBACEBAkBBACgCmIDAgAAiAg0AQaCAwIAAIQJBAEGggMCAADYCmIDAgAALAkACQCACQQdqQXhxIgIgAGoiACACSQ0AIAA/AEEQdCIDTQ0BQQAhASAAIANrQRB2IABB//8DcUEAR2pAAEF/Rw0BCyABDwtBACAANgKYgMCAAEEAQQAoApyAwIAAQQFqNgKcgMCAACACC0AAAkAgAEUNACABRQ0AQQAoApyAwIAAIgBFDQBBACAAQX9qIgA2ApyAwIAAIAANAEEAQaCAwIAANgKYgMCAAAsL5AMBB38jgICAgABBwABrIgIkgICAgABBACEDQQAhBAJAIAFFDQBBACEEIAAhBSABIQYDQCACQTBqIAUtAAAQgoCAgAAgAigCNEEBIAIoAjAbIARqIQQgBUEBaiEFIAZBf2oiBg0ACwtBDCEFAkAgBEEMaiIHEIWAgIAAIghFDQAgAEEBIAEbIQYgAkEoakEAQQQgCCAHEICAgIAAIAIoAiwhAyACKAIoIQAgAiAHNgI8IAAgAyACQTxqQQQQg4CAgAAgAkEgakEEQQggCCAHEICAgIAAIAIoAiQhAyACKAIgIQAgAkEBNgI8IAAgAyACQTxqQQQQg4CAgAAgAkEYakEIQQwgCCAHEICAgIAAIAIoAhwhAyACKAIYIQAgAiAENgI8IAAgAyACQTxqQQQQg4CAgAADQAJAAkACQCABRQ0AIAJBEGogBi0AACIDEIKAgIAAIAIoAhAiBEUNASACQQhqIAUgAigCFCIDIAVqIgAgCCAHEICAgIAAIAIoAgggAigCDCAEIAMQg4CAgAAgACEFDAILIAghAwwDCwJAIAUgB08NACAIIAVqIAM6AAAgBUEBaiEFDAELIAUgBxCIgICAAAALIAZBAWohBiABQX9qIQEMAAsLIAJBwABqJICAgIAAIAMLCQAQiYCAgAAACwMAAAsLIQEAQYCAwAALGCZhbXA7Jmx0OyZndDsmcXVvdDsmIzM5Ow==";
|
package/src/native/htmlents.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* HTML entity escaping — TypeScript boundary over the
|
|
2
|
+
* HTML entity escaping — TypeScript boundary over the Rust wasm module.
|
|
3
3
|
*
|
|
4
4
|
* One single-pass escaper for Telegram HTML parse mode, replacing the
|
|
5
5
|
* five chained regex passes it supersedes. Every outbound Telegram
|
|
6
|
-
* render flows through here (frontend/telegram/formatting.ts). The
|
|
7
|
-
* source lives in native/htmlents-
|
|
6
|
+
* render flows through here (frontend/telegram/formatting.ts). The Rust
|
|
7
|
+
* source lives in native/htmlents-wasm and exports a three-function
|
|
8
8
|
* C ABI (alloc / dealloc / escape_html) — see that module's README for
|
|
9
9
|
* the contract, and src/native/runtime.ts for the embedding and memory
|
|
10
10
|
* conventions shared by every native module.
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
type WasmCoreExports,
|
|
25
25
|
} from "./runtime.js";
|
|
26
26
|
|
|
27
|
-
/** The C-ABI surface exported by native/htmlents-
|
|
27
|
+
/** The C-ABI surface exported by native/htmlents-wasm. */
|
|
28
28
|
interface HtmlentsExports extends WasmCoreExports {
|
|
29
29
|
escape_html(inputPtr: number, len: number): number;
|
|
30
30
|
}
|
package/src/native/registry.ts
CHANGED
|
@@ -75,9 +75,9 @@ export const NATIVE_MODULES: readonly NativeModuleSpec[] = [
|
|
|
75
75
|
},
|
|
76
76
|
{
|
|
77
77
|
name: "strsim",
|
|
78
|
-
language: "
|
|
79
|
-
target: "wasm32-
|
|
80
|
-
sourceDir: "native/strsim-
|
|
78
|
+
language: "Rust",
|
|
79
|
+
target: "wasm32-unknown-unknown",
|
|
80
|
+
sourceDir: "native/strsim-wasm",
|
|
81
81
|
sizeBytes: async () =>
|
|
82
82
|
base64ByteLength(
|
|
83
83
|
(await import("./strsim-wasm-bytes.js")).STRSIM_WASM_BASE64,
|
|
@@ -90,9 +90,9 @@ export const NATIVE_MODULES: readonly NativeModuleSpec[] = [
|
|
|
90
90
|
},
|
|
91
91
|
{
|
|
92
92
|
name: "sqlguard",
|
|
93
|
-
language: "
|
|
94
|
-
target: "wasm32-
|
|
95
|
-
sourceDir: "native/sqlguard-
|
|
93
|
+
language: "Rust",
|
|
94
|
+
target: "wasm32-unknown-unknown",
|
|
95
|
+
sourceDir: "native/sqlguard-wasm",
|
|
96
96
|
sizeBytes: async () =>
|
|
97
97
|
base64ByteLength(
|
|
98
98
|
(await import("./sqlguard-wasm-bytes.js")).SQLGUARD_WASM_BASE64,
|
|
@@ -107,9 +107,9 @@ export const NATIVE_MODULES: readonly NativeModuleSpec[] = [
|
|
|
107
107
|
},
|
|
108
108
|
{
|
|
109
109
|
name: "htmlents",
|
|
110
|
-
language: "
|
|
111
|
-
target: "wasm32-
|
|
112
|
-
sourceDir: "native/htmlents-
|
|
110
|
+
language: "Rust",
|
|
111
|
+
target: "wasm32-unknown-unknown",
|
|
112
|
+
sourceDir: "native/htmlents-wasm",
|
|
113
113
|
sizeBytes: async () =>
|
|
114
114
|
base64ByteLength(
|
|
115
115
|
(await import("./htmlents-wasm-bytes.js")).HTMLENTS_WASM_BASE64,
|
package/src/native/runtime.ts
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* Embedded-wasm runtime — the one place that knows how Talon's native
|
|
3
3
|
* modules cross the JS boundary.
|
|
4
4
|
*
|
|
5
|
-
* Every wasm module in the native plane (Rust blake3,
|
|
6
|
-
*
|
|
5
|
+
* Every wasm module in the native plane (Rust blake3, strsim,
|
|
6
|
+
* sqlguard, htmlents; Zig textops) follows the same contract:
|
|
7
7
|
*
|
|
8
8
|
* - The artifact ships as base64 inside a generated `*-bytes.ts`
|
|
9
9
|
* module, so it survives `bun build --compile` single binaries —
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Generated by native/shared/build-lib.mjs — do not edit by hand.
|
|
2
|
-
// Rebuild with `npm run build:
|
|
3
|
-
// Source: native/sqlguard-
|
|
2
|
+
// Rebuild with `npm run build:wasm` after changing the source.
|
|
3
|
+
// Source: native/sqlguard-wasm (1805 bytes of wasm32-unknown-unknown).
|
|
4
4
|
|
|
5
|
-
/** native/sqlguard-
|
|
5
|
+
/** native/sqlguard-wasm artifact, base64-encoded. Decoded + instantiated lazily by src/native/sqlguard.ts. */
|
|
6
6
|
export const SQLGUARD_WASM_BASE64 =
|
|
7
|
-
"
|
|
7
|
+
"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";
|
package/src/native/sqlguard.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* pattern for a LIKE search (get_user_messages), and `ftsQuote` turns
|
|
7
7
|
* free-form text into a literal FTS5 MATCH expression (search_history).
|
|
8
8
|
*
|
|
9
|
-
* The
|
|
9
|
+
* The Rust source lives in native/sqlguard-wasm and exports a four-function
|
|
10
10
|
* C ABI (alloc / dealloc / escape_like / fts_quote) — see that module's
|
|
11
11
|
* README for the contract, and src/native/runtime.ts for the embedding
|
|
12
12
|
* and memory conventions shared by every native module.
|
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
} from "./runtime.js";
|
|
28
28
|
import { SQLGUARD_WASM_BASE64 } from "./sqlguard-wasm-bytes.js";
|
|
29
29
|
|
|
30
|
-
/** The C-ABI surface exported by native/sqlguard-
|
|
30
|
+
/** The C-ABI surface exported by native/sqlguard-wasm. */
|
|
31
31
|
interface SqlguardExports extends WasmCoreExports {
|
|
32
32
|
escape_like(inputPtr: number, len: number): number;
|
|
33
33
|
fts_quote(inputPtr: number, len: number): number;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Generated by native/shared/build-lib.mjs — do not edit by hand.
|
|
2
|
-
// Rebuild with `npm run build:
|
|
3
|
-
// Source: native/strsim-
|
|
2
|
+
// Rebuild with `npm run build:wasm` after changing the source.
|
|
3
|
+
// Source: native/strsim-wasm (725 bytes of wasm32-unknown-unknown).
|
|
4
4
|
|
|
5
|
-
/** native/strsim-
|
|
5
|
+
/** native/strsim-wasm artifact, base64-encoded. Decoded + instantiated lazily by src/native/strsim.ts. */
|
|
6
6
|
export const STRSIM_WASM_BASE64 =
|
|
7
|
-
"
|
|
7
|
+
"AGFzbQEAAAABFgRgAX8Bf2ACf38AYAR/f39/AX9gAAADBgUAAQIBAwUDAQARBhkDfwFBgIDAAAt/AEGQoMAAC38AQYygwAALB0UGBm1lbW9yeQIABWFsbG9jAAALX19oZWFwX2Jhc2UDAQdkZWFsbG9jAAELbGV2ZW5zaHRlaW4AAgpfX2RhdGFfZW5kAwIKwwQFogEBA38CQCAADQBBAA8LQQAhAQJAQQAoAoSgwIAAIgINAEGQoMCAACECQQBBkKDAgAA2AoSgwIAACwJAAkAgAkEHakF4cSICIABqIgAgAkkNACAAPwBBEHQiA00NAUEAIQEgACADa0EQdiAAQf//A3FBAEdqQABBf0cNAQsgAQ8LQQAgADYChKDAgABBAEEAKAKIoMCAAEEBajYCiKDAgAAgAgtAAAJAIABFDQAgAUUNAEEAKAKIoMCAACIARQ0AQQAgAEF/aiIANgKIoMCAACAADQBBAEGQoMCAADYChKDAgAALC80CAQl/QX8hBAJAIAFBgAhLDQAgA0GACEsNAAJAIAENACADDwsCQCADDQAgAQ8LIANBAWohBUEAIQRBgIDAgAAhBgJAA0AgBSAERg0BIAYgBDYCACAGQQRqIQYgBEEBaiEEDAALCyAAIAFqIQdBACEIQQAoAoCAwIAAIQUCQANAIAAgB0YNASAALQAAIQFBACEEQQAgCEEBaiIINgKAgMCAACAAQQFqIQBBhIDAgAAhBiABQf8BcSEJIAghAQNAIAIgBGohCgJAIAMgBEcNACAIIQUMAgsgBEEBaiELAkAgBEGACEcNACALQYEIEIOAgIAAAAsgBiABQQFqIgQgBigCACIMQQFqIgEgBSAJIAotAABHaiIFIAEgBUkbIgUgBCAFSRsiATYCACAGQQRqIQYgCyEEIAwhBQwACwsLIANBAnQoAoCAwIAAIQQLIAQLCQAQhICAgAAACwMAAAs=";
|
package/src/native/strsim.ts
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* String similarity — TypeScript boundary over the C wasm module.
|
|
3
3
|
*
|
|
4
4
|
* Powers "did you mean ...?" suggestions for unknown Telegram slash
|
|
5
|
-
* commands and unknown CLI subcommands. The
|
|
6
|
-
* native/strsim-
|
|
5
|
+
* commands and unknown CLI subcommands. The Rust source lives in
|
|
6
|
+
* native/strsim-wasm and exports a three-function C ABI (alloc / dealloc /
|
|
7
7
|
* levenshtein) — see that module's README for the contract, and
|
|
8
8
|
* src/native/runtime.ts for the embedding and memory conventions
|
|
9
9
|
* shared by every native module.
|
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
/** Returned by the wasm side when either input exceeds its DP buffer. */
|
|
30
30
|
const OVERFLOW = 0xffffffff;
|
|
31
31
|
|
|
32
|
-
/** The C-ABI surface exported by native/strsim-
|
|
32
|
+
/** The C-ABI surface exported by native/strsim-wasm. */
|
|
33
33
|
interface StrsimExports extends WasmCoreExports {
|
|
34
34
|
levenshtein(aPtr: number, aLen: number, bPtr: number, bLen: number): number;
|
|
35
35
|
}
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
import { readFileSync } from "node:fs";
|
|
22
22
|
import { resolve } from "node:path";
|
|
23
23
|
import { fileURLToPath } from "node:url";
|
|
24
|
-
import type { TalonPlugin } from "../../core/plugin/
|
|
24
|
+
import type { TalonPlugin } from "../../core/plugin/types.js";
|
|
25
25
|
import { log, logWarn } from "../../util/log.js";
|
|
26
26
|
import { dirs } from "../../util/paths.js";
|
|
27
27
|
|
|
@@ -18,7 +18,7 @@ import { existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
|
18
18
|
import { resolve } from "node:path";
|
|
19
19
|
import { execFile as execFileCb, execFileSync } from "node:child_process";
|
|
20
20
|
import { promisify } from "node:util";
|
|
21
|
-
import type { TalonPlugin } from "../../core/plugin/
|
|
21
|
+
import type { TalonPlugin } from "../../core/plugin/types.js";
|
|
22
22
|
import { log, logWarn } from "../../util/log.js";
|
|
23
23
|
import { dirs } from "../../util/paths.js";
|
|
24
24
|
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
|
|
22
22
|
import { existsSync, readFileSync } from "node:fs";
|
|
23
23
|
import { resolve } from "node:path";
|
|
24
|
-
import type { TalonPlugin } from "../../core/plugin/
|
|
24
|
+
import type { TalonPlugin } from "../../core/plugin/types.js";
|
|
25
25
|
import { log } from "../../util/log.js";
|
|
26
26
|
|
|
27
27
|
export function createPlaywrightPlugin(config: {
|
|
@@ -22,63 +22,12 @@ import { recordError } from "../util/watchdog.js";
|
|
|
22
22
|
import { files } from "../util/paths.js";
|
|
23
23
|
import { importLegacyJson } from "./legacy-import.js";
|
|
24
24
|
import * as repo from "./repositories/chat-settings-repo.js";
|
|
25
|
-
import type { ReasoningEffortLevel } from "../
|
|
25
|
+
import type { ReasoningEffortLevel } from "../types/effort.js";
|
|
26
26
|
|
|
27
27
|
export type EffortLevel = ReasoningEffortLevel;
|
|
28
28
|
|
|
29
|
-
export type ChatSettings
|
|
30
|
-
|
|
31
|
-
* Per-backend model overrides for this chat. Keyed by backend id
|
|
32
|
-
* (`"claude"`, `"codex"`, `"openai-agents"`, etc). Each entry is the
|
|
33
|
-
* model id the user picked on that backend.
|
|
34
|
-
*
|
|
35
|
-
* Switching backends preserves each side's last pick — your Codex
|
|
36
|
-
* chat remembers `gpt-5.5`, your OpenRouter chat remembers
|
|
37
|
-
* `meta-llama/...`. Replaces the single legacy `model` field which
|
|
38
|
-
* couldn't differentiate per-backend choices and produced the
|
|
39
|
-
* orphan-bug class (model from backend X persisting when switching
|
|
40
|
-
* to backend Y).
|
|
41
|
-
*
|
|
42
|
-
* Resolution order (see `core/models/active-model.ts`):
|
|
43
|
-
* 1. `modelByBackend[activeBackend]` if it validates on the catalog
|
|
44
|
-
* 2. `backend.getDefaultModel()` (canonical for backends that have one)
|
|
45
|
-
* 3. `config.backendDefaults[activeBackend]` (operator override)
|
|
46
|
-
* 4. `config.model` (only when activeBackend === config.backend)
|
|
47
|
-
* 5. null → "No model selected" UI + send guard refuses.
|
|
48
|
-
*/
|
|
49
|
-
modelByBackend?: Record<string, string>;
|
|
50
|
-
/**
|
|
51
|
-
* @deprecated Single-slot model field. Retained for back-compat with
|
|
52
|
-
* old stores; migrated into `modelByBackend` on load. New writes go
|
|
53
|
-
* through `setChatModelForBackend` instead.
|
|
54
|
-
*/
|
|
55
|
-
model?: string;
|
|
56
|
-
/**
|
|
57
|
-
* Backend override for this chat. When set, queries from this chat
|
|
58
|
-
* route to the override backend instead of the global `config.backend`.
|
|
59
|
-
* The backend controller refcounts pool instances, so two chats on
|
|
60
|
-
* two different backends keep both alive concurrently.
|
|
61
|
-
*
|
|
62
|
-
* Stored as the registry id (e.g. `"claude"`, `"openai-agents"`).
|
|
63
|
-
* Cleared via `setChatBackend(cid, undefined)` — chat reverts to
|
|
64
|
-
* the global default.
|
|
65
|
-
*/
|
|
66
|
-
backend?: string;
|
|
67
|
-
/** Effort level override (maps to SDK thinking + effort options). */
|
|
68
|
-
effort?: EffortLevel;
|
|
69
|
-
/** Whether pulse is enabled for this chat. */
|
|
70
|
-
pulse?: boolean;
|
|
71
|
-
/** Per-chat pulse check interval in milliseconds. */
|
|
72
|
-
pulseIntervalMs?: number;
|
|
73
|
-
/** Last message ID checked by pulse (persisted to avoid reprocessing on restart). */
|
|
74
|
-
pulseLastCheckMsgId?: number;
|
|
75
|
-
/**
|
|
76
|
-
* When true, the model picker filters to free-tier models by default.
|
|
77
|
-
* Only meaningful for backends that report free-tier metadata (currently
|
|
78
|
-
* `openai-agents` against OpenRouter); other backends ignore the flag.
|
|
79
|
-
*/
|
|
80
|
-
freeOnly?: boolean;
|
|
81
|
-
};
|
|
29
|
+
export type { ChatSettings } from "./repositories/chat-settings-repo.js";
|
|
30
|
+
import type { ChatSettings } from "./repositories/chat-settings-repo.js";
|
|
82
31
|
|
|
83
32
|
// In-memory cache over the chat_settings table. Reads serve live
|
|
84
33
|
// references from here; writes go through persist() so each mutation
|
|
@@ -488,9 +437,3 @@ export const EFFORT_LEVELS: EffortLevel[] = [
|
|
|
488
437
|
"max",
|
|
489
438
|
"xhigh",
|
|
490
439
|
];
|
|
491
|
-
|
|
492
|
-
/**
|
|
493
|
-
* Resolve a user-provided model name (alias or full ID) to the canonical model ID.
|
|
494
|
-
* Delegates to the model registry; falls through unknown names unchanged.
|
|
495
|
-
*/
|
|
496
|
-
export { resolveModelId as resolveModelName } from "../core/models/catalog.js";
|
|
@@ -20,87 +20,13 @@ import { inTransaction } from "./db.js";
|
|
|
20
20
|
import * as repo from "./repositories/cron-repo.js";
|
|
21
21
|
import { nextDueMs, type CatchupPolicy } from "../native/scheduler-core.js";
|
|
22
22
|
|
|
23
|
-
export type
|
|
23
|
+
export type {
|
|
24
|
+
CronJob,
|
|
25
|
+
CronJobType,
|
|
26
|
+
CronRunStatus,
|
|
27
|
+
} from "./repositories/cron-repo.js";
|
|
24
28
|
export type { CatchupPolicy };
|
|
25
|
-
|
|
26
|
-
/** Outcome of the most recent execution — surfaced in list_cron_jobs. */
|
|
27
|
-
export type CronRunStatus = "ok" | "error";
|
|
28
|
-
|
|
29
|
-
export type CronJob = {
|
|
30
|
-
id: string;
|
|
31
|
-
chatId: string;
|
|
32
|
-
/**
|
|
33
|
-
* Cron expression (5-field: minute hour day month weekday). Optional — a job
|
|
34
|
-
* carries EITHER `schedule` (cron mode) OR `everyMs` (interval mode), never
|
|
35
|
-
* both. The store validator (`isCronJob`) enforces exactly one.
|
|
36
|
-
*/
|
|
37
|
-
schedule?: string;
|
|
38
|
-
/**
|
|
39
|
-
* Fixed interval in milliseconds (interval mode). Mutually exclusive with
|
|
40
|
-
* `schedule`. The job fires roughly every `everyMs` after its anchor
|
|
41
|
-
* (`lastRunAt`, else `startAt`, else `createdAt`). Wires the native
|
|
42
|
-
* scheduler-core interval math (next-due + missed-run catch-up) directly.
|
|
43
|
-
*/
|
|
44
|
-
everyMs?: number;
|
|
45
|
-
/** "message" sends content as text; "query" runs content as a Claude prompt with tools */
|
|
46
|
-
type: CronJobType;
|
|
47
|
-
/** The message text or query prompt */
|
|
48
|
-
content: string;
|
|
49
|
-
/** Human-readable name for the job */
|
|
50
|
-
name: string;
|
|
51
|
-
enabled: boolean;
|
|
52
|
-
createdAt: number;
|
|
53
|
-
lastRunAt?: number;
|
|
54
|
-
runCount: number;
|
|
55
|
-
/** IANA timezone (e.g. "America/New_York"). Defaults to system timezone. */
|
|
56
|
-
timezone?: string;
|
|
57
|
-
/**
|
|
58
|
-
* Optional model override for `query` jobs. Unset = the chat's model. `query`
|
|
59
|
-
* cron jobs run as an isolated one-shot (no chat session), so unlike triggers
|
|
60
|
-
* the model may be on a different provider — see `provider`.
|
|
61
|
-
*/
|
|
62
|
-
model?: string;
|
|
63
|
-
/**
|
|
64
|
-
* Optional provider/backend id for the override (e.g. a cheaper provider than
|
|
65
|
-
* the chat). Requires `model`. Unset = the chat's backend. Since cron runs
|
|
66
|
-
* isolated, a different provider is fine here.
|
|
67
|
-
*/
|
|
68
|
-
provider?: string;
|
|
69
|
-
/**
|
|
70
|
-
* Optional short brief that becomes the isolated agent's system prompt — what
|
|
71
|
-
* the job is and how to do it. Useful to orient a cheaper override model.
|
|
72
|
-
*/
|
|
73
|
-
instructions?: string;
|
|
74
|
-
/**
|
|
75
|
-
* Don't fire before this epoch-ms instant (a delayed start / "not before").
|
|
76
|
-
* Unset = eligible immediately.
|
|
77
|
-
*/
|
|
78
|
-
startAt?: number;
|
|
79
|
-
/**
|
|
80
|
-
* Don't fire after this epoch-ms instant; the job auto-disables once now
|
|
81
|
-
* passes it (a natural expiry / "until"). Unset = no end.
|
|
82
|
-
*/
|
|
83
|
-
endAt?: number;
|
|
84
|
-
/**
|
|
85
|
-
* Auto-disable after this many total runs (`runCount >= maxRuns`). A value of
|
|
86
|
-
* 1 makes the job one-shot. Unset = unbounded.
|
|
87
|
-
*/
|
|
88
|
-
maxRuns?: number;
|
|
89
|
-
/**
|
|
90
|
-
* Missed-run policy for runs that were due while Talon was down:
|
|
91
|
-
* "skip" (default) — drop them, resume on the next due tick
|
|
92
|
-
* "once" — collapse any number of missed runs into a single catch-up
|
|
93
|
-
* "all" — replay every missed run, capped by CATCHUP_MAX
|
|
94
|
-
* Decided by the native scheduler-core `catchupRunCount`.
|
|
95
|
-
*/
|
|
96
|
-
catchup?: CatchupPolicy;
|
|
97
|
-
/** Status of the most recent execution. */
|
|
98
|
-
lastStatus?: CronRunStatus;
|
|
99
|
-
/** Error message from the most recent failed execution (cleared on success). */
|
|
100
|
-
lastError?: string;
|
|
101
|
-
/** Wall-clock duration of the most recent execution, in ms. */
|
|
102
|
-
lastDurationMs?: number;
|
|
103
|
-
};
|
|
29
|
+
import type { CronJob, CronRunStatus } from "./repositories/cron-repo.js";
|
|
104
30
|
|
|
105
31
|
/** Telemetry recorded after each execution attempt. */
|
|
106
32
|
export type CronRunOutcome = {
|
|
@@ -17,25 +17,16 @@
|
|
|
17
17
|
import { randomUUID } from "node:crypto";
|
|
18
18
|
import * as repo from "./repositories/goals-repo.js";
|
|
19
19
|
|
|
20
|
-
export type
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
priority: GoalPriority;
|
|
31
|
-
createdAt: number;
|
|
32
|
-
updatedAt: number;
|
|
33
|
-
/** Optional soft deadline (unix ms). */
|
|
34
|
-
dueAt?: number;
|
|
35
|
-
/** Rolling note from the last progress update. */
|
|
36
|
-
lastProgressNote?: string;
|
|
37
|
-
lastProgressAt?: number;
|
|
38
|
-
};
|
|
20
|
+
export type {
|
|
21
|
+
Goal,
|
|
22
|
+
GoalPriority,
|
|
23
|
+
GoalStatus,
|
|
24
|
+
} from "./repositories/goals-repo.js";
|
|
25
|
+
import type {
|
|
26
|
+
Goal,
|
|
27
|
+
GoalPriority,
|
|
28
|
+
GoalStatus,
|
|
29
|
+
} from "./repositories/goals-repo.js";
|
|
39
30
|
|
|
40
31
|
export const GOAL_STATUSES: readonly GoalStatus[] = [
|
|
41
32
|
"active",
|