stable-harness 0.0.41 → 0.0.44
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 -1
- package/docs/tooling/0.1.0-bettercall-tool-quality.zh.md +3 -3
- package/node_modules/@stable-harness/adapter-deepagents/dist/src/internal/builtin/task-inventory.js +1 -1
- package/node_modules/@stable-harness/adapter-deepagents/dist/src/internal/builtin-call-repair.js +1 -1
- package/node_modules/@stable-harness/adapter-deepagents/package.json +1 -1
- package/node_modules/@stable-harness/core/dist/runtime/selection-repair.d.ts +14 -4
- package/node_modules/@stable-harness/core/dist/runtime/selection-repair.js +1 -1
- package/node_modules/@stable-harness/core/package.json +0 -1
- package/node_modules/@stable-harness/tool-gateway/dist/src/argument-guard.d.ts +1 -1
- package/node_modules/@stable-harness/tool-gateway/dist/src/argument-guard.js +1 -1
- package/node_modules/@stable-harness/tool-gateway/dist/src/schema-validation.js +1 -1
- package/node_modules/@stable-harness/tool-gateway/package.json +1 -1
- package/package.json +2 -2
- package/packages/adapter-deepagents/dist/src/internal/builtin/task-inventory.js +1 -1
- package/packages/adapter-deepagents/dist/src/internal/builtin-call-repair.js +1 -1
- package/packages/adapter-deepagents/package.json +1 -1
- package/packages/core/dist/runtime/selection-repair.d.ts +14 -4
- package/packages/core/dist/runtime/selection-repair.js +1 -1
- package/packages/core/package.json +0 -1
- package/packages/tool-gateway/dist/src/argument-guard.d.ts +1 -1
- package/packages/tool-gateway/dist/src/argument-guard.js +1 -1
- package/packages/tool-gateway/dist/src/schema-validation.js +1 -1
- package/packages/tool-gateway/package.json +1 -1
package/README.md
CHANGED
|
@@ -199,7 +199,7 @@ semantics.
|
|
|
199
199
|
|
|
200
200
|
## Tool Reliability
|
|
201
201
|
|
|
202
|
-
Stable Harness uses `@
|
|
202
|
+
Stable Harness uses `@easynet/better-call` at the tool-gateway boundary. The
|
|
203
203
|
default CLI path configures repair mode for registered tools, so malformed or
|
|
204
204
|
near-miss tool calls can be repaired before execution while agent inventory,
|
|
205
205
|
schema validation, semantic validators, and governance policy still define what
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# BetterCall Tool Call Quality
|
|
2
2
|
|
|
3
|
-
本文说明 `stable-harness` 如何通过 `@
|
|
3
|
+
本文说明 `stable-harness` 如何通过 `@easynet/better-call` 提升 tool call 质量。
|
|
4
4
|
|
|
5
5
|
核心原则:
|
|
6
6
|
|
|
@@ -180,7 +180,7 @@ sequenceDiagram
|
|
|
180
180
|
|
|
181
181
|
## Sequence: BetterCall repairModel 模式
|
|
182
182
|
|
|
183
|
-
`@
|
|
183
|
+
`@easynet/better-call` package 本身支持:
|
|
184
184
|
|
|
185
185
|
```ts
|
|
186
186
|
const tools = betterTools([searchTool, calculatorTool], { repairModel });
|
|
@@ -228,4 +228,4 @@ sequenceDiagram
|
|
|
228
228
|
- `npm run check`
|
|
229
229
|
- `npm test`:74/74 通过
|
|
230
230
|
- `npm run check:rules`
|
|
231
|
-
- BetterCall package 已发布到 npm:`@
|
|
231
|
+
- BetterCall package 已发布到 npm:`@easynet/better-call@0.1.60`
|
package/node_modules/@stable-harness/adapter-deepagents/dist/src/internal/builtin/task-inventory.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{repairCallSelection as e}from"@
|
|
1
|
+
import{repairCallSelection as e}from"@easynet/better-call";import{ToolMessage as t}from"@langchain/core/messages";import{normalizeArgsRecord as n}from"../builtin-args.js";export async function repairTaskCall(r,a){const s=function allowedTaskTypes(e){const t=readConfigRecord(e.agent.config,"deepagents");if(!0===t?.generalPurposeAgent)return;const n=readConfigRecord(e.agent.config,"builtinTools")?.modelExposed;return!1===n?[]:Array.isArray(n)?n.includes("task")?e.agent.subagents:[]:0===e.agent.subagents.length?[]:void 0}(r);if(void 0===s)return{request:a};const i=n(a.toolCall?.args),o=function readTaskSubagentType(e){const t=isRecord(e)?e:{};return readString(t.subagent_type)??readString(t.subagentType)}(i);if(o&&s.includes(o))return{request:a};const d=await e({call:{id:o,args:i},candidates:taskCallCandidates(r,s),mode:"repair"});if(d.ok){emitInventoryRepair(r,"repaired",o,d.candidateId,s);const e={...i,...d.args,subagent_type:d.candidateId};return{request:{...a,toolCall:{...a.toolCall,args:e}}}}emitInventoryRepair(r,"blocked",o,void 0,s,d.reason);const l=o?`: ${o}`:"",c=s.length>0?s.join(", "):"none";return{request:a,blocked:new t({tool_call_id:a.toolCall?.id??"stable-harness-task-policy",name:"task",status:"error",content:[`Task delegation target is not in the workspace inventory${l}. Allowed task targets: ${c}.`,"Retry with an allowed target only when that target semantically owns the original user request.","Do not substitute another specialist just to continue the same evidence need; synthesize from already collected evidence when no allowed target is a semantic match."].join(" ")})}}function emitInventoryRepair(e,t,n,r,a,s){e.emit({type:"runtime.adapter.event",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,event:{adapter:"deepagents",phase:"inventory.repair",status:t,diagnostic:{layer:"task",owner:"stable_runtime_policy",originalId:n,repairedId:r,candidateIds:a,reason:s}}})}function taskCallCandidates(e,t){return t.map(t=>({id:t,description:e.workspace.agents.get(t)?.description,schema:{type:"object",properties:{subagent_type:{type:"string"},subagentType:{type:"string"},description:{type:"string"}},required:["description"],additionalProperties:!0}}))}function readConfigRecord(e,t){const n=isRecord(e)?e:{};return isRecord(n[t])?n[t]:void 0}function readString(e){return"string"==typeof e&&e.trim()?e:void 0}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}
|
package/node_modules/@stable-harness/adapter-deepagents/dist/src/internal/builtin-call-repair.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{repairCallSelection as t}from"@
|
|
1
|
+
import{repairCallSelection as t}from"@easynet/better-call";import{normalizeArgsRecord as e,normalizeExecuteArgs as r,normalizeFilesystemArgs as o,normalizeWriteTodosArgs as i,shallowEqualRecord as s}from"./builtin-args.js";const a=new Set(["ls","read_file","write_file","edit_file","glob","grep"]);export async function repairBuiltinToolRequest(e){const r=normalizeBuiltinArgs(e.toolId,e.request.toolCall?.args,e.workspaceRoot),o=function builtinCandidate(t){const e=n[t];return e?{id:t,description:`DeepAgents builtin tool ${t}`,schema:e}:void 0}(e.toolId);if(!o)return updateRequestArgs(e.request,e.request.toolCall?.args,r);const i=await t({call:{id:e.toolId,args:r},candidates:[o],mode:"repair"}),s=i.ok?normalizeBuiltinArgs(e.toolId,i.args,e.workspaceRoot):r;return updateRequestArgs(e.request,e.request.toolCall?.args,s)}function normalizeBuiltinArgs(t,s,n){return"write_todos"===t?i(s):"read_todos"===t||"task"===t?e(s):"execute"===t?r(s,n):a.has(t)?o(t,s,n):e(s)}function updateRequestArgs(t,e,r){return function isRecord(t){return"object"==typeof t&&null!==t&&!Array.isArray(t)}(e)&&s(e,r)?t:{...t,toolCall:{...t.toolCall,args:r}}}const n={write_todos:objectSchema({todos:{type:"array",items:{type:"object",additionalProperties:!0}}}),read_todos:objectSchema({}),task:objectSchema({subagent_type:{type:"string"},subagentType:{type:"string"},description:{type:"string"},task:{type:"string"}}),execute:objectSchema({command:{type:"string"},cwd:{type:"string"},timeoutMs:{type:"number"}}),ls:objectSchema({path:{type:"string"}}),read_file:objectSchema({path:{type:"string"},file_path:{type:"string"}}),write_file:objectSchema({path:{type:"string"},file_path:{type:"string"},content:{type:"string"}}),edit_file:objectSchema({path:{type:"string"},file_path:{type:"string"},old_string:{type:"string"},new_string:{type:"string"}}),glob:objectSchema({path:{type:"string"},pattern:{type:"string"}}),grep:objectSchema({path:{type:"string"},pattern:{type:"string"}})};function objectSchema(t){return{type:"object",properties:t,additionalProperties:!0}}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"main": "dist/src/index.js",
|
|
11
11
|
"types": "dist/src/index.d.ts",
|
|
12
12
|
"peerDependencies": {
|
|
13
|
-
"@
|
|
13
|
+
"@easynet/better-call": "^0.1.60",
|
|
14
14
|
"@langchain/core": "^1.1.43",
|
|
15
15
|
"@langchain/ollama": "^1.2.7",
|
|
16
16
|
"@langchain/openai": "^1.4.5",
|
|
@@ -1,13 +1,23 @@
|
|
|
1
|
-
import type { CallCandidate, CallSelectionDiagnostics } from "@botbotgo/better-call";
|
|
2
1
|
import type { RuntimeEvent, RuntimeInventoryRepairDiagnostic, RuntimeInventoryRepairLayer } from "./events.js";
|
|
2
|
+
export type RuntimeSelectionCandidate = {
|
|
3
|
+
id: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
metadata?: Record<string, unknown>;
|
|
6
|
+
};
|
|
7
|
+
export type RuntimeSelectionDiagnostics = {
|
|
8
|
+
originalId: string;
|
|
9
|
+
repairedId?: string;
|
|
10
|
+
matchSource?: string;
|
|
11
|
+
confidence?: number;
|
|
12
|
+
};
|
|
3
13
|
export type RuntimeSelectionRepairResult = {
|
|
4
14
|
ok: true;
|
|
5
15
|
id: string;
|
|
6
|
-
diagnostics?:
|
|
16
|
+
diagnostics?: RuntimeSelectionDiagnostics;
|
|
7
17
|
} | {
|
|
8
18
|
ok: false;
|
|
9
19
|
reason: string;
|
|
10
|
-
diagnostics?:
|
|
20
|
+
diagnostics?: RuntimeSelectionDiagnostics;
|
|
11
21
|
};
|
|
12
22
|
export type RuntimeSelectionRepairTrace = {
|
|
13
23
|
layer: RuntimeInventoryRepairLayer;
|
|
@@ -19,6 +29,6 @@ export type RuntimeSelectionRepairTrace = {
|
|
|
19
29
|
};
|
|
20
30
|
export declare function repairRuntimeSelection(input: {
|
|
21
31
|
id: string;
|
|
22
|
-
candidates:
|
|
32
|
+
candidates: RuntimeSelectionCandidate[];
|
|
23
33
|
trace?: RuntimeSelectionRepairTrace;
|
|
24
34
|
}): Promise<RuntimeSelectionRepairResult>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
export async function repairRuntimeSelection(e){const i=function repairSelection(e,i){const n=normalizeSelectionId(e),t=i.filter(e=>normalizeSelectionId(e.id)===n);if(1===t.length){const i=t[0];return{ok:!0,id:i.id,diagnostics:{originalId:e,repairedId:i.id,matchSource:i.id===e?"exact":"normalized_id",confidence:i.id===e?1:.95}}}return{ok:!1,reason:t.length>1?"ambiguous":"no_candidate",diagnostics:{originalId:e}}}(e.id,e.candidates);return i.ok?(emitSelectionRepair(e,"repaired",i.id,i.diagnostics),i):(emitSelectionRepair(e,"blocked",void 0,i.diagnostics,i.reason),i)}function emitSelectionRepair(e,i,n,t,o){const r=e.trace;r&&r.emit({type:"runtime.inventory.repair",requestId:r.requestId,sessionId:r.sessionId,agentId:r.agentId,status:i,diagnostic:{layer:r.layer,owner:r.owner,originalId:t?.originalId??e.id,repairedId:t?.repairedId??n,candidateIds:e.candidates.map(e=>e.id),reason:o,matchSource:t?.matchSource,confidence:t?.confidence}})}function normalizeSelectionId(e){return e.trim().toLowerCase().replace(/[^a-z0-9]+/gu,"")}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BetterToolLike, RepairFunction, RepairModelLike, RepairPolicy } from "@
|
|
1
|
+
import type { BetterToolLike, RepairFunction, RepairModelLike, RepairPolicy } from "@easynet/better-call";
|
|
2
2
|
import type { ToolArgumentGuard, ToolArgumentIssue, ToolGatewayContext, ToolGatewayTool } from "./types.js";
|
|
3
3
|
type ToolGatewayRuntimeTool = ToolGatewayTool & {
|
|
4
4
|
validationTool?: BetterToolLike;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{BetterToolValidationError as o,betterTools as t,defaultRepair as e,reliableToolCalls as a}from"@
|
|
1
|
+
import{BetterToolValidationError as o,betterTools as t,defaultRepair as e,reliableToolCalls as a}from"@easynet/better-call";import{isRecord as r,validateWithZodSchema as i}from"./schema-validation.js";export class ToolArgumentValidationError extends Error{toolId;issues;constructor(o,t){super(`Tool argument validation failed for ${o}: ${t.map(o=>`${o.path} ${o.message}`).join("; ")}`),this.toolId=o,this.issues=t,this.name="ToolArgumentValidationError"}}export function createDefaultArgumentGuard(t={}){return{async validate(e){const a=e.tool.validateArgs?await e.tool.validateArgs({args:e.args,context:e.context}):{action:"allow",args:e.args};if("reject"===a.action)return a;const r=await async function validateWithBetterCall(t,e,a){const r=i(t.schema,e);if(void 0===t.schema)return r??{action:"allow",args:e};const l=await async function invokeBetterCallValidation(t,e,a){try{return{action:"allow",args:await createBetterCallValidationTool(t,a).invoke(e)}}catch(t){if(t instanceof o)return{action:"reject",reason:"BetterCall validation failed",issues:t.issues.map(toToolArgumentIssue)};throw t}}(t,"allow"===r?.action?r.args:e,a);return r?"allow"===r.action?l:"reject"===l.action?r:i(t.schema,l.args)||l:l}(e.tool,a.args,t.betterCall);return"reject"===r.action?r:"repair"===a.action?{...a,args:r.args}:r}}}export function assertToolArguments(o,t,e,a){return Promise.resolve(a.validate({tool:o,args:t,context:e})).then(t=>{if("reject"===t.action)throw new ToolArgumentValidationError(o.id,t.issues);return t.args})}export function prepareBetterCallTools(o,e){const a=t(o.map(toBetterCallTool),toBetterToolsOptions(e));return o.map((o,t)=>({...o,validationTool:a[t]}))}export async function repairBetterCallToolSelection(o){const t=function resolveRepair(o){return o?.repair??(o?.repairModel?e(o.repairModel):void 0)}(o.options);if(!t||0===o.tools.length)return;const i=await a({userInput:JSON.stringify({tool:o.toolId,args:o.args}),tools:o.tools.map(toToolDefinition),calls:[{tool:o.toolId,args:(l=o.args,r(l)?l:{input:l})}],repair:t,repairPolicy:o.options?.repairPolicy??{allowCoercion:!0,allowClamp:!0,allowArrayStringSplit:!0,allowModelRepair:!0},mode:o.options?.mode??"repair"});var l;const n=i.ok?i.calls.find(t=>o.tools.some(o=>o.id===t.tool)):void 0;return n?{toolId:n.tool,args:n.args}:void 0}function createBetterCallValidationTool(o,e){return o.validationTool??t([toBetterCallTool(o)],toBetterToolsOptions(e))[0]}function toBetterCallTool(o){return{name:o.id,description:o.description,schema:o.schema,invoke:o=>o}}function toToolDefinition(o){return{name:o.id,description:o.description,schema:o.schema}}function toToolArgumentIssue(o){return{path:o.path.replace(/^\$\.calls\[\d+\]\.args/u,"$"),message:o.message,expected:void 0===o.expected?void 0:String(o.expected),actual:o.actual}}function toBetterToolsOptions(o){return{mode:o?.mode??"repair",repair:o?.repair,repairModel:o?.repairModel,repairPolicy:o?.repairPolicy??{allowCoercion:!0,allowClamp:!0,allowArrayStringSplit:!0,allowModelRepair:!0}}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{normalizeArgsBySchema as e}from"@
|
|
1
|
+
import{normalizeArgsBySchema as e}from"@easynet/better-call";export function validateWithZodSchema(a,r){return isZodLike(a)?toZodGuardResult(a.safeParse(r??{})):function isZodShape(e){return isRecord(e)&&Object.values(e).length>0&&Object.values(e).every(isZodLike)}(a)?function validateWithZodShape(a,r){const t=function normalizeZodShapeArgs(a,r){const t=isRecord(r)?r:{};return e(a,t,{allowCoercion:!0,allowClamp:!0,allowArrayStringSplit:!0}).args}(a,r),s={},o=[];for(const[e,r]of Object.entries(a)){const a=r.safeParse(t[e]);a.success?void 0!==a.data&&(s[e]=a.data):o.push(...a.error.issues.map(a=>({...a,path:[e,...a.path]})))}return o.length>0?toZodGuardResult({success:!1,error:{issues:o}}):{action:"allow",args:s}}(a,r):void 0}export function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function toZodGuardResult(e){return e.success?{action:"allow",args:e.data}:{action:"reject",reason:"Zod schema validation failed",issues:e.error.issues.map(e=>{return{path:(a=e.path,a.length>0?`$.${a.map(String).join(".")}`:"$"),message:e.message,expected:"schema"};var a})}}function isZodLike(e){return isRecord(e)&&"function"==typeof e.safeParse}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stable-harness",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.44",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Stable application runtime and operator control plane for agent workspaces.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
"packages/*"
|
|
79
79
|
],
|
|
80
80
|
"dependencies": {
|
|
81
|
-
"@
|
|
81
|
+
"@easynet/better-call": "^0.1.60",
|
|
82
82
|
"@langchain/core": "^1.1.43",
|
|
83
83
|
"@langchain/langgraph": "^1.3.0",
|
|
84
84
|
"@langchain/langgraph-api": "^1.2.1",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{repairCallSelection as e}from"@
|
|
1
|
+
import{repairCallSelection as e}from"@easynet/better-call";import{ToolMessage as t}from"@langchain/core/messages";import{normalizeArgsRecord as n}from"../builtin-args.js";export async function repairTaskCall(r,a){const s=function allowedTaskTypes(e){const t=readConfigRecord(e.agent.config,"deepagents");if(!0===t?.generalPurposeAgent)return;const n=readConfigRecord(e.agent.config,"builtinTools")?.modelExposed;return!1===n?[]:Array.isArray(n)?n.includes("task")?e.agent.subagents:[]:0===e.agent.subagents.length?[]:void 0}(r);if(void 0===s)return{request:a};const i=n(a.toolCall?.args),o=function readTaskSubagentType(e){const t=isRecord(e)?e:{};return readString(t.subagent_type)??readString(t.subagentType)}(i);if(o&&s.includes(o))return{request:a};const d=await e({call:{id:o,args:i},candidates:taskCallCandidates(r,s),mode:"repair"});if(d.ok){emitInventoryRepair(r,"repaired",o,d.candidateId,s);const e={...i,...d.args,subagent_type:d.candidateId};return{request:{...a,toolCall:{...a.toolCall,args:e}}}}emitInventoryRepair(r,"blocked",o,void 0,s,d.reason);const l=o?`: ${o}`:"",c=s.length>0?s.join(", "):"none";return{request:a,blocked:new t({tool_call_id:a.toolCall?.id??"stable-harness-task-policy",name:"task",status:"error",content:[`Task delegation target is not in the workspace inventory${l}. Allowed task targets: ${c}.`,"Retry with an allowed target only when that target semantically owns the original user request.","Do not substitute another specialist just to continue the same evidence need; synthesize from already collected evidence when no allowed target is a semantic match."].join(" ")})}}function emitInventoryRepair(e,t,n,r,a,s){e.emit({type:"runtime.adapter.event",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,event:{adapter:"deepagents",phase:"inventory.repair",status:t,diagnostic:{layer:"task",owner:"stable_runtime_policy",originalId:n,repairedId:r,candidateIds:a,reason:s}}})}function taskCallCandidates(e,t){return t.map(t=>({id:t,description:e.workspace.agents.get(t)?.description,schema:{type:"object",properties:{subagent_type:{type:"string"},subagentType:{type:"string"},description:{type:"string"}},required:["description"],additionalProperties:!0}}))}function readConfigRecord(e,t){const n=isRecord(e)?e:{};return isRecord(n[t])?n[t]:void 0}function readString(e){return"string"==typeof e&&e.trim()?e:void 0}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{repairCallSelection as t}from"@
|
|
1
|
+
import{repairCallSelection as t}from"@easynet/better-call";import{normalizeArgsRecord as e,normalizeExecuteArgs as r,normalizeFilesystemArgs as o,normalizeWriteTodosArgs as i,shallowEqualRecord as s}from"./builtin-args.js";const a=new Set(["ls","read_file","write_file","edit_file","glob","grep"]);export async function repairBuiltinToolRequest(e){const r=normalizeBuiltinArgs(e.toolId,e.request.toolCall?.args,e.workspaceRoot),o=function builtinCandidate(t){const e=n[t];return e?{id:t,description:`DeepAgents builtin tool ${t}`,schema:e}:void 0}(e.toolId);if(!o)return updateRequestArgs(e.request,e.request.toolCall?.args,r);const i=await t({call:{id:e.toolId,args:r},candidates:[o],mode:"repair"}),s=i.ok?normalizeBuiltinArgs(e.toolId,i.args,e.workspaceRoot):r;return updateRequestArgs(e.request,e.request.toolCall?.args,s)}function normalizeBuiltinArgs(t,s,n){return"write_todos"===t?i(s):"read_todos"===t||"task"===t?e(s):"execute"===t?r(s,n):a.has(t)?o(t,s,n):e(s)}function updateRequestArgs(t,e,r){return function isRecord(t){return"object"==typeof t&&null!==t&&!Array.isArray(t)}(e)&&s(e,r)?t:{...t,toolCall:{...t.toolCall,args:r}}}const n={write_todos:objectSchema({todos:{type:"array",items:{type:"object",additionalProperties:!0}}}),read_todos:objectSchema({}),task:objectSchema({subagent_type:{type:"string"},subagentType:{type:"string"},description:{type:"string"},task:{type:"string"}}),execute:objectSchema({command:{type:"string"},cwd:{type:"string"},timeoutMs:{type:"number"}}),ls:objectSchema({path:{type:"string"}}),read_file:objectSchema({path:{type:"string"},file_path:{type:"string"}}),write_file:objectSchema({path:{type:"string"},file_path:{type:"string"},content:{type:"string"}}),edit_file:objectSchema({path:{type:"string"},file_path:{type:"string"},old_string:{type:"string"},new_string:{type:"string"}}),glob:objectSchema({path:{type:"string"},pattern:{type:"string"}}),grep:objectSchema({path:{type:"string"},pattern:{type:"string"}})};function objectSchema(t){return{type:"object",properties:t,additionalProperties:!0}}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"main": "dist/src/index.js",
|
|
11
11
|
"types": "dist/src/index.d.ts",
|
|
12
12
|
"peerDependencies": {
|
|
13
|
-
"@
|
|
13
|
+
"@easynet/better-call": "^0.1.60",
|
|
14
14
|
"@langchain/core": "^1.1.43",
|
|
15
15
|
"@langchain/ollama": "^1.2.7",
|
|
16
16
|
"@langchain/openai": "^1.4.5",
|
|
@@ -1,13 +1,23 @@
|
|
|
1
|
-
import type { CallCandidate, CallSelectionDiagnostics } from "@botbotgo/better-call";
|
|
2
1
|
import type { RuntimeEvent, RuntimeInventoryRepairDiagnostic, RuntimeInventoryRepairLayer } from "./events.js";
|
|
2
|
+
export type RuntimeSelectionCandidate = {
|
|
3
|
+
id: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
metadata?: Record<string, unknown>;
|
|
6
|
+
};
|
|
7
|
+
export type RuntimeSelectionDiagnostics = {
|
|
8
|
+
originalId: string;
|
|
9
|
+
repairedId?: string;
|
|
10
|
+
matchSource?: string;
|
|
11
|
+
confidence?: number;
|
|
12
|
+
};
|
|
3
13
|
export type RuntimeSelectionRepairResult = {
|
|
4
14
|
ok: true;
|
|
5
15
|
id: string;
|
|
6
|
-
diagnostics?:
|
|
16
|
+
diagnostics?: RuntimeSelectionDiagnostics;
|
|
7
17
|
} | {
|
|
8
18
|
ok: false;
|
|
9
19
|
reason: string;
|
|
10
|
-
diagnostics?:
|
|
20
|
+
diagnostics?: RuntimeSelectionDiagnostics;
|
|
11
21
|
};
|
|
12
22
|
export type RuntimeSelectionRepairTrace = {
|
|
13
23
|
layer: RuntimeInventoryRepairLayer;
|
|
@@ -19,6 +29,6 @@ export type RuntimeSelectionRepairTrace = {
|
|
|
19
29
|
};
|
|
20
30
|
export declare function repairRuntimeSelection(input: {
|
|
21
31
|
id: string;
|
|
22
|
-
candidates:
|
|
32
|
+
candidates: RuntimeSelectionCandidate[];
|
|
23
33
|
trace?: RuntimeSelectionRepairTrace;
|
|
24
34
|
}): Promise<RuntimeSelectionRepairResult>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
export async function repairRuntimeSelection(e){const i=function repairSelection(e,i){const n=normalizeSelectionId(e),t=i.filter(e=>normalizeSelectionId(e.id)===n);if(1===t.length){const i=t[0];return{ok:!0,id:i.id,diagnostics:{originalId:e,repairedId:i.id,matchSource:i.id===e?"exact":"normalized_id",confidence:i.id===e?1:.95}}}return{ok:!1,reason:t.length>1?"ambiguous":"no_candidate",diagnostics:{originalId:e}}}(e.id,e.candidates);return i.ok?(emitSelectionRepair(e,"repaired",i.id,i.diagnostics),i):(emitSelectionRepair(e,"blocked",void 0,i.diagnostics,i.reason),i)}function emitSelectionRepair(e,i,n,t,o){const r=e.trace;r&&r.emit({type:"runtime.inventory.repair",requestId:r.requestId,sessionId:r.sessionId,agentId:r.agentId,status:i,diagnostic:{layer:r.layer,owner:r.owner,originalId:t?.originalId??e.id,repairedId:t?.repairedId??n,candidateIds:e.candidates.map(e=>e.id),reason:o,matchSource:t?.matchSource,confidence:t?.confidence}})}function normalizeSelectionId(e){return e.trim().toLowerCase().replace(/[^a-z0-9]+/gu,"")}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BetterToolLike, RepairFunction, RepairModelLike, RepairPolicy } from "@
|
|
1
|
+
import type { BetterToolLike, RepairFunction, RepairModelLike, RepairPolicy } from "@easynet/better-call";
|
|
2
2
|
import type { ToolArgumentGuard, ToolArgumentIssue, ToolGatewayContext, ToolGatewayTool } from "./types.js";
|
|
3
3
|
type ToolGatewayRuntimeTool = ToolGatewayTool & {
|
|
4
4
|
validationTool?: BetterToolLike;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{BetterToolValidationError as o,betterTools as t,defaultRepair as e,reliableToolCalls as a}from"@
|
|
1
|
+
import{BetterToolValidationError as o,betterTools as t,defaultRepair as e,reliableToolCalls as a}from"@easynet/better-call";import{isRecord as r,validateWithZodSchema as i}from"./schema-validation.js";export class ToolArgumentValidationError extends Error{toolId;issues;constructor(o,t){super(`Tool argument validation failed for ${o}: ${t.map(o=>`${o.path} ${o.message}`).join("; ")}`),this.toolId=o,this.issues=t,this.name="ToolArgumentValidationError"}}export function createDefaultArgumentGuard(t={}){return{async validate(e){const a=e.tool.validateArgs?await e.tool.validateArgs({args:e.args,context:e.context}):{action:"allow",args:e.args};if("reject"===a.action)return a;const r=await async function validateWithBetterCall(t,e,a){const r=i(t.schema,e);if(void 0===t.schema)return r??{action:"allow",args:e};const l=await async function invokeBetterCallValidation(t,e,a){try{return{action:"allow",args:await createBetterCallValidationTool(t,a).invoke(e)}}catch(t){if(t instanceof o)return{action:"reject",reason:"BetterCall validation failed",issues:t.issues.map(toToolArgumentIssue)};throw t}}(t,"allow"===r?.action?r.args:e,a);return r?"allow"===r.action?l:"reject"===l.action?r:i(t.schema,l.args)||l:l}(e.tool,a.args,t.betterCall);return"reject"===r.action?r:"repair"===a.action?{...a,args:r.args}:r}}}export function assertToolArguments(o,t,e,a){return Promise.resolve(a.validate({tool:o,args:t,context:e})).then(t=>{if("reject"===t.action)throw new ToolArgumentValidationError(o.id,t.issues);return t.args})}export function prepareBetterCallTools(o,e){const a=t(o.map(toBetterCallTool),toBetterToolsOptions(e));return o.map((o,t)=>({...o,validationTool:a[t]}))}export async function repairBetterCallToolSelection(o){const t=function resolveRepair(o){return o?.repair??(o?.repairModel?e(o.repairModel):void 0)}(o.options);if(!t||0===o.tools.length)return;const i=await a({userInput:JSON.stringify({tool:o.toolId,args:o.args}),tools:o.tools.map(toToolDefinition),calls:[{tool:o.toolId,args:(l=o.args,r(l)?l:{input:l})}],repair:t,repairPolicy:o.options?.repairPolicy??{allowCoercion:!0,allowClamp:!0,allowArrayStringSplit:!0,allowModelRepair:!0},mode:o.options?.mode??"repair"});var l;const n=i.ok?i.calls.find(t=>o.tools.some(o=>o.id===t.tool)):void 0;return n?{toolId:n.tool,args:n.args}:void 0}function createBetterCallValidationTool(o,e){return o.validationTool??t([toBetterCallTool(o)],toBetterToolsOptions(e))[0]}function toBetterCallTool(o){return{name:o.id,description:o.description,schema:o.schema,invoke:o=>o}}function toToolDefinition(o){return{name:o.id,description:o.description,schema:o.schema}}function toToolArgumentIssue(o){return{path:o.path.replace(/^\$\.calls\[\d+\]\.args/u,"$"),message:o.message,expected:void 0===o.expected?void 0:String(o.expected),actual:o.actual}}function toBetterToolsOptions(o){return{mode:o?.mode??"repair",repair:o?.repair,repairModel:o?.repairModel,repairPolicy:o?.repairPolicy??{allowCoercion:!0,allowClamp:!0,allowArrayStringSplit:!0,allowModelRepair:!0}}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{normalizeArgsBySchema as e}from"@
|
|
1
|
+
import{normalizeArgsBySchema as e}from"@easynet/better-call";export function validateWithZodSchema(a,r){return isZodLike(a)?toZodGuardResult(a.safeParse(r??{})):function isZodShape(e){return isRecord(e)&&Object.values(e).length>0&&Object.values(e).every(isZodLike)}(a)?function validateWithZodShape(a,r){const t=function normalizeZodShapeArgs(a,r){const t=isRecord(r)?r:{};return e(a,t,{allowCoercion:!0,allowClamp:!0,allowArrayStringSplit:!0}).args}(a,r),s={},o=[];for(const[e,r]of Object.entries(a)){const a=r.safeParse(t[e]);a.success?void 0!==a.data&&(s[e]=a.data):o.push(...a.error.issues.map(a=>({...a,path:[e,...a.path]})))}return o.length>0?toZodGuardResult({success:!1,error:{issues:o}}):{action:"allow",args:s}}(a,r):void 0}export function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function toZodGuardResult(e){return e.success?{action:"allow",args:e.data}:{action:"reject",reason:"Zod schema validation failed",issues:e.error.issues.map(e=>{return{path:(a=e.path,a.length>0?`$.${a.map(String).join(".")}`:"$"),message:e.message,expected:"schema"};var a})}}function isZodLike(e){return isRecord(e)&&"function"==typeof e.safeParse}
|