skimpyclaw 0.3.14 → 0.4.0
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/README.md +47 -37
- package/dist/__tests__/adapter-types.test.d.ts +4 -0
- package/dist/__tests__/adapter-types.test.js +63 -0
- package/dist/__tests__/anthropic-adapter.test.d.ts +4 -0
- package/dist/__tests__/anthropic-adapter.test.js +264 -0
- package/dist/__tests__/api.test.js +0 -1
- package/dist/__tests__/cli.integration.test.js +2 -4
- package/dist/__tests__/cli.test.js +0 -1
- package/dist/__tests__/code-agents-notifications.test.js +137 -0
- package/dist/__tests__/code-agents-parser.test.js +19 -1
- package/dist/__tests__/code-agents-preflight.test.js +3 -28
- package/dist/__tests__/code-agents-utils.test.js +34 -9
- package/dist/__tests__/code-agents-worktrees.test.js +116 -0
- package/dist/__tests__/codex-adapter.test.js +184 -0
- package/dist/__tests__/codex-auth.test.js +66 -0
- package/dist/__tests__/codex-provider-gating.test.js +35 -0
- package/dist/__tests__/codex-unified-loop.test.js +111 -0
- package/dist/__tests__/config-security.test.js +127 -0
- package/dist/__tests__/config.test.js +23 -0
- package/dist/__tests__/context-manager.test.js +243 -164
- package/dist/__tests__/cron-run.test.js +250 -0
- package/dist/__tests__/cron.test.js +12 -38
- package/dist/__tests__/digests.test.js +67 -0
- package/dist/__tests__/discord-attachments.test.js +211 -0
- package/dist/__tests__/discord-docs.test.d.ts +1 -0
- package/dist/__tests__/discord-docs.test.js +27 -0
- package/dist/__tests__/discord-thread-agents.test.d.ts +1 -0
- package/dist/__tests__/discord-thread-agents.test.js +115 -0
- package/dist/__tests__/discord-thread-context.test.d.ts +1 -0
- package/dist/__tests__/discord-thread-context.test.js +42 -0
- package/dist/__tests__/doctor.formatters.test.js +4 -4
- package/dist/__tests__/doctor.index.test.js +1 -1
- package/dist/__tests__/doctor.runner.test.js +3 -15
- package/dist/__tests__/env-sanitizer.test.d.ts +1 -0
- package/dist/__tests__/env-sanitizer.test.js +45 -0
- package/dist/__tests__/exec-approval.test.js +61 -0
- package/dist/__tests__/fetch-tool.test.d.ts +1 -0
- package/dist/__tests__/fetch-tool.test.js +85 -0
- package/dist/__tests__/gateway-status-auth.test.d.ts +1 -0
- package/dist/__tests__/gateway-status-auth.test.js +72 -0
- package/dist/__tests__/heartbeat.test.js +3 -3
- package/dist/__tests__/interactive-sessions.test.d.ts +1 -0
- package/dist/__tests__/interactive-sessions.test.js +96 -0
- package/dist/__tests__/langfuse.test.js +6 -18
- package/dist/__tests__/model-selection.test.js +3 -4
- package/dist/__tests__/providers-init.test.js +2 -8
- package/dist/__tests__/providers-routing.test.js +1 -1
- package/dist/__tests__/providers-utils.test.js +13 -3
- package/dist/__tests__/sessions.test.js +14 -10
- package/dist/__tests__/setup.test.js +12 -29
- package/dist/__tests__/skills.test.js +10 -7
- package/dist/__tests__/stream-formatter.test.d.ts +1 -0
- package/dist/__tests__/stream-formatter.test.js +114 -0
- package/dist/__tests__/token-efficiency.test.js +131 -15
- package/dist/__tests__/tool-loop.test.d.ts +4 -0
- package/dist/__tests__/tool-loop.test.js +505 -0
- package/dist/__tests__/tools.test.js +101 -276
- package/dist/__tests__/utils.test.d.ts +1 -0
- package/dist/__tests__/utils.test.js +14 -0
- package/dist/__tests__/voice.test.js +21 -0
- package/dist/agent.js +35 -4
- package/dist/api.js +113 -37
- package/dist/channels/discord/attachments.d.ts +50 -0
- package/dist/channels/discord/attachments.js +137 -0
- package/dist/channels/discord/delegation.d.ts +5 -0
- package/dist/channels/discord/delegation.js +136 -0
- package/dist/channels/discord/handlers.js +694 -7
- package/dist/channels/discord/index.d.ts +16 -1
- package/dist/channels/discord/index.js +64 -1
- package/dist/channels/discord/thread-agents.d.ts +54 -0
- package/dist/channels/discord/thread-agents.js +323 -0
- package/dist/channels/discord/threads.d.ts +58 -0
- package/dist/channels/discord/threads.js +192 -0
- package/dist/channels/discord/types.js +4 -2
- package/dist/channels/discord/utils.d.ts +16 -0
- package/dist/channels/discord/utils.js +86 -6
- package/dist/channels/telegram/index.d.ts +1 -1
- package/dist/channels/telegram/types.js +1 -1
- package/dist/channels/telegram/utils.js +9 -3
- package/dist/channels.d.ts +1 -1
- package/dist/cli.js +20 -400
- package/dist/code-agents/executor.d.ts +1 -1
- package/dist/code-agents/executor.js +101 -45
- package/dist/code-agents/index.d.ts +2 -7
- package/dist/code-agents/index.js +111 -80
- package/dist/code-agents/interactive-resume.d.ts +6 -0
- package/dist/code-agents/interactive-resume.js +98 -0
- package/dist/code-agents/interactive-sessions.d.ts +20 -0
- package/dist/code-agents/interactive-sessions.js +132 -0
- package/dist/code-agents/parser.js +5 -1
- package/dist/code-agents/registry.d.ts +7 -1
- package/dist/code-agents/registry.js +11 -23
- package/dist/code-agents/stream-formatter.d.ts +8 -0
- package/dist/code-agents/stream-formatter.js +92 -0
- package/dist/code-agents/types.d.ts +16 -24
- package/dist/code-agents/utils.d.ts +35 -11
- package/dist/code-agents/utils.js +349 -95
- package/dist/code-agents/worktrees.d.ts +37 -0
- package/dist/code-agents/worktrees.js +116 -0
- package/dist/config.d.ts +2 -4
- package/dist/config.js +123 -23
- package/dist/cron.d.ts +1 -6
- package/dist/cron.js +175 -82
- package/dist/dashboard/assets/index-B345aOO-.js +65 -0
- package/dist/dashboard/assets/index-ZWK4dalJ.css +1 -0
- package/dist/dashboard/index.html +2 -2
- package/dist/digests.d.ts +1 -0
- package/dist/digests.js +132 -42
- package/dist/doctor/checks.d.ts +0 -3
- package/dist/doctor/checks.js +1 -108
- package/dist/doctor/runner.js +1 -4
- package/dist/env-sanitizer.d.ts +2 -0
- package/dist/env-sanitizer.js +61 -0
- package/dist/exec-approval.d.ts +11 -1
- package/dist/exec-approval.js +17 -4
- package/dist/gateway.d.ts +3 -1
- package/dist/gateway.js +17 -7
- package/dist/heartbeat.js +1 -6
- package/dist/langfuse.js +3 -29
- package/dist/model-selection.js +3 -1
- package/dist/providers/adapter.d.ts +118 -0
- package/dist/providers/adapter.js +6 -0
- package/dist/providers/adapters/anthropic-adapter.d.ts +22 -0
- package/dist/providers/adapters/anthropic-adapter.js +204 -0
- package/dist/providers/adapters/codex-adapter.d.ts +26 -0
- package/dist/providers/adapters/codex-adapter.js +203 -0
- package/dist/providers/anthropic.d.ts +1 -0
- package/dist/providers/anthropic.js +10 -272
- package/dist/providers/codex.d.ts +21 -0
- package/dist/providers/codex.js +149 -330
- package/dist/providers/content.d.ts +1 -1
- package/dist/providers/content.js +2 -2
- package/dist/providers/context-manager.d.ts +18 -6
- package/dist/providers/context-manager.js +199 -223
- package/dist/providers/index.d.ts +9 -1
- package/dist/providers/index.js +73 -64
- package/dist/providers/loop-utils.d.ts +20 -0
- package/dist/providers/loop-utils.js +30 -0
- package/dist/providers/tool-loop.d.ts +12 -0
- package/dist/providers/tool-loop.js +251 -0
- package/dist/providers/utils.d.ts +19 -3
- package/dist/providers/utils.js +100 -29
- package/dist/secure-store.d.ts +8 -0
- package/dist/secure-store.js +80 -0
- package/dist/service.js +3 -28
- package/dist/sessions.d.ts +3 -0
- package/dist/sessions.js +147 -18
- package/dist/setup-templates.js +13 -25
- package/dist/setup.d.ts +10 -6
- package/dist/setup.js +84 -292
- package/dist/skills.js +3 -11
- package/dist/tools/agent-delegation.d.ts +19 -0
- package/dist/tools/agent-delegation.js +49 -0
- package/dist/tools/bash-tool.js +89 -34
- package/dist/tools/definitions.d.ts +199 -302
- package/dist/tools/definitions.js +70 -123
- package/dist/tools/execute-context.d.ts +13 -4
- package/dist/tools/fetch-tool.js +109 -13
- package/dist/tools/file-tools.js +7 -1
- package/dist/tools.d.ts +7 -7
- package/dist/tools.js +133 -151
- package/dist/types.d.ts +37 -30
- package/dist/utils.js +4 -6
- package/dist/voice.d.ts +1 -1
- package/dist/voice.js +17 -4
- package/package.json +33 -23
- package/templates/TOOLS.md +0 -27
- package/dist/__tests__/audit.test.js +0 -122
- package/dist/__tests__/code-agents-orchestrator.test.js +0 -216
- package/dist/__tests__/code-agents-sandbox.test.js +0 -163
- package/dist/__tests__/orchestrator.test.js +0 -425
- package/dist/__tests__/sandbox-bridge.test.js +0 -116
- package/dist/__tests__/sandbox-manager.test.js +0 -144
- package/dist/__tests__/sandbox-mount-security.test.js +0 -139
- package/dist/__tests__/sandbox-runtime.test.js +0 -176
- package/dist/__tests__/subagent.test.js +0 -240
- package/dist/__tests__/telegram.test.js +0 -42
- package/dist/code-agents/orchestrator.d.ts +0 -29
- package/dist/code-agents/orchestrator.js +0 -694
- package/dist/code-agents/worktree.d.ts +0 -40
- package/dist/code-agents/worktree.js +0 -215
- package/dist/dashboard/assets/index-BoTHPby4.js +0 -65
- package/dist/dashboard/assets/index-D4mufvBg.css +0 -1
- package/dist/dashboard.d.ts +0 -8
- package/dist/dashboard.js +0 -4071
- package/dist/discord.d.ts +0 -8
- package/dist/discord.js +0 -792
- package/dist/mcp-context-a8c.d.ts +0 -13
- package/dist/mcp-context-a8c.js +0 -34
- package/dist/orchestrator.d.ts +0 -15
- package/dist/orchestrator.js +0 -676
- package/dist/providers/openai.d.ts +0 -10
- package/dist/providers/openai.js +0 -355
- package/dist/sandbox/bridge.d.ts +0 -5
- package/dist/sandbox/bridge.js +0 -63
- package/dist/sandbox/index.d.ts +0 -5
- package/dist/sandbox/index.js +0 -4
- package/dist/sandbox/manager.d.ts +0 -7
- package/dist/sandbox/manager.js +0 -100
- package/dist/sandbox/mount-security.d.ts +0 -12
- package/dist/sandbox/mount-security.js +0 -122
- package/dist/sandbox/runtime.d.ts +0 -39
- package/dist/sandbox/runtime.js +0 -192
- package/dist/sandbox-utils.d.ts +0 -6
- package/dist/sandbox-utils.js +0 -36
- package/dist/subagent.d.ts +0 -19
- package/dist/subagent.js +0 -407
- package/dist/telegram.d.ts +0 -2
- package/dist/telegram.js +0 -11
- package/dist/tools/browser-tool.d.ts +0 -3
- package/dist/tools/browser-tool.js +0 -266
- package/sandbox/Dockerfile +0 -40
- /package/dist/__tests__/{audit.test.d.ts → code-agents-notifications.test.d.ts} +0 -0
- /package/dist/__tests__/{code-agents-orchestrator.test.d.ts → code-agents-worktrees.test.d.ts} +0 -0
- /package/dist/__tests__/{code-agents-sandbox.test.d.ts → codex-adapter.test.d.ts} +0 -0
- /package/dist/__tests__/{orchestrator.test.d.ts → codex-auth.test.d.ts} +0 -0
- /package/dist/__tests__/{sandbox-bridge.test.d.ts → codex-provider-gating.test.d.ts} +0 -0
- /package/dist/__tests__/{sandbox-manager.test.d.ts → codex-unified-loop.test.d.ts} +0 -0
- /package/dist/__tests__/{sandbox-mount-security.test.d.ts → config-security.test.d.ts} +0 -0
- /package/dist/__tests__/{sandbox-runtime.test.d.ts → cron-run.test.d.ts} +0 -0
- /package/dist/__tests__/{subagent.test.d.ts → digests.test.d.ts} +0 -0
- /package/dist/__tests__/{telegram.test.d.ts → discord-attachments.test.d.ts} +0 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/** Env var name patterns that should never be exposed to model-executed commands. */
|
|
2
|
+
const SENSITIVE_ENV_PATTERNS = [
|
|
3
|
+
/api.?key/i, /token/i, /secret/i, /password/i, /credential/i,
|
|
4
|
+
/^ANTHROPIC_/i, /^OPENAI_/i, /^CLAUDE/i, /^CODEX_/i, /^MINIMAX_/i,
|
|
5
|
+
/^KIMI_/i, /^TOGETHER_/i, /^GROQ_/i, /^OPENROUTER_/i,
|
|
6
|
+
];
|
|
7
|
+
/** Env vars that match SENSITIVE_ENV_PATTERNS but should be kept (e.g. tool auth). */
|
|
8
|
+
const SENSITIVE_ENV_ALLOWLIST = new Set(['GH_TOKEN']);
|
|
9
|
+
/** Common tool paths that may be missing when launched as a service/daemon. */
|
|
10
|
+
const EXTRA_PATH_DIRS = ['/opt/homebrew/bin', '/opt/homebrew/sbin', '/usr/local/bin'];
|
|
11
|
+
const CRON_ENV_ALLOWLIST = new Set([
|
|
12
|
+
'HOME',
|
|
13
|
+
'USER',
|
|
14
|
+
'LOGNAME',
|
|
15
|
+
'SHELL',
|
|
16
|
+
'PATH',
|
|
17
|
+
'TMPDIR',
|
|
18
|
+
'LANG',
|
|
19
|
+
'LC_ALL',
|
|
20
|
+
'LC_CTYPE',
|
|
21
|
+
'TZ',
|
|
22
|
+
'TERM',
|
|
23
|
+
'PWD',
|
|
24
|
+
'SHLVL',
|
|
25
|
+
'GH_TOKEN',
|
|
26
|
+
]);
|
|
27
|
+
export function sanitizeExecEnv() {
|
|
28
|
+
const env = { ...process.env };
|
|
29
|
+
for (const key of Object.keys(env)) {
|
|
30
|
+
if (!SENSITIVE_ENV_ALLOWLIST.has(key) && SENSITIVE_ENV_PATTERNS.some(p => p.test(key))) {
|
|
31
|
+
delete env[key];
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const currentPath = env.PATH || '';
|
|
35
|
+
const missing = EXTRA_PATH_DIRS.filter(d => !currentPath.includes(d));
|
|
36
|
+
if (missing.length > 0) {
|
|
37
|
+
env.PATH = currentPath ? `${currentPath}:${missing.join(':')}` : missing.join(':');
|
|
38
|
+
}
|
|
39
|
+
return env;
|
|
40
|
+
}
|
|
41
|
+
export function sanitizeCronEnv() {
|
|
42
|
+
const env = {};
|
|
43
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
44
|
+
if (value === undefined)
|
|
45
|
+
continue;
|
|
46
|
+
if (CRON_ENV_ALLOWLIST.has(key) || key.startsWith('SKIMPYCLAW_')) {
|
|
47
|
+
env[key] = value;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
for (const key of Object.keys(env)) {
|
|
51
|
+
if (!SENSITIVE_ENV_ALLOWLIST.has(key) && SENSITIVE_ENV_PATTERNS.some(p => p.test(key))) {
|
|
52
|
+
delete env[key];
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const currentPath = env.PATH || '';
|
|
56
|
+
const missing = EXTRA_PATH_DIRS.filter(d => !currentPath.includes(d));
|
|
57
|
+
if (missing.length > 0) {
|
|
58
|
+
env.PATH = currentPath ? `${currentPath}:${missing.join(':')}` : missing.join(':');
|
|
59
|
+
}
|
|
60
|
+
return env;
|
|
61
|
+
}
|
package/dist/exec-approval.d.ts
CHANGED
|
@@ -17,7 +17,14 @@ export interface ExecApprovalConfig {
|
|
|
17
17
|
ttlMs?: number;
|
|
18
18
|
requireForTiers?: number[];
|
|
19
19
|
}
|
|
20
|
-
export type ApprovalStatus = 'pending' | 'approved' | 'denied' | 'expired';
|
|
20
|
+
export type ApprovalStatus = 'pending' | 'approved' | 'denied' | 'expired' | 'consumed';
|
|
21
|
+
/** A single status transition record (append-only). */
|
|
22
|
+
export interface ApprovalHistoryEntry {
|
|
23
|
+
from: ApprovalStatus;
|
|
24
|
+
to: ApprovalStatus;
|
|
25
|
+
at: Date;
|
|
26
|
+
by?: string;
|
|
27
|
+
}
|
|
21
28
|
export interface PendingApproval {
|
|
22
29
|
id: string;
|
|
23
30
|
command: string;
|
|
@@ -32,6 +39,8 @@ export interface PendingApproval {
|
|
|
32
39
|
resolvedAt?: Date;
|
|
33
40
|
/** Channel context metadata — where the request originated */
|
|
34
41
|
channelMeta?: ApprovalChannelMeta;
|
|
42
|
+
/** Append-only history of status transitions */
|
|
43
|
+
history: ApprovalHistoryEntry[];
|
|
35
44
|
}
|
|
36
45
|
/** Metadata about the channel/chat where an approval request originated */
|
|
37
46
|
export interface ApprovalChannelMeta {
|
|
@@ -83,6 +92,7 @@ export declare function denyRequest(id: string, deniedBy?: string): boolean;
|
|
|
83
92
|
export declare function findApprovedRequest(command: string, cwd?: string): PendingApproval | undefined;
|
|
84
93
|
/**
|
|
85
94
|
* Mark an approved request as consumed (after successful execution).
|
|
95
|
+
* Preserves the approval in the registry with status 'consumed' for history.
|
|
86
96
|
*/
|
|
87
97
|
export declare function consumeApproval(id: string): void;
|
|
88
98
|
/**
|
package/dist/exec-approval.js
CHANGED
|
@@ -274,6 +274,7 @@ export function createApprovalRequest(command, cwd, classification, config, chan
|
|
|
274
274
|
expiresAt: new Date(now.getTime() + ttlMs),
|
|
275
275
|
status: 'pending',
|
|
276
276
|
channelMeta,
|
|
277
|
+
history: [],
|
|
277
278
|
};
|
|
278
279
|
approvals.set(approval.id, approval);
|
|
279
280
|
emitEvent('created', approval);
|
|
@@ -314,9 +315,11 @@ export function approveRequest(id, approvedBy) {
|
|
|
314
315
|
const approval = approvals.get(id);
|
|
315
316
|
if (!approval || approval.status !== 'pending')
|
|
316
317
|
return false;
|
|
318
|
+
const now = new Date();
|
|
319
|
+
approval.history.push({ from: approval.status, to: 'approved', at: now, by: approvedBy });
|
|
317
320
|
approval.status = 'approved';
|
|
318
321
|
approval.approvedBy = approvedBy;
|
|
319
|
-
approval.resolvedAt =
|
|
322
|
+
approval.resolvedAt = now;
|
|
320
323
|
emitEvent('approved', approval);
|
|
321
324
|
return true;
|
|
322
325
|
}
|
|
@@ -328,9 +331,11 @@ export function denyRequest(id, deniedBy) {
|
|
|
328
331
|
const approval = approvals.get(id);
|
|
329
332
|
if (!approval || approval.status !== 'pending')
|
|
330
333
|
return false;
|
|
334
|
+
const now = new Date();
|
|
335
|
+
approval.history.push({ from: approval.status, to: 'denied', at: now, by: deniedBy });
|
|
331
336
|
approval.status = 'denied';
|
|
332
337
|
approval.deniedBy = deniedBy;
|
|
333
|
-
approval.resolvedAt =
|
|
338
|
+
approval.resolvedAt = now;
|
|
334
339
|
emitEvent('denied', approval);
|
|
335
340
|
return true;
|
|
336
341
|
}
|
|
@@ -351,9 +356,14 @@ export function findApprovedRequest(command, cwd) {
|
|
|
351
356
|
}
|
|
352
357
|
/**
|
|
353
358
|
* Mark an approved request as consumed (after successful execution).
|
|
359
|
+
* Preserves the approval in the registry with status 'consumed' for history.
|
|
354
360
|
*/
|
|
355
361
|
export function consumeApproval(id) {
|
|
356
|
-
approvals.
|
|
362
|
+
const approval = approvals.get(id);
|
|
363
|
+
if (!approval)
|
|
364
|
+
return;
|
|
365
|
+
approval.history.push({ from: approval.status, to: 'consumed', at: new Date() });
|
|
366
|
+
approval.status = 'consumed';
|
|
357
367
|
}
|
|
358
368
|
/**
|
|
359
369
|
* Expire pending approvals past their TTL.
|
|
@@ -362,8 +372,10 @@ export function cleanupExpired() {
|
|
|
362
372
|
const now = Date.now();
|
|
363
373
|
for (const [id, approval] of approvals) {
|
|
364
374
|
if (approval.status === 'pending' && approval.expiresAt.getTime() <= now) {
|
|
375
|
+
const resolvedAt = new Date(now);
|
|
376
|
+
approval.history.push({ from: 'pending', to: 'expired', at: resolvedAt });
|
|
365
377
|
approval.status = 'expired';
|
|
366
|
-
approval.resolvedAt =
|
|
378
|
+
approval.resolvedAt = resolvedAt;
|
|
367
379
|
emitEvent('expired', approval);
|
|
368
380
|
}
|
|
369
381
|
}
|
|
@@ -413,6 +425,7 @@ export function waitForApproval(id, timeoutMs) {
|
|
|
413
425
|
createdAt: new Date(),
|
|
414
426
|
expiresAt: new Date(),
|
|
415
427
|
status: 'expired',
|
|
428
|
+
history: [],
|
|
416
429
|
});
|
|
417
430
|
}
|
|
418
431
|
}, timeoutMs);
|
package/dist/gateway.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { FastifyInstance } from 'fastify';
|
|
2
|
-
import type { Config } from './types.js';
|
|
2
|
+
import type { Config, ThinkingLevel } from './types.js';
|
|
3
3
|
export declare function setGatewayConfig(cfg: Config): void;
|
|
4
4
|
export declare function createGateway(cfg: Config): Promise<FastifyInstance>;
|
|
5
5
|
export declare function getCurrentModel(): string;
|
|
6
6
|
export declare function setCurrentModel(model: string): void;
|
|
7
|
+
export declare function getCurrentThinking(): ThinkingLevel | undefined;
|
|
8
|
+
export declare function setCurrentThinking(thinking: ThinkingLevel | undefined): void;
|
|
7
9
|
export declare function getLastMessage(): Date | undefined;
|
|
8
10
|
export declare function setLastMessage(date: Date): void;
|
package/dist/gateway.js
CHANGED
|
@@ -21,13 +21,16 @@ let config;
|
|
|
21
21
|
let startTime;
|
|
22
22
|
let lastMessage;
|
|
23
23
|
let currentModel;
|
|
24
|
+
let currentThinking;
|
|
24
25
|
export function setGatewayConfig(cfg) {
|
|
25
26
|
config = cfg;
|
|
26
27
|
}
|
|
27
28
|
export async function createGateway(cfg) {
|
|
28
29
|
config = cfg;
|
|
29
30
|
startTime = new Date();
|
|
30
|
-
|
|
31
|
+
const defaultAgent = cfg.agents.list[cfg.agents.default];
|
|
32
|
+
currentModel = defaultAgent?.model || 'claude-sonnet-4-5';
|
|
33
|
+
currentThinking = defaultAgent?.thinking;
|
|
31
34
|
const fastify = Fastify({
|
|
32
35
|
logger: {
|
|
33
36
|
level: 'info',
|
|
@@ -54,6 +57,7 @@ export async function createGateway(cfg) {
|
|
|
54
57
|
uptime: Date.now() - startTime.getTime(),
|
|
55
58
|
agent: config.agents.default,
|
|
56
59
|
model: currentModel,
|
|
60
|
+
thinking: currentThinking,
|
|
57
61
|
lastMessage,
|
|
58
62
|
cronJobs: jobs.map(j => ({
|
|
59
63
|
id: j.id,
|
|
@@ -71,7 +75,7 @@ export async function createGateway(cfg) {
|
|
|
71
75
|
try {
|
|
72
76
|
const response = await runAgentTurn(config.agents.default, message, config, model || currentModel, undefined, undefined, {
|
|
73
77
|
channel: 'gateway',
|
|
74
|
-
metadata: { ip: request.ip },
|
|
78
|
+
metadata: { ip: request.ip, thinkingOverride: currentThinking },
|
|
75
79
|
});
|
|
76
80
|
lastMessage = new Date();
|
|
77
81
|
return { response };
|
|
@@ -109,15 +113,15 @@ export async function createGateway(cfg) {
|
|
|
109
113
|
// Ensure dashboard token exists
|
|
110
114
|
const dashboardToken = ensureDashboardToken(config);
|
|
111
115
|
console.log(`[dashboard] URL: http://localhost:${config.gateway.port}/dashboard`);
|
|
112
|
-
// Auth guard for gateway
|
|
116
|
+
// Auth guard for sensitive gateway endpoints (same token as dashboard).
|
|
113
117
|
const PROTECTED_ROUTES = new Set(['/message', '/model', '/reload']);
|
|
114
118
|
fastify.addHook('onRequest', async (request, reply) => {
|
|
115
119
|
const url = request.url;
|
|
116
|
-
|
|
117
|
-
|
|
120
|
+
const isProtected = PROTECTED_ROUTES.has(url) ||
|
|
121
|
+
url.startsWith('/cron/') ||
|
|
122
|
+
url === '/status';
|
|
123
|
+
if (!isProtected)
|
|
118
124
|
return;
|
|
119
|
-
if (request.method === 'GET')
|
|
120
|
-
return; // GET /health, GET /status are fine
|
|
121
125
|
if (!dashboardToken)
|
|
122
126
|
return; // No token configured, allow access
|
|
123
127
|
if (!validateBearerToken(dashboardToken, request.headers.authorization)) {
|
|
@@ -140,6 +144,12 @@ export function getCurrentModel() {
|
|
|
140
144
|
export function setCurrentModel(model) {
|
|
141
145
|
currentModel = model;
|
|
142
146
|
}
|
|
147
|
+
export function getCurrentThinking() {
|
|
148
|
+
return currentThinking;
|
|
149
|
+
}
|
|
150
|
+
export function setCurrentThinking(thinking) {
|
|
151
|
+
currentThinking = thinking;
|
|
152
|
+
}
|
|
143
153
|
export function getLastMessage() {
|
|
144
154
|
return lastMessage;
|
|
145
155
|
}
|
package/dist/heartbeat.js
CHANGED
|
@@ -7,7 +7,6 @@ import { join } from 'path';
|
|
|
7
7
|
import { homedir } from 'os';
|
|
8
8
|
import { runAgentTurn } from './agent.js';
|
|
9
9
|
import { resolveAllowedPaths } from './config.js';
|
|
10
|
-
import { pruneIdle, SANDBOX_DEFAULTS } from './sandbox/index.js';
|
|
11
10
|
import { getActiveChannelId, isActiveChannelSilenced, sendActiveChannelProactiveMessage, } from './channels.js';
|
|
12
11
|
let heartbeatTimer = null;
|
|
13
12
|
let running = false;
|
|
@@ -15,9 +14,7 @@ function getHeartbeatTools(config) {
|
|
|
15
14
|
if (config.heartbeat.tools) {
|
|
16
15
|
return {
|
|
17
16
|
...config.heartbeat.tools,
|
|
18
|
-
allowedPaths: config.heartbeat.tools.allowedPaths
|
|
19
|
-
? config.heartbeat.tools.allowedPaths
|
|
20
|
-
: resolveAllowedPaths(config),
|
|
17
|
+
allowedPaths: resolveAllowedPaths(config, config.heartbeat.tools.allowedPaths),
|
|
21
18
|
};
|
|
22
19
|
}
|
|
23
20
|
return {
|
|
@@ -75,8 +72,6 @@ export function stopHeartbeat() {
|
|
|
75
72
|
}
|
|
76
73
|
}
|
|
77
74
|
export async function runHeartbeatCheck(config) {
|
|
78
|
-
// Prune idle sandbox containers
|
|
79
|
-
pruneIdle(config.sandbox?.idleTimeoutMs ?? SANDBOX_DEFAULTS.idleTimeoutMs ?? 3_600_000).catch(() => { });
|
|
80
75
|
if (running) {
|
|
81
76
|
console.log('[heartbeat] Skipping — previous check still running');
|
|
82
77
|
return 'Skipped — previous check still running';
|
package/dist/langfuse.js
CHANGED
|
@@ -13,28 +13,14 @@ export const MODEL_PRICING = {
|
|
|
13
13
|
'claude-haiku-4-5': { inputPerMTok: 0.25, outputPerMTok: 1.25 },
|
|
14
14
|
'claude-haiku-4': { inputPerMTok: 1.0, outputPerMTok: 5.0 },
|
|
15
15
|
'claude-opus-4': { inputPerMTok: 15.0, outputPerMTok: 75.0 },
|
|
16
|
-
// OpenAI models (https://developers.openai.com/api/docs/pricing)
|
|
17
|
-
'gpt-4o': { inputPerMTok: 2.5, outputPerMTok: 10.0 },
|
|
18
|
-
'gpt-4o-mini': { inputPerMTok: 0.15, outputPerMTok: 0.6 },
|
|
19
|
-
'gpt-4.1': { inputPerMTok: 2.0, outputPerMTok: 8.0 },
|
|
20
|
-
'gpt-4.1-mini': { inputPerMTok: 0.4, outputPerMTok: 1.6 },
|
|
21
|
-
'gpt-4.1-nano': { inputPerMTok: 0.1, outputPerMTok: 0.4 },
|
|
22
|
-
'gpt-4-turbo': { inputPerMTok: 10.0, outputPerMTok: 30.0 },
|
|
23
|
-
'gpt-3.5-turbo': { inputPerMTok: 0.5, outputPerMTok: 1.5 },
|
|
24
16
|
// Codex pricing
|
|
25
17
|
'gpt-5.1-codex': { inputPerMTok: 1.25, outputPerMTok: 10.0 },
|
|
26
18
|
'gpt-5.2-codex': { inputPerMTok: 1.75, outputPerMTok: 14.0 },
|
|
27
19
|
'gpt-5.3-codex': { inputPerMTok: 1.75, outputPerMTok: 14.0 },
|
|
20
|
+
'gpt-5.5': { inputPerMTok: 5.0, outputPerMTok: 30.0 },
|
|
28
21
|
'codex-5.1': { inputPerMTok: 1.25, outputPerMTok: 10.0 },
|
|
29
22
|
'codex-5.2': { inputPerMTok: 1.75, outputPerMTok: 14.0 },
|
|
30
23
|
'codex-5.3': { inputPerMTok: 1.75, outputPerMTok: 14.0 },
|
|
31
|
-
// MiniMax models (https://platform.minimax.io/docs/pricing/pay-as-you-go)
|
|
32
|
-
'minimax-m2.1': { inputPerMTok: 0.3, outputPerMTok: 1.2 },
|
|
33
|
-
'minimax-m2.5': { inputPerMTok: 0.3, outputPerMTok: 1.2 },
|
|
34
|
-
// Kimi/Moonshot models (https://platform.moonshot.ai/docs/pricing/chat.en-US)
|
|
35
|
-
// kimi-k2.5 series - using cache miss pricing for input (non-cached)
|
|
36
|
-
'kimi-k2.5': { inputPerMTok: 0.6, outputPerMTok: 3.0 },
|
|
37
|
-
'kimi-for-coding': { inputPerMTok: 0.6, outputPerMTok: 3.0 }
|
|
38
24
|
};
|
|
39
25
|
/** Map of common aliases/shorthand to canonical model IDs in MODEL_PRICING */
|
|
40
26
|
const MODEL_ALIAS_MAP = {
|
|
@@ -47,24 +33,12 @@ const MODEL_ALIAS_MAP = {
|
|
|
47
33
|
'claude-opus': 'claude-opus-4',
|
|
48
34
|
'claude-3.5-sonnet': 'claude-3-5-sonnet',
|
|
49
35
|
'claude-3-opus': 'claude-3-opus',
|
|
50
|
-
// OpenAI aliases
|
|
51
|
-
gpt4o: 'gpt-4o',
|
|
52
|
-
'gpt4o-mini': 'gpt-4o-mini',
|
|
53
|
-
'gpt4.1': 'gpt-4.1',
|
|
54
|
-
'gpt4.1-mini': 'gpt-4.1-mini',
|
|
55
|
-
'gpt4.1-nano': 'gpt-4.1-nano',
|
|
56
|
-
'gpt4-turbo': 'gpt-4-turbo',
|
|
57
|
-
'gpt35-turbo': 'gpt-3.5-turbo',
|
|
58
|
-
'gpt-3.5': 'gpt-3.5-turbo',
|
|
59
36
|
'gpt-codex': 'gpt-5.3-codex',
|
|
60
37
|
'codex5.1': 'gpt-5.1-codex',
|
|
61
38
|
'codex5.2': 'gpt-5.2-codex',
|
|
62
39
|
'codex5.3': 'gpt-5.3-codex',
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
minimax: 'minimax-m2.5',
|
|
66
|
-
// Kimi/Moonshot aliases
|
|
67
|
-
kimi: 'kimi-k2.5'
|
|
40
|
+
'codex5.5': 'gpt-5.5',
|
|
41
|
+
codex: 'gpt-5.5',
|
|
68
42
|
};
|
|
69
43
|
/**
|
|
70
44
|
* Calculate the USD cost of a model invocation based on token usage.
|
package/dist/model-selection.js
CHANGED
|
@@ -34,7 +34,9 @@ export function resolveModelSelection(input, config) {
|
|
|
34
34
|
if (FULL_MODEL_SPEC_RE.test(value)) {
|
|
35
35
|
return { ok: true, resolved: resolveModel(value, config) };
|
|
36
36
|
}
|
|
37
|
-
if (
|
|
37
|
+
// Accept bare model IDs only if they contain a version-like pattern (digits after a hyphen/dot)
|
|
38
|
+
// e.g. "claude-sonnet-4-5", "gpt-5.3-codex" — but NOT "claude-opuis"
|
|
39
|
+
if (BARE_MODEL_ID_RE.test(value) && /[-.]/.test(value) && /\d/.test(value)) {
|
|
38
40
|
return { ok: true, resolved: resolveModel(value, config) };
|
|
39
41
|
}
|
|
40
42
|
if (!SAFE_MODEL_INPUT_RE.test(value) || value.includes('/')) {
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider adapter interface for the unified tool loop.
|
|
3
|
+
* Each provider implements this to handle API-specific concerns
|
|
4
|
+
* while sharing the same agentic loop orchestration logic.
|
|
5
|
+
*/
|
|
6
|
+
import type { ChatMessage, ChatOptions, Config } from '../types.js';
|
|
7
|
+
import type { ContextManagementConfig } from './context-manager.js';
|
|
8
|
+
import type { ExecuteToolContext } from '../tools.js';
|
|
9
|
+
/** Normalized representation of a model response within the tool loop. */
|
|
10
|
+
export interface NormalizedResponse {
|
|
11
|
+
/** Whether the model wants to call tools (vs. returning a final answer) */
|
|
12
|
+
hasToolCalls: boolean;
|
|
13
|
+
/** Tool calls extracted from the response */
|
|
14
|
+
toolCalls: NormalizedToolCall[];
|
|
15
|
+
/** Text content from the response (final answer when hasToolCalls=false) */
|
|
16
|
+
textContent: string;
|
|
17
|
+
/** Raw usage data from the provider */
|
|
18
|
+
usage?: {
|
|
19
|
+
inputTokens: number;
|
|
20
|
+
outputTokens: number;
|
|
21
|
+
cacheReadTokens?: number;
|
|
22
|
+
cacheCreationTokens?: number;
|
|
23
|
+
};
|
|
24
|
+
/** Cost details for this response */
|
|
25
|
+
cost?: {
|
|
26
|
+
input: number;
|
|
27
|
+
output: number;
|
|
28
|
+
total: number;
|
|
29
|
+
};
|
|
30
|
+
/** Raw response object (provider-specific, for appending to message history) */
|
|
31
|
+
rawResponse: unknown;
|
|
32
|
+
}
|
|
33
|
+
export interface NormalizedToolCall {
|
|
34
|
+
/** Unique ID for this tool call (tool_use_id, call_id, toolCall.id) */
|
|
35
|
+
id: string;
|
|
36
|
+
/** Tool name */
|
|
37
|
+
name: string;
|
|
38
|
+
/** Parsed arguments */
|
|
39
|
+
args: Record<string, any>;
|
|
40
|
+
/** Raw arguments string (for logging) */
|
|
41
|
+
rawArgs: string;
|
|
42
|
+
}
|
|
43
|
+
export interface CompactionResult<T> {
|
|
44
|
+
/** Updated messages array */
|
|
45
|
+
messages: T[];
|
|
46
|
+
/** Whether compaction actually occurred */
|
|
47
|
+
compacted: boolean;
|
|
48
|
+
/** Compaction method used (if any) */
|
|
49
|
+
method?: 'llm' | 'truncation';
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Provider-specific message container.
|
|
53
|
+
* Each adapter defines its own internal message format.
|
|
54
|
+
*/
|
|
55
|
+
export interface ProviderMessages {
|
|
56
|
+
/** The mutable message array (format depends on provider) */
|
|
57
|
+
messages: any[];
|
|
58
|
+
/** System prompt / instructions (extracted once, reused per call) */
|
|
59
|
+
systemParam?: any;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Message format helper for generic compaction.
|
|
63
|
+
* Each provider implements this to teach the compaction engine
|
|
64
|
+
* how to inspect/truncate/serialize its message format.
|
|
65
|
+
*/
|
|
66
|
+
export interface MessageFormatHelper<T> {
|
|
67
|
+
/** Check if an item is a tool result (to be truncated during fallback compaction). */
|
|
68
|
+
isToolResult(item: T): boolean;
|
|
69
|
+
/** Truncate a tool result item's content to maxChars. Returns a new item (no mutation). */
|
|
70
|
+
truncateToolResult(item: T, maxChars: number): T;
|
|
71
|
+
/** Serialize a list of items into a human-readable transcript for LLM summarization. */
|
|
72
|
+
serialize(items: T[]): string;
|
|
73
|
+
/** Build a summary message that replaces compacted head messages. */
|
|
74
|
+
buildSummaryMessage(summary: string): T;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Adapter interface that each provider implements.
|
|
78
|
+
* Separates provider-specific API details from shared tool loop logic.
|
|
79
|
+
*/
|
|
80
|
+
export interface ProviderAdapter {
|
|
81
|
+
readonly name: string;
|
|
82
|
+
/** Check whether this provider is initialized and ready to handle requests. */
|
|
83
|
+
isAvailable(): boolean;
|
|
84
|
+
/** Simple chat (no tool loop). Returns the model's text response. */
|
|
85
|
+
chat(messages: ChatMessage[], options: ChatOptions, config: Config): Promise<string>;
|
|
86
|
+
/** Provider-specific tool discovery options (e.g. disable MCP for non-Anthropic providers). */
|
|
87
|
+
getToolDefinitionOptions?(toolContext?: ExecuteToolContext, config?: Config): {
|
|
88
|
+
includeMcp?: boolean;
|
|
89
|
+
};
|
|
90
|
+
/** Build the initial API messages from ChatMessage[] (strip system, format content) */
|
|
91
|
+
buildMessages(messages: ChatMessage[], options: ChatOptions, config: Config): ProviderMessages;
|
|
92
|
+
/** Build tool definitions in provider-native format */
|
|
93
|
+
buildToolDefs(toolDefs: any[], config: Config): any[];
|
|
94
|
+
/** Make one API call with tools. Returns normalized response. */
|
|
95
|
+
call(messages: ProviderMessages, toolDefs: any[], options: ChatOptions, config: Config): Promise<NormalizedResponse>;
|
|
96
|
+
/** Append the assistant's raw response to the message history */
|
|
97
|
+
appendAssistantResponse(messages: ProviderMessages, rawResponse: unknown): void;
|
|
98
|
+
/** Append a single tool result to the message history.
|
|
99
|
+
* For providers that batch tool results (e.g. Anthropic), use appendToolResults instead. */
|
|
100
|
+
appendToolResult(messages: ProviderMessages, toolCallId: string, result: string, isError?: boolean): void;
|
|
101
|
+
/** Append multiple tool results as a single message (for providers that batch).
|
|
102
|
+
* Default implementation calls appendToolResult for each. */
|
|
103
|
+
appendToolResults?(messages: ProviderMessages, results: {
|
|
104
|
+
toolCallId: string;
|
|
105
|
+
result: string;
|
|
106
|
+
isError?: boolean;
|
|
107
|
+
}[]): void;
|
|
108
|
+
/**
|
|
109
|
+
* Optional hook called when the model's final response has no text but tool calls were made.
|
|
110
|
+
* Allows providers (e.g. Codex) to make an additional API call to elicit a text summary.
|
|
111
|
+
* Returns the finalized text, or undefined to use the default fallback.
|
|
112
|
+
*/
|
|
113
|
+
onEmptyFinalResponse?(providerMessages: ProviderMessages, toolDefs: any[], options: ChatOptions, config: Config): Promise<string | undefined>;
|
|
114
|
+
/** Compact messages when context grows too large */
|
|
115
|
+
compactMessages(messages: ProviderMessages, config: ContextManagementConfig | undefined, iteration: number, fullConfig?: Config): Promise<CompactionResult<any>>;
|
|
116
|
+
/** Record usage/cost to the usage tracking system */
|
|
117
|
+
recordUsage(model: string, usage: unknown, trigger?: string, agentId?: string): void;
|
|
118
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anthropic provider adapter for the unified tool loop.
|
|
3
|
+
*/
|
|
4
|
+
import type { ChatMessage, ChatOptions, Config } from '../../types.js';
|
|
5
|
+
import type { ProviderAdapter, ProviderMessages, NormalizedResponse, CompactionResult } from '../adapter.js';
|
|
6
|
+
export declare class AnthropicAdapter implements ProviderAdapter {
|
|
7
|
+
readonly name = "anthropic";
|
|
8
|
+
isAvailable(): boolean;
|
|
9
|
+
chat(messages: ChatMessage[], options: ChatOptions, config: Config): Promise<string>;
|
|
10
|
+
buildMessages(messages: ChatMessage[], options: ChatOptions, config: Config): ProviderMessages;
|
|
11
|
+
buildToolDefs(toolDefs: any[], config: Config): any[];
|
|
12
|
+
call(providerMessages: ProviderMessages, toolDefs: any[], options: ChatOptions, config: Config): Promise<NormalizedResponse>;
|
|
13
|
+
appendAssistantResponse(providerMessages: ProviderMessages, rawResponse: unknown): void;
|
|
14
|
+
appendToolResult(providerMessages: ProviderMessages, toolCallId: string, result: string, isError?: boolean): void;
|
|
15
|
+
appendToolResults(providerMessages: ProviderMessages, results: {
|
|
16
|
+
toolCallId: string;
|
|
17
|
+
result: string;
|
|
18
|
+
isError?: boolean;
|
|
19
|
+
}[]): void;
|
|
20
|
+
compactMessages(providerMessages: ProviderMessages, config: any, iteration: number, fullConfig?: Config): Promise<CompactionResult<any>>;
|
|
21
|
+
recordUsage(model: string, usage: unknown, trigger?: string, agentId?: string): void;
|
|
22
|
+
}
|