@tangle-network/agent-runtime 0.7.0 → 0.9.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/README.md +42 -13
- package/dist/index.d.ts +235 -35
- package/dist/index.js +282 -3
- package/dist/index.js.map +1 -1
- package/dist/platform.d.ts +197 -0
- package/dist/platform.js +185 -0
- package/dist/platform.js.map +1 -0
- package/package.json +11 -3
- package/docs/domain-agent-runtime-integration-issues.md +0 -165
- package/docs/product-runtime-kernel.md +0 -326
package/dist/index.js
CHANGED
|
@@ -290,6 +290,279 @@ function stringValue(value) {
|
|
|
290
290
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
291
291
|
}
|
|
292
292
|
|
|
293
|
+
// src/chat-turn.ts
|
|
294
|
+
import { mergeAgentProfiles } from "@tangle-network/sandbox";
|
|
295
|
+
var RUNTIME_PATH = "/runtime/agents/run/stream";
|
|
296
|
+
async function* runChatTurn(options) {
|
|
297
|
+
const turnProfile = options.overlay ? composeTurnProfile(options.profile, options.overlay) : options.profile;
|
|
298
|
+
const url = `${options.sandbox.runtimeUrl}${RUNTIME_PATH}`;
|
|
299
|
+
const backendType = turnProfile.metadata?.backend?.type ?? "claude-code";
|
|
300
|
+
const body = JSON.stringify({
|
|
301
|
+
backend: {
|
|
302
|
+
type: backendType,
|
|
303
|
+
profile: turnProfile,
|
|
304
|
+
...options.modelOverride ? { model: { default: options.modelOverride } } : {}
|
|
305
|
+
},
|
|
306
|
+
messages: [...options.priorMessages, { role: "user", content: options.message }]
|
|
307
|
+
});
|
|
308
|
+
const headers = {
|
|
309
|
+
"Content-Type": "application/json",
|
|
310
|
+
Accept: "text/event-stream"
|
|
311
|
+
};
|
|
312
|
+
if (options.sandbox.authHeader) {
|
|
313
|
+
headers[options.sandbox.authHeader.name] = options.sandbox.authHeader.value;
|
|
314
|
+
}
|
|
315
|
+
const fetchImpl = options.fetch ?? fetch;
|
|
316
|
+
const response = await fetchImpl(url, {
|
|
317
|
+
method: "POST",
|
|
318
|
+
headers,
|
|
319
|
+
body,
|
|
320
|
+
signal: options.signal
|
|
321
|
+
});
|
|
322
|
+
if (!response.ok || !response.body) {
|
|
323
|
+
const text = response.body ? await response.text() : "";
|
|
324
|
+
throw new ChatTurnError(
|
|
325
|
+
`runChatTurn: sandbox returned ${response.status} ${response.statusText}: ${text.slice(0, 500)}`,
|
|
326
|
+
response.status
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
yield* parseSseStream(response.body);
|
|
330
|
+
}
|
|
331
|
+
function composeTurnProfile(base, overlay) {
|
|
332
|
+
const partial = {};
|
|
333
|
+
if (overlay.promptOverlay) partial.prompt = overlay.promptOverlay;
|
|
334
|
+
if (overlay.resourcesOverlay) partial.resources = overlay.resourcesOverlay;
|
|
335
|
+
if (overlay.subagentsOverlay) partial.subagents = overlay.subagentsOverlay;
|
|
336
|
+
if (overlay.metadata) partial.metadata = overlay.metadata;
|
|
337
|
+
return mergeAgentProfiles(base, partial) ?? base;
|
|
338
|
+
}
|
|
339
|
+
async function* parseSseStream(stream) {
|
|
340
|
+
const decoder = new TextDecoder();
|
|
341
|
+
const reader = stream.getReader();
|
|
342
|
+
let buffer = "";
|
|
343
|
+
try {
|
|
344
|
+
while (true) {
|
|
345
|
+
const { value, done } = await reader.read();
|
|
346
|
+
if (done) break;
|
|
347
|
+
buffer += decoder.decode(value, { stream: true });
|
|
348
|
+
let idx = buffer.indexOf("\n");
|
|
349
|
+
while (idx >= 0) {
|
|
350
|
+
const line = buffer.slice(0, idx).replace(/\r$/, "").trim();
|
|
351
|
+
buffer = buffer.slice(idx + 1);
|
|
352
|
+
idx = buffer.indexOf("\n");
|
|
353
|
+
if (!line) continue;
|
|
354
|
+
const payload = line.startsWith("data:") ? line.slice(5).trim() : line;
|
|
355
|
+
if (payload === "[DONE]" || payload === "") continue;
|
|
356
|
+
try {
|
|
357
|
+
const parsed = JSON.parse(payload);
|
|
358
|
+
yield parsed;
|
|
359
|
+
} catch {
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
} finally {
|
|
364
|
+
reader.releaseLock();
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
var ChatTurnError = class extends Error {
|
|
368
|
+
constructor(message, status) {
|
|
369
|
+
super(message);
|
|
370
|
+
this.status = status;
|
|
371
|
+
this.name = "ChatTurnError";
|
|
372
|
+
}
|
|
373
|
+
status;
|
|
374
|
+
};
|
|
375
|
+
function sandboxAsChatTurnTarget(instance, opts) {
|
|
376
|
+
const base = instance.url ?? instance.connection?.runtimeUrl ?? "";
|
|
377
|
+
if (!base) {
|
|
378
|
+
throw new ChatTurnError(
|
|
379
|
+
`sandboxAsChatTurnTarget: SandboxInstance has neither .url nor .connection.runtimeUrl set`
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
return {
|
|
383
|
+
id: instance.id,
|
|
384
|
+
runtimeUrl: base.replace(/\/+$/, ""),
|
|
385
|
+
authHeader: opts?.authHeader
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// src/intent-router.ts
|
|
390
|
+
var DEFAULT_PATTERN_WEIGHT = 1.5;
|
|
391
|
+
var DEFAULT_MIN_SCORE = 1;
|
|
392
|
+
function isMatcher(value) {
|
|
393
|
+
if (!value || typeof value !== "object") return false;
|
|
394
|
+
const v = value;
|
|
395
|
+
return Array.isArray(v.keywords) || Array.isArray(v.patterns) || typeof v.minScore === "number";
|
|
396
|
+
}
|
|
397
|
+
function readMatcher(subagent) {
|
|
398
|
+
const m = subagent.metadata;
|
|
399
|
+
if (!m) return null;
|
|
400
|
+
const raw = m.matchers ?? m.matcher ?? null;
|
|
401
|
+
if (!raw) return null;
|
|
402
|
+
if (Array.isArray(raw)) {
|
|
403
|
+
const merged = { keywords: [], patterns: [], minScore: DEFAULT_MIN_SCORE };
|
|
404
|
+
for (const entry of raw) {
|
|
405
|
+
if (!isMatcher(entry)) continue;
|
|
406
|
+
merged.keywords = [...merged.keywords ?? [], ...entry.keywords ?? []];
|
|
407
|
+
merged.patterns = [...merged.patterns ?? [], ...entry.patterns ?? []];
|
|
408
|
+
if (entry.minScore !== void 0) merged.minScore = entry.minScore;
|
|
409
|
+
}
|
|
410
|
+
return merged;
|
|
411
|
+
}
|
|
412
|
+
if (isMatcher(raw)) return raw;
|
|
413
|
+
return null;
|
|
414
|
+
}
|
|
415
|
+
function classifyIntent(profile, message, opts = {}) {
|
|
416
|
+
const lower = message.toLowerCase();
|
|
417
|
+
const subagents = profile.subagents ?? {};
|
|
418
|
+
const skip = new Set(opts.skip ?? []);
|
|
419
|
+
const defaultMinScore = opts.defaultMinScore ?? DEFAULT_MIN_SCORE;
|
|
420
|
+
const scores = {};
|
|
421
|
+
const evaluated = {};
|
|
422
|
+
let bestId = null;
|
|
423
|
+
let bestScore = -Infinity;
|
|
424
|
+
let bestSubagent = null;
|
|
425
|
+
for (const [id, subagent] of Object.entries(subagents)) {
|
|
426
|
+
if (skip.has(id)) continue;
|
|
427
|
+
if (opts.filter && !opts.filter(subagent, id)) continue;
|
|
428
|
+
const matcher = readMatcher(subagent);
|
|
429
|
+
if (!matcher) {
|
|
430
|
+
evaluated[id] = { keywordHits: 0, patternHits: 0, minScore: defaultMinScore };
|
|
431
|
+
scores[id] = 0;
|
|
432
|
+
continue;
|
|
433
|
+
}
|
|
434
|
+
const weight = matcher.weight ?? 1;
|
|
435
|
+
let kHits = 0;
|
|
436
|
+
for (const kw of matcher.keywords ?? []) {
|
|
437
|
+
if (kw && lower.includes(kw.toLowerCase())) kHits += 1;
|
|
438
|
+
}
|
|
439
|
+
let pHits = 0;
|
|
440
|
+
for (const p of matcher.patterns ?? []) {
|
|
441
|
+
const re = p instanceof RegExp ? p : new RegExp(p, "i");
|
|
442
|
+
if (re.test(message)) pHits += 1;
|
|
443
|
+
}
|
|
444
|
+
const score = kHits * weight + pHits * DEFAULT_PATTERN_WEIGHT * weight;
|
|
445
|
+
const minScore = matcher.minScore ?? defaultMinScore;
|
|
446
|
+
scores[id] = score;
|
|
447
|
+
evaluated[id] = { keywordHits: kHits, patternHits: pHits, minScore };
|
|
448
|
+
if (score >= minScore && score > bestScore) {
|
|
449
|
+
bestScore = score;
|
|
450
|
+
bestId = id;
|
|
451
|
+
bestSubagent = subagent;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
if (bestId === null) {
|
|
455
|
+
return { id: null, subagent: null, score: 0, scores, evaluated };
|
|
456
|
+
}
|
|
457
|
+
return { id: bestId, subagent: bestSubagent, score: bestScore, scores, evaluated };
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// src/profile-conformance.ts
|
|
461
|
+
var DEFAULT_SHELL_CAPS = [
|
|
462
|
+
"bash",
|
|
463
|
+
"sh",
|
|
464
|
+
"python",
|
|
465
|
+
"node",
|
|
466
|
+
"curl",
|
|
467
|
+
"web",
|
|
468
|
+
"browser",
|
|
469
|
+
"shell"
|
|
470
|
+
];
|
|
471
|
+
var DEFAULT_MIN_PROMPT_CHARS = 800;
|
|
472
|
+
function checkSystemPrompt(profile, minChars) {
|
|
473
|
+
const prompt = profile.prompt?.systemPrompt;
|
|
474
|
+
if (!prompt || prompt.trim().length < minChars) {
|
|
475
|
+
return [
|
|
476
|
+
{
|
|
477
|
+
severity: "error",
|
|
478
|
+
code: "system-prompt-too-short",
|
|
479
|
+
path: "prompt.systemPrompt",
|
|
480
|
+
message: `prompt.systemPrompt is ${prompt?.length ?? 0} chars; min required is ${minChars}. Profiles below this threshold are almost always placeholders.`
|
|
481
|
+
}
|
|
482
|
+
];
|
|
483
|
+
}
|
|
484
|
+
return [];
|
|
485
|
+
}
|
|
486
|
+
function checkToolsVsMcp(profile, opts) {
|
|
487
|
+
const issues = [];
|
|
488
|
+
const shellCaps = new Set(opts.knownShellCapabilities ?? DEFAULT_SHELL_CAPS);
|
|
489
|
+
const allowedWithoutMcp = new Set(opts.toolsAllowedWithoutMcp ?? []);
|
|
490
|
+
const tools = profile.tools ?? {};
|
|
491
|
+
const mcp = profile.mcp ?? {};
|
|
492
|
+
for (const [name, value] of Object.entries(tools)) {
|
|
493
|
+
if (shellCaps.has(name)) {
|
|
494
|
+
issues.push({
|
|
495
|
+
severity: "error",
|
|
496
|
+
code: "shell-capability-as-tool",
|
|
497
|
+
path: `tools.${name}`,
|
|
498
|
+
message: `'${name}' is a shell capability, not a tool. Move to permissions and remove from tools.`
|
|
499
|
+
});
|
|
500
|
+
continue;
|
|
501
|
+
}
|
|
502
|
+
if (allowedWithoutMcp.has(name)) continue;
|
|
503
|
+
if (value === false) continue;
|
|
504
|
+
if (!mcp[name]) {
|
|
505
|
+
issues.push({
|
|
506
|
+
severity: "error",
|
|
507
|
+
code: "decorative-tool-without-mcp",
|
|
508
|
+
path: `tools.${name}`,
|
|
509
|
+
message: `'${name}' declared in tools but no corresponding mcp[${name}] AgentProfileMcpServer. Either wire an MCP server, list this name in toolsAllowedWithoutMcp (if it's a file-mounted CLI binary), or remove from tools.`
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
return issues;
|
|
514
|
+
}
|
|
515
|
+
function checkSubagentShape(subagent, id) {
|
|
516
|
+
const issues = [];
|
|
517
|
+
if (!subagent.description || subagent.description.trim().length < 40) {
|
|
518
|
+
issues.push({
|
|
519
|
+
severity: "error",
|
|
520
|
+
code: "subagent-description-too-short",
|
|
521
|
+
path: `subagents.${id}.description`,
|
|
522
|
+
message: `subagents.${id}.description is missing or too short. Required for routing UX + audit trail.`
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
if (!subagent.prompt || subagent.prompt.trim().length < 100) {
|
|
526
|
+
issues.push({
|
|
527
|
+
severity: "error",
|
|
528
|
+
code: "subagent-prompt-too-short",
|
|
529
|
+
path: `subagents.${id}.prompt`,
|
|
530
|
+
message: `subagents.${id}.prompt is missing or below 100 chars. Either ship the specialist prompt or move this subagent under metadata.plannedSubagents.`
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
return issues;
|
|
534
|
+
}
|
|
535
|
+
function checkScaffoldSubagents(profile, strict) {
|
|
536
|
+
const issues = [];
|
|
537
|
+
const subagents = profile.subagents ?? {};
|
|
538
|
+
for (const [id, subagent] of Object.entries(subagents)) {
|
|
539
|
+
const meta = subagent.metadata;
|
|
540
|
+
const status = meta?.status;
|
|
541
|
+
if (status === "scaffold" || status === "not-implemented" || meta?.implemented === false) {
|
|
542
|
+
issues.push({
|
|
543
|
+
severity: strict ? "error" : "warn",
|
|
544
|
+
code: "subagent-scaffold-shipped",
|
|
545
|
+
path: `subagents.${id}`,
|
|
546
|
+
message: `subagents.${id} is a scaffold (metadata.status='${status}'). Router must gate on metadata.status === 'full' OR caller must opt into scaffolds explicitly.`
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
return issues;
|
|
551
|
+
}
|
|
552
|
+
function assertProfileConformance(profile, opts = {}) {
|
|
553
|
+
const issues = [
|
|
554
|
+
...checkSystemPrompt(profile, opts.minSystemPromptChars ?? DEFAULT_MIN_PROMPT_CHARS),
|
|
555
|
+
...checkToolsVsMcp(profile, opts),
|
|
556
|
+
...checkScaffoldSubagents(profile, opts.strictNoScaffolds ?? false)
|
|
557
|
+
];
|
|
558
|
+
for (const [id, subagent] of Object.entries(profile.subagents ?? {})) {
|
|
559
|
+
issues.push(...checkSubagentShape(subagent, id));
|
|
560
|
+
}
|
|
561
|
+
const errors = issues.filter((i) => i.severity === "error");
|
|
562
|
+
const warnings = issues.filter((i) => i.severity === "warn");
|
|
563
|
+
return { valid: errors.length === 0, errors, warnings };
|
|
564
|
+
}
|
|
565
|
+
|
|
293
566
|
// src/readiness.ts
|
|
294
567
|
var DEFAULT_MINIMUM_READINESS_SCORE = 0.7;
|
|
295
568
|
function decideKnowledgeReadiness(report, options = {}) {
|
|
@@ -1296,9 +1569,9 @@ function projectToTraceEvent(event) {
|
|
|
1296
1569
|
phase: "tool_call",
|
|
1297
1570
|
toolName: event.toolName,
|
|
1298
1571
|
toolCallId: event.toolCallId
|
|
1299
|
-
// Args
|
|
1300
|
-
//
|
|
1301
|
-
//
|
|
1572
|
+
// Args omitted — trace events are point-in-time markers, not the
|
|
1573
|
+
// canonical store for tool I/O. Attach payloads to a `ToolSpan`
|
|
1574
|
+
// when retention is required.
|
|
1302
1575
|
}
|
|
1303
1576
|
};
|
|
1304
1577
|
case "tool_result":
|
|
@@ -1363,6 +1636,7 @@ export {
|
|
|
1363
1636
|
AgentEvalError2 as AgentEvalError,
|
|
1364
1637
|
BackendTransportError,
|
|
1365
1638
|
CaptureIntegrityError,
|
|
1639
|
+
ChatTurnError,
|
|
1366
1640
|
ConfigError,
|
|
1367
1641
|
InMemoryRuntimeSessionStore,
|
|
1368
1642
|
JudgeError,
|
|
@@ -1372,6 +1646,9 @@ export {
|
|
|
1372
1646
|
SessionMismatchError,
|
|
1373
1647
|
ValidationError,
|
|
1374
1648
|
VerificationError,
|
|
1649
|
+
assertProfileConformance,
|
|
1650
|
+
classifyIntent,
|
|
1651
|
+
composeTurnProfile,
|
|
1375
1652
|
createIterableBackend,
|
|
1376
1653
|
createOpenAICompatibleBackend,
|
|
1377
1654
|
createRuntimeEventCollector,
|
|
@@ -1383,7 +1660,9 @@ export {
|
|
|
1383
1660
|
readinessServerSentEvent,
|
|
1384
1661
|
runAgentTask,
|
|
1385
1662
|
runAgentTaskStream,
|
|
1663
|
+
runChatTurn,
|
|
1386
1664
|
runtimeStreamServerSentEvent,
|
|
1665
|
+
sandboxAsChatTurnTarget,
|
|
1387
1666
|
sanitizeAgentRuntimeEvent,
|
|
1388
1667
|
sanitizeKnowledgeReadinessReport,
|
|
1389
1668
|
sanitizeRuntimeStreamEvent,
|