@renxqoo/renx-code 0.0.4 → 0.0.5

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 (209) hide show
  1. package/bin/renx.cjs +16 -0
  2. package/package.json +2 -45
  3. package/src/agent/runtime/runtime.context-usage.test.ts +4 -5
  4. package/src/agent/runtime/runtime.error-handling.test.ts +4 -5
  5. package/src/agent/runtime/runtime.test.ts +7 -4
  6. package/src/agent/runtime/runtime.ts +3 -9
  7. package/src/agent/runtime/runtime.usage-forwarding.test.ts +4 -5
  8. package/src/agent/runtime/source-modules.test.ts +16 -35
  9. package/src/agent/runtime/source-modules.ts +17 -0
  10. package/vendor/agent-root/src/agent/ENTERPRISE_ACCEPTANCE_CHECKLIST.md +95 -0
  11. package/vendor/agent-root/src/agent/ENTERPRISE_REALTIME.html +1345 -0
  12. package/vendor/agent-root/src/agent/ENTERPRISE_REALTIME.md +1353 -0
  13. package/vendor/agent-root/src/agent/ERROR_CONTRACT.md +60 -0
  14. package/vendor/agent-root/src/agent/TEST_COVERAGE_ANALYSIS.md +278 -0
  15. package/vendor/agent-root/src/agent/__test__/error-contract.test.ts +72 -0
  16. package/vendor/agent-root/src/agent/__test__/types.test.ts +137 -0
  17. package/vendor/agent-root/src/agent/agent/__test__/abort-runtime.test.ts +83 -0
  18. package/vendor/agent-root/src/agent/agent/__test__/callback-safety.test.ts +34 -0
  19. package/vendor/agent-root/src/agent/agent/__test__/compaction.test.ts +323 -0
  20. package/vendor/agent-root/src/agent/agent/__test__/concurrency.test.ts +290 -0
  21. package/vendor/agent-root/src/agent/agent/__test__/error-normalizer.test.ts +377 -0
  22. package/vendor/agent-root/src/agent/agent/__test__/error.test.ts +212 -0
  23. package/vendor/agent-root/src/agent/agent/__test__/fault-injection.test.ts +295 -0
  24. package/vendor/agent-root/src/agent/agent/__test__/index.test.ts +3607 -0
  25. package/vendor/agent-root/src/agent/agent/__test__/logger.test.ts +35 -0
  26. package/vendor/agent-root/src/agent/agent/__test__/message-utils.test.ts +517 -0
  27. package/vendor/agent-root/src/agent/agent/__test__/telemetry.test.ts +97 -0
  28. package/vendor/agent-root/src/agent/agent/__test__/timeout-budget.test.ts +479 -0
  29. package/vendor/agent-root/src/agent/agent/__test__/tool-call-merge.test.ts +80 -0
  30. package/vendor/agent-root/src/agent/agent/__test__/tool-execution-ledger.test.ts +76 -0
  31. package/vendor/agent-root/src/agent/agent/__test__/write-buffer.test.ts +173 -0
  32. package/vendor/agent-root/src/agent/agent/__test__/write-file-session.test.ts +109 -0
  33. package/vendor/agent-root/src/agent/agent/abort-runtime.ts +71 -0
  34. package/vendor/agent-root/src/agent/agent/callback-safety.ts +33 -0
  35. package/vendor/agent-root/src/agent/agent/compaction.ts +291 -0
  36. package/vendor/agent-root/src/agent/agent/concurrency.ts +103 -0
  37. package/vendor/agent-root/src/agent/agent/error-normalizer.ts +190 -0
  38. package/vendor/agent-root/src/agent/agent/error.ts +198 -0
  39. package/vendor/agent-root/src/agent/agent/index.ts +1772 -0
  40. package/vendor/agent-root/src/agent/agent/logger.ts +65 -0
  41. package/vendor/agent-root/src/agent/agent/message-utils.ts +101 -0
  42. package/vendor/agent-root/src/agent/agent/stream-events.ts +61 -0
  43. package/vendor/agent-root/src/agent/agent/telemetry.ts +123 -0
  44. package/vendor/agent-root/src/agent/agent/timeout-budget.ts +227 -0
  45. package/vendor/agent-root/src/agent/agent/tool-call-merge.ts +111 -0
  46. package/vendor/agent-root/src/agent/agent/tool-execution-ledger.ts +164 -0
  47. package/vendor/agent-root/src/agent/agent/write-buffer.ts +188 -0
  48. package/vendor/agent-root/src/agent/agent/write-file-session.ts +238 -0
  49. package/vendor/agent-root/src/agent/app/__test__/agent-app-service.test.ts +1053 -0
  50. package/vendor/agent-root/src/agent/app/__test__/minimal-agent-application.test.ts +158 -0
  51. package/vendor/agent-root/src/agent/app/__test__/sqlite-agent-app-store.test.ts +437 -0
  52. package/vendor/agent-root/src/agent/app/agent-app-service.ts +748 -0
  53. package/vendor/agent-root/src/agent/app/contracts.ts +109 -0
  54. package/vendor/agent-root/src/agent/app/index.ts +5 -0
  55. package/vendor/agent-root/src/agent/app/minimal-agent-application.ts +151 -0
  56. package/vendor/agent-root/src/agent/app/ports.ts +72 -0
  57. package/vendor/agent-root/src/agent/app/sqlite-agent-app-store.ts +1182 -0
  58. package/vendor/agent-root/src/agent/app/sqlite-client.ts +177 -0
  59. package/vendor/agent-root/src/agent/docs/cli-app-layer/00-README.md +36 -0
  60. package/vendor/agent-root/src/agent/docs/cli-app-layer/01-scope-and-goals.md +33 -0
  61. package/vendor/agent-root/src/agent/docs/cli-app-layer/02-architecture-overview.md +40 -0
  62. package/vendor/agent-root/src/agent/docs/cli-app-layer/03-domain-model-and-contracts.md +91 -0
  63. package/vendor/agent-root/src/agent/docs/cli-app-layer/04-ports-and-interfaces.md +116 -0
  64. package/vendor/agent-root/src/agent/docs/cli-app-layer/05-run-orchestration-and-state-machine.md +52 -0
  65. package/vendor/agent-root/src/agent/docs/cli-app-layer/06-cli-commands-and-ux.md +53 -0
  66. package/vendor/agent-root/src/agent/docs/cli-app-layer/07-storage-design-local.md +52 -0
  67. package/vendor/agent-root/src/agent/docs/cli-app-layer/08-error-and-observability.md +40 -0
  68. package/vendor/agent-root/src/agent/docs/cli-app-layer/09-security-and-policy-boundary.md +19 -0
  69. package/vendor/agent-root/src/agent/docs/cli-app-layer/10-test-plan-and-acceptance.md +28 -0
  70. package/vendor/agent-root/src/agent/docs/cli-app-layer/11-implementation-phases.md +26 -0
  71. package/vendor/agent-root/src/agent/docs/cli-app-layer/12-open-questions-and-risks.md +30 -0
  72. package/vendor/agent-root/src/agent/docs/cli-app-layer/13-sqlite-schema-fields-and-rationale.md +567 -0
  73. package/vendor/agent-root/src/agent/docs/cli-app-layer/14-project-flow-mermaid.md +583 -0
  74. package/vendor/agent-root/src/agent/docs/cli-app-layer/15-openclaw-style-project-blueprint.md +972 -0
  75. package/vendor/agent-root/src/agent/error-contract.ts +154 -0
  76. package/vendor/agent-root/src/agent/prompts/system.ts +246 -0
  77. package/vendor/agent-root/src/agent/prompts/system1.ts +208 -0
  78. package/vendor/agent-root/src/agent/storage/__test__/file-history-store.test.ts +98 -0
  79. package/vendor/agent-root/src/agent/storage/file-history-store.ts +313 -0
  80. package/vendor/agent-root/src/agent/storage/file-storage-config.ts +94 -0
  81. package/vendor/agent-root/src/agent/storage/file-system.ts +31 -0
  82. package/vendor/agent-root/src/agent/storage/file-write-service.ts +21 -0
  83. package/vendor/agent-root/src/agent/tool/__test__/base-tool.test.ts +413 -0
  84. package/vendor/agent-root/src/agent/tool/__test__/bash-policy.test.ts +356 -0
  85. package/vendor/agent-root/src/agent/tool/__test__/bash.mocked-coverage.test.ts +375 -0
  86. package/vendor/agent-root/src/agent/tool/__test__/bash.test.ts +372 -0
  87. package/vendor/agent-root/src/agent/tool/__test__/error.test.ts +108 -0
  88. package/vendor/agent-root/src/agent/tool/__test__/file-edit-tool.test.ts +258 -0
  89. package/vendor/agent-root/src/agent/tool/__test__/file-history-tools.test.ts +121 -0
  90. package/vendor/agent-root/src/agent/tool/__test__/file-read-tool.test.ts +210 -0
  91. package/vendor/agent-root/src/agent/tool/__test__/glob.test.ts +139 -0
  92. package/vendor/agent-root/src/agent/tool/__test__/grep.mocked-coverage.test.ts +456 -0
  93. package/vendor/agent-root/src/agent/tool/__test__/grep.test.ts +192 -0
  94. package/vendor/agent-root/src/agent/tool/__test__/lsp.test.ts +300 -0
  95. package/vendor/agent-root/src/agent/tool/__test__/outside-workspace-confirmation.test.ts +214 -0
  96. package/vendor/agent-root/src/agent/tool/__test__/path-security.test.ts +336 -0
  97. package/vendor/agent-root/src/agent/tool/__test__/skill-loader.test.ts +494 -0
  98. package/vendor/agent-root/src/agent/tool/__test__/skill-parser.test.ts +543 -0
  99. package/vendor/agent-root/src/agent/tool/__test__/skill-tool.test.ts +172 -0
  100. package/vendor/agent-root/src/agent/tool/__test__/task-concurrency-and-version.test.ts +116 -0
  101. package/vendor/agent-root/src/agent/tool/__test__/task-create-get-list-update.test.ts +267 -0
  102. package/vendor/agent-root/src/agent/tool/__test__/task-create.test.ts +519 -0
  103. package/vendor/agent-root/src/agent/tool/__test__/task-errors.test.ts +225 -0
  104. package/vendor/agent-root/src/agent/tool/__test__/task-output-blocking.test.ts +223 -0
  105. package/vendor/agent-root/src/agent/tool/__test__/task-output.test.ts +184 -0
  106. package/vendor/agent-root/src/agent/tool/__test__/task-parent-abort.test.ts +287 -0
  107. package/vendor/agent-root/src/agent/tool/__test__/task-real-runner-adapter.test.ts +190 -0
  108. package/vendor/agent-root/src/agent/tool/__test__/task-run-lifecycle.test.ts +352 -0
  109. package/vendor/agent-root/src/agent/tool/__test__/task-store-runner-branches.test.ts +395 -0
  110. package/vendor/agent-root/src/agent/tool/__test__/task-store.test.ts +391 -0
  111. package/vendor/agent-root/src/agent/tool/__test__/task-subagent-config-integration.test.ts +176 -0
  112. package/vendor/agent-root/src/agent/tool/__test__/task-subagent-config.test.ts +68 -0
  113. package/vendor/agent-root/src/agent/tool/__test__/task-tools-core-edges.test.ts +630 -0
  114. package/vendor/agent-root/src/agent/tool/__test__/task-tools-runtime-edges.test.ts +732 -0
  115. package/vendor/agent-root/src/agent/tool/__test__/task-types.test.ts +494 -0
  116. package/vendor/agent-root/src/agent/tool/__test__/task-utils-branches.test.ts +175 -0
  117. package/vendor/agent-root/src/agent/tool/__test__/tool-manager.test.ts +505 -0
  118. package/vendor/agent-root/src/agent/tool/__test__/types.test.ts +55 -0
  119. package/vendor/agent-root/src/agent/tool/__test__/web-fetch.test.ts +244 -0
  120. package/vendor/agent-root/src/agent/tool/__test__/web-search.test.ts +290 -0
  121. package/vendor/agent-root/src/agent/tool/__test__/write-file.test.ts +368 -0
  122. package/vendor/agent-root/src/agent/tool/base-tool.ts +345 -0
  123. package/vendor/agent-root/src/agent/tool/bash-policy.ts +636 -0
  124. package/vendor/agent-root/src/agent/tool/bash.ts +688 -0
  125. package/vendor/agent-root/src/agent/tool/error.ts +131 -0
  126. package/vendor/agent-root/src/agent/tool/file-edit-tool.ts +264 -0
  127. package/vendor/agent-root/src/agent/tool/file-history-list.ts +103 -0
  128. package/vendor/agent-root/src/agent/tool/file-history-restore.ts +149 -0
  129. package/vendor/agent-root/src/agent/tool/file-read-tool.ts +211 -0
  130. package/vendor/agent-root/src/agent/tool/glob.ts +171 -0
  131. package/vendor/agent-root/src/agent/tool/grep.ts +496 -0
  132. package/vendor/agent-root/src/agent/tool/lsp.ts +481 -0
  133. package/vendor/agent-root/src/agent/tool/path-security.ts +117 -0
  134. package/vendor/agent-root/src/agent/tool/search/common.ts +153 -0
  135. package/vendor/agent-root/src/agent/tool/skill/index.ts +13 -0
  136. package/vendor/agent-root/src/agent/tool/skill/loader.ts +229 -0
  137. package/vendor/agent-root/src/agent/tool/skill/parser.ts +124 -0
  138. package/vendor/agent-root/src/agent/tool/skill/types.ts +27 -0
  139. package/vendor/agent-root/src/agent/tool/skill-tool.ts +143 -0
  140. package/vendor/agent-root/src/agent/tool/task-create.ts +186 -0
  141. package/vendor/agent-root/src/agent/tool/task-errors.ts +42 -0
  142. package/vendor/agent-root/src/agent/tool/task-get.ts +116 -0
  143. package/vendor/agent-root/src/agent/tool/task-graph.ts +78 -0
  144. package/vendor/agent-root/src/agent/tool/task-list.ts +141 -0
  145. package/vendor/agent-root/src/agent/tool/task-mock-runner-adapter.ts +232 -0
  146. package/vendor/agent-root/src/agent/tool/task-output.ts +223 -0
  147. package/vendor/agent-root/src/agent/tool/task-parent-abort.ts +115 -0
  148. package/vendor/agent-root/src/agent/tool/task-real-runner-adapter.ts +336 -0
  149. package/vendor/agent-root/src/agent/tool/task-runner-adapter.ts +55 -0
  150. package/vendor/agent-root/src/agent/tool/task-stop.ts +187 -0
  151. package/vendor/agent-root/src/agent/tool/task-store.ts +217 -0
  152. package/vendor/agent-root/src/agent/tool/task-subagent-config.ts +149 -0
  153. package/vendor/agent-root/src/agent/tool/task-types.ts +264 -0
  154. package/vendor/agent-root/src/agent/tool/task-update.ts +315 -0
  155. package/vendor/agent-root/src/agent/tool/task.ts +209 -0
  156. package/vendor/agent-root/src/agent/tool/tool-manager.ts +362 -0
  157. package/vendor/agent-root/src/agent/tool/tool-prompts.ts +242 -0
  158. package/vendor/agent-root/src/agent/tool/types.ts +116 -0
  159. package/vendor/agent-root/src/agent/tool/web-fetch.ts +227 -0
  160. package/vendor/agent-root/src/agent/tool/web-search.ts +208 -0
  161. package/vendor/agent-root/src/agent/tool/write-file.ts +497 -0
  162. package/vendor/agent-root/src/agent/types.ts +232 -0
  163. package/vendor/agent-root/src/agent/utils/__tests__/index.test.ts +18 -0
  164. package/vendor/agent-root/src/agent/utils/__tests__/message-utils.test.ts +610 -0
  165. package/vendor/agent-root/src/agent/utils/__tests__/message.test.ts +223 -0
  166. package/vendor/agent-root/src/agent/utils/__tests__/token.test.ts +42 -0
  167. package/vendor/agent-root/src/agent/utils/index.ts +16 -0
  168. package/vendor/agent-root/src/agent/utils/message.ts +171 -0
  169. package/vendor/agent-root/src/agent/utils/token.ts +28 -0
  170. package/vendor/agent-root/src/config/__tests__/load-config-to-env.test.ts +129 -0
  171. package/vendor/agent-root/src/config/__tests__/loader.test.ts +247 -0
  172. package/vendor/agent-root/src/config/__tests__/runtime.test.ts +88 -0
  173. package/vendor/agent-root/src/config/index.ts +54 -0
  174. package/vendor/agent-root/src/config/loader.ts +431 -0
  175. package/vendor/agent-root/src/config/paths.ts +30 -0
  176. package/vendor/agent-root/src/config/runtime.ts +163 -0
  177. package/vendor/agent-root/src/config/types.ts +70 -0
  178. package/vendor/agent-root/src/logger/index.ts +57 -0
  179. package/vendor/agent-root/src/logger/logger.ts +819 -0
  180. package/vendor/agent-root/src/logger/types.ts +150 -0
  181. package/vendor/agent-root/src/providers/__tests__/errors.test.ts +441 -0
  182. package/vendor/agent-root/src/providers/__tests__/index.test.ts +16 -0
  183. package/vendor/agent-root/src/providers/__tests__/openai-compatible.options.test.ts +318 -0
  184. package/vendor/agent-root/src/providers/__tests__/openai-compatible.test.ts +600 -0
  185. package/vendor/agent-root/src/providers/__tests__/registry.test.ts +449 -0
  186. package/vendor/agent-root/src/providers/__tests__/responses-adapter.test.ts +298 -0
  187. package/vendor/agent-root/src/providers/adapters/__tests__/anthropic.test.ts +354 -0
  188. package/vendor/agent-root/src/providers/adapters/__tests__/kimi.test.ts +58 -0
  189. package/vendor/agent-root/src/providers/adapters/__tests__/standard.test.ts +261 -0
  190. package/vendor/agent-root/src/providers/adapters/anthropic.ts +572 -0
  191. package/vendor/agent-root/src/providers/adapters/base.ts +131 -0
  192. package/vendor/agent-root/src/providers/adapters/kimi.ts +48 -0
  193. package/vendor/agent-root/src/providers/adapters/responses.ts +732 -0
  194. package/vendor/agent-root/src/providers/adapters/standard.ts +120 -0
  195. package/vendor/agent-root/src/providers/http/__tests__/client.timeout.test.ts +313 -0
  196. package/vendor/agent-root/src/providers/http/client.ts +289 -0
  197. package/vendor/agent-root/src/providers/http/stream-parser.ts +109 -0
  198. package/vendor/agent-root/src/providers/index.ts +76 -0
  199. package/vendor/agent-root/src/providers/kimi-headers.ts +177 -0
  200. package/vendor/agent-root/src/providers/openai-compatible.ts +387 -0
  201. package/vendor/agent-root/src/providers/registry/model-config.ts +230 -0
  202. package/vendor/agent-root/src/providers/registry/provider-factory.ts +123 -0
  203. package/vendor/agent-root/src/providers/registry.ts +135 -0
  204. package/vendor/agent-root/src/providers/types/api.ts +284 -0
  205. package/vendor/agent-root/src/providers/types/config.ts +58 -0
  206. package/vendor/agent-root/src/providers/types/errors.ts +323 -0
  207. package/vendor/agent-root/src/providers/types/index.ts +72 -0
  208. package/vendor/agent-root/src/providers/types/provider.ts +45 -0
  209. package/vendor/agent-root/src/providers/types/registry.ts +88 -0
@@ -0,0 +1,164 @@
1
+ import type { Message } from '../types';
2
+
3
+ export interface ToolExecutionLedgerRecord {
4
+ success: boolean;
5
+ output: string;
6
+ summary: string;
7
+ payload?: unknown;
8
+ metadata?: Record<string, unknown>;
9
+ errorName?: string;
10
+ errorMessage?: string;
11
+ errorCode?: string;
12
+ recordedAt: number;
13
+ }
14
+
15
+ export interface ToolExecutionOnceResult {
16
+ record: ToolExecutionLedgerRecord;
17
+ fromCache: boolean;
18
+ }
19
+
20
+ export interface ToolExecutionLedger {
21
+ get(executionId: string, toolCallId: string): Promise<ToolExecutionLedgerRecord | undefined>;
22
+ set(executionId: string, toolCallId: string, record: ToolExecutionLedgerRecord): Promise<void>;
23
+ executeOnce(
24
+ executionId: string,
25
+ toolCallId: string,
26
+ execute: () => Promise<ToolExecutionLedgerRecord>
27
+ ): Promise<ToolExecutionOnceResult>;
28
+ }
29
+
30
+ export class NoopToolExecutionLedger implements ToolExecutionLedger {
31
+ async get(
32
+ _executionId: string,
33
+ _toolCallId: string
34
+ ): Promise<ToolExecutionLedgerRecord | undefined> {
35
+ return undefined;
36
+ }
37
+
38
+ async set(
39
+ _executionId: string,
40
+ _toolCallId: string,
41
+ _record: ToolExecutionLedgerRecord
42
+ ): Promise<void> {
43
+ return;
44
+ }
45
+
46
+ async executeOnce(
47
+ _executionId: string,
48
+ _toolCallId: string,
49
+ execute: () => Promise<ToolExecutionLedgerRecord>
50
+ ): Promise<ToolExecutionOnceResult> {
51
+ const record = await execute();
52
+ return {
53
+ record,
54
+ fromCache: false,
55
+ };
56
+ }
57
+ }
58
+
59
+ export class InMemoryToolExecutionLedger implements ToolExecutionLedger {
60
+ private readonly store = new Map<string, ToolExecutionLedgerRecord>();
61
+ private readonly inflight = new Map<string, Promise<ToolExecutionLedgerRecord>>();
62
+
63
+ async get(
64
+ executionId: string,
65
+ toolCallId: string
66
+ ): Promise<ToolExecutionLedgerRecord | undefined> {
67
+ return this.store.get(this.buildKey(executionId, toolCallId));
68
+ }
69
+
70
+ async set(
71
+ executionId: string,
72
+ toolCallId: string,
73
+ record: ToolExecutionLedgerRecord
74
+ ): Promise<void> {
75
+ this.store.set(this.buildKey(executionId, toolCallId), record);
76
+ }
77
+
78
+ async executeOnce(
79
+ executionId: string,
80
+ toolCallId: string,
81
+ execute: () => Promise<ToolExecutionLedgerRecord>
82
+ ): Promise<ToolExecutionOnceResult> {
83
+ const key = this.buildKey(executionId, toolCallId);
84
+ const cached = this.store.get(key);
85
+ if (cached) {
86
+ return {
87
+ record: cached,
88
+ fromCache: true,
89
+ };
90
+ }
91
+
92
+ const inflight = this.inflight.get(key);
93
+ if (inflight) {
94
+ const record = await inflight;
95
+ return {
96
+ record,
97
+ fromCache: true,
98
+ };
99
+ }
100
+
101
+ const pending = (async () => {
102
+ const record = await execute();
103
+ this.store.set(key, record);
104
+ return record;
105
+ })();
106
+ this.inflight.set(key, pending);
107
+
108
+ try {
109
+ const record = await pending;
110
+ return {
111
+ record,
112
+ fromCache: false,
113
+ };
114
+ } finally {
115
+ this.inflight.delete(key);
116
+ }
117
+ }
118
+
119
+ private buildKey(executionId: string, toolCallId: string): string {
120
+ return `${executionId}:${toolCallId}`;
121
+ }
122
+ }
123
+
124
+ export function createToolResultMessage(params: {
125
+ toolCallId: string;
126
+ content: string;
127
+ metadata?: Record<string, unknown>;
128
+ createMessageId: () => string;
129
+ }): Message {
130
+ const { toolCallId, content, metadata, createMessageId } = params;
131
+ return {
132
+ messageId: createMessageId(),
133
+ type: 'tool-result',
134
+ role: 'tool',
135
+ content,
136
+ tool_call_id: toolCallId,
137
+ timestamp: Date.now(),
138
+ metadata,
139
+ };
140
+ }
141
+
142
+ export async function executeToolCallWithLedger(params: {
143
+ ledger: ToolExecutionLedger;
144
+ executionId: string | undefined;
145
+ toolCallId: string;
146
+ execute: () => Promise<ToolExecutionLedgerRecord>;
147
+ onError?: (error: unknown) => void;
148
+ }): Promise<ToolExecutionOnceResult> {
149
+ const { ledger, executionId, toolCallId, execute, onError } = params;
150
+ try {
151
+ if (!executionId) {
152
+ const record = await execute();
153
+ return {
154
+ record,
155
+ fromCache: false,
156
+ };
157
+ }
158
+
159
+ return await ledger.executeOnce(executionId, toolCallId, execute);
160
+ } catch (error) {
161
+ onError?.(error);
162
+ throw error;
163
+ }
164
+ }
@@ -0,0 +1,188 @@
1
+ import * as path from 'node:path';
2
+ import { randomUUID } from 'node:crypto';
3
+ import { promises as fs } from 'node:fs';
4
+ import { resolveWriteBufferBaseDir } from '../storage/file-storage-config';
5
+ import { createConfiguredFileHistoryStore } from '../storage/file-history-store';
6
+ import { writeTextFileWithHistory } from '../storage/file-write-service';
7
+
8
+ export interface WriteBufferSessionMeta {
9
+ bufferId: string;
10
+ messageId: string;
11
+ toolCallId?: string;
12
+ targetPath?: string;
13
+ baseDir: string;
14
+ rawArgsPath: string;
15
+ contentPath: string;
16
+ metaPath: string;
17
+ createdAt: number;
18
+ updatedAt: number;
19
+ rawArgsBytes: number;
20
+ contentBytes: number;
21
+ status: 'active' | 'finalized' | 'aborted';
22
+ }
23
+
24
+ export interface CreateWriteBufferSessionInput {
25
+ messageId?: string;
26
+ toolCallId?: string;
27
+ targetPath?: string;
28
+ baseDir?: string;
29
+ }
30
+
31
+ export interface AppendResult {
32
+ bytesWritten: number;
33
+ totalBytes: number;
34
+ updatedAt: number;
35
+ }
36
+
37
+ function getDefaultBaseDir(): string {
38
+ return resolveWriteBufferBaseDir();
39
+ }
40
+
41
+ function safeSegment(input: string): string {
42
+ return input.replace(/[^a-zA-Z0-9_-]/g, '_');
43
+ }
44
+
45
+ async function writeMeta(meta: WriteBufferSessionMeta): Promise<void> {
46
+ await fs.writeFile(meta.metaPath, JSON.stringify(meta, null, 2), 'utf8');
47
+ }
48
+
49
+ async function readMeta(metaPath: string): Promise<WriteBufferSessionMeta> {
50
+ const content = await fs.readFile(metaPath, 'utf8');
51
+ return JSON.parse(content) as WriteBufferSessionMeta;
52
+ }
53
+
54
+ export function resolveBufferId(toolCallId?: string): string {
55
+ if (typeof toolCallId === 'string' && toolCallId.trim().length > 0) {
56
+ return toolCallId.trim();
57
+ }
58
+ return `buffer_${randomUUID()}`;
59
+ }
60
+
61
+ export async function createWriteBufferSession(
62
+ input: CreateWriteBufferSessionInput
63
+ ): Promise<WriteBufferSessionMeta> {
64
+ const now = Date.now();
65
+ const baseDir = input.baseDir ? path.resolve(input.baseDir) : getDefaultBaseDir();
66
+ const messageId = input.messageId?.trim() || `msg_${now}`;
67
+ const bufferId = resolveBufferId(input.toolCallId);
68
+ const uniqueSuffix = `${now}_${randomUUID().slice(0, 8)}`;
69
+ const fileStem = `${safeSegment(messageId)}_${safeSegment(bufferId)}_${uniqueSuffix}`;
70
+
71
+ await fs.mkdir(baseDir, { recursive: true });
72
+
73
+ const rawArgsPath = path.join(baseDir, `${fileStem}.args.tmp`);
74
+ const contentPath = path.join(baseDir, `${fileStem}.content.tmp`);
75
+ const metaPath = path.join(baseDir, `${fileStem}.meta.json`);
76
+
77
+ await fs.writeFile(rawArgsPath, '', 'utf8');
78
+ await fs.writeFile(contentPath, '', 'utf8');
79
+
80
+ const meta: WriteBufferSessionMeta = {
81
+ bufferId,
82
+ messageId,
83
+ toolCallId: input.toolCallId,
84
+ targetPath: input.targetPath,
85
+ baseDir,
86
+ rawArgsPath,
87
+ contentPath,
88
+ metaPath,
89
+ createdAt: now,
90
+ updatedAt: now,
91
+ rawArgsBytes: 0,
92
+ contentBytes: 0,
93
+ status: 'active',
94
+ };
95
+
96
+ await writeMeta(meta);
97
+ return meta;
98
+ }
99
+
100
+ export async function appendRawArgs(
101
+ session: Pick<WriteBufferSessionMeta, 'rawArgsPath' | 'metaPath'>,
102
+ chunk: string
103
+ ): Promise<AppendResult> {
104
+ const bytesWritten = Buffer.byteLength(chunk, 'utf8');
105
+ await fs.appendFile(session.rawArgsPath, chunk, 'utf8');
106
+
107
+ const meta = await readMeta(session.metaPath);
108
+ const updatedAt = Date.now();
109
+ meta.updatedAt = updatedAt;
110
+ meta.rawArgsBytes += bytesWritten;
111
+ await writeMeta(meta);
112
+
113
+ return {
114
+ bytesWritten,
115
+ totalBytes: meta.rawArgsBytes,
116
+ updatedAt,
117
+ };
118
+ }
119
+
120
+ export async function appendContent(
121
+ session: Pick<WriteBufferSessionMeta, 'contentPath' | 'metaPath'>,
122
+ chunk: string
123
+ ): Promise<AppendResult> {
124
+ const bytesWritten = Buffer.byteLength(chunk, 'utf8');
125
+ await fs.appendFile(session.contentPath, chunk, 'utf8');
126
+
127
+ const meta = await readMeta(session.metaPath);
128
+ const updatedAt = Date.now();
129
+ meta.updatedAt = updatedAt;
130
+ meta.contentBytes += bytesWritten;
131
+ await writeMeta(meta);
132
+
133
+ return {
134
+ bytesWritten,
135
+ totalBytes: meta.contentBytes,
136
+ updatedAt,
137
+ };
138
+ }
139
+
140
+ export async function finalizeWriteBufferSession(
141
+ session: Pick<WriteBufferSessionMeta, 'contentPath' | 'metaPath' | 'targetPath'>
142
+ ): Promise<WriteBufferSessionMeta> {
143
+ if (!session.targetPath) {
144
+ throw new Error('targetPath is required to finalize write buffer session');
145
+ }
146
+
147
+ const meta = await readMeta(session.metaPath);
148
+ if (meta.status !== 'active') {
149
+ throw new Error(`cannot finalize session in status=${meta.status}`);
150
+ }
151
+
152
+ const targetPath = path.resolve(session.targetPath);
153
+ const content = await fs.readFile(session.contentPath, 'utf8');
154
+ await writeTextFileWithHistory(targetPath, content, {
155
+ source: 'write_buffer_finalize',
156
+ historyStore: createConfiguredFileHistoryStore(),
157
+ });
158
+
159
+ meta.targetPath = targetPath;
160
+ meta.updatedAt = Date.now();
161
+ meta.status = 'finalized';
162
+ await writeMeta(meta);
163
+
164
+ return meta;
165
+ }
166
+
167
+ export async function abortWriteBufferSession(
168
+ session: Pick<WriteBufferSessionMeta, 'rawArgsPath' | 'contentPath' | 'metaPath'>
169
+ ): Promise<void> {
170
+ const meta = await readMeta(session.metaPath);
171
+ meta.status = 'aborted';
172
+ meta.updatedAt = Date.now();
173
+ await writeMeta(meta);
174
+ }
175
+
176
+ export async function cleanupWriteBufferSessionFiles(
177
+ session: Pick<WriteBufferSessionMeta, 'rawArgsPath' | 'contentPath' | 'metaPath'>
178
+ ): Promise<void> {
179
+ await Promise.all([
180
+ fs.rm(session.rawArgsPath, { force: true }),
181
+ fs.rm(session.contentPath, { force: true }),
182
+ fs.rm(session.metaPath, { force: true }),
183
+ ]);
184
+ }
185
+
186
+ export async function loadWriteBufferSession(metaPath: string): Promise<WriteBufferSessionMeta> {
187
+ return readMeta(metaPath);
188
+ }
@@ -0,0 +1,238 @@
1
+ import type { ToolCall } from '../../providers';
2
+ import {
3
+ appendContent,
4
+ appendRawArgs,
5
+ cleanupWriteBufferSessionFiles,
6
+ createWriteBufferSession,
7
+ loadWriteBufferSession,
8
+ type WriteBufferSessionMeta,
9
+ } from './write-buffer';
10
+
11
+ export const WRITE_FILE_TOOL_NAME = 'write_file';
12
+
13
+ export interface WriteBufferRuntime {
14
+ session: WriteBufferSessionMeta;
15
+ bufferedContentChars: number;
16
+ }
17
+
18
+ interface WriteFileProtocolPayload {
19
+ ok: boolean;
20
+ code:
21
+ | 'OK'
22
+ | 'WRITE_FILE_PARTIAL_BUFFERED'
23
+ | 'WRITE_FILE_NEED_FINALIZE'
24
+ | 'WRITE_FILE_FINALIZE_OK';
25
+ nextAction: 'finalize' | 'none';
26
+ }
27
+
28
+ export function buildWriteFileSessionKey(params: {
29
+ executionId?: string;
30
+ stepIndex: number;
31
+ toolCallId: string;
32
+ }): string {
33
+ const { executionId, stepIndex, toolCallId } = params;
34
+ const executionKey = executionId && executionId.trim().length > 0 ? executionId : '__anonymous__';
35
+ return `${executionKey}:${stepIndex}:${toolCallId}`;
36
+ }
37
+
38
+ export function isWriteFileToolCall(toolCall: ToolCall): boolean {
39
+ return toolCall.function.name?.trim() === WRITE_FILE_TOOL_NAME;
40
+ }
41
+
42
+ export function extractWriteFileContentPrefix(argumentsText: string): string | null {
43
+ const contentMarkerMatch = /"content"\s*:\s*"/.exec(argumentsText);
44
+ if (!contentMarkerMatch || typeof contentMarkerMatch.index !== 'number') {
45
+ return null;
46
+ }
47
+
48
+ let cursor = contentMarkerMatch.index + contentMarkerMatch[0].length;
49
+ let output = '';
50
+
51
+ while (cursor < argumentsText.length) {
52
+ const ch = argumentsText[cursor];
53
+ if (ch === '"') {
54
+ return output;
55
+ }
56
+
57
+ if (ch !== '\\') {
58
+ output += ch;
59
+ cursor += 1;
60
+ continue;
61
+ }
62
+
63
+ if (cursor + 1 >= argumentsText.length) {
64
+ return output;
65
+ }
66
+
67
+ const esc = argumentsText[cursor + 1];
68
+ if (esc === '"' || esc === '\\' || esc === '/') {
69
+ output += esc;
70
+ cursor += 2;
71
+ } else if (esc === 'b') {
72
+ output += '\b';
73
+ cursor += 2;
74
+ } else if (esc === 'f') {
75
+ output += '\f';
76
+ cursor += 2;
77
+ } else if (esc === 'n') {
78
+ output += '\n';
79
+ cursor += 2;
80
+ } else if (esc === 'r') {
81
+ output += '\r';
82
+ cursor += 2;
83
+ } else if (esc === 't') {
84
+ output += '\t';
85
+ cursor += 2;
86
+ } else if (esc === 'u') {
87
+ const unicodeHex = argumentsText.slice(cursor + 2, cursor + 6);
88
+ if (!/^[0-9a-fA-F]{4}$/.test(unicodeHex)) {
89
+ return output;
90
+ }
91
+ output += String.fromCharCode(parseInt(unicodeHex, 16));
92
+ cursor += 6;
93
+ } else {
94
+ output += esc;
95
+ cursor += 2;
96
+ }
97
+ }
98
+
99
+ return output;
100
+ }
101
+
102
+ export async function bufferWriteFileToolCallChunk(params: {
103
+ toolCall: ToolCall;
104
+ argumentsChunk: string;
105
+ messageId: string;
106
+ sessionKey?: string;
107
+ sessions: Map<string, WriteBufferRuntime>;
108
+ onError?: (error: unknown) => void;
109
+ }): Promise<void> {
110
+ const { toolCall, argumentsChunk, messageId, sessionKey, sessions, onError } = params;
111
+ if (!isWriteFileToolCall(toolCall)) {
112
+ return;
113
+ }
114
+
115
+ try {
116
+ const runtimeKey = sessionKey || toolCall.id;
117
+ let runtime = sessions.get(runtimeKey);
118
+ if (!runtime) {
119
+ const session = await createWriteBufferSession({
120
+ messageId,
121
+ toolCallId: toolCall.id,
122
+ });
123
+ runtime = {
124
+ session,
125
+ bufferedContentChars: 0,
126
+ };
127
+ sessions.set(runtimeKey, runtime);
128
+ }
129
+
130
+ if (argumentsChunk) {
131
+ await appendRawArgs(runtime.session, argumentsChunk);
132
+ }
133
+
134
+ const decodedContentPrefix = extractWriteFileContentPrefix(toolCall.function.arguments);
135
+ if (decodedContentPrefix === null) {
136
+ return;
137
+ }
138
+
139
+ if (decodedContentPrefix.length <= runtime.bufferedContentChars) {
140
+ return;
141
+ }
142
+
143
+ const contentDelta = decodedContentPrefix.slice(runtime.bufferedContentChars);
144
+ await appendContent(runtime.session, contentDelta);
145
+ runtime.bufferedContentChars = decodedContentPrefix.length;
146
+ } catch (error) {
147
+ onError?.(error);
148
+ }
149
+ }
150
+
151
+ export async function enrichWriteFileToolError(
152
+ toolCall: ToolCall,
153
+ content: string,
154
+ sessions: Map<string, WriteBufferRuntime>,
155
+ sessionKey?: string
156
+ ): Promise<string> {
157
+ if (!isWriteFileToolCall(toolCall)) {
158
+ return content;
159
+ }
160
+ const runtime = sessions.get(sessionKey || toolCall.id);
161
+ if (!runtime) {
162
+ return JSON.stringify({
163
+ ok: false,
164
+ code: 'WRITE_FILE_NEED_FINALIZE',
165
+ message: content,
166
+ nextAction: 'finalize',
167
+ });
168
+ }
169
+ try {
170
+ const meta = await loadWriteBufferSession(runtime.session.metaPath);
171
+ return JSON.stringify({
172
+ ok: false,
173
+ code: 'WRITE_FILE_PARTIAL_BUFFERED',
174
+ message: content,
175
+ buffer: {
176
+ bufferId: meta.bufferId,
177
+ path: meta.targetPath || '',
178
+ bufferedBytes: meta.contentBytes,
179
+ maxChunkBytes: 32768,
180
+ },
181
+ nextAction: 'finalize',
182
+ });
183
+ } catch {
184
+ return JSON.stringify({
185
+ ok: false,
186
+ code: 'WRITE_FILE_NEED_FINALIZE',
187
+ message: content,
188
+ nextAction: 'finalize',
189
+ });
190
+ }
191
+ }
192
+
193
+ export function isWriteFileProtocolOutput(content: string | undefined): content is string {
194
+ if (!content || content.trim().length === 0) {
195
+ return false;
196
+ }
197
+ try {
198
+ const parsed = JSON.parse(content) as Partial<WriteFileProtocolPayload>;
199
+ if (!parsed || typeof parsed !== 'object') {
200
+ return false;
201
+ }
202
+ return (
203
+ typeof parsed.code === 'string' &&
204
+ typeof parsed.ok === 'boolean' &&
205
+ (parsed.nextAction === 'finalize' || parsed.nextAction === 'none')
206
+ );
207
+ } catch {
208
+ return false;
209
+ }
210
+ }
211
+
212
+ export function shouldEnrichWriteFileFailure(
213
+ error: { name?: string } | undefined,
214
+ output?: string
215
+ ): boolean {
216
+ if (isWriteFileProtocolOutput(output)) {
217
+ return false;
218
+ }
219
+ const errorName = error?.name;
220
+ return errorName === 'InvalidArgumentsError' || errorName === 'ToolValidationError';
221
+ }
222
+
223
+ export async function cleanupWriteFileBufferIfNeeded(
224
+ toolCall: ToolCall,
225
+ sessions: Map<string, WriteBufferRuntime>,
226
+ sessionKey?: string
227
+ ): Promise<void> {
228
+ if (!isWriteFileToolCall(toolCall)) {
229
+ return;
230
+ }
231
+ const runtimeKey = sessionKey || toolCall.id;
232
+ const runtime = sessions.get(runtimeKey);
233
+ if (!runtime) {
234
+ return;
235
+ }
236
+ sessions.delete(runtimeKey);
237
+ await cleanupWriteBufferSessionFiles(runtime.session);
238
+ }