stable-harness 0.0.3 → 0.0.5
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 +189 -9
- package/dist/cli.js +1 -1
- package/dist/compat/agent-harness.js +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/dist/runtime/compat/agent-harness-compat-runner.js +1 -1
- package/dist/runtime/compat/json.js +1 -1
- package/dist/runtime/compat/presentation.js +1 -1
- package/dist/runtime/compat/prompts.js +1 -1
- package/dist/runtime/model/ollama.js +1 -1
- package/dist/runtime/skills/skill-metadata.js +1 -1
- package/dist/workspace/compile.js +1 -1
- package/package.json +4 -3
- package/packages/adapter-deepagents/dist/src/adapter.js +1 -1
- package/packages/adapter-deepagents/dist/src/internal/builtin-args.d.ts +4 -0
- package/packages/adapter-deepagents/dist/src/internal/builtin-args.js +1 -0
- package/packages/adapter-deepagents/dist/src/internal/builtin-tool-policy.d.ts +9 -4
- package/packages/adapter-deepagents/dist/src/internal/builtin-tool-policy.js +1 -1
- package/packages/adapter-deepagents/dist/src/internal/gateway-tools.d.ts +29 -1
- package/packages/adapter-deepagents/dist/src/internal/gateway-tools.js +1 -1
- package/packages/adapter-deepagents/dist/src/internal/messages.js +1 -1
- package/packages/adapter-deepagents/dist/src/internal/raw-tool-call-parser.d.ts +12 -0
- package/packages/adapter-deepagents/dist/src/internal/raw-tool-call-parser.js +1 -0
- package/packages/adapter-deepagents/dist/src/internal/skill-file-policy.d.ts +10 -0
- package/packages/adapter-deepagents/dist/src/internal/skill-file-policy.js +1 -0
- package/packages/adapter-deepagents/dist/src/internal/stream-events.js +1 -1
- package/packages/adapter-deepagents/dist/src/internal/tool-repeat-visibility.d.ts +4 -0
- package/packages/adapter-deepagents/dist/src/internal/tool-repeat-visibility.js +1 -0
- package/packages/adapter-deepagents/dist/src/internal/trace-projection.d.ts +1 -1
- package/packages/adapter-deepagents/dist/src/internal/trace-projection.js +1 -1
- package/packages/adapter-deepagents/dist/src/memory.js +1 -1
- package/packages/adapter-deepagents/dist/src/model-providers.d.ts +4 -0
- package/packages/adapter-deepagents/dist/src/model-providers.js +1 -0
- package/packages/adapter-deepagents/dist/src/retry-policy.js +1 -1
- package/packages/adapter-deepagents/dist/src/types.d.ts +7 -1
- package/packages/adapter-deepagents/package.json +1 -0
- package/packages/adapter-langgraph/dist/src/graph.js +1 -1
- package/packages/adapter-langgraph/dist/src/runtime.js +1 -1
- package/packages/adapter-langgraph/dist/src/skill-providers.js +1 -1
- package/packages/cli/dist/src/args.d.ts +6 -3
- package/packages/cli/dist/src/args.js +1 -1
- package/packages/cli/dist/src/cli.js +1 -1
- package/packages/cli/dist/src/event-view.d.ts +9 -0
- package/packages/cli/dist/src/event-view.js +1 -0
- package/packages/cli/dist/src/index.d.ts +3 -0
- package/packages/cli/dist/src/index.js +1 -1
- package/packages/cli/dist/src/langgraph-env.d.ts +5 -0
- package/packages/cli/dist/src/langgraph-env.js +1 -0
- package/packages/cli/dist/src/langgraph-official.d.ts +2 -0
- package/packages/cli/dist/src/langgraph-official.js +1 -1
- package/packages/cli/dist/src/memory/lifecycle.d.ts +2 -0
- package/packages/cli/dist/src/memory/lifecycle.js +1 -0
- package/packages/cli/dist/src/memory/providers.d.ts +3 -0
- package/packages/cli/dist/src/memory/providers.js +1 -0
- package/packages/cli/dist/src/output.js +1 -1
- package/packages/cli/dist/src/server.d.ts +2 -0
- package/packages/cli/dist/src/server.js +1 -1
- package/packages/cli/package.json +2 -0
- package/packages/core/dist/evaluations/index.d.ts +18 -0
- package/packages/core/dist/evaluations/index.js +1 -0
- package/packages/core/dist/execution-contract.js +1 -1
- package/packages/core/dist/index.d.ts +3 -0
- package/packages/core/dist/index.js +1 -1
- package/packages/core/dist/memory-plugins/maintenance.js +1 -1
- package/packages/core/dist/memory-plugins/shared.js +1 -1
- package/packages/core/dist/memory-plugins.js +1 -1
- package/packages/core/dist/recovery/tool-call.d.ts +15 -0
- package/packages/core/dist/recovery/tool-call.js +1 -1
- package/packages/core/dist/runtime/completion.js +1 -1
- package/packages/core/dist/runtime/direct-tool-call.js +1 -1
- package/packages/core/dist/runtime/events.d.ts +77 -20
- package/packages/core/dist/runtime/memory.js +1 -1
- package/packages/core/dist/runtime/persistence/artifacts.js +1 -1
- package/packages/core/dist/runtime/persistence/inspection.js +1 -1
- package/packages/core/dist/runtime/persistence/queue.js +1 -1
- package/packages/core/dist/runtime/persistence/stores.js +1 -1
- package/packages/core/dist/runtime/progress-narration.d.ts +33 -0
- package/packages/core/dist/runtime/progress-narration.js +1 -0
- package/packages/core/dist/runtime/tool-gateway.d.ts +5 -0
- package/packages/core/dist/runtime.d.ts +2 -1
- package/packages/core/dist/runtime.js +1 -1
- package/packages/core/dist/spec-driven/config.d.ts +4 -0
- package/packages/core/dist/spec-driven/config.js +1 -0
- package/packages/core/dist/spec-driven/events.d.ts +11 -0
- package/packages/core/dist/spec-driven/events.js +1 -0
- package/packages/core/dist/spec-driven/index.d.ts +4 -0
- package/packages/core/dist/spec-driven/index.js +1 -0
- package/packages/core/dist/spec-driven/lifecycle.d.ts +11 -0
- package/packages/core/dist/spec-driven/lifecycle.js +1 -0
- package/packages/core/dist/spec-driven/types.d.ts +38 -0
- package/packages/core/dist/spec-driven/types.js +1 -0
- package/packages/core/dist/trace.d.ts +1 -1
- package/packages/core/dist/trace.js +1 -1
- package/packages/core/dist/types.d.ts +15 -1
- package/packages/core/dist/workflows/index.js +1 -1
- package/packages/core/dist/workflows/runtime.js +1 -1
- package/packages/core/dist/workspace/types.d.ts +9 -0
- package/packages/governance/dist/src/skill-candidates.js +1 -1
- package/packages/memory/dist/src/langmem-service.js +1 -1
- package/packages/memory/dist/src/maintenance.js +1 -1
- package/packages/memory/dist/src/policy.js +1 -1
- package/packages/memory/dist/src/provider.js +1 -1
- package/packages/memory/dist/src/store.js +1 -1
- package/packages/protocols/dist/src/http-server.js +1 -1
- package/packages/protocols/dist/src/openai-compatible.js +1 -1
- package/packages/protocols/dist/src/openai-payload.js +1 -1
- package/packages/protocols/dist/src/openai-stream.js +1 -1
- package/packages/tool-gateway/dist/src/argument-guard.d.ts +2 -1
- package/packages/tool-gateway/dist/src/argument-guard.js +1 -1
- package/packages/tool-gateway/dist/src/in-memory.js +1 -1
- package/packages/tool-gateway/dist/src/module-loader.js +1 -1
- package/packages/tool-gateway/dist/src/schema-validation.js +1 -1
- package/packages/tool-gateway/dist/src/types.d.ts +3 -0
- package/packages/tool-gateway/package.json +1 -1
- package/packages/workspace-yaml/dist/discovery.js +1 -1
- package/packages/workspace-yaml/dist/documents.js +1 -1
- package/packages/workspace-yaml/dist/evaluations.d.ts +9 -0
- package/packages/workspace-yaml/dist/evaluations.js +1 -0
- package/packages/workspace-yaml/dist/loader.js +1 -1
- package/packages/workspace-yaml/dist/workflows.js +1 -1
package/README.md
CHANGED
|
@@ -1,32 +1,212 @@
|
|
|
1
1
|
# stable-harness
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/stable-harness)
|
|
4
|
+
[](https://www.npmjs.com/package/stable-harness)
|
|
5
|
+
|
|
3
6
|
Stable runtime and operator control plane for agent applications.
|
|
4
7
|
|
|
5
|
-
`stable-harness`
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
`stable-harness` lets a team keep its chosen agent framework while adding the
|
|
9
|
+
production surfaces that real workspaces need: YAML inventory, runtime requests,
|
|
10
|
+
sessions, event traces, artifacts, memory lifecycle, governance hooks, recovery,
|
|
11
|
+
tool repair, and protocol access.
|
|
12
|
+
|
|
13
|
+
It is not another agent execution framework. Upstream frameworks own execution
|
|
14
|
+
semantics. Stable Harness owns the runtime boundary around them.
|
|
15
|
+
|
|
16
|
+
## Why Use It
|
|
17
|
+
|
|
18
|
+
Agent frameworks are good at deciding what an agent should do next. Production
|
|
19
|
+
applications also need a stable layer that can be inspected, governed, resumed,
|
|
20
|
+
replayed, and called through predictable APIs.
|
|
9
21
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
22
|
+
Stable Harness gives you that layer without rewriting the backend:
|
|
23
|
+
|
|
24
|
+
- define agents, tools, models, memory, workflows, and protocol exposure in YAML
|
|
25
|
+
- run the same workspace through CLI, SDK, HTTP, and OpenAI-compatible clients
|
|
26
|
+
- keep DeepAgents and LangGraph behavior upstream-native through thin adapters
|
|
27
|
+
- validate and repair tool calls at the runtime gateway before execution
|
|
28
|
+
- observe upstream tool, planning, delegation, progress, memory, and artifact events
|
|
29
|
+
- keep downstream product logic in the workspace, not inside the framework
|
|
30
|
+
|
|
31
|
+
## Install
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm install stable-harness
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Stable Harness currently targets Node.js `>=24 <25`.
|
|
13
38
|
|
|
14
39
|
## First Run
|
|
15
40
|
|
|
41
|
+
Clone the repo when developing the framework itself:
|
|
42
|
+
|
|
16
43
|
```bash
|
|
44
|
+
git clone git@github.com:botbotgo/stable-harness.git
|
|
45
|
+
cd stable-harness
|
|
17
46
|
npm install
|
|
18
47
|
npm run build
|
|
19
48
|
npm run check:rules
|
|
20
|
-
npm
|
|
49
|
+
npm test
|
|
21
50
|
npm run example:minimal
|
|
22
51
|
```
|
|
23
52
|
|
|
53
|
+
Run an existing Stable Harness workspace:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
stable-harness -w ./examples/minimal-deepagents "hello stable harness"
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Inspect the workspace without running an agent:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
stable-harness -w ./examples/minimal-deepagents
|
|
63
|
+
stable-harness agent render orchestra -w ./examples/minimal-deepagents
|
|
64
|
+
stable-harness workflow render review-shell -w ./examples/minimal-deepagents
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Start the OpenAI-compatible facade:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
stable-harness start -w ./examples/minimal-deepagents --port 8642
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Then point compatible clients at:
|
|
74
|
+
|
|
75
|
+
```text
|
|
76
|
+
http://127.0.0.1:8642/v1
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Embed In An App
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
import { createStableHarnessRuntime } from "stable-harness";
|
|
83
|
+
|
|
84
|
+
const runtime = await createStableHarnessRuntime("/path/to/workspace");
|
|
85
|
+
|
|
86
|
+
const response = await runtime.request({
|
|
87
|
+
input: "Review the current release evidence.",
|
|
88
|
+
agentId: "orchestra",
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
console.log(response.output);
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
The runtime also exposes `subscribe`, `inspect`, `getRun`, `listRequests`,
|
|
95
|
+
`listSessions`, `inspectRequest`, `cancel`, and `stop` so applications can build
|
|
96
|
+
operator workflows around the same execution surface.
|
|
97
|
+
|
|
98
|
+
## Workspace Shape
|
|
99
|
+
|
|
100
|
+
A workspace is a folder with Kubernetes-style YAML documents:
|
|
101
|
+
|
|
102
|
+
```text
|
|
103
|
+
config/
|
|
104
|
+
runtime/workspace.yaml
|
|
105
|
+
agents/orchestra.yaml
|
|
106
|
+
catalogs/models.yaml
|
|
107
|
+
catalogs/tools.yaml
|
|
108
|
+
workflows/review-shell.yaml
|
|
109
|
+
resources/
|
|
110
|
+
tools/
|
|
111
|
+
skills/
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Minimal runtime:
|
|
115
|
+
|
|
116
|
+
```yaml
|
|
117
|
+
apiVersion: stable-harness.dev/v1
|
|
118
|
+
kind: Runtime
|
|
119
|
+
metadata:
|
|
120
|
+
name: app-runtime
|
|
121
|
+
spec:
|
|
122
|
+
routing:
|
|
123
|
+
defaultAgentId: orchestra
|
|
124
|
+
protocols:
|
|
125
|
+
inProcess: true
|
|
126
|
+
openaiCompatible:
|
|
127
|
+
bearerToken: ${env:STABLE_HARNESS_API_KEY}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Minimal agent:
|
|
131
|
+
|
|
132
|
+
```yaml
|
|
133
|
+
apiVersion: stable-harness.dev/v1
|
|
134
|
+
kind: Agent
|
|
135
|
+
metadata:
|
|
136
|
+
name: orchestra
|
|
137
|
+
spec:
|
|
138
|
+
backend: deepagents
|
|
139
|
+
modelRef: local-dev
|
|
140
|
+
systemPrompt: You are a concise workspace agent.
|
|
141
|
+
tools:
|
|
142
|
+
- shell
|
|
143
|
+
subagents:
|
|
144
|
+
- reviewer
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Runtime Boundary
|
|
148
|
+
|
|
149
|
+
```mermaid
|
|
150
|
+
flowchart LR
|
|
151
|
+
Client["CLI / SDK / HTTP / OpenAI-compatible client"]
|
|
152
|
+
Runtime["Stable Harness runtime"]
|
|
153
|
+
Inventory["YAML workspace inventory"]
|
|
154
|
+
Gateway["Tool gateway + repair policy"]
|
|
155
|
+
Adapter["Thin backend adapter"]
|
|
156
|
+
Backend["DeepAgents / LangGraph / future backend"]
|
|
157
|
+
Ops["Events / runs / memory / approvals / artifacts"]
|
|
158
|
+
|
|
159
|
+
Client --> Runtime
|
|
160
|
+
Inventory --> Runtime
|
|
161
|
+
Runtime --> Gateway
|
|
162
|
+
Runtime --> Adapter
|
|
163
|
+
Adapter --> Backend
|
|
164
|
+
Runtime --> Ops
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Stable Harness owns lifecycle, governance, observability, persistence, recovery,
|
|
168
|
+
protocol access, and tool-gateway policy. It does not infer routing from user
|
|
169
|
+
keywords, synthesize upstream planning calls, or rebuild backend-native agent
|
|
170
|
+
semantics.
|
|
171
|
+
|
|
172
|
+
## Current Backends
|
|
173
|
+
|
|
174
|
+
| Backend | Status | Boundary |
|
|
175
|
+
| --- | --- | --- |
|
|
176
|
+
| DeepAgents | Primary adapter | Upstream execution, skills, planning, delegation, and built-ins are passed through; Stable Harness observes and governs the runtime edge. |
|
|
177
|
+
| LangGraph | Runtime and workflow adapter | Stable Harness can compile explicit workflow topology and expose LangGraph-compatible protocol surfaces. |
|
|
178
|
+
| Custom adapters | Supported through SDK | Implement `RuntimeAdapter` and declare the backend in workspace YAML. |
|
|
179
|
+
|
|
180
|
+
## Tool Reliability
|
|
181
|
+
|
|
182
|
+
Stable Harness uses `@botbotgo/better-call` at the tool-gateway boundary. The
|
|
183
|
+
default CLI path configures repair mode for registered tools, so malformed or
|
|
184
|
+
near-miss tool calls can be repaired before execution while agent inventory,
|
|
185
|
+
schema validation, semantic validators, and governance policy still define what
|
|
186
|
+
is allowed.
|
|
187
|
+
|
|
188
|
+
This is constrained repair, not silent magic:
|
|
189
|
+
|
|
190
|
+
- unknown or unauthorized tools stay blocked
|
|
191
|
+
- semantic validators remain authoritative
|
|
192
|
+
- upstream built-ins stay upstream-owned
|
|
193
|
+
- repaired calls are observable through runtime events and traces
|
|
194
|
+
|
|
195
|
+
## Protocols
|
|
196
|
+
|
|
197
|
+
- OpenAI-compatible facade: [docs/protocols/openai-compatible.md](docs/protocols/openai-compatible.md)
|
|
198
|
+
- LangGraph-compatible facade: [docs/protocols/langgraph-compatible.md](docs/protocols/langgraph-compatible.md)
|
|
199
|
+
- HTTP runtime protocol: [docs/protocols/http-runtime.md](docs/protocols/http-runtime.md)
|
|
200
|
+
|
|
24
201
|
## Product Boundary
|
|
25
202
|
|
|
26
|
-
|
|
203
|
+
Read these before adding public runtime behavior:
|
|
27
204
|
|
|
28
205
|
- [Product boundary](docs/product-boundary.md)
|
|
29
206
|
- [Compatibility matrix](docs/compatibility-matrix.md)
|
|
30
207
|
- [Implementation blueprint](docs/implementation-blueprint.md)
|
|
31
208
|
- [Engineering rules](docs/engineering-rules.md)
|
|
32
209
|
- [Adapter contract](docs/adapter-contract.md)
|
|
210
|
+
|
|
211
|
+
The short rule: pass through upstream execution semantics first, then add only
|
|
212
|
+
small, typed, replaceable runtime capabilities around them.
|
package/dist/cli.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{createDeepAgentsAdapter as t}from"@stable-harness/adapter-deepagents";import{createStableHarnessRuntime as o,projectEvent as e,projectRuntimeTrace as a}from"@stable-harness/core";import{createModuleToolGateway as
|
|
2
|
+
import{createDeepAgentsAdapter as t}from"@stable-harness/adapter-deepagents";import{createStableHarnessRuntime as o,projectEvent as e,projectRuntimeTrace as a}from"@stable-harness/core";import{createModuleToolGateway as r}from"@stable-harness/tool-gateway";import{loadWorkspaceFromYaml as s}from"@stable-harness/workspace-yaml";import{createAgentHarness as n,request as i,stop as c}from"./compat/agent-harness.js";import{formatCompatDelta as l}from"./runtime/compat/presentation.js";const p=function parseArgs(t){let o,e,a,r=process.cwd(),s=!1,n=!1,i=!1,c=!1,l=!1,p=Number(process.env.STABLE_HARNESS_CLI_TIMEOUT_MS??3e5);const g=[];for(let m=0;m<t.length;m+=1)"-h"===t[m]||"--help"===t[m]?l=!0:"-w"===t[m]||"--workspace"===t[m]?r=t[++m]??r:"--agent"===t[m]?o=t[++m]:"--tool"===t[m]?e=t[++m]:"--tool-args-json"===t[m]?a=parseJsonArg(t[++m]??"{}"):"--trace"===t[m]?s=!0:"--trace-json"===t[m]?n=!0:"--progress"===t[m]?i=!0:"--native"===t[m]?c=!0:"--timeout-ms"===t[m]?p=Number(t[++m]??p):g.push(t[m]);return{workspaceRoot:r,agentId:o,toolId:e,toolArgs:a,trace:s,traceJson:n,progress:i,native:c,help:l,timeoutMs:p,prompt:g.join(" ")||"hello"}}(process.argv.slice(2));function formatNativeCompatEvent(t){return"runtime.tool.direct.started"===t.type?[`agent:${t.agentId} Running tool ${t.toolId}`]:[]}function parseJsonArg(t){try{return JSON.parse(t)}catch(t){throw new Error(`Invalid --tool-args-json value: ${t instanceof Error?t.message:String(t)}`)}}p.help?console.log(function legacyHelpText(){return["Usage:"," botbotgo -w <workspace> [--agent <id>] [prompt]"," botbotgo --native -w <workspace> [--agent <id>] [prompt]"," botbotgo --native -w <workspace> --tool <id> --tool-args-json <json>","","Options:"," -w, --workspace <path> Workspace root."," --agent <id> Select an agent."," --native Use the native stable runtime path."," --tool <id> Invoke an explicit registered tool on the native path."," --tool-args-json <json> Tool arguments for --tool."," --trace Print trace lines."," --trace-json Print trace JSON."," --progress Print native progress narration."," --timeout-ms <ms> Request timeout."," -h, --help Show this help."].join("\n")}()):p.toolId||p.native?await async function runNative(n){const i=setTimeout(()=>{process.stderr.write(`botbotgo native request timed out after ${n.timeoutMs}ms\n`),process.exit(124)},n.timeoutMs);try{const i=await s(n.workspaceRoot),c=await r({tools:i.tools.values()}),l=o({workspace:i,toolGateway:c,adapters:[t()],progressNarration:n.progress?{enabled:!0,style:"cli"}:void 0});n.trace?l.subscribe(t=>{const o=e(t);o&&console.log(`trace:${o.agentId}:${o.label}${function formatTraceDetail(t){return"string"==typeof t?.toolId?`:${t.toolId}`:""}(o.detail)}`);for(const o of formatNativeCompatEvent(t))console.log(o)}):l.subscribe(t=>{for(const o of formatNativeCompatEvent(t))console.log(o)}),n.progress&&l.subscribe(t=>{"runtime.progress.narration"===t.type&&console.log(`progress:${t.agentId}:${t.message}`)});const p=await l.request({agentId:n.agentId,input:n.prompt,toolCall:n.toolId?{toolId:n.toolId,args:n.toolArgs}:void 0});!function printNativeTrace(t,o,e){if(!e.trace&&!e.traceJson)return;const r=t.getRun(o),s=r?a(r):[];e.traceJson&&console.log(JSON.stringify({trace:s}))}(l,p.requestId,n),console.log(p.output),process.exitCode="completed"===p.state?0:1}finally{clearTimeout(i)}}(p):await async function runCompat(t){const o=await n(t.workspaceRoot);try{const e=await i(o,{agentId:t.agentId,input:t.prompt,dataListener(t){for(const o of l(t))console.log(o)}});console.log(e.output),process.exitCode="completed"===e.state?0:1}finally{await c(o)}}(p);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{randomUUID as e}from"node:crypto";import{runAgentHarnessCompatAgent as t}from"../runtime/compat/agent-harness-compat-runner.js";import{loadWorkspace as n}from"../workspace/compile.js";export{loadWorkspace}from"../workspace/compile.js";export async function createAgentHarness(t){return function(t){const n=new Set;return{workspace:t,async request(
|
|
1
|
+
import{randomUUID as e}from"node:crypto";import{runAgentHarnessCompatAgent as t}from"../runtime/compat/agent-harness-compat-runner.js";import{loadWorkspace as n}from"../workspace/compile.js";export{loadWorkspace}from"../workspace/compile.js";export async function createAgentHarness(t){return function createRuntime(t){const n=new Set;return{workspace:t,async request(o){const r=e();return runRequest(t,o,r,n)},async*streamEvents(o){const r=e();yield{type:"event",event:{eventType:"request.created",requestId:r}};const s=function createAsyncQueue(){const e=[],t=[];let n=!1;return{push(n){const o=t.shift();o?o({value:n,done:!1}):e.push(n)},close(){for(n=!0;t.length;)t.shift()({value:void 0,done:!0})},[Symbol.asyncIterator]:()=>({next:async()=>{const o=e.shift();return o?{value:o,done:!1}:n?{value:void 0,done:!0}:new Promise(e=>t.push(e))}})}}(),a=runRequest(t,o,r,n,e=>{"agent.tool.start"===e.type?s.push({type:"event",event:{payload:{event:"on_tool_start",run_type:"tool",name:e.toolName}}}):s.push({requestId:r,...e,type:"tool-result"})}).finally(()=>s.close());for await(const e of s)yield e;await a},cancel(e){n.add(e)},async stop(){n.clear()}}}(await n(t))}export async function request(e,t){return e.request(t)}export async function cancelRequest(e,t){t.reason,e.cancel(t.requestId)}export async function stop(e){await e.stop()}async function runRequest(e,n,o,r,s){const a=n.agentId??"orchestra",c=e.agents.get(a);if(!c)throw new Error(`Unknown agent: ${a}`);return t({workspace:e,agent:c,request:{...n,dataListener:s??n.dataListener},requestId:o,cancelled:r})}
|
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,8 @@ export { createDeepAgentsMiddlewareSkillProvider, createLangGraphRuntimeAdapter,
|
|
|
5
5
|
export type { LangGraphNodeHandler, LangGraphNodeHandlerInput, LangGraphNodeResolver, LangGraphNodeResolverInput, LangGraphSkillMiddlewareProvider, LangGraphSkillMiddlewareProviderInput, LangGraphSkillResolverProvider, LangGraphWorkflowAdapterOptions, LangGraphWorkflowState, LangGraphWorkflowTraceEntry, } from "@stable-harness/adapter-langgraph";
|
|
6
6
|
export type { LangGraphRegistrySkillOutput } from "@stable-harness/adapter-langgraph";
|
|
7
7
|
export { createLangMemServiceProvider } from "@stable-harness/memory";
|
|
8
|
-
export
|
|
8
|
+
export { applySpecDrivenPhaseTransition, containsRecoverableResultOutput, createSpecDrivenArtifact, createSpecDrivenArtifactEvent, createSpecDrivenPhaseEvent, createSpecDrivenWorkflowPolicy, createSpecDrivenWorkflowState, projectRuntimeTrace, } from "@stable-harness/core";
|
|
9
|
+
export type { CompiledWorkspace, RuntimeAdapter, RuntimeEvent, RuntimeWorkflowAdapter, RuntimeRequest, RuntimeResponse, RuntimeRunRecord, RuntimeTraceEntry, StableHarnessRuntime, SpecDrivenPhaseRecord, SpecDrivenPhaseStatus, SpecDrivenPhaseTransition, SpecDrivenWorkflowState, WorkspaceAgent, WorkspaceModel, WorkspaceRuntimePolicy, WorkspaceSpecDrivenPhase, WorkspaceSpecDrivenWorkflowPolicy, WorkspaceTool, } from "@stable-harness/core";
|
|
9
10
|
export { loadWorkspaceFromYaml } from "@stable-harness/workspace-yaml";
|
|
10
11
|
export { createInMemoryToolGateway, createModuleToolGateway } from "@stable-harness/tool-gateway";
|
|
11
12
|
export type { ModuleToolDescriptor, ToolGateway, ToolGatewayContext, ToolGatewayInvokeRequest, ToolGatewayInvokeResult, ToolGatewayTool, } from "@stable-harness/tool-gateway";
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createDeepAgentsAdapter as e}from"@stable-harness/adapter-deepagents";import{createLangGraphRuntimeAdapter as r,createLangGraphWorkflowAdapter as
|
|
1
|
+
import{createDeepAgentsAdapter as e}from"@stable-harness/adapter-deepagents";import{createLangGraphRuntimeAdapter as r,createLangGraphWorkflowAdapter as a,createRegistrySkillResolverProvider as t}from"@stable-harness/adapter-langgraph";import{createStableHarnessRuntime as o}from"@stable-harness/core";import{createModuleToolGateway as n}from"@stable-harness/tool-gateway";import{loadWorkspaceFromYaml as s}from"@stable-harness/workspace-yaml";export{createDeepAgentsAdapter,createDeepAgentsMemoryMaintenanceTarget}from"@stable-harness/adapter-deepagents";export{createDeepAgentsMiddlewareSkillProvider,createLangGraphRuntimeAdapter,createLangGraphWorkflowAdapter,createRegistrySkillResolverProvider}from"@stable-harness/adapter-langgraph";export{createLangMemServiceProvider}from"@stable-harness/memory";export{applySpecDrivenPhaseTransition,containsRecoverableResultOutput,createSpecDrivenArtifact,createSpecDrivenArtifactEvent,createSpecDrivenPhaseEvent,createSpecDrivenWorkflowPolicy,createSpecDrivenWorkflowState,projectRuntimeTrace}from"@stable-harness/core";export{loadWorkspaceFromYaml}from"@stable-harness/workspace-yaml";export{createInMemoryToolGateway,createModuleToolGateway}from"@stable-harness/tool-gateway";export function createStableHarnessRuntime(e){return"string"==typeof e?createStableRuntime({workspaceRoot:e}):"workspaceRoot"in e?createStableRuntime(e):o(e)}export async function createStableRuntime(e){const r=await s(e.workspaceRoot),a=e.toolGateway??await n({tools:r.tools.values()});return o({workspace:r,toolGateway:a,adapters:e.adapters??createRuntimeAdapters(r,e),workflowAdapters:e.workflowAdapters??createWorkflowAdapters(r,e)})}export async function requestStableRuntime(e,r){return e.request(r)}function createRuntimeAdapters(a,t){const o={deepagents:({policy:r})=>e(r.config?{config:r.config}:{}),langgraph:({policy:e})=>r({...readLangGraphOptions(e.config),name:e.name}),...t.adapterFactories},n=function runtimeAdapterPolicies(e){const r=e.runtime.adapters?.filter(e=>!1!==e.enabled);return r&&r.length>0?r:[...new Set([...e.agents.values()].map(e=>e.backend))].map(e=>({name:e}))}(a);return n.map(e=>{const r=o[e.name];if(r)return r({policy:e,workspace:a});throw new Error(`Unsupported runtime adapter: ${e.name}`)})}function createWorkflowAdapters(e,r){const t={langgraph:({name:e,options:r})=>a({...readLangGraphOptions(r),name:e}),...r.workflowAdapterFactories};return[...new Set([...e.workflows.values()].map(e=>e.adapter??"").filter(Boolean))].map(a=>{const o=t[a];return o?.({name:a,workspace:e,options:readWorkflowAdapterOptions(r,a)})}).filter(e=>Boolean(e))}function readWorkflowAdapterOptions(e,r){return e.workflowAdapterOptions?.[r]??{}}function readLangGraphOptions(e){return isRecord(e)?{...e,...void 0!==readLangGraphSkillProvider(e)?{skillProvider:readLangGraphSkillProvider(e)}:{}}:{}}function readLangGraphSkillProvider(e){if(!1===e.skillProvider)return!1;const r=function readSkillProviderConfig(e){return isRecord(e.skills)?e.skills:isRecord(e.skillProvider)?e.skillProvider:void 0}(e);if(!r)return;const a=readString(r.provider)??readString(r.name)??"registry-resolver";if(["none","disabled","false"].includes(a))return!1;if("registry-resolver"!==a)throw new Error(`Unsupported LangGraph skill provider: ${a}`);return t({..."boolean"==typeof r.includeContent?{includeContent:r.includeContent}:{},..."number"==typeof r.maxBytes&&Number.isFinite(r.maxBytes)?{maxBytes:r.maxBytes}:{}})}function readString(e){return"string"==typeof e&&e.trim()?e.trim():void 0}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{generateWithModel as
|
|
1
|
+
import{generateWithModel as e}from"../model/ollama.js";import{parseToolCall as t}from"./json.js";import{buildAgentPrompt as n,buildRoutingPrompt as o,buildRoutingRepairPrompt as r,buildToolSelectionPrompt as a}from"./prompts.js";import{loadAgentTools as s}from"./tool-registry.js";export async function runAgentHarnessCompatAgent(a){const i=await async function resolveAgents(t){if(0===t.agent.subagentRefs.length)return[t.agent];const n=resolveModel(t.workspace,t.agent),a=t.request.input??"",s=await e(n,o(t.workspace,t.agent,a)),i=await async function repairRouting(t,n,o,a){let s=a,i=parseRouting(s);for(let a=0;a<3&&!hasEnoughRouting(t,o,i);a+=1)s=await e(n,r({workspace:t.workspace,agent:t.agent,userInput:o,previousResponse:s,issue:routingIssue(t,o,i)})),i=parseRouting(s);return hasEnoughRouting(t,o,i)?i:function readConfiguredRoutingTree(e,t){if(requiredRoutingCount(e,t)<=1)return[];const n=String(e.deepAgentConfig.systemPrompt??"").split(/\r?\n/u).map(e=>e.trim());for(const o of n){if(!/delegation tree|路由|委托/u.test(o)||!/must|必须|include|包含/u.test(o))continue;const n=e.subagentRefs.filter(e=>{return new RegExp(`\\b${t=e,t.replace(/[.*+?^${}()|[\]\\]/gu,"\\$&")}\\b`,"u").test(o);var t});if(n.length>=requiredRoutingCount(e,t))return n}return[]}(t.agent,o)}(t,n,a,s);return i.map(e=>t.workspace.agents.get(e)).filter(e=>Boolean(e))}(a),u=i[0]??a.agent,l=[];!function emitRoutingEvents(e,t,n,o){if(t.length>1&&e.request.dataListener?.({type:"delegation.plan",requestId:e.requestId,agentId:e.agent.id,agentIds:t.map(e=>e.id)}),n.id===e.agent.id)return;const r={agentId:e.agent.id,toolName:"task",output:n.id};o.push(r),e.request.dataListener?.({type:"agent.tool.result",requestId:e.requestId,...r}),e.request.dataListener?.({type:"delegation.start",requestId:e.requestId,agentId:n.id})}(a,i,u,l);const d=resolveModel(a.workspace,u),c=await s(a.workspace,u.toolRefs),g=[],f=new Set;if(u.toolRefs.length>0){const e=await forceToolSelection(a,u,c,g);e&&c.has(e.name)&&(recordToolResult(a,l,g,await invokeToolWithStart(a,u,c.get(e.name),e.arguments)),f.add(toolKey(e.name,e.arguments)),await ensureEvidenceAfterPlanning(a,u,c,f,l,g))}for(let o=0;o<4;o+=1){if(a.cancelled.has(a.requestId))return completed(a.requestId,g.join("\n\n"),l,u.id);const o=n({workspace:a.workspace,agent:u,model:d,userInput:a.request.input??"",tools:c,observations:g}),r=await e(d,o),s=t(r);if(!s&&hasEvidence(l))return completed(a.requestId,r,l,u.id);const i=s??await forceToolSelection(a,u,c,g);if(!i||!c.has(i.name)){const e=await forceToolSelection(a,u,availableNextTools(c,f),g);if(e&&c.has(e.name)){recordToolResult(a,l,g,await invokeToolWithStart(a,u,c.get(e.name),e.arguments)),f.add(toolKey(e.name,e.arguments));continue}return completed(a.requestId,r,l,u.id)}const p=toolKey(i.name,i.arguments);if(f.has(p)){const e=await forceToolSelection(a,u,availableNextTools(c,f),g);if(e&&c.has(e.name)){recordToolResult(a,l,g,await invokeToolWithStart(a,u,c.get(e.name),e.arguments)),f.add(toolKey(e.name,e.arguments));continue}return completed(a.requestId,g.join("\n\n"),l,u.id)}recordToolResult(a,l,g,await invokeToolWithStart(a,u,c.get(i.name),i.arguments)),f.add(p),await ensureEvidenceAfterPlanning(a,u,c,f,l,g)}return completed(a.requestId,g.join("\n\n"),l,u.id)}async function forceToolSelection(n,o,r,s){if(n.cancelled.has(n.requestId)||0===r.size)return;const i=resolveModel(n.workspace,o),u=await e(i,a({agent:o,userInput:n.request.input??"",tools:r,observations:s}));return t(u)}function hasEnoughRouting(e,t,n){return n.filter(t=>e.workspace.agents.has(t)).length>=requiredRoutingCount(e.agent,t)}function requiredRoutingCount(e,t){const n=t.split(/\r?\n/u).filter(e=>/^\s*\d+[.)、]/u.test(e)).length;return Math.max(1,Math.min(e.subagentRefs.length,n||1))}function routingIssue(e,t,n){const o=requiredRoutingCount(e.agent,t);return 0===n.length?"No valid available subagent id was returned.":`Only ${n.length} valid subagent ids were returned; at least ${o} are required for this multi-part request.`}function resolveModel(e,t){const n=t.modelRef??"default",o=e.models.get(n)??e.models.get("default");if(!o)throw new Error(`No model configured for agent ${t.id}`);return o}async function invokeToolWithStart(e,t,n,o){return e.request.dataListener?.({type:"agent.tool.start",requestId:e.requestId,agentId:t.id,toolName:n.name}),await new Promise(e=>setImmediate(e)),e.cancelled.has(e.requestId)?{agentId:t.id,toolName:n.name,output:"cancelled before tool invocation",isError:!0}:async function invokeTool(e,t,n,o){const r=process.cwd();try{process.chdir(e.workspace.workspaceRoot);const r=await n.invoke(o);return{agentId:t.id,toolName:n.name,output:stringifyOutput(r)}}catch(e){return{agentId:t.id,toolName:n.name,output:stringifyOutput(e),isError:!0}}finally{process.chdir(r)}}(e,t,n,o)}function recordToolResult(e,t,n,o){t.push(o),e.request.dataListener?.({type:"agent.tool.result",requestId:e.requestId,...o}),n.push(`Tool ${o.toolName} returned:\n${o.output}`)}function isPlanningTool(e){return"task"===e}function hasEvidence(e){return e.some(e=>!e.isError&&!isPlanningTool(e.toolName))}async function ensureEvidenceAfterPlanning(e,t,n,o,r,a){if(hasEvidence(r))return;const s=await forceToolSelection(e,t,availableNextTools(n,o),a);s&&n.has(s.name)&&(recordToolResult(e,r,a,await invokeToolWithStart(e,t,n.get(s.name),s.arguments)),o.add(toolKey(s.name,s.arguments)))}function availableNextTools(e,t){return new Map([...e].filter(([e])=>!isPlanningTool(e)&&!function hasExecutedTool(e,t){return[...t].some(t=>t.startsWith(`${e}:`))}(e,t)))}function parseRouting(e){try{const t=function parseJsonObject(e){try{return JSON.parse(e)}catch{const t=e.indexOf("{"),n=e.lastIndexOf("}");if(t>=0&&n>t)return JSON.parse(e.slice(t,n+1));throw new Error("No JSON object found")}}(e),n=function readRoutingIds(e){for(const t of["agentIds","routing","agents","delegations","plan"]){const n=e[t],o=Array.isArray(n)?n.filter(e=>"string"==typeof e):[];if(o.length>0)return[...new Set(o)]}return[]}(t);if(n.length>0)return n;const o=function readRoutingId(e){for(const t of["agentId","agent","owner","route"]){const n=e[t];if("string"==typeof n&&n.trim().length>0)return n}}(t);return o?[o]:[]}catch{return[]}}function completed(e,t,n,o){return{requestId:e,state:"completed",output:t,metadata:{executedToolResults:n,routedAgentId:o}}}function stringifyOutput(e){return e instanceof Error?e.stack??e.message:"string"==typeof e?e:JSON.stringify(e)}function toolKey(e,t){return`${e}:${JSON.stringify(t)}`}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export function parseToolCall(
|
|
1
|
+
export function parseToolCall(n){return function parseToolCalls(n){if("string"!=typeof n)return[];const t=[];for(const e of function jsonCandidates(n){return[n.trim(),extractJsonObject(n)].filter(n=>Boolean(n))}(n)){const n=normalizeToolCall(safeParse(e));n&&t.push(n)}for(const e of function objectCandidates(n){return n.match(/\{[^{}]*"name"\s*:\s*"[^"]+"[\s\S]*?\}\s*\}/gu)??[]}(n)){const n=normalizeToolCall(safeParse(e));n&&t.push(n)}return function uniqueToolCalls(n){const t=new Set;return n.filter(n=>{const e=`${n.name}:${JSON.stringify(n.arguments)}`;return!t.has(e)&&(t.add(e),!0)})}(t)}(n)[0]}export function extractJsonObject(n){const t=n.indexOf("{"),e=n.lastIndexOf("}");return t>=0&&e>t?n.slice(t,e+1):void 0}function safeParse(n){try{return JSON.parse(n)}catch{return}}function normalizeToolCall(n){if("object"!=typeof n||null===n)return;const t=n;if("string"!=typeof t.name)return;const e="object"==typeof t.arguments&&null!==t.arguments?t.arguments:{};return{name:t.name,arguments:e}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{readPlanTodos as t}from"@stable-harness/core";export function formatCompatDelta(
|
|
1
|
+
import{readPlanTodos as t}from"@stable-harness/core";export function formatCompatDelta(e){return"delegation.start"===e.type?[`agent:${e.agentId} Starting delegated execution.`]:"delegation.plan"===e.type?[`Planned delegation tree: ${readAgentIds(e).join(" -> ")}`]:"agent.tool.start"===e.type?[`agent:${e.agentId} Running tool ${n=e.toolName,"write_todos"===n?"Call Write Todos.":String(n)}`]:"agent.tool.result"===e.type?function formatToolResult(e){if("task"===e.toolName)return[`agent:${e.agentId} Delegating to ${e.output}.`];if("write_todos"===e.toolName){const n=t(e.output);return n.length>0?function formatTodoBurnDown(t,e){const n=e.filter(t=>"completed"===t.status).length,o=e.filter(t=>"in_progress"===t.status).length,r=e.filter(t=>"pending"===t.status).length,a=[`agent:${t} TODO Burn Down | ${n}/${e.length} done | ${o} active | ${r} pending`];for(const t of e)a.push(` [${todoMarker(t.status)}] ${t.content}`);for(const n of e.filter(t=>"completed"===t.status||"failed"===t.status))a.push(`agent:${t} TODO ${n.status}: ${n.content}`);return a}(String(e.agentId),n):[]}return[]}(e):[];var n}function readAgentIds(t){return Array.isArray(t.agentIds)?t.agentIds.map(String):[]}function todoMarker(t){return"completed"===t?"x":"in_progress"===t?"~":"failed"===t?"!":" "}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export function buildAgentPrompt(
|
|
1
|
+
export function buildAgentPrompt(e){const o=[...e.tools.values()].map(e=>({name:e.name,description:e.description??""}));return[e.agent.deepAgentConfig.systemPrompt??"","","You are running inside stable-harness.",toolPolicyExcerpt(e.agent,e.tools),'If a tool is needed, return exactly one JSON object: {"name":"tool_name","arguments":{...}}.',"Before returning a final answer, re-read the tool policy excerpts and run any required follow-up evidence tool for this request.","If enough evidence is available, return the final answer only.","Available tools:",JSON.stringify(o,null,2),e.observations.length?`Tool observations:\n${e.observations.join("\n\n")}`:"",`User request:\n${e.userInput}`].filter(Boolean).join("\n")}export function buildRoutingPrompt(e,o,t){const n=o.subagentRefs.map(o=>e.agents.get(o)).filter(e=>Boolean(e)).map(e=>({id:e.id,description:e.description}));return["Choose the best subagent plan for this request.",routingPolicyExcerpt(o,n.map(e=>e.id)),o.deepAgentConfig.systemPrompt??"","Routing policy excerpts are authoritative workspace config.","This is a routing-only step. Tools are unavailable here.","Do not return write_todos, read_todos, task, markdown, or prose.","If a routing policy says a request must include one or more available subagents, include those subagents.",'If one owner is enough, return exactly JSON: {"agentId":"one_id","reason":"short reason"}.','If the request contains multiple specialist-owned tasks, return exactly JSON: {"agentIds":["first_id","second_id"],"reason":"short reason"}.',"For numbered or multi-part requests, check every item and include an owner for each distinct specialist-owned task.","Do not omit later tasks after selecting earlier owners.","Return only ids from Available subagents.","Available subagents:",JSON.stringify(n,null,2),`User request:\n${t}`].filter(Boolean).join("\n")}export function buildRoutingRepairPrompt(e){const o=e.agent.subagentRefs.map(o=>e.workspace.agents.get(o)).filter(e=>Boolean(e)).map(e=>({id:e.id,description:e.description}));return[e.issue??"Your previous routing response did not contain a valid complete routing plan.",routingPolicyExcerpt(e.agent,o.map(e=>e.id)),"This is a routing-only repair. Tools are unavailable here.","Do not return write_todos, read_todos, task, markdown, or prose.","Return JSON only.",'For one owner: {"agentId":"one_id","reason":"short reason"}.','For multiple owners: {"agentIds":["first_id","second_id"],"reason":"short reason"}.',"Use only ids from Available subagents.","Available subagents:",JSON.stringify(o,null,2),`Previous response:\n${e.previousResponse}`,`User request:\n${e.userInput}`].filter(Boolean).join("\n")}export function buildToolSelectionPrompt(e){const o=[...e.tools.values()].map(e=>({name:e.name,description:e.description??""}));return[`Agent: ${e.agent.id}`,e.agent.description?`Responsibility: ${e.agent.description}`:"",toolPolicyExcerpt(e.agent,e.tools),"Choose the single best evidence tool from the available tools.",'Return exactly one JSON object: {"name":"tool_name","arguments":{...}}.',"Infer arguments from the user request, tool description, and tool policy excerpts.","If the user explicitly names an available tool, choose that exact tool.","Do not use empty arguments when the tool policy or user request provides a concrete schema, URL, path, question, ticker, or command.","Prefer the most specific evidence tool for the requested artifact or operation before choosing a general search tool.","Available tools:",JSON.stringify(o,null,2),e.observations?.length?`Tool observations already gathered:\n${e.observations.join("\n\n")}`:"",`User request:\n${e.userInput}`].filter(Boolean).join("\n")}function toolPolicyExcerpt(e,o){const t=String(e.deepAgentConfig.systemPrompt??""),n=[...o.keys()].filter(e=>"write_todos"!==e&&"read_todos"!==e),r=t.split(/\r?\n/u).map(e=>e.trim()).filter(e=>n.some(o=>e.includes(o))).slice(0,12);return r.length>0?`Tool policy excerpts:\n${r.join("\n")}`:""}function routingPolicyExcerpt(e,o){const t=String(e.deepAgentConfig.systemPrompt??"").split(/\r?\n/u).map(e=>e.trim()).filter(e=>function isRoutingPolicyLine(e,o){const t=e.toLowerCase();return o.some(o=>e.includes(o))&&/\b(route|routing|delegate|delegation|owner|specialist|include|tree|task)\b/u.test(t)}(e,o)),n=t.map(e=>({line:e,score:routingPolicyScore(e,o)})).sort((e,o)=>o.score-e.score).map(e=>e.line).slice(0,16);return n.length>0?`Routing policy excerpts:\n${n.join("\n")}`:""}function routingPolicyScore(e,o){const t=e.toLowerCase();return 10*o.filter(o=>e.includes(o)).length+(t.match(/\b(must|include|order|tree|requires|required|exactly|only)\b/gu)??[]).length}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export async function generateWithModel(
|
|
1
|
+
export async function generateWithModel(e,t){if("ollama"!==e.provider&&"openai-compatible"!==e.provider)throw new Error(`Unsupported model provider: ${e.provider}`);const r=function normalizeBaseUrl(e){const t=e.trim()||"http://127.0.0.1:11434/";return t.endsWith("/")?t:`${t}/`}(String(e.init.baseUrl??e.init.baseURL??"")),n="ollama"===e.provider?`${r}api/generate`:`${function normalizeOpenAiBaseUrl(e){return e.endsWith("/v1/")?e:`${e}v1/`}(r)}chat/completions`,o=Number(e.init.timeout??12e4),i=Number(e.init.retries??2)+1;let a;for(let r=1;r<=i;r+=1)try{return await requestModel(e,n,t,o)}catch(e){if(a=e,r===i||!isRetryableModelError(e))throw e;await delay(500*r)}throw a instanceof Error?a:new Error(String(a))}async function requestModel(e,t,r,n){const o=new AbortController,i=setTimeout(()=>o.abort(),n),a=await function fetchModel(e,t,r,n){return fetch(t,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify("ollama"===e.provider?ollamaBody(e,r):openAiBody(e,r)),signal:n})}(e,t,r,o.signal).finally(()=>clearTimeout(i));if(!a.ok)throw new Error(`Model request failed with HTTP ${a.status}: ${await a.text()}`);const s=await a.json();return function parseModelResponse(e,t){if("ollama"===e)return String(t.response??"");const r=(Array.isArray(t.choices)?t.choices:[])[0];return String(r?.message?.content??"")}(e.provider,s)}function isRetryableModelError(e){if(e instanceof DOMException&&"AbortError"===e.name)return!1;const t=e instanceof Error?e.message:String(e);return/fetch failed|ECONNRESET|ETIMEDOUT|EAI_AGAIN|socket|network/i.test(t)}function delay(e){return new Promise(t=>setTimeout(t,e))}function ollamaBody(e,t){return{model:e.model,prompt:t,stream:!1,think:e.init.think??!1,options:{num_ctx:e.init.numCtx,num_predict:e.init.numPredict,temperature:e.init.temperature}}}function openAiBody(e,t){return{model:e.model,messages:[{role:"user",content:t}],temperature:e.init.temperature??0}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{readFileSync as r,statSync as t}from"node:fs";import*as
|
|
1
|
+
import{readFileSync as r,statSync as t}from"node:fs";import*as e from"node:path";import{parse as n}from"yaml";export function validateSkillMetadata(o){const i=t(o).isDirectory()?e.join(o,"SKILL.md"):o,a=r(i,"utf8").match(/^---\n([\s\S]*?)\n---/u);if(!a)throw new Error(`${i} is missing YAML front matter`);const s=n(a[1]),m=readString(s.name,"name",i),l=readString(s.description,"description",i),d=function readStringList(r){return Array.isArray(r)?r.filter(r=>"string"==typeof r&&r.trim().length>0):[]}(s["allowed-tools"]);if(0===d.length)throw new Error(`${e.basename(o)} must declare allowed-tools`);return{name:m,description:l,allowedTools:d,path:i}}function readString(r,t,e){if("string"!=typeof r||!r.trim())throw new Error(`${e} front matter requires ${t}`);return r.trim()}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{readdir as e,readFile as t}from"node:fs/promises";import*as s from"node:path";import{parseAllDocuments as
|
|
1
|
+
import{readdir as e,readFile as t}from"node:fs/promises";import*as s from"node:path";import{parseAllDocuments as o}from"yaml";import{validateSkillMetadata as r}from"../runtime/skills/skill-metadata.js";export async function loadWorkspace(n){const i=s.resolve(n),a=await async function readConfigDocuments(e){const s=await listYamlFiles(e),r=[];for(const e of s){const s=await t(e,"utf8");for(const e of o(s)){const t=e.toJSON();t?.kind&&r.push({...t,metadata:t.metadata??{},spec:t.spec??{}})}}return r}(s.join(i,"config")),c=function compileModels(e){const t=new Map;for(const s of e){if("Models"===s.kind&&Array.isArray(s.spec))for(const e of s.spec){const s=compileModel(e);t.set(String(s.name),s)}if("Model"===s.kind&&isRecord(s.spec)){const e=compileModel({name:s.metadata?.name,...s.spec});t.set(String(e.name),e)}}return t}(a),m=function compileAgents(e,t){const o=new Map;for(const r of t.filter(e=>"Agent"===e.kind)){if(!isRecord(r.spec))continue;const t=String(r.metadata?.name),n=isRecord(r.spec.config)?r.spec.config:{},i=readSystemPrompt(r.spec,n);o.set(t,{id:t,name:t,description:r.metadata?.description??"",sourcePath:s.join(e,"config","agents",`${t}.yaml`),modelRef:normalizeRef(r.spec.modelRef),toolRefs:readStringList(r.spec.tools),skillPathRefs:readStringList(r.spec.skills).map(t=>s.join(e,"resources","skills",t)),subagentRefs:readStringList(r.spec.subagents),memorySources:readMemorySources(r.spec.memory),deepAgentConfig:{...n,responseFormat:normalizeResponseFormat(n.responseFormat),systemPrompt:i}})}return o}(i,a),l=await async function compileTools(o,r){const n=new Map;for(const e of r.filter(e=>"Tool"===e.kind)){const t=e.metadata?.name;t&&n.set(t,{name:t,id:t,spec:e.spec})}return await async function loadToolExports(o,r){const n=await e(o,{withFileTypes:!0});for(const e of n){if(!e.isFile()||!e.name.endsWith(".mjs")||e.name.startsWith("_"))continue;const n=s.join(o,e.name),i=(await t(n,"utf8")).match(/export\s+const\s+([A-Za-z_][A-Za-z0-9_]*)\s*=\s*tool/u),a=i?.[1]??s.basename(e.name,".mjs");r.set(a,{name:a,id:a,sourcePath:n})}}(s.join(o,"resources","tools"),n),n}(i,a),p=await async function compileSkills(t){const o=new Map,n=s.join(t,"resources","skills");for(const t of await e(n,{withFileTypes:!0})){if(!t.isDirectory())continue;const e=s.join(n,t.name),i=r(e);o.set(i.name,{name:i.name,path:e,description:i.description,allowedTools:i.allowedTools})}return o}(i);return function resolveAgentSkillNames(e,t){for(const o of e.values())o.skillPathRefs=o.skillPathRefs.map(e=>{const o=s.basename(e);return t.get(o)?.path??e})}(m,p),function ensureDirectAgent(e,t){e.has("direct")||e.set("direct",{id:"direct",name:"direct",description:"Direct execution agent.",sourcePath:s.join(t,"config","runtime","workspace.yaml"),toolRefs:[],skillPathRefs:[],subagentRefs:[],memorySources:[],deepAgentConfig:{}})}(m,i),{workspaceRoot:i,models:c,agents:m,tools:l,skills:p,bindings:compileBindings(m)}}async function listYamlFiles(t){const o=await e(t,{withFileTypes:!0});return(await Promise.all(o.map(async e=>{const o=s.join(t,e.name);return e.isDirectory()?listYamlFiles(o):e.isFile()&&/\.ya?ml$/iu.test(e.name)?[o]:[]}))).flat().sort()}function compileModel(e){const t=String(e.name),s={...e};return delete s.name,delete s.provider,delete s.model,{name:t,provider:String(resolveValue(e.provider)),model:String(resolveValue(e.model)),init:(o=s,Object.fromEntries(Object.entries(o).map(([e,t])=>[e,resolveValue(t)])))};var o}function compileBindings(e){const t=new Map;for(const[s,o]of e){const e=o.deepAgentConfig;t.set(s,{agentId:s,deepAgentParams:{responseFormat:e.responseFormat,systemPrompt:e.systemPrompt}})}return t}function readSystemPrompt(e,t){return"string"==typeof e.systemPrompt?e.systemPrompt:"string"==typeof t.systemPrompt?t.systemPrompt:""}function normalizeResponseFormat(e){const t={type:"object",properties:{status:{type:"string",enum:["completed","blocked","failed","refused"]},summary:{type:"array",items:{type:"string"}},findings:{type:"array",items:{type:"string"}},blockers:{type:"array",items:{type:"string"}},nextActions:{type:"array",items:{type:"string"}},report:{type:"string"}},required:["status","summary","findings","blockers","nextActions","report"]};if(!isRecord(e))return t;const s=isRecord(e.properties)?e.properties:{},o=Array.isArray(e.required)?e.required:[];return{...t,...e,properties:{...t.properties,...s},required:[...new Set([...t.required,...o].filter(e=>"string"==typeof e))]}}function readMemorySources(e){return Array.isArray(e)?e.flatMap(e=>isRecord(e)&&"string"==typeof e.path?[e.path]:[]):[]}function readStringList(e){return Array.isArray(e)?e.filter(e=>"string"==typeof e&&e.trim().length>0):[]}function normalizeRef(e){return"string"==typeof e?e.replace(/^[^/]+\//u,""):void 0}function resolveValue(e){if("string"!=typeof e)return e;const t=e.replace(/\$\{env:([A-Za-z_][A-Za-z0-9_]*)(?::-(.*?))?\}/gu,(e,t,s)=>process.env[t]??s??"");return/^(true|false)$/iu.test(t)?"true"===t.toLowerCase():/^-?\d+$/u.test(t)?Number(t):t}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stable-harness",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Stable application runtime and operator control plane for agent workspaces.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
"test:skill-mining:e2e": "node scripts/run-skill-candidate-mining-e2e.mjs",
|
|
65
65
|
"prepublishOnly": "npm run build && npm run release:check-package",
|
|
66
66
|
"prepack": "npm run release:minify",
|
|
67
|
-
"release:minify": "find dist packages -type f -name '*.js' \\( -path 'dist/*' -o -path '*/dist/*' \\) -exec sh -c 'for f do ./node_modules/.bin/terser \"$f\" --compress passes=2 --mangle --
|
|
67
|
+
"release:minify": "find dist packages -type f -name '*.js' \\( -path 'dist/*' -o -path '*/dist/*' \\) -exec sh -c 'for f do ./node_modules/.bin/terser \"$f\" --compress passes=2 --mangle keep_fnames=true --keep-fnames --keep-classnames --module --comments false --output \"$f\"; done' sh {} +",
|
|
68
68
|
"release:check-package": "node scripts/release/check-npm-package.mjs",
|
|
69
69
|
"release:pack": "npm run build && npm run release:check-package && npm pack --dry-run",
|
|
70
70
|
"release:publish": "npm publish --access public --registry https://registry.npmjs.org/",
|
|
@@ -74,10 +74,11 @@
|
|
|
74
74
|
"packages/*"
|
|
75
75
|
],
|
|
76
76
|
"dependencies": {
|
|
77
|
-
"@botbotgo/better-call": "^0.1.
|
|
77
|
+
"@botbotgo/better-call": "^0.1.18",
|
|
78
78
|
"@langchain/core": "^1.1.43",
|
|
79
79
|
"@langchain/langgraph": "^1.3.0",
|
|
80
80
|
"@langchain/ollama": "^1.2.7",
|
|
81
|
+
"@langchain/openai": "^1.4.5",
|
|
81
82
|
"@stable-harness/adapter-deepagents": "file:packages/adapter-deepagents",
|
|
82
83
|
"@stable-harness/adapter-langgraph": "file:packages/adapter-langgraph",
|
|
83
84
|
"@stable-harness/core": "file:packages/core",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{realpathSync as e}from"node:fs";import t from"node:path";import{createBuiltinToolPolicyMiddleware as r,createObserverMiddleware as n,resolveFilesystemPermissions as o}from"./internal/builtin-tool-policy.js";import{buildGatewayTools as s,createToolRepeatState as i,stringifyDeepAgentResult as a}from"./internal/gateway-tools.js";import{resolveDeepAgentsNativeMemories as l}from"./memory.js";import{buildDeepAgentRequest as d}from"./internal/messages.js";import{createRawToolCallParserMiddleware as c}from"./internal/raw-tool-call-parser.js";import{createBackendModel as p}from"./model-providers.js";import{createDeepAgentsRetryMiddleware as u}from"./retry-policy.js";import{streamDeepAgentResult as g}from"./internal/stream-events.js";export function createDeepAgentsAdapter(e={}){return{name:"deepagents",canRun:e=>"deepagents"===e.backend,async run(t){if(t.emit({type:"runtime.adapter.event",requestId:t.requestId,sessionId:t.sessionId,agentId:t.agent.id,event:{adapter:"deepagents",phase:"agent.handoff",modelRef:t.agent.modelRef,tools:t.agent.tools,subagents:t.agent.subagents}}),e.runner)return e.runner(t);const r=e.createDeepAgent?void 0:await async function loadDeepAgentsModule(){try{return await async function importOptionalPackage(e){return new Function("specifier","return import(specifier)")(e)}("deepagents")}catch(e){throw new Error(`DeepAgents package is required for the default adapter path: ${function formatError(e){return e instanceof Error?e.message:String(e)}(e)}`)}}(),n=e.createDeepAgent??function readCreateDeepAgent(e){const t=e?.createDeepAgent;if("function"==typeof t)return t;throw new Error("DeepAgents package does not export createDeepAgent.")}(r),i=n(function buildDeepAgentParams(e,t,r){const n={...readDeepAgentsConfig(t),...readDeepAgentsConfig(e.agent.config.deepagents)},i=resolveDeepAgentsSkills(e,e.agent),a=n.permissions??o(e,e.agent),l=requestScopedRepeatState(e,e.agent.id);return pruneUndefined({name:e.agent.id,model:n.model??resolveAgentModel(e,e.agent),systemPrompt:buildSystemPrompt(e,e.agent),backend:n.backend??resolveDeepAgentsBackend(e,r,i),checkpointer:n.checkpointer,store:n.store,middleware:mergeMiddleware(e,e.agent,n.middleware,l),responseFormat:n.responseFormat,contextSchema:n.contextSchema,interruptOn:n.interruptOn,generalPurposeAgent:readBoolean(n.generalPurposeAgent),taskDescription:readString(n.taskDescription),permissions:a,tools:s(e,e.agent.id,e.agent.tools,resolveAgentRepairModel(e,e.agent,n),l),subagents:e.agent.subagents.map(t=>{const r=e.workspace.agents.get(t),n=readDeepAgentsConfig(r?.config.deepagents),i=n.permissions??o(e,r),a=scopedInput(e,r),l=requestScopedRepeatState(e,t);return pruneUndefined({name:t,description:r?.description??readString(r?.config.description)??r?.id,systemPrompt:buildSystemPrompt(e,r),model:n.model??(r?resolveAgentModel(e,r):void 0),middleware:mergeMiddleware(a,r,n.middleware,l),interruptOn:n.interruptOn,generalPurposeAgent:readBoolean(n.generalPurposeAgent),taskDescription:readString(n.taskDescription),permissions:i,responseFormat:n.responseFormat,tools:s(e,t,r?.tools??[],resolveAgentRepairModel(a,r,n),l),memory:resolveDeepAgentsMemory(e,r),skills:resolveDeepAgentsSkills(e,r)})}),memory:resolveDeepAgentsMemory(e,e.agent),skills:i})}(t,e.config,r)),l=d(t),c=function buildDeepAgentInvokeConfig(e){return pruneUndefined({recursionLimit:readNumber(readDeepAgentsConfig(e.config.deepagents).recursionLimit)??readNumber(e.config.recursionLimit)})}(t.agent);if(!0===t.request.metadata?.openaiStream&&i.streamEvents)return g(t,i.streamEvents(l,{version:"v2",...c}),a);const p=await i.invoke(l,c);return a(p)}}}function buildSystemPrompt(e,t){const r=t?.systemPrompt??readString(t?.config.systemPrompt),n=function buildSkillInventoryPolicy(e,t){const r=(t?.skills??[]).map(t=>e.workspace.skills.get(t)).filter(e=>Boolean(e));if(0!==r.length)return["## Stable Harness Skill Inventory","Use only the skills listed in this workspace inventory. Do not infer or invent skill names, skill directories, or SKILL.md paths from generic examples.","Skill files are already registered with the backend. Do not read local SKILL.md files or skill directories through filesystem tools.","If none of these skills match the task, continue with the configured tools and collected evidence instead of reading an unlisted skill path.",r.map(e=>{const t=e.allowedTools.length>0?`; allowed tools: ${e.allowedTools.join(", ")}`:"",r=e.description?`: ${e.description}${t}`:t;return`- ${e.id}${r}`}).join("\n")].join("\n")}(e,t),o=function buildResponseLanguagePolicy(e){const t=isRecord(e.workspace.runtime.responseLanguage)?e.workspace.runtime.responseLanguage:{};if("matchUser"!==(readString(t.mode)??readString(t.strategy)))return;const r=function detectRequestLanguage(e){return/\p{Script=Han}/u.test(e)?"Chinese":/[\p{Script=Hiragana}\p{Script=Katakana}]/u.test(e)?"Japanese":/\p{Script=Hangul}/u.test(e)?"Korean":/\p{Script=Arabic}/u.test(e)?"Arabic":/\p{Script=Hebrew}/u.test(e)?"Hebrew":/\p{Script=Thai}/u.test(e)?"Thai":/\p{Script=Cyrillic}/u.test(e)?"Cyrillic-script language":void 0}(e.request.input);return["Stable runtime response language policy:",...r?[`- Detected request language: ${r}.`]:[],"- Match the final answer language to the original user request unless the user explicitly asks for another language.","- If tool or subagent evidence is in a different language, translate the final user-facing answer into the detected request language.","- When delegating to subagents, include the same response-language requirement in delegated instructions.","- Do not call another tool or subagent only to translate, rewrite, format, or synthesize a completed answer.",`Original user request:\n${e.request.input}`].join("\n")}(e),s=function buildResponsePresentationPolicy(e,t){const r=isRecord(e.workspace.runtime.responsePresentation)?e.workspace.runtime.responsePresentation:{};if(!0===r.enabled&&!function hasStructuredResponseFormat(e){return void 0!==e?.config.responseFormat||isRecord(e?.config.deepagents)&&void 0!==e.config.deepagents.responseFormat}(t)&&"markdown"===(readString(r.style)??"markdown"))return["Stable runtime final-answer presentation policy:","- For user-facing natural-language final answers, use GitHub-flavored Markdown.","- Prefer clear section headings, short paragraphs, and concise bullets over dense prose.","- Use tables only when they make comparison or planning details easier to scan.","- For detailed investigations, plans, or reports, include assumptions, findings, recommendations, and concrete next steps.","- Preserve exact plain text, JSON, code, or other structured output when the user or response format asks for it.","- Do not end with generic follow-up offers; deliver the requested answer directly."].join("\n")}(e,t);return[r,n,o,s].filter(Boolean).join("\n\n")||void 0}function resolveDeepAgentsMemory(e,t){const r=readDeepAgentsStringArray(t?.config,"memory");if(r)return r;const n=l(e.workspace).map(e=>`/memories/${e.id}.md`);return n.length>0?n:void 0}function resolveDeepAgentsSkills(r,n){const o=readDeepAgentsStringArray(n?.config,"skills");if(o)return o;const s=[...new Set((n?.skills??[]).map(e=>r.workspace.skills.get(e)?.path).filter(e=>"string"==typeof e&&e.trim().length>0).map(n=>function backendSkillSourcePath(r,n){const o=t.dirname(t.dirname(n)),s=t.relative(r,o);return!s||s.startsWith("..")||t.isAbsolute(s)?""===s?"/":function canonicalPath(t){try{return e.native(t)}catch{return t}}(o):`/${s.split(t.sep).join("/")}`}(r.workspace.root,n)))];return s.length>0?s:void 0}function resolveDeepAgentsBackend(e,t,r){if(t?.FilesystemBackend&&r&&0!==r.length)return()=>new t.FilesystemBackend({rootDir:e.workspace.root})}function mergeMiddleware(e,t,o,s=i(e.workspace.runtime.toolGateway)){const a=Array.isArray(o)?o:[],l=scopedInput(e,t),d=new Set;return[n(l,{observedToolIds:d,repeatState:s}),r(l,{repeatState:s}),...u(e.workspace.runtime.retry),...a,c(l)]}function requestScopedRepeatState(e,t){const r=`deepagents.repeat.${t}`,n=e.requestState?.get(r);if(n)return n;const o=i(e.workspace.runtime.toolGateway);return e.requestState&&o&&e.requestState.set(r,o),o}function scopedInput(e,t){return t?{...e,agent:t}:e}function resolveAgentModel(e,t){const r=t.modelRef?e.workspace.models.get(t.modelRef):void 0;return r?p(r):void 0}function resolveAgentRepairModel(e,t,r){const n=r.model;if(isRepairModel(n))return n;if(!t)return;const o=resolveAgentModel(e,t);return isRepairModel(o)?o:void 0}function readDeepAgentsConfig(e){return isRecord(e)?e:{}}function readDeepAgentsStringArray(e,t){const r=isRecord(e)?e:{},n=readDeepAgentsConfig(r.deepagents),o="memory"===t?["memory","memorySources"]:["skills","skillSources"];for(const e of o){const t=readStringArray(n[e]);if(t)return t}return readStringArray(r[t])}function pruneUndefined(e){return Object.fromEntries(Object.entries(e).filter(([,e])=>void 0!==e))}function readString(e){return"string"==typeof e&&e.trim()?e:void 0}function readNumber(e){return"number"==typeof e&&Number.isFinite(e)?e:void 0}function readBoolean(e){return"boolean"==typeof e?e:void 0}function readStringArray(e){return Array.isArray(e)?e.filter(e=>"string"==typeof e):void 0}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function isRepairModel(e){return"object"==typeof e&&null!==e&&"invoke"in e&&"function"==typeof e.invoke}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function normalizeArgsRecord(args: unknown): Record<string, unknown>;
|
|
2
|
+
export declare function normalizeWriteTodosArgs(args: unknown): Record<string, unknown>;
|
|
3
|
+
export declare function normalizeFilesystemArgs(toolId: string, args: unknown, workspaceRoot: string): Record<string, unknown>;
|
|
4
|
+
export declare function shallowEqualRecord(left: Record<string, unknown>, right: Record<string, unknown>): boolean;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import r from"node:path";export function normalizeArgsRecord(r){if(isRecord(r))return r;if("string"!=typeof r||!r.trim())return{};try{const t=JSON.parse(r);return isRecord(t)?t:{}}catch{return{}}}export function normalizeWriteTodosArgs(r){if(Array.isArray(r))return{todos:r.map(normalizeTodoItem)};if("string"==typeof r&&r.trim())try{const t=JSON.parse(r);if(Array.isArray(t))return{todos:t.map(normalizeTodoItem)}}catch{return{todos:[{content:r.trim()}]}}const t=normalizeArgsRecord(r),e=readTodoList(t);return e?{...t,todos:e.map(normalizeTodoItem)}:t}export function normalizeFilesystemArgs(r,t,e){const n={...normalizeArgsRecord(t)};return normalizePathField(n,e,"path"),normalizePathField(n,e,"file_path"),normalizePathField(n,e,"pattern","glob"===r),n}export function shallowEqualRecord(r,t){const e=Object.keys(r),n=Object.keys(t);return e.length===n.length&&e.every(e=>r[e]===t[e])}function readTodoList(r){const t=r.todos??r.items??r.tasks??r.plan;if(Array.isArray(t))return t;if("string"==typeof t&&t.trim())try{const r=JSON.parse(t);return Array.isArray(r)?r:isRecord(r)?readTodoList(r):void 0}catch{return}}function normalizeTodoItem(r){if("string"==typeof r&&r.trim())return{content:r.trim()};if(!isRecord(r))return r;const t=readString(r.content)??readString(r.description)??readString(r.task)??readString(r.title)??readString(r.name),e=function normalizeTodoStatus(r){if(!r)return;const t=r.toLowerCase().replaceAll("-","_").replaceAll(" ","_");return"todo"===t||"not_started"===t?"pending":"doing"===t||"in_progress"===t?"in_progress":"done"===t||"complete"===t?"completed":t}(readString(r.status)??readString(r.state));return{...t?{content:t}:{},...e?{status:e}:{}}}function readString(r){return"string"==typeof r&&r.trim()?r.trim():void 0}function normalizePathField(t,e,n,o=!0){const i=o?readString(t[n]):void 0;i&&(t[n]=function workspaceBackendPath(t,e){if(!e.startsWith("/"))return e;if(function isPathInside(t,e){const n=r.relative(t,e);return""===n||!!n&&!n.startsWith("..")&&!r.isAbsolute(n)}(t,e)){const n=r.relative(t,e).split(r.sep).filter(Boolean).join("/");return n?`/${n}`:"/"}return e}(e,i))}function isRecord(r){return"object"==typeof r&&null!==r&&!Array.isArray(r)}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { ToolMessage } from "@langchain/core/messages";
|
|
2
|
-
import type { RuntimeAdapter } from "@stable-harness/core";
|
|
3
|
-
import type
|
|
2
|
+
import type { RuntimeAdapter, WorkspaceAgent } from "@stable-harness/core";
|
|
3
|
+
import { type ToolRepeatState } from "./gateway-tools.js";
|
|
4
4
|
type AdapterRunInput = Parameters<RuntimeAdapter["run"]>[0];
|
|
5
|
-
export declare function createBuiltinToolPolicyMiddleware(input: AdapterRunInput
|
|
5
|
+
export declare function createBuiltinToolPolicyMiddleware(input: AdapterRunInput, options?: {
|
|
6
|
+
repeatState?: ToolRepeatState;
|
|
7
|
+
}): {
|
|
6
8
|
name: string;
|
|
7
9
|
wrapModelCall(request: {
|
|
8
10
|
tools?: Array<{
|
|
@@ -16,7 +18,10 @@ export declare function validateFilesystemBuiltinCall(input: AdapterRunInput, to
|
|
|
16
18
|
id?: string;
|
|
17
19
|
};
|
|
18
20
|
}): ToolMessage<import("@langchain/core/messages").MessageStructure<import("@langchain/core/messages").MessageToolSet>> | undefined;
|
|
19
|
-
export declare function createObserverMiddleware(input: AdapterRunInput
|
|
21
|
+
export declare function createObserverMiddleware(input: AdapterRunInput, options?: {
|
|
22
|
+
observedToolIds?: Set<string>;
|
|
23
|
+
repeatState?: ToolRepeatState;
|
|
24
|
+
}): {
|
|
20
25
|
name: string;
|
|
21
26
|
wrapToolCall(request: {
|
|
22
27
|
toolCall?: {
|