@gajae-code/coding-agent 0.12.6 → 0.12.8

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 (150) hide show
  1. package/CHANGELOG.md +22 -1
  2. package/dist/types/async/job-manager.d.ts +36 -1
  3. package/dist/types/commands/notify.d.ts +4 -0
  4. package/dist/types/config/file-lock.d.ts +3 -0
  5. package/dist/types/config/model-discovery-manager.d.ts +6 -1
  6. package/dist/types/config/model-registry.d.ts +2 -0
  7. package/dist/types/config/resolve-config-value.d.ts +2 -1
  8. package/dist/types/config/settings-schema.d.ts +17 -1
  9. package/dist/types/eval/backend.d.ts +2 -2
  10. package/dist/types/exec/bash-executor.d.ts +8 -10
  11. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +0 -36
  12. package/dist/types/internal-urls/registry-helpers.d.ts +0 -18
  13. package/dist/types/main.d.ts +1 -0
  14. package/dist/types/modes/components/hook-selector.d.ts +20 -0
  15. package/dist/types/modes/components/notifications-settings-editor.d.ts +53 -4
  16. package/dist/types/modes/controllers/extension-ui-controller.d.ts +1 -1
  17. package/dist/types/modes/controllers/selector-controller.d.ts +4 -2
  18. package/dist/types/sdk/broker/broker.d.ts +5 -1
  19. package/dist/types/sdk/broker/lifecycle-ledger.d.ts +1 -0
  20. package/dist/types/sdk/bus/chat-daemon-control.d.ts +11 -3
  21. package/dist/types/sdk/bus/config.d.ts +75 -113
  22. package/dist/types/sdk/bus/discord-live-provider.d.ts +8 -2
  23. package/dist/types/sdk/bus/discord-provider.d.ts +19 -0
  24. package/dist/types/sdk/bus/index.d.ts +1 -1
  25. package/dist/types/sdk/bus/notification-orchestration.d.ts +127 -18
  26. package/dist/types/sdk/bus/notification-service.d.ts +31 -4
  27. package/dist/types/sdk/bus/session-control.d.ts +8 -3
  28. package/dist/types/sdk/bus/slack-live-provider.d.ts +9 -2
  29. package/dist/types/sdk/bus/slack-provider.d.ts +22 -0
  30. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +9 -4
  31. package/dist/types/sdk/client/discovery.d.ts +2 -1
  32. package/dist/types/sdk/host/host.d.ts +2 -23
  33. package/dist/types/sdk/host/query/handlers.d.ts +2 -0
  34. package/dist/types/sdk/host/reverse-leases.d.ts +4 -7
  35. package/dist/types/sdk/index.d.ts +1 -0
  36. package/dist/types/sdk/providers.d.ts +22 -0
  37. package/dist/types/sdk/session-directory.d.ts +1 -1
  38. package/dist/types/session/artifacts.d.ts +5 -8
  39. package/dist/types/session/client-bridge.d.ts +0 -14
  40. package/dist/types/session/internal/managed-session-scope.d.ts +17 -1
  41. package/dist/types/session/internal/managed-session-storage.d.ts +11 -4
  42. package/dist/types/session/internal/native-publish-outcome.d.ts +1 -0
  43. package/dist/types/session/internal/session-open-errors.d.ts +5 -0
  44. package/dist/types/session/messages.d.ts +1 -4
  45. package/dist/types/session/session-manager.d.ts +7 -6
  46. package/dist/types/session/session-storage.d.ts +2 -0
  47. package/dist/types/session/streaming-output.d.ts +0 -32
  48. package/dist/types/task/receipt.d.ts +1 -2
  49. package/dist/types/task/types.d.ts +43 -178
  50. package/dist/types/tools/bash.d.ts +1 -1
  51. package/dist/types/tools/fetch.d.ts +0 -3
  52. package/dist/types/tools/output-meta.d.ts +5 -37
  53. package/dist/types/tools/subagent.d.ts +15 -0
  54. package/dist/types/tools/todo-write.d.ts +3 -0
  55. package/dist/types/tools/tool-result.d.ts +0 -1
  56. package/package.json +10 -8
  57. package/scripts/build-sdk-package-smoke.ts +51 -0
  58. package/scripts/capture-notifications-settings-showcase.ts +1 -1
  59. package/src/async/job-manager.ts +430 -46
  60. package/src/cli/notify-cli.ts +204 -94
  61. package/src/commands/notify.ts +19 -8
  62. package/src/config/file-lock.ts +6 -0
  63. package/src/config/model-discovery-manager.ts +50 -8
  64. package/src/config/model-registry.ts +862 -75
  65. package/src/config/resolve-config-value.ts +10 -8
  66. package/src/config/settings-schema.ts +25 -15
  67. package/src/config/settings.ts +15 -15
  68. package/src/cursor.ts +13 -20
  69. package/src/defaults/gjc/skills/ultragoal/SKILL.md +2 -3
  70. package/src/eval/backend.ts +2 -2
  71. package/src/exec/bash-executor.ts +151 -425
  72. package/src/gjc-runtime/session-state-sidecar.ts +12 -2
  73. package/src/gjc-runtime/ultragoal-runtime.ts +3 -280
  74. package/src/gjc-runtime/workflow-manifest.generated.json +1 -40
  75. package/src/gjc-runtime/workflow-manifest.ts +0 -12
  76. package/src/internal-urls/agent-protocol.ts +38 -98
  77. package/src/internal-urls/artifact-protocol.ts +17 -28
  78. package/src/internal-urls/docs-index.generated.ts +7 -7
  79. package/src/internal-urls/registry-helpers.ts +0 -62
  80. package/src/main.ts +43 -7
  81. package/src/modes/DESIGN.md +11 -11
  82. package/src/modes/acp/acp-agent.ts +0 -24
  83. package/src/modes/acp/acp-client-bridge.ts +11 -154
  84. package/src/modes/components/hook-selector.ts +155 -44
  85. package/src/modes/components/model-selector.ts +242 -49
  86. package/src/modes/components/notifications-settings-editor.ts +631 -85
  87. package/src/modes/controllers/extension-ui-controller.ts +114 -15
  88. package/src/modes/controllers/selector-controller.ts +464 -50
  89. package/src/prompts/tools/bash.md +2 -2
  90. package/src/sdk/acp/adapter.ts +9 -200
  91. package/src/sdk/broker/broker.ts +54 -10
  92. package/src/sdk/broker/lifecycle-ledger.ts +34 -1
  93. package/src/sdk/broker/lifecycle.ts +47 -50
  94. package/src/sdk/bus/chat-daemon-cli.ts +12 -5
  95. package/src/sdk/bus/chat-daemon-control.ts +35 -8
  96. package/src/sdk/bus/chat-daemon-runtime.ts +16 -2
  97. package/src/sdk/bus/config.ts +534 -286
  98. package/src/sdk/bus/discord-live-provider.ts +57 -2
  99. package/src/sdk/bus/discord-provider.ts +21 -0
  100. package/src/sdk/bus/index.ts +105 -359
  101. package/src/sdk/bus/notification-orchestration.ts +371 -68
  102. package/src/sdk/bus/notification-service.ts +431 -130
  103. package/src/sdk/bus/session-control.ts +119 -44
  104. package/src/sdk/bus/slack-live-provider.ts +78 -2
  105. package/src/sdk/bus/slack-provider.ts +24 -0
  106. package/src/sdk/bus/telegram-daemon-cli.ts +9 -2
  107. package/src/sdk/bus/telegram-daemon-contract.ts +9 -4
  108. package/src/sdk/bus/telegram-daemon-control.ts +3 -3
  109. package/src/sdk/bus/telegram-daemon.ts +23 -15
  110. package/src/sdk/client/discovery.ts +10 -4
  111. package/src/sdk/host/host.ts +9 -187
  112. package/src/sdk/host/query/handlers.ts +20 -1
  113. package/src/sdk/host/reverse-leases.ts +42 -249
  114. package/src/sdk/index.ts +1 -0
  115. package/src/sdk/protocol/operation-inventory.generated.json +18 -0
  116. package/src/sdk/protocol/operation-registry.ts +5 -2
  117. package/src/sdk/providers.ts +51 -0
  118. package/src/sdk/session-directory.ts +1 -0
  119. package/src/sdk/session.ts +10 -7
  120. package/src/session/agent-session.ts +59 -202
  121. package/src/session/artifacts.ts +74 -211
  122. package/src/session/client-bridge.ts +0 -15
  123. package/src/session/internal/managed-session-scope.ts +981 -450
  124. package/src/session/internal/managed-session-storage.ts +362 -124
  125. package/src/session/internal/native-publish-outcome.ts +3 -1
  126. package/src/session/internal/session-open-errors.ts +11 -0
  127. package/src/session/messages.ts +7 -32
  128. package/src/session/session-manager.ts +48 -15
  129. package/src/session/session-storage.ts +72 -5
  130. package/src/session/streaming-output.ts +40 -198
  131. package/src/slash-commands/builtin-registry.ts +82 -4
  132. package/src/task/index.ts +237 -65
  133. package/src/task/receipt.ts +2 -4
  134. package/src/task/render.ts +0 -30
  135. package/src/task/types.ts +10 -34
  136. package/src/tools/ask.ts +1 -1
  137. package/src/tools/bash.ts +239 -818
  138. package/src/tools/eval.ts +4 -99
  139. package/src/tools/fetch.ts +15 -77
  140. package/src/tools/find.ts +3 -7
  141. package/src/tools/output-meta.ts +114 -696
  142. package/src/tools/search.ts +4 -9
  143. package/src/tools/subagent.ts +97 -75
  144. package/src/tools/telegram-send.ts +4 -2
  145. package/src/tools/todo-write.ts +47 -0
  146. package/src/tools/tool-result.ts +0 -1
  147. package/dist/types/gjc-runtime/ultragoal-review-source.d.ts +0 -83
  148. package/dist/types/utils/artifact-id.d.ts +0 -2
  149. package/src/gjc-runtime/ultragoal-review-source.ts +0 -378
  150. package/src/utils/artifact-id.ts +0 -6
@@ -11,16 +11,11 @@
11
11
  * - agent://<id>?q=<query> - JSON extraction via query form
12
12
  */
13
13
  import { createHash } from "node:crypto";
14
+ import * as fs from "node:fs/promises";
14
15
  import * as path from "node:path";
15
16
  import { isEnoent } from "@gajae-code/utils";
16
17
  import { applyQuery, pathToQuery } from "./json-query";
17
- import {
18
- type AuthorizedArtifactsRoot,
19
- authorizedArtifactsDirsFromContext,
20
- closeAuthorizedArtifactsRoot,
21
- openAuthorizedArtifactsRoot,
22
- readAuthorizedArtifactFile,
23
- } from "./registry-helpers";
18
+ import { authorizedArtifactsDirsFromContext } from "./registry-helpers";
24
19
  import type { InternalResource, InternalUrl, ProtocolHandler, ResolveContext } from "./types";
25
20
 
26
21
  interface AgentOutputMetadata {
@@ -41,24 +36,6 @@ interface ManagedOutputSelector {
41
36
  metadataSha256: string;
42
37
  }
43
38
 
44
- const MAX_AGENT_SELECTOR_BYTES = 64 * 1024;
45
- const MAX_AGENT_METADATA_BYTES = 1024 * 1024;
46
- const MAX_AGENT_OUTPUT_BYTES = 16 * 1024 * 1024;
47
-
48
- function readRetainedAgentFile(
49
- outputId: string,
50
- root: AuthorizedArtifactsRoot,
51
- filename: string,
52
- maxBytes: number,
53
- ): Buffer | null {
54
- const captured = readAuthorizedArtifactFile(root, filename, maxBytes);
55
- if (!captured) return null;
56
- if (captured.size !== captured.bytes.byteLength) {
57
- throw new Error(`agent://${outputId} retained file exceeds read capacity`);
58
- }
59
- return captured.bytes;
60
- }
61
-
62
39
  function isSafeGenerationFilename(filename: unknown): filename is string {
63
40
  return typeof filename === "string" && /^[a-zA-Z0-9_.-]+$/.test(filename);
64
41
  }
@@ -79,14 +56,17 @@ function isManagedOutputSelector(value: unknown, outputId: string): value is Man
79
56
  );
80
57
  }
81
58
 
82
- function readManagedOutputSelector(
59
+ async function readManagedOutputSelector(
83
60
  outputId: string,
84
- root: AuthorizedArtifactsRoot,
85
- selectorFilename: string,
86
- ): ManagedOutputSelector | null {
87
- const bytes = readRetainedAgentFile(outputId, root, selectorFilename, MAX_AGENT_SELECTOR_BYTES);
88
- if (!bytes) return null;
89
- const raw = bytes.toString("utf8");
61
+ selectorPath: string,
62
+ ): Promise<ManagedOutputSelector | null> {
63
+ let raw: string;
64
+ try {
65
+ raw = await Bun.file(selectorPath).text();
66
+ } catch (error) {
67
+ if (isEnoent(error)) return null;
68
+ throw error;
69
+ }
90
70
  let parsed: unknown;
91
71
  try {
92
72
  parsed = JSON.parse(raw);
@@ -96,18 +76,6 @@ function readManagedOutputSelector(
96
76
  if (!isManagedOutputSelector(parsed, outputId)) throw new Error(`agent://${outputId} malformed output selector`);
97
77
  return parsed;
98
78
  }
99
- function sameManagedOutputSelector(left: ManagedOutputSelector, right: ManagedOutputSelector): boolean {
100
- return (
101
- left.outputFilename === right.outputFilename &&
102
- left.metadataFilename === right.metadataFilename &&
103
- left.outputSizeBytes === right.outputSizeBytes &&
104
- left.outputSha256 === right.outputSha256 &&
105
- left.metadataSizeBytes === right.metadataSizeBytes &&
106
- left.metadataSha256 === right.metadataSha256
107
- );
108
- }
109
-
110
- const MANAGED_SELECTOR_READ_ATTEMPTS = 8;
111
79
 
112
80
  function isAgentOutputMetadata(value: unknown, outputId: string): value is AgentOutputMetadata {
113
81
  if (!value || typeof value !== "object") return false;
@@ -124,11 +92,18 @@ function isAgentOutputMetadata(value: unknown, outputId: string): value is Agent
124
92
 
125
93
  async function verifyAgentOutputMetadata(
126
94
  outputId: string,
127
- metadataBytes: Buffer,
95
+ foundPath: string,
96
+ metadataPath: string,
128
97
  bytes: Buffer,
129
98
  selector?: ManagedOutputSelector,
130
99
  ): Promise<void> {
131
- const metaRaw = metadataBytes.toString("utf8");
100
+ let metaRaw: string;
101
+ try {
102
+ metaRaw = await Bun.file(metadataPath).text();
103
+ } catch (err) {
104
+ if (isEnoent(err)) throw new Error(`agent://${outputId} missing metadata`);
105
+ throw err;
106
+ }
132
107
  let parsed: unknown;
133
108
  try {
134
109
  parsed = JSON.parse(metaRaw);
@@ -138,7 +113,9 @@ async function verifyAgentOutputMetadata(
138
113
  if (!isAgentOutputMetadata(parsed, outputId)) {
139
114
  throw new Error(`agent://${outputId} malformed metadata`);
140
115
  }
141
- if (bytes.byteLength !== parsed.sizeBytes) {
116
+ const metadataBytes = Buffer.from(metaRaw, "utf8");
117
+ const stat = await fs.stat(foundPath);
118
+ if (stat.size !== parsed.sizeBytes || bytes.byteLength !== parsed.sizeBytes) {
142
119
  throw new Error(`agent://${outputId} size mismatch`);
143
120
  }
144
121
  const sha256 = createHash("sha256").update(bytes).digest("hex");
@@ -195,64 +172,27 @@ export class AgentProtocolHandler implements ProtocolHandler {
195
172
  let foundPath: string | undefined;
196
173
  let foundMetadataPath: string | undefined;
197
174
  let foundSelector: ManagedOutputSelector | undefined;
198
- let foundBytes: Buffer | undefined;
199
- let foundMetadataBytes: Buffer | undefined;
200
175
  let anyDirExists = false;
201
176
 
202
177
  for (const dir of dirs) {
203
- let root: AuthorizedArtifactsRoot;
204
178
  try {
205
- root = openAuthorizedArtifactsRoot(dir);
179
+ await fs.stat(dir);
206
180
  anyDirExists = true;
207
181
  } catch (err) {
208
182
  if (isEnoent(err)) continue;
209
183
  throw err;
210
184
  }
185
+ const selector = await readManagedOutputSelector(outputId, path.join(dir, `${outputId}.md.selector.json`));
186
+ const candidate = selector ? path.join(dir, selector.outputFilename) : path.join(dir, `${outputId}.md`);
187
+ const metadataPath = selector ? path.join(dir, selector.metadataFilename) : `${candidate}.meta.json`;
211
188
  try {
212
- const selectorFilename = `${outputId}.md.selector.json`;
213
- let selector = readManagedOutputSelector(outputId, root, selectorFilename);
214
- for (let attempt = 0; attempt < MANAGED_SELECTOR_READ_ATTEMPTS; attempt++) {
215
- const candidateFilename = selector ? selector.outputFilename : `${outputId}.md`;
216
- const metadataFilename = selector ? selector.metadataFilename : `${candidateFilename}.meta.json`;
217
- const rawBuffer = readRetainedAgentFile(outputId, root, candidateFilename, MAX_AGENT_OUTPUT_BYTES);
218
- const metadataBuffer = readRetainedAgentFile(outputId, root, metadataFilename, MAX_AGENT_METADATA_BYTES);
219
- if (rawBuffer && metadataBuffer) {
220
- if (selector) {
221
- const refreshed = readManagedOutputSelector(outputId, root, selectorFilename);
222
- if (!refreshed || !sameManagedOutputSelector(selector, refreshed)) {
223
- if (attempt + 1 >= MANAGED_SELECTOR_READ_ATTEMPTS) {
224
- throw new Error(`agent://${outputId} generation changed during read`);
225
- }
226
- if (!refreshed) throw new Error(`agent://${outputId} generation changed during read`);
227
- selector = refreshed;
228
- continue;
229
- }
230
- }
231
- if (foundPath) throw new Error(`agent://${outputId} ambiguous id in authorized artifacts`);
232
- foundPath = path.join(dir, candidateFilename);
233
- foundMetadataPath = path.join(dir, metadataFilename);
234
- foundSelector = selector ?? undefined;
235
- foundBytes = rawBuffer;
236
- foundMetadataBytes = metadataBuffer;
237
- break;
238
- }
239
- if (!selector) {
240
- if (!rawBuffer) break;
241
- throw new Error(`agent://${outputId} missing metadata`);
242
- }
243
- if (attempt + 1 >= MANAGED_SELECTOR_READ_ATTEMPTS) {
244
- throw new Error(`agent://${outputId} generation changed during read`);
245
- }
246
- const refreshed = readManagedOutputSelector(outputId, root, selectorFilename);
247
- if (!refreshed) throw new Error(`agent://${outputId} generation changed during read`);
248
- if (sameManagedOutputSelector(selector, refreshed)) {
249
- if (!rawBuffer) throw new Error(`agent://${outputId} selected generation unavailable`);
250
- throw new Error(`agent://${outputId} missing metadata`);
251
- }
252
- selector = refreshed;
253
- }
254
- } finally {
255
- closeAuthorizedArtifactsRoot(root);
189
+ await fs.stat(candidate);
190
+ if (foundPath) throw new Error(`agent://${outputId} ambiguous id in authorized artifacts`);
191
+ foundPath = candidate;
192
+ foundMetadataPath = metadataPath;
193
+ foundSelector = selector ?? undefined;
194
+ } catch (err) {
195
+ if (!isEnoent(err)) throw err;
256
196
  }
257
197
  }
258
198
 
@@ -260,12 +200,12 @@ export class AgentProtocolHandler implements ProtocolHandler {
260
200
  throw new Error("No artifacts directory found");
261
201
  }
262
202
 
263
- if (!foundPath || !foundMetadataPath || !foundBytes || !foundMetadataBytes) {
203
+ if (!foundPath) {
264
204
  throw new Error(`agent://${outputId} not found`);
265
205
  }
266
206
 
267
- const rawBytes = foundBytes;
268
- await verifyAgentOutputMetadata(outputId, foundMetadataBytes, rawBytes, foundSelector);
207
+ const rawBytes = Buffer.from(await Bun.file(foundPath).arrayBuffer());
208
+ await verifyAgentOutputMetadata(outputId, foundPath, foundMetadataPath!, rawBytes, foundSelector);
269
209
  const rawContent = rawBytes.toString("utf8");
270
210
  const notes: string[] = [];
271
211
  let content = rawContent;
@@ -12,14 +12,7 @@
12
12
  import * as fs from "node:fs/promises";
13
13
  import * as path from "node:path";
14
14
  import { isEnoent } from "@gajae-code/utils";
15
- import { isValidArtifactId } from "../utils/artifact-id";
16
- import {
17
- type AuthorizedArtifactsRoot,
18
- authorizedArtifactsDirsFromContext,
19
- closeAuthorizedArtifactsRoot,
20
- openAuthorizedArtifactsRoot,
21
- readAuthorizedArtifactFile,
22
- } from "./registry-helpers";
15
+ import { authorizedArtifactsDirsFromContext } from "./registry-helpers";
23
16
  import type { InternalResource, InternalUrl, ProtocolHandler, ResolveContext } from "./types";
24
17
 
25
18
  export class ArtifactProtocolHandler implements ProtocolHandler {
@@ -31,7 +24,7 @@ export class ArtifactProtocolHandler implements ProtocolHandler {
31
24
  if (!id) {
32
25
  throw new Error("artifact:// URL requires a numeric ID: artifact://0");
33
26
  }
34
- if (!isValidArtifactId(id)) {
27
+ if (!/^\d+$/.test(id)) {
35
28
  throw new Error(`artifact:// ID must be numeric, got: ${id}`);
36
29
  }
37
30
 
@@ -42,33 +35,23 @@ export class ArtifactProtocolHandler implements ProtocolHandler {
42
35
  }
43
36
 
44
37
  let foundPath: string | undefined;
45
- let foundBytes: Buffer | undefined;
46
- let foundSize = 0;
47
38
  let anyDirExists = false;
48
- const MAX_ARTIFACT_READ_BYTES = 16 * 1024 * 1024;
49
39
 
50
40
  for (const dir of dirs) {
51
- let root: AuthorizedArtifactsRoot;
41
+ let files: string[];
52
42
  try {
53
- root = openAuthorizedArtifactsRoot(dir);
43
+ files = await fs.readdir(dir);
54
44
  anyDirExists = true;
55
45
  } catch (err) {
56
46
  if (isEnoent(err)) continue;
57
47
  throw err;
58
48
  }
59
- try {
60
- const files = await fs.readdir(dir);
61
- for (const f of files) {
62
- if (f.endsWith(".meta.json") || !f.startsWith(`${id}.`)) continue;
63
- const captured = readAuthorizedArtifactFile(root, f, MAX_ARTIFACT_READ_BYTES);
64
- if (!captured) continue;
49
+ for (const f of files) {
50
+ if (f.endsWith(".meta.json")) continue;
51
+ if (f.startsWith(`${id}.`)) {
65
52
  if (foundPath) throw new Error(`artifact://${id} ambiguous id in authorized artifacts`);
66
53
  foundPath = path.join(dir, f);
67
- foundBytes = captured.bytes;
68
- foundSize = captured.size;
69
54
  }
70
- } finally {
71
- closeAuthorizedArtifactsRoot(root);
72
55
  }
73
56
  }
74
57
 
@@ -76,14 +59,20 @@ export class ArtifactProtocolHandler implements ProtocolHandler {
76
59
  throw new Error("No artifacts directory found");
77
60
  }
78
61
 
79
- if (!foundPath || !foundBytes) {
62
+ if (!foundPath) {
80
63
  throw new Error(`artifact://${id} not found`);
81
64
  }
82
65
 
66
+ // F20: cap the materialized artifact so reading a huge spilled artifact cannot
67
+ // buffer GBs into memory (the range selector is applied downstream, so without a
68
+ // cap a `artifact://id:range` over a multi-GB artifact still reads it whole).
69
+ const MAX_ARTIFACT_READ_BYTES = 16 * 1024 * 1024;
70
+ const file = Bun.file(foundPath);
71
+ const fullSize = file.size;
83
72
  const content =
84
- foundSize > foundBytes.byteLength
85
- ? `${foundBytes.toString("utf8")}\n\n[Artifact truncated: first ${foundBytes.byteLength} of ${foundSize} bytes shown; use a narrower range or a specialized tool for the full content.]`
86
- : foundBytes.toString("utf8");
73
+ fullSize > MAX_ARTIFACT_READ_BYTES
74
+ ? `${await file.slice(0, MAX_ARTIFACT_READ_BYTES).text()}\n\n[Artifact truncated: first ${MAX_ARTIFACT_READ_BYTES} of ${fullSize} bytes shown; use a narrower range or a specialized tool for the full content.]`
75
+ : await file.text();
87
76
  return {
88
77
  url: url.href,
89
78
  content,