@tangle-network/agent-runtime 0.23.0 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/agent.d.ts CHANGED
@@ -1,103 +1,9 @@
1
1
  import * as _tangle_network_agent_eval from '@tangle-network/agent-eval';
2
- import { FindingSubject, TraceAnalystKindSpec, AnalystFinding } from '@tangle-network/agent-eval';
2
+ import { TraceAnalystKindSpec, AnalystFinding } from '@tangle-network/agent-eval';
3
3
  import { R as RuntimeStreamEvent } from './types-BFgFD_sl.js';
4
- import { I as ImprovementAdapter, K as KnowledgeAdapter, a as RunAnalystLoopResult } from './types-D_MXrmJP.js';
5
-
6
- /**
7
- * `AgentSurfaces` — declarative map of the mutable file/directory paths
8
- * the self-improvement loop can edit on behalf of an agent.
9
- *
10
- * The substrate uses this map to resolve every parsed `FindingSubject`
11
- * (from agent-eval) to a real on-disk path. No per-vertical glue;
12
- * no fabricated paths; no silent `existsSync(...)` skips that hide
13
- * misconfiguration from the operator.
14
- *
15
- * Surfaces are validated at `defineAgent` time — missing paths fail
16
- * loud with a list of every offender. A surface that's not needed
17
- * (e.g. an agent with no RAG corpora) is simply omitted; the loop
18
- * refuses to route those subjects rather than fabricating a target.
19
- */
20
-
21
- /**
22
- * Surface declarations. Every path is repo-relative (or absolute) at
23
- * `defineAgent` time. At resolution time, paths are joined against the
24
- * agent's `repoRoot`.
25
- *
26
- * `systemPrompt`, `tools`, `personas` are DIRECTORIES; the loop appends
27
- * `<section>.md`, `<tool>/README.md`, `<persona-id>.yaml` etc.
28
- * `rubric`, `outputSchema` are SINGLE FILES; the loop edits them in
29
- * place.
30
- *
31
- * `knowledge` is the agent-knowledge root (typically `.agent-knowledge`);
32
- * `applyKnowledgeWriteBlocks` writes pages relative to it.
33
- *
34
- * Optional surfaces (`scaffolding`, `memory`, `rag`, `outputSchema`)
35
- * can be omitted — the loop will reject findings targeting them with a
36
- * clear log message instead of fabricating a path.
37
- */
38
- interface AgentSurfaces {
39
- /** Directory containing one markdown file per system-prompt section. */
40
- systemPrompt: string;
41
- /** Directory containing one subdir per tool (`<tool>/README.md`). */
42
- tools: string;
43
- /** Single file (TypeScript module) defining the rubric weights + dimensions. */
44
- rubric: string;
45
- /** Knowledge-base root; typically `.agent-knowledge`. */
46
- knowledge: string;
47
- /** Directory containing one YAML/JSON file per persona. */
48
- personas: string;
49
- /** Optional: directory containing scaffolding rules (precondition checks, retry policies). */
50
- scaffolding?: string;
51
- /** Optional: memory store path (JSONL / SQLite / DB). */
52
- memory?: string;
53
- /** Optional: directory containing RAG corpora (`<corpus>/<doc-id>.md`). */
54
- rag?: string;
55
- /** Optional: single file defining the output schema (Zod / JSON Schema). */
56
- outputSchema?: string;
57
- }
58
- interface ResolvedSurface {
59
- /** Absolute filesystem path the operator can `cat` / `vim`. */
60
- absolutePath: string;
61
- /** Repo-relative path for PR descriptions, diffs, audit logs. */
62
- repoRelativePath: string;
63
- /** Whether the path currently exists on disk. */
64
- exists: boolean;
65
- /** The substrate's intent: edit an existing file or create a new one. */
66
- intent: 'edit-existing' | 'create-new';
67
- }
68
- /**
69
- * Resolve a parsed `FindingSubject` to the file path the substrate
70
- * should edit (or create) on disk.
71
- *
72
- * Returns `null` when:
73
- * - the subject targets a surface the agent didn't declare
74
- * (e.g. `rag:*` when `surfaces.rag` is undefined), OR
75
- * - the subject is a `cluster` (failure-mode emits these as evidence,
76
- * not actionable mutations — they don't route to a file).
77
- *
78
- * Returns a `ResolvedSurface` with `intent: 'create-new'` when the
79
- * subject names a path that doesn't yet exist (e.g. a new wiki page).
80
- * The caller chooses whether to honour the create — for tightly-managed
81
- * surfaces like `systemPrompt` it's usually a contract violation
82
- * (the analyst named a section that doesn't exist); for `knowledge`
83
- * it's the whole point.
84
- */
85
- declare function resolveSubjectPath(subject: FindingSubject, surfaces: AgentSurfaces, repoRoot: string): ResolvedSurface | null;
86
- /**
87
- * Validate that every declared surface exists on disk under `repoRoot`.
88
- *
89
- * Returns an array of `SurfaceValidationIssue` — empty when all required
90
- * surfaces resolve. `defineAgent` throws with the issues rendered, so
91
- * a misconfigured manifest fails at startup (not at the first finding
92
- * the loop produces 20 minutes later).
93
- */
94
- interface SurfaceValidationIssue {
95
- surface: keyof AgentSurfaces;
96
- path: string;
97
- reason: 'missing' | 'not-directory' | 'not-file';
98
- }
99
- declare function validateSurfaces(surfaces: AgentSurfaces, repoRoot: string): ReadonlyArray<SurfaceValidationIssue>;
100
- declare function renderSurfaceIssues(issues: ReadonlyArray<SurfaceValidationIssue>, repoRoot: string): string;
4
+ import { A as AgentSurfaces } from './improvement-adapter-CaZxFxTd.js';
5
+ export { C as CreateSurfaceImprovementAdapterOpts, D as DraftPatchInput, a as DraftPatchOutput, R as ResolvedSurface, S as SurfaceImprovementEdit, b as SurfaceValidationIssue, c as createSurfaceImprovementAdapter, r as renderSurfaceIssues, d as resolveSubjectPath, v as validateSurfaces } from './improvement-adapter-CaZxFxTd.js';
6
+ import { K as KnowledgeAdapter, a as RunAnalystLoopResult } from './types-D_MXrmJP.js';
101
7
 
102
8
  /**
103
9
  * The full agent manifest. Each agent ships ONE of these.
@@ -342,113 +248,6 @@ declare class AgentManifestError extends Error {
342
248
  */
343
249
  declare function defineAgent<TPersona = unknown, TRunOutput = unknown>(manifest: AgentManifest<TPersona, TRunOutput>): AgentManifest<TPersona, TRunOutput>;
344
250
 
345
- /**
346
- * Substrate-default `ImprovementAdapter` — surfaces-driven, LLM-drafted
347
- * patches, optional auto-apply or PR-open.
348
- *
349
- * This is the one ImprovementAdapter every vertical agent uses. The
350
- * substrate parses each finding's `subject` via
351
- * `parseFindingSubject` (agent-eval), resolves it to a real file path
352
- * via the agent's `AgentSurfaces`, reads the current content, and asks
353
- * an LLM to draft a unified-diff patch given the finding + current
354
- * content + per-kind editing-discipline rules.
355
- *
356
- * Auto-apply gates on the source-finding's confidence and the
357
- * autoApply.improvement policy. Two modes:
358
- * `write` — apply the patch in-place via `git apply -p0`. Operator
359
- * reviews via `git diff`.
360
- * `open-pr` — write to a branch, commit, push, open a PR via `gh`.
361
- * Operator reviews via the PR UI.
362
- *
363
- * Fail-loud rules:
364
- * - Findings whose subject doesn't parse → counted in `errors`.
365
- * - Findings whose subject targets an undeclared surface → counted in
366
- * `errors` with the offending kind in the message.
367
- * - Findings whose target path doesn't exist AND the kind isn't a
368
- * create-new variant (`new-tool`, `knowledge.wiki`) → counted in
369
- * `errors` with the resolved path in the message.
370
- * - LLM drafts that fail JSON-schema validation → counted in
371
- * `errors` with the schema issue.
372
- *
373
- * No silent skips. Every dropped finding has a recorded reason the
374
- * loop's report surfaces.
375
- */
376
-
377
- interface SurfaceImprovementEdit {
378
- /** Stable id derived from the source finding so re-proposals are idempotent. */
379
- id: string;
380
- /** The finding that produced this edit — for revert + audit trail. */
381
- sourceFindingId: string;
382
- /** Parsed subject; included so the apply step doesn't re-parse. */
383
- subject: FindingSubject;
384
- /** Resolved on-disk target. */
385
- target: ResolvedSurface;
386
- /** SHA-256 of the current file content the patch was drafted against. */
387
- baseSha256: string;
388
- /** Unified-diff patch the LLM drafted (relative to `target.absolutePath`). */
389
- patch: string;
390
- /** One-line summary the operator sees in the report / PR title. */
391
- summary: string;
392
- /** Multi-line rationale for the PR body — finding context + LLM reasoning. */
393
- rationale: string;
394
- /** Carry-forward from the finding so the apply gate can check the threshold. */
395
- confidence: number;
396
- /** Carry-forward severity for prioritization. */
397
- severity: AnalystFinding['severity'];
398
- }
399
- interface CreateSurfaceImprovementAdapterOpts {
400
- surfaces: AgentSurfaces;
401
- repoRoot: string;
402
- /**
403
- * LLM-draft callback. Given a finding + current file content + the
404
- * resolved target, returns a unified-diff patch + summary + rationale.
405
- *
406
- * Required — the substrate doesn't ship a hardcoded prompt; the agent
407
- * author picks the model (Haiku for cheap routine drafts, Sonnet for
408
- * substantive prompt rewrites, etc.) via this callback.
409
- */
410
- draftPatch: (input: DraftPatchInput) => Promise<DraftPatchOutput>;
411
- /**
412
- * Apply mode:
413
- * `write` — `git apply` in-place; operator reviews via `git diff`
414
- * `open-pr` — branch + commit + push + `gh pr create`
415
- * `none` — never apply; collect proposals for the report only
416
- *
417
- * The `apply` method honours this even when the loop calls it; the
418
- * effective behaviour is also gated on the per-finding confidence
419
- * threshold via `runAnalystLoop`'s `autoApply` policy.
420
- */
421
- mode?: 'write' | 'open-pr' | 'none';
422
- /** When `mode === 'open-pr'`, the base branch new PRs target. Default: `main`. */
423
- baseBranch?: string;
424
- /** Required for `mode === 'open-pr'` — the GH owner/repo (`tangle-network/tax-agent`). */
425
- ghRepo?: string;
426
- /**
427
- * When the resolved target doesn't exist, allow the substrate to
428
- * CREATE the file (for `knowledge.wiki`, `new-tool` subjects). Default
429
- * true for those kinds, false for `system-prompt` / `rubric` / etc.
430
- * (named sections that don't exist are a contract violation, not a
431
- * scaffolding opportunity).
432
- */
433
- allowCreateForKinds?: ReadonlyArray<FindingSubject['kind']>;
434
- }
435
- interface DraftPatchInput {
436
- finding: AnalystFinding;
437
- subject: FindingSubject;
438
- target: ResolvedSurface;
439
- /** Current file content (empty string when `intent === 'create-new'`). */
440
- currentContent: string;
441
- }
442
- interface DraftPatchOutput {
443
- /** Unified diff against the current file content. Empty string skips this finding. */
444
- patch: string;
445
- /** One-line summary for the operator. */
446
- summary: string;
447
- /** Multi-line rationale for the PR body. */
448
- rationale: string;
449
- }
450
- declare function createSurfaceImprovementAdapter(opts: CreateSurfaceImprovementAdapterOpts): ImprovementAdapter<SurfaceImprovementEdit>;
451
-
452
251
  /**
453
252
  * Substrate-default `KnowledgeAdapter` — wraps agent-knowledge's
454
253
  * `proposeFromFindings` + `applyKnowledgeWriteBlocks` with substrate
@@ -580,4 +379,4 @@ declare function measureOutcome<TProposal, TEdit>(result: RunAnalystLoopResult<T
580
379
  outcome: OutcomeMeasurement;
581
380
  }>;
582
381
 
583
- export { type AgentManifest, AgentManifestError, type AgentRubric, type AgentRunContext, type AgentRunInvocation, type AgentRuntime, type AgentSurfaces, type AnalystConfig, type AutoApplyPolicy, type CreateSurfaceImprovementAdapterOpts, type CreateSurfaceKnowledgeAdapterOpts, type DraftPatchInput, type DraftPatchOutput, type JudgeConfig, type KnowledgeAdapterDeps, type OutcomeMeasurement, type OutcomeMeasurementOpts, type ResolvedSurface, type RubricDimension, type SurfaceImprovementEdit, type SurfaceValidationIssue, collectAgentRun, createSurfaceImprovementAdapter, createSurfaceKnowledgeAdapter, defineAgent, measureOutcome, renderSurfaceIssues, resolveSubjectPath, unimplementedAgentRun, validateSurfaces };
382
+ export { type AgentManifest, AgentManifestError, type AgentRubric, type AgentRunContext, type AgentRunInvocation, type AgentRuntime, AgentSurfaces, type AnalystConfig, type AutoApplyPolicy, type CreateSurfaceKnowledgeAdapterOpts, type JudgeConfig, type KnowledgeAdapterDeps, type OutcomeMeasurement, type OutcomeMeasurementOpts, type RubricDimension, collectAgentRun, createSurfaceKnowledgeAdapter, defineAgent, measureOutcome, unimplementedAgentRun };
@@ -0,0 +1,92 @@
1
+ // src/mcp/local-harness.ts
2
+ import { spawn } from "child_process";
3
+ var HARNESS_INVOCATIONS = {
4
+ claude: {
5
+ command: "claude",
6
+ buildArgs: (taskPrompt) => ["--headless", "-p", taskPrompt]
7
+ },
8
+ codex: {
9
+ command: "codex",
10
+ buildArgs: (taskPrompt) => ["run", taskPrompt]
11
+ },
12
+ opencode: {
13
+ command: "opencode",
14
+ buildArgs: (taskPrompt) => ["run", taskPrompt]
15
+ }
16
+ };
17
+ var DEFAULT_TIMEOUT_MS = 5 * 60 * 1e3;
18
+ function runLocalHarness(options) {
19
+ const { harness, cwd, taskPrompt } = options;
20
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
21
+ const env = options.env ?? process.env;
22
+ const spawnImpl = options.spawn ?? spawn;
23
+ const invocation = HARNESS_INVOCATIONS[harness];
24
+ if (!invocation) {
25
+ return Promise.reject(new Error(`runLocalHarness: unknown harness ${String(harness)}`));
26
+ }
27
+ const startedAt = Date.now();
28
+ const args = invocation.buildArgs(taskPrompt);
29
+ return new Promise((resolve, reject) => {
30
+ let child;
31
+ try {
32
+ child = spawnImpl(invocation.command, args, { cwd, env, stdio: "pipe" });
33
+ } catch (err) {
34
+ reject(err instanceof Error ? err : new Error(String(err)));
35
+ return;
36
+ }
37
+ let stdout = "";
38
+ let stderr = "";
39
+ let timedOut = false;
40
+ let settled = false;
41
+ const timer = timeoutMs > 0 ? setTimeout(() => {
42
+ timedOut = true;
43
+ if (!child.killed) child.kill("SIGTERM");
44
+ }, timeoutMs) : null;
45
+ if (timer && typeof timer.unref === "function") {
46
+ ;
47
+ timer.unref();
48
+ }
49
+ const onAbort = () => {
50
+ if (!child.killed) child.kill("SIGTERM");
51
+ };
52
+ if (options.signal) {
53
+ if (options.signal.aborted) onAbort();
54
+ else options.signal.addEventListener("abort", onAbort, { once: true });
55
+ }
56
+ child.stdout?.on("data", (chunk) => {
57
+ stdout += String(chunk);
58
+ });
59
+ child.stderr?.on("data", (chunk) => {
60
+ stderr += String(chunk);
61
+ });
62
+ const finalize = (result) => {
63
+ if (settled) return;
64
+ settled = true;
65
+ if (timer) clearTimeout(timer);
66
+ options.signal?.removeEventListener("abort", onAbort);
67
+ resolve(result);
68
+ };
69
+ child.on("error", (err) => {
70
+ if (settled) return;
71
+ settled = true;
72
+ if (timer) clearTimeout(timer);
73
+ options.signal?.removeEventListener("abort", onAbort);
74
+ reject(err);
75
+ });
76
+ child.on("close", (code, signal) => {
77
+ finalize({
78
+ exitCode: code,
79
+ stdout,
80
+ stderr,
81
+ killedBySignal: signal,
82
+ durationMs: Date.now() - startedAt,
83
+ timedOut
84
+ });
85
+ });
86
+ });
87
+ }
88
+
89
+ export {
90
+ runLocalHarness
91
+ };
92
+ //# sourceMappingURL=chunk-GLR25NG7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/mcp/local-harness.ts"],"sourcesContent":["/**\n * @experimental\n *\n * Subprocess wrappers for the local coding-harness CLIs installed in the\n * sandbox image (claude-code, codex, opencode). Used by the in-process\n * delegation executor (`createInProcessExecutor`) so a `delegate_code` call\n * spawns a real harness on a real git worktree instead of provisioning a\n * sibling sandbox.\n *\n * All harness invocations:\n * - run with `cwd` set to the worktree\n * - inherit env from the parent (the MCP server inside the sandbox has\n * the harness's auth already)\n * - capture stdout/stderr\n * - support cancellation via AbortSignal\n * - enforce a wall-clock timeout\n */\n\nimport { type ChildProcess, spawn } from 'node:child_process'\n\n/** Local coding harness available inside the sandbox. */\nexport type LocalHarness = 'claude' | 'codex' | 'opencode'\n\n/** Default per-harness command + arg shape. */\nconst HARNESS_INVOCATIONS: Record<\n LocalHarness,\n { command: string; buildArgs: (taskPrompt: string) => string[] }\n> = {\n claude: {\n command: 'claude',\n buildArgs: (taskPrompt) => ['--headless', '-p', taskPrompt],\n },\n codex: {\n command: 'codex',\n buildArgs: (taskPrompt) => ['run', taskPrompt],\n },\n opencode: {\n command: 'opencode',\n buildArgs: (taskPrompt) => ['run', taskPrompt],\n },\n}\n\n/** @experimental */\nexport interface RunLocalHarnessOptions {\n harness: LocalHarness\n /** Working directory for the subprocess (typically a worktree path). */\n cwd: string\n /** Prompt forwarded as the harness CLI's task argument. */\n taskPrompt: string\n /** Wall-clock kill deadline (ms). Default 5 min. Subprocess SIGTERMed on expiry. */\n timeoutMs?: number\n /** Caller cancellation. SIGTERM is sent on abort. */\n signal?: AbortSignal\n /** Override env (defaults to inheriting from the parent). */\n env?: NodeJS.ProcessEnv\n /**\n * Test seam — inject a custom spawner so unit tests can mock the\n * subprocess without touching the OS. Defaults to node's `child_process.spawn`.\n */\n spawn?: (\n command: string,\n args: ReadonlyArray<string>,\n opts: {\n cwd: string\n env: NodeJS.ProcessEnv\n stdio: 'pipe'\n },\n ) => ChildProcess\n}\n\n/** @experimental */\nexport interface LocalHarnessResult {\n /** OS exit code. `null` when killed before exit. */\n exitCode: number | null\n /** Concatenated stdout. */\n stdout: string\n /** Concatenated stderr. */\n stderr: string\n /** Set when the process exited via signal (timeout / abort). */\n killedBySignal: NodeJS.Signals | null\n /** Wall-clock duration ms (spawn → exit). */\n durationMs: number\n /** Set when timeoutMs elapsed before exit. */\n timedOut: boolean\n}\n\nconst DEFAULT_TIMEOUT_MS = 5 * 60 * 1000\n\n/**\n * Spawn a local coding harness CLI as a subprocess + collect its output.\n *\n * NOT responsible for parsing the harness's output or extracting a diff —\n * the in-process executor's `streamPrompt` orchestrates `git diff` against\n * the worktree after this resolves. This function is intentionally narrow:\n * spawn, wait, capture, return.\n *\n * Fails loud — throws when:\n * - `cwd` doesn't exist (subprocess emits ENOENT; surfaced as Error)\n * - the harness binary is not on PATH (ENOENT)\n *\n * Does NOT throw when:\n * - the subprocess exits non-zero (`result.exitCode` carries the code)\n * - the subprocess is aborted / timed out (`result.killedBySignal` /\n * `result.timedOut` carries the reason)\n *\n * @experimental\n */\nexport function runLocalHarness(options: RunLocalHarnessOptions): Promise<LocalHarnessResult> {\n const { harness, cwd, taskPrompt } = options\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS\n const env = options.env ?? process.env\n const spawnImpl = options.spawn ?? spawn\n\n const invocation = HARNESS_INVOCATIONS[harness]\n if (!invocation) {\n return Promise.reject(new Error(`runLocalHarness: unknown harness ${String(harness)}`))\n }\n\n const startedAt = Date.now()\n const args = invocation.buildArgs(taskPrompt)\n\n return new Promise<LocalHarnessResult>((resolve, reject) => {\n let child: ChildProcess\n try {\n child = spawnImpl(invocation.command, args, { cwd, env, stdio: 'pipe' })\n } catch (err) {\n reject(err instanceof Error ? err : new Error(String(err)))\n return\n }\n\n let stdout = ''\n let stderr = ''\n let timedOut = false\n let settled = false\n\n const timer =\n timeoutMs > 0\n ? setTimeout(() => {\n timedOut = true\n if (!child.killed) child.kill('SIGTERM')\n }, timeoutMs)\n : null\n if (timer && typeof (timer as { unref?: () => void }).unref === 'function') {\n ;(timer as { unref: () => void }).unref()\n }\n\n const onAbort = () => {\n if (!child.killed) child.kill('SIGTERM')\n }\n if (options.signal) {\n if (options.signal.aborted) onAbort()\n else options.signal.addEventListener('abort', onAbort, { once: true })\n }\n\n child.stdout?.on('data', (chunk) => {\n stdout += String(chunk)\n })\n child.stderr?.on('data', (chunk) => {\n stderr += String(chunk)\n })\n\n const finalize = (result: LocalHarnessResult) => {\n if (settled) return\n settled = true\n if (timer) clearTimeout(timer)\n options.signal?.removeEventListener('abort', onAbort)\n resolve(result)\n }\n\n child.on('error', (err) => {\n if (settled) return\n settled = true\n if (timer) clearTimeout(timer)\n options.signal?.removeEventListener('abort', onAbort)\n reject(err)\n })\n\n child.on('close', (code, signal) => {\n finalize({\n exitCode: code,\n stdout,\n stderr,\n killedBySignal: signal,\n durationMs: Date.now() - startedAt,\n timedOut,\n })\n })\n })\n}\n"],"mappings":";AAkBA,SAA4B,aAAa;AAMzC,IAAM,sBAGF;AAAA,EACF,QAAQ;AAAA,IACN,SAAS;AAAA,IACT,WAAW,CAAC,eAAe,CAAC,cAAc,MAAM,UAAU;AAAA,EAC5D;AAAA,EACA,OAAO;AAAA,IACL,SAAS;AAAA,IACT,WAAW,CAAC,eAAe,CAAC,OAAO,UAAU;AAAA,EAC/C;AAAA,EACA,UAAU;AAAA,IACR,SAAS;AAAA,IACT,WAAW,CAAC,eAAe,CAAC,OAAO,UAAU;AAAA,EAC/C;AACF;AA8CA,IAAM,qBAAqB,IAAI,KAAK;AAqB7B,SAAS,gBAAgB,SAA8D;AAC5F,QAAM,EAAE,SAAS,KAAK,WAAW,IAAI;AACrC,QAAM,YAAY,QAAQ,aAAa;AACvC,QAAM,MAAM,QAAQ,OAAO,QAAQ;AACnC,QAAM,YAAY,QAAQ,SAAS;AAEnC,QAAM,aAAa,oBAAoB,OAAO;AAC9C,MAAI,CAAC,YAAY;AACf,WAAO,QAAQ,OAAO,IAAI,MAAM,oCAAoC,OAAO,OAAO,CAAC,EAAE,CAAC;AAAA,EACxF;AAEA,QAAM,YAAY,KAAK,IAAI;AAC3B,QAAM,OAAO,WAAW,UAAU,UAAU;AAE5C,SAAO,IAAI,QAA4B,CAAC,SAAS,WAAW;AAC1D,QAAI;AACJ,QAAI;AACF,cAAQ,UAAU,WAAW,SAAS,MAAM,EAAE,KAAK,KAAK,OAAO,OAAO,CAAC;AAAA,IACzE,SAAS,KAAK;AACZ,aAAO,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC,CAAC;AAC1D;AAAA,IACF;AAEA,QAAI,SAAS;AACb,QAAI,SAAS;AACb,QAAI,WAAW;AACf,QAAI,UAAU;AAEd,UAAM,QACJ,YAAY,IACR,WAAW,MAAM;AACf,iBAAW;AACX,UAAI,CAAC,MAAM,OAAQ,OAAM,KAAK,SAAS;AAAA,IACzC,GAAG,SAAS,IACZ;AACN,QAAI,SAAS,OAAQ,MAAiC,UAAU,YAAY;AAC1E;AAAC,MAAC,MAAgC,MAAM;AAAA,IAC1C;AAEA,UAAM,UAAU,MAAM;AACpB,UAAI,CAAC,MAAM,OAAQ,OAAM,KAAK,SAAS;AAAA,IACzC;AACA,QAAI,QAAQ,QAAQ;AAClB,UAAI,QAAQ,OAAO,QAAS,SAAQ;AAAA,UAC/B,SAAQ,OAAO,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;AAAA,IACvE;AAEA,UAAM,QAAQ,GAAG,QAAQ,CAAC,UAAU;AAClC,gBAAU,OAAO,KAAK;AAAA,IACxB,CAAC;AACD,UAAM,QAAQ,GAAG,QAAQ,CAAC,UAAU;AAClC,gBAAU,OAAO,KAAK;AAAA,IACxB,CAAC;AAED,UAAM,WAAW,CAAC,WAA+B;AAC/C,UAAI,QAAS;AACb,gBAAU;AACV,UAAI,MAAO,cAAa,KAAK;AAC7B,cAAQ,QAAQ,oBAAoB,SAAS,OAAO;AACpD,cAAQ,MAAM;AAAA,IAChB;AAEA,UAAM,GAAG,SAAS,CAAC,QAAQ;AACzB,UAAI,QAAS;AACb,gBAAU;AACV,UAAI,MAAO,cAAa,KAAK;AAC7B,cAAQ,QAAQ,oBAAoB,SAAS,OAAO;AACpD,aAAO,GAAG;AAAA,IACZ,CAAC;AAED,UAAM,GAAG,SAAS,CAAC,MAAM,WAAW;AAClC,eAAS;AAAA,QACP,UAAU;AAAA,QACV;AAAA,QACA;AAAA,QACA,gBAAgB;AAAA,QAChB,YAAY,KAAK,IAAI,IAAI;AAAA,QACzB;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAAA,EACH,CAAC;AACH;","names":[]}
@@ -114,7 +114,7 @@ function createOtelExporter(config) {
114
114
  }
115
115
  ]
116
116
  };
117
- const url = endpoint.replace(/\/+$/, "") + "/v1/traces";
117
+ const url = `${endpoint.replace(/\/+$/, "")}/v1/traces`;
118
118
  try {
119
119
  await fetch(url, {
120
120
  method: "POST",
@@ -197,4 +197,4 @@ export {
197
197
  createOtelExporter,
198
198
  loopEventToOtelSpan
199
199
  };
200
- //# sourceMappingURL=chunk-7HN72MF3.js.map
200
+ //# sourceMappingURL=chunk-QZEDHTT2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/mcp/openai-tools.ts","../src/otel-export.ts"],"sourcesContent":["/**\n * @experimental\n *\n * OpenAI Chat Completions `tools[]` projection of the 5 agent-runtime MCP\n * delegation tools.\n *\n * Use when configuring `createOpenAICompatibleBackend({ tools: ... })` so the\n * model can call `delegate_code`, `delegate_research`, `delegate_feedback`,\n * `delegation_status`, and `delegation_history` through the OpenAI-compat\n * transport (tcloud, OpenRouter, OpenAI direct, cli-bridge). The runtime\n * surfaces tool calls as `tool_call` stream events — execution is the\n * caller's responsibility (typically the parent sandbox runtime's MCP\n * mount).\n *\n * Sandbox-SDK callers do NOT need this helper: the sandbox runtime mounts\n * MCP servers natively and the in-sandbox harness discovers tools via the\n * runtime, not via an OpenAI tools array.\n *\n * Tool name + description + JSON-schema are pulled from the canonical\n * `DELEGATE_*` constants exported by `./tools/*` so the projection cannot\n * drift from the server's own validators.\n */\n\nimport type { OpenAIChatTool } from '../types'\nimport {\n DELEGATE_CODE_DESCRIPTION,\n DELEGATE_CODE_INPUT_SCHEMA,\n DELEGATE_CODE_TOOL_NAME,\n} from './tools/delegate-code'\nimport {\n DELEGATE_FEEDBACK_DESCRIPTION,\n DELEGATE_FEEDBACK_INPUT_SCHEMA,\n DELEGATE_FEEDBACK_TOOL_NAME,\n} from './tools/delegate-feedback'\nimport {\n DELEGATE_RESEARCH_DESCRIPTION,\n DELEGATE_RESEARCH_INPUT_SCHEMA,\n DELEGATE_RESEARCH_TOOL_NAME,\n} from './tools/delegate-research'\nimport {\n DELEGATION_HISTORY_DESCRIPTION,\n DELEGATION_HISTORY_INPUT_SCHEMA,\n DELEGATION_HISTORY_TOOL_NAME,\n} from './tools/delegation-history'\nimport {\n DELEGATION_STATUS_DESCRIPTION,\n DELEGATION_STATUS_INPUT_SCHEMA,\n DELEGATION_STATUS_TOOL_NAME,\n} from './tools/delegation-status'\n\nfunction buildTool(\n name: string,\n description: string,\n parameters: Readonly<Record<string, unknown>>,\n): OpenAIChatTool {\n // `parameters` arrives as a deeply-readonly `as const` literal. The\n // OpenAI-compat backend JSON-serializes the body so a shallow copy\n // into a plain object is sufficient — and shields callers that mutate\n // the returned descriptor from corrupting the source constant.\n return {\n type: 'function',\n function: { name, description, parameters: { ...parameters } },\n }\n}\n\n/**\n * @experimental\n *\n * Returns the 5 delegation tools projected into OpenAI Chat Completions\n * `tools[]` shape. The order is stable: `delegate_code`,\n * `delegate_research`, `delegate_feedback`, `delegation_status`,\n * `delegation_history`.\n */\nexport function mcpToolsForRuntimeMcp(): OpenAIChatTool[] {\n return [\n buildTool(\n DELEGATE_CODE_TOOL_NAME,\n DELEGATE_CODE_DESCRIPTION,\n DELEGATE_CODE_INPUT_SCHEMA as Readonly<Record<string, unknown>>,\n ),\n buildTool(\n DELEGATE_RESEARCH_TOOL_NAME,\n DELEGATE_RESEARCH_DESCRIPTION,\n DELEGATE_RESEARCH_INPUT_SCHEMA as Readonly<Record<string, unknown>>,\n ),\n buildTool(\n DELEGATE_FEEDBACK_TOOL_NAME,\n DELEGATE_FEEDBACK_DESCRIPTION,\n DELEGATE_FEEDBACK_INPUT_SCHEMA as Readonly<Record<string, unknown>>,\n ),\n buildTool(\n DELEGATION_STATUS_TOOL_NAME,\n DELEGATION_STATUS_DESCRIPTION,\n DELEGATION_STATUS_INPUT_SCHEMA as Readonly<Record<string, unknown>>,\n ),\n buildTool(\n DELEGATION_HISTORY_TOOL_NAME,\n DELEGATION_HISTORY_DESCRIPTION,\n DELEGATION_HISTORY_INPUT_SCHEMA as Readonly<Record<string, unknown>>,\n ),\n ]\n}\n\n/**\n * @experimental\n *\n * Subset filter — return only the projected tools whose `function.name`\n * appears in `names`. Useful for curated mounts (e.g. only the queue-bound\n * delegation tools, omitting `delegate_feedback`). Unknown names are\n * silently ignored; pass an empty array to get an empty result.\n */\nexport function mcpToolsForRuntimeMcpSubset(names: ReadonlyArray<string>): OpenAIChatTool[] {\n const allowed = new Set(names)\n return mcpToolsForRuntimeMcp().filter((tool) => allowed.has(tool.function.name))\n}\n","/**\n * OTEL span exporter — streams LoopTraceEvents to an OTLP/HTTP collector.\n *\n * Reads OTEL_EXPORTER_OTLP_ENDPOINT + OTEL_EXPORTER_OTLP_HEADERS from env\n * when no explicit config is given. Keeps the runtime dep-free from\n * @opentelemetry/sdk-trace-base — minimal OTLP/JSON serializer.\n *\n * The exporter accepts both raw OtelSpan objects and LoopTraceEvents\n * (which get converted to OTLP spans automatically).\n */\n\nexport interface OtelExportConfig {\n /** OTLP endpoint. Reads OTEL_EXPORTER_OTLP_ENDPOINT env by default. */\n endpoint?: string\n /** OTLP headers. Reads OTEL_EXPORTER_OTLP_HEADERS env by default. */\n headers?: Record<string, string>\n /** Batch size before flush. Default 64. */\n batchSize?: number\n /** Flush interval ms. Default 5000. */\n flushIntervalMs?: number\n /** Resource attributes stamped on every export. */\n resourceAttributes?: Record<string, string | number | boolean>\n /** Service name. Default 'agent-runtime'. */\n serviceName?: string\n}\n\nexport interface OtelExporter {\n /** Export a span. */\n exportSpan(span: OtelSpan): void\n /** Force flush pending spans. */\n flush(): Promise<void>\n /** Shutdown cleanly. */\n shutdown(): Promise<void>\n}\n\nexport interface OtelSpan {\n traceId: string\n spanId: string\n parentSpanId?: string\n name: string\n kind?: number\n startTimeUnixNano: string\n endTimeUnixNano: string\n attributes?: OtelAttribute[]\n status?: { code: number; message?: string }\n}\n\nexport interface OtelAttribute {\n key: string\n value: { stringValue?: string; intValue?: string; doubleValue?: number; boolValue?: boolean }\n}\n\ninterface OtlpResourceSpans {\n resource: { attributes: OtelAttribute[] }\n scopeSpans: Array<{ scope: { name: string; version: string }; spans: OtelSpan[] }>\n}\n\ninterface OtlpExport {\n resourceSpans: OtlpResourceSpans[]\n}\n\nconst SCOPE = { name: '@tangle-network/agent-runtime', version: '0.23.0' }\n\n/**\n * Create an OTEL exporter. Returns undefined when no endpoint is configured.\n */\nexport function createOtelExporter(config?: OtelExportConfig): OtelExporter | undefined {\n const resolvedEndpoint =\n config?.endpoint ??\n (typeof process !== 'undefined' ? process.env.OTEL_EXPORTER_OTLP_ENDPOINT : undefined)\n if (!resolvedEndpoint) return undefined\n const endpoint: string = resolvedEndpoint\n\n const headers = config?.headers ?? parseHeadersFromEnv()\n const batchSize = config?.batchSize ?? 64\n const flushIntervalMs = config?.flushIntervalMs ?? 5000\n const serviceName = config?.serviceName ?? 'agent-runtime'\n const resourceAttrs = config?.resourceAttributes ?? {}\n\n const pending: OtelSpan[] = []\n let timer: ReturnType<typeof setInterval> | undefined\n let stopped = false\n\n const exporter: OtelExporter = {\n exportSpan(span: OtelSpan): void {\n if (stopped) return\n pending.push(span)\n if (pending.length >= batchSize) {\n void doFlush()\n }\n },\n\n async flush(): Promise<void> {\n await doFlush()\n },\n\n async shutdown(): Promise<void> {\n stopped = true\n if (timer !== undefined) {\n clearInterval(timer)\n timer = undefined\n }\n await doFlush()\n },\n }\n\n timer = setInterval(() => {\n if (pending.length > 0) void doFlush()\n }, flushIntervalMs)\n if (typeof timer === 'object' && 'unref' in timer) {\n ;(timer as NodeJS.Timeout).unref()\n }\n\n async function doFlush(): Promise<void> {\n if (pending.length === 0) return\n const batch = pending.splice(0)\n const body: OtlpExport = {\n resourceSpans: [\n {\n resource: {\n attributes: toAttributes({\n 'service.name': serviceName,\n ...resourceAttrs,\n }),\n },\n scopeSpans: [{ scope: SCOPE, spans: batch }],\n },\n ],\n }\n const url = `${endpoint.replace(/\\/+$/, '')}/v1/traces`\n try {\n await fetch(url, {\n method: 'POST',\n headers: { 'content-type': 'application/json', ...headers },\n body: JSON.stringify(body),\n })\n } catch {\n // Best-effort — telemetry export must not crash the runtime.\n }\n }\n\n return exporter\n}\n\n/**\n * Convert a LoopTraceEvent into an OtelSpan for export.\n */\nexport function loopEventToOtelSpan(\n event: {\n kind: string\n runId: string\n timestamp: number\n payload: object\n },\n traceId: string,\n parentSpanId?: string,\n): OtelSpan {\n const spanId = generateSpanId()\n const attrs: Record<string, string | number | boolean> = {\n 'loop.event_kind': event.kind,\n 'loop.run_id': event.runId,\n }\n for (const [k, v] of Object.entries(event.payload)) {\n if (typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean') {\n attrs[`loop.${k}`] = v\n }\n }\n const ts = msToNs(event.timestamp)\n return {\n traceId: padTraceId(traceId),\n spanId,\n parentSpanId: parentSpanId ? padSpanId(parentSpanId) : undefined,\n name: event.kind,\n kind: 1,\n startTimeUnixNano: ts,\n endTimeUnixNano: ts,\n attributes: toAttributes(attrs),\n status: { code: 1 },\n }\n}\n\nfunction parseHeadersFromEnv(): Record<string, string> {\n if (typeof process === 'undefined') return {}\n const raw = process.env.OTEL_EXPORTER_OTLP_HEADERS\n if (!raw) return {}\n const out: Record<string, string> = {}\n for (const pair of raw.split(',')) {\n const eq = pair.indexOf('=')\n if (eq < 0) continue\n const key = pair.slice(0, eq).trim()\n const value = pair.slice(eq + 1).trim()\n if (key) out[key] = value\n }\n return out\n}\n\nfunction toAttributes(record: Record<string, string | number | boolean>): OtelAttribute[] {\n return Object.entries(record).map(([key, value]) => ({\n key,\n value:\n typeof value === 'number'\n ? Number.isInteger(value)\n ? { intValue: value.toString() }\n : { doubleValue: value }\n : typeof value === 'boolean'\n ? { boolValue: value }\n : { stringValue: value },\n }))\n}\n\nfunction msToNs(ms: number): string {\n return (BigInt(Math.floor(ms)) * 1_000_000n).toString()\n}\n\nfunction padSpanId(id: string): string {\n const cleaned = id.replace(/-/g, '')\n return cleaned.slice(0, 16).padEnd(16, '0')\n}\n\nfunction padTraceId(id: string): string {\n const cleaned = id.replace(/-/g, '')\n return cleaned.slice(0, 32).padEnd(32, '0')\n}\n\nfunction generateSpanId(): string {\n const bytes = new Uint8Array(8)\n if (typeof globalThis.crypto?.getRandomValues === 'function') {\n globalThis.crypto.getRandomValues(bytes)\n } else {\n for (let i = 0; i < 8; i++) bytes[i] = Math.floor(Math.random() * 256)\n }\n return Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('')\n}\n"],"mappings":"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","names":[]}