@linnlabs/linnkit 0.8.0 → 0.10.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.
Files changed (83) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +1 -1
  3. package/README.zh-CN.md +1 -1
  4. package/bin/linnkit.cjs +7 -0
  5. package/dist/{agentSpec-EkmviZjy.d.cts → agentSpec-Du4Iye0q.d.cts} +16 -1
  6. package/dist/{agentSpec-EkmviZjy.d.ts → agentSpec-Du4Iye0q.d.ts} +16 -1
  7. package/dist/cli.cjs +234 -91
  8. package/dist/cli.cjs.map +1 -1
  9. package/dist/cli.js +234 -91
  10. package/dist/cli.js.map +1 -1
  11. package/dist/context-manager.cjs +230 -32
  12. package/dist/context-manager.cjs.map +1 -1
  13. package/dist/context-manager.d.cts +52 -15
  14. package/dist/context-manager.d.ts +52 -15
  15. package/dist/context-manager.js +230 -33
  16. package/dist/context-manager.js.map +1 -1
  17. package/dist/{context-trace-HE2qY5Q-.d.cts → context-trace-BHKDS-eq.d.cts} +2 -2
  18. package/dist/{context-trace-DRi5M4lX.d.ts → context-trace-CHbqHmyE.d.ts} +2 -2
  19. package/dist/contracts.cjs +3 -1
  20. package/dist/contracts.cjs.map +1 -1
  21. package/dist/contracts.d.cts +3 -3
  22. package/dist/contracts.d.ts +3 -3
  23. package/dist/contracts.js +3 -1
  24. package/dist/contracts.js.map +1 -1
  25. package/dist/{defaultGraphExecutor-BBswR8wn.d.ts → defaultGraphExecutor-B29_qTHy.d.ts} +16 -15
  26. package/dist/{defaultGraphExecutor-BIjJj7WF.d.cts → defaultGraphExecutor-C2E59v_R.d.cts} +16 -15
  27. package/dist/{index-Cm-JbzTH.d.cts → index-BAaUP9yU.d.cts} +38 -15
  28. package/dist/{index-DRBWi1fy.d.ts → index-BaVpVNi2.d.ts} +38 -15
  29. package/dist/{index-DO4dQgf2.d.cts → index-BnYCS8Zg.d.cts} +2 -2
  30. package/dist/{index-CJeWHopy.d.ts → index-C0DAjsdX.d.ts} +2 -2
  31. package/dist/{index-Dl5PLgAv.d.cts → index-CKQzzZ5Y.d.cts} +2 -2
  32. package/dist/{index-CHqwkvGp.d.ts → index-D0mKxTR5.d.ts} +2 -2
  33. package/dist/index.cjs +327 -110
  34. package/dist/index.cjs.map +1 -1
  35. package/dist/index.d.cts +10 -10
  36. package/dist/index.d.ts +10 -10
  37. package/dist/index.js +327 -110
  38. package/dist/index.js.map +1 -1
  39. package/dist/{ports-DnLuKfpE.d.ts → ports-DpPTFhSd.d.ts} +2 -2
  40. package/dist/{ports-DaatKJXp.d.cts → ports-s-tSp3sB.d.cts} +2 -2
  41. package/dist/quickstart.cjs +232 -88
  42. package/dist/quickstart.cjs.map +1 -1
  43. package/dist/quickstart.d.cts +7 -7
  44. package/dist/quickstart.d.ts +7 -7
  45. package/dist/quickstart.js +232 -88
  46. package/dist/quickstart.js.map +1 -1
  47. package/dist/{runAgent-CPj_9e58.d.ts → runAgent-C6F-399C.d.ts} +5 -5
  48. package/dist/{runAgent-HYKlXbVr.d.cts → runAgent-ilEj66Ik.d.cts} +5 -5
  49. package/dist/{runHandle-D3gPsD7B.d.cts → runHandle-BNOqS-Bl.d.cts} +3 -3
  50. package/dist/{runHandle-CyXvzgzk.d.ts → runHandle-BdLXOFqF.d.ts} +3 -3
  51. package/dist/runtime-kernel/events.cjs +1 -0
  52. package/dist/runtime-kernel/events.cjs.map +1 -1
  53. package/dist/runtime-kernel/events.d.cts +4 -4
  54. package/dist/runtime-kernel/events.d.ts +4 -4
  55. package/dist/runtime-kernel/events.js +1 -0
  56. package/dist/runtime-kernel/events.js.map +1 -1
  57. package/dist/runtime-kernel.cjs +318 -103
  58. package/dist/runtime-kernel.cjs.map +1 -1
  59. package/dist/runtime-kernel.d.cts +8 -8
  60. package/dist/runtime-kernel.d.ts +8 -8
  61. package/dist/runtime-kernel.js +315 -104
  62. package/dist/runtime-kernel.js.map +1 -1
  63. package/dist/testkit.cjs +331 -116
  64. package/dist/testkit.cjs.map +1 -1
  65. package/dist/testkit.d.cts +8 -8
  66. package/dist/testkit.d.ts +8 -8
  67. package/dist/testkit.js +331 -116
  68. package/dist/testkit.js.map +1 -1
  69. package/dist/{todo-B1PmDlp3.d.cts → todo-Ca8llpRQ.d.cts} +1 -1
  70. package/dist/{todo-B1PmDlp3.d.ts → todo-Ca8llpRQ.d.ts} +1 -1
  71. package/dist/{toolContracts-CLkQmhTG.d.cts → toolContracts-Bm3EZ1UM.d.cts} +13 -2
  72. package/dist/{toolContracts-Blll0241.d.ts → toolContracts-f8lzZBNa.d.ts} +13 -2
  73. package/docs/integration/README.md +1 -1
  74. package/docs/integration/agent-registration-guide.md +1 -1
  75. package/docs/integration/child-runs.md +4 -1
  76. package/docs/integration/context-engineering.md +30 -15
  77. package/docs/integration/context-fences.md +32 -3
  78. package/docs/integration/llm-provider.md +1 -1
  79. package/docs/integration/persistence.md +1 -0
  80. package/docs/integration/run-supervisor.md +3 -0
  81. package/docs/integration/tool-development-guide.md +7 -5
  82. package/docs/integration/tool-history.md +43 -17
  83. package/package.json +5 -4
@@ -166,6 +166,7 @@ var AgentSpecBudgetPolicy = z.object({
166
166
  });
167
167
  var AgentSpecToolHistoryPolicy = z.object({
168
168
  strategy: z.enum(["per-pair", "per-run", "none"]).optional(),
169
+ retentionMode: z.enum(["drop", "compress"]).optional(),
169
170
  keepLatestToolPairs: z.number().int().nonnegative().optional(),
170
171
  keepLatestRuns: z.number().int().nonnegative().optional(),
171
172
  maxInteractionGroups: z.number().int().nonnegative().optional(),
@@ -280,12 +281,13 @@ var AgentSpecContextPolicy = z.object({
280
281
  var DEFAULT_CONTEXT_POLICY = {
281
282
  profileId: "agent",
282
283
  budget: {
283
- maxTokens: 12e4,
284
+ maxTokens: 232e3,
284
285
  reservedForResponse: 2400,
285
286
  workingMemoryBudgetPercentage: 0.7
286
287
  },
287
288
  toolHistory: {
288
289
  strategy: "per-run",
290
+ retentionMode: "drop",
289
291
  keepLatestToolPairs: 2,
290
292
  keepLatestRuns: 1,
291
293
  maxInteractionGroups: 12,
@@ -1271,6 +1273,17 @@ var logger = new Logger("GraphExecutor");
1271
1273
  function asLocalRecord(local) {
1272
1274
  return local && typeof local === "object" ? { ...local } : {};
1273
1275
  }
1276
+ function readNonEmptyString(value) {
1277
+ return typeof value === "string" && value.trim().length > 0 ? value : void 0;
1278
+ }
1279
+ function readRuntimeConversationId(state) {
1280
+ const local = state?.local && typeof state.local === "object" ? state.local : void 0;
1281
+ return readNonEmptyString(local?.conversationId);
1282
+ }
1283
+ function readRuntimeTurnId(state) {
1284
+ const local = state?.local && typeof state.local === "object" ? state.local : void 0;
1285
+ return readNonEmptyString(local?.turnId);
1286
+ }
1274
1287
  var GraphExecutor = class {
1275
1288
  constructor(checkpointer, config = {}) {
1276
1289
  this.checkpointer = checkpointer;
@@ -1288,8 +1301,8 @@ var GraphExecutor = class {
1288
1301
  registerNode(node) {
1289
1302
  this.nodes.set(node.id, node);
1290
1303
  }
1291
- async peekCheckpoint(conversationId) {
1292
- return await this.checkpointer.load(conversationId);
1304
+ async peekCheckpoint(checkpointKey) {
1305
+ return await this.checkpointer.load(checkpointKey);
1293
1306
  }
1294
1307
  sanitize(state) {
1295
1308
  const local = asLocalRecord(state.local);
@@ -1301,8 +1314,8 @@ var GraphExecutor = class {
1301
1314
  local
1302
1315
  };
1303
1316
  }
1304
- async prime(conversationId, local, nodeId = "user") {
1305
- this.ephemeralLocals.set(conversationId, { ...local || {} });
1317
+ async prime(checkpointKey, local, nodeId = "user") {
1318
+ this.ephemeralLocals.set(checkpointKey, { ...local || {} });
1306
1319
  const localSansMemory = { ...local || {} };
1307
1320
  if ("memory" in localSansMemory) delete localSansMemory.memory;
1308
1321
  const state = {
@@ -1310,10 +1323,10 @@ var GraphExecutor = class {
1310
1323
  schemaVersion: ENGINE_STATE_SCHEMA_VERSION,
1311
1324
  local: localSansMemory
1312
1325
  };
1313
- await this.checkpointer.save(conversationId, state);
1326
+ await this.checkpointer.save(checkpointKey, state);
1314
1327
  }
1315
- async setNode(conversationId, nodeId, localPatch) {
1316
- const current = await this.checkpointer.load(conversationId) || {
1328
+ async setNode(checkpointKey, nodeId, localPatch) {
1329
+ const current = await this.checkpointer.load(checkpointKey) || {
1317
1330
  schemaVersion: ENGINE_STATE_SCHEMA_VERSION,
1318
1331
  local: {}
1319
1332
  };
@@ -1324,19 +1337,46 @@ var GraphExecutor = class {
1324
1337
  schemaVersion: current.schemaVersion ?? ENGINE_STATE_SCHEMA_VERSION,
1325
1338
  local: mergedLocal
1326
1339
  };
1327
- await this.checkpointer.save(conversationId, next);
1340
+ await this.checkpointer.save(checkpointKey, next);
1328
1341
  }
1329
- async runUntilYield(conversationId) {
1342
+ async runUntilYield(checkpointKey) {
1330
1343
  const runId = `run_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
1331
1344
  let lifecyclePhase = "completed";
1345
+ let initialState = null;
1346
+ try {
1347
+ initialState = await this.loadInitialState(checkpointKey);
1348
+ } catch (err) {
1349
+ lifecyclePhase = "failed";
1350
+ this.telemetryPort.emit({
1351
+ kind: "run_lifecycle",
1352
+ runId,
1353
+ phase: "spawned",
1354
+ scope: {}
1355
+ });
1356
+ this.telemetryPort.emit({
1357
+ kind: "run_lifecycle",
1358
+ runId,
1359
+ phase: lifecyclePhase,
1360
+ scope: {}
1361
+ });
1362
+ throw err;
1363
+ }
1364
+ let lifecycleConversationId = readRuntimeConversationId(initialState);
1365
+ let lifecycleTurnId = readRuntimeTurnId(initialState);
1332
1366
  this.telemetryPort.emit({
1333
1367
  kind: "run_lifecycle",
1334
1368
  runId,
1335
1369
  phase: "spawned",
1336
- scope: { conversationId: conversationId || void 0 }
1370
+ scope: {
1371
+ conversationId: lifecycleConversationId,
1372
+ turnId: lifecycleTurnId
1373
+ }
1337
1374
  });
1338
1375
  try {
1339
- return await this.runUntilYieldInternal(conversationId);
1376
+ const result = await this.runUntilYieldInternal(checkpointKey, initialState);
1377
+ lifecycleConversationId = readRuntimeConversationId(result.checkpoint);
1378
+ lifecycleTurnId = readRuntimeTurnId(result.checkpoint);
1379
+ return result;
1340
1380
  } catch (err) {
1341
1381
  lifecyclePhase = err?.name === "AbortError" ? "cancelled" : "failed";
1342
1382
  throw err;
@@ -1345,17 +1385,23 @@ var GraphExecutor = class {
1345
1385
  kind: "run_lifecycle",
1346
1386
  runId,
1347
1387
  phase: lifecyclePhase,
1348
- scope: { conversationId: conversationId || void 0 }
1388
+ scope: {
1389
+ conversationId: lifecycleConversationId,
1390
+ turnId: lifecycleTurnId
1391
+ }
1349
1392
  });
1350
1393
  }
1351
1394
  }
1352
- async runUntilYieldInternal(conversationId) {
1353
- let state = await this.checkpointer.load(conversationId) || {
1395
+ async loadInitialState(checkpointKey) {
1396
+ return await this.checkpointer.load(checkpointKey) || {
1354
1397
  nodeId: "user",
1355
1398
  schemaVersion: ENGINE_STATE_SCHEMA_VERSION,
1356
1399
  local: {}
1357
1400
  };
1358
- const ephemeral = this.ephemeralLocals.get(conversationId) || {};
1401
+ }
1402
+ async runUntilYieldInternal(checkpointKey, initialState) {
1403
+ let state = initialState;
1404
+ const ephemeral = this.ephemeralLocals.get(checkpointKey) || {};
1359
1405
  state = {
1360
1406
  ...state,
1361
1407
  schemaVersion: state.schemaVersion ?? ENGINE_STATE_SCHEMA_VERSION,
@@ -1384,7 +1430,7 @@ var GraphExecutor = class {
1384
1430
  const signalRaw = state.local?.signal;
1385
1431
  if (isAbortSignal2(signalRaw) && signalRaw.aborted) {
1386
1432
  logger.warn("[GraphExecutor] \u6536\u5230 AbortSignal\uFF0C\u7ACB\u5373\u505C\u6B62\u63A8\u7406\u5FAA\u73AF");
1387
- this.ephemeralLocals.delete(conversationId);
1433
+ this.ephemeralLocals.delete(checkpointKey);
1388
1434
  throwAbortError();
1389
1435
  }
1390
1436
  const isLastStep = cycleStepCount >= this.config.maxSteps;
@@ -1432,8 +1478,8 @@ var GraphExecutor = class {
1432
1478
  checkpointCount
1433
1479
  });
1434
1480
  const cp2 = this.sanitize(state);
1435
- await this.checkpointer.save(conversationId, cp2);
1436
- this.ephemeralLocals.delete(conversationId);
1481
+ await this.checkpointer.save(checkpointKey, cp2);
1482
+ this.ephemeralLocals.delete(checkpointKey);
1437
1483
  return { events: allEvents, checkpoint: cp2, stepCount };
1438
1484
  }
1439
1485
  logger.info("[GraphExecutor] \u8282\u70B9\u5207\u6362", {
@@ -1444,18 +1490,18 @@ var GraphExecutor = class {
1444
1490
  });
1445
1491
  const nodeRunStartedAt = Date.now();
1446
1492
  const nodeIdForTelemetry = state.nodeId;
1493
+ const conversationIdForTelemetry = readRuntimeConversationId(state);
1447
1494
  let result;
1448
1495
  try {
1449
1496
  result = await node.run(state);
1450
1497
  } finally {
1451
- const turnIdForTelemetry = typeof state.local?.turnId === "string" ? state.local.turnId : void 0;
1452
1498
  this.telemetryPort.emit({
1453
1499
  kind: "graph_node",
1454
1500
  nodeId: nodeIdForTelemetry,
1455
1501
  durationMs: Date.now() - nodeRunStartedAt,
1456
1502
  scope: {
1457
- conversationId: conversationId || void 0,
1458
- turnId: turnIdForTelemetry
1503
+ conversationId: conversationIdForTelemetry,
1504
+ turnId: readRuntimeTurnId(state)
1459
1505
  }
1460
1506
  });
1461
1507
  }
@@ -1494,7 +1540,7 @@ var GraphExecutor = class {
1494
1540
  });
1495
1541
  state = { ...state, nodeId: nextNodeId };
1496
1542
  const cp2 = this.sanitize(state);
1497
- await this.checkpointer.save(conversationId, cp2);
1543
+ await this.checkpointer.save(checkpointKey, cp2);
1498
1544
  continue;
1499
1545
  }
1500
1546
  if (result.kind === "yield") {
@@ -1505,7 +1551,7 @@ var GraphExecutor = class {
1505
1551
  checkpointCount
1506
1552
  });
1507
1553
  const cp2 = this.sanitize(state);
1508
- await this.checkpointer.save(conversationId, cp2);
1554
+ await this.checkpointer.save(checkpointKey, cp2);
1509
1555
  return { events: allEvents, checkpoint: cp2, stepCount };
1510
1556
  }
1511
1557
  if (result.kind === "pause") {
@@ -1516,7 +1562,7 @@ var GraphExecutor = class {
1516
1562
  checkpointCount
1517
1563
  });
1518
1564
  const cp2 = this.sanitize(state);
1519
- await this.checkpointer.save(conversationId, cp2);
1565
+ await this.checkpointer.save(checkpointKey, cp2);
1520
1566
  return { events: allEvents, checkpoint: cp2, stepCount };
1521
1567
  }
1522
1568
  }
@@ -1527,8 +1573,8 @@ var GraphExecutor = class {
1527
1573
  checkpointCount
1528
1574
  });
1529
1575
  const cp = this.sanitize(state);
1530
- await this.checkpointer.save(conversationId, cp);
1531
- this.ephemeralLocals.delete(conversationId);
1576
+ await this.checkpointer.save(checkpointKey, cp);
1577
+ this.ephemeralLocals.delete(checkpointKey);
1532
1578
  return { events: allEvents, checkpoint: cp, stepCount };
1533
1579
  }
1534
1580
  };
@@ -1691,15 +1737,15 @@ function splitConcatenatedJsonObjects(input) {
1691
1737
  }
1692
1738
 
1693
1739
  // src/runtime-kernel/graph-engine/tick-pipeline/helpers.ts
1694
- function readNonEmptyString(value) {
1740
+ function readNonEmptyString2(value) {
1695
1741
  if (typeof value !== "string") return void 0;
1696
1742
  const trimmed = value.trim();
1697
1743
  return trimmed.length > 0 ? trimmed : void 0;
1698
1744
  }
1699
1745
  function resolveConversationIdForRuntimeEvents(toolContext) {
1700
- const fromCamel = readNonEmptyString(toolContext?.conversationId);
1746
+ const fromCamel = readNonEmptyString2(toolContext?.conversationId);
1701
1747
  if (fromCamel) return fromCamel;
1702
- const fromSnake = toolContext ? readNonEmptyString(toolContext["conversation_id"]) : void 0;
1748
+ const fromSnake = toolContext ? readNonEmptyString2(toolContext["conversation_id"]) : void 0;
1703
1749
  if (fromSnake) return fromSnake;
1704
1750
  return generateMessageId();
1705
1751
  }
@@ -2056,7 +2102,7 @@ var runModelLockMiddleware = async (ctx, stage, next) => {
2056
2102
  if (stage.id !== "execute_llm") {
2057
2103
  return;
2058
2104
  }
2059
- const normalized = readNonEmptyString(ctx.cloudQuotaFallbackAppliedModelId);
2105
+ const normalized = readNonEmptyString2(ctx.cloudQuotaFallbackAppliedModelId);
2060
2106
  if (!normalized) {
2061
2107
  return;
2062
2108
  }
@@ -2644,7 +2690,7 @@ function createExecuteLlmStage(dependencies) {
2644
2690
  streamEventHandler,
2645
2691
  ctx.signal,
2646
2692
  (fallbackModelId) => {
2647
- ctx.cloudQuotaFallbackAppliedModelId = readNonEmptyString(fallbackModelId);
2693
+ ctx.cloudQuotaFallbackAppliedModelId = readNonEmptyString2(fallbackModelId);
2648
2694
  },
2649
2695
  (info) => {
2650
2696
  ctx.modelFallbackAudit = info;
@@ -2688,7 +2734,7 @@ function createPrepareCallStage(dependencies) {
2688
2734
  return {
2689
2735
  id: "prepare_call",
2690
2736
  async run(ctx) {
2691
- const lockedRunModelId = readNonEmptyString(ctx.executorLocal?.runLockedModelId);
2737
+ const lockedRunModelId = readNonEmptyString2(ctx.executorLocal?.runLockedModelId);
2692
2738
  const requestedModelId = lockedRunModelId ?? ctx.request.model_id;
2693
2739
  ctx.modelId = dependencies.modelResolver.resolveModelId(requestedModelId);
2694
2740
  await emitAuditEnvelope(ctx.audit, {
@@ -2760,7 +2806,7 @@ var GraphAgentExecutor = class {
2760
2806
  this.llmCaller = dependencies.llmCaller;
2761
2807
  this.toolRuntime = dependencies.toolRuntime;
2762
2808
  this.contextBuilder = dependencies.contextBuilder;
2763
- this.cloudQuotaFallbackModelId = readNonEmptyString(dependencies.cloudQuotaFallbackModelId);
2809
+ this.cloudQuotaFallbackModelId = readNonEmptyString2(dependencies.cloudQuotaFallbackModelId);
2764
2810
  this.modelCatalog = dependencies.modelCatalog ?? createEmptyModelCatalog();
2765
2811
  this.modelResolver = dependencies.modelResolver ?? new ModelResolver({
2766
2812
  modelCatalog: this.modelCatalog
@@ -2808,7 +2854,7 @@ var GraphAgentExecutor = class {
2808
2854
  llmMessages: [],
2809
2855
  mode: input.request.mode === "chat" ? "chat" : "agent",
2810
2856
  conversationId: resolveConversationIdForRuntimeEvents(input.toolContext),
2811
- turnId: readNonEmptyString(input.toolContext?.turnId) ?? `turn_${Date.now()}`,
2857
+ turnId: readNonEmptyString2(input.toolContext?.turnId) ?? `turn_${Date.now()}`,
2812
2858
  telemetry: this.telemetryPort,
2813
2859
  audit: this.auditPort
2814
2860
  };
@@ -2858,18 +2904,18 @@ function isSummarizationCallbacks(value) {
2858
2904
  function isGraphSseSink(value) {
2859
2905
  return typeof value === "function";
2860
2906
  }
2861
- function readNonEmptyString2(value) {
2907
+ function readNonEmptyString3(value) {
2862
2908
  if (typeof value !== "string") return void 0;
2863
2909
  const trimmed = value.trim();
2864
2910
  return trimmed.length > 0 ? trimmed : void 0;
2865
2911
  }
2866
2912
  function readGraphAgentLocal(local) {
2867
2913
  const source = local ?? {};
2868
- const answerId = readNonEmptyString2(source.answerId);
2914
+ const answerId = readNonEmptyString3(source.answerId);
2869
2915
  const chunkSeq = Number.isInteger(source.chunkSeq) ? Number(source.chunkSeq) : 0;
2870
2916
  return {
2871
- conversationId: readNonEmptyString2(source.conversationId) ?? "",
2872
- turnId: readNonEmptyString2(source.turnId),
2917
+ conversationId: readNonEmptyString3(source.conversationId) ?? "",
2918
+ turnId: readNonEmptyString3(source.turnId),
2873
2919
  request: isAgentInvocationRequest(source.request) ? source.request : void 0,
2874
2920
  toolContext: isToolExecutionContext(source.toolContext) ? source.toolContext : void 0,
2875
2921
  history: isRuntimeEventArray(source.history) ? source.history : [],
@@ -3634,7 +3680,7 @@ var LlmNode = class {
3634
3680
  function isRecord8(value) {
3635
3681
  return typeof value === "object" && value !== null && !Array.isArray(value);
3636
3682
  }
3637
- function readNonEmptyString3(value) {
3683
+ function readNonEmptyString4(value) {
3638
3684
  if (typeof value !== "string") {
3639
3685
  return void 0;
3640
3686
  }
@@ -3656,7 +3702,7 @@ function resolveFinalAnswerFromToolResult(toolName, parsedResult) {
3656
3702
  return void 0;
3657
3703
  }
3658
3704
  if (toolName === "write_report") {
3659
- const report = readNonEmptyString3(data.report);
3705
+ const report = readNonEmptyString4(data.report);
3660
3706
  if (!report) {
3661
3707
  throw new Error("[write_report] \u5DE5\u5177\u8F93\u51FA\u7F3A\u5C11 data.report\uFF08\u5FC5\u987B\u63D0\u4F9B\u5B8C\u6574\u62A5\u544A\u6B63\u6587\uFF09");
3662
3708
  }
@@ -3666,7 +3712,7 @@ function resolveFinalAnswerFromToolResult(toolName, parsedResult) {
3666
3712
  if (data.success !== true) {
3667
3713
  return void 0;
3668
3714
  }
3669
- const finalAnswer = readNonEmptyString3(data.final_answer);
3715
+ const finalAnswer = readNonEmptyString4(data.final_answer);
3670
3716
  if (!finalAnswer) {
3671
3717
  throw new Error("[research_run_writer] \u5DE5\u5177\u8F93\u51FA\u7F3A\u5C11 data.final_answer\uFF08success=true \u65F6\u5FC5\u987B\u63D0\u4F9B\u6700\u7EC8\u62A5\u544A\u6B63\u6587\uFF09");
3672
3718
  }
@@ -4562,6 +4608,22 @@ var ToolNode = class {
4562
4608
  });
4563
4609
  }
4564
4610
  async run(state) {
4611
+ const events = [];
4612
+ while (true) {
4613
+ const result = await this.runNextPendingToolCall(state);
4614
+ if (Array.isArray(result.events) && result.events.length > 0) {
4615
+ events.push(...result.events);
4616
+ }
4617
+ if (result.kind === "route" && result.nextNodeId === "tool") {
4618
+ continue;
4619
+ }
4620
+ return {
4621
+ ...result,
4622
+ events
4623
+ };
4624
+ }
4625
+ }
4626
+ async runNextPendingToolCall(state) {
4565
4627
  const calls = state.local?.pendingToolCalls ?? [];
4566
4628
  const signalRaw = state.local?.signal;
4567
4629
  if (isAbortSignal(signalRaw) && signalRaw.aborted) {
@@ -4769,18 +4831,25 @@ var ToolNode = class {
4769
4831
  rawArguments: context.call.function?.arguments,
4770
4832
  parsedArguments: context.toolArgs
4771
4833
  });
4834
+ const remainingCalls = context.calls.slice(1);
4772
4835
  context.state.local = buildErrorLocalState({
4773
4836
  local: context.local,
4774
- remainingCalls: context.calls.slice(1),
4837
+ remainingCalls,
4775
4838
  conversationId: context.conversationId,
4776
4839
  turnId: context.turnId,
4777
4840
  runtimeEvents: context.bridge.getRuntimeEvents(),
4778
4841
  nextProtocolErrorCount: fuse.nextCount
4779
4842
  });
4780
- if (fuse.shouldFuse) {
4843
+ if (fuse.shouldFuse && remainingCalls.length === 0) {
4781
4844
  throw createToolProtocolFuseError(fuse.nextCount, context.exec.error);
4782
4845
  }
4783
- return { kind: "route", nextNodeId: "llm", events: context.bridge.getRuntimeEvents() };
4846
+ return {
4847
+ kind: "route",
4848
+ // 同一个 assistant.tool_calls batch 必须为每个 call 产出 tool_output。
4849
+ // 出错时也继续消费剩余 call,ToolNode.run 会在本节点内 drain 完 batch 再回 LLM。
4850
+ nextNodeId: remainingCalls.length > 0 ? "tool" : "llm",
4851
+ events: context.bridge.getRuntimeEvents()
4852
+ };
4784
4853
  }
4785
4854
  };
4786
4855
 
@@ -4977,12 +5046,12 @@ function asRecord(value) {
4977
5046
  }
4978
5047
  return value;
4979
5048
  }
4980
- function summarizeCheckpoint(conversationId, state, savedAt) {
5049
+ function summarizeCheckpoint(checkpointKey, state, savedAt) {
4981
5050
  const local = asRecord(state.local);
4982
5051
  const executorLocal = asRecord(local?.executorLocal);
4983
5052
  const pendingToolCalls = local?.pendingToolCalls;
4984
5053
  return {
4985
- conversationId,
5054
+ checkpointKey,
4986
5055
  schemaVersion: state.schemaVersion ?? 1,
4987
5056
  savedAt,
4988
5057
  currentNode: state.nodeId,
@@ -5001,25 +5070,25 @@ var MemoryCheckpointer = class {
5001
5070
  local: state.local && typeof state.local === "object" && !Array.isArray(state.local) ? { ...state.local } : state.local
5002
5071
  };
5003
5072
  }
5004
- async load(conversationId) {
5005
- const entry = this.store.get(conversationId);
5073
+ async load(checkpointKey) {
5074
+ const entry = this.store.get(checkpointKey);
5006
5075
  return entry ? this.cloneState(entry.state) : null;
5007
5076
  }
5008
- async save(conversationId, state) {
5009
- this.store.set(conversationId, {
5077
+ async save(checkpointKey, state) {
5078
+ this.store.set(checkpointKey, {
5010
5079
  state: this.cloneState(state),
5011
5080
  savedAt: Date.now()
5012
5081
  });
5013
5082
  }
5014
- async clear(conversationId) {
5015
- this.store.delete(conversationId);
5083
+ async clear(checkpointKey) {
5084
+ this.store.delete(checkpointKey);
5016
5085
  }
5017
- async peekMeta(conversationId) {
5018
- const entry = this.store.get(conversationId);
5019
- return entry ? summarizeCheckpoint(conversationId, entry.state, entry.savedAt) : null;
5086
+ async peekMeta(checkpointKey) {
5087
+ const entry = this.store.get(checkpointKey);
5088
+ return entry ? summarizeCheckpoint(checkpointKey, entry.state, entry.savedAt) : null;
5020
5089
  }
5021
5090
  async list(filter = {}) {
5022
- const summaries = Array.from(this.store.entries()).map(([conversationId, entry]) => summarizeCheckpoint(conversationId, entry.state, entry.savedAt)).filter((summary) => filter.savedAfter === void 0 ? true : summary.savedAt > filter.savedAfter).sort((left, right) => right.savedAt - left.savedAt);
5091
+ const summaries = Array.from(this.store.entries()).map(([checkpointKey, entry]) => summarizeCheckpoint(checkpointKey, entry.state, entry.savedAt)).filter((summary) => filter.savedAfter === void 0 ? true : summary.savedAt > filter.savedAfter).sort((left, right) => right.savedAt - left.savedAt);
5023
5092
  if (filter.limit === void 0) {
5024
5093
  return summaries;
5025
5094
  }
@@ -5276,18 +5345,18 @@ function createClassification(category, reason, suggestedDelay, extras) {
5276
5345
  }
5277
5346
  var ErrorClassifier = class {
5278
5347
  static classify(error, context) {
5279
- const isRecord21 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
5348
+ const isRecord22 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
5280
5349
  const baseMsg = (error.message || "").toLowerCase();
5281
5350
  const causeMsg = (() => {
5282
5351
  const cause = error.cause;
5283
5352
  if (typeof cause === "string") return cause.toLowerCase();
5284
5353
  if (cause instanceof Error) return (cause.message || "").toLowerCase();
5285
- if (isRecord21(cause) && typeof cause["message"] === "string") return String(cause["message"]).toLowerCase();
5354
+ if (isRecord22(cause) && typeof cause["message"] === "string") return String(cause["message"]).toLowerCase();
5286
5355
  return "";
5287
5356
  })();
5288
5357
  const causeCode = (() => {
5289
5358
  const cause = error.cause;
5290
- if (isRecord21(cause) && typeof cause["code"] === "string") return String(cause["code"]);
5359
+ if (isRecord22(cause) && typeof cause["code"] === "string") return String(cause["code"]);
5291
5360
  return "";
5292
5361
  })();
5293
5362
  const errorMessage = `${baseMsg} ${causeMsg}`.trim();
@@ -5963,6 +6032,67 @@ function assertToolCallsHaveValidJsonArguments(toolCalls) {
5963
6032
  }
5964
6033
  }
5965
6034
 
6035
+ // src/runtime-kernel/llm/reasoning-details.ts
6036
+ var mergeableTextFields = ["reasoning_content"];
6037
+ function isRecord18(value) {
6038
+ return !!value && typeof value === "object" && !Array.isArray(value);
6039
+ }
6040
+ function findMergeableTextField(detail) {
6041
+ if (!isRecord18(detail)) return void 0;
6042
+ if (typeof detail.provider !== "string") return void 0;
6043
+ if (typeof detail.type !== "string") return void 0;
6044
+ for (const field of mergeableTextFields) {
6045
+ if (typeof detail[field] === "string") {
6046
+ return field;
6047
+ }
6048
+ }
6049
+ return void 0;
6050
+ }
6051
+ function hasOnlyStableTextDetailFields(detail, textField) {
6052
+ const allowedKeys = /* @__PURE__ */ new Set(["provider", "type", textField]);
6053
+ return Object.keys(detail).every((key) => allowedKeys.has(key));
6054
+ }
6055
+ function canMergeTextDetails(previous, incoming) {
6056
+ if (!isRecord18(previous) || !isRecord18(incoming)) return false;
6057
+ const previousField = findMergeableTextField(previous);
6058
+ const incomingField = findMergeableTextField(incoming);
6059
+ if (!previousField || previousField !== incomingField) return false;
6060
+ return previous.provider === incoming.provider && previous.type === incoming.type && hasOnlyStableTextDetailFields(previous, previousField) && hasOnlyStableTextDetailFields(incoming, incomingField);
6061
+ }
6062
+ function mergeStreamingText(previous, incoming) {
6063
+ if (!previous) return incoming;
6064
+ if (!incoming) return previous;
6065
+ if (incoming.startsWith(previous)) {
6066
+ return incoming;
6067
+ }
6068
+ if (previous.endsWith(incoming)) {
6069
+ return previous;
6070
+ }
6071
+ return `${previous}${incoming}`;
6072
+ }
6073
+ function appendStreamingProviderReasoningDetails(existing, incoming) {
6074
+ const next = [...existing];
6075
+ for (const detail of incoming) {
6076
+ const previous = next[next.length - 1];
6077
+ if (canMergeTextDetails(previous, detail)) {
6078
+ const textField = findMergeableTextField(previous);
6079
+ if (textField && isRecord18(detail)) {
6080
+ const mergedText = mergeStreamingText(String(previous[textField]), String(detail[textField]));
6081
+ if (mergedText === previous[textField]) {
6082
+ continue;
6083
+ }
6084
+ next[next.length - 1] = {
6085
+ ...previous,
6086
+ [textField]: mergedText
6087
+ };
6088
+ continue;
6089
+ }
6090
+ }
6091
+ next.push(detail);
6092
+ }
6093
+ return next;
6094
+ }
6095
+
5966
6096
  // src/runtime-kernel/llm/streaming-adapter.ts
5967
6097
  async function callLlmStream(params) {
5968
6098
  const {
@@ -5975,7 +6105,7 @@ async function callLlmStream(params) {
5975
6105
  } = params;
5976
6106
  let fullResponse = "";
5977
6107
  let streamError = null;
5978
- const reasoningDetails = [];
6108
+ let reasoningDetails = [];
5979
6109
  const streamAnswerId = generateMessageId();
5980
6110
  let streamChunkSeq = 0;
5981
6111
  let capturedUsage = void 0;
@@ -6045,13 +6175,21 @@ async function callLlmStream(params) {
6045
6175
  const reasoning = isRecord17(chunk) ? chunk["reasoning_details"] : void 0;
6046
6176
  if (reasoning !== void 0) {
6047
6177
  const newReasoningDetails = Array.isArray(reasoning) ? reasoning : [reasoning];
6048
- reasoningDetails.push(...newReasoningDetails);
6049
- eventHandler({
6050
- type: "provider_sidecar",
6051
- id: generateMessageId(),
6052
- timestamp: Date.now(),
6053
- reasoning_details: newReasoningDetails
6054
- });
6178
+ const previousReasoningDetails = reasoningDetails;
6179
+ const previousLength = previousReasoningDetails.length;
6180
+ const compactedReasoningDetails = appendStreamingProviderReasoningDetails(reasoningDetails, newReasoningDetails);
6181
+ reasoningDetails = compactedReasoningDetails;
6182
+ const previousLastChanged = previousLength > 0 && compactedReasoningDetails[previousLength - 1] !== previousReasoningDetails[previousLength - 1];
6183
+ const emitFromIndex = previousLastChanged ? previousLength - 1 : previousLength;
6184
+ const emittedReasoningDetails = compactedReasoningDetails.slice(Math.max(0, emitFromIndex));
6185
+ if (emittedReasoningDetails.length > 0) {
6186
+ eventHandler({
6187
+ type: "provider_sidecar",
6188
+ id: generateMessageId(),
6189
+ timestamp: Date.now(),
6190
+ reasoning_details: emittedReasoningDetails
6191
+ });
6192
+ }
6055
6193
  }
6056
6194
  if (chunk.tool_calls) {
6057
6195
  emitThoughtComplete(thoughtSegmenter.onBoundary());
@@ -6490,7 +6628,7 @@ function cloneRunRecord(record) {
6490
6628
  ...record,
6491
6629
  iterationBudget: record.iterationBudget ? { ...record.iterationBudget } : void 0,
6492
6630
  errorIfAny: record.errorIfAny ? { ...record.errorIfAny } : void 0,
6493
- metadata: record.metadata ? { ...record.metadata } : void 0
6631
+ metadata: record.metadata ? structuredClone(record.metadata) : void 0
6494
6632
  };
6495
6633
  }
6496
6634
  function matchesStatus(candidate, filter) {
@@ -6622,7 +6760,7 @@ function runRecordToMeta(record) {
6622
6760
  errorIfAny: record.errorIfAny ? { ...record.errorIfAny } : void 0
6623
6761
  };
6624
6762
  }
6625
- function isRecord18(value) {
6763
+ function isRecord19(value) {
6626
6764
  return typeof value === "object" && value !== null && !Array.isArray(value);
6627
6765
  }
6628
6766
  function readStringField(record, key) {
@@ -6643,7 +6781,7 @@ function getRunIdFromMetadata(event) {
6643
6781
  return snakeCaseRunId;
6644
6782
  }
6645
6783
  const runContext = metadata["run_context"];
6646
- if (isRecord18(runContext)) {
6784
+ if (isRecord19(runContext)) {
6647
6785
  return readStringField(runContext, "runId") ?? readStringField(runContext, "run_id");
6648
6786
  }
6649
6787
  return void 0;
@@ -6861,7 +6999,7 @@ function runMetaFromRecord(record) {
6861
6999
  }
6862
7000
 
6863
7001
  // src/runtime-kernel/run-supervisor/runSupervisor.ts
6864
- function isRecord19(value) {
7002
+ function isRecord20(value) {
6865
7003
  return typeof value === "object" && value !== null && !Array.isArray(value);
6866
7004
  }
6867
7005
  function readStringField2(record, key) {
@@ -6878,7 +7016,7 @@ function readRunIdFromRuntimeEvent(event) {
6878
7016
  return directRunId;
6879
7017
  }
6880
7018
  const runContext = metadata["run_context"];
6881
- if (!isRecord19(runContext)) {
7019
+ if (!isRecord20(runContext)) {
6882
7020
  return void 0;
6883
7021
  }
6884
7022
  return readStringField2(runContext, "runId") ?? readStringField2(runContext, "run_id");
@@ -6890,7 +7028,7 @@ function readAwaitingUserReason(event) {
6890
7028
  if (typeof event.prompt === "string" && event.prompt.trim().length > 0) {
6891
7029
  return event.prompt;
6892
7030
  }
6893
- if (isRecord19(event.form)) {
7031
+ if (isRecord20(event.form)) {
6894
7032
  const prompt = readStringField2(event.form, "prompt");
6895
7033
  if (prompt && prompt.trim().length > 0) {
6896
7034
  return prompt;
@@ -6902,7 +7040,7 @@ function isTerminalStatus(status) {
6902
7040
  return status === "completed" || status === "failed" || status === "cancelled";
6903
7041
  }
6904
7042
  function cloneMetadata(metadata) {
6905
- return metadata ? { ...metadata } : void 0;
7043
+ return metadata ? structuredClone(metadata) : void 0;
6906
7044
  }
6907
7045
  function recordToSnapshot(record) {
6908
7046
  return {
@@ -6989,7 +7127,7 @@ var DefaultRunSupervisor = class {
6989
7127
  startedAt,
6990
7128
  updatedAt: startedAt,
6991
7129
  iterationBudget: spec.iterationBudget ? { ...spec.iterationBudget } : void 0,
6992
- metadata: spec.metadata ? { ...spec.metadata } : void 0
7130
+ metadata: cloneMetadata(spec.metadata)
6993
7131
  };
6994
7132
  await this.registryStore.save(record);
6995
7133
  const handle = new DefaultRunHandle({
@@ -7163,12 +7301,13 @@ var DefaultRunSupervisor = class {
7163
7301
  }
7164
7302
  try {
7165
7303
  await handle.markRunning({ currentNode: "detached" });
7304
+ const registeredRecord = await this.registryStore.load(handle.runId);
7166
7305
  const executorOutcome = await this.executor.execute({
7167
7306
  runId: handle.runId,
7168
- parentRunId: spec.parentRunId,
7169
- conversationId: spec.conversationId,
7170
- agentSpec: spec.agentSpec,
7171
- request: spec.request,
7307
+ parentRunId: handle.parentRunId,
7308
+ conversationId: registeredRecord?.conversationId ?? spec.conversationId,
7309
+ agentSpec: await handle.spec(),
7310
+ request: await handle.request(),
7172
7311
  signal: handle.signal,
7173
7312
  eventBus: spec.eventBus,
7174
7313
  eventStore: spec.eventStore,
@@ -7177,7 +7316,7 @@ var DefaultRunSupervisor = class {
7177
7316
  contextFences: spec.contextFences,
7178
7317
  wakeSource: spec.wakeSource,
7179
7318
  ephemeral: spec.ephemeral,
7180
- metadata: spec.metadata
7319
+ metadata: cloneMetadata(registeredRecord?.metadata ?? spec.metadata)
7181
7320
  });
7182
7321
  const outcome = await this.persistExecutorOutcome(handle, executorOutcome);
7183
7322
  this.notifyTerminalWaiters(outcome);
@@ -7241,10 +7380,14 @@ var DefaultRunSupervisor = class {
7241
7380
  await this.registryStore.save(record);
7242
7381
  }
7243
7382
  const completedAt = executorOutcome?.completedAt ?? this.now();
7383
+ const fallbackMeta = record ? void 0 : await handle.meta();
7244
7384
  const outcome = recordToTerminalOutcome(record ?? {
7245
7385
  runId: handle.runId,
7246
7386
  parentRunId: handle.parentRunId,
7247
- status: executorOutcome?.status ?? "completed"}, completedAt);
7387
+ conversationId: fallbackMeta?.conversationId ?? "",
7388
+ agentSpecId: fallbackMeta?.agentSpecId,
7389
+ status: executorOutcome?.status ?? "completed",
7390
+ startedAt: fallbackMeta?.startedAt ?? completedAt}, completedAt);
7248
7391
  const nextOutcome = {
7249
7392
  ...outcome,
7250
7393
  metadata: {
@@ -7500,7 +7643,7 @@ function createQuickstartTelemetryPort(collector) {
7500
7643
  }
7501
7644
 
7502
7645
  // src/quickstart/runAgent.ts
7503
- function isRecord20(value) {
7646
+ function isRecord21(value) {
7504
7647
  return typeof value === "object" && value !== null && !Array.isArray(value);
7505
7648
  }
7506
7649
  function readString4(value) {
@@ -7514,7 +7657,7 @@ function resolveModelId(agent, options) {
7514
7657
  return modelId;
7515
7658
  }
7516
7659
  function isQuickstartStreamChunkEvent(value) {
7517
- return isRecord20(value) && value.type === "stream_chunk" && typeof value.content === "string";
7660
+ return isRecord21(value) && value.type === "stream_chunk" && typeof value.content === "string";
7518
7661
  }
7519
7662
  function createNoopObservationPreview() {
7520
7663
  return {
@@ -7554,7 +7697,7 @@ function readFinalAnswer(events, checkpointLocal) {
7554
7697
  if (chunks.length > 0) {
7555
7698
  return chunks.join("");
7556
7699
  }
7557
- if (isRecord20(checkpointLocal)) {
7700
+ if (isRecord21(checkpointLocal)) {
7558
7701
  const finalAnswer = checkpointLocal["finalAnswer"];
7559
7702
  if (typeof finalAnswer === "string") {
7560
7703
  return finalAnswer;
@@ -7563,7 +7706,7 @@ function readFinalAnswer(events, checkpointLocal) {
7563
7706
  return "";
7564
7707
  }
7565
7708
  function readContextTrace(checkpointLocal) {
7566
- if (!isRecord20(checkpointLocal)) return void 0;
7709
+ if (!isRecord21(checkpointLocal)) return void 0;
7567
7710
  return checkpointLocal["contextTrace"];
7568
7711
  }
7569
7712
  async function emitRunEvent(event, sink) {
@@ -7572,6 +7715,7 @@ async function emitRunEvent(event, sink) {
7572
7715
  async function runAgent(agent, options) {
7573
7716
  const modelId = resolveModelId(agent, options);
7574
7717
  const conversationId = options.conversationId ?? `conv_${Date.now()}`;
7718
+ const checkpointKey = conversationId;
7575
7719
  const runId = options.runId ?? generateRunId();
7576
7720
  const turnId = runId;
7577
7721
  const costCollector = new QuickstartRunCostCollector();
@@ -7626,7 +7770,7 @@ async function runAgent(agent, options) {
7626
7770
  };
7627
7771
  await handle.markRunning({ currentNode: "user" });
7628
7772
  try {
7629
- await executor.prime(conversationId, {
7773
+ await executor.prime(checkpointKey, {
7630
7774
  conversationId,
7631
7775
  turnId,
7632
7776
  request: {
@@ -7651,7 +7795,7 @@ async function runAgent(agent, options) {
7651
7795
  return void 0;
7652
7796
  }
7653
7797
  });
7654
- const result = await executor.runUntilYield(conversationId);
7798
+ const result = await executor.runUntilYield(checkpointKey);
7655
7799
  runtimeEvents.push(...result.events);
7656
7800
  for (const event of result.events) {
7657
7801
  if (event.type === "final_answer_chunk") continue;