la-machina-engine 0.11.3 → 0.12.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 +1 -0
- package/dist/{contract-BY-GqoYk.d.cts → contract-CK8kUyam.d.cts} +38 -1
- package/dist/{contract-BY-GqoYk.d.ts → contract-CK8kUyam.d.ts} +38 -1
- package/dist/index.cjs +52 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +40 -13
- package/dist/index.d.ts +40 -13
- package/dist/index.js +51 -1
- package/dist/index.js.map +1 -1
- package/dist/node-tools.cjs.map +1 -1
- package/dist/node-tools.d.cts +1 -1
- package/dist/node-tools.d.ts +1 -1
- package/dist/node-tools.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1861,6 +1861,7 @@ All features ported 1:1 from La-Machina's production runtime. Pure JS, Workers-c
|
|
|
1861
1861
|
- [x] Bash error cascading (AbortController aborts sibling tools)
|
|
1862
1862
|
- [x] 22 built-in tools
|
|
1863
1863
|
- [x] Custom tool registration via `defineTool()`
|
|
1864
|
+
- [x] Gated tools via `defineGatedTool()` — pause the run on tool_use, resume via `engine.resumeAsync({toolResult})`. Full transcript preserved across the pause. Use for human-in-the-loop inputs, out-of-band approvals, anything where the result comes from a caller decision.
|
|
1864
1865
|
- [x] Device path blocking (/dev/zero, /dev/random, /proc/kcore)
|
|
1865
1866
|
- [x] Knowledge base (`SearchKnowledge` + `ReadKnowledge`) — opt-in, per-tenant vault under `workspaces/{ws}/knowledge/`, section-level indexing, format extractors (md/txt/json/csv/html native; pdf/docx via optional deps), external link headers with non-persistence guarantee
|
|
1866
1867
|
|
|
@@ -96,6 +96,20 @@ interface Tool<TSchema extends z.ZodTypeAny = z.ZodTypeAny> {
|
|
|
96
96
|
* Do not set this manually.
|
|
97
97
|
*/
|
|
98
98
|
readonly isCapabilityStub?: boolean;
|
|
99
|
+
/**
|
|
100
|
+
* Plan 043 — when true, the engine pauses the run as soon as the
|
|
101
|
+
* model calls this tool instead of dispatching `execute()`. The
|
|
102
|
+
* pending tool call (id, name, input) is captured in the snapshot;
|
|
103
|
+
* resume via `engine.resumeAsync({toolResult: {...}})` injects a
|
|
104
|
+
* synthetic `tool_result` message and continues the same loop with
|
|
105
|
+
* full transcript intact.
|
|
106
|
+
*
|
|
107
|
+
* Pauses emit `pauseReason: 'awaiting_tool_result'`.
|
|
108
|
+
*
|
|
109
|
+
* `execute` is never invoked on a gated tool — `defineGatedTool()`
|
|
110
|
+
* fills in a no-op stub so the Tool type stays consistent.
|
|
111
|
+
*/
|
|
112
|
+
readonly gated?: boolean;
|
|
99
113
|
execute(input: z.infer<TSchema>, context: ToolContext): Promise<ToolResult>;
|
|
100
114
|
}
|
|
101
115
|
/**
|
|
@@ -114,6 +128,29 @@ interface Tool<TSchema extends z.ZodTypeAny = z.ZodTypeAny> {
|
|
|
114
128
|
* ```
|
|
115
129
|
*/
|
|
116
130
|
declare function defineTool<TSchema extends z.ZodTypeAny>(tool: Tool<TSchema>): Tool<TSchema>;
|
|
131
|
+
/**
|
|
132
|
+
* Plan 043 — define a tool that pauses the run instead of executing.
|
|
133
|
+
*
|
|
134
|
+
* The engine stops on `tool_use`, persists the pending call in the
|
|
135
|
+
* run snapshot, and waits for `engine.resumeAsync({toolResult: {...}})`
|
|
136
|
+
* to deliver the answer. Use this for human-in-the-loop inputs,
|
|
137
|
+
* out-of-band approvals, or any tool whose result comes from a
|
|
138
|
+
* caller decision rather than in-process computation.
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* ```ts
|
|
142
|
+
* const AskHuman = defineGatedTool({
|
|
143
|
+
* name: 'AskHuman',
|
|
144
|
+
* description: 'Ask a human for information.',
|
|
145
|
+
* inputSchema: z.object({ question: z.string() }),
|
|
146
|
+
* })
|
|
147
|
+
* ```
|
|
148
|
+
*
|
|
149
|
+
* `execute` is filled in with a no-op stub that flags the tool as
|
|
150
|
+
* "should not have been executed" if it's ever called — that
|
|
151
|
+
* indicates a code-path bug where the gated-tool pause was bypassed.
|
|
152
|
+
*/
|
|
153
|
+
declare function defineGatedTool<TSchema extends z.ZodTypeAny>(tool: Omit<Tool<TSchema>, 'execute' | 'gated'>): Tool<TSchema>;
|
|
117
154
|
/**
|
|
118
155
|
* In-memory registry of tools by name. Used by the engine's tool runtime
|
|
119
156
|
* to dispatch tool calls. Re-registering the same name throws so typos
|
|
@@ -130,4 +167,4 @@ declare class ToolRegistry {
|
|
|
130
167
|
count(): number;
|
|
131
168
|
}
|
|
132
169
|
|
|
133
|
-
export { type ToolResult as T, ToolRegistry as a, type Tool as b, type ToolContext as c,
|
|
170
|
+
export { type ToolResult as T, ToolRegistry as a, type Tool as b, type ToolContext as c, defineGatedTool as d, defineTool as e };
|
|
@@ -96,6 +96,20 @@ interface Tool<TSchema extends z.ZodTypeAny = z.ZodTypeAny> {
|
|
|
96
96
|
* Do not set this manually.
|
|
97
97
|
*/
|
|
98
98
|
readonly isCapabilityStub?: boolean;
|
|
99
|
+
/**
|
|
100
|
+
* Plan 043 — when true, the engine pauses the run as soon as the
|
|
101
|
+
* model calls this tool instead of dispatching `execute()`. The
|
|
102
|
+
* pending tool call (id, name, input) is captured in the snapshot;
|
|
103
|
+
* resume via `engine.resumeAsync({toolResult: {...}})` injects a
|
|
104
|
+
* synthetic `tool_result` message and continues the same loop with
|
|
105
|
+
* full transcript intact.
|
|
106
|
+
*
|
|
107
|
+
* Pauses emit `pauseReason: 'awaiting_tool_result'`.
|
|
108
|
+
*
|
|
109
|
+
* `execute` is never invoked on a gated tool — `defineGatedTool()`
|
|
110
|
+
* fills in a no-op stub so the Tool type stays consistent.
|
|
111
|
+
*/
|
|
112
|
+
readonly gated?: boolean;
|
|
99
113
|
execute(input: z.infer<TSchema>, context: ToolContext): Promise<ToolResult>;
|
|
100
114
|
}
|
|
101
115
|
/**
|
|
@@ -114,6 +128,29 @@ interface Tool<TSchema extends z.ZodTypeAny = z.ZodTypeAny> {
|
|
|
114
128
|
* ```
|
|
115
129
|
*/
|
|
116
130
|
declare function defineTool<TSchema extends z.ZodTypeAny>(tool: Tool<TSchema>): Tool<TSchema>;
|
|
131
|
+
/**
|
|
132
|
+
* Plan 043 — define a tool that pauses the run instead of executing.
|
|
133
|
+
*
|
|
134
|
+
* The engine stops on `tool_use`, persists the pending call in the
|
|
135
|
+
* run snapshot, and waits for `engine.resumeAsync({toolResult: {...}})`
|
|
136
|
+
* to deliver the answer. Use this for human-in-the-loop inputs,
|
|
137
|
+
* out-of-band approvals, or any tool whose result comes from a
|
|
138
|
+
* caller decision rather than in-process computation.
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* ```ts
|
|
142
|
+
* const AskHuman = defineGatedTool({
|
|
143
|
+
* name: 'AskHuman',
|
|
144
|
+
* description: 'Ask a human for information.',
|
|
145
|
+
* inputSchema: z.object({ question: z.string() }),
|
|
146
|
+
* })
|
|
147
|
+
* ```
|
|
148
|
+
*
|
|
149
|
+
* `execute` is filled in with a no-op stub that flags the tool as
|
|
150
|
+
* "should not have been executed" if it's ever called — that
|
|
151
|
+
* indicates a code-path bug where the gated-tool pause was bypassed.
|
|
152
|
+
*/
|
|
153
|
+
declare function defineGatedTool<TSchema extends z.ZodTypeAny>(tool: Omit<Tool<TSchema>, 'execute' | 'gated'>): Tool<TSchema>;
|
|
117
154
|
/**
|
|
118
155
|
* In-memory registry of tools by name. Used by the engine's tool runtime
|
|
119
156
|
* to dispatch tool calls. Re-registering the same name throws so typos
|
|
@@ -130,4 +167,4 @@ declare class ToolRegistry {
|
|
|
130
167
|
count(): number;
|
|
131
168
|
}
|
|
132
169
|
|
|
133
|
-
export { type ToolResult as T, ToolRegistry as a, type Tool as b, type ToolContext as c,
|
|
170
|
+
export { type ToolResult as T, ToolRegistry as a, type Tool as b, type ToolContext as c, defineGatedTool as d, defineTool as e };
|
package/dist/index.cjs
CHANGED
|
@@ -41,6 +41,16 @@ var init_cjs_shims = __esm({
|
|
|
41
41
|
function defineTool(tool) {
|
|
42
42
|
return tool;
|
|
43
43
|
}
|
|
44
|
+
function defineGatedTool(tool) {
|
|
45
|
+
return {
|
|
46
|
+
...tool,
|
|
47
|
+
gated: true,
|
|
48
|
+
execute: async () => ({
|
|
49
|
+
content: `gated tool "${tool.name}" should not have been executed \u2014 the engine was supposed to pause on its invocation (Plan 043). File a bug against la-machina-engine.`,
|
|
50
|
+
isError: true
|
|
51
|
+
})
|
|
52
|
+
};
|
|
53
|
+
}
|
|
44
54
|
var ToolRegistry;
|
|
45
55
|
var init_contract = __esm({
|
|
46
56
|
"src/tools/contract.ts"() {
|
|
@@ -889,6 +899,7 @@ __export(src_exports, {
|
|
|
889
899
|
createSmartMemory: () => createSmartMemory,
|
|
890
900
|
defaultSamplingHandler: () => defaultSamplingHandler,
|
|
891
901
|
defaultToolResultSummarizer: () => defaultToolResultSummarizer,
|
|
902
|
+
defineGatedTool: () => defineGatedTool,
|
|
892
903
|
defineTool: () => defineTool,
|
|
893
904
|
detectRuntime: () => detectRuntime,
|
|
894
905
|
getCoordinatorBasePrompt: () => getCoordinatorBasePrompt,
|
|
@@ -2389,6 +2400,8 @@ var RunSnapshotSchema = import_zod2.z.lazy(
|
|
|
2389
2400
|
"gate_required",
|
|
2390
2401
|
"subagent_gate_required",
|
|
2391
2402
|
"handoff_to_runner",
|
|
2403
|
+
// Plan 043 — caller pauses for a gated tool's human-supplied result.
|
|
2404
|
+
"awaiting_tool_result",
|
|
2392
2405
|
"max_turns",
|
|
2393
2406
|
"explicit",
|
|
2394
2407
|
"timeout"
|
|
@@ -3345,6 +3358,25 @@ async function agentLoop(options) {
|
|
|
3345
3358
|
}
|
|
3346
3359
|
}
|
|
3347
3360
|
}
|
|
3361
|
+
for (const call of toolCallsToDispatch) {
|
|
3362
|
+
const tool = options.registry?.get(call.name);
|
|
3363
|
+
if (tool?.gated === true) {
|
|
3364
|
+
const paused = await pauseHere({
|
|
3365
|
+
ctx,
|
|
3366
|
+
transcript,
|
|
3367
|
+
reason: "awaiting_tool_result",
|
|
3368
|
+
pendingToolCall: {
|
|
3369
|
+
toolName: call.name,
|
|
3370
|
+
toolUseId: call.id,
|
|
3371
|
+
input: call.input,
|
|
3372
|
+
calledAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3373
|
+
},
|
|
3374
|
+
storage: options.storage,
|
|
3375
|
+
subagentRegistry: options.subagentRegistry
|
|
3376
|
+
});
|
|
3377
|
+
return paused;
|
|
3378
|
+
}
|
|
3379
|
+
}
|
|
3348
3380
|
if (options.handoffToRunner === true) {
|
|
3349
3381
|
for (const call of toolCallsToDispatch) {
|
|
3350
3382
|
const tool = options.registry?.get(call.name);
|
|
@@ -10883,7 +10915,25 @@ var Engine = class {
|
|
|
10883
10915
|
});
|
|
10884
10916
|
if (snapshot.pendingToolCall) {
|
|
10885
10917
|
const pending = snapshot.pendingToolCall;
|
|
10886
|
-
if (
|
|
10918
|
+
if (snapshot.pauseReason === "awaiting_tool_result") {
|
|
10919
|
+
if (options.toolResult === void 0) {
|
|
10920
|
+
throw new EngineError(
|
|
10921
|
+
"ERR_GATED_RESUME_NO_RESULT",
|
|
10922
|
+
"resume of `awaiting_tool_result` pause requires `toolResult` in ResumeOptions"
|
|
10923
|
+
);
|
|
10924
|
+
}
|
|
10925
|
+
if (options.toolResult.id !== pending.toolUseId) {
|
|
10926
|
+
throw new EngineError(
|
|
10927
|
+
"ERR_GATED_RESUME_ID_MISMATCH",
|
|
10928
|
+
`toolResult.id "${options.toolResult.id}" does not match pending toolUseId "${pending.toolUseId}" \u2014 resume aborted`
|
|
10929
|
+
);
|
|
10930
|
+
}
|
|
10931
|
+
await ctx.addToolResult(
|
|
10932
|
+
pending.toolUseId,
|
|
10933
|
+
options.toolResult.content,
|
|
10934
|
+
options.toolResult.isError ?? false
|
|
10935
|
+
);
|
|
10936
|
+
} else if (options.gateAnswer !== void 0) {
|
|
10887
10937
|
const answer = typeof options.gateAnswer === "string" ? options.gateAnswer : JSON.stringify(options.gateAnswer);
|
|
10888
10938
|
await ctx.addToolResult(pending.toolUseId, answer, false);
|
|
10889
10939
|
} else {
|
|
@@ -12255,6 +12305,7 @@ function resolveApiKey(config) {
|
|
|
12255
12305
|
createSmartMemory,
|
|
12256
12306
|
defaultSamplingHandler,
|
|
12257
12307
|
defaultToolResultSummarizer,
|
|
12308
|
+
defineGatedTool,
|
|
12258
12309
|
defineTool,
|
|
12259
12310
|
detectRuntime,
|
|
12260
12311
|
getCoordinatorBasePrompt,
|