@linnlabs/linnkit 0.8.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 (123) hide show
  1. package/CHANGELOG.md +84 -0
  2. package/LICENSE +21 -0
  3. package/README.md +178 -0
  4. package/README.zh-CN.md +182 -0
  5. package/dist/agent-invocation-BHcNfrBV.d.cts +30 -0
  6. package/dist/agent-invocation-BznDaXDs.d.ts +30 -0
  7. package/dist/agentEvents-DEB7Fy_J.d.cts +81 -0
  8. package/dist/agentEvents-DEB7Fy_J.d.ts +81 -0
  9. package/dist/agentSpec-EkmviZjy.d.cts +2621 -0
  10. package/dist/agentSpec-EkmviZjy.d.ts +2621 -0
  11. package/dist/ai-engine.types-BpeU_XQG.d.cts +158 -0
  12. package/dist/ai-engine.types-vZRnQcJa.d.ts +158 -0
  13. package/dist/audit-BaRUGaqv.d.cts +307 -0
  14. package/dist/audit-BaRUGaqv.d.ts +307 -0
  15. package/dist/audit-CtcfART1.d.ts +33 -0
  16. package/dist/audit-LeOrm2hX.d.cts +33 -0
  17. package/dist/checkpointMarker-DAI3wUQu.d.cts +8 -0
  18. package/dist/checkpointMarker-DAI3wUQu.d.ts +8 -0
  19. package/dist/cli.cjs +8028 -0
  20. package/dist/cli.cjs.map +1 -0
  21. package/dist/cli.d.cts +4 -0
  22. package/dist/cli.d.ts +4 -0
  23. package/dist/cli.js +8025 -0
  24. package/dist/cli.js.map +1 -0
  25. package/dist/context-manager.cjs +8704 -0
  26. package/dist/context-manager.cjs.map +1 -0
  27. package/dist/context-manager.d.cts +2190 -0
  28. package/dist/context-manager.d.ts +2190 -0
  29. package/dist/context-manager.js +8650 -0
  30. package/dist/context-manager.js.map +1 -0
  31. package/dist/context-trace-DRi5M4lX.d.ts +239 -0
  32. package/dist/context-trace-HE2qY5Q-.d.cts +239 -0
  33. package/dist/contracts.cjs +1333 -0
  34. package/dist/contracts.cjs.map +1 -0
  35. package/dist/contracts.d.cts +8 -0
  36. package/dist/contracts.d.ts +8 -0
  37. package/dist/contracts.js +1214 -0
  38. package/dist/contracts.js.map +1 -0
  39. package/dist/defaultGraphExecutor-BBswR8wn.d.ts +624 -0
  40. package/dist/defaultGraphExecutor-BIjJj7WF.d.cts +624 -0
  41. package/dist/execution-CAIypb41.d.cts +129 -0
  42. package/dist/execution-CAIypb41.d.ts +129 -0
  43. package/dist/index-CHqwkvGp.d.ts +149 -0
  44. package/dist/index-CJeWHopy.d.ts +584 -0
  45. package/dist/index-Cm-JbzTH.d.cts +1450 -0
  46. package/dist/index-Cvr23YCl.d.cts +23 -0
  47. package/dist/index-DDzuSb0n.d.ts +23 -0
  48. package/dist/index-DO4dQgf2.d.cts +584 -0
  49. package/dist/index-DRBWi1fy.d.ts +1450 -0
  50. package/dist/index-Dl5PLgAv.d.cts +149 -0
  51. package/dist/index.cjs +9577 -0
  52. package/dist/index.cjs.map +1 -0
  53. package/dist/index.d.cts +89 -0
  54. package/dist/index.d.ts +89 -0
  55. package/dist/index.js +9563 -0
  56. package/dist/index.js.map +1 -0
  57. package/dist/messages-XthmnHZ3.d.cts +8007 -0
  58. package/dist/messages-XthmnHZ3.d.ts +8007 -0
  59. package/dist/ports-DaatKJXp.d.cts +90 -0
  60. package/dist/ports-DnLuKfpE.d.ts +90 -0
  61. package/dist/ports.cjs +4 -0
  62. package/dist/ports.cjs.map +1 -0
  63. package/dist/ports.d.cts +7 -0
  64. package/dist/ports.d.ts +7 -0
  65. package/dist/ports.js +3 -0
  66. package/dist/ports.js.map +1 -0
  67. package/dist/quickstart.cjs +7697 -0
  68. package/dist/quickstart.cjs.map +1 -0
  69. package/dist/quickstart.d.cts +24 -0
  70. package/dist/quickstart.d.ts +24 -0
  71. package/dist/quickstart.js +7691 -0
  72. package/dist/quickstart.js.map +1 -0
  73. package/dist/runAgent-CPj_9e58.d.ts +88 -0
  74. package/dist/runAgent-HYKlXbVr.d.cts +88 -0
  75. package/dist/runHandle-CyXvzgzk.d.ts +239 -0
  76. package/dist/runHandle-D3gPsD7B.d.cts +239 -0
  77. package/dist/runtime-kernel/events.cjs +1485 -0
  78. package/dist/runtime-kernel/events.cjs.map +1 -0
  79. package/dist/runtime-kernel/events.d.cts +8 -0
  80. package/dist/runtime-kernel/events.d.ts +8 -0
  81. package/dist/runtime-kernel/events.js +1475 -0
  82. package/dist/runtime-kernel/events.js.map +1 -0
  83. package/dist/runtime-kernel.cjs +8656 -0
  84. package/dist/runtime-kernel.cjs.map +1 -0
  85. package/dist/runtime-kernel.d.cts +19 -0
  86. package/dist/runtime-kernel.d.ts +19 -0
  87. package/dist/runtime-kernel.js +8568 -0
  88. package/dist/runtime-kernel.js.map +1 -0
  89. package/dist/sse-vPyrOPa0.d.cts +1687 -0
  90. package/dist/sse-vPyrOPa0.d.ts +1687 -0
  91. package/dist/testkit.cjs +10613 -0
  92. package/dist/testkit.cjs.map +1 -0
  93. package/dist/testkit.d.cts +284 -0
  94. package/dist/testkit.d.ts +284 -0
  95. package/dist/testkit.js +10593 -0
  96. package/dist/testkit.js.map +1 -0
  97. package/dist/todo-B1PmDlp3.d.cts +2253 -0
  98. package/dist/todo-B1PmDlp3.d.ts +2253 -0
  99. package/dist/tokenizer-DFL4I7-I.d.ts +28 -0
  100. package/dist/tokenizer-DH_JXv-H.d.cts +28 -0
  101. package/dist/toolContracts-Blll0241.d.ts +463 -0
  102. package/dist/toolContracts-CLkQmhTG.d.cts +463 -0
  103. package/docs/README.md +76 -0
  104. package/docs/integration/01-installation.md +94 -0
  105. package/docs/integration/02-quickstart.md +104 -0
  106. package/docs/integration/README.md +223 -0
  107. package/docs/integration/agent-registration-guide.md +330 -0
  108. package/docs/integration/audit.md +64 -0
  109. package/docs/integration/child-runs.md +87 -0
  110. package/docs/integration/constraints-and-pitfalls.md +87 -0
  111. package/docs/integration/context-engineering.md +650 -0
  112. package/docs/integration/context-fences.md +289 -0
  113. package/docs/integration/glossary.md +69 -0
  114. package/docs/integration/llm-provider.md +76 -0
  115. package/docs/integration/persistence.md +44 -0
  116. package/docs/integration/realtime.md +76 -0
  117. package/docs/integration/run-supervisor.md +69 -0
  118. package/docs/integration/telemetry.md +48 -0
  119. package/docs/integration/testing.md +95 -0
  120. package/docs/integration/tool-development-guide.md +362 -0
  121. package/docs/integration/tool-history.md +202 -0
  122. package/docs/integration/tools.md +188 -0
  123. package/package.json +115 -0
@@ -0,0 +1,202 @@
1
+ # Tool History · 工具历史压缩策略
2
+
3
+ > **What** · 工具历史压缩策略配置 —— `per-pair` / `per-run` / `none` 三种策略 + `overflowStrategy` 溢出兜底。
4
+ > **When to read** · 上下文里工具调用反复占满 token;想配置工具调用历史的压缩窗口;做长 run 任务的成本控制。
5
+ > **Prerequisites** · [`tools.md`](./tools.md) · [`context-engineering.md` §5](./context-engineering.md)。
6
+ > **Key exports** · `toolHistory` field in `AgentSpec.contextPolicy` from `@linnlabs/linnkit` · `ToolHistoryCompressor` preprocessor 由 framework 内置自动注入。
7
+ > **Related** · [`context-engineering.md`](./context-engineering.md) ⭐ · [`tools.md`](./tools.md) · [`agent-registration-guide.md`](./agent-registration-guide.md) ⭐
8
+
9
+ linnkit 的 agent preprocessor 支持三种工具历史压缩策略,host 可在 `AgentDefinition.config.contextPolicy.toolHistory` 中显式声明。
10
+
11
+ ## 1. 三种策略对比
12
+
13
+ | 策略 | 适用场景 | 行为 | 风险 |
14
+ |------|----------|------|------|
15
+ | `per-pair` | 4K/8K 小上下文模型;需要强力控 token | 全局保留最近 N 组完整工具交互,其余压成自然语言摘要 | 可能跨 run 腰斩同一轮工具链,prompt cache prefix 不稳定 |
16
+ | **`per-run`**(默认推荐)| 多步 agent、review、workspace 操作 | 按 `user_input` 划 run,完整保留最近 K 个历史 run 的工具序列 | token 使用量可能高于 per-pair |
17
+ | `none` | 200K+ 长上下文模型;调试回放;审计敏感链路 | 不做常规压缩,只保留安全阀 | 长历史会明显涨 token |
18
+
19
+ **未传配置时默认走 `per-run` + `keepLatestRuns: 1`**。host 仍应在各自的 `AgentDefinition.config.contextPolicy.toolHistory` 中显式声明策略,避免依赖全局默认。
20
+
21
+ ## 2. 默认值汇总
22
+
23
+ | 字段 | 默认 |
24
+ |------|------|
25
+ | `toolHistory.strategy` | `'per-run'` |
26
+ | `toolHistory.keepLatestRuns` | `1`(保留上一个 run 完整工具序列)|
27
+ | `toolHistory.keepLatestToolPairs` | `2`(仅 `strategy='per-pair'` 时生效)|
28
+ | `toolHistory.maxInteractionGroups` | `12` |
29
+ | `toolHistory.overflowStrategy` | `'keep-latest'` |
30
+ | `toolHistory.maxPairTokens` | `6000` |
31
+ | `toolHistory.maxOutputSummaryTokens` | `1000` |
32
+
33
+ ## 3. 安全阀
34
+
35
+ 所有策略共用:
36
+
37
+ - `maxInteractionGroups`:硬上限,默认 12
38
+ - `overflowStrategy: 'keep-latest'`:超过上限时保留最近工具组,压缩更旧组
39
+ - `overflowStrategy: 'fail-fast'`:超过上限时抛 `ContextProviderError`,`code = 'TOOL_HISTORY_OVERFLOW'`,适合 CI 或生产 invariant
40
+
41
+ ## 4. `AgentSpecContextPolicy.toolHistory` 字段
42
+
43
+ ```ts
44
+ interface AgentSpecContextPolicy {
45
+ profileId: string;
46
+
47
+ budget?: {
48
+ maxTokens?: number;
49
+ reservedForResponse?: number;
50
+ workingMemoryBudgetPercentage?: number;
51
+ };
52
+
53
+ toolHistory?: {
54
+ /**
55
+ * 压缩策略类型(默认 'per-run')
56
+ * - 'per-pair':按工具对个数裁(旧默认;适合 4K/8K 等超紧上下文模型)
57
+ * - 'per-run':按 user_input 划 run 边界,保留最近 K 个 run 完整工具序列(prompt cache 友好;通用默认)
58
+ * - 'none':不压缩(适合 200K+ 长 context 模型;仅靠单 tool_output token cap 兜底)
59
+ *
60
+ * 注:当前 run(最后一条 user_input 之后)所有工具对永不压缩,不受 strategy 影响。
61
+ */
62
+ strategy?: 'per-pair' | 'per-run' | 'none';
63
+
64
+ /** strategy='per-pair' 时:保留最近 N 组完整工具对(默认 2)*/
65
+ keepLatestToolPairs?: number;
66
+
67
+ /** strategy='per-run' 时:保留最近 K 个 run 完整工具序列(默认 1)*/
68
+ keepLatestRuns?: number;
69
+
70
+ /** 工作记忆层最多保留的工具交互组总数硬上限(所有 strategy 共用,默认 12)*/
71
+ maxInteractionGroups?: number;
72
+
73
+ /**
74
+ * 溢出 maxInteractionGroups 时的处置(默认 'keep-latest')
75
+ * - 'keep-latest':按 originalIndex 倒序截,留尾不留头(保证最近行动可见)
76
+ * - 'fail-fast':抛 ContextProviderError,让 host 显式处理
77
+ */
78
+ overflowStrategy?: 'keep-latest' | 'fail-fast';
79
+
80
+ /** 单组 tool pair token 上限(所有 strategy 共用,超过触发截断,默认 6000)*/
81
+ maxPairTokens?: number;
82
+
83
+ /** tool_output 摘要后的 token 上限(所有 strategy 共用,默认 1000)*/
84
+ maxOutputSummaryTokens?: number;
85
+ };
86
+
87
+ summarization?: {
88
+ triggerThreshold?: number;
89
+ budgetPercentage?: number;
90
+ oldestMessagesPercentage?: number;
91
+ agentId?: string;
92
+ failureBehavior?: 'fail-fast' | 'continue-if-within-budget';
93
+ };
94
+ }
95
+ ```
96
+
97
+ ## 5. AgentSpec 装配
98
+
99
+ AgentSpec schema 已落到 `@linnlabs/linnkit/contracts`,host 装配时可用 `contextPolicy.toolHistory` 控制策略:
100
+
101
+ ```ts
102
+ import type { AgentSpec } from '@linnlabs/linnkit/contracts';
103
+
104
+ const myAgentSpec: AgentSpec = {
105
+ id: 'my-research-agent',
106
+ version: '1.0.0',
107
+ capabilities: ['llm', 'tools'],
108
+ tools: [],
109
+ contextPolicy: {
110
+ profileId: 'agent',
111
+ toolHistory: {
112
+ strategy: 'per-run',
113
+ keepLatestRuns: 2, // 高密度子调度 agent 建议 K=2-3
114
+ maxInteractionGroups: 12,
115
+ overflowStrategy: 'keep-latest',
116
+ },
117
+ },
118
+ };
119
+ ```
120
+
121
+ ## 6. 低层 preprocessor 注入
122
+
123
+ 测试或自定义 registry 也可以直接从默认 preprocessor registry 注入:
124
+
125
+ ```ts
126
+ import { createDefaultAgentPreprocessorRegistry } from '@linnlabs/linnkit/context-manager';
127
+
128
+ const registry = createDefaultAgentPreprocessorRegistry({
129
+ toolHistory: {
130
+ strategy: 'per-run',
131
+ keepLatestRuns: 1,
132
+ maxInteractionGroups: 12,
133
+ overflowStrategy: 'keep-latest',
134
+ },
135
+ });
136
+ ```
137
+
138
+ ## 7. 默认值变更的兼容声明
139
+
140
+ `strategy` 默认从历史隐式的 `per-pair`(N=2)→ `'per-run'`(K=1)。对所有 host 来说:
141
+
142
+ - 不会引入新 bug(per-run 是 per-pair 的超集——保留更多消息,不会少留)
143
+ - 平均 history token 数 +20-40%(具体看 history 中工具调用密度)
144
+ - prompt cache 命中率上升
145
+ - host 想保持旧行为:在 AgentSpec 显式设 `toolHistory.strategy: 'per-pair'`
146
+
147
+ ## 8. 每个 agent 显式声明(推荐做法)
148
+
149
+ 每个 agent 在自己的 `index.ts` 显式声明 `contextPolicy`,**不走预设模板、不依赖全局默认**:
150
+
151
+ ```ts
152
+ // app-hosts/your-app/agent-registry/agents/research-agent/index.ts
153
+ import type { AgentDefinition } from '../../types';
154
+ import type { AgentSpecContextPolicy } from '@linnlabs/linnkit/contracts';
155
+
156
+ const RESEARCH_AGENT_CONTEXT_POLICY: AgentSpecContextPolicy = {
157
+ profileId: 'agent',
158
+ toolHistory: {
159
+ strategy: 'per-run',
160
+ keepLatestRuns: 3, // 深度子调度,保留更多上下文
161
+ },
162
+ };
163
+
164
+ export const researchAgent: AgentDefinition = {
165
+ // ...
166
+ config: {
167
+ contextPolicy: RESEARCH_AGENT_CONTEXT_POLICY,
168
+ },
169
+ };
170
+ ```
171
+
172
+ **理由**:
173
+
174
+ - agent 作者最了解自己的预期工具密度 + 配置的模型 ctx + 工具结果体量
175
+ - 全局默认 per-run K=1 是合理兜底,但高密度子调度 agent 应该显式 K=2-3;translation / autocomplete / 内部子 agent 应该显式 N=0 极致省 token
176
+ - 预设模板会增加间接耦合——换模型时不知道哪些预设需要联动改
177
+
178
+ ## 9. 用 ContextTrace 验证策略真的生效
179
+
180
+ `toolHistory` 最终会影响 preprocessor 与 working-memory provider 的消息选择。调试时建议临时打开:
181
+
182
+ ```ts
183
+ contextPolicy: {
184
+ profileId: 'agent',
185
+ toolHistory: {
186
+ strategy: 'per-run',
187
+ keepLatestRuns: 2,
188
+ },
189
+ contextTrace: {
190
+ enabled: true,
191
+ includeMessageIds: true,
192
+ includeTokenBreakdown: true,
193
+ maxTraceEvents: 200,
194
+ },
195
+ }
196
+ ```
197
+
198
+ 然后检查 `ContextBuildResult.contextTrace.events`:
199
+
200
+ - `provider` 事件能看到 working-memory 阶段保留消息数与 token delta。
201
+ - `message-decision` 事件能看到每条 `tool_calls` / `tool_output` 的 keep/drop 决策。
202
+ - 如果 trace `overflowed=true`,说明这次上下文太大,先提高 `maxTraceEvents` 做一次诊断;不要长期把它开太大。
@@ -0,0 +1,188 @@
1
+ # Tools · 注册工具集
2
+
3
+ > **What** · 把工具注册进 linnkit —— `ToolRuntimePort` 接入面 + `ObservationPreviewPort` 治理超长 observation。
4
+ > **When to read** · 要给 agent 加工具;要治理超长 observation;接 MCP / 远程工具;review 既有工具实现。
5
+ > **Prerequisites** · [`02-quickstart.md`](./02-quickstart.md);写自定义工具前推荐先读 [`tool-development-guide.md`](./tool-development-guide.md) ⭐。
6
+ > **Key exports** · `BaseTool` / `ToolRuntimePort` / `ObservationPreviewPort` / `ToolExecutionContext` from `@linnlabs/linnkit/runtime-kernel`。
7
+ > **Related** · [`tool-development-guide.md`](./tool-development-guide.md) ⭐ · [`tool-history.md`](./tool-history.md) · [`context-engineering.md` §6](./context-engineering.md)
8
+
9
+ ## 1. linnkit 给你的合同
10
+
11
+ - `BaseTool` + `CommonParameterTypes`(来自 `@linnlabs/linnkit/runtime-kernel`):抽象类,要求实现 `name` / `description` / `parameters` / `run(args, context)`。`run` 必须返回 `Promise<string>`(强烈推荐 `JSON.stringify({ data, observation })` 格式)。
12
+ - `ToolExecutionContext` / `ToolSchemaContext`(同上):执行时 / schema 构建时收到的 context 形状。
13
+ - `ToolRuntimePort` / `ToolCatalogPort` / `ToolExecutionPort` / `ToolPresentationPort`(同上):把工具集合装成"runtime 可调用"的合同;host 默认 `ToolManager` 实现要满足这些 port。
14
+ - `ObservationPreviewPort`(同上):工具产出 observation 在 UI 展示前的预览决策点。
15
+ - `TokenizerPort`(来自 `@linnlabs/linnkit/ports`,0.8.0+):host 可选注入的上下文预算 token 估算合同;它不属于工具执行,但会影响工具 observation / tool_calls 在上下文窗口里能保留多少。
16
+ - `ensureToolContextRuntimeCapability`(同上):把 runtime 必需的保留字段补进 host 的 patch,避免手抖漏字段。
17
+
18
+ ## 2. linnkit 自带的 mock primitive
19
+
20
+ - `createToolContextFixture`(来自 `@linnlabs/linnkit/testkit`):最小 `ToolExecutionContext`,已自动通过 `ensureToolContextRuntimeCapability` 补全 runtime 字段。
21
+
22
+ ## 3. 你必须做的
23
+
24
+ 1. 把每个工具定义为 `BaseTool` 的子类(或满足 `AgentTool` 接口的对象);详细规范见 [`tool-development-guide.md`](./tool-development-guide.md)。
25
+ 2. 决定哪些字段走通用 `ToolExecutionContext`,哪些走 host patch;patch 必须经 `ensureToolContextRuntimeCapability` 补齐保留字段。
26
+ 3. 把工具集合装进 host 的 `ToolManager` / `ToolRuntimePort` 实现,让 runtime 在 LLM 决策返回 tool calls 时能 dispatch。
27
+ 4. 实现 `ObservationPreviewPort`,决定超长 observation 的完整副本写到哪里;再在 runtime assembly 里传给 `createDefaultGraphExecutor({ observationPreview })` 或你的自定义 `ToolNode` 装配。
28
+ 5. 把 AgentSpec 与工具集合装配在一起;详细规范见 [`agent-registration-guide.md`](./agent-registration-guide.md)。
29
+
30
+ ## 4. 你不要做的
31
+
32
+ - 不要让工具直接吃 host 的全局单例(数据库、配置中心等都按 patch / context 注入)。
33
+ - 不要把 runtime 保留字段(`__runtime` / `__capabilities`)手工拼进 patch;统一过 `ensureToolContextRuntimeCapability`。
34
+ - 不要从 deep path 抓 helper(凡是没出现在 `@linnlabs/linnkit/runtime-kernel` 公开符号里的,下个 minor 可能就消失)。
35
+
36
+ ## 5. 最小验证
37
+
38
+ - 单测:用 `createToolContextFixture()` 直接测 `tool.execute(args, fixtureContext)`。
39
+ - 集成测:在 host-bound `ToolRuntimeHarness` 上覆盖"失败恢复 / 并行调用 / observation 预览"路径。
40
+
41
+ ## 6. ObservationPreviewPort:配置超长 observation 存储路径
42
+
43
+ `contextPolicy.toolOutput.observationGovernance` 只控制**什么时候治理**:
44
+
45
+ ```ts
46
+ contextPolicy: {
47
+ profileId: 'agent',
48
+ toolOutput: {
49
+ observationGovernance: {
50
+ enabled: true,
51
+ maxChars: 20_000,
52
+ maxLines: 1_200,
53
+ },
54
+ },
55
+ }
56
+ ```
57
+
58
+ 完整内容**存到哪里**由 host 的 `ObservationPreviewPort` 决定。最小形态:
59
+
60
+ ```ts
61
+ import path from 'node:path';
62
+ import { promises as fsp } from 'node:fs';
63
+ import { createHash } from 'node:crypto';
64
+ import type { ObservationPreviewPort } from '@linnlabs/linnkit/runtime-kernel';
65
+
66
+ export function createFsObservationPreviewPort(params: {
67
+ rootDir: string;
68
+ }): ObservationPreviewPort {
69
+ return {
70
+ async truncateObservation({ context, toolName, text, maxChars, maxLines }) {
71
+ const raw = String(text ?? '');
72
+ const lines = raw.split('\n');
73
+ if (raw.length <= maxChars && lines.length <= maxLines) {
74
+ return { truncated: false, preview: raw };
75
+ }
76
+
77
+ const blobId = createHash('sha256')
78
+ .update(JSON.stringify({
79
+ conversationId: context.conversationId,
80
+ turnId: context.turnId,
81
+ toolName,
82
+ textHash: createHash('sha256').update(raw).digest('hex'),
83
+ }))
84
+ .digest('hex')
85
+ .slice(0, 16);
86
+
87
+ const filePath = path.join(params.rootDir, 'tool_output', 'blobs', `${blobId}.json`);
88
+ await fsp.mkdir(path.dirname(filePath), { recursive: true });
89
+ await fsp.writeFile(
90
+ filePath,
91
+ JSON.stringify({
92
+ version: 1,
93
+ kind: 'tool_output_text',
94
+ conversation_id: context.conversationId,
95
+ turn_id: context.turnId,
96
+ tool_name: toolName,
97
+ text: raw,
98
+ }, null, 2),
99
+ 'utf-8',
100
+ );
101
+
102
+ return {
103
+ truncated: true,
104
+ blob_id: blobId,
105
+ preview: [
106
+ raw.slice(0, Math.floor(maxChars * 0.7)),
107
+ '',
108
+ `...(内容已截断,完整内容已写入 blob_id=${blobId})...`,
109
+ ].join('\n'),
110
+ };
111
+ },
112
+ };
113
+ }
114
+ ```
115
+
116
+ 然后在 executor 装配处传入:
117
+
118
+ ```ts
119
+ import { createDefaultGraphExecutor } from '@linnlabs/linnkit/runtime-kernel';
120
+
121
+ const observationPreview = createFsObservationPreviewPort({
122
+ rootDir: process.env.MY_AGENT_TOOL_OUTPUT_DIR ?? '/var/lib/my-agent',
123
+ });
124
+
125
+ const executor = createDefaultGraphExecutor({
126
+ llmNode,
127
+ toolRuntime,
128
+ observationPreview,
129
+ });
130
+ ```
131
+
132
+ **重要边界**:
133
+
134
+ - `blob_id` 只是指针。你如果提供读取工具(例如 `resource_read("tool_output://blobs/<blob_id>")`),读取工具必须和 `ObservationPreviewPort` 使用同一个 store / rootDir。
135
+ - AgentSpec 不应该包含本地路径、S3 bucket、数据库 DSN 这类基础设施字段;这些属于 host 部署配置。
136
+ - 示例 host 可以使用 workspace root 下的 conversation artifact 路径:
137
+ `<workspaceRoot>/Artifacts/v1/conversations/<conversationId>/instances/<instanceId>/tool_output/blobs/<blobId>.json`。
138
+ 其中 `workspaceRoot` 应来自 host 自己的部署配置、环境变量或工作区配置。读取 `tool_output://blobs/<blob_id>` 的工具也必须使用同一个 store,否则模型拿到 `blob_id` 后无法续读。
139
+
140
+ ## 7. 工具最小示例
141
+
142
+ > 完整规范见 [`tool-development-guide.md`](./tool-development-guide.md)。本节只给最小可运行示例。
143
+
144
+ ```ts
145
+ import {
146
+ BaseTool,
147
+ type ToolArgs,
148
+ type ToolExecutionContext,
149
+ type ToolParameterSchema,
150
+ } from '@linnlabs/linnkit/runtime-kernel';
151
+
152
+ interface EchoArgs extends ToolArgs {
153
+ text: string;
154
+ }
155
+
156
+ export class EchoTool extends BaseTool<EchoArgs> {
157
+ readonly name = 'echo';
158
+ readonly description = '回声测试,用于验证工具调用链路';
159
+
160
+ readonly parameters: ToolParameterSchema = {
161
+ type: 'object',
162
+ properties: {
163
+ text: { type: 'string', description: '要回声的文本' },
164
+ },
165
+ required: ['text'],
166
+ };
167
+
168
+ async run(args: EchoArgs, _context: ToolExecutionContext): Promise<string> {
169
+ const result = {
170
+ data: { echoed: args.text },
171
+ observation: `Echoed: ${args.text}`,
172
+ };
173
+ return JSON.stringify(result);
174
+ }
175
+ }
176
+ ```
177
+
178
+ **协议层强制约束**(违反会被运行时拒绝或导致下游解析失败):
179
+
180
+ | 约束 | 协议层守门 |
181
+ |------|-----------|
182
+ | `name` / `description` / `parameters` 必填 | `BaseTool` 抽象类强制 |
183
+ | `run` 返回 `Promise<string>`(不是对象,是字符串)| `BaseTool.run` 签名 |
184
+ | 强烈推荐 `run` 返回 `JSON.stringify({ data, observation })` 格式 | 上下游工具卡片解析依赖 |
185
+ | 必填参数走 `parameters.required[]`,**不要**在 `run` 里手写 if-check | `BaseTool.validateArguments` 自动校验 |
186
+ | 失败必须 `throw`,**不能**返回伪装成功的 JSON | tool 配对不变量 C10 + AuditEnvelope |
187
+
188
+ 详细的设计思想、错误处理规范、超长 observation 治理、交互工具协议等见 [`tool-development-guide.md`](./tool-development-guide.md)。
package/package.json ADDED
@@ -0,0 +1,115 @@
1
+ {
2
+ "name": "@linnlabs/linnkit",
3
+ "version": "0.8.0",
4
+ "type": "module",
5
+ "description": "linnkit · vendor-neutral Agent framework. runtime-kernel + context-manager + ports + testkit + browser-safe events seam.",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/linnlabs/linnkit.git"
10
+ },
11
+ "homepage": "https://github.com/linnlabs/linnkit#readme",
12
+ "bugs": {
13
+ "url": "https://github.com/linnlabs/linnkit/issues"
14
+ },
15
+ "publishConfig": {
16
+ "registry": "https://registry.npmjs.org/",
17
+ "access": "public",
18
+ "provenance": true
19
+ },
20
+ "files": [
21
+ "dist",
22
+ "LICENSE",
23
+ "CHANGELOG.md",
24
+ "README.md",
25
+ "README.zh-CN.md",
26
+ "docs/README.md",
27
+ "docs/integration"
28
+ ],
29
+ "main": "./dist/index.cjs",
30
+ "module": "./dist/index.js",
31
+ "types": "./dist/index.d.ts",
32
+ "bin": {
33
+ "linnkit": "./dist/cli.cjs"
34
+ },
35
+ "exports": {
36
+ ".": {
37
+ "types": "./dist/index.d.ts",
38
+ "import": "./dist/index.js",
39
+ "require": "./dist/index.cjs"
40
+ },
41
+ "./ports": {
42
+ "types": "./dist/ports.d.ts",
43
+ "import": "./dist/ports.js",
44
+ "require": "./dist/ports.cjs"
45
+ },
46
+ "./contracts": {
47
+ "types": "./dist/contracts.d.ts",
48
+ "import": "./dist/contracts.js",
49
+ "require": "./dist/contracts.cjs"
50
+ },
51
+ "./runtime-kernel": {
52
+ "types": "./dist/runtime-kernel.d.ts",
53
+ "import": "./dist/runtime-kernel.js",
54
+ "require": "./dist/runtime-kernel.cjs"
55
+ },
56
+ "./runtime-kernel/events": {
57
+ "types": "./dist/runtime-kernel/events.d.ts",
58
+ "import": "./dist/runtime-kernel/events.js",
59
+ "require": "./dist/runtime-kernel/events.cjs"
60
+ },
61
+ "./context-manager": {
62
+ "types": "./dist/context-manager.d.ts",
63
+ "import": "./dist/context-manager.js",
64
+ "require": "./dist/context-manager.cjs"
65
+ },
66
+ "./testkit": {
67
+ "types": "./dist/testkit.d.ts",
68
+ "import": "./dist/testkit.js",
69
+ "require": "./dist/testkit.cjs"
70
+ },
71
+ "./quickstart": {
72
+ "types": "./dist/quickstart.d.ts",
73
+ "import": "./dist/quickstart.js",
74
+ "require": "./dist/quickstart.cjs"
75
+ },
76
+ "./package.json": "./package.json"
77
+ },
78
+ "scripts": {
79
+ "build": "tsup",
80
+ "build:clean": "rm -rf dist",
81
+ "prepublishOnly": "npm run build:clean && npm run build && npm run test:smoke && npm run test:smoke:dist",
82
+ "publish:npm": "npm publish",
83
+ "publish:dry-run": "npm pack --dry-run",
84
+ "typecheck": "npx tsc --noEmit -p tsconfig.json",
85
+ "test:smoke": "npx vitest run __tests__/package.shell.test.ts",
86
+ "test:smoke:dist": "npx vitest run __tests__/package.runtime-import.test.ts __tests__/package.events-browser-safe.test.ts"
87
+ },
88
+ "dependencies": {
89
+ "tiktoken": "^1.0.22"
90
+ },
91
+ "peerDependencies": {
92
+ "vitest": "^2.0.0 || ^3.0.0",
93
+ "zod": "^3.22.0"
94
+ },
95
+ "peerDependenciesMeta": {
96
+ "vitest": {
97
+ "optional": true
98
+ }
99
+ },
100
+ "devDependencies": {
101
+ "@types/node": "^24.3.0",
102
+ "tsup": "^8.0.0",
103
+ "typescript": "^5.5.4",
104
+ "vitest": "^3.2.4"
105
+ },
106
+ "linnkit": {
107
+ "phase": "released (0.8.0 — TokenizerPort:host 可选注入自定义 tokenizer,默认行为保持 0.7.x)",
108
+ "sourceOfTruth": "CHANGELOG.md",
109
+ "notes": [
110
+ "子入口 ./runtime-kernel/events 是 browser-safe slim seam:仅暴露 events governance 纯函数,禁止扩展任何 Node-only 依赖(如 node:async_hooks / crypto / fs)",
111
+ "前端代码禁止从 ./runtime-kernel(namespace 全展开入口)import,否则会把 crypto / node:async_hooks 等 Node-only 子树拖进 frontend bundle",
112
+ "0.x 版本号策略:任何加 export / 改既有签名都 bump minor(不是 patch),保证消费者不会在 patch 升级中吃到公开 API 变化"
113
+ ]
114
+ }
115
+ }