@sapiom/harness 0.1.4 → 0.1.6
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/CHANGELOG.md +74 -0
- package/dist/cli/bin.js +24 -7
- package/dist/cli/bin.js.map +1 -1
- package/dist/cli/consent.js +1 -1
- package/dist/core/adapters/adapter.d.ts +2 -2
- package/dist/core/api-key-provider.d.ts +79 -0
- package/dist/core/api-key-provider.d.ts.map +1 -0
- package/dist/core/api-key-provider.js +91 -0
- package/dist/core/api-key-provider.js.map +1 -0
- package/dist/core/canvas-run-state.d.ts +99 -0
- package/dist/core/canvas-run-state.d.ts.map +1 -0
- package/dist/core/canvas-run-state.js +191 -0
- package/dist/core/canvas-run-state.js.map +1 -0
- package/dist/core/canvas-svg.d.ts.map +1 -1
- package/dist/core/canvas-svg.js +28 -8
- package/dist/core/canvas-svg.js.map +1 -1
- package/dist/core/canvas-template.d.ts +4 -3
- package/dist/core/canvas-template.d.ts.map +1 -1
- package/dist/core/canvas-template.js +75 -3
- package/dist/core/canvas-template.js.map +1 -1
- package/dist/core/definition-slug-resolver.d.ts +46 -0
- package/dist/core/definition-slug-resolver.d.ts.map +1 -0
- package/dist/core/definition-slug-resolver.js +110 -0
- package/dist/core/definition-slug-resolver.js.map +1 -0
- package/dist/core/execution-detector.d.ts +59 -0
- package/dist/core/execution-detector.d.ts.map +1 -0
- package/dist/core/execution-detector.js +110 -0
- package/dist/core/execution-detector.js.map +1 -0
- package/dist/core/inject/mcp-config.d.ts.map +1 -1
- package/dist/core/inject/mcp-config.js +9 -1
- package/dist/core/inject/mcp-config.js.map +1 -1
- package/dist/core/render-local-run.d.ts +82 -0
- package/dist/core/render-local-run.d.ts.map +1 -0
- package/dist/core/render-local-run.js +105 -0
- package/dist/core/render-local-run.js.map +1 -0
- package/dist/core/render-log-slice.d.ts +30 -0
- package/dist/core/render-log-slice.d.ts.map +1 -0
- package/dist/core/render-log-slice.js +49 -0
- package/dist/core/render-log-slice.js.map +1 -0
- package/dist/core/render-run-state.d.ts +9 -0
- package/dist/core/render-run-state.d.ts.map +1 -0
- package/dist/core/render-run-state.js +120 -0
- package/dist/core/render-run-state.js.map +1 -0
- package/dist/core/run-local-bootstrap.d.ts +93 -0
- package/dist/core/run-local-bootstrap.d.ts.map +1 -0
- package/dist/core/run-local-bootstrap.js +155 -0
- package/dist/core/run-local-bootstrap.js.map +1 -0
- package/dist/core/run-state.d.ts +60 -0
- package/dist/core/run-state.d.ts.map +1 -0
- package/dist/core/run-state.js +122 -0
- package/dist/core/run-state.js.map +1 -0
- package/dist/core/stub-feedback.d.ts +52 -0
- package/dist/core/stub-feedback.d.ts.map +1 -0
- package/dist/core/stub-feedback.js +33 -0
- package/dist/core/stub-feedback.js.map +1 -0
- package/dist/core/workflow-registry.d.ts +12 -1
- package/dist/core/workflow-registry.d.ts.map +1 -1
- package/dist/core/workflow-registry.js +2 -0
- package/dist/core/workflow-registry.js.map +1 -1
- package/dist/server/actions.d.ts +139 -0
- package/dist/server/actions.d.ts.map +1 -0
- package/dist/server/actions.js +430 -0
- package/dist/server/actions.js.map +1 -0
- package/dist/server/auth-routes.d.ts +92 -0
- package/dist/server/auth-routes.d.ts.map +1 -0
- package/dist/server/auth-routes.js +151 -0
- package/dist/server/auth-routes.js.map +1 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +0 -0
- package/dist/server/index.js.map +1 -1
- package/dist/server/rest.d.ts +5 -0
- package/dist/server/rest.d.ts.map +1 -1
- package/dist/server/rest.js +36 -12
- package/dist/server/rest.js.map +1 -1
- package/dist/server/runs.d.ts +34 -0
- package/dist/server/runs.d.ts.map +1 -0
- package/dist/server/runs.js +59 -0
- package/dist/server/runs.js.map +1 -0
- package/dist/server/static.d.ts +15 -1
- package/dist/server/static.d.ts.map +1 -1
- package/dist/server/static.js +54 -4
- package/dist/server/static.js.map +1 -1
- package/dist/shared/types.d.ts +136 -2
- package/dist/shared/types.d.ts.map +1 -1
- package/dist/shared/types.js.map +1 -1
- package/dist/web/assets/index-DCtyI4VW.css +32 -0
- package/dist/web/assets/index-WSw5RvYR.js +445 -0
- package/dist/web/canvas/sess-boot/index.html +305 -0
- package/dist/web/index.html +4 -9
- package/dist/web/welcome-hero-dark.png +0 -0
- package/dist/web/welcome-hero-light.png +0 -0
- package/package.json +6 -3
- package/dist/server/skills.d.ts +0 -51
- package/dist/server/skills.d.ts.map +0 -1
- package/dist/server/skills.js +0 -312
- package/dist/server/skills.js.map +0 -1
- package/dist/web/assets/index-BhKGSTTT.js +0 -214
- package/dist/web/assets/index-DRTrwTku.css +0 -32
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Serve-time enrichment: resolves a deployed agent's `definitionSlug` from the
|
|
3
|
+
* Sapiom Agents API by its `definitionId`. The registry only knows the id (from
|
|
4
|
+
* `sapiom.json`'s `{ "definitionId": "188" }`) — the slug lives server-side,
|
|
5
|
+
* keyed by id.
|
|
6
|
+
*
|
|
7
|
+
* Resolution is cached in-memory (id→slug is stable once deployed; a slug
|
|
8
|
+
* never changes for a given id) so repeat calls across requests are free.
|
|
9
|
+
* Null resolutions are NOT cached: a transient network failure should be
|
|
10
|
+
* retryable without a server restart.
|
|
11
|
+
*
|
|
12
|
+
* Mirrors `@sapiom/tools`'s DEFAULT_BASE_URL for the base URL resolution.
|
|
13
|
+
*/
|
|
14
|
+
/** Returns the agents API base URL, honouring the same env-var precedence as
|
|
15
|
+
* @sapiom/tools's DEFAULT_BASE_URL. */
|
|
16
|
+
export function resolveAgentsBaseUrl() {
|
|
17
|
+
return (process.env.SAPIOM_AGENTS_URL ??
|
|
18
|
+
process.env.SAPIOM_TOOLS_BASE ??
|
|
19
|
+
"https://tools.sapiom.ai");
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Resolve the CORE surface base URL (`api.<env>`), distinct from the agents
|
|
23
|
+
* host (`tools.<env>`) resolved above. A run lives in the agents env, and any
|
|
24
|
+
* core-surface call for that run must target the MATCHING core env — otherwise
|
|
25
|
+
* a prod run queried against dev 401s. So we DERIVE the core host from the
|
|
26
|
+
* agents host (`tools.<env>` → `api.<env>`) rather than reading
|
|
27
|
+
* `SAPIOM_API_URL`, which in some setups points at a different env than the
|
|
28
|
+
* agents surface. An explicit `SAPIOM_CORE_URL` still wins for full control.
|
|
29
|
+
*
|
|
30
|
+
* Co-located with {@link resolveAgentsBaseUrl} (its sole dependency) so the two
|
|
31
|
+
* env-precedence helpers live together.
|
|
32
|
+
*/
|
|
33
|
+
export function resolveCoreBaseUrl() {
|
|
34
|
+
const override = process.env.SAPIOM_CORE_URL;
|
|
35
|
+
if (override)
|
|
36
|
+
return override;
|
|
37
|
+
const agents = resolveAgentsBaseUrl();
|
|
38
|
+
try {
|
|
39
|
+
const url = new URL(agents);
|
|
40
|
+
if (url.hostname.startsWith("tools.")) {
|
|
41
|
+
url.hostname = `api.${url.hostname.slice("tools.".length)}`;
|
|
42
|
+
return url.origin;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// Unparseable agents URL — fall through to the prod default.
|
|
47
|
+
}
|
|
48
|
+
return "https://api.sapiom.ai";
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Creates a resolver that fetches `GET /agents/v1/definitions/<id>` with the
|
|
52
|
+
* caller's API key and returns the `slug` field from the response.
|
|
53
|
+
*
|
|
54
|
+
* Safe to call from any request handler: never throws, returns null on any
|
|
55
|
+
* failure (network, 4xx, missing field, unparseable body).
|
|
56
|
+
*/
|
|
57
|
+
export function createDefinitionSlugResolver(opts) {
|
|
58
|
+
const { apiKey, baseUrl = resolveAgentsBaseUrl(), fetchImpl = fetch } = opts;
|
|
59
|
+
const cache = new Map();
|
|
60
|
+
// Each id's resolution failure is logged at most once. resolve() runs on
|
|
61
|
+
// every /api/state and /api/workflows poll and null results are deliberately
|
|
62
|
+
// not cached (so a transient failure stays retryable), so without this a
|
|
63
|
+
// persistent failure — e.g. the harness signed into an account that can't
|
|
64
|
+
// see this agent — would reprint on every poll. One line per id is enough to
|
|
65
|
+
// diagnose why the snippet panel fell back to the project name.
|
|
66
|
+
const loggedFailures = new Set();
|
|
67
|
+
const warnOnce = (definitionId, reason) => {
|
|
68
|
+
if (loggedFailures.has(definitionId))
|
|
69
|
+
return;
|
|
70
|
+
loggedFailures.add(definitionId);
|
|
71
|
+
console.error(`[harness] could not resolve deployed-agent slug for definitionId=${definitionId} ` +
|
|
72
|
+
`at ${baseUrl} (${reason}); the snippet panel will fall back to the project name — ` +
|
|
73
|
+
`check the harness is signed into the account that owns this agent`);
|
|
74
|
+
};
|
|
75
|
+
return {
|
|
76
|
+
async resolve(definitionId) {
|
|
77
|
+
// Not signed in: expected (a harness launched without auth), not a
|
|
78
|
+
// failure worth logging — the panel's project-name fallback covers it.
|
|
79
|
+
if (!apiKey)
|
|
80
|
+
return null;
|
|
81
|
+
const cached = cache.get(definitionId);
|
|
82
|
+
if (cached !== undefined)
|
|
83
|
+
return cached;
|
|
84
|
+
try {
|
|
85
|
+
const url = `${baseUrl}/agents/v1/definitions/${encodeURIComponent(definitionId)}`;
|
|
86
|
+
const response = await fetchImpl(url, {
|
|
87
|
+
headers: { "x-sapiom-api-key": apiKey },
|
|
88
|
+
});
|
|
89
|
+
if (!response.ok) {
|
|
90
|
+
warnOnce(definitionId, `HTTP ${response.status}`);
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
const body = (await response.json());
|
|
94
|
+
const slug = typeof body.slug === "string" ? body.slug : null;
|
|
95
|
+
if (slug !== null) {
|
|
96
|
+
cache.set(definitionId, slug);
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
warnOnce(definitionId, "response had no string `slug` field");
|
|
100
|
+
}
|
|
101
|
+
return slug;
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
warnOnce(definitionId, err instanceof Error ? err.message : String(err));
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=definition-slug-resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"definition-slug-resolver.js","sourceRoot":"","sources":["../../src/core/definition-slug-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH;wCACwC;AACxC,MAAM,UAAU,oBAAoB;IAClC,OAAO,CACL,OAAO,CAAC,GAAG,CAAC,iBAAiB;QAC7B,OAAO,CAAC,GAAG,CAAC,iBAAiB;QAC7B,yBAAyB,CAC1B,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,kBAAkB;IAChC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;IAC7C,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAC9B,MAAM,MAAM,GAAG,oBAAoB,EAAE,CAAC;IACtC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;QAC5B,IAAI,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YACtC,GAAG,CAAC,QAAQ,GAAG,OAAO,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YAC5D,OAAO,GAAG,CAAC,MAAM,CAAC;QACpB,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,6DAA6D;IAC/D,CAAC;IACD,OAAO,uBAAuB,CAAC;AACjC,CAAC;AAMD;;;;;;GAMG;AACH,MAAM,UAAU,4BAA4B,CAAC,IAK5C;IACC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,oBAAoB,EAAE,EAAE,SAAS,GAAG,KAAK,EAAE,GAAG,IAAI,CAAC;IAC7E,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAC;IACxC,yEAAyE;IACzE,6EAA6E;IAC7E,yEAAyE;IACzE,0EAA0E;IAC1E,6EAA6E;IAC7E,gEAAgE;IAChE,MAAM,cAAc,GAAG,IAAI,GAAG,EAAU,CAAC;IACzC,MAAM,QAAQ,GAAG,CAAC,YAAoB,EAAE,MAAc,EAAQ,EAAE;QAC9D,IAAI,cAAc,CAAC,GAAG,CAAC,YAAY,CAAC;YAAE,OAAO;QAC7C,cAAc,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QACjC,OAAO,CAAC,KAAK,CACX,oEAAoE,YAAY,GAAG;YACjF,MAAM,OAAO,KAAK,MAAM,4DAA4D;YACpF,mEAAmE,CACtE,CAAC;IACJ,CAAC,CAAC;IAEF,OAAO;QACL,KAAK,CAAC,OAAO,CAAC,YAAoB;YAChC,mEAAmE;YACnE,uEAAuE;YACvE,IAAI,CAAC,MAAM;gBAAE,OAAO,IAAI,CAAC;YAEzB,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;YACvC,IAAI,MAAM,KAAK,SAAS;gBAAE,OAAO,MAAM,CAAC;YAExC,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,GAAG,OAAO,0BAA0B,kBAAkB,CAAC,YAAY,CAAC,EAAE,CAAC;gBACnF,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,GAAG,EAAE;oBACpC,OAAO,EAAE,EAAE,kBAAkB,EAAE,MAAM,EAAE;iBACxC,CAAC,CAAC;gBAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;oBACjB,QAAQ,CAAC,YAAY,EAAE,QAAQ,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;oBAClD,OAAO,IAAI,CAAC;gBACd,CAAC;gBAED,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAA4B,CAAC;gBAChE,MAAM,IAAI,GAAG,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;gBAC9D,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;oBAClB,KAAK,CAAC,GAAG,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;gBAChC,CAAC;qBAAM,CAAC;oBACN,QAAQ,CAAC,YAAY,EAAE,qCAAqC,CAAC,CAAC;gBAChE,CAAC;gBACD,OAAO,IAAI,CAAC;YACd,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,QAAQ,CACN,YAAY,EACZ,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CACjD,CAAC;gBACF,OAAO,IAAI,CAAC;YACd,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Execution detection — mirrors {@link PortDetector} (core/port-detector.ts).
|
|
3
|
+
*
|
|
4
|
+
* A run can start two ways in the harness, and this catches both:
|
|
5
|
+
* 1. The CLI prints `✓ Started execution <executionId>` (`sapiom agents run`).
|
|
6
|
+
* 2. The coding agent starts the run via the MCP run tool — the common path —
|
|
7
|
+
* whose result carries the id as an `executionId` field
|
|
8
|
+
* (`"executionId":"<id>"` / `executionId: <id>`).
|
|
9
|
+
* The detector is fed the SAME tool.call output PortDetector is (server/index.ts
|
|
10
|
+
* `onNormalizedEvent`, including the tool RESPONSE summary), so an MCP-triggered
|
|
11
|
+
* run is caught without changing how runs are triggered. It fires `onExecution`
|
|
12
|
+
* once per distinct (session, executionId); the integrator wires that to
|
|
13
|
+
* broadcast `{ type: "execution.started", … }` on /ws/events.
|
|
14
|
+
*
|
|
15
|
+
* Same streaming-safe design as PortDetector: a match touching the very end of
|
|
16
|
+
* the buffered text is held back (the id may still be growing in the next
|
|
17
|
+
* chunk) and finalized by `flush()`, which the integrator calls right after
|
|
18
|
+
* feeding a discrete, already-complete string (one tool.call field).
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* Pure parser: every execution id announced in a chunk of CLI/tool output, in
|
|
22
|
+
* order (empty when the chunk announces none). This is the mutation-tested
|
|
23
|
+
* core — the {@link ExecutionDetector} class only adds streaming buffering and
|
|
24
|
+
* per-session dedupe on top of it.
|
|
25
|
+
*/
|
|
26
|
+
export declare function parseExecutionIds(text: string): string[];
|
|
27
|
+
/** Where a detected run executes. Only `"prod"` is emitted today: the CLI's
|
|
28
|
+
* `Started execution` line is the prod-run announcement; local runs are
|
|
29
|
+
* rendered from their final result, not polled (see the design). */
|
|
30
|
+
export type ExecutionTarget = "prod" | "local";
|
|
31
|
+
export interface ExecutionDetectorDeps {
|
|
32
|
+
onExecution(harnessSessionId: string, executionId: string, target: ExecutionTarget): void;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Stateful, streaming-safe, and dedupes per (session, executionId) so a run id
|
|
36
|
+
* that reappears in output only ever fires `onExecution` once for the session's
|
|
37
|
+
* lifetime. `reset()` clears a session's state on exit so a reused session id
|
|
38
|
+
* starts clean.
|
|
39
|
+
*/
|
|
40
|
+
export declare class ExecutionDetector {
|
|
41
|
+
private readonly deps;
|
|
42
|
+
private readonly buffers;
|
|
43
|
+
private readonly seen;
|
|
44
|
+
constructor(deps: ExecutionDetectorDeps);
|
|
45
|
+
feed(chunk: string, harnessSessionId: string): void;
|
|
46
|
+
/**
|
|
47
|
+
* Finalizes whatever's held back for a session, immediately. Call this right
|
|
48
|
+
* after `feed()` whenever the fed text is a discrete, complete string (one
|
|
49
|
+
* tool.call field): without it, an id landing at the very end — the common
|
|
50
|
+
* shape "✓ Started execution exec_123" — would be held pending forever, since
|
|
51
|
+
* no "next chunk" is coming.
|
|
52
|
+
*/
|
|
53
|
+
flush(harnessSessionId: string): void;
|
|
54
|
+
/** Drops buffered/dedupe state for a session — call on session exit so a
|
|
55
|
+
* reused session id starts clean. */
|
|
56
|
+
reset(harnessSessionId: string): void;
|
|
57
|
+
private tryEmit;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=execution-detector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"execution-detector.d.ts","sourceRoot":"","sources":["../../src/core/execution-detector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAgBH;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAMxD;AAED;;qEAEqE;AACrE,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,OAAO,CAAC;AAE/C,MAAM,WAAW,qBAAqB;IACpC,WAAW,CACT,gBAAgB,EAAE,MAAM,EACxB,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,eAAe,GACtB,IAAI,CAAC;CACT;AAED;;;;;GAKG;AACH,qBAAa,iBAAiB;IAIhB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAHjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA6B;IACrD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAkC;gBAE1B,IAAI,EAAE,qBAAqB;IAExD,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,GAAG,IAAI;IAsBnD;;;;;;OAMG;IACH,KAAK,CAAC,gBAAgB,EAAE,MAAM,GAAG,IAAI;IAUrC;0CACsC;IACtC,KAAK,CAAC,gBAAgB,EAAE,MAAM,GAAG,IAAI;IAKrC,OAAO,CAAC,OAAO;CAUhB"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Execution detection — mirrors {@link PortDetector} (core/port-detector.ts).
|
|
3
|
+
*
|
|
4
|
+
* A run can start two ways in the harness, and this catches both:
|
|
5
|
+
* 1. The CLI prints `✓ Started execution <executionId>` (`sapiom agents run`).
|
|
6
|
+
* 2. The coding agent starts the run via the MCP run tool — the common path —
|
|
7
|
+
* whose result carries the id as an `executionId` field
|
|
8
|
+
* (`"executionId":"<id>"` / `executionId: <id>`).
|
|
9
|
+
* The detector is fed the SAME tool.call output PortDetector is (server/index.ts
|
|
10
|
+
* `onNormalizedEvent`, including the tool RESPONSE summary), so an MCP-triggered
|
|
11
|
+
* run is caught without changing how runs are triggered. It fires `onExecution`
|
|
12
|
+
* once per distinct (session, executionId); the integrator wires that to
|
|
13
|
+
* broadcast `{ type: "execution.started", … }` on /ws/events.
|
|
14
|
+
*
|
|
15
|
+
* Same streaming-safe design as PortDetector: a match touching the very end of
|
|
16
|
+
* the buffered text is held back (the id may still be growing in the next
|
|
17
|
+
* chunk) and finalized by `flush()`, which the integrator calls right after
|
|
18
|
+
* feeding a discrete, already-complete string (one tool.call field).
|
|
19
|
+
*/
|
|
20
|
+
/** Execution ids are the stable handles the executions API mints (e.g.
|
|
21
|
+
* `exec_ab12…`). Id-safe characters only, so a trailing space, newline, or
|
|
22
|
+
* ANSI reset ends the match rather than being captured into the id. */
|
|
23
|
+
const EXECUTION_ID = String.raw `[A-Za-z0-9_-]+`;
|
|
24
|
+
/** Two anchors, one capture group: the CLI's `Started execution <id>` line, or
|
|
25
|
+
* the `executionId` field from the run tool's structured result (quote/colon/
|
|
26
|
+
* space/equals between the key and the id). Both are start-time signals tied to
|
|
27
|
+
* a real run, so a match always refers to a run the user is actually driving. */
|
|
28
|
+
const EXECUTION_PATTERN_SOURCE = String.raw `(?:Started execution |executionId["'\s:=]+)(${EXECUTION_ID})`;
|
|
29
|
+
/** Long enough to hold either anchor ("Started execution " / `executionId":"`)
|
|
30
|
+
* split across a chunk boundary, plus a few id characters either side. */
|
|
31
|
+
const TAIL_SAFETY = 48;
|
|
32
|
+
/**
|
|
33
|
+
* Pure parser: every execution id announced in a chunk of CLI/tool output, in
|
|
34
|
+
* order (empty when the chunk announces none). This is the mutation-tested
|
|
35
|
+
* core — the {@link ExecutionDetector} class only adds streaming buffering and
|
|
36
|
+
* per-session dedupe on top of it.
|
|
37
|
+
*/
|
|
38
|
+
export function parseExecutionIds(text) {
|
|
39
|
+
const regex = new RegExp(EXECUTION_PATTERN_SOURCE, "g");
|
|
40
|
+
const ids = [];
|
|
41
|
+
let match;
|
|
42
|
+
while ((match = regex.exec(text)))
|
|
43
|
+
ids.push(match[1]);
|
|
44
|
+
return ids;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Stateful, streaming-safe, and dedupes per (session, executionId) so a run id
|
|
48
|
+
* that reappears in output only ever fires `onExecution` once for the session's
|
|
49
|
+
* lifetime. `reset()` clears a session's state on exit so a reused session id
|
|
50
|
+
* starts clean.
|
|
51
|
+
*/
|
|
52
|
+
export class ExecutionDetector {
|
|
53
|
+
constructor(deps) {
|
|
54
|
+
this.deps = deps;
|
|
55
|
+
this.buffers = new Map();
|
|
56
|
+
this.seen = new Map();
|
|
57
|
+
}
|
|
58
|
+
feed(chunk, harnessSessionId) {
|
|
59
|
+
const combined = (this.buffers.get(harnessSessionId) ?? "") + chunk;
|
|
60
|
+
const regex = new RegExp(EXECUTION_PATTERN_SOURCE, "g");
|
|
61
|
+
let match;
|
|
62
|
+
let pendingFrom = null;
|
|
63
|
+
while ((match = regex.exec(combined))) {
|
|
64
|
+
const end = match.index + match[0].length;
|
|
65
|
+
if (end === combined.length) {
|
|
66
|
+
// The id could still be growing (more chars due next chunk) — hold back.
|
|
67
|
+
pendingFrom = match.index;
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
this.tryEmit(harnessSessionId, match[1]);
|
|
71
|
+
}
|
|
72
|
+
const retainFrom = pendingFrom ?? Math.max(0, combined.length - TAIL_SAFETY);
|
|
73
|
+
this.buffers.set(harnessSessionId, combined.slice(retainFrom));
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Finalizes whatever's held back for a session, immediately. Call this right
|
|
77
|
+
* after `feed()` whenever the fed text is a discrete, complete string (one
|
|
78
|
+
* tool.call field): without it, an id landing at the very end — the common
|
|
79
|
+
* shape "✓ Started execution exec_123" — would be held pending forever, since
|
|
80
|
+
* no "next chunk" is coming.
|
|
81
|
+
*/
|
|
82
|
+
flush(harnessSessionId) {
|
|
83
|
+
const pending = this.buffers.get(harnessSessionId);
|
|
84
|
+
if (!pending)
|
|
85
|
+
return;
|
|
86
|
+
// Re-parse the whole pending buffer (vs an inline loop) — same result, and
|
|
87
|
+
// tryEmit dedupes so a match already emitted by feed() is a harmless no-op.
|
|
88
|
+
for (const id of parseExecutionIds(pending))
|
|
89
|
+
this.tryEmit(harnessSessionId, id);
|
|
90
|
+
this.buffers.delete(harnessSessionId);
|
|
91
|
+
}
|
|
92
|
+
/** Drops buffered/dedupe state for a session — call on session exit so a
|
|
93
|
+
* reused session id starts clean. */
|
|
94
|
+
reset(harnessSessionId) {
|
|
95
|
+
this.buffers.delete(harnessSessionId);
|
|
96
|
+
this.seen.delete(harnessSessionId);
|
|
97
|
+
}
|
|
98
|
+
tryEmit(harnessSessionId, executionId) {
|
|
99
|
+
let ids = this.seen.get(harnessSessionId);
|
|
100
|
+
if (!ids) {
|
|
101
|
+
ids = new Set();
|
|
102
|
+
this.seen.set(harnessSessionId, ids);
|
|
103
|
+
}
|
|
104
|
+
if (ids.has(executionId))
|
|
105
|
+
return;
|
|
106
|
+
ids.add(executionId);
|
|
107
|
+
this.deps.onExecution(harnessSessionId, executionId, "prod");
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=execution-detector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"execution-detector.js","sourceRoot":"","sources":["../../src/core/execution-detector.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-config.d.ts","sourceRoot":"","sources":["../../../src/core/inject/mcp-config.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,gBAAgB;IAC/B,0EAA0E;IAC1E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB;iFAC6E;IAC7E,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACrC,gBAAgB,EAAE,MAAM,EACxB,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"mcp-config.d.ts","sourceRoot":"","sources":["../../../src/core/inject/mcp-config.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,gBAAgB;IAC/B,0EAA0E;IAC1E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB;iFAC6E;IAC7E,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACrC,gBAAgB,EAAE,MAAM,EACxB,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,MAAM,CAAC,CAqCjB"}
|
|
@@ -25,7 +25,15 @@ export async function generateMcpConfig(harnessSessionId, options = {}) {
|
|
|
25
25
|
},
|
|
26
26
|
"sapiom-dev": {
|
|
27
27
|
command: "npx",
|
|
28
|
-
|
|
28
|
+
// Pin the dist-tag (`@latest`) rather than the bare name so npx always
|
|
29
|
+
// resolves the PUBLISHED package from the registry. A bare
|
|
30
|
+
// `@sapiom/mcp` resolves a LOCAL workspace copy whenever the harness
|
|
31
|
+
// runs from inside the sapiom-js monorepo (dogfooding/dev) — whose bin
|
|
32
|
+
// isn't linked, so the server fails to launch ("sapiom-mcp: command
|
|
33
|
+
// not found" → JSON-RPC -32000). A dist-tag spec forces registry
|
|
34
|
+
// resolution and is behaviourally identical to what a real user
|
|
35
|
+
// (outside the monorepo) already gets, so it's a pure robustness fix.
|
|
36
|
+
args: ["-y", "@sapiom/mcp@latest"],
|
|
29
37
|
...(devEnv ? { env: devEnv } : {}),
|
|
30
38
|
},
|
|
31
39
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-config.js","sourceRoot":"","sources":["../../../src/core/inject/mcp-config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAoBhD;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,gBAAwB,EACxB,UAA4B,EAAE;IAE9B,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,aAAa,IAAI,aAAa,CAAC,SAAS,CAAC,EAAE,gBAAgB,CAAC,CAAC;IACtG,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAEzC,MAAM,iBAAiB,GAAG,OAAO,CAAC,WAAW,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC;IAChF,MAAM,MAAM,GAAuC,iBAAiB;QAClE,CAAC,CAAC,EAAE,kBAAkB,EAAE,iBAAiB,EAAE;QAC3C,CAAC,CAAC,SAAS,CAAC;IAEd,MAAM,MAAM,GAAG;QACb,UAAU,EAAE;YACV,MAAM,EAAE;gBACN,IAAI,EAAE,MAAM;gBACZ,GAAG,EAAE,8BAA8B;gBACnC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,WAAW,EAAE,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACxE;YACD,YAAY,EAAE;gBACZ,OAAO,EAAE,KAAK;gBACd,IAAI,EAAE,CAAC,IAAI,EAAE,
|
|
1
|
+
{"version":3,"file":"mcp-config.js","sourceRoot":"","sources":["../../../src/core/inject/mcp-config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAoBhD;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,gBAAwB,EACxB,UAA4B,EAAE;IAE9B,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,aAAa,IAAI,aAAa,CAAC,SAAS,CAAC,EAAE,gBAAgB,CAAC,CAAC;IACtG,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAEzC,MAAM,iBAAiB,GAAG,OAAO,CAAC,WAAW,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC;IAChF,MAAM,MAAM,GAAuC,iBAAiB;QAClE,CAAC,CAAC,EAAE,kBAAkB,EAAE,iBAAiB,EAAE;QAC3C,CAAC,CAAC,SAAS,CAAC;IAEd,MAAM,MAAM,GAAG;QACb,UAAU,EAAE;YACV,MAAM,EAAE;gBACN,IAAI,EAAE,MAAM;gBACZ,GAAG,EAAE,8BAA8B;gBACnC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,WAAW,EAAE,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACxE;YACD,YAAY,EAAE;gBACZ,OAAO,EAAE,KAAK;gBACd,uEAAuE;gBACvE,2DAA2D;gBAC3D,qEAAqE;gBACrE,uEAAuE;gBACvE,oEAAoE;gBACpE,iEAAiE;gBACjE,gEAAgE;gBAChE,sEAAsE;gBACtE,IAAI,EAAE,CAAC,IAAI,EAAE,oBAAoB,CAAC;gBAClC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACnC;SACF;KACF,CAAC;IAEF,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,iBAAiB,CAAC,CAAC;IACnD,yEAAyE;IACzE,oEAAoE;IACpE,MAAM,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACtF,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* renderLocalRun — the pure mapper from an offline stub run's per-step traces
|
|
3
|
+
* ({@link LocalStepTrace}[], streamed as NDJSON by `POST /api/runs/local`) to
|
|
4
|
+
* the same {@link RunView} the canvas renders for prod runs. Its twin is
|
|
5
|
+
* {@link renderRunState} (prod / local-backend); both emit the identical
|
|
6
|
+
* cost-free {@link StepView} shape, so ONE inspector renders every run source.
|
|
7
|
+
*
|
|
8
|
+
* Pure and deterministic: no LLM, no I/O, no clock. Every field is derived from
|
|
9
|
+
* the traces (plus the caller-supplied execution id and terminal outcome), so
|
|
10
|
+
* the same function can map a partial trace mid-stream and the final trace at
|
|
11
|
+
* the end — only the input grows, never this mapping. It is a Stryker target;
|
|
12
|
+
* the tests are mutation-first.
|
|
13
|
+
*
|
|
14
|
+
* Honest absence, matching renderRunState and the SDK's "null is honest absence,
|
|
15
|
+
* never a fabricated value" contract:
|
|
16
|
+
* - **No cost** — local runs are stubbed and free by construction; a StepView
|
|
17
|
+
* from here never carries `costUsd` (so the inspector reads "local run ·
|
|
18
|
+
* free", never "$0.00").
|
|
19
|
+
* - **No latency** — a {@link LocalStepTrace} carries no timing (agent-core's
|
|
20
|
+
* in-process dispatcher records none, and agent-core is consumed as-is), so
|
|
21
|
+
* `latencyMs` is ABSENT rather than a fabricated `0`.
|
|
22
|
+
* - **Output** is present only when the trace captured one (a step that threw,
|
|
23
|
+
* or a `continue` with no value, carries none) — but ABSENCE is `undefined`
|
|
24
|
+
* on the trace, and a genuine `null`/`false`/`0`/`""` output is a real value
|
|
25
|
+
* that passes through.
|
|
26
|
+
* - **Input** is always carried by a trace (every attempt ran on some input,
|
|
27
|
+
* even `undefined`); it is surfaced whenever it isn't `undefined`.
|
|
28
|
+
* - **Logs and errors** collapse to the same `logSlice`/`error` fields prod
|
|
29
|
+
* uses, via the shared formatter.
|
|
30
|
+
* - **Stub signal (WB15-2)** is RUN-LEVEL. A local run is stub-served by
|
|
31
|
+
* construction, so the run carries `stubbed: true` (prod's renderRunState
|
|
32
|
+
* never does) and the inspector marks each executed step with a "stubbed"
|
|
33
|
+
* chip — agent-core records no per-CALL stub attribution, so that is the
|
|
34
|
+
* honest granularity. The terminal summary's `unusedStubs` (a supplied key
|
|
35
|
+
* that matched nothing) and `stubWarnings` (a wrong-shape value) pass through
|
|
36
|
+
* only when NON-empty, so a clean run surfaces no notice at all.
|
|
37
|
+
*/
|
|
38
|
+
import type { LocalStepTrace, LocalRunOutcome } from "@sapiom/agent-core";
|
|
39
|
+
import type { RunView, UnusedStubView } from "../shared/types.js";
|
|
40
|
+
/**
|
|
41
|
+
* Options carrying the run-level facts a `LocalStepTrace[]` alone can't provide.
|
|
42
|
+
* Both are optional so the mapper can render a live, still-streaming trace (no
|
|
43
|
+
* terminal summary yet) exactly as it renders the final one.
|
|
44
|
+
*/
|
|
45
|
+
export interface RenderLocalRunOptions {
|
|
46
|
+
/**
|
|
47
|
+
* The execution id to stamp on the {@link RunView}. A local run has no
|
|
48
|
+
* server-issued id, so the caller (the SPA's run store) synthesizes and owns
|
|
49
|
+
* one; the mapper stays pure by taking it rather than minting time/randomness.
|
|
50
|
+
* Defaults to an empty string when the caller has none yet.
|
|
51
|
+
*/
|
|
52
|
+
executionId?: string;
|
|
53
|
+
/**
|
|
54
|
+
* The run's terminal outcome from the NDJSON summary line, when it has
|
|
55
|
+
* arrived. Absent while the stream is still open — the mapper then reports the
|
|
56
|
+
* run as `running` (steps may still be coming). Passing it flips the run to
|
|
57
|
+
* its settled status.
|
|
58
|
+
*/
|
|
59
|
+
outcome?: LocalRunOutcome;
|
|
60
|
+
/**
|
|
61
|
+
* Supplied stub keys that matched no capability call this run — from the
|
|
62
|
+
* terminal summary line. A no-op mock (typo'd path, wrong plural/singular)
|
|
63
|
+
* otherwise fails silently; surfacing it is the whole point of WB15-2. Only a
|
|
64
|
+
* NON-empty list reaches the {@link RunView} (honest absence), so pass it as-is
|
|
65
|
+
* from the summary and the mapper decides. Absent while the stream is open.
|
|
66
|
+
*/
|
|
67
|
+
unusedStubs?: UnusedStubView[];
|
|
68
|
+
/**
|
|
69
|
+
* Warnings about stub values that matched a key but carried the wrong shape —
|
|
70
|
+
* from the terminal summary line. Same honest-absence handling as
|
|
71
|
+
* {@link RenderLocalRunOptions.unusedStubs}: an empty list is dropped.
|
|
72
|
+
*/
|
|
73
|
+
stubWarnings?: string[];
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Map an offline stub run's traces (plus the caller-owned execution id and, once
|
|
77
|
+
* known, terminal outcome) to the {@link RunView} the canvas renders. Order-
|
|
78
|
+
* preserving over `traces` (execution order); total — never throws, mirroring
|
|
79
|
+
* renderRunState — because the traces are already the decoded wire shape.
|
|
80
|
+
*/
|
|
81
|
+
export declare function renderLocalRun(traces: LocalStepTrace[], options?: RenderLocalRunOptions): RunView;
|
|
82
|
+
//# sourceMappingURL=render-local-run.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render-local-run.d.ts","sourceRoot":"","sources":["../../src/core/render-local-run.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAE1E,OAAO,KAAK,EACV,OAAO,EAIP,cAAc,EACf,MAAM,oBAAoB,CAAC;AAQ5B;;;;GAIG;AACH,MAAM,WAAW,qBAAqB;IACpC;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,cAAc,EAAE,CAAC;IAC/B;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAoED;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,cAAc,EAAE,EACxB,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAsBT"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// Reuse prod's exact log-buffer formatter so a step's `logSlice` is byte-
|
|
2
|
+
// identical whether it came from a prod projection or a local trace — the
|
|
3
|
+
// inspector can never disagree with itself about how a log renders. Imported
|
|
4
|
+
// from the dependency-free module (NOT render-run-state) so this mapper carries
|
|
5
|
+
// no `@sapiom/agent-core`/`node:fs` runtime dep into the browser bundle.
|
|
6
|
+
import { toLogSlice } from "./render-log-slice.js";
|
|
7
|
+
/**
|
|
8
|
+
* Fold a single step-attempt's status into a {@link StepStatus}. A local trace's
|
|
9
|
+
* status vocabulary is exactly `succeeded | threw` (see agent-core's
|
|
10
|
+
* LocalStubDispatcher). `succeeded` → `passed`; `threw` → `failed` (it did not
|
|
11
|
+
* pass — mirrors renderRunState's fold of `threw`). Anything unrecognized is
|
|
12
|
+
* `pending` (defensive; the union shouldn't produce it), NEVER silently
|
|
13
|
+
* `passed`.
|
|
14
|
+
*/
|
|
15
|
+
function toStepStatus(raw) {
|
|
16
|
+
if (raw === "succeeded")
|
|
17
|
+
return "passed";
|
|
18
|
+
if (raw === "threw")
|
|
19
|
+
return "failed";
|
|
20
|
+
return "pending";
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Fold the run's terminal outcome into the four states the UI draws. A local run
|
|
24
|
+
* that `paused` is not yet done (the auto-resume loop or a human must advance
|
|
25
|
+
* it), so it reads as `running`, not a terminal state. An absent outcome (stream
|
|
26
|
+
* still open) is also `running`. `completed`/`failed` map straight through;
|
|
27
|
+
* there is no local "cancelled" outcome.
|
|
28
|
+
*/
|
|
29
|
+
function toRunStatus(outcome) {
|
|
30
|
+
if (outcome === "completed")
|
|
31
|
+
return "completed";
|
|
32
|
+
if (outcome === "failed")
|
|
33
|
+
return "failed";
|
|
34
|
+
// "paused", "running", or absent (still streaming) — not terminal.
|
|
35
|
+
return "running";
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Map one trace line to its {@link StepView}. Optional fields are assigned only
|
|
39
|
+
* when they carry a real value, so honest absence survives into the JSON the
|
|
40
|
+
* inspector reads (a missing field renders nothing rather than a fabricated
|
|
41
|
+
* zero/empty payload).
|
|
42
|
+
*/
|
|
43
|
+
function toStepView(trace) {
|
|
44
|
+
const view = {
|
|
45
|
+
// Local traces have no OTel span id; the (step, attempt) pair is the stable,
|
|
46
|
+
// collision-free key across a run's attempts of the same step.
|
|
47
|
+
id: `${trace.step}-${trace.attempt}`,
|
|
48
|
+
name: trace.step,
|
|
49
|
+
status: toStepStatus(trace.status),
|
|
50
|
+
};
|
|
51
|
+
// No costUsd, no latencyMs — a local run is free and untimed (see the file
|
|
52
|
+
// header). Their honest absence is the whole point of the local target.
|
|
53
|
+
if (trace.input !== undefined)
|
|
54
|
+
view.input = trace.input;
|
|
55
|
+
if (trace.output !== undefined)
|
|
56
|
+
view.output = trace.output;
|
|
57
|
+
if (trace.error?.message)
|
|
58
|
+
view.error = trace.error.message;
|
|
59
|
+
const logSlice = toLogSlice(trace.logs);
|
|
60
|
+
if (logSlice !== undefined)
|
|
61
|
+
view.logSlice = logSlice;
|
|
62
|
+
// Per-step capability calls from the stub client. Honest absence: only a
|
|
63
|
+
// non-empty list reaches the view (an empty list means no calls were made —
|
|
64
|
+
// but absence on the trace means the sink was never wired, so we leave it
|
|
65
|
+
// absent too). The StubCallRecord shape maps directly onto StepCall.
|
|
66
|
+
if (trace.calls && trace.calls.length > 0) {
|
|
67
|
+
view.calls = trace.calls.map((c) => ({
|
|
68
|
+
capability: c.capability,
|
|
69
|
+
stubUsed: c.stubUsed,
|
|
70
|
+
args: c.args,
|
|
71
|
+
result: c.result,
|
|
72
|
+
}));
|
|
73
|
+
}
|
|
74
|
+
return view;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Map an offline stub run's traces (plus the caller-owned execution id and, once
|
|
78
|
+
* known, terminal outcome) to the {@link RunView} the canvas renders. Order-
|
|
79
|
+
* preserving over `traces` (execution order); total — never throws, mirroring
|
|
80
|
+
* renderRunState — because the traces are already the decoded wire shape.
|
|
81
|
+
*/
|
|
82
|
+
export function renderLocalRun(traces, options = {}) {
|
|
83
|
+
const view = {
|
|
84
|
+
executionId: options.executionId ?? "",
|
|
85
|
+
status: toRunStatus(options.outcome),
|
|
86
|
+
steps: traces.map(toStepView),
|
|
87
|
+
// A local run is stub-served by construction (every ctx.sapiom.* call
|
|
88
|
+
// resolves from a stub, never the network) — so the run itself is `stubbed`,
|
|
89
|
+
// and the inspector marks each executed step with the chip. This is the
|
|
90
|
+
// honest per-run truth: agent-core records no per-CALL stub attribution, so
|
|
91
|
+
// there is nothing finer to claim. renderRunState (prod) never sets this.
|
|
92
|
+
stubbed: true,
|
|
93
|
+
};
|
|
94
|
+
// Honest absence: only a NON-empty signal reaches the view, so the read-only
|
|
95
|
+
// notice renders nothing when the run had no stub-hygiene problems. A supplied
|
|
96
|
+
// empty array is indistinguishable from "no problems" and must not surface.
|
|
97
|
+
if (options.unusedStubs && options.unusedStubs.length > 0) {
|
|
98
|
+
view.unusedStubs = options.unusedStubs;
|
|
99
|
+
}
|
|
100
|
+
if (options.stubWarnings && options.stubWarnings.length > 0) {
|
|
101
|
+
view.stubWarnings = options.stubWarnings;
|
|
102
|
+
}
|
|
103
|
+
return view;
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=render-local-run.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render-local-run.js","sourceRoot":"","sources":["../../src/core/render-local-run.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* render-log-slice — the shared, dependency-free formatter for a step's executor
|
|
3
|
+
* log buffer. Both run mappers use it so a step's `logSlice` is byte-identical
|
|
4
|
+
* whether it came from a prod {@link import("./render-run-state.js").renderRunState}
|
|
5
|
+
* projection or a local {@link import("./render-local-run.js").renderLocalRun}
|
|
6
|
+
* trace — the inspector can never disagree with itself about how a log renders.
|
|
7
|
+
*
|
|
8
|
+
* Kept in its OWN module (rather than in render-run-state.ts) precisely because
|
|
9
|
+
* it has NO `@sapiom/agent-core` dependency: render-run-state pulls in agent-core
|
|
10
|
+
* runtime helpers (e.g. `isExecutionTerminal`) that reach for `node:fs`, which
|
|
11
|
+
* must never enter the browser bundle. The local-run mapper is imported by the
|
|
12
|
+
* SPA, so it depends on THIS pure module instead — no Node built-ins ride along.
|
|
13
|
+
*
|
|
14
|
+
* Pure and deterministic: no I/O, no clock. Safe in both Node and the browser.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Cap on the characters of a step's executor log buffer surfaced in `logSlice`.
|
|
18
|
+
* The TAIL is kept (most recent lines) because failures surface at the end of a
|
|
19
|
+
* log. This is a payload guard on the poll/stream response; the debug-macro
|
|
20
|
+
* context extractor does the final, smaller trim for prompt injection.
|
|
21
|
+
*/
|
|
22
|
+
export declare const LOG_SLICE_MAX = 4000;
|
|
23
|
+
/** One executor log entry → a compact line. Accepts the `{ ts, level, msg }`
|
|
24
|
+
* wire shape (or `message`), a bare string, or anything else (stringified).
|
|
25
|
+
* A local trace's `{ level, msg }` entry is a subset of this same shape. */
|
|
26
|
+
export declare function formatLogEntry(entry: unknown): string;
|
|
27
|
+
/** Format the executor log buffer into a trimmed, tail-preserving slice, or
|
|
28
|
+
* `undefined` when there are no usable logs. */
|
|
29
|
+
export declare function toLogSlice(logs: unknown): string | undefined;
|
|
30
|
+
//# sourceMappingURL=render-log-slice.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render-log-slice.d.ts","sourceRoot":"","sources":["../../src/core/render-log-slice.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH;;;;;GAKG;AACH,eAAO,MAAM,aAAa,OAAO,CAAC;AAElC;;6EAE6E;AAC7E,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAgBrD;AAED;iDACiD;AACjD,wBAAgB,UAAU,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAO5D"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* render-log-slice — the shared, dependency-free formatter for a step's executor
|
|
3
|
+
* log buffer. Both run mappers use it so a step's `logSlice` is byte-identical
|
|
4
|
+
* whether it came from a prod {@link import("./render-run-state.js").renderRunState}
|
|
5
|
+
* projection or a local {@link import("./render-local-run.js").renderLocalRun}
|
|
6
|
+
* trace — the inspector can never disagree with itself about how a log renders.
|
|
7
|
+
*
|
|
8
|
+
* Kept in its OWN module (rather than in render-run-state.ts) precisely because
|
|
9
|
+
* it has NO `@sapiom/agent-core` dependency: render-run-state pulls in agent-core
|
|
10
|
+
* runtime helpers (e.g. `isExecutionTerminal`) that reach for `node:fs`, which
|
|
11
|
+
* must never enter the browser bundle. The local-run mapper is imported by the
|
|
12
|
+
* SPA, so it depends on THIS pure module instead — no Node built-ins ride along.
|
|
13
|
+
*
|
|
14
|
+
* Pure and deterministic: no I/O, no clock. Safe in both Node and the browser.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Cap on the characters of a step's executor log buffer surfaced in `logSlice`.
|
|
18
|
+
* The TAIL is kept (most recent lines) because failures surface at the end of a
|
|
19
|
+
* log. This is a payload guard on the poll/stream response; the debug-macro
|
|
20
|
+
* context extractor does the final, smaller trim for prompt injection.
|
|
21
|
+
*/
|
|
22
|
+
export const LOG_SLICE_MAX = 4000;
|
|
23
|
+
/** One executor log entry → a compact line. Accepts the `{ ts, level, msg }`
|
|
24
|
+
* wire shape (or `message`), a bare string, or anything else (stringified).
|
|
25
|
+
* A local trace's `{ level, msg }` entry is a subset of this same shape. */
|
|
26
|
+
export function formatLogEntry(entry) {
|
|
27
|
+
if (typeof entry === "string")
|
|
28
|
+
return entry;
|
|
29
|
+
if (entry !== null && typeof entry === "object") {
|
|
30
|
+
const e = entry;
|
|
31
|
+
const parts = [e.ts, e.level, e.msg ?? e.message].filter((p) => typeof p === "string" || typeof p === "number");
|
|
32
|
+
if (parts.length > 0)
|
|
33
|
+
return parts.map(String).join(" ");
|
|
34
|
+
}
|
|
35
|
+
return String(entry);
|
|
36
|
+
}
|
|
37
|
+
/** Format the executor log buffer into a trimmed, tail-preserving slice, or
|
|
38
|
+
* `undefined` when there are no usable logs. */
|
|
39
|
+
export function toLogSlice(logs) {
|
|
40
|
+
if (!Array.isArray(logs) || logs.length === 0)
|
|
41
|
+
return undefined;
|
|
42
|
+
const text = logs.map(formatLogEntry).join("\n").trim();
|
|
43
|
+
if (text === "")
|
|
44
|
+
return undefined;
|
|
45
|
+
return text.length > LOG_SLICE_MAX
|
|
46
|
+
? text.slice(text.length - LOG_SLICE_MAX)
|
|
47
|
+
: text;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=render-log-slice.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render-log-slice.js","sourceRoot":"","sources":["../../src/core/render-log-slice.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,CAAC;AAElC;;6EAE6E;AAC7E,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC5C,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAChD,MAAM,CAAC,GAAG,KAKT,CAAC;QACF,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM,CACtD,CAAC,CAAC,EAAwB,EAAE,CAC1B,OAAO,CAAC,KAAK,QAAQ,IAAI,OAAO,CAAC,KAAK,QAAQ,CACjD,CAAC;QACF,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC3D,CAAC;IACD,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AAED;iDACiD;AACjD,MAAM,UAAU,UAAU,CAAC,IAAa;IACtC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAChE,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;IACxD,IAAI,IAAI,KAAK,EAAE;QAAE,OAAO,SAAS,CAAC;IAClC,OAAO,IAAI,CAAC,MAAM,GAAG,aAAa;QAChC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,aAAa,CAAC;QACzC,CAAC,CAAC,IAAI,CAAC;AACX,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ExecutionProjection } from "@sapiom/agent-core";
|
|
2
|
+
import type { RunView } from "../shared/types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Map a decoded {@link ExecutionProjection} to the {@link RunView} the canvas
|
|
5
|
+
* renders. Order-preserving over `steps`; total (never throws) because the
|
|
6
|
+
* projection is already the decoded, degradation-tolerant shape.
|
|
7
|
+
*/
|
|
8
|
+
export declare function renderRunState(decoded: ExecutionProjection): RunView;
|
|
9
|
+
//# sourceMappingURL=render-run-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render-run-state.d.ts","sourceRoot":"","sources":["../../src/core/render-run-state.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EACV,mBAAmB,EAEpB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,KAAK,EAAE,OAAO,EAAwB,MAAM,oBAAoB,CAAC;AA0FxE;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAMpE"}
|