@linnlabs/linnkit 0.8.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/CHANGELOG.md +18 -0
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/cli.cjs +118 -28
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +118 -28
- package/dist/cli.js.map +1 -1
- package/dist/context-manager.cjs +0 -4
- package/dist/context-manager.cjs.map +1 -1
- package/dist/context-manager.js +0 -4
- package/dist/context-manager.js.map +1 -1
- package/dist/{index-Cm-JbzTH.d.cts → index-BanRABEt.d.cts} +14 -3
- package/dist/{index-DRBWi1fy.d.ts → index-Z8NXKNwI.d.ts} +14 -3
- package/dist/index.cjs +146 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +146 -30
- package/dist/index.js.map +1 -1
- package/dist/quickstart.cjs +115 -25
- package/dist/quickstart.cjs.map +1 -1
- package/dist/quickstart.js +115 -25
- package/dist/quickstart.js.map +1 -1
- package/dist/runtime-kernel.cjs +142 -26
- package/dist/runtime-kernel.cjs.map +1 -1
- package/dist/runtime-kernel.d.cts +1 -1
- package/dist/runtime-kernel.d.ts +1 -1
- package/dist/runtime-kernel.js +140 -27
- package/dist/runtime-kernel.js.map +1 -1
- package/dist/testkit.cjs +155 -39
- package/dist/testkit.cjs.map +1 -1
- package/dist/testkit.js +155 -39
- package/dist/testkit.js.map +1 -1
- package/package.json +3 -3
package/dist/cli.js
CHANGED
|
@@ -1081,7 +1081,6 @@ var Logger = class {
|
|
|
1081
1081
|
constructor(moduleName) {
|
|
1082
1082
|
this.moduleName = moduleName;
|
|
1083
1083
|
}
|
|
1084
|
-
moduleName;
|
|
1085
1084
|
debug(message, data) {
|
|
1086
1085
|
this.log(0 /* DEBUG */, "debug", message, data);
|
|
1087
1086
|
}
|
|
@@ -1163,7 +1162,6 @@ var GraphExecutor = class {
|
|
|
1163
1162
|
};
|
|
1164
1163
|
this.telemetryPort = config.telemetryPort ?? noopTelemetry;
|
|
1165
1164
|
}
|
|
1166
|
-
checkpointer;
|
|
1167
1165
|
nodes = /* @__PURE__ */ new Map();
|
|
1168
1166
|
ephemeralLocals = /* @__PURE__ */ new Map();
|
|
1169
1167
|
config;
|
|
@@ -4445,6 +4443,22 @@ var ToolNode = class {
|
|
|
4445
4443
|
});
|
|
4446
4444
|
}
|
|
4447
4445
|
async run(state) {
|
|
4446
|
+
const events = [];
|
|
4447
|
+
while (true) {
|
|
4448
|
+
const result = await this.runNextPendingToolCall(state);
|
|
4449
|
+
if (Array.isArray(result.events) && result.events.length > 0) {
|
|
4450
|
+
events.push(...result.events);
|
|
4451
|
+
}
|
|
4452
|
+
if (result.kind === "route" && result.nextNodeId === "tool") {
|
|
4453
|
+
continue;
|
|
4454
|
+
}
|
|
4455
|
+
return {
|
|
4456
|
+
...result,
|
|
4457
|
+
events
|
|
4458
|
+
};
|
|
4459
|
+
}
|
|
4460
|
+
}
|
|
4461
|
+
async runNextPendingToolCall(state) {
|
|
4448
4462
|
const calls = state.local?.pendingToolCalls ?? [];
|
|
4449
4463
|
const signalRaw = state.local?.signal;
|
|
4450
4464
|
if (isAbortSignal(signalRaw) && signalRaw.aborted) {
|
|
@@ -4652,18 +4666,25 @@ var ToolNode = class {
|
|
|
4652
4666
|
rawArguments: context.call.function?.arguments,
|
|
4653
4667
|
parsedArguments: context.toolArgs
|
|
4654
4668
|
});
|
|
4669
|
+
const remainingCalls = context.calls.slice(1);
|
|
4655
4670
|
context.state.local = buildErrorLocalState({
|
|
4656
4671
|
local: context.local,
|
|
4657
|
-
remainingCalls
|
|
4672
|
+
remainingCalls,
|
|
4658
4673
|
conversationId: context.conversationId,
|
|
4659
4674
|
turnId: context.turnId,
|
|
4660
4675
|
runtimeEvents: context.bridge.getRuntimeEvents(),
|
|
4661
4676
|
nextProtocolErrorCount: fuse.nextCount
|
|
4662
4677
|
});
|
|
4663
|
-
if (fuse.shouldFuse) {
|
|
4678
|
+
if (fuse.shouldFuse && remainingCalls.length === 0) {
|
|
4664
4679
|
throw createToolProtocolFuseError(fuse.nextCount, context.exec.error);
|
|
4665
4680
|
}
|
|
4666
|
-
return {
|
|
4681
|
+
return {
|
|
4682
|
+
kind: "route",
|
|
4683
|
+
// 同一个 assistant.tool_calls batch 必须为每个 call 产出 tool_output。
|
|
4684
|
+
// 出错时也继续消费剩余 call,ToolNode.run 会在本节点内 drain 完 batch 再回 LLM。
|
|
4685
|
+
nextNodeId: remainingCalls.length > 0 ? "tool" : "llm",
|
|
4686
|
+
events: context.bridge.getRuntimeEvents()
|
|
4687
|
+
};
|
|
4667
4688
|
}
|
|
4668
4689
|
};
|
|
4669
4690
|
|
|
@@ -5159,18 +5180,18 @@ function createClassification(category, reason, suggestedDelay, extras) {
|
|
|
5159
5180
|
}
|
|
5160
5181
|
var ErrorClassifier = class {
|
|
5161
5182
|
static classify(error, context) {
|
|
5162
|
-
const
|
|
5183
|
+
const isRecord23 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
|
|
5163
5184
|
const baseMsg = (error.message || "").toLowerCase();
|
|
5164
5185
|
const causeMsg = (() => {
|
|
5165
5186
|
const cause = error.cause;
|
|
5166
5187
|
if (typeof cause === "string") return cause.toLowerCase();
|
|
5167
5188
|
if (cause instanceof Error) return (cause.message || "").toLowerCase();
|
|
5168
|
-
if (
|
|
5189
|
+
if (isRecord23(cause) && typeof cause["message"] === "string") return String(cause["message"]).toLowerCase();
|
|
5169
5190
|
return "";
|
|
5170
5191
|
})();
|
|
5171
5192
|
const causeCode = (() => {
|
|
5172
5193
|
const cause = error.cause;
|
|
5173
|
-
if (
|
|
5194
|
+
if (isRecord23(cause) && typeof cause["code"] === "string") return String(cause["code"]);
|
|
5174
5195
|
return "";
|
|
5175
5196
|
})();
|
|
5176
5197
|
const errorMessage = `${baseMsg} ${causeMsg}`.trim();
|
|
@@ -5846,6 +5867,67 @@ function assertToolCallsHaveValidJsonArguments(toolCalls) {
|
|
|
5846
5867
|
}
|
|
5847
5868
|
}
|
|
5848
5869
|
|
|
5870
|
+
// src/runtime-kernel/llm/reasoning-details.ts
|
|
5871
|
+
var mergeableTextFields = ["reasoning_content"];
|
|
5872
|
+
function isRecord18(value) {
|
|
5873
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
5874
|
+
}
|
|
5875
|
+
function findMergeableTextField(detail) {
|
|
5876
|
+
if (!isRecord18(detail)) return void 0;
|
|
5877
|
+
if (typeof detail.provider !== "string") return void 0;
|
|
5878
|
+
if (typeof detail.type !== "string") return void 0;
|
|
5879
|
+
for (const field of mergeableTextFields) {
|
|
5880
|
+
if (typeof detail[field] === "string") {
|
|
5881
|
+
return field;
|
|
5882
|
+
}
|
|
5883
|
+
}
|
|
5884
|
+
return void 0;
|
|
5885
|
+
}
|
|
5886
|
+
function hasOnlyStableTextDetailFields(detail, textField) {
|
|
5887
|
+
const allowedKeys = /* @__PURE__ */ new Set(["provider", "type", textField]);
|
|
5888
|
+
return Object.keys(detail).every((key) => allowedKeys.has(key));
|
|
5889
|
+
}
|
|
5890
|
+
function canMergeTextDetails(previous, incoming) {
|
|
5891
|
+
if (!isRecord18(previous) || !isRecord18(incoming)) return false;
|
|
5892
|
+
const previousField = findMergeableTextField(previous);
|
|
5893
|
+
const incomingField = findMergeableTextField(incoming);
|
|
5894
|
+
if (!previousField || previousField !== incomingField) return false;
|
|
5895
|
+
return previous.provider === incoming.provider && previous.type === incoming.type && hasOnlyStableTextDetailFields(previous, previousField) && hasOnlyStableTextDetailFields(incoming, incomingField);
|
|
5896
|
+
}
|
|
5897
|
+
function mergeStreamingText(previous, incoming) {
|
|
5898
|
+
if (!previous) return incoming;
|
|
5899
|
+
if (!incoming) return previous;
|
|
5900
|
+
if (incoming.startsWith(previous)) {
|
|
5901
|
+
return incoming;
|
|
5902
|
+
}
|
|
5903
|
+
if (previous.endsWith(incoming)) {
|
|
5904
|
+
return previous;
|
|
5905
|
+
}
|
|
5906
|
+
return `${previous}${incoming}`;
|
|
5907
|
+
}
|
|
5908
|
+
function appendStreamingProviderReasoningDetails(existing, incoming) {
|
|
5909
|
+
const next = [...existing];
|
|
5910
|
+
for (const detail of incoming) {
|
|
5911
|
+
const previous = next[next.length - 1];
|
|
5912
|
+
if (canMergeTextDetails(previous, detail)) {
|
|
5913
|
+
const textField = findMergeableTextField(previous);
|
|
5914
|
+
if (textField && isRecord18(detail)) {
|
|
5915
|
+
const mergedText = mergeStreamingText(String(previous[textField]), String(detail[textField]));
|
|
5916
|
+
if (mergedText === previous[textField]) {
|
|
5917
|
+
continue;
|
|
5918
|
+
}
|
|
5919
|
+
next[next.length - 1] = {
|
|
5920
|
+
...previous,
|
|
5921
|
+
[textField]: mergedText
|
|
5922
|
+
};
|
|
5923
|
+
continue;
|
|
5924
|
+
}
|
|
5925
|
+
}
|
|
5926
|
+
next.push(detail);
|
|
5927
|
+
}
|
|
5928
|
+
return next;
|
|
5929
|
+
}
|
|
5930
|
+
|
|
5849
5931
|
// src/runtime-kernel/llm/streaming-adapter.ts
|
|
5850
5932
|
async function callLlmStream(params) {
|
|
5851
5933
|
const {
|
|
@@ -5858,7 +5940,7 @@ async function callLlmStream(params) {
|
|
|
5858
5940
|
} = params;
|
|
5859
5941
|
let fullResponse = "";
|
|
5860
5942
|
let streamError = null;
|
|
5861
|
-
|
|
5943
|
+
let reasoningDetails = [];
|
|
5862
5944
|
const streamAnswerId = generateMessageId();
|
|
5863
5945
|
let streamChunkSeq = 0;
|
|
5864
5946
|
let capturedUsage = void 0;
|
|
@@ -5928,13 +6010,21 @@ async function callLlmStream(params) {
|
|
|
5928
6010
|
const reasoning = isRecord17(chunk) ? chunk["reasoning_details"] : void 0;
|
|
5929
6011
|
if (reasoning !== void 0) {
|
|
5930
6012
|
const newReasoningDetails = Array.isArray(reasoning) ? reasoning : [reasoning];
|
|
5931
|
-
reasoningDetails
|
|
5932
|
-
|
|
5933
|
-
|
|
5934
|
-
|
|
5935
|
-
|
|
5936
|
-
|
|
5937
|
-
|
|
6013
|
+
const previousReasoningDetails = reasoningDetails;
|
|
6014
|
+
const previousLength = previousReasoningDetails.length;
|
|
6015
|
+
const compactedReasoningDetails = appendStreamingProviderReasoningDetails(reasoningDetails, newReasoningDetails);
|
|
6016
|
+
reasoningDetails = compactedReasoningDetails;
|
|
6017
|
+
const previousLastChanged = previousLength > 0 && compactedReasoningDetails[previousLength - 1] !== previousReasoningDetails[previousLength - 1];
|
|
6018
|
+
const emitFromIndex = previousLastChanged ? previousLength - 1 : previousLength;
|
|
6019
|
+
const emittedReasoningDetails = compactedReasoningDetails.slice(Math.max(0, emitFromIndex));
|
|
6020
|
+
if (emittedReasoningDetails.length > 0) {
|
|
6021
|
+
eventHandler({
|
|
6022
|
+
type: "provider_sidecar",
|
|
6023
|
+
id: generateMessageId(),
|
|
6024
|
+
timestamp: Date.now(),
|
|
6025
|
+
reasoning_details: emittedReasoningDetails
|
|
6026
|
+
});
|
|
6027
|
+
}
|
|
5938
6028
|
}
|
|
5939
6029
|
if (chunk.tool_calls) {
|
|
5940
6030
|
emitThoughtComplete(thoughtSegmenter.onBoundary());
|
|
@@ -6505,7 +6595,7 @@ function runRecordToMeta(record) {
|
|
|
6505
6595
|
errorIfAny: record.errorIfAny ? { ...record.errorIfAny } : void 0
|
|
6506
6596
|
};
|
|
6507
6597
|
}
|
|
6508
|
-
function
|
|
6598
|
+
function isRecord19(value) {
|
|
6509
6599
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6510
6600
|
}
|
|
6511
6601
|
function readStringField(record, key) {
|
|
@@ -6526,7 +6616,7 @@ function getRunIdFromMetadata(event) {
|
|
|
6526
6616
|
return snakeCaseRunId;
|
|
6527
6617
|
}
|
|
6528
6618
|
const runContext = metadata["run_context"];
|
|
6529
|
-
if (
|
|
6619
|
+
if (isRecord19(runContext)) {
|
|
6530
6620
|
return readStringField(runContext, "runId") ?? readStringField(runContext, "run_id");
|
|
6531
6621
|
}
|
|
6532
6622
|
return void 0;
|
|
@@ -6744,7 +6834,7 @@ function runMetaFromRecord(record) {
|
|
|
6744
6834
|
}
|
|
6745
6835
|
|
|
6746
6836
|
// src/runtime-kernel/run-supervisor/runSupervisor.ts
|
|
6747
|
-
function
|
|
6837
|
+
function isRecord20(value) {
|
|
6748
6838
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6749
6839
|
}
|
|
6750
6840
|
function readStringField2(record, key) {
|
|
@@ -6761,7 +6851,7 @@ function readRunIdFromRuntimeEvent(event) {
|
|
|
6761
6851
|
return directRunId;
|
|
6762
6852
|
}
|
|
6763
6853
|
const runContext = metadata["run_context"];
|
|
6764
|
-
if (!
|
|
6854
|
+
if (!isRecord20(runContext)) {
|
|
6765
6855
|
return void 0;
|
|
6766
6856
|
}
|
|
6767
6857
|
return readStringField2(runContext, "runId") ?? readStringField2(runContext, "run_id");
|
|
@@ -6773,7 +6863,7 @@ function readAwaitingUserReason(event) {
|
|
|
6773
6863
|
if (typeof event.prompt === "string" && event.prompt.trim().length > 0) {
|
|
6774
6864
|
return event.prompt;
|
|
6775
6865
|
}
|
|
6776
|
-
if (
|
|
6866
|
+
if (isRecord20(event.form)) {
|
|
6777
6867
|
const prompt = readStringField2(event.form, "prompt");
|
|
6778
6868
|
if (prompt && prompt.trim().length > 0) {
|
|
6779
6869
|
return prompt;
|
|
@@ -7383,7 +7473,7 @@ function createQuickstartTelemetryPort(collector) {
|
|
|
7383
7473
|
}
|
|
7384
7474
|
|
|
7385
7475
|
// src/quickstart/runAgent.ts
|
|
7386
|
-
function
|
|
7476
|
+
function isRecord21(value) {
|
|
7387
7477
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7388
7478
|
}
|
|
7389
7479
|
function readString4(value) {
|
|
@@ -7397,7 +7487,7 @@ function resolveModelId(agent, options) {
|
|
|
7397
7487
|
return modelId;
|
|
7398
7488
|
}
|
|
7399
7489
|
function isQuickstartStreamChunkEvent(value) {
|
|
7400
|
-
return
|
|
7490
|
+
return isRecord21(value) && value.type === "stream_chunk" && typeof value.content === "string";
|
|
7401
7491
|
}
|
|
7402
7492
|
function createNoopObservationPreview() {
|
|
7403
7493
|
return {
|
|
@@ -7437,7 +7527,7 @@ function readFinalAnswer(events, checkpointLocal) {
|
|
|
7437
7527
|
if (chunks.length > 0) {
|
|
7438
7528
|
return chunks.join("");
|
|
7439
7529
|
}
|
|
7440
|
-
if (
|
|
7530
|
+
if (isRecord21(checkpointLocal)) {
|
|
7441
7531
|
const finalAnswer = checkpointLocal["finalAnswer"];
|
|
7442
7532
|
if (typeof finalAnswer === "string") {
|
|
7443
7533
|
return finalAnswer;
|
|
@@ -7446,7 +7536,7 @@ function readFinalAnswer(events, checkpointLocal) {
|
|
|
7446
7536
|
return "";
|
|
7447
7537
|
}
|
|
7448
7538
|
function readContextTrace(checkpointLocal) {
|
|
7449
|
-
if (!
|
|
7539
|
+
if (!isRecord21(checkpointLocal)) return void 0;
|
|
7450
7540
|
return checkpointLocal["contextTrace"];
|
|
7451
7541
|
}
|
|
7452
7542
|
async function emitRunEvent(event, sink) {
|
|
@@ -7570,11 +7660,11 @@ async function runAgent(agent, options) {
|
|
|
7570
7660
|
}
|
|
7571
7661
|
|
|
7572
7662
|
// src/cli/configLoader.ts
|
|
7573
|
-
function
|
|
7663
|
+
function isRecord22(value) {
|
|
7574
7664
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7575
7665
|
}
|
|
7576
7666
|
function readDefaultExport(moduleValue) {
|
|
7577
|
-
if (
|
|
7667
|
+
if (isRecord22(moduleValue) && "default" in moduleValue) {
|
|
7578
7668
|
return moduleValue.default;
|
|
7579
7669
|
}
|
|
7580
7670
|
return moduleValue;
|
|
@@ -7585,7 +7675,7 @@ async function loadConfig(configPath, cwd) {
|
|
|
7585
7675
|
moduleUrl.searchParams.set("t", String(Date.now()));
|
|
7586
7676
|
const loaded = await import(moduleUrl.href);
|
|
7587
7677
|
const config = readDefaultExport(loaded);
|
|
7588
|
-
if (!
|
|
7678
|
+
if (!isRecord22(config)) {
|
|
7589
7679
|
throw new Error(`[linnkit] config must export an object: ${absolutePath}`);
|
|
7590
7680
|
}
|
|
7591
7681
|
return defineConfig(config);
|