stable-harness 0.0.39 → 0.0.41
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 +13 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/docs/architecture/runtime-events.md +10 -0
- package/docs/guides/index.md +2 -0
- package/docs/guides/workspace-docker-build.md +88 -0
- package/docs/protocols/http-runtime.md +8 -0
- package/docs/protocols/langgraph-compatible.md +22 -0
- package/node_modules/@stable-harness/adapter-deepagents/dist/src/adapter.js +1 -1
- package/node_modules/@stable-harness/core/dist/index.d.ts +2 -0
- package/node_modules/@stable-harness/core/dist/index.js +1 -1
- package/node_modules/@stable-harness/core/dist/runtime/events.d.ts +6 -2
- package/node_modules/@stable-harness/core/dist/runtime/progress-narration.js +1 -1
- package/node_modules/@stable-harness/core/dist/runtime/tracing/langsmith.d.ts +27 -0
- package/node_modules/@stable-harness/core/dist/runtime/tracing/langsmith.js +1 -0
- package/node_modules/@stable-harness/core/dist/runtime.d.ts +2 -0
- package/node_modules/@stable-harness/core/dist/runtime.js +1 -1
- package/node_modules/@stable-harness/core/dist/trace.d.ts +26 -0
- package/node_modules/@stable-harness/core/dist/trace.js +1 -1
- package/node_modules/@stable-harness/core/dist/workspace/types.d.ts +1 -0
- package/node_modules/@stable-harness/protocols/dist/src/http-server.js +1 -1
- package/node_modules/@stable-harness/tool-gateway/dist/src/module-loader.js +1 -1
- package/node_modules/@stable-harness/workspace-yaml/dist/documents.js +1 -1
- package/package.json +5 -2
- package/packages/adapter-deepagents/dist/src/adapter.js +1 -1
- package/packages/cli/dist/src/args.d.ts +3 -1
- package/packages/cli/dist/src/args.js +1 -1
- package/packages/cli/dist/src/build.d.ts +9 -0
- package/packages/cli/dist/src/build.js +1 -0
- package/packages/cli/dist/src/cli.js +1 -1
- package/packages/cli/dist/src/index.d.ts +1 -0
- package/packages/cli/dist/src/index.js +1 -1
- package/packages/cli/dist/src/langgraph-official.js +1 -1
- package/packages/core/dist/index.d.ts +2 -0
- package/packages/core/dist/index.js +1 -1
- package/packages/core/dist/runtime/events.d.ts +6 -2
- package/packages/core/dist/runtime/progress-narration.js +1 -1
- package/packages/core/dist/runtime/tracing/langsmith.d.ts +27 -0
- package/packages/core/dist/runtime/tracing/langsmith.js +1 -0
- package/packages/core/dist/runtime.d.ts +2 -0
- package/packages/core/dist/runtime.js +1 -1
- package/packages/core/dist/trace.d.ts +26 -0
- package/packages/core/dist/trace.js +1 -1
- package/packages/core/dist/workspace/types.d.ts +1 -0
- package/packages/protocols/dist/src/http-server.js +1 -1
- package/packages/tool-gateway/dist/src/module-loader.js +1 -1
- package/packages/workspace-yaml/dist/documents.js +1 -1
package/README.md
CHANGED
|
@@ -84,6 +84,18 @@ Then point compatible clients at:
|
|
|
84
84
|
http://127.0.0.1:8642/v1
|
|
85
85
|
```
|
|
86
86
|
|
|
87
|
+
Build a portable Docker runtime artifact for the workspace:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
stable-harness build --target docker -w ./examples/minimal-deepagents --output ./dist/workspace-docker
|
|
91
|
+
cd ./dist/workspace-docker
|
|
92
|
+
docker compose up --build
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
The generated Compose file mounts persistent runtime data at `/data`. The build
|
|
96
|
+
copies the workspace while excluding local secrets and build output such as
|
|
97
|
+
`.env`, `.git`, `node_modules`, and `dist`.
|
|
98
|
+
|
|
87
99
|
## Embed In An App
|
|
88
100
|
|
|
89
101
|
```ts
|
|
@@ -211,6 +223,7 @@ This is constrained repair, not silent magic:
|
|
|
211
223
|
- [Documentation index](docs/guides/index.md)
|
|
212
224
|
- [Getting started](docs/guides/getting-started.md)
|
|
213
225
|
- [Workspace authoring](docs/guides/workspace-authoring.md)
|
|
226
|
+
- [Workspace Docker build](docs/guides/workspace-docker-build.md)
|
|
214
227
|
- [Integration guide](docs/guides/integration-guide.md)
|
|
215
228
|
- [Operator runbook](docs/guides/operator-runbook.md)
|
|
216
229
|
- [Adoption playbook](docs/product/adoption-playbook.md)
|
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ 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 { applySpecDrivenPhaseTransition, containsRecoverableResultOutput, createSpecDrivenArtifact, createSpecDrivenArtifactEvent, createSpecDrivenPhaseEvent, createSpecDrivenWorkflowPolicy, createSpecDrivenWorkflowState, projectRuntimeTrace, } from "@stable-harness/core";
|
|
8
|
+
export { applySpecDrivenPhaseTransition, containsRecoverableResultOutput, createSpecDrivenArtifact, createSpecDrivenArtifactEvent, createSpecDrivenPhaseEvent, createSpecDrivenWorkflowPolicy, createSpecDrivenWorkflowState, projectRuntimeTrace, resolveEnabledMemories, } from "@stable-harness/core";
|
|
9
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";
|
|
10
10
|
export { loadWorkspaceFromYaml } from "@stable-harness/workspace-yaml";
|
|
11
11
|
export { createInMemoryToolGateway, createModuleToolGateway } from "@stable-harness/tool-gateway";
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createBackendModel as e,createDeepAgentsAdapter as r}from"@stable-harness/adapter-deepagents";import{createLangGraphRuntimeAdapter as t,createLangGraphWorkflowAdapter as a,createRegistrySkillResolverProvider as o}from"@stable-harness/adapter-langgraph";import{createStableHarnessRuntime as n}from"@stable-harness/core";import{createModuleToolGateway as i}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):n(e)}export async function createStableRuntime(e){const r=await s(e.workspaceRoot),t=e.toolGateway??await i({tools:r.tools.values()});return n({workspace:r,toolGateway:t,qualityReviewModel:createQualityReviewModel(r),adapters:e.adapters??createRuntimeAdapters(r,e),workflowAdapters:e.workflowAdapters??createWorkflowAdapters(r,e)})}function createQualityReviewModel(r){const t=function readQualityModelRef(e){const r=isRecord(e)?e:{};return readString((isRecord(r.reviewer)?r.reviewer:r).modelRef)}(r.runtime.quality),a=t?r.models.get(t):void 0,o=a?e(a):void 0;return function isQualityReviewModel(e){return isRecord(e)&&"function"==typeof e.invoke}(o)?o:void 0}export async function requestStableRuntime(e,r){return e.request(r)}function createRuntimeAdapters(e,a){const o={deepagents:({policy:e})=>r(e.config?{config:e.config}:{}),langgraph:({policy:e})=>t({...readLangGraphOptions(e.config),name:e.name}),...a.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}))}(e);return n.map(r=>{const t=o[r.name];if(t)return t({policy:r,workspace:e});throw new Error(`Unsupported runtime adapter: ${r.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 t=readString(r.provider)??readString(r.name)??"registry-resolver";if(["none","disabled","false"].includes(t))return!1;if("registry-resolver"!==t)throw new Error(`Unsupported LangGraph skill provider: ${t}`);return o({..."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
|
+
import{createBackendModel as e,createDeepAgentsAdapter as r}from"@stable-harness/adapter-deepagents";import{createLangGraphRuntimeAdapter as t,createLangGraphWorkflowAdapter as a,createRegistrySkillResolverProvider as o}from"@stable-harness/adapter-langgraph";import{createStableHarnessRuntime as n}from"@stable-harness/core";import{createModuleToolGateway as i}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,resolveEnabledMemories}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):n(e)}export async function createStableRuntime(e){const r=await s(e.workspaceRoot),t=e.toolGateway??await i({tools:r.tools.values()});return n({workspace:r,toolGateway:t,qualityReviewModel:createQualityReviewModel(r),adapters:e.adapters??createRuntimeAdapters(r,e),workflowAdapters:e.workflowAdapters??createWorkflowAdapters(r,e)})}function createQualityReviewModel(r){const t=function readQualityModelRef(e){const r=isRecord(e)?e:{};return readString((isRecord(r.reviewer)?r.reviewer:r).modelRef)}(r.runtime.quality),a=t?r.models.get(t):void 0,o=a?e(a):void 0;return function isQualityReviewModel(e){return isRecord(e)&&"function"==typeof e.invoke}(o)?o:void 0}export async function requestStableRuntime(e,r){return e.request(r)}function createRuntimeAdapters(e,a){const o={deepagents:({policy:e})=>r(e.config?{config:e.config}:{}),langgraph:({policy:e})=>t({...readLangGraphOptions(e.config),name:e.name}),...a.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}))}(e);return n.map(r=>{const t=o[r.name];if(t)return t({policy:r,workspace:e});throw new Error(`Unsupported runtime adapter: ${r.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 t=readString(r.provider)??readString(r.name)??"registry-resolver";if(["none","disabled","false"].includes(t))return!1;if("registry-resolver"!==t)throw new Error(`Unsupported LangGraph skill provider: ${t}`);return o({..."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)}
|
|
@@ -226,6 +226,16 @@ not replace original facts or create execution semantics.
|
|
|
226
226
|
| `runtime.trace.spec` | Trace category `spec` | `runtime.specDriven.phase.*` | Spec-driven phase timeline. |
|
|
227
227
|
| `runtime.trace.plan` | Trace category `plan` | `runtime.adapter.event.traceType: "plan"` | Plan/TODO presentation. |
|
|
228
228
|
| `runtime.trace.delegation` | Trace category `delegation` | `runtime.adapter.event.traceType: "delegation"` | Delegation presentation. |
|
|
229
|
+
| `runtime.trace.spans` | Span projection from stored events | `projectRuntimeTraceSpans(run)` / `GET /runs/:id/spans` | Studio-style client tree input. |
|
|
230
|
+
|
|
231
|
+
Span projection is a view layer. It adds `spanId`, optional `parentSpanId`,
|
|
232
|
+
`kind`, `name`, `status`, event references, and timing from event metadata. It
|
|
233
|
+
must not create execution semantics or infer domain-specific routing.
|
|
234
|
+
|
|
235
|
+
`runtime.tracing.langsmith` is an optional export capability over this span
|
|
236
|
+
projection. It writes the stored run tree to LangSmith after request completion;
|
|
237
|
+
it is not an adapter and must not participate in planning, tool invocation, or
|
|
238
|
+
recovery decisions.
|
|
229
239
|
|
|
230
240
|
### 3.2 Category: Runtime Stream Views
|
|
231
241
|
|
package/docs/guides/index.md
CHANGED
|
@@ -12,6 +12,8 @@ embed it, operate it, or explain why it exists.
|
|
|
12
12
|
workflows, memory, and protocol exposure in YAML.
|
|
13
13
|
- [Integration guide](integration-guide.md): embed the runtime inside an app,
|
|
14
14
|
run it through CLI, or expose it through HTTP/OpenAI-compatible clients.
|
|
15
|
+
- [Workspace Docker build](workspace-docker-build.md): package a workspace as a
|
|
16
|
+
portable Docker runtime with a generic persistent data mount.
|
|
15
17
|
- [Quality gates](quality-gates.md): enable plan review, execution evidence
|
|
16
18
|
review, and configured recovery loops without replacing upstream planning.
|
|
17
19
|
- [Operator runbook](operator-runbook.md): validate a workspace, inspect
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Workspace Docker Build
|
|
2
|
+
|
|
3
|
+
Stable Harness can package a workspace as a portable Docker runtime artifact.
|
|
4
|
+
This is a distribution boundary for a complete agent workspace, not a new agent
|
|
5
|
+
execution framework and not a cloud-specific deploy command.
|
|
6
|
+
|
|
7
|
+
## Build
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
stable-harness build --target docker -w ./my-agent-app --output ./dist/workspace-docker
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The output directory contains:
|
|
14
|
+
|
|
15
|
+
```text
|
|
16
|
+
Dockerfile
|
|
17
|
+
docker-compose.yaml
|
|
18
|
+
.dockerignore
|
|
19
|
+
.env.example
|
|
20
|
+
stable-workspace.json
|
|
21
|
+
workspace/
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
`workspace/` is a copy of the source workspace. The copy excludes local build
|
|
25
|
+
and secret-bearing paths such as `.env`, `.git`, `node_modules`, and `dist`.
|
|
26
|
+
|
|
27
|
+
## Run
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
cd ./dist/workspace-docker
|
|
31
|
+
docker compose up --build
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The generated image starts the OpenAI-compatible Stable Harness runtime:
|
|
35
|
+
|
|
36
|
+
```text
|
|
37
|
+
http://127.0.0.1:3000/v1
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Use another host port if `3000` is already allocated:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
STABLE_HARNESS_PORT=8642 docker compose up --build
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Persistence
|
|
47
|
+
|
|
48
|
+
The generated Compose file creates a named volume:
|
|
49
|
+
|
|
50
|
+
```text
|
|
51
|
+
stable-harness-data -> /data
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Runtime services can use `/data` for persistent memory, artifacts, run state, or
|
|
55
|
+
other runtime-owned storage. Workspace business data should stay owned by the
|
|
56
|
+
application that defines it.
|
|
57
|
+
|
|
58
|
+
## Versioning
|
|
59
|
+
|
|
60
|
+
The Dockerfile pins the Stable Harness package version used by the build
|
|
61
|
+
command. It installs that package into an isolated `/stable-runtime` npm project
|
|
62
|
+
so runtime dependencies resolve like a normal application install:
|
|
63
|
+
|
|
64
|
+
```dockerfile
|
|
65
|
+
ARG STABLE_HARNESS_PACKAGE=stable-harness@<current package version>
|
|
66
|
+
WORKDIR /stable-runtime
|
|
67
|
+
RUN npm init -y && npm install "${STABLE_HARNESS_PACKAGE}"
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Override the version during image build when needed:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
docker compose build --build-arg STABLE_HARNESS_PACKAGE=stable-harness@latest
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Boundary
|
|
77
|
+
|
|
78
|
+
Stable Harness owns the generic workspace-to-Docker artifact:
|
|
79
|
+
|
|
80
|
+
- workspace inventory packaging
|
|
81
|
+
- runtime entrypoint
|
|
82
|
+
- protocol exposure
|
|
83
|
+
- generic data mount
|
|
84
|
+
- build manifest
|
|
85
|
+
|
|
86
|
+
Product layers such as Flev can consume this artifact and add their own product
|
|
87
|
+
templates, dashboard, account model, deployment target, and business data
|
|
88
|
+
layout without leaking those assumptions into Stable Harness core.
|
|
@@ -25,6 +25,7 @@ Inspection endpoints expose normalized runtime state:
|
|
|
25
25
|
- `GET /requests`
|
|
26
26
|
- `GET /requests/:id`
|
|
27
27
|
- `GET /runs/:id/trace`
|
|
28
|
+
- `GET /runs/:id/spans`
|
|
28
29
|
- `GET /sessions`
|
|
29
30
|
- `GET /workflows`
|
|
30
31
|
- `GET /workflows/:id/mermaid`
|
|
@@ -35,3 +36,10 @@ configured spec-driven policy summary, and `GET /requests/:id` includes the
|
|
|
35
36
|
run-derived `specDrivenWorkflow` phase state. That state is projected from
|
|
36
37
|
stored `runtime.specDriven.phase.*` events and preserves the raw events in the
|
|
37
38
|
request timeline.
|
|
39
|
+
|
|
40
|
+
`GET /runs/:id/trace` returns the existing flat trace timeline. `GET
|
|
41
|
+
/runs/:id/spans` returns a tree-ready span projection for Studio-style clients.
|
|
42
|
+
Each span includes `spanId`, optional `parentSpanId`, `kind`, `name`, `status`,
|
|
43
|
+
event references, and timing when the stored events include `emittedAt`.
|
|
44
|
+
Clients should render hierarchy from `parentSpanId` instead of inferring it from
|
|
45
|
+
display labels.
|
|
@@ -61,6 +61,28 @@ The LangGraph service is started through the official
|
|
|
61
61
|
upstream LangGraph behavior. `stable-harness` only loads the environment before
|
|
62
62
|
that server starts.
|
|
63
63
|
|
|
64
|
+
Native Stable Harness spans can also be exported to LangSmith. This is separate
|
|
65
|
+
from LangGraph's upstream tracing path: it projects stored runtime events through
|
|
66
|
+
`projectRuntimeTraceSpans(run)` and writes the resulting run tree to LangSmith at
|
|
67
|
+
request completion.
|
|
68
|
+
|
|
69
|
+
Enable native export in runtime YAML:
|
|
70
|
+
|
|
71
|
+
```yaml
|
|
72
|
+
apiVersion: stable-harness.dev/v1
|
|
73
|
+
kind: Runtime
|
|
74
|
+
spec:
|
|
75
|
+
routing:
|
|
76
|
+
defaultAgentId: orchestra
|
|
77
|
+
tracing:
|
|
78
|
+
langsmith:
|
|
79
|
+
enabled: true
|
|
80
|
+
projectName: stable-harness-local
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The exporter consumes runtime facts only. It does not change backend execution,
|
|
84
|
+
tool selection, delegation, recovery, or protocol behavior.
|
|
85
|
+
|
|
64
86
|
By default, `stable-harness start` reads `.env` from the workspace root for the
|
|
65
87
|
LangGraph service. Existing shell environment variables win over `.env` values.
|
|
66
88
|
For Studio, put your key in the workspace `.env` file:
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{realpathSync as e}from"node:fs";import t from"node:path";import{buildRuntimeSystemPrompt as r}from"@stable-harness/core";import{createBuiltinToolPolicyMiddleware as n,createObserverMiddleware as o}from"./internal/builtin-tool-policy.js";import{resolveFilesystemPermissions as s}from"./internal/builtin/permissions.js";import{createToolRepeatState as a}from"@stable-harness/core";import{buildGatewayTools as i,stringifyDeepAgentResult as p}from"./internal/gateway-tools.js";import{resolveDeepAgentsNativeMemories as
|
|
1
|
+
import{realpathSync as e}from"node:fs";import t from"node:path";import{buildRuntimeSystemPrompt as r}from"@stable-harness/core";import{createBuiltinToolPolicyMiddleware as n,createObserverMiddleware as o}from"./internal/builtin-tool-policy.js";import{resolveFilesystemPermissions as s}from"./internal/builtin/permissions.js";import{createToolRepeatState as a}from"@stable-harness/core";import{buildGatewayTools as i,stringifyDeepAgentResult as p}from"./internal/gateway-tools.js";import{resolveDeepAgentsNativeMemories as c}from"./memory.js";import{buildDeepAgentRequest as d}from"./internal/messages.js";import{createRawToolCallParserMiddleware as l}from"./internal/raw-tool-call-parser.js";import{createBackendModel as u}from"./model-providers.js";import{createDeepAgentsRetryMiddleware as m}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 import(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),o=n(function buildDeepAgentParams(e,t,r){const n={...readDeepAgentsConfig(t),...readDeepAgentsConfig(e.agent.config.deepagents)},o=resolveDeepAgentsSkills(e,e.agent),a=n.permissions??s(e,e.agent),p=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,o),checkpointer:n.checkpointer,store:n.store,middleware:mergeMiddleware(e,e.agent,n.middleware,p),responseFormat:n.responseFormat,contextSchema:n.contextSchema,interruptOn:n.interruptOn,generalPurposeAgent:readBoolean(n.generalPurposeAgent),taskDescription:readString(n.taskDescription),permissions:a,tools:i(e,e.agent.id,e.agent.tools,resolveAgentRepairModel(e,e.agent,n),p),subagents:e.agent.subagents.map(t=>{const r=e.workspace.agents.get(t),n=readDeepAgentsConfig(r?.config.deepagents),o=n.permissions??s(e,r),a=scopedInput(e,r),p=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,p),interruptOn:n.interruptOn,generalPurposeAgent:readBoolean(n.generalPurposeAgent),taskDescription:readString(n.taskDescription),permissions:o,responseFormat:n.responseFormat,tools:i(e,t,r?.tools??[],resolveAgentRepairModel(a,r,n),p),memory:resolveDeepAgentsMemory(e,r),skills:resolveDeepAgentsSkills(e,r)})}),memory:resolveDeepAgentsMemory(e,e.agent),skills:o})}(t,e.config,r)),a=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&&o.streamEvents)return g(t,o.streamEvents(a,{version:"v2",...c}),p);const l=await o.invoke(a,c);return p(l)}}}function buildSystemPrompt(e,t){const n=t?.systemPrompt??readString(t?.config.systemPrompt);return r({workspace:e.workspace,request:e.request,agent:t},n)}function resolveDeepAgentsMemory(e,t){const r=readDeepAgentsStringArray(t?.config,"memory");if(r)return r;const n=[...readAgentMemorySources(e.workspace.root,t),...c(e.workspace).map(e=>`/memories/${e.id}.md`)],o=[...new Set(n)];return o.length>0?o:void 0}function readAgentMemorySources(e,t){return(t?.memory??[]).flatMap(t=>"string"==typeof t&&t.trim()?[backendMemorySourcePath(e,t.trim())]:isRecord(t)&&"string"==typeof t.path&&t.path.trim()?[backendMemorySourcePath(e,t.path.trim())]:[])}function backendMemorySourcePath(e,r){if(r.startsWith("/"))return r;if(t.isAbsolute(r)){const n=t.relative(e,r);return n&&!n.startsWith("..")?`/${n.split(t.sep).join("/")}`:canonicalPath(r)}const n=r.split(t.sep).join("/");return n.startsWith("/")?n:`/${n}`}function resolveDeepAgentsSkills(e,r){const n=readDeepAgentsStringArray(r?.config,"skills");if(n)return n;const o=[...new Set((r?.skills??[]).map(t=>e.workspace.skills.get(t)?.path).filter(e=>"string"==typeof e&&e.trim().length>0).map(r=>function backendSkillSourcePath(e,r){const n=t.dirname(t.dirname(r)),o=t.relative(e,n);return!o||o.startsWith("..")||t.isAbsolute(o)?""===o?"/":canonicalPath(n):`/${o.split(t.sep).join("/")}`}(e.workspace.root,r)))];return o.length>0?o: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,r,s=a(e.workspace.runtime.toolGateway)){const i=Array.isArray(r)?r:[],p=scopedInput(e,t),c=new Set;return[o(p,{observedToolIds:c,repeatState:s}),n(p,{repeatState:s}),...m(e.workspace.runtime.retry),...i,l(p)]}function requestScopedRepeatState(e,t){const r=`deepagents.repeat.${t}`,n=e.requestState?.get(r);if(n)return n;const o=a(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?u(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 canonicalPath(t){try{return e.native(t)}catch{return t}}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}
|
|
@@ -6,11 +6,13 @@ export * from "./recovery/tool-call.js";
|
|
|
6
6
|
export * from "./runtime/persistence/inspection.js";
|
|
7
7
|
export { createWorkspaceSandboxPolicy } from "./runtime/governance/sandbox.js";
|
|
8
8
|
export * from "./memory-plugins.js";
|
|
9
|
+
export { resolveEnabledMemories } from "./memory-plugins/shared.js";
|
|
9
10
|
export * from "./runtime/persistence/queue.js";
|
|
10
11
|
export * from "./runtime/policy/projection.js";
|
|
11
12
|
export * from "./runtime.js";
|
|
12
13
|
export * from "./runtime/selection-repair.js";
|
|
13
14
|
export * from "./runtime/tool-failure.js";
|
|
15
|
+
export * from "./runtime/tracing/langsmith.js";
|
|
14
16
|
export * from "./runtime/policy/tool-invocation.js";
|
|
15
17
|
export * from "./runtime/persistence/stores.js";
|
|
16
18
|
export * from "./trace.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export*from"./runtime/persistence/artifacts.js";export*from"./boundary-scan.js";export*from"./execution-contract.js";export*from"./recovery/tool-call.js";export*from"./runtime/persistence/inspection.js";export{createWorkspaceSandboxPolicy}from"./runtime/governance/sandbox.js";export*from"./memory-plugins.js";export*from"./runtime/persistence/queue.js";export*from"./runtime/policy/projection.js";export*from"./runtime.js";export*from"./runtime/selection-repair.js";export*from"./runtime/tool-failure.js";export*from"./runtime/policy/tool-invocation.js";export*from"./runtime/persistence/stores.js";export*from"./trace.js";export*from"./types.js";export*from"./workspace/tool-quality.js";export*from"./evaluations/index.js";export*from"./quality/index.js";export*from"./spec-driven/index.js";export*from"./workflows/index.js";
|
|
1
|
+
export*from"./runtime/persistence/artifacts.js";export*from"./boundary-scan.js";export*from"./execution-contract.js";export*from"./recovery/tool-call.js";export*from"./runtime/persistence/inspection.js";export{createWorkspaceSandboxPolicy}from"./runtime/governance/sandbox.js";export*from"./memory-plugins.js";export{resolveEnabledMemories}from"./memory-plugins/shared.js";export*from"./runtime/persistence/queue.js";export*from"./runtime/policy/projection.js";export*from"./runtime.js";export*from"./runtime/selection-repair.js";export*from"./runtime/tool-failure.js";export*from"./runtime/tracing/langsmith.js";export*from"./runtime/policy/tool-invocation.js";export*from"./runtime/persistence/stores.js";export*from"./trace.js";export*from"./types.js";export*from"./workspace/tool-quality.js";export*from"./evaluations/index.js";export*from"./quality/index.js";export*from"./spec-driven/index.js";export*from"./workflows/index.js";
|
|
@@ -15,7 +15,11 @@ export type RuntimeInventoryRepairDiagnostic = {
|
|
|
15
15
|
matchSource?: string;
|
|
16
16
|
confidence?: number;
|
|
17
17
|
};
|
|
18
|
-
export type
|
|
18
|
+
export type RuntimeEventMetadata = {
|
|
19
|
+
eventId?: string;
|
|
20
|
+
emittedAt?: string;
|
|
21
|
+
};
|
|
22
|
+
export type RuntimeEvent = RuntimeEventMetadata & ({
|
|
19
23
|
type: "runtime.request.started";
|
|
20
24
|
requestId: string;
|
|
21
25
|
sessionId: string;
|
|
@@ -289,7 +293,7 @@ export type RuntimeEvent = {
|
|
|
289
293
|
sessionId: string;
|
|
290
294
|
agentId: string;
|
|
291
295
|
event: unknown;
|
|
292
|
-
};
|
|
296
|
+
});
|
|
293
297
|
export type RuntimeMemoryHook = "read-before-plan" | "read-before-finalize" | "write-after-run";
|
|
294
298
|
export type RuntimeEventListener = (event: RuntimeEvent) => void;
|
|
295
299
|
export type RuntimeEmit = (event: RuntimeEvent) => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export function resolveProgressNarrator(e){if(!1===e.options)return;const t=readProgressPolicy(e.policy);return e.options?.enabled??t.enabled?e.options?.provider??function createTemplateProgressNarrator(){const e=new Map,t=new Map;return{name:"template",narrate(r,n){var o;"runtime.request.started"===r.type&&(e.set(r.requestId,{language:(o=r.input??"",/\p{Script=Han}/u.test(o)?"zh":void 0),input:r.input}),t.delete(r.requestId));const i=function templateMessage(e,t){const r="zh"===t?.language;return"runtime.request.started"===e.type?r?`我开始处理这个请求:${summarizeText(e.input)}`:`I'm starting on this request: ${summarizeText(e.input)}`:"runtime.request.completed"===e.type?r?"我已经完成执行链,正在交付最终结果。":"I've finished the execution chain and am returning the final result.":"runtime.request.failed"===e.type?r?"执行链失败了,我会保留具体错误方便定位。":"The execution chain failed; I'm keeping the concrete error visible for diagnosis.":"runtime.request.cancelled"===e.type?r?"这个请求已取消,后续执行会停止。":"The request was cancelled, so the remaining execution will stop.":"runtime.tool.direct.started"===e.type?r?`我正在运行 ${e.toolId}。`:`I'm running ${e.toolId}.`:"runtime.tool.direct.completed"===e.type?r?`${e.toolId} 已返回结果,我会继续使用这份证据。`:`${e.toolId} returned; I'll keep using that evidence.`:"runtime.workflow.started"===e.type?r?`我正在启动 workflow ${e.workflowId}。`:`I'm starting workflow ${e.workflowId}.`:"runtime.workflow.completed"===e.type?r?`workflow ${e.workflowId} 已完成。`:`Workflow ${e.workflowId} is complete.`:"runtime.specDriven.phase.started"===e.type?r?`我正在进入 spec-driven 阶段 ${e.phaseId}。`:`I'm starting spec-driven phase ${e.phaseId}.`:"runtime.specDriven.phase.blocked"===e.type?r?`spec-driven 阶段 ${e.phaseId} 被 gate 阻塞。`:`Spec-driven phase ${e.phaseId} is blocked by a gate.`:"runtime.specDriven.phase.completed"===e.type?r?`spec-driven 阶段 ${e.phaseId} 已完成。`:`Spec-driven phase ${e.phaseId} is complete.`:"runtime.specDriven.phase.verified"===e.type?r?`spec-driven 阶段 ${e.phaseId} 已验证。`:`Spec-driven phase ${e.phaseId} is verified.`:"runtime.artifact.created"===e.type?r?"我已经保存一份运行产物。":"I've saved a runtime artifact.":"runtime.execution.contract.failed"===e.type?r?"运行证据没有满足执行契约,我会进入恢复或失败路径。":"The run evidence did not satisfy the execution contract, so I'll recover or fail explicitly.":"runtime.inventory.repair"===e.type?inventoryRepairMessage(e.status,e.diagnostic.layer,r):"runtime.skill.candidate.created"===e.type?r?`我发现一个可沉淀的 skill 候选:${e.name}。`:`I found a reusable skill candidate: ${e.name}.`:e.type.startsWith("runtime.memory.")?function memoryMessage(e,t){return"runtime.memory.lifecycle"===e.type?t?`我进入记忆阶段:${e.hook}。`:`I'm in the memory lifecycle phase: ${e.hook}.`:"runtime.memory.recall.completed"===e.type?t?`我召回了 ${e.recordIds.length} 条相关记忆。`:`I recalled ${e.recordIds.length} relevant memory records.`:"runtime.memory.candidate.submitted"===e.type?t?"我提交了一条候选记忆。":"I submitted a memory candidate.":"runtime.memory.approval.requested"===e.type?t?"我提交了记忆审批请求。":"I requested memory approval.":"runtime.memory.plugin.started"===e.type?t?`我正在运行记忆插件 ${e.provider}。`:`I'm running memory plugin ${e.provider}.`:"runtime.memory.plugin.completed"===e.type?t?`记忆插件 ${e.provider} 已完成。`:`Memory plugin ${e.provider} completed.`:"runtime.memory.plugin.failed"===e.type?t?`记忆插件 ${e.provider} 失败了;主任务会继续,不把它当成执行结果。`:`Memory plugin ${e.provider} failed; the main task will continue without treating it as the result.`:"runtime.memory.maintenance.started"===e.type?t?`我正在维护 ${e.target} 记忆。`:`I'm maintaining ${e.target} memory.`:"runtime.memory.maintenance.completed"===e.type?t?`${e.target} 记忆维护完成。`:`${e.target} memory maintenance completed.`:"runtime.memory.maintenance.failed"===e.type?t?`${e.target} 记忆维护失败。`:`${e.target} memory maintenance failed.`:void 0}(e,r):"runtime.adapter.event"===e.type?function adapterMessage(e,t){if(isRecord(e)){if("agent.handoff"===e.phase)return t?"我把请求交给上游 agent backend,让它负责规划和执行。":"I'm handing the request to the upstream agent backend for planning and execution.";if("agent.tool.start"===e.phase&&"string"==typeof e.toolId)return function toolStartMessage(e,t,r){if("task"===e){const e=function readTaskTarget(e){const t=isRecord(e)?e:{};return readString(t.subagent_type)??readString(t.subagentType)}(t),n=function readDescription(e){const t=isRecord(e)?e:{};return summarizeText(readString(t.description)??readString(t.task))}(t);if(e&&n)return r?`我正在请求 task 工具委派给 ${e}:${n}`:`I'm asking the task tool to delegate to ${e}: ${n}`;if(e)return r?`我正在请求 task 工具委派给 ${e}。`:`I'm asking the task tool to delegate to ${e}.`}return r?`我正在运行 ${e} 收集证据。`:`I'm running ${e} to gather evidence.`}(e.toolId,e.args,t);if("agent.tool.result"===e.phase&&"string"==typeof e.toolId)return function toolResultMessage(e,t,r,n,o){if(r)return o?`${e} 返回错误:${summarizeText(String(r))}`:`${e} returned an error: ${summarizeText(String(r))}`;const i="string"==typeof n?n:function readToolControlStatus(e){const t=function parseToolOutputRecord(e){if(isRecord(e))return e;if("string"==typeof e)try{const t=JSON.parse(e);return isRecord(t)?t:void 0}catch{return}}(e);return"string"==typeof t?.status?t.status:"string"==typeof t?.error?t.error:"string"==typeof e&&e.startsWith("Task delegation target is not in the workspace inventory")?"task_inventory_blocked":void 0}(t);if(i)return function toolControlMessage(e,t,r){return"duplicate_tool_call"===t?r?`${e} 重复调用已复用已有证据。`:`${e} repeated an equivalent call, so the existing evidence was reused.`:"repeated_tool_call_limit"===t?r?`${e} 出现重复调用循环,我会基于已有证据继续收敛。`:`${e} entered a repeated-call loop, so I'll continue from the evidence already collected.`:"tool_argument_error"===t||"tool_argument_validation_failed"===t?r?`${e} 参数被工具 schema 拒绝,我会让 agent 修正参数或改用合适工具。`:`${e} arguments were rejected by the tool schema; I'll have the agent repair them or choose a suitable tool.`:"task_inventory_blocked"===t?r?"task 委派目标不在当前 workspace inventory 中,已被运行时策略阻止。":"The task delegation target is not in the current workspace inventory and was blocked by runtime policy.":r?`${e} 返回运行时控制状态:${t}。`:`${e} returned runtime control status: ${t}.`}(e,i,o);if("task"===e)return o?"委派任务已返回结果,我会基于这些证据继续推进。":"The delegated task returned; I'll keep going with that evidence.";const a=function summarizeToolOutput(e){if("string"==typeof e)return summarizeText(e);if(isRecord(e)){if("string"==typeof e.status)return summarizeText(e.status);if("string"==typeof e.summary)return summarizeText(e.summary)}}(t);return o?`${e} 已返回${a?`:${a}`:",我会继续判断下一步。"}`:`${e} returned${a?`: ${a}`:"; I'll decide the next step from here."}`}(e.toolId,e.output,e.error,e.controlStatus,t);if("agent.langgraph.invoke"===e.phase)return t?"我正在调用 workflow backend。":"I'm invoking the workflow backend.";if("agent.node.completed"===e.phase&&"string"==typeof e.nodeId)return t?`节点 ${e.nodeId} 已完成。`:`Node ${e.nodeId} is complete.`;if("inventory.repair"===e.phase&&"string"==typeof e.status){const r=isRecord(e.diagnostic)?e.diagnostic:{};return inventoryRepairMessage(e.status,readString(r.layer)??"selection",t)}}}(e.event,r):void 0}(r,e.get(r.requestId));if(i&&i!==t.get(r.requestId))return t.set(r.requestId,i),"runtime.request.failed"!==r.type&&"runtime.request.cancelled"!==r.type||(e.delete(r.requestId),t.delete(r.requestId)),function pruneRequestState(e,t){for(;e.size>200;){const r=e.keys().next().value;if(!r)break;e.delete(r),t.delete(r)}}(e,t),{message:i,style:n.style,model:n.model}}}}():void 0}export function createProgressNarrationEvent(e){if(!e.narrator||"runtime.progress.narration"===e.source.type)return;const t=readProgressPolicy(e.policy),r=!1===e.options?void 0:e.options,n=e.narrator.narrate(e.source,{style:r?.style??t.style,model:r?.model??t.model});return function isPromiseLike(e){return isRecord(e)&&"function"==typeof e.then}(n)?n.then(t=>toNarrationEvent(e.source,e.narrator,t)):toNarrationEvent(e.source,e.narrator,n)}export function createProgressNarrationCapability(e){const t=resolveProgressNarrator(e);if(t)return{id:"runtime.progress.narration",onEvent(r,n){try{const o=createProgressNarrationEvent({source:r,narrator:t,options:e.options,policy:e.policy});!function isRuntimeEventPromise(e){return isRecord(e)&&"function"==typeof e.then}(o)?o&&n(o):o.then(e=>{e&&n(e)}).catch(()=>{})}catch{return}}}}function toNarrationEvent(e,t,r){if(r?.message.trim())return{type:"runtime.progress.narration",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agentId,message:r.message,provider:t.name,sourceEventTypes:r.sourceEventTypes??[e.type],sourceEventIds:r.sourceEventIds,model:r.model,style:r.style}}function inventoryRepairMessage(e,t,r){return"repaired"===e?r?`${t} 选择已按 workspace inventory 修正。`:`${t} selection was repaired against the workspace inventory.`:r?`${t} 选择不在 workspace inventory 中,已被阻止。`:`${t} selection was blocked because it is outside the workspace inventory.`}function summarizeText(e){const t=e?.replace(/\s+/gu," ").trim();return t?t.length>120?`${t.slice(0,117)}...`:t:""}function readString(e){return"string"==typeof e&&e.trim()?e:void 0}function readProgressPolicy(e){const t=isRecord(e?.progress)?e.progress:{},r=isRecord(t.narration)?t.narration:{};return{enabled:"boolean"==typeof r.enabled?r.enabled:void 0,style:"string"==typeof r.style?r.style:void 0,model:"string"==typeof r.model?r.model:void 0}}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}
|
|
1
|
+
export function resolveProgressNarrator(e){if(!1===e.options)return;const t=readProgressPolicy(e.policy);return e.options?.enabled??t.enabled?e.options?.provider??function createTemplateProgressNarrator(){const e=new Map,t=new Map;return{name:"template",narrate(r,n){var o;"runtime.request.started"===r.type&&(e.set(r.requestId,{language:(o=r.input??"",/\p{Script=Han}/u.test(o)?"zh":void 0),input:r.input}),t.delete(r.requestId));const i=function templateMessage(e,t){const r="zh"===t?.language;return"runtime.request.started"===e.type?r?`我开始处理这个请求:${summarizeText(e.input)}`:`I'm starting on this request: ${summarizeText(e.input)}`:"runtime.request.completed"===e.type?r?"我已经完成执行链,正在交付最终结果。":"I've finished the execution chain and am returning the final result.":"runtime.request.failed"===e.type?r?"执行链失败了,我会保留具体错误方便定位。":"The execution chain failed; I'm keeping the concrete error visible for diagnosis.":"runtime.request.cancelled"===e.type?r?"这个请求已取消,后续执行会停止。":"The request was cancelled, so the remaining execution will stop.":"runtime.tool.direct.started"===e.type?r?`我正在运行 ${e.toolId}。`:`I'm running ${e.toolId}.`:"runtime.tool.direct.completed"===e.type?r?`${e.toolId} 已返回结果,我会继续使用这份证据。`:`${e.toolId} returned; I'll keep using that evidence.`:"runtime.workflow.started"===e.type?r?`我正在启动 workflow ${e.workflowId}。`:`I'm starting workflow ${e.workflowId}.`:"runtime.workflow.completed"===e.type?r?`workflow ${e.workflowId} 已完成。`:`Workflow ${e.workflowId} is complete.`:"runtime.specDriven.phase.started"===e.type?r?`我正在进入 spec-driven 阶段 ${e.phaseId}。`:`I'm starting spec-driven phase ${e.phaseId}.`:"runtime.specDriven.phase.blocked"===e.type?r?`spec-driven 阶段 ${e.phaseId} 被 gate 阻塞。`:`Spec-driven phase ${e.phaseId} is blocked by a gate.`:"runtime.specDriven.phase.completed"===e.type?r?`spec-driven 阶段 ${e.phaseId} 已完成。`:`Spec-driven phase ${e.phaseId} is complete.`:"runtime.specDriven.phase.verified"===e.type?r?`spec-driven 阶段 ${e.phaseId} 已验证。`:`Spec-driven phase ${e.phaseId} is verified.`:"runtime.artifact.created"===e.type?r?"我已经保存一份运行产物。":"I've saved a runtime artifact.":"runtime.execution.contract.failed"===e.type?r?"运行证据没有满足执行契约,我会进入恢复或失败路径。":"The run evidence did not satisfy the execution contract, so I'll recover or fail explicitly.":"runtime.inventory.repair"===e.type?inventoryRepairMessage(e.status,e.diagnostic.layer,r):"runtime.skill.candidate.created"===e.type?r?`我发现一个可沉淀的 skill 候选:${e.name}。`:`I found a reusable skill candidate: ${e.name}.`:e.type.startsWith("runtime.memory.")?function memoryMessage(e,t){return"runtime.memory.lifecycle"===e.type?t?`我进入记忆阶段:${e.hook}。`:`I'm in the memory lifecycle phase: ${e.hook}.`:"runtime.memory.recall.completed"===e.type?t?`我召回了 ${e.recordIds.length} 条相关记忆。`:`I recalled ${e.recordIds.length} relevant memory records.`:"runtime.memory.candidate.submitted"===e.type?t?"我提交了一条候选记忆。":"I submitted a memory candidate.":"runtime.memory.approval.requested"===e.type?t?"我提交了记忆审批请求。":"I requested memory approval.":"runtime.memory.plugin.started"===e.type?t?`我正在运行记忆插件 ${e.provider}。`:`I'm running memory plugin ${e.provider}.`:"runtime.memory.plugin.completed"===e.type?t?`记忆插件 ${e.provider} 已完成。`:`Memory plugin ${e.provider} completed.`:"runtime.memory.plugin.failed"===e.type?t?`记忆插件 ${e.provider} 失败了;主任务会继续,不把它当成执行结果。`:`Memory plugin ${e.provider} failed; the main task will continue without treating it as the result.`:"runtime.memory.maintenance.started"===e.type?t?`我正在维护 ${e.target} 记忆。`:`I'm maintaining ${e.target} memory.`:"runtime.memory.maintenance.completed"===e.type?t?`${e.target} 记忆维护完成。`:`${e.target} memory maintenance completed.`:"runtime.memory.maintenance.failed"===e.type?t?`${e.target} 记忆维护失败。`:`${e.target} memory maintenance failed.`:void 0}(e,r):"runtime.adapter.event"===e.type?function adapterMessage(e,t){if(isRecord(e)){if("agent.handoff"===e.phase)return t?"我把请求交给上游 agent backend,让它负责规划和执行。":"I'm handing the request to the upstream agent backend for planning and execution.";if("agent.tool.start"===e.phase&&"string"==typeof e.toolId)return function toolStartMessage(e,t,r){if("task"===e){const e=function readTaskTarget(e){const t=isRecord(e)?e:{};return readString(t.subagent_type)??readString(t.subagentType)}(t),n=function readDescription(e){const t=isRecord(e)?e:{};return summarizeText(readString(t.description)??readString(t.task))}(t);if(e&&n)return r?`我正在请求 task 工具委派给 ${e}:${n}`:`I'm asking the task tool to delegate to ${e}: ${n}`;if(e)return r?`我正在请求 task 工具委派给 ${e}。`:`I'm asking the task tool to delegate to ${e}.`}return r?`我正在运行 ${e} 收集证据。`:`I'm running ${e} to gather evidence.`}(e.toolId,e.args,t);if("agent.tool.result"===e.phase&&"string"==typeof e.toolId)return function toolResultMessage(e,t,r,n,o){if(r)return o?`${e} 返回错误:${summarizeText(String(r))}`:`${e} returned an error: ${summarizeText(String(r))}`;const i="string"==typeof n?n:function readToolControlStatus(e){const t=function parseToolOutputRecord(e){if(isRecord(e))return e;if("string"==typeof e)try{const t=JSON.parse(e);return isRecord(t)?t:void 0}catch{return}}(e);return"string"==typeof t?.status?t.status:"string"==typeof t?.error?t.error:"string"==typeof e&&e.startsWith("Task delegation target is not in the workspace inventory")?"task_inventory_blocked":void 0}(t);if(i)return function toolControlMessage(e,t,r){return"duplicate_tool_call"===t?r?`${e} 重复调用已复用已有证据。`:`${e} repeated an equivalent call, so the existing evidence was reused.`:"repeated_tool_call_limit"===t?r?`${e} 出现重复调用循环,我会基于已有证据继续收敛。`:`${e} entered a repeated-call loop, so I'll continue from the evidence already collected.`:"tool_argument_error"===t||"tool_argument_validation_failed"===t?r?`${e} 参数被工具 schema 拒绝,我会让 agent 修正参数或改用合适工具。`:`${e} arguments were rejected by the tool schema; I'll have the agent repair them or choose a suitable tool.`:"task_inventory_blocked"===t?r?"task 委派目标不在当前 workspace inventory 中,已被运行时策略阻止。":"The task delegation target is not in the current workspace inventory and was blocked by runtime policy.":r?`${e} 返回运行时控制状态:${t}。`:`${e} returned runtime control status: ${t}.`}(e,i,o);if("task"===e)return o?"委派任务已返回结果,我会基于这些证据继续推进。":"The delegated task returned; I'll keep going with that evidence.";const a=function summarizeToolOutput(e){if("string"==typeof e)return summarizeText(e);if(isRecord(e)){if("string"==typeof e.status)return summarizeText(e.status);if("string"==typeof e.summary)return summarizeText(e.summary)}}(t);return o?`${e} 已返回${a?`:${a}`:",我会继续判断下一步。"}`:`${e} returned${a?`: ${a}`:"; I'll decide the next step from here."}`}(e.toolId,e.output,e.error,e.controlStatus,t);if("agent.langgraph.invoke"===e.phase)return t?"我正在调用 workflow backend。":"I'm invoking the workflow backend.";if("agent.node.completed"===e.phase&&"string"==typeof e.nodeId)return t?`节点 ${e.nodeId} 已完成。`:`Node ${e.nodeId} is complete.`;if("inventory.repair"===e.phase&&"string"==typeof e.status){const r=isRecord(e.diagnostic)?e.diagnostic:{};return inventoryRepairMessage(e.status,readString(r.layer)??"selection",t)}}}(e.event,r):void 0}(r,e.get(r.requestId));if(i&&i!==t.get(r.requestId))return t.set(r.requestId,i),"runtime.request.failed"!==r.type&&"runtime.request.cancelled"!==r.type||(e.delete(r.requestId),t.delete(r.requestId)),function pruneRequestState(e,t){for(;e.size>200;){const r=e.keys().next().value;if(!r)break;e.delete(r),t.delete(r)}}(e,t),{message:i,style:n.style,model:n.model}}}}():void 0}export function createProgressNarrationEvent(e){if(!e.narrator||"runtime.progress.narration"===e.source.type)return;const t=readProgressPolicy(e.policy),r=!1===e.options?void 0:e.options,n=e.narrator.narrate(e.source,{style:r?.style??t.style,model:r?.model??t.model});return function isPromiseLike(e){return isRecord(e)&&"function"==typeof e.then}(n)?n.then(t=>toNarrationEvent(e.source,e.narrator,t)):toNarrationEvent(e.source,e.narrator,n)}export function createProgressNarrationCapability(e){const t=resolveProgressNarrator(e);if(t)return{id:"runtime.progress.narration",onEvent(r,n){try{const o=createProgressNarrationEvent({source:r,narrator:t,options:e.options,policy:e.policy});!function isRuntimeEventPromise(e){return isRecord(e)&&"function"==typeof e.then}(o)?o&&n(o):o.then(e=>{e&&n(e)}).catch(()=>{})}catch{return}}}}function toNarrationEvent(e,t,r){if(r?.message.trim())return{type:"runtime.progress.narration",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agentId,message:r.message,provider:t.name,sourceEventTypes:r.sourceEventTypes??[e.type],sourceEventIds:r.sourceEventIds??(e.eventId?[e.eventId]:void 0),model:r.model,style:r.style}}function inventoryRepairMessage(e,t,r){return"repaired"===e?r?`${t} 选择已按 workspace inventory 修正。`:`${t} selection was repaired against the workspace inventory.`:r?`${t} 选择不在 workspace inventory 中,已被阻止。`:`${t} selection was blocked because it is outside the workspace inventory.`}function summarizeText(e){const t=e?.replace(/\s+/gu," ").trim();return t?t.length>120?`${t.slice(0,117)}...`:t:""}function readString(e){return"string"==typeof e&&e.trim()?e:void 0}function readProgressPolicy(e){const t=isRecord(e?.progress)?e.progress:{},r=isRecord(t.narration)?t.narration:{};return{enabled:"boolean"==typeof r.enabled?r.enabled:void 0,style:"string"==typeof r.style?r.style:void 0,model:"string"==typeof r.model?r.model:void 0}}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Client } from "langsmith";
|
|
2
|
+
import type { RuntimeCapabilityModule } from "../capabilities.js";
|
|
3
|
+
import type { RuntimeRunRecord, RuntimeStore } from "../types.js";
|
|
4
|
+
import type { WorkspaceRuntimePolicy } from "../../workspace/types.js";
|
|
5
|
+
export type LangSmithTraceClient = {
|
|
6
|
+
createRun(run: LangSmithRunCreate): Promise<void>;
|
|
7
|
+
flush?(): Promise<void>;
|
|
8
|
+
awaitPendingTraceBatches?(): Promise<void>;
|
|
9
|
+
};
|
|
10
|
+
type LangSmithRunCreate = Parameters<Client["createRun"]>[0];
|
|
11
|
+
export type RuntimeLangSmithTracingOptions = {
|
|
12
|
+
enabled?: boolean;
|
|
13
|
+
projectName?: string;
|
|
14
|
+
apiKey?: string;
|
|
15
|
+
apiUrl?: string;
|
|
16
|
+
workspaceId?: string;
|
|
17
|
+
tags?: string[];
|
|
18
|
+
client?: LangSmithTraceClient;
|
|
19
|
+
failOnError?: boolean;
|
|
20
|
+
};
|
|
21
|
+
export declare function createLangSmithTracingCapability(input: {
|
|
22
|
+
policy: WorkspaceRuntimePolicy;
|
|
23
|
+
store: RuntimeStore;
|
|
24
|
+
options?: RuntimeLangSmithTracingOptions | false;
|
|
25
|
+
}): RuntimeCapabilityModule | undefined;
|
|
26
|
+
export declare function createLangSmithRunTree(run: RuntimeRunRecord, options?: RuntimeLangSmithTracingOptions): LangSmithRunCreate;
|
|
27
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createHash as t}from"node:crypto";import{projectRuntimeTraceSpans as e}from"../../trace.js";export function createLangSmithTracingCapability(t){const e=function resolveOptions(t,e){if(!1===e)return{enabled:!1};const n=function readLangSmithConfig(t){const e=readRecord(t.tracing),n=readRecord(e?.langsmith);return{enabled:readBoolean(n?.enabled),projectName:readString(n?.projectName)??readString(n?.project),apiKey:readString(n?.apiKey),apiUrl:readString(n?.apiUrl),workspaceId:readString(n?.workspaceId),tags:(r=n?.tags,Array.isArray(r)?r.filter(t=>"string"==typeof t&&t.trim().length>0):void 0),failOnError:readBoolean(n?.failOnError)};var r}(t);return{...n,...e,enabled:e?.enabled??n.enabled??!1}}(t.policy,t.options);if(!e.enabled)return;const n=function createClientResolver(t){let e;return()=>(e??=async function resolveClient(t){if(t.client)return t.client;const{Client:e}=await import("langsmith");return new e({apiKey:t.apiKey,apiUrl:t.apiUrl,workspaceId:t.workspaceId,autoBatchTracing:!0})}(t),e)}(e),r=function createLangSmithExporter(t,e,n){return async r=>{const a=await n(),i=e.getRun(r);i&&(await a.createRun(createLangSmithRunTree(i,t)),await flushClient(a))}}(e,t.store,n),a=new Set,i=[];return{id:"runtime.tracing.langsmith",onEvent(t){if(!function isTerminalRequestEvent(t){return"runtime.request.completed"===t.type||"runtime.request.failed"===t.type||"runtime.request.cancelled"===t.type}(t))return;const e=r(t.requestId).catch(t=>{i.push(t)});a.add(e),e.finally(()=>a.delete(e)).catch(()=>{})},async stop(){if(await Promise.allSettled([...a]),await flushClient(await n()),e.failOnError&&i[0])throw i[0]}}}export function createLangSmithRunTree(t,n={}){const r=e(t),a=r.find(t=>"run"===t.kind)??r[0];if(!a)return function createFallbackRun(t,e){return{id:stableUuid(`run:${t.requestId}`),name:t.agentId,run_type:"chain",start_time:t.startedAt,end_time:t.completedAt,inputs:{input:t.input},outputs:t.output?{output:t.output}:void 0,error:t.error,project_name:e.projectName}}(t,n);const i=stableUuid(a.spanId);return{...toLangSmithRun(a,i,n),child_runs:childRuns(a.spanId,r,i,n)}}async function flushClient(t){await(t.flush?.()),await(t.awaitPendingTraceBatches?.())}function toLangSmithRun(t,e,n){return{id:stableUuid(t.spanId),name:t.name,run_type:"tool"===t.kind?"tool":"chain",start_time:t.startedAt,end_time:t.completedAt,inputs:{spanId:t.spanId},outputs:"failed"===t.status?void 0:{status:t.status},error:"failed"===t.status?String(t.attributes?.error??"failed"):void 0,parent_run_id:t.parentSpanId?stableUuid(t.parentSpanId):void 0,trace_id:e,project_name:n.projectName,extra:{metadata:metadataForSpan(t,n)}}}function childRuns(t,e,n,r){return e.filter(e=>e.parentSpanId===t).map(t=>({...toLangSmithRun(t,n,r),child_runs:childRuns(t.spanId,e,n,r)}))}function metadataForSpan(t,e){return{stableHarness:{requestId:t.requestId,sessionId:t.sessionId,agentId:t.agentId,kind:t.kind,status:t.status,startEventIndex:t.startEventIndex,endEventIndex:t.endEventIndex,durationMs:t.durationMs,events:t.events,attributes:t.attributes,tags:["stable-harness",...e.tags??[]]}}}function stableUuid(e){const n=t("sha256").update(e).digest("hex").slice(0,32);return`${n.slice(0,8)}-${n.slice(8,12)}-4${n.slice(13,16)}-8${n.slice(17,20)}-${n.slice(20,32)}`}function readRecord(t){return"object"!=typeof t||null===t||Array.isArray(t)?void 0:t}function readString(t){return"string"==typeof t&&t.trim()?t.trim():void 0}function readBoolean(t){return"boolean"==typeof t?t:void 0}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ApprovalQueue } from "@stable-harness/governance";
|
|
2
2
|
import type { MemoryProvider, RuntimeMemoryStore } from "@stable-harness/memory";
|
|
3
3
|
import type { QualityReviewModel } from "./quality/index.js";
|
|
4
|
+
import { createLangSmithTracingCapability } from "./runtime/tracing/langsmith.js";
|
|
4
5
|
import type { CompiledWorkspace, RuntimeCapabilityModule, RuntimeToolGateway, RuntimeAdapter, RuntimeArtifactStore, RuntimeSandboxPolicy, RuntimeStore, RuntimeProgressNarrationOptions, RuntimeWorkflowAdapter, StableHarnessRuntime } from "./types.js";
|
|
5
6
|
type RuntimeFactoryInput = {
|
|
6
7
|
workspace: CompiledWorkspace;
|
|
@@ -16,6 +17,7 @@ type RuntimeFactoryInput = {
|
|
|
16
17
|
progressNarration?: RuntimeProgressNarrationOptions | false;
|
|
17
18
|
qualityReviewModel?: QualityReviewModel;
|
|
18
19
|
capabilities?: RuntimeCapabilityModule[];
|
|
20
|
+
langSmithTracing?: false | Parameters<typeof createLangSmithTracingCapability>[0]["options"];
|
|
19
21
|
};
|
|
20
22
|
export declare function createStableHarnessRuntime(input: RuntimeFactoryInput): StableHarnessRuntime;
|
|
21
23
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{randomUUID as e}from"node:crypto";import{assertExecutionContract as t}from"./execution-contract.js";import{assertNoRawToolCallOutput as r,assertNoToolExecutionErrorOutput as a,buildAdapterErrorRecoveryPrompt as
|
|
1
|
+
import{randomUUID as e}from"node:crypto";import{assertExecutionContract as t}from"./execution-contract.js";import{assertNoRawToolCallOutput as r,assertNoToolExecutionErrorOutput as a,buildAdapterErrorRecoveryPrompt as o,buildEvidenceSynthesisOutput as s,buildExecutionContractRecoveryRequest as n,buildResultRecoveryRequest as i,containsRawToolCallOutput as u,isRecoverableAdapterError as c,rawToolCallFailureMessage as l,toolCallRecoveryEnabled as p}from"./recovery/tool-call.js";import{recoverQualityReview as d,resolveQualityPolicy as m}from"./quality/index.js";import{completeRun as w,failRun as y}from"./runtime/completion.js";import{runDirectToolCall as f}from"./runtime/direct-tool-call.js";import{createApprovalGatedToolGateway as g}from"./runtime/governance/approval-gate.js";import{createSandboxedToolGateway as I}from"./runtime/governance/sandbox.js";import{createRuntimeInspectionMethods as q}from"./runtime/inspection/methods.js";import{createRuntimeCapabilityRegistry as R,normalizeAdapterResult as v}from"./runtime/capabilities.js";import{createMemoryRuntimeCapability as b}from"./runtime/memory.js";import{createInMemoryRuntimeStore as k}from"./runtime/persistence/stores.js";import{createProgressNarrationCapability as A}from"./runtime/progress-narration.js";import{repairRuntimeSelection as C}from"./runtime/selection-repair.js";import{createLangSmithTracingCapability as x}from"./runtime/tracing/langsmith.js";import{createToolFailureTracker as h}from"./runtime/tool-failure.js";import{runWorkflowRequest as j}from"./workflows/runtime.js";export function createStableHarnessRuntime(t){const r=new Set,a=t.store??k(),s=R([b(t),A({options:t.progressNarration,policy:t.workspace.runtime}),x({policy:t.workspace.runtime,store:a,options:t.langSmithTracing}),...t.capabilities??[]]),emitBase=t=>{const o=function enrichRuntimeEvent(t){return{...t,eventId:t.eventId??e(),emittedAt:t.emittedAt??(new Date).toISOString()}}(t);a.appendEvent(o);for(const e of r)e(o)},emit=e=>{emitBase(e),s.emitSideEffects(e,emitBase)},i=I({gateway:g({gateway:t.toolGateway,approvals:t.approvals,workspace:t.workspace,emit:emit}),workspace:t.workspace,sandbox:t.sandbox,emit:emit}),u={...t,toolGateway:i},l=h(function readToolFailurePolicy(e){if("object"!=typeof e||null===e||Array.isArray(e))return;const t=e.failurePolicy;return"object"!=typeof t||null===t||Array.isArray(t)?void 0:t}(t.workspace.runtime.toolGateway));return{request:async t=>async function runRuntimeRequest(t){const r=t.request.requestId??e(),a=t.request.sessionId??e(),s=[],{agent:i,adapter:u}=await async function resolveExecution(e,t,r){const a=t.agentId?await async function resolveRequestedAgentId(e,t,r){if(e.agents.has(t))return t;const a=await C({id:t,candidates:[...e.agents.values()].map(e=>({id:e.id,description:e.description})),trace:{...r,agentId:t,layer:"agent",owner:"stable_runtime_policy"}});return a.ok?a.id:t}(e.workspace,t.agentId,r):function resolveRoutedAgentId(e,t){for(const r of e.runtime.routes??[])if(routeMatches(r,t))return r.agentId;return e.runtime.defaultAgentId}(e.workspace,t.input),o=e.workspace.agents.get(a);if(!o)throw new Error(`Agent ${a} is not defined in the workspace`);if(t.toolCall||t.workflow)return{agent:o,adapter:void 0};const s=e.adapters.find(e=>e.canRun(o));if(!s)throw new Error(`No runtime adapter can run backend ${o.backend} for agent ${o.id}`);return{agent:o,adapter:s}}(t.input,t.request,{requestId:r,sessionId:a,emit:e=>s.push(e)});t.store.createRun(function createRunRecord(e,t,r,a){return{requestId:t,sessionId:r,agentId:a.id,input:e.input,state:"running",parentRunId:e.parentRunId,metadata:e.metadata,artifacts:[],startedAt:(new Date).toISOString(),events:[]}}(t.request,r,a,i)),s.forEach(t.emit),t.emit({type:"runtime.request.started",requestId:r,sessionId:a,agentId:i.id,input:t.request.input});try{if(t.request.workflow){const e=await j({workspace:t.input.workspace,adapters:t.input.workflowAdapters??[],toolGateway:t.input.toolGateway,request:{input:t.request.input,...t.request.workflow},requestId:r,sessionId:a,agentId:i.id,emit:t.emit});return w({store:t.store,emit:t.emit,requestId:r,sessionId:a,agent:i,result:e,artifacts:t.input.artifacts})}if(t.request.toolCall){const e=await f({gateway:t.input.toolGateway,workspace:t.input.workspace,emit:t.emit,request:t.request,requestId:r,sessionId:a,agent:i,toolFailureTracker:t.toolFailureTracker});return w({store:t.store,emit:t.emit,requestId:r,sessionId:a,agent:i,result:e,artifacts:t.input.artifacts})}return await async function runAdapterRequest(e){if(!e.adapter)throw new Error(`No runtime adapter can run backend ${e.agent.backend} for agent ${e.agent.id}`);const t=e.adapter,r=await e.capabilities.beforeAdapterRun(createCapabilityContext(e)),a=r.memory,s=r.pluginMemories??[],i=e.input.workspace.runtime,u=m(e.input.workspace.runtime,e.agent),l=new Map;let p;try{p=await runAdapterOnce(e,t,e.request,a,s,l)}catch(r){if(!c(r,i))throw r;p=await runAdapterOnce(e,t,o(e.request,r,i),a,s,l)}p=await recoverAdapterResultOutput(e,t,e.request,p,a,s,i,l),p=await d(createQualityRuntimeInput(e,a,s,l),e.request,p,u),await e.capabilities.beforeAdapterResultContract({...createCapabilityContext(e),result:p});try{assertRequestExecutionContract(e)}catch(r){const o=n({request:e.request,events:e.store.getRun(e.requestId)?.events??[],policy:i});if(!o)throw r;p=await runAdapterOnce(e,t,o,a,s,l),p=await recoverAdapterResultOutput(e,t,o,p,a,s,i,l),p=await d(createQualityRuntimeInput(e,a,s,l),o,p,u),assertRequestExecutionContract(e)}const y=w({store:e.store,emit:e.emit,requestId:e.requestId,sessionId:e.sessionId,agent:e.agent,result:p,artifacts:e.input.artifacts});return await e.capabilities.afterAdapterResponse({...createCapabilityContext(e),result:p,response:y}),y}({...t,adapter:u,requestId:r,sessionId:a,agent:i})}catch(e){return y({store:t.store,emit:t.emit,requestId:r,sessionId:a,agent:i,error:e})}}({input:u,capabilities:s,store:a,emit:emit,request:t,toolFailureTracker:l}),subscribe:e=>(r.add(e),()=>r.delete(e)),...q({workspace:t.workspace,store:a,artifacts:t.artifacts,approvals:t.approvals,emit:emit}),cancel(e,t){const r=a.getRun(e);r&&"running"===r.state&&(a.updateRun(e,{state:"cancelled",completedAt:(new Date).toISOString()}),emit({type:"runtime.request.cancelled",requestId:e,sessionId:r.sessionId,agentId:r.agentId,reason:t}))},async stop(){await s.stop(),r.clear()}}}function createCapabilityContext(e){return{workspace:e.input.workspace,store:e.store,emit:e.emit,request:e.request,requestId:e.requestId,sessionId:e.sessionId,agent:e.agent}}function createQualityRuntimeInput(e,t,r,a){return{workspace:e.input.workspace,agent:e.agent,request:e.request,requestId:e.requestId,sessionId:e.sessionId,events:e.store.getRun(e.requestId)?.events??[],emit:e.emit,getEvents:()=>e.store.getRun(e.requestId)?.events??[],runAdapter:o=>runAdapterOnce(e,e.adapter,o,t,r,a),reviewModel:e.input.qualityReviewModel,memory:t,pluginMemories:r}}async function recoverAdapterResultOutput(e,t,o,n,c,d,m,w){let y=o;const f=function resultRecoveryAttempts(e){const t="object"!=typeof e||null===e||Array.isArray(e)?void 0:e.recovery,r="object"!=typeof t||null===t||Array.isArray(t)?void 0:t.toolCall,a="object"!=typeof r||null===r||Array.isArray(r)?void 0:r.maxResultRecoveryAttempts;return"number"==typeof a&&Number.isInteger(a)&&a>0?a:3}(m);let g=0;for(let r=0;r<f;r+=1){const r=e.store.getRun(e.requestId)?.events??[],a=i({request:y,output:n.text,events:r.slice(g),availableToolIds:e.agent.tools,policy:m});if(!a)break;y=a,g=e.store.getRun(e.requestId)?.events.length??0,n=await runAdapterOnce(e,t,a,c,d,w)}if(p(m)){let t=!1;u(n.text,m)&&function rawToolCallFailureReturnsMessage(e){return"message"===("object"!=typeof e?.toolCallRecovery||null===e.toolCallRecovery||Array.isArray(e.toolCallRecovery)?{}:e.toolCallRecovery).onFailure}(o.metadata)&&(n={...n,text:l(),metadata:{...n.metadata,toolCallRecovery:{failed:!0,reason:"raw_tool_call_output"}}});const i=s({request:o,output:n.text,events:e.store.getRun(e.requestId)?.events??[],policy:m});i&&(t=!0,n={...n,text:i,metadata:{...n.metadata,toolCallRecovery:{synthesized:!0,reason:"raw_tool_call_output_with_evidence"}}}),t||(r(n.text,m),a(n.text,m))}return n}function assertRequestExecutionContract(e){t({store:e.store,emit:e.emit,requestId:e.requestId,sessionId:e.sessionId,agent:e.agent,metadata:e.request.metadata})}async function runAdapterOnce(e,t,r,a,o,s){return v(await t.run({workspace:e.input.workspace,agent:e.agent,request:r,requestId:e.requestId,sessionId:e.sessionId,memory:a,pluginMemories:o,toolGateway:e.input.toolGateway,toolFailureTracker:e.input.toolFailureTracker,requestState:s,getEvents:()=>e.store.getRun(e.requestId)?.events??[],emit:e.emit}))}function routeMatches(e,t){if(e.pattern)try{if(new RegExp(e.pattern,"iu").test(t))return!0}catch{return!1}const r=t.toLowerCase();return(e.keywords??[]).some(e=>r.includes(e.toLowerCase()))}
|
|
@@ -14,8 +14,34 @@ export type RuntimeAdapterTraceProjection = {
|
|
|
14
14
|
traceType: RuntimeTraceEntry["type"];
|
|
15
15
|
traceLabel: string;
|
|
16
16
|
} & Record<string, unknown>;
|
|
17
|
+
export type RuntimeTraceSpanKind = "run" | "tool" | "workflow" | "spec" | "adapter" | "memory" | "artifact" | "progress" | "plan" | "delegation" | "approval" | "quality";
|
|
18
|
+
export type RuntimeTraceSpanStatus = "running" | "completed" | "failed" | "blocked" | "event";
|
|
19
|
+
export type RuntimeTraceSpanEventRef = {
|
|
20
|
+
index: number;
|
|
21
|
+
eventId: string;
|
|
22
|
+
type: RuntimeEvent["type"];
|
|
23
|
+
emittedAt?: string;
|
|
24
|
+
};
|
|
25
|
+
export type RuntimeTraceSpan = {
|
|
26
|
+
spanId: string;
|
|
27
|
+
parentSpanId?: string;
|
|
28
|
+
requestId: string;
|
|
29
|
+
sessionId: string;
|
|
30
|
+
agentId: string;
|
|
31
|
+
kind: RuntimeTraceSpanKind;
|
|
32
|
+
name: string;
|
|
33
|
+
status: RuntimeTraceSpanStatus;
|
|
34
|
+
startedAt?: string;
|
|
35
|
+
completedAt?: string;
|
|
36
|
+
startEventIndex: number;
|
|
37
|
+
endEventIndex?: number;
|
|
38
|
+
durationMs?: number;
|
|
39
|
+
attributes?: Record<string, unknown>;
|
|
40
|
+
events: RuntimeTraceSpanEventRef[];
|
|
41
|
+
};
|
|
17
42
|
export declare function createDelegationTraceProjection(label: "delegation.start" | "delegation.completed", detail?: Record<string, unknown>): RuntimeAdapterTraceProjection;
|
|
18
43
|
export declare function createPlanTraceProjection(label: "plan.updated", detail?: Record<string, unknown>): RuntimeAdapterTraceProjection;
|
|
19
44
|
export declare function projectRuntimeTrace(run: RuntimeRunRecord): RuntimeTraceEntry[];
|
|
45
|
+
export declare function projectRuntimeTraceSpans(run: RuntimeRunRecord): RuntimeTraceSpan[];
|
|
20
46
|
export declare function projectEvent(event: RuntimeEvent): RuntimeTraceEntry | undefined;
|
|
21
47
|
export declare function readPlanTodos(output: unknown): PlanTodoItem[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export function createDelegationTraceProjection(e,t={}){return{traceType:"delegation",traceLabel:e,...t}}export function createPlanTraceProjection(e,t={}){return{traceType:"plan",traceLabel:e,...t}}export function projectRuntimeTrace(e){return e.events.map(projectEvent).filter(isTraceEntry)}export function projectEvent(e){return"runtime.request.started"===e.type||"runtime.request.completed"===e.type||"runtime.request.failed"===e.type?base(e,"request",e.type):"runtime.request.cancelled"===e.type?base(e,"request",e.type,{reason:e.reason}):"runtime.execution.contract.failed"===e.type?base(e,"request",e.type,{reason:e.reason,missingEvidenceTools:e.missingEvidenceTools}):"runtime.inventory.repair"===e.type?base(e,"adapter","runtime.inventory.repair",{status:e.status,...e.diagnostic}):"runtime.tool.direct.started"===e.type?base(e,"tool","runtime.tool.direct.started",{toolId:e.toolId}):"runtime.tool.direct.completed"===e.type?base(e,"tool","runtime.tool.direct.completed",{toolId:e.toolId}):"runtime.sandbox.decision"===e.type?base(e,"tool","runtime.sandbox.decision",{toolId:e.toolId,...e.decision}):"runtime.tool.failure"===e.type?base(e,"tool","runtime.tool.failure",{toolId:e.toolId,...e.failure}):"runtime.tool.circuit.opened"===e.type?base(e,"tool","runtime.tool.circuit.opened",{toolId:e.toolId,reason:e.reason}):"runtime.workflow.started"===e.type||"runtime.workflow.completed"===e.type?base(e,"workflow",e.type,{workflowId:e.workflowId,adapter:e.adapter}):function isSpecDrivenPhaseEvent(e){return e.type.startsWith("runtime.specDriven.phase.")}(e)?base(e,"spec",e.type,{phaseId:e.phaseId,..."workflowId"in e&&e.workflowId?{workflowId:e.workflowId}:{},..."reason"in e?{reason:e.reason}:{},..."artifact"in e&&e.artifact?{artifact:e.artifact}:{}}):"runtime.adapter.event"===e.type?function adapterTrace(e){const t=e.event;if(isRecord(t)&&"string"==typeof t.phase){const r=function semanticAdapterTrace(e,t){if("inventory.repair"===t.phase)return base(e,"adapter","runtime.inventory.repair",{status:t.status,...isRecord(t.diagnostic)?t.diagnostic:{}});const r=
|
|
1
|
+
export function createDelegationTraceProjection(e,t={}){return{traceType:"delegation",traceLabel:e,...t}}export function createPlanTraceProjection(e,t={}){return{traceType:"plan",traceLabel:e,...t}}export function projectRuntimeTrace(e){return e.events.map(projectEvent).filter(isTraceEntry)}export function projectRuntimeTraceSpans(e){const t=function createSpanBuilder(e){const t=`run:${e.requestId}`,r=[],o=new Map;return{ensureRoot(){if(o.has(t))return;const n={spanId:t,requestId:e.requestId,sessionId:e.sessionId,agentId:e.agentId,kind:"run",name:e.agentId,status:(a=e.state,"completed"===a?"completed":"failed"===a?"failed":"cancelled"===a?"blocked":"running"),startedAt:e.startedAt,completedAt:e.completedAt,startEventIndex:0,...e.parentRunId?{parentSpanId:`run:${e.parentRunId}`}:{},events:[]};var a;r.push(n),o.set(t,n)},markRoot(e,r,n,a){const i=o.get(t);i&&(addEvent(i,e,r),i.status=n,i.attributes=merge(i.attributes,a),"running"!==n&&close(i,e,r))},start(e,n,a,i,s,d){const p=createSpan(`${t}/${e}:${s}`,t,n,a,i,s,d);r.push(p),o.set(e,p)},complete(e,n,a,i){const s=o.get(e)??createSpan(`${t}/${e}:${a}`,t,"adapter",e,n,a);return o.has(e)||r.push(s),addEvent(s,n,a),s.attributes=merge(s.attributes,i),close(s,n,a),o.delete(e),s},fail(e,t,r,o){this.complete(e,t,r,o).status="failed"},event(e,o,n,a,i){const s=createSpan(`${t}/event:${a}`,t,e,o,n,a,i);s.status=function readBlockedStatus(e){return e.type.endsWith(".blocked")||e.type.includes(".approval.")?"blocked":void 0}(n)??"event",close(s,n,a),r.push(s)},closeRoot(){const r=o.get(t);r&&e.completedAt&&(r.completedAt=e.completedAt)},spans:()=>r}}(e);return t.ensureRoot(),e.events.forEach((e,r)=>function projectSpanEvent(e,t,r){return t.type.startsWith("runtime.request.")?function projectRequestSpanEvent(e,t,r){return"runtime.request.started"===t.type?e.markRoot(t,r,"running",{input:t.input}):"runtime.request.completed"===t.type?e.markRoot(t,r,"completed",{output:t.output}):"runtime.request.failed"===t.type?e.markRoot(t,r,"failed",{error:t.error}):"runtime.request.cancelled"===t.type?e.markRoot(t,r,"blocked",{reason:t.reason}):"runtime.execution.contract.failed"===t.type?projectSingleSpan(e,t,r,"quality",t.type,{reason:t.reason,missingEvidenceTools:t.missingEvidenceTools}):void 0}(e,t,r):"runtime.tool.direct.started"===t.type?e.start(`tool:${t.toolId}`,"tool",t.toolId,t,r,{toolId:t.toolId}):"runtime.tool.direct.completed"===t.type?e.complete(`tool:${t.toolId}`,t,r,{toolId:t.toolId,output:t.output}):"runtime.workflow.started"===t.type?e.start(`workflow:${t.workflowId}`,"workflow",t.workflowId,t,r,{workflowId:t.workflowId,adapter:t.adapter}):"runtime.workflow.completed"===t.type?e.complete(`workflow:${t.workflowId}`,t,r,{workflowId:t.workflowId,adapter:t.adapter}):t.type.startsWith("runtime.approval.")||t.type.startsWith("runtime.memory.approval.")?projectSingleSpan(e,t,r,"approval",t.type):t.type.startsWith("runtime.specDriven.phase.")?projectSingleSpan(e,t,r,"spec",function readPhaseName(e,t){return"phaseId"in e&&"string"==typeof e.phaseId?e.phaseId:t}(t,"phase")):t.type.startsWith("runtime.memory.")?projectSingleSpan(e,t,r,"memory",t.type):t.type.startsWith("runtime.quality.")?projectSingleSpan(e,t,r,"quality",t.type):"runtime.artifact.created"===t.type?projectSingleSpan(e,t,r,"artifact",t.artifact.id,{artifact:t.artifact}):"runtime.progress.narration"===t.type?projectSingleSpan(e,t,r,"progress",t.message,{provider:t.provider}):"runtime.adapter.event"===t.type?function projectAdapterSpanEvent(e,t,r){const o=isRecord(t.event)?t.event:void 0,n=readString(o?.phase)??"runtime.adapter.event",a=readTraceType(o?.traceType),i=readString(o?.traceLabel);return"delegation"===a&&i?.endsWith(".start")?e.start(delegationKey(o),"delegation",readString(o?.subagentType)??i,t,r,o):"delegation"===a&&i?.endsWith(".completed")?e.complete(delegationKey(o),t,r,o):"plan"===a&&i?projectSingleSpan(e,t,r,"plan",i,o):"agent.tool.start"===n?e.start(`agent-tool:${readString(o?.toolId)??"unknown"}`,"tool",readString(o?.toolId)??n,t,r,o):"agent.tool.result"===n?e.complete(`agent-tool:${readString(o?.toolId)??"unknown"}`,t,r,o):projectSingleSpan(e,t,r,"adapter",n,o)}(e,t,r):"runtime.inventory.repair"===t.type?projectSingleSpan(e,t,r,"adapter","runtime.inventory.repair",{status:t.status,...t.diagnostic}):"runtime.sandbox.decision"===t.type?projectSingleSpan(e,t,r,"tool",`sandbox:${t.toolId}`,{toolId:t.toolId,...t.decision}):"runtime.tool.failure"===t.type?e.fail(`tool:${t.toolId}`,t,r,{toolId:t.toolId,...t.failure}):"runtime.tool.circuit.opened"===t.type?projectSingleSpan(e,t,r,"tool",`circuit:${t.toolId}`,{toolId:t.toolId,reason:t.reason}):void 0}(t,e,r)),t.closeRoot(),t.spans()}export function projectEvent(e){return"runtime.request.started"===e.type||"runtime.request.completed"===e.type||"runtime.request.failed"===e.type?base(e,"request",e.type):"runtime.request.cancelled"===e.type?base(e,"request",e.type,{reason:e.reason}):"runtime.execution.contract.failed"===e.type?base(e,"request",e.type,{reason:e.reason,missingEvidenceTools:e.missingEvidenceTools}):"runtime.inventory.repair"===e.type?base(e,"adapter","runtime.inventory.repair",{status:e.status,...e.diagnostic}):"runtime.tool.direct.started"===e.type?base(e,"tool","runtime.tool.direct.started",{toolId:e.toolId}):"runtime.tool.direct.completed"===e.type?base(e,"tool","runtime.tool.direct.completed",{toolId:e.toolId}):"runtime.sandbox.decision"===e.type?base(e,"tool","runtime.sandbox.decision",{toolId:e.toolId,...e.decision}):"runtime.tool.failure"===e.type?base(e,"tool","runtime.tool.failure",{toolId:e.toolId,...e.failure}):"runtime.tool.circuit.opened"===e.type?base(e,"tool","runtime.tool.circuit.opened",{toolId:e.toolId,reason:e.reason}):"runtime.workflow.started"===e.type||"runtime.workflow.completed"===e.type?base(e,"workflow",e.type,{workflowId:e.workflowId,adapter:e.adapter}):function isSpecDrivenPhaseEvent(e){return e.type.startsWith("runtime.specDriven.phase.")}(e)?base(e,"spec",e.type,{phaseId:e.phaseId,..."workflowId"in e&&e.workflowId?{workflowId:e.workflowId}:{},..."reason"in e?{reason:e.reason}:{},..."artifact"in e&&e.artifact?{artifact:e.artifact}:{}}):"runtime.adapter.event"===e.type?function adapterTrace(e){const t=e.event;if(isRecord(t)&&"string"==typeof t.phase){const r=function semanticAdapterTrace(e,t){if("inventory.repair"===t.phase)return base(e,"adapter","runtime.inventory.repair",{status:t.status,...isRecord(t.diagnostic)?t.diagnostic:{}});const r=readTraceType(t.traceType),o=readString(t.traceLabel);return r&&o?base(e,r,o,t):void 0}(e,t);return r||base(e,"adapter",t.phase.startsWith("agent.")?t.phase:`adapter.${t.phase}`,t)}return base(e,"adapter","runtime.adapter.event",{event:t})}(e):"runtime.artifact.created"===e.type?base(e,"artifact","runtime.artifact.created",{artifact:e.artifact}):e.type.startsWith("runtime.memory.")?base(e,"memory",e.type):"runtime.progress.narration"===e.type?base(e,"progress",e.type,{message:e.message,provider:e.provider,sourceEventTypes:e.sourceEventTypes}):void 0}export function readPlanTodos(e){const t=function readPlanRecord(e){if(isRecord(e))return e;if("string"==typeof e)try{const t=JSON.parse(e);return isRecord(t)?t:void 0}catch{return}}(e),r=function readTodosArray(e){const t=isRecord(e?.args)?e.args:void 0;return Array.isArray(e?.todos)?e.todos:Array.isArray(t?.todos)?t.todos:[]}(t);return r.map(readTodo).filter(isPlanTodoItem)}function readTodo(e){if(isRecord(e)&&"string"==typeof e.content)return{content:e.content,status:"string"==typeof e.status?e.status:"pending"}}function isPlanTodoItem(e){return void 0!==e}function base(e,t,r,o){return{type:t,label:r,agentId:e.agentId,requestId:e.requestId,detail:o}}function isTraceEntry(e){return void 0!==e}function projectSingleSpan(e,t,r,o,n,a){return e.event(o,n,t,r,a)}function delegationKey(e){return`delegation:${readString(e?.subagentType)??readString(e?.toolId)??"task"}`}function createSpan(e,t,r,o,n,a,i){const s={spanId:e,parentSpanId:t,requestId:n.requestId,sessionId:n.sessionId,agentId:n.agentId,kind:r,name:o,status:"running",startedAt:n.emittedAt,startEventIndex:a,attributes:i,events:[]};return addEvent(s,n,a),s}function addEvent(e,t,r){e.events.push({index:r,eventId:t.eventId??`${t.requestId}:${r}`,type:t.type,emittedAt:t.emittedAt})}function close(e,t,r){e.status="failed"===e.status||"blocked"===e.status?e.status:"completed",e.completedAt=t.emittedAt,e.endEventIndex=r,e.durationMs=function durationMs(e,t){if(!e||!t)return;const r=Date.parse(t)-Date.parse(e);return Number.isFinite(r)&&r>=0?r:void 0}(e.startedAt,e.completedAt)}function merge(e,t){return t?{...e,...t}:e}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function readTraceType(e){const t=readString(e);return"request"===t||"tool"===t||"workflow"===t||"spec"===t||"adapter"===t||"memory"===t||"artifact"===t||"progress"===t||"plan"===t||"delegation"===t?t:void 0}function readString(e){return"string"==typeof e&&e.trim()?e:void 0}
|
|
@@ -66,6 +66,7 @@ export type WorkspaceRuntimePolicy = {
|
|
|
66
66
|
sandbox?: Record<string, unknown>;
|
|
67
67
|
memory?: Record<string, unknown>;
|
|
68
68
|
protocols?: Record<string, unknown>;
|
|
69
|
+
tracing?: Record<string, unknown>;
|
|
69
70
|
progress?: Record<string, unknown>;
|
|
70
71
|
cli?: Record<string, unknown>;
|
|
71
72
|
quality?: string | Record<string, unknown>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createServer as t}from"node:http";import{compileWorkflowPlan as e,projectRuntimeTrace as r,renderWorkflowMermaid as
|
|
1
|
+
import{createServer as t}from"node:http";import{compileWorkflowPlan as e,projectRuntimeTrace as r,projectRuntimeTraceSpans as o,renderWorkflowMermaid as n}from"@stable-harness/core";export function createHttpServer(s){return t(async(t,a)=>{try{if("GET"===t.method&&"/health"===t.url)return void sendJson(a,200,{ok:!0});if("GET"===t.method&&"/inspect"===t.url)return void sendJson(a,200,s.inspect());if("GET"===t.method&&"/requests"===t.url)return void sendJson(a,200,s.listRequests());if("GET"===t.method&&"/sessions"===t.url)return void sendJson(a,200,s.listSessions());const d=function readArtifactList(t){if(!t?.startsWith("/artifacts"))return;const e=new URL(t,"http://stable-harness.local");return"/artifacts"===e.pathname?{...e.searchParams.get("requestId")?{requestId:e.searchParams.get("requestId")}:{},...e.searchParams.get("sessionId")?{sessionId:e.searchParams.get("sessionId")}:{},...e.searchParams.get("agentId")?{agentId:e.searchParams.get("agentId")}:{}}:void 0}(t.url);if("GET"===t.method&&d)return void sendJson(a,200,s.listArtifacts(d));const i=function readArtifactContentId(t){const e=(t??"").match(/^\/artifacts\/([^/]+)\/content$/u);return e?.[1]?decodeURIComponent(e[1]):void 0}(t.url);if("GET"===t.method&&i){const t=s.getArtifact(i),e=s.readArtifact(i);return void sendJson(a,t&&void 0!==e?200:404,t&&void 0!==e?{artifact:t,content:e}:{error:"artifact_content_not_found"})}const u=function readArtifactId(t){const e=(t??"").match(/^\/artifacts\/([^/]+)$/u);return e?.[1]?decodeURIComponent(e[1]):void 0}(t.url);if("GET"===t.method&&u){const t=s.getArtifact(u);return void sendJson(a,t?200:404,t??{error:"artifact_not_found"})}const c=function readApprovalList(t){if(!t?.startsWith("/approvals"))return;const e=new URL(t,"http://stable-harness.local");if("/approvals"!==e.pathname)return;const r=e.searchParams.get("status");return{status:"pending"===r||"approved"===r||"rejected"===r?r:void 0}}(t.url);if("GET"===t.method&&c)return void sendJson(a,200,await s.listApprovals(c.status));const f=function readApprovalDecision(t){const e=(t??"").match(/^\/approvals\/([^/]+)\/(approve|reject)$/u);if(e?.[1]&&e[2])return{id:decodeURIComponent(e[1]),status:"approve"===e[2]?"approved":"rejected"}}(t.url);if("POST"===t.method&&f){const t=await s.resolveApproval(f.id,f.status);return void sendJson(a,t?200:404,t??{error:"approval_not_found"})}if("GET"===t.method&&"/workflows"===t.url)return void sendJson(a,200,s.inspect().workflows);const l=function readWorkflowMermaidId(t){const e=(t??"").match(/^\/workflows\/([^/]+)\/mermaid$/u);return e?.[1]?decodeURIComponent(e[1]):void 0}(t.url);if("GET"===t.method&&l){const t=s.getWorkflow(l);return void sendJson(a,t?200:404,t?{mermaid:n(t)}:{error:"workflow_not_found"})}const p=function readWorkflowPlanId(t){const e=(t??"").match(/^\/workflows\/([^/]+)\/plan$/u);return e?.[1]?decodeURIComponent(e[1]):void 0}(t.url);if("GET"===t.method&&p){const t=s.getWorkflow(p);return void sendJson(a,t?200:404,t?e(t):{error:"workflow_not_found"})}const m=function readRequestInspectionId(t){const e=(t??"").match(/^\/requests\/([^/]+)$/u);return e?.[1]?decodeURIComponent(e[1]):void 0}(t.url);if("GET"===t.method&&m){const t=s.inspectRequest(m);return void sendJson(a,t?200:404,t??{error:"request_not_found"})}const h=function readTraceRequestId(t){const e=(t??"").match(/^\/runs\/([^/]+)\/trace$/u);return e?.[1]?decodeURIComponent(e[1]):void 0}(t.url);if("GET"===t.method&&h){const t=s.getRun(h);return void sendJson(a,t?200:404,t?r(t):{error:"run_not_found"})}const I=function readSpanRequestId(t){const e=(t??"").match(/^\/runs\/([^/]+)\/spans$/u);return e?.[1]?decodeURIComponent(e[1]):void 0}(t.url);if("GET"===t.method&&I){const t=s.getRun(I);return void sendJson(a,t?200:404,t?o(t):{error:"run_not_found"})}const v=function readReplayRequestId(t){const e=(t??"").match(/^\/requests\/([^/]+)\/replay$/u);return e?.[1]?decodeURIComponent(e[1]):void 0}(t.url);if("GET"===t.method&&v){const t=s.exportReplayBundle(v);return void sendJson(a,t?200:404,t??{error:"run_not_found"})}if("POST"===t.method&&"/requests"===t.url){const e=await async function readJson(t){const e=[];for await(const r of t)e.push(Buffer.isBuffer(r)?r:Buffer.from(r));return 0===e.length?{}:JSON.parse(Buffer.concat(e).toString("utf8"))}(t);return void sendJson(a,200,await s.request(function readRuntimeRequest(t){const e=function readToolCall(t){if("object"!=typeof t||null===t)return;const e=t;return"string"==typeof e.toolId?{toolId:e.toolId,args:e.args}:void 0}(t.toolCall),r=function readWorkflow(t){const e=readRecord(t);if(e)return{..."string"==typeof e.workflowId?{workflowId:e.workflowId}:{},..."string"==typeof e.routeId?{routeId:e.routeId}:{},...void 0!==e.input?{input:e.input}:{},..."object"==typeof e.metadata&&e.metadata?{metadata:e.metadata}:{}}}(t.workflow),o=function readMemory(t){const e=readRecord(t);if(e)return{..."string"==typeof e.namespace?{namespace:e.namespace}:{},...!1===e.recall||readRecord(e.recall)?{recall:e.recall}:{},...Array.isArray(e.candidates)?{candidates:e.candidates}:{}}}(t.memory),n=readRecord(t.metadata);return{input:"string"==typeof t.input?t.input:"",..."string"==typeof t.agentId?{agentId:t.agentId}:{},..."string"==typeof t.sessionId?{sessionId:t.sessionId}:{},..."string"==typeof t.requestId?{requestId:t.requestId}:{},..."string"==typeof t.parentRunId?{parentRunId:t.parentRunId}:{},...e?{toolCall:e}:{},...r?{workflow:r}:{},...o?{memory:o}:{},...n?{metadata:n}:{}}}(e)))}sendJson(a,404,{error:"not_found"})}catch(t){sendJson(a,500,{error:t instanceof Error?t.message:String(t)})}})}function readRecord(t){return"object"!=typeof t||null===t||Array.isArray(t)?void 0:t}function sendJson(t,e,r){t.writeHead(e,{"content-type":"application/json"}),t.end(JSON.stringify(r))}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import o from"node:path";import{pathToFileURL as e}from"node:url";import{createInMemoryToolGateway as t}from"./in-memory.js";export async function createModuleToolGateway(o){const e=[];for(const t of o.tools){const o=await loadModuleTool(t);o&&e.push(o)}return t(e,o.options)}async function loadModuleTool(t){if(!t.sourcePath)return;const n=await import(e(o.resolve(t.sourcePath)).href),r=n[t.name??t.id]??n.default;return function hasInvoke(o){return"object"==typeof o&&null!==o&&"invoke"in o&&"function"==typeof o.invoke}(r)?{id:t.id,description:t.description??r.description,schema:t.schema??r.schema,validateArgs:r.validateArgs,invoke:(o,e)=>async function invokeWithWorkspaceCwd(o,e,t){const n=process.cwd();try{return process.chdir(t.workspaceRoot),await o(e,t)}finally{process.chdir(n)}}(r.invoke,o,e)}:void 0}
|
|
1
|
+
import o from"node:path";import{pathToFileURL as e}from"node:url";import{createInMemoryToolGateway as t}from"./in-memory.js";export async function createModuleToolGateway(o){const e=[];for(const t of o.tools){const o=await loadModuleTool(t);o&&e.push(o)}return t(e,o.options)}async function loadModuleTool(t){if(!t.sourcePath||!function isJavaScriptModule(e){const t=o.extname(e);return".mjs"===t||".js"===t||".cjs"===t}(t.sourcePath))return;const n=await import(e(o.resolve(t.sourcePath)).href),r=n[t.name??t.id]??n.default;return function hasInvoke(o){return"object"==typeof o&&null!==o&&"invoke"in o&&"function"==typeof o.invoke}(r)?{id:t.id,description:t.description??r.description,schema:t.schema??r.schema,validateArgs:r.validateArgs,invoke:(o,e)=>async function invokeWithWorkspaceCwd(o,e,t){const n=process.cwd();try{return process.chdir(t.workspaceRoot),await o(e,t)}finally{process.chdir(n)}}(r.invoke,o,e)}:void 0}
|