@tyvm/knowhow 0.0.142 → 0.0.144
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 +1 -1
- package/src/agents/developer/developer.ts +4 -1
- package/src/agents/patcher/patcher.ts +1 -1
- package/src/agents/tools/connectAgent.ts +2 -0
- package/src/agents/tools/observe.ts +2 -0
- package/src/agents/tools/replyToParent.ts +1 -0
- package/src/agents/tools/runGenerate.ts +1 -0
- package/src/agents/tools/sendAgentMessage.ts +1 -0
- package/src/agents/tools/startAgentTask.ts +1 -0
- package/src/agents/tools/waitForAgentCompleted.ts +1 -0
- package/src/chat/modules/SystemModule.ts +4 -3
- package/src/clients/pricing/fireworks.ts +73 -25
- package/src/clients/types.ts +8 -0
- package/src/commands/sandbox.ts +139 -114
- package/src/services/Mcp.ts +3 -0
- package/src/services/Tools.ts +12 -2
- package/src/types.ts +2 -0
- package/tests/services/Tools.test.ts +13 -11
- package/tests/unit/commands/sandbox.test.ts +350 -0
- package/ts_build/package.json +1 -1
- package/ts_build/src/agents/developer/developer.js +4 -0
- package/ts_build/src/agents/developer/developer.js.map +1 -1
- package/ts_build/src/agents/patcher/patcher.js +1 -1
- package/ts_build/src/agents/patcher/patcher.js.map +1 -1
- package/ts_build/src/agents/tools/connectAgent.js +2 -0
- package/ts_build/src/agents/tools/connectAgent.js.map +1 -1
- package/ts_build/src/agents/tools/observe.js +2 -0
- package/ts_build/src/agents/tools/observe.js.map +1 -1
- package/ts_build/src/agents/tools/replyToParent.js +1 -0
- package/ts_build/src/agents/tools/replyToParent.js.map +1 -1
- package/ts_build/src/agents/tools/runGenerate.js +1 -0
- package/ts_build/src/agents/tools/runGenerate.js.map +1 -1
- package/ts_build/src/agents/tools/sendAgentMessage.js +1 -0
- package/ts_build/src/agents/tools/sendAgentMessage.js.map +1 -1
- package/ts_build/src/agents/tools/startAgentTask.js +1 -0
- package/ts_build/src/agents/tools/startAgentTask.js.map +1 -1
- package/ts_build/src/agents/tools/waitForAgentCompleted.js +1 -0
- package/ts_build/src/agents/tools/waitForAgentCompleted.js.map +1 -1
- package/ts_build/src/chat/modules/SystemModule.js +4 -3
- package/ts_build/src/chat/modules/SystemModule.js.map +1 -1
- package/ts_build/src/clients/pricing/fireworks.d.ts +26 -0
- package/ts_build/src/clients/pricing/fireworks.js +51 -25
- package/ts_build/src/clients/pricing/fireworks.js.map +1 -1
- package/ts_build/src/clients/types.d.ts +1 -0
- package/ts_build/src/commands/sandbox.js +79 -80
- package/ts_build/src/commands/sandbox.js.map +1 -1
- package/ts_build/src/services/Mcp.js.map +1 -1
- package/ts_build/src/services/Tools.js +7 -2
- package/ts_build/src/services/Tools.js.map +1 -1
- package/ts_build/src/types.d.ts +26 -0
- package/ts_build/src/types.js +2 -0
- package/ts_build/src/types.js.map +1 -1
- package/ts_build/tests/services/Tools.test.js +6 -4
- package/ts_build/tests/services/Tools.test.js.map +1 -1
- package/ts_build/tests/unit/commands/sandbox.test.d.ts +1 -0
- package/ts_build/tests/unit/commands/sandbox.test.js +258 -0
- package/ts_build/tests/unit/commands/sandbox.test.js.map +1 -0
package/package.json
CHANGED
|
@@ -13,6 +13,10 @@ export class DeveloperAgent extends BaseAgent {
|
|
|
13
13
|
model: Models.anthropic.Sonnet4_6,
|
|
14
14
|
provider: "anthropic",
|
|
15
15
|
},
|
|
16
|
+
{
|
|
17
|
+
model: Models.openai.GPT_56_Sol,
|
|
18
|
+
provider: "openai",
|
|
19
|
+
},
|
|
16
20
|
]);
|
|
17
21
|
}
|
|
18
22
|
|
|
@@ -70,4 +74,3 @@ export class DeveloperAgent extends BaseAgent {
|
|
|
70
74
|
] as Message[];
|
|
71
75
|
}
|
|
72
76
|
}
|
|
73
|
-
|
|
@@ -81,7 +81,7 @@ export class PatchingAgent extends BaseAgent {
|
|
|
81
81
|
this.setModelPreferences([
|
|
82
82
|
{ model: Models.anthropic.Sonnet4_6, provider: "anthropic" },
|
|
83
83
|
{ model: Models.openai.GPT_56_Sol, provider: "openai" },
|
|
84
|
-
{ model: Models.xai.
|
|
84
|
+
{ model: Models.xai.Grok4_5, provider: "xai" },
|
|
85
85
|
]);
|
|
86
86
|
}
|
|
87
87
|
|
|
@@ -288,6 +288,7 @@ export const connectAgentDefinition: Tool = {
|
|
|
288
288
|
parameters: {
|
|
289
289
|
type: "object",
|
|
290
290
|
positional: false,
|
|
291
|
+
usesContext: true,
|
|
291
292
|
properties: {
|
|
292
293
|
connections: {
|
|
293
294
|
type: "array",
|
|
@@ -340,6 +341,7 @@ export const stopConnectionsDefinition: Tool = {
|
|
|
340
341
|
parameters: {
|
|
341
342
|
type: "object",
|
|
342
343
|
positional: false,
|
|
344
|
+
usesContext: true,
|
|
343
345
|
properties: {
|
|
344
346
|
id: {
|
|
345
347
|
type: "string",
|
|
@@ -182,6 +182,7 @@ export const observeDefinition: Tool = {
|
|
|
182
182
|
parameters: {
|
|
183
183
|
type: "object",
|
|
184
184
|
positional: false,
|
|
185
|
+
usesContext: true,
|
|
185
186
|
properties: {
|
|
186
187
|
toolName: {
|
|
187
188
|
type: "string",
|
|
@@ -232,6 +233,7 @@ export const stopObservingDefinition: Tool = {
|
|
|
232
233
|
parameters: {
|
|
233
234
|
type: "object",
|
|
234
235
|
positional: false,
|
|
236
|
+
usesContext: true,
|
|
235
237
|
properties: {
|
|
236
238
|
id: {
|
|
237
239
|
type: "string",
|
|
@@ -142,6 +142,7 @@ export const runGenerateDefinition: Tool = {
|
|
|
142
142
|
"When `sources` is omitted, it runs the sources defined in .knowhow/knowhow.json (optionally scoped by name/filter).",
|
|
143
143
|
parameters: {
|
|
144
144
|
type: "object",
|
|
145
|
+
usesContext: true,
|
|
145
146
|
properties: {
|
|
146
147
|
sources: {
|
|
147
148
|
type: "array",
|
|
@@ -102,11 +102,12 @@ export class SystemModule extends BaseChatModule {
|
|
|
102
102
|
|
|
103
103
|
// Get default model for new provider
|
|
104
104
|
const ChatModelDefaults = {
|
|
105
|
-
openai: Models.openai.
|
|
105
|
+
openai: Models.openai.GPT_56_Sol,
|
|
106
106
|
anthropic: Models.anthropic.Sonnet4_6,
|
|
107
|
-
google: Models.google.
|
|
108
|
-
xai: Models.xai.
|
|
107
|
+
google: Models.google.Gemini_35_Flash_Lite,
|
|
108
|
+
xai: Models.xai.Grok4_5,
|
|
109
109
|
knowhow: "anthropic/" + Models.anthropic.Sonnet4_6,
|
|
110
|
+
fireworks: Models.fireworks.KimiK3,
|
|
110
111
|
};
|
|
111
112
|
|
|
112
113
|
const newModel =
|
|
@@ -1,45 +1,93 @@
|
|
|
1
1
|
import { ModelPricing } from "./types";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Fireworks AI
|
|
4
|
+
* Fireworks AI model IDs, pricing, and catalog.
|
|
5
|
+
* Single source of truth for all Fireworks models.
|
|
5
6
|
* Source: https://fireworks.ai/pricing
|
|
6
7
|
*/
|
|
8
|
+
|
|
9
|
+
// ─── Model IDs ────────────────────────────────────────────────────────────────
|
|
10
|
+
|
|
11
|
+
export const FireworksModels = {
|
|
12
|
+
// Moonshot AI
|
|
13
|
+
KimiK3: "accounts/fireworks/models/kimi-k3",
|
|
14
|
+
KimiK2p7Code: "accounts/fireworks/models/kimi-k2p7-code",
|
|
15
|
+
KimiK2_6: "accounts/fireworks/models/kimi-k2-6",
|
|
16
|
+
KimiK2_5: "accounts/fireworks/models/kimi-k2-5",
|
|
17
|
+
|
|
18
|
+
// MiniMax
|
|
19
|
+
MinimaxM3: "accounts/fireworks/models/minimax-m3",
|
|
20
|
+
MinimaxM2_7: "accounts/fireworks/models/minimax-m2-7",
|
|
21
|
+
MinimaxM2_5: "accounts/fireworks/models/minimax-m2-5",
|
|
22
|
+
|
|
23
|
+
// Qwen
|
|
24
|
+
Qwen3p7Plus: "accounts/fireworks/models/qwen3p7-plus",
|
|
25
|
+
|
|
26
|
+
// Deprecated Qwen Models
|
|
27
|
+
Qwen3_6Plus: "accounts/fireworks/models/qwen3-6-plus",
|
|
28
|
+
Qwen3p6Plus: "accounts/fireworks/models/qwen3p6-plus",
|
|
29
|
+
Qwen3Vl30bA3bThinking: "accounts/fireworks/models/qwen3-vl-30b-a3b-thinking",
|
|
30
|
+
Qwen3Vl30bA3bInstruct: "accounts/fireworks/models/qwen3-vl-30b-a3b-instruct",
|
|
31
|
+
Qwen3_8b: "accounts/fireworks/models/qwen3-8b",
|
|
32
|
+
|
|
33
|
+
// Z.ai
|
|
34
|
+
Glm5p2: "accounts/fireworks/models/glm-5p2",
|
|
35
|
+
Glm5p1: "accounts/fireworks/models/glm-5p1",
|
|
36
|
+
Glm5: "accounts/fireworks/models/glm-5",
|
|
37
|
+
Glm4_7: "accounts/fireworks/models/glm-4-7",
|
|
38
|
+
|
|
39
|
+
// DeepSeek AI
|
|
40
|
+
DeepseekV4Pro: "accounts/fireworks/models/deepseek-v4-pro",
|
|
41
|
+
DeepseekV4Flash: "accounts/fireworks/models/deepseek-v4-flash",
|
|
42
|
+
DeepseekV3_2: "accounts/fireworks/models/deepseek-v3-2",
|
|
43
|
+
DeepseekV3_1: "accounts/fireworks/models/deepseek-v3-1",
|
|
44
|
+
|
|
45
|
+
// OpenAI OSS
|
|
46
|
+
GptOss120b: "accounts/fireworks/models/gpt-oss-120b",
|
|
47
|
+
|
|
48
|
+
// Embedding / Reranker
|
|
49
|
+
Qwen3Embedding8b: "accounts/fireworks/models/qwen3-embedding-8b",
|
|
50
|
+
Qwen3Reranker8b: "accounts/fireworks/models/qwen3-reranker-8b",
|
|
51
|
+
} as const;
|
|
52
|
+
|
|
53
|
+
// ─── Text model pricing (USD per 1M tokens) ───────────────────────────────────
|
|
54
|
+
|
|
7
55
|
export const FireworksTextPricing: Record<string, ModelPricing> = {
|
|
8
56
|
// Moonshot AI
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
57
|
+
[FireworksModels.KimiK3]: { input: 3.0, cache_hit: 0.30, output: 15.0 },
|
|
58
|
+
[FireworksModels.KimiK2p7Code]: { input: 0.95, cache_hit: 0.19, output: 4.0 },
|
|
59
|
+
[FireworksModels.KimiK2_6]: { input: 0.95, cache_hit: 0.16, output: 4.0 },
|
|
60
|
+
[FireworksModels.KimiK2_5]: { input: 0.60, cache_hit: 0.10, output: 3.0 },
|
|
13
61
|
|
|
14
62
|
// MiniMax
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
63
|
+
[FireworksModels.MinimaxM3]: { input: 0.30, cache_hit: 0.06, output: 1.20 },
|
|
64
|
+
[FireworksModels.MinimaxM2_7]: { input: 0.30, cache_hit: 0.06, output: 1.20 },
|
|
65
|
+
[FireworksModels.MinimaxM2_5]: { input: 0.30, cache_hit: 0.03, output: 1.20 },
|
|
18
66
|
|
|
19
67
|
// Qwen
|
|
20
|
-
|
|
68
|
+
[FireworksModels.Qwen3p7Plus]: { input: 0.40, cache_hit: 0.08, output: 1.60 },
|
|
21
69
|
|
|
22
70
|
// Deprecated QWEN Models
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
71
|
+
[FireworksModels.Qwen3_6Plus]: { input: 0.50, cache_hit: 0.10, output: 3.0, deprecated: true },
|
|
72
|
+
[FireworksModels.Qwen3p6Plus]: { input: 0.50, cache_hit: 0.10, output: 3.0, deprecated: true },
|
|
73
|
+
[FireworksModels.Qwen3Vl30bA3bThinking]: { input: 0.15, cache_hit: 0.08, output: 0.60, deprecated: true },
|
|
74
|
+
[FireworksModels.Qwen3Vl30bA3bInstruct]: { input: 0.15, cache_hit: 0.08, output: 0.60, deprecated: true },
|
|
75
|
+
[FireworksModels.Qwen3_8b]: { input: 0.20, cache_hit: 0.10, output: 0.20, deprecated: true },
|
|
28
76
|
|
|
29
77
|
// Z.ai
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
78
|
+
[FireworksModels.Glm5p2]: { input: 1.40, cache_hit: 0.26, output: 4.40 },
|
|
79
|
+
[FireworksModels.Glm5p1]: { input: 1.40, cache_hit: 0.26, output: 4.40 },
|
|
80
|
+
[FireworksModels.Glm5]: { input: 1.00, cache_hit: 0.20, output: 3.20, deprecated: true },
|
|
81
|
+
[FireworksModels.Glm4_7]: { input: 0.60, cache_hit: 0.30, output: 2.20, deprecated: true },
|
|
34
82
|
|
|
35
83
|
// DeepSeek AI
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
84
|
+
[FireworksModels.DeepseekV4Pro]: { input: 1.74, cache_hit: 0.15, output: 3.48 },
|
|
85
|
+
[FireworksModels.DeepseekV4Flash]: { input: 0.14, cache_hit: 0.03, output: 0.28 },
|
|
86
|
+
[FireworksModels.DeepseekV3_2]: { input: 0.56, cache_hit: 0.28, output: 1.68, deprecated: true },
|
|
87
|
+
[FireworksModels.DeepseekV3_1]: { input: 0.56, cache_hit: 0.28, output: 1.68, deprecated: true },
|
|
40
88
|
|
|
41
89
|
// OpenAI OSS
|
|
42
|
-
|
|
90
|
+
[FireworksModels.GptOss120b]: { input: 0.15, cache_hit: 0.01, output: 0.60 },
|
|
43
91
|
};
|
|
44
92
|
|
|
45
93
|
/**
|
|
@@ -48,8 +96,8 @@ export const FireworksTextPricing: Record<string, ModelPricing> = {
|
|
|
48
96
|
*/
|
|
49
97
|
export const FireworksEmbeddingPricing: Record<string, ModelPricing> = {
|
|
50
98
|
// Qwen3 Embedding 8B — $0.10/M tokens, context 40k
|
|
51
|
-
|
|
99
|
+
[FireworksModels.Qwen3Embedding8b]: { input: 0.10, output: 0.10 },
|
|
52
100
|
|
|
53
101
|
// Qwen3 Reranker 8B — $0.20/M tokens, context 40k
|
|
54
|
-
|
|
102
|
+
[FireworksModels.Qwen3Reranker8b]: { input: 0.20, output: 0.20 },
|
|
55
103
|
};
|
package/src/clients/types.ts
CHANGED
|
@@ -40,6 +40,14 @@ export interface Tool {
|
|
|
40
40
|
parameters: {
|
|
41
41
|
type: string;
|
|
42
42
|
positional?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* When true, this tool is a local/in-memory (functional) tool that needs
|
|
45
|
+
* the per-call agent context. ToolsService will inject `_ctx` into the
|
|
46
|
+
* invocation (last positional arg for positional tools, or the `_ctx` key
|
|
47
|
+
* for non-positional tools). Tools that don't set this (e.g. MCP tools,
|
|
48
|
+
* which are serialized over the wire) never receive `_ctx`.
|
|
49
|
+
*/
|
|
50
|
+
usesContext?: boolean;
|
|
43
51
|
properties: {
|
|
44
52
|
[key: string]: ToolProp;
|
|
45
53
|
};
|
package/src/commands/sandbox.ts
CHANGED
|
@@ -60,6 +60,7 @@ interface SandboxSnapshot {
|
|
|
60
60
|
status: string;
|
|
61
61
|
errorMsg?: string | null;
|
|
62
62
|
statusMessage?: string | null;
|
|
63
|
+
regenerationSandboxId?: string | null;
|
|
63
64
|
snapshotContent?: string;
|
|
64
65
|
setupScript?: string | null;
|
|
65
66
|
}
|
|
@@ -122,6 +123,51 @@ async function resolveSnapshot(
|
|
|
122
123
|
|
|
123
124
|
// ─── Polling helpers ──────────────────────────────────────────────────────────
|
|
124
125
|
|
|
126
|
+
/**
|
|
127
|
+
* Poll a snapshot's status until regeneration is complete.
|
|
128
|
+
*
|
|
129
|
+
* The backend's POST /regenerate endpoint is fire-and-forget — it returns
|
|
130
|
+
* immediately with regenerationSandboxId set. We poll GET /snapshots/:id
|
|
131
|
+
* until one of:
|
|
132
|
+
* - regenerationSandboxId is null AND status === "ready" → success
|
|
133
|
+
* - status === "error" → failure
|
|
134
|
+
* - deadline exceeded → timeout
|
|
135
|
+
*
|
|
136
|
+
* statusMessage is printed whenever it changes so the user sees progress.
|
|
137
|
+
*/
|
|
138
|
+
async function pollRegeneration(
|
|
139
|
+
sandboxId: string,
|
|
140
|
+
snapshotId: string,
|
|
141
|
+
timeoutMs: number
|
|
142
|
+
): Promise<SandboxSnapshot> {
|
|
143
|
+
const deadline = Date.now() + timeoutMs;
|
|
144
|
+
let lastStatus = "";
|
|
145
|
+
let lastMessage = "";
|
|
146
|
+
while (Date.now() < deadline) {
|
|
147
|
+
const snap = await apiGet<SandboxSnapshot>(
|
|
148
|
+
`/sandboxes/${sandboxId}/snapshots/${snapshotId}`
|
|
149
|
+
);
|
|
150
|
+
if (snap.status !== lastStatus) {
|
|
151
|
+
console.log(` [snapshot] status: ${snap.status}`);
|
|
152
|
+
lastStatus = snap.status;
|
|
153
|
+
}
|
|
154
|
+
if (snap.statusMessage && snap.statusMessage !== lastMessage) {
|
|
155
|
+
console.log(` [snapshot] ${snap.statusMessage}`);
|
|
156
|
+
lastMessage = snap.statusMessage;
|
|
157
|
+
}
|
|
158
|
+
if (!snap.regenerationSandboxId && snap.status === "ready") return snap;
|
|
159
|
+
if (snap.status === "error") {
|
|
160
|
+
throw new Error(
|
|
161
|
+
`Snapshot regeneration failed: ${snap.errorMsg || snap.statusMessage || "(no details)"}`
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
await new Promise((r) => setTimeout(r, 5000));
|
|
165
|
+
}
|
|
166
|
+
throw new Error(
|
|
167
|
+
`Timeout waiting for snapshot regeneration to complete (${timeoutMs}ms). Last status: ${lastStatus}`
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
125
171
|
async function pollSandboxStatus(
|
|
126
172
|
sandboxId: string,
|
|
127
173
|
targetStatuses: string[],
|
|
@@ -373,7 +419,7 @@ export function addSandboxCommand(program: Command): void {
|
|
|
373
419
|
.option("--sandbox-name <name>", "Source sandbox name (alternative to --sandbox-id)")
|
|
374
420
|
.option("--snapshot-id <id>", "Snapshot ID to regenerate")
|
|
375
421
|
.option("--snapshot-name <name>", "Snapshot label to regenerate")
|
|
376
|
-
.option("--no-wait", "
|
|
422
|
+
.option("--no-wait", "Trigger regeneration and return immediately (do not poll for completion)")
|
|
377
423
|
.option("--timeout-minutes <min>", "Max time for the full regen cycle", "60")
|
|
378
424
|
.action(
|
|
379
425
|
async (opts: {
|
|
@@ -381,20 +427,18 @@ export function addSandboxCommand(program: Command): void {
|
|
|
381
427
|
sandboxName?: string;
|
|
382
428
|
snapshotId?: string;
|
|
383
429
|
snapshotName?: string;
|
|
384
|
-
wait: boolean;
|
|
430
|
+
wait: boolean;
|
|
385
431
|
timeoutMinutes: string;
|
|
386
432
|
}) => {
|
|
387
433
|
const timeoutMs = Number(opts.timeoutMinutes) * 60 * 1000;
|
|
388
|
-
const deadline = Date.now() + timeoutMs;
|
|
389
434
|
|
|
390
|
-
// ── 1. Resolve sandbox
|
|
435
|
+
// ── 1. Resolve sandbox & snapshot ──────────────────────────────────
|
|
391
436
|
if (!opts.sandboxId && !opts.sandboxName) {
|
|
392
437
|
throw new Error("Provide --sandbox-id or --sandbox-name");
|
|
393
438
|
}
|
|
394
439
|
const sandboxId =
|
|
395
440
|
opts.sandboxId || (await resolveSandboxId(opts.sandboxName!));
|
|
396
441
|
|
|
397
|
-
// ── 2. Resolve snapshot ─────────────────────────────────────────────
|
|
398
442
|
if (!opts.snapshotId && !opts.snapshotName) {
|
|
399
443
|
throw new Error("Provide --snapshot-id or --snapshot-name");
|
|
400
444
|
}
|
|
@@ -410,132 +454,113 @@ export function addSandboxCommand(program: Command): void {
|
|
|
410
454
|
);
|
|
411
455
|
console.log(` Source sandbox: ${sandboxId}`);
|
|
412
456
|
|
|
413
|
-
|
|
457
|
+
// ── 2. Validate preconditions ───────────────────────────────────────
|
|
458
|
+
if (snapshot.status !== "ready" && !snapshot.regenerationSandboxId) {
|
|
414
459
|
throw new Error(
|
|
415
460
|
`Snapshot must be in 'ready' status to regenerate (current: ${snapshot.status})`
|
|
416
461
|
);
|
|
417
462
|
}
|
|
418
|
-
if (!snapshot.setupScript) {
|
|
419
|
-
throw new Error(
|
|
420
|
-
"Snapshot has no setupScript. Add one via the UI before regenerating."
|
|
421
|
-
);
|
|
422
|
-
}
|
|
423
463
|
|
|
424
|
-
// ── 3.
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
464
|
+
// ── 3. Kick off background regeneration via backend API ─────────────
|
|
465
|
+
// The backend POST /regenerate endpoint returns immediately with the
|
|
466
|
+
// updated snapshot (regenerationSandboxId set). It runs the full
|
|
467
|
+
// cycle (fork ephemeral → setupScript → snapshot) in the background.
|
|
468
|
+
console.log("\n📡 Triggering backend regeneration...");
|
|
469
|
+
const triggered = await apiPost<SandboxSnapshot>(
|
|
470
|
+
`/sandboxes/${sandboxId}/snapshots/${snapshotId}/regenerate`
|
|
430
471
|
);
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
472
|
+
console.log(` regenerationSandboxId: ${triggered.regenerationSandboxId || "(none)"}`);
|
|
473
|
+
|
|
474
|
+
if (!opts.wait) {
|
|
475
|
+
console.log(
|
|
476
|
+
`\n✅ Regeneration triggered. Monitor with:\n` +
|
|
477
|
+
` knowhow sandbox snapshot-status --sandbox-id ${sandboxId} --snapshot-id ${snapshotId}`
|
|
435
478
|
);
|
|
479
|
+
return;
|
|
436
480
|
}
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
481
|
+
|
|
482
|
+
// ── 4. Poll until complete ──────────────────────────────────────────
|
|
483
|
+
console.log("\n⏳ Waiting for regeneration to complete (Ctrl+C to stop polling)...");
|
|
484
|
+
const done = await pollRegeneration(sandboxId, snapshotId, timeoutMs);
|
|
485
|
+
|
|
486
|
+
console.log(
|
|
487
|
+
`\n✅ Snapshot regeneration complete!\n` +
|
|
488
|
+
` Snapshot ID : ${done.id}\n` +
|
|
489
|
+
` Label : ${done.label || "(none)"}\n` +
|
|
490
|
+
` Status : ${done.status}`
|
|
446
491
|
);
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
// ── 5. Execute the setupScript ──────────────────────────────────────
|
|
450
|
-
console.log("\n🔧 Step 3/4: Executing setupScript...");
|
|
451
|
-
console.log(` Script length: ${snapshot.setupScript.length} chars`);
|
|
452
|
-
let execResult: { stdout?: string; stderr?: string; exitCode?: number };
|
|
453
|
-
try {
|
|
454
|
-
execResult = await apiPost<{
|
|
455
|
-
stdout?: string;
|
|
456
|
-
stderr?: string;
|
|
457
|
-
exitCode?: number;
|
|
458
|
-
}>(`/sandboxes/${ephemeralId}/exec`, {
|
|
459
|
-
command: "/bin/bash",
|
|
460
|
-
args: ["-c", snapshot.setupScript],
|
|
461
|
-
timeoutMs: Math.min(deadline - Date.now(), 25 * 60 * 1000),
|
|
462
|
-
});
|
|
463
|
-
} catch (err: any) {
|
|
464
|
-
console.error(`\n❌ setupScript exec failed: ${err.message}`);
|
|
465
|
-
await safeDestroy(ephemeralId);
|
|
466
|
-
throw err;
|
|
467
|
-
}
|
|
492
|
+
}
|
|
493
|
+
);
|
|
468
494
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
495
|
+
// ── sandbox snapshot-status ──────────────────────────────────────────────────
|
|
496
|
+
sandboxCmd
|
|
497
|
+
.command("snapshot-status")
|
|
498
|
+
.description(
|
|
499
|
+
"Check and optionally wait for a snapshot's regeneration to complete.\n" +
|
|
500
|
+
"Useful after triggering regeneration with --no-wait."
|
|
501
|
+
)
|
|
502
|
+
.option("--sandbox-id <id>", "Sandbox ID")
|
|
503
|
+
.option("--sandbox-name <name>", "Sandbox name")
|
|
504
|
+
.option("--snapshot-id <id>", "Snapshot ID")
|
|
505
|
+
.option("--snapshot-name <name>", "Snapshot label")
|
|
506
|
+
.option("--wait", "Poll until regeneration completes (or times out)")
|
|
507
|
+
.option("--timeout-minutes <min>", "Max poll time in minutes", "60")
|
|
508
|
+
.option("--json", "Output raw JSON")
|
|
509
|
+
.action(
|
|
510
|
+
async (opts: {
|
|
511
|
+
sandboxId?: string;
|
|
512
|
+
sandboxName?: string;
|
|
513
|
+
snapshotId?: string;
|
|
514
|
+
snapshotName?: string;
|
|
515
|
+
wait?: boolean;
|
|
516
|
+
timeoutMinutes: string;
|
|
517
|
+
json?: boolean;
|
|
518
|
+
}) => {
|
|
519
|
+
if (!opts.sandboxId && !opts.sandboxName) {
|
|
520
|
+
throw new Error("Provide --sandbox-id or --sandbox-name");
|
|
476
521
|
}
|
|
477
|
-
|
|
478
|
-
await
|
|
479
|
-
|
|
522
|
+
const sandboxId =
|
|
523
|
+
opts.sandboxId || (await resolveSandboxId(opts.sandboxName!));
|
|
524
|
+
|
|
525
|
+
if (!opts.snapshotId && !opts.snapshotName) {
|
|
526
|
+
throw new Error("Provide --snapshot-id or --snapshot-name");
|
|
480
527
|
}
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
snapshotContent: snapshot.snapshotContent || "full",
|
|
494
|
-
snapshotType: "full",
|
|
495
|
-
}
|
|
528
|
+
const snapshot = opts.snapshotId
|
|
529
|
+
? await apiGet<SandboxSnapshot>(
|
|
530
|
+
`/sandboxes/${sandboxId}/snapshots/${opts.snapshotId}`
|
|
531
|
+
)
|
|
532
|
+
: await resolveSnapshot(sandboxId, opts.snapshotName!);
|
|
533
|
+
|
|
534
|
+
if (opts.wait && snapshot.regenerationSandboxId) {
|
|
535
|
+
console.log("⏳ Waiting for regeneration to complete...");
|
|
536
|
+
const done = await pollRegeneration(
|
|
537
|
+
sandboxId,
|
|
538
|
+
snapshot.id,
|
|
539
|
+
Number(opts.timeoutMinutes) * 60 * 1000
|
|
496
540
|
);
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
541
|
+
if (opts.json) {
|
|
542
|
+
console.log(JSON.stringify(done, null, 2));
|
|
543
|
+
} else {
|
|
544
|
+
console.log(
|
|
545
|
+
`✅ Done — status: ${done.status} label: ${done.label || "(none)"}`
|
|
546
|
+
);
|
|
547
|
+
}
|
|
548
|
+
return;
|
|
501
549
|
}
|
|
502
550
|
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
newSnapshotId,
|
|
512
|
-
["ready"],
|
|
513
|
-
Math.min(deadline - Date.now(), 20 * 60 * 1000)
|
|
551
|
+
if (opts.json) {
|
|
552
|
+
console.log(JSON.stringify(snapshot, null, 2));
|
|
553
|
+
} else {
|
|
554
|
+
const regenInfo = snapshot.regenerationSandboxId
|
|
555
|
+
? `🔄 regenerating (ephemeral: ${snapshot.regenerationSandboxId})`
|
|
556
|
+
: `✅ ${snapshot.status}`;
|
|
557
|
+
console.log(
|
|
558
|
+
`Snapshot ${snapshot.id} label=${snapshot.label || "(none)"} ${regenInfo}`
|
|
514
559
|
);
|
|
515
|
-
|
|
560
|
+
if (snapshot.statusMessage) {
|
|
561
|
+
console.log(` message: ${snapshot.statusMessage}`);
|
|
562
|
+
}
|
|
516
563
|
}
|
|
517
|
-
|
|
518
|
-
// ── 7. Destroy ephemeral sandbox ────────────────────────────────────
|
|
519
|
-
console.log("\n🗑 Destroying ephemeral sandbox...");
|
|
520
|
-
await safeDestroy(ephemeralId);
|
|
521
|
-
console.log(" ✓ Destroyed");
|
|
522
|
-
|
|
523
|
-
console.log(
|
|
524
|
-
`\n✅ Snapshot regeneration complete!\n` +
|
|
525
|
-
` Old snapshot ID : ${snapshotId}\n` +
|
|
526
|
-
` New snapshot ID : ${newSnapshotId}\n` +
|
|
527
|
-
` Label : ${newLabel}\n\n` +
|
|
528
|
-
`ℹ The old snapshot still exists. Clean it up with:\n` +
|
|
529
|
-
` knowhow sandbox delete-snapshot --sandbox-id ${sandboxId} --snapshot-id ${snapshotId}`
|
|
530
|
-
);
|
|
531
564
|
}
|
|
532
565
|
);
|
|
533
566
|
}
|
|
534
|
-
|
|
535
|
-
async function safeDestroy(sandboxId: string): Promise<void> {
|
|
536
|
-
try {
|
|
537
|
-
await apiDelete(`/sandboxes/${sandboxId}`);
|
|
538
|
-
} catch (err: any) {
|
|
539
|
-
console.warn(`⚠ Could not destroy sandbox ${sandboxId}: ${err.message}`);
|
|
540
|
-
}
|
|
541
|
-
}
|
package/src/services/Mcp.ts
CHANGED
|
@@ -442,6 +442,9 @@ export class McpService {
|
|
|
442
442
|
|
|
443
443
|
const realName = this.parseToolName(toolName);
|
|
444
444
|
return async (args: any) => {
|
|
445
|
+
// MCP tools are registered without `usesContext`, so ToolsService.callTool
|
|
446
|
+
// never injects the per-call `_ctx` here — the args we receive are already
|
|
447
|
+
// safe to serialize over the wire (no circular BaseAgent/AI-client refs).
|
|
445
448
|
console.log("Calling tool via mcp client", realName, "with args", args);
|
|
446
449
|
const tool = await client.callTool(
|
|
447
450
|
{
|
package/src/services/Tools.ts
CHANGED
|
@@ -245,9 +245,15 @@ export class ToolsService {
|
|
|
245
245
|
const properties = toolDefinition?.function?.parameters?.properties || {};
|
|
246
246
|
const isPositional =
|
|
247
247
|
toolDefinition?.function?.parameters?.positional || false;
|
|
248
|
+
const usesContext =
|
|
249
|
+
toolDefinition?.function?.parameters?.usesContext || false;
|
|
248
250
|
|
|
249
251
|
// Thread the per-call context (`_ctx`) into the invocation so tools can be
|
|
250
252
|
// self-referential (e.g. read the calling agent off `_ctx.caller`).
|
|
253
|
+
// Only tools that opt-in via `usesContext: true` receive `_ctx`. This keeps
|
|
254
|
+
// the context (which holds non-serializable, circular references like the
|
|
255
|
+
// calling agent / AI client) out of tools that don't need it — notably MCP
|
|
256
|
+
// tools, whose args are serialized to JSON and sent over the wire.
|
|
251
257
|
// - positional tools receive `_ctx` as the LAST positional argument
|
|
252
258
|
// - non-positional tools receive it merged onto the args object as `_ctx`
|
|
253
259
|
const ctx: ToolCallContext = { ...(callContext || {}), Tools: this };
|
|
@@ -257,8 +263,12 @@ export class ToolsService {
|
|
|
257
263
|
: functionArgs;
|
|
258
264
|
|
|
259
265
|
const fnArgs = isPositional
|
|
260
|
-
?
|
|
261
|
-
|
|
266
|
+
? usesContext
|
|
267
|
+
? [...positionalArgs, ctx]
|
|
268
|
+
: positionalArgs
|
|
269
|
+
: usesContext
|
|
270
|
+
? { ...functionArgs, _ctx: ctx }
|
|
271
|
+
: functionArgs;
|
|
262
272
|
|
|
263
273
|
// Execute the function
|
|
264
274
|
const rawResponse = isPositional
|