@tangle-network/traces 0.17.0 → 0.17.1
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/{chunk-4QY4U4XW.js → chunk-SGQOMHZP.js} +26 -8
- package/dist/cli.js +1 -1
- package/dist/index.js +1 -1
- package/docs/trace-analysts.md +41 -0
- package/package.json +2 -2
|
@@ -2448,7 +2448,10 @@ function distinctClaudeEvent(event, seen, sourcePath, fallbackUid) {
|
|
|
2448
2448
|
if (previous.fingerprint !== fingerprint) {
|
|
2449
2449
|
throw new ClaudeEventConflictError(sourcePath, uid);
|
|
2450
2450
|
}
|
|
2451
|
-
if (previous.originKind === null && originKind !== null)
|
|
2451
|
+
if (previous.originKind === null && originKind !== null) {
|
|
2452
|
+
seen.set(uid, { fingerprint, originKind });
|
|
2453
|
+
return { originEnrichment: originKind, uid };
|
|
2454
|
+
}
|
|
2452
2455
|
if (previous.originKind !== null && originKind !== null && previous.originKind !== originKind) {
|
|
2453
2456
|
throw new ClaudeEventConflictError(sourcePath, uid);
|
|
2454
2457
|
}
|
|
@@ -15478,7 +15481,10 @@ var FILE_DELIVERY_CONTRACT_LINES = [
|
|
|
15478
15481
|
' "confidence": number 0..1}',
|
|
15479
15482
|
"Do NOT include a rationale field. Keep every string SHORT \u2014 long strings get corrupted in transport and void your work.",
|
|
15480
15483
|
`Report at most ${MAX_FINDINGS} findings; a finding cites 1..${MAX_SPAN_IDS_PER_FINDING} span_ids, each copied VERBATIM from a span in ${TRAJECTORY_FILE_NAME}.`,
|
|
15481
|
-
'"findings" is [] only for a clean trajectory.'
|
|
15484
|
+
'"findings" is [] only for a clean trajectory.',
|
|
15485
|
+
"FINAL MESSAGE RULE: after your REPL work is done, send ONE last message that contains NOTHING BUT the",
|
|
15486
|
+
"fenced ```json block above \u2014 no preamble, no summary, no code identifiers around it. A reply whose last",
|
|
15487
|
+
"message is prose or a partial block is discarded unread."
|
|
15482
15488
|
];
|
|
15483
15489
|
function buildPrompt(question, projection) {
|
|
15484
15490
|
if (projection.delivery.mode === "file-cwd") {
|
|
@@ -15499,16 +15505,23 @@ function buildPrompt(question, projection) {
|
|
|
15499
15505
|
projection.rendered
|
|
15500
15506
|
].join("\n");
|
|
15501
15507
|
}
|
|
15502
|
-
function
|
|
15508
|
+
function replyWasTruncated(previousReply, finishReason) {
|
|
15509
|
+
if (finishReason === "length") return true;
|
|
15510
|
+
if (finishReason !== null) return false;
|
|
15511
|
+
return extractJsonObject(previousReply) === null;
|
|
15512
|
+
}
|
|
15513
|
+
function buildRepairPrompt(parseDefect, previousReply, finishReason) {
|
|
15514
|
+
const truncated = replyWasTruncated(previousReply, finishReason);
|
|
15503
15515
|
return [
|
|
15504
15516
|
"Your previous reply to a trace-analysis task was structurally malformed and could not be parsed",
|
|
15505
|
-
`(${parseDefect}). Below is your previous reply verbatim
|
|
15506
|
-
"fenced ```json block, no other text, no tools. The JSON object has exactly two fields:",
|
|
15517
|
+
`(${parseDefect}${truncated ? "; it was cut off by the output limit" : ""}). Below is your previous reply verbatim.`,
|
|
15518
|
+
"Re-emit ONLY the corrected JSON \u2014 one fenced ```json block, no other text, no tools. The JSON object has exactly two fields:",
|
|
15507
15519
|
' "answer": string (ONE short sentence, max 300 chars)',
|
|
15508
15520
|
' "findings": array (possibly empty) of {"span_ids": [string, ...],',
|
|
15509
15521
|
' "severity": "critical"|"high"|"medium"|"low"|"info", "area": string (max 40 chars),',
|
|
15510
15522
|
' "claim": string (max 200 chars), "action": string (optional, max 200 chars), "confidence": number 0..1}',
|
|
15511
|
-
"No rationale field. Keep every string SHORT.
|
|
15523
|
+
"No rationale field. Keep every string SHORT.",
|
|
15524
|
+
truncated ? "Your previous reply was cut off by the output limit, so re-emit at most 3 findings \u2014 your strongest \u2014 with terse strings (claim and action under 120 chars each); do not reproduce the whole cut-off list." : "Preserve the span ids and verdicts of your previous reply exactly; shorten prose freely.",
|
|
15512
15525
|
"",
|
|
15513
15526
|
"PREVIOUS REPLY:",
|
|
15514
15527
|
previousReply
|
|
@@ -15675,7 +15688,12 @@ async function callBridge(transport, url, model, content, timeoutMs, callerSigna
|
|
|
15675
15688
|
if (typeof replyContent !== "string" || replyContent.length === 0) {
|
|
15676
15689
|
throw new PrimeBridgeError(`${turn}bridge reply carries no message content`);
|
|
15677
15690
|
}
|
|
15678
|
-
|
|
15691
|
+
const finishReason = record2.choices?.[0]?.finish_reason;
|
|
15692
|
+
return {
|
|
15693
|
+
content: replyContent,
|
|
15694
|
+
usage: normalizeUsage(record2.usage),
|
|
15695
|
+
finishReason: typeof finishReason === "string" && finishReason.length > 0 ? finishReason : null
|
|
15696
|
+
};
|
|
15679
15697
|
}
|
|
15680
15698
|
function failure(output, error) {
|
|
15681
15699
|
return { analyzer: ANALYST_ID, kind: "report", ok: false, output, error };
|
|
@@ -15748,7 +15766,7 @@ function primeAnalyzer(opts = {}) {
|
|
|
15748
15766
|
transport,
|
|
15749
15767
|
url,
|
|
15750
15768
|
model,
|
|
15751
|
-
buildRepairPrompt(parseDefect, reply.content),
|
|
15769
|
+
buildRepairPrompt(parseDefect, reply.content, reply.finishReason),
|
|
15752
15770
|
timeoutMs,
|
|
15753
15771
|
analyzeOpts.signal,
|
|
15754
15772
|
"repair-turn "
|
package/dist/cli.js
CHANGED
package/dist/index.js
CHANGED
package/docs/trace-analysts.md
CHANGED
|
@@ -389,6 +389,47 @@ Output expectations:
|
|
|
389
389
|
|
|
390
390
|
The scored prime-vs-dspy comparison — same trajectories, same scoring — lives in `@tangle-network/agent-eval`'s analyst benchmark (`runAnalystBenchmark`); this engine is the capture-side entry point, not the scoreboard.
|
|
391
391
|
|
|
392
|
+
### Verifying against a real prime
|
|
393
|
+
|
|
394
|
+
The suite above runs against a scripted bridge over real HTTP. A full real run — real
|
|
395
|
+
prime-agent, real model, real REPL — is a manual, credentials-on-the-host affair:
|
|
396
|
+
|
|
397
|
+
1. **Install prime-agent the way cli-bridge pins it.** It is a hard fork of pi that reuses
|
|
398
|
+
pi's npm name, so `npm i` gives you the WRONG agent: clone
|
|
399
|
+
`PrimeIntellect-ai/prime-agent`, `git checkout <the commit src/backends/prime.ts names>`
|
|
400
|
+
(be9e2fa0 at the time of writing), build, and expose the bundle as `prime-agent`
|
|
401
|
+
(cli-bridge's `pnpm install:harness prime` does exactly this).
|
|
402
|
+
2. **Start cli-bridge with the prime backend**: `BRIDGE_BACKENDS=prime
|
|
403
|
+
PRIME_MODELS_JSON=<operator models.json> BRIDGE_PORT=4181 pnpm start`. The
|
|
404
|
+
models.json names an `apiKey` as an exported ENV VAR (not a literal — prime
|
|
405
|
+
resolves env-var-first, so an unforwarded name becomes the literal key), and the
|
|
406
|
+
bridge forwards exactly the names the file names into prime's neutral child env.
|
|
407
|
+
Mind which endpoint a key is funded on: a coding-plan GLM key only works on the
|
|
408
|
+
coding endpoint, and the same key 402s on the general one.
|
|
409
|
+
3. **Give the kernel a persistent interpreter.** Prime's Python kernel bootstraps a
|
|
410
|
+
venv under its HOME, and the bridge gives every run an isolated one — a
|
|
411
|
+
uv-managed interpreter installed inside an ephemeral HOME dies with it. Pre-provision
|
|
412
|
+
a venv (ipykernel + the default rlm extras + an editable
|
|
413
|
+
`prime-agent-runtime`) from a system Python and export
|
|
414
|
+
`PRIME_AGENT_KERNEL_PYTHON=<that venv's python>` to the bridge; prime skips
|
|
415
|
+
auto-bootstrap when the named interpreter already passes its checks.
|
|
416
|
+
4. Run with `TRACES_PRIME_BRIDGE_URL=http://localhost:4181 TRACES_PRIME_MODEL=prime/<provider>/<model>`.
|
|
417
|
+
The opt-in suite (`TRACES_PRIME_REAL_E2E=1` in `tests/analyst-engine-prime.test.ts`)
|
|
418
|
+
exercises the same path from vitest.
|
|
419
|
+
|
|
420
|
+
What real runs have shown (GLM-5.3 via a coding-plan endpoint, fork at the pinned commit):
|
|
421
|
+
|
|
422
|
+
- **Inline, small session:** clean single-call success — one fenced JSON block, five findings,
|
|
423
|
+
all grounded, exit 0.
|
|
424
|
+
- **File, ~121 spans / 788 KB artifact:** the RLM loads and pages the file (14 calls, ~156k input
|
|
425
|
+
tokens); its first reply gets cut by the endpoint's output limit mid-JSON, and the
|
|
426
|
+
truncation-aware repair turn (re-emit at most 3 findings, terse strings) recovers it — ok:true,
|
|
427
|
+
findings grounded, exit 0.
|
|
428
|
+
- **File, ~794 spans / 2.1 MB artifact:** ~2.2M input tokens of genuine REPL inspection, but the
|
|
429
|
+
reply never survives the endpoint's output cap even after repair — the run fails honestly
|
|
430
|
+
(exit 1, raw reply preserved, no findings invented). At that trajectory size, a model with a
|
|
431
|
+
larger output budget — or a paged finalization — is the next step, not silence about it.
|
|
432
|
+
|
|
392
433
|
## Write one analyst
|
|
393
434
|
|
|
394
435
|
An analyst receives a paged trace store and returns typed findings.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/traces",
|
|
3
|
-
"version": "0.17.
|
|
4
|
-
"description": "Point it at your coding-agent session traces (Claude Code, Codex, OpenCode, Gemini, Pi,
|
|
3
|
+
"version": "0.17.1",
|
|
4
|
+
"description": "Point it at your coding-agent session traces (Claude Code, Codex, OpenCode, Gemini, Pi, …) and get failure-mode + efficiency findings. CLI + SDK over the @tangle-network/agent-eval analyst suite — observe live sessions, run your own analysts, redact, and upload to the Tangle Intelligence Platform.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|