@langchain/quickjs 0.5.0 → 0.6.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 +11 -4
- package/dist/index.cjs +193 -114
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +193 -114
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -100,7 +100,9 @@ PTC configuration is progressive:
|
|
|
100
100
|
|
|
101
101
|
### Recursive Language Model (RLM)
|
|
102
102
|
|
|
103
|
-
When the
|
|
103
|
+
When the agent has subagents configured, a `task()` global is available inside
|
|
104
|
+
the REPL (no PTC needed), so the agent can spawn sub-agents in parallel from
|
|
105
|
+
within the REPL:
|
|
104
106
|
|
|
105
107
|
```typescript
|
|
106
108
|
const agent = createDeepAgent({
|
|
@@ -112,7 +114,7 @@ const agent = createDeepAgent({
|
|
|
112
114
|
systemPrompt: "...",
|
|
113
115
|
},
|
|
114
116
|
],
|
|
115
|
-
middleware: [createQuickJSMiddleware(
|
|
117
|
+
middleware: [createQuickJSMiddleware()],
|
|
116
118
|
});
|
|
117
119
|
```
|
|
118
120
|
|
|
@@ -122,16 +124,21 @@ The agent then writes code like:
|
|
|
122
124
|
const topics = ["quantum computing", "fusion energy", "CRISPR"];
|
|
123
125
|
const results = await Promise.all(
|
|
124
126
|
topics.map((topic) =>
|
|
125
|
-
|
|
127
|
+
task({
|
|
126
128
|
description: `Research ${topic} in depth`,
|
|
127
129
|
subagentType: "general-purpose",
|
|
128
130
|
}),
|
|
129
131
|
),
|
|
130
132
|
);
|
|
133
|
+
// Aggregate and return the report from the eval; the agent then writes it to a
|
|
134
|
+
// file with its own write_file tool.
|
|
131
135
|
const report = topics.map((t, i) => `## ${t}\n${results[i]}`).join("\n\n");
|
|
132
|
-
|
|
136
|
+
report;
|
|
133
137
|
```
|
|
134
138
|
|
|
139
|
+
> `task` cannot be exposed via `ptc` — it is reserved for the `task()` global,
|
|
140
|
+
> so passing `ptc: ["task"]` throws.
|
|
141
|
+
|
|
135
142
|
## API
|
|
136
143
|
|
|
137
144
|
### `createQuickJSMiddleware(options?)`
|
package/dist/index.cjs
CHANGED
|
@@ -30,6 +30,8 @@ let quickjs_emscripten = require("quickjs-emscripten");
|
|
|
30
30
|
let quickjs_emscripten_core = require("quickjs-emscripten-core");
|
|
31
31
|
let json_schema_to_typescript = require("json-schema-to-typescript");
|
|
32
32
|
let _langchain_core_utils_json_schema = require("@langchain/core/utils/json_schema");
|
|
33
|
+
let _langchain_langgraph = require("@langchain/langgraph");
|
|
34
|
+
let _langchain_core_messages = require("@langchain/core/messages");
|
|
33
35
|
let acorn = require("acorn");
|
|
34
36
|
let _sveltejs_acorn_typescript = require("@sveltejs/acorn-typescript");
|
|
35
37
|
let estree_walker = require("estree-walker");
|
|
@@ -121,6 +123,59 @@ async function toolToTypeSignature(name, description, jsonSchema) {
|
|
|
121
123
|
`;
|
|
122
124
|
}
|
|
123
125
|
//#endregion
|
|
126
|
+
//#region src/coerce.ts
|
|
127
|
+
/**
|
|
128
|
+
* Coercion of tool / subagent return values for the QuickJS bridge.
|
|
129
|
+
*
|
|
130
|
+
* The deepagents `task` tool resolves to a LangGraph `Command` whose payload
|
|
131
|
+
* carries the subagent's final message(s) under `update.messages`; some tools
|
|
132
|
+
* return a `ToolMessage` or a list of messages. The interpreter bridges need
|
|
133
|
+
* the underlying output, not the envelope, so this unwraps those shapes to the
|
|
134
|
+
* content the model actually cares about.
|
|
135
|
+
*/
|
|
136
|
+
/**
|
|
137
|
+
* Return the trailing message content from a `Command`'s `update.messages`,
|
|
138
|
+
* scanning from the end for the last message that actually has content. Returns
|
|
139
|
+
* the command unchanged when it has no message-shaped payload.
|
|
140
|
+
*/
|
|
141
|
+
function extractCommandContent(command) {
|
|
142
|
+
const update = command.update;
|
|
143
|
+
const messages = update !== null && typeof update === "object" ? update.messages : void 0;
|
|
144
|
+
if (Array.isArray(messages)) for (let i = messages.length - 1; i >= 0; i--) {
|
|
145
|
+
const message = messages[i];
|
|
146
|
+
if (_langchain_core_messages.BaseMessage.isInstance(message) && message.content != null) return message.content;
|
|
147
|
+
}
|
|
148
|
+
return command;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Unwrap a LangChain `Command` / `ToolMessage` / message-list envelope to the
|
|
152
|
+
* underlying content. Non-envelope values (strings, content-block arrays, plain
|
|
153
|
+
* objects) are returned unchanged.
|
|
154
|
+
*
|
|
155
|
+
* @param value The raw value returned by a tool or subagent dispatch.
|
|
156
|
+
* @returns The unwrapped content, or `value` itself when it isn't an envelope.
|
|
157
|
+
*/
|
|
158
|
+
function unwrapToolEnvelope(value) {
|
|
159
|
+
if (typeof value === "string") return value;
|
|
160
|
+
if ((0, _langchain_langgraph.isCommand)(value)) {
|
|
161
|
+
const inner = extractCommandContent(value);
|
|
162
|
+
return inner === value ? value : unwrapToolEnvelope(inner);
|
|
163
|
+
}
|
|
164
|
+
if (_langchain_core_messages.BaseMessage.isInstance(value)) return unwrapToolEnvelope(value.content);
|
|
165
|
+
if (Array.isArray(value)) {
|
|
166
|
+
for (let i = value.length - 1; i >= 0; i--) {
|
|
167
|
+
const entry = value[i];
|
|
168
|
+
if (_langchain_core_messages.BaseMessage.isInstance(entry)) return unwrapToolEnvelope(entry.content);
|
|
169
|
+
if ((0, _langchain_langgraph.isCommand)(entry)) {
|
|
170
|
+
const inner = extractCommandContent(entry);
|
|
171
|
+
if (inner !== entry) return unwrapToolEnvelope(inner);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return value;
|
|
175
|
+
}
|
|
176
|
+
return value;
|
|
177
|
+
}
|
|
178
|
+
//#endregion
|
|
124
179
|
//#region src/transform.ts
|
|
125
180
|
/**
|
|
126
181
|
* AST-based code transform pipeline for the REPL.
|
|
@@ -422,6 +477,7 @@ function getSharedModule() {
|
|
|
422
477
|
* @returns Plain string representation of the tool output.
|
|
423
478
|
*/
|
|
424
479
|
function extractToolText(result) {
|
|
480
|
+
result = unwrapToolEnvelope(result);
|
|
425
481
|
if (typeof result === "string") return result;
|
|
426
482
|
if (Array.isArray(result)) {
|
|
427
483
|
const texts = [];
|
|
@@ -516,7 +572,7 @@ var ReplSession = class ReplSession {
|
|
|
516
572
|
subagentQueue = null;
|
|
517
573
|
bridgeDispatchRef = null;
|
|
518
574
|
/** Allowed keys in the subagent input object. */
|
|
519
|
-
static SUBAGENT_ALLOWED_KEYS = new Set([
|
|
575
|
+
static SUBAGENT_ALLOWED_KEYS = /* @__PURE__ */ new Set([
|
|
520
576
|
"description",
|
|
521
577
|
"subagentType",
|
|
522
578
|
"responseSchema"
|
|
@@ -926,8 +982,10 @@ function renderSubagentPrompt(toolName) {
|
|
|
926
982
|
### Dispatching Subagents with \`task\`
|
|
927
983
|
|
|
928
984
|
\`task\` is your primitive for running configured subagents from inside the
|
|
929
|
-
JavaScript REPL.
|
|
930
|
-
|
|
985
|
+
JavaScript REPL. Your job here is to DISTRIBUTE work, not to do it yourself:
|
|
986
|
+
write JavaScript that fans work out to subagents and assembles their results.
|
|
987
|
+
You handle the orchestration - fan-out, filtering, deduplication, multi-stage
|
|
988
|
+
flow, and synthesis - in plain JavaScript.
|
|
931
989
|
|
|
932
990
|
#### The primitive
|
|
933
991
|
|
|
@@ -945,13 +1003,19 @@ function renderSubagentPrompt(toolName) {
|
|
|
945
1003
|
the configured subagent names.
|
|
946
1004
|
|
|
947
1005
|
\`description\` is the only prompt the subagent receives for this dispatch. Make
|
|
948
|
-
it complete:
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
the
|
|
1006
|
+
it complete: the goal, the constraints, what to inspect, and the exact shape
|
|
1007
|
+
or level of detail you expect back. Give context as locators — file paths and
|
|
1008
|
+
symbol names — not as pasted file contents. If you already read a file while
|
|
1009
|
+
exploring, still pass its path and let the subagent read it; do not paste back
|
|
1010
|
+
what you read. Each dispatch is stateless from the caller's perspective; you
|
|
1011
|
+
cannot send follow-up messages to the same subagent run.
|
|
952
1012
|
|
|
953
|
-
\`responseSchema\` is optional
|
|
954
|
-
|
|
1013
|
+
\`responseSchema\` is optional, but set it on any dispatch whose result feeds
|
|
1014
|
+
later code. A deterministic, typed shape is what lets you compose the next
|
|
1015
|
+
stage reliably — index it, sort it, compare fields, branch on it, merge it —
|
|
1016
|
+
instead of parsing free-form text. This is what makes a whole workflow
|
|
1017
|
+
composable as one script. When provided, the resolved value is already a typed
|
|
1018
|
+
JavaScript value matching the schema; do not call \`JSON.parse\` unless the
|
|
955
1019
|
subagent intentionally returned a JSON string. Dynamic schemas work for
|
|
956
1020
|
declarative subagents; runnable-backed subagents reject dynamic schemas because
|
|
957
1021
|
their runnable is already compiled.
|
|
@@ -972,9 +1036,12 @@ function renderSubagentPrompt(toolName) {
|
|
|
972
1036
|
dispatch result back onto its item. Multi-stage analysis means: run a pass,
|
|
973
1037
|
filter or regroup the array in JS, then run another pass over the survivors.
|
|
974
1038
|
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
1039
|
+
You can run the whole workflow in one \`${toolName}\` call or split it across
|
|
1040
|
+
several — both are fine. A single end-to-end script (generate, compare, pick a
|
|
1041
|
+
winner; or review every item, then synthesize) is clean when you can write it
|
|
1042
|
+
in one go; splitting is also fine when you want to inspect results between
|
|
1043
|
+
stages. Either way, don't redo work across calls — reuse what is already in
|
|
1044
|
+
scope (see "Reuse what earlier evals left in scope" below).
|
|
978
1045
|
|
|
979
1046
|
#### Fan out with bounded concurrency
|
|
980
1047
|
|
|
@@ -983,13 +1050,15 @@ function renderSubagentPrompt(toolName) {
|
|
|
983
1050
|
enforces a hard per-REPL cap of 32 concurrent subagent calls.
|
|
984
1051
|
|
|
985
1052
|
\`\`\`javascript
|
|
1053
|
+
const files = ["/src/a.ts", "/src/b.ts", "/src/c.ts"]; // found while exploring
|
|
986
1054
|
const batchSize = 10;
|
|
987
1055
|
const reviewed = [];
|
|
988
|
-
for (let i = 0; i <
|
|
989
|
-
const batch =
|
|
990
|
-
reviewed.push(...(await Promise.all(batch.map(async (
|
|
1056
|
+
for (let i = 0; i < files.length; i += batchSize) {
|
|
1057
|
+
const batch = files.slice(i, i + batchSize);
|
|
1058
|
+
reviewed.push(...(await Promise.all(batch.map(async (file) => {
|
|
991
1059
|
const result = await task({
|
|
992
|
-
description: "
|
|
1060
|
+
description: "Read " + file + " and review it for SQL injection. " +
|
|
1061
|
+
"Cite line numbers.",
|
|
993
1062
|
subagentType: "reviewer",
|
|
994
1063
|
responseSchema: {
|
|
995
1064
|
type: "object",
|
|
@@ -1010,61 +1079,44 @@ function renderSubagentPrompt(toolName) {
|
|
|
1010
1079
|
required: ["vulnerabilities"],
|
|
1011
1080
|
},
|
|
1012
1081
|
});
|
|
1013
|
-
return {
|
|
1082
|
+
return { file, ...result };
|
|
1014
1083
|
}))));
|
|
1015
1084
|
}
|
|
1016
1085
|
\`\`\`
|
|
1017
1086
|
|
|
1018
|
-
####
|
|
1087
|
+
#### Explore with your own tools first, then distribute
|
|
1019
1088
|
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1089
|
+
You already have your normal tools for reading, listing, globbing, and
|
|
1090
|
+
grepping files. Use them to explore and understand the task BEFORE you write
|
|
1091
|
+
the orchestration script. These are ordinary tool calls, separate from the
|
|
1092
|
+
\`${toolName}\` tool: read the data file, list or glob the directory, grep for
|
|
1093
|
+
what matters, then decide how to split the work.
|
|
1025
1094
|
|
|
1026
|
-
|
|
1027
|
-
or
|
|
1028
|
-
|
|
1095
|
+
Never write \`${toolName}\` code that spawns a subagent just to read or parse a
|
|
1096
|
+
file or list a directory. That is a deterministic step you do yourself with a
|
|
1097
|
+
direct tool call; spending a whole agent loop on it is wasteful.
|
|
1029
1098
|
|
|
1030
|
-
|
|
1099
|
+
Once you understand the shape of the work, you have creative freedom in how
|
|
1100
|
+
you split it:
|
|
1031
1101
|
|
|
1032
|
-
|
|
1033
|
-
|
|
1102
|
+
- One dispatch per file or per record, when the items are already separate.
|
|
1103
|
+
- Chunk a large input yourself — read it, split it, optionally write a small
|
|
1104
|
+
input file per chunk — and dispatch one subagent per chunk.
|
|
1105
|
+
- A cheap classification pass first, then deeper dispatches only for the items
|
|
1106
|
+
that warrant them.
|
|
1034
1107
|
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
.filter(Boolean);
|
|
1108
|
+
Then write JavaScript in the \`${toolName}\` tool that distributes the heavy,
|
|
1109
|
+
agentic work to subagents with \`task()\`: analyzing file contents, exploring a
|
|
1110
|
+
codebase, making judgment calls, rewriting code, or synthesizing a report.
|
|
1039
1111
|
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
const batch = items.slice(i, i + batchSize);
|
|
1049
|
-
results.push(...(await Promise.all(batch.map(async (it) => {
|
|
1050
|
-
const finding = await task({
|
|
1051
|
-
description:
|
|
1052
|
-
"Review this file for auth bypasses. Return concrete findings only.\\n\\n" +
|
|
1053
|
-
"File: " + it.file + "\\n\\n" +
|
|
1054
|
-
it.content,
|
|
1055
|
-
subagentType: "reviewer",
|
|
1056
|
-
responseSchema: {
|
|
1057
|
-
type: "object",
|
|
1058
|
-
properties: {
|
|
1059
|
-
findings: { type: "array", items: { type: "object" } },
|
|
1060
|
-
},
|
|
1061
|
-
required: ["findings"],
|
|
1062
|
-
},
|
|
1063
|
-
});
|
|
1064
|
-
return { ...it, ...finding };
|
|
1065
|
-
}))));
|
|
1066
|
-
}
|
|
1067
|
-
\`\`\`
|
|
1112
|
+
Hand each subagent a locator, not a payload. Subagents have their own file
|
|
1113
|
+
tools, so for anything that lives in a file — a file to review, rewrite, or
|
|
1114
|
+
audit — pass the path and let the subagent read it. Do NOT read a whole file
|
|
1115
|
+
just to paste its contents into the description; that bloats every dispatch
|
|
1116
|
+
and duplicates the file across them. Reserve inline content for small or
|
|
1117
|
+
derived data that has no path of its own: a single parsed record, or a chunk
|
|
1118
|
+
you split out of a larger input (write the chunk to its own file and pass that
|
|
1119
|
+
path if it is large). Assemble the results in JS.
|
|
1068
1120
|
|
|
1069
1121
|
#### Compose multiple stages
|
|
1070
1122
|
|
|
@@ -1073,67 +1125,89 @@ function renderSubagentPrompt(toolName) {
|
|
|
1073
1125
|
only for those items.
|
|
1074
1126
|
|
|
1075
1127
|
\`\`\`javascript
|
|
1076
|
-
const tagged =
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
});
|
|
1089
|
-
return { ...it, ...tag };
|
|
1090
|
-
}))));
|
|
1091
|
-
}
|
|
1128
|
+
const tagged = await Promise.all(files.map((file) =>
|
|
1129
|
+
task({
|
|
1130
|
+
description: "Read " + file + " and classify it as handler, util, " +
|
|
1131
|
+
"test, or config.",
|
|
1132
|
+
subagentType: "reviewer",
|
|
1133
|
+
responseSchema: {
|
|
1134
|
+
type: "object",
|
|
1135
|
+
properties: { kind: { type: "string" }, risky: { type: "boolean" } },
|
|
1136
|
+
required: ["kind", "risky"],
|
|
1137
|
+
},
|
|
1138
|
+
}).then((tag) => ({ file, ...tag }))
|
|
1139
|
+
));
|
|
1092
1140
|
|
|
1093
1141
|
const riskyHandlers = tagged.filter((it) => it.kind === "handler" && it.risky);
|
|
1094
|
-
const deepReviews =
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
subagentType: "reviewer",
|
|
1101
|
-
});
|
|
1102
|
-
return { ...it, review };
|
|
1103
|
-
}))));
|
|
1104
|
-
}
|
|
1142
|
+
const deepReviews = await Promise.all(riskyHandlers.map((it) =>
|
|
1143
|
+
task({
|
|
1144
|
+
description: "Deep security review of " + it.file + ". Cite line numbers.",
|
|
1145
|
+
subagentType: "reviewer",
|
|
1146
|
+
}).then((review) => ({ ...it, review }))
|
|
1147
|
+
));
|
|
1105
1148
|
\`\`\`
|
|
1106
1149
|
|
|
1107
|
-
####
|
|
1150
|
+
#### Return results via the last expression, not \`console.log\`
|
|
1151
|
+
|
|
1152
|
+
The value of the last expression in an \`${toolName}\` call (or a resolved
|
|
1153
|
+
top-level \`await\`) is returned to you as the result. Make that final
|
|
1154
|
+
expression the variable holding your result and read it from there.
|
|
1155
|
+
\`console.log\` is only for incidental debugging: its output is capped and
|
|
1156
|
+
truncated, while the returned value is not, so never \`console.log\` your
|
|
1157
|
+
actual results.
|
|
1158
|
+
|
|
1159
|
+
Keep large intermediate sets in JS variables and return only a compact
|
|
1160
|
+
summary or a small slice, not the entire dataset. To persist full output,
|
|
1161
|
+
have a subagent write it, or write it with your own file tool outside the
|
|
1162
|
+
\`${toolName}\` call.
|
|
1163
|
+
|
|
1164
|
+
#### Reuse what earlier evals left in scope
|
|
1108
1165
|
|
|
1109
|
-
|
|
1110
|
-
|
|
1166
|
+
The REPL is persistent within a turn: every top-level variable, function, and
|
|
1167
|
+
class you declare is kept and is available in your next \`${toolName}\` call
|
|
1168
|
+
(each is hoisted to global scope). So if a later step needs something an
|
|
1169
|
+
earlier eval produced or bound, **reference that variable by name** — do not
|
|
1170
|
+
write a new literal that re-types data a previous eval already returned or
|
|
1171
|
+
computed.
|
|
1172
|
+
|
|
1173
|
+
If you catch yourself pasting a big array or object of values you produced in
|
|
1174
|
+
an earlier call, that is the tell: the variable is still in scope, so use it.
|
|
1175
|
+
Re-typing prior results as a fresh literal wastes tokens and drifts from what
|
|
1176
|
+
actually ran.
|
|
1111
1177
|
|
|
1112
1178
|
\`\`\`javascript
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
content: JSON.stringify(deepReviews),
|
|
1116
|
-
});
|
|
1117
|
-
\`\`\`
|
|
1179
|
+
// An earlier eval bound this:
|
|
1180
|
+
// const auditResults = await Promise.all(files.map(/* ...audit... */));
|
|
1118
1181
|
|
|
1119
|
-
|
|
1120
|
-
|
|
1182
|
+
// A later eval — reference it; do NOT paste the findings back in as a literal:
|
|
1183
|
+
const findings = auditResults.flatMap((r) =>
|
|
1184
|
+
r.findings.map((f) => ({ ...f, file: r.file }))
|
|
1185
|
+
);
|
|
1186
|
+
const verified = await Promise.all(findings.map((f) =>
|
|
1187
|
+
task({ description: "Verify this finding: " + f.evidence, subagentType: "verifier" })
|
|
1188
|
+
.then((v) => ({ ...f, ...v }))
|
|
1189
|
+
));
|
|
1190
|
+
\`\`\`
|
|
1121
1191
|
|
|
1122
|
-
####
|
|
1192
|
+
#### When the user asks for a "workflow"
|
|
1123
1193
|
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1194
|
+
If the user's request mentions running a "workflow" (or otherwise uses the
|
|
1195
|
+
word "workflow"), fan the work out to subagents rather than doing it all
|
|
1196
|
+
yourself. Explore with your own tools first as needed, then write JavaScript
|
|
1197
|
+
in the \`${toolName}\` tool that dispatches subagents with \`task()\` and
|
|
1198
|
+
assembles their results. The point is to distribute the heavy work in
|
|
1199
|
+
parallel, not to grind through it one tool call at a time.
|
|
1127
1200
|
`;
|
|
1128
1201
|
}
|
|
1129
1202
|
function renderReplSystemPrompt(opts) {
|
|
1203
|
+
const sideEffects = opts.hasPtc ? " External side effects from inside the REPL are reachable only via the `tools.*` namespace documented below." : " The REPL is pure computation; do any filesystem or other I/O with your normal tools, outside this tool.";
|
|
1130
1204
|
return dedent.default`
|
|
1131
1205
|
### Interpreter
|
|
1132
1206
|
|
|
1133
1207
|
An \`${opts.toolName}\` tool is available. It runs JavaScript in a persistent REPL.
|
|
1134
1208
|
- State (variables, functions) persists across tool calls within a single turn of conversation. They DO NOT persist across multiple turns.
|
|
1135
1209
|
- Top-level \`await\` works; Promises resolve before the call returns.
|
|
1136
|
-
- Runtime sandbox: no built-in filesystem, network, stdlib, or wall-clock APIs (\`fetch\`, \`require\`, \`fs\`, \`process\`, real \`Date.now()\` are unavailable or stubbed)
|
|
1210
|
+
- Runtime sandbox: no built-in filesystem, network, stdlib, or wall-clock APIs (\`fetch\`, \`require\`, \`fs\`, \`process\`, real \`Date.now()\` are unavailable or stubbed).${sideEffects}
|
|
1137
1211
|
- Timeout: ${opts.timeout}s per call. Memory: ${opts.memoryLimitMb} MB total.
|
|
1138
1212
|
- \`console.log\` output is captured and returned alongside the result.
|
|
1139
1213
|
`;
|
|
@@ -1178,10 +1252,14 @@ async function generatePtcPrompt(tools) {
|
|
|
1178
1252
|
* StructuredToolInterface objects. Strings are looked up by name in agentTools;
|
|
1179
1253
|
* instances are included directly without requiring agent registration. Strings
|
|
1180
1254
|
* that don't match any agent tool are silently omitted.
|
|
1255
|
+
*
|
|
1256
|
+
* Throws if the subagent `task` tool is requested (by name or instance): it is
|
|
1257
|
+
* reserved for the `task()` global and cannot be a `tools.*` PTC member.
|
|
1181
1258
|
*/
|
|
1182
1259
|
function resolveToolList(items, agentTools) {
|
|
1183
1260
|
const agentByName = new Map(agentTools.map((t) => [t.name, t]));
|
|
1184
1261
|
return items.flatMap((item) => {
|
|
1262
|
+
if ((typeof item === "string" ? item : item.name) === "task") throw new Error("The subagent `task` tool cannot be exposed via `ptc`. It is always available as the top-level `task()` global inside the REPL (with `subagentType` and `responseSchema` support); exposing it through the `tools.*` namespace would create a second, conflicting dispatch path that drops `responseSchema`. Remove \"task\" from `ptc`.");
|
|
1185
1263
|
if (typeof item === "string") {
|
|
1186
1264
|
const found = agentByName.get(item);
|
|
1187
1265
|
return found ? [found] : [];
|
|
@@ -1196,11 +1274,6 @@ function createCodeInterpreterMiddleware(options = {}) {
|
|
|
1196
1274
|
const { ptc, memoryLimitBytes = DEFAULT_MEMORY_LIMIT, maxStackSizeBytes = DEFAULT_MAX_STACK_SIZE, executionTimeoutMs = DEFAULT_EXECUTION_TIMEOUT, systemPrompt: customSystemPrompt = null, maxPtcCalls = 256, maxResultChars = DEFAULT_MAX_RESULTS_CHARS, toolName = DEFAULT_TOOL_NAME, captureConsole = true, subagents = true } = options;
|
|
1197
1275
|
const maxSubagentConcurrency = subagents ? 32 : 0;
|
|
1198
1276
|
if (maxPtcCalls !== null && maxPtcCalls !== void 0 && maxPtcCalls < 1) throw new Error("`maxPtcCalls` must be >= 1 or null");
|
|
1199
|
-
const baseSystemPrompt = customSystemPrompt || renderReplSystemPrompt({
|
|
1200
|
-
toolName,
|
|
1201
|
-
timeout: executionTimeoutMs / 1e3,
|
|
1202
|
-
memoryLimitMb: Math.floor(memoryLimitBytes / (1024 * 1024))
|
|
1203
|
-
});
|
|
1204
1277
|
const middlewareId = crypto.randomUUID();
|
|
1205
1278
|
let cachedPtcPrompt = null;
|
|
1206
1279
|
let ptcTools = [];
|
|
@@ -1223,16 +1296,16 @@ function createCodeInterpreterMiddleware(options = {}) {
|
|
|
1223
1296
|
...hasSchema && { [deepagents.SUBAGENT_RESPONSE_FORMAT_CONFIG_KEY]: input.responseSchema }
|
|
1224
1297
|
}
|
|
1225
1298
|
};
|
|
1226
|
-
const
|
|
1299
|
+
const content = unwrapToolEnvelope(await subagentTaskTool.invoke({
|
|
1227
1300
|
description: input.description,
|
|
1228
1301
|
subagent_type: input.subagentType
|
|
1229
|
-
}, toolConfig);
|
|
1230
|
-
if (hasSchema && typeof
|
|
1231
|
-
return JSON.parse(
|
|
1302
|
+
}, toolConfig));
|
|
1303
|
+
if (hasSchema && typeof content === "string") try {
|
|
1304
|
+
return JSON.parse(content);
|
|
1232
1305
|
} catch {
|
|
1233
|
-
return
|
|
1306
|
+
return content;
|
|
1234
1307
|
}
|
|
1235
|
-
return
|
|
1308
|
+
return content;
|
|
1236
1309
|
};
|
|
1237
1310
|
}
|
|
1238
1311
|
return (0, langchain.createMiddleware)({
|
|
@@ -1270,6 +1343,12 @@ function createCodeInterpreterMiddleware(options = {}) {
|
|
|
1270
1343
|
ptcTools = filterToolsForPtc(agentTools);
|
|
1271
1344
|
if (!taskTool && maxSubagentConcurrency > 0) taskTool = findTaskTool(agentTools);
|
|
1272
1345
|
if (ptcTools.length > 0 && !cachedPtcPrompt) cachedPtcPrompt = await generatePtcPrompt(ptcTools);
|
|
1346
|
+
const baseSystemPrompt = customSystemPrompt || renderReplSystemPrompt({
|
|
1347
|
+
toolName,
|
|
1348
|
+
timeout: executionTimeoutMs / 1e3,
|
|
1349
|
+
memoryLimitMb: Math.floor(memoryLimitBytes / (1024 * 1024)),
|
|
1350
|
+
hasPtc: ptcTools.length > 0
|
|
1351
|
+
});
|
|
1273
1352
|
const subagentPrompt = taskTool && maxSubagentConcurrency > 0 ? renderSubagentPrompt(toolName) : "";
|
|
1274
1353
|
const systemMessage = request.systemMessage.concat(baseSystemPrompt).concat(subagentPrompt).concat(cachedPtcPrompt || "");
|
|
1275
1354
|
return handler({
|