@code-yeongyu/senpi-agent-core 2026.9.15-2 → 2026.9.16

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 (40) hide show
  1. package/dist/empty-assistant-recovery.d.ts.map +1 -1
  2. package/dist/empty-assistant-recovery.js +51 -12
  3. package/dist/empty-assistant-recovery.js.map +1 -1
  4. package/dist/harness/tools/read.d.ts +3 -0
  5. package/dist/harness/tools/read.d.ts.map +1 -1
  6. package/dist/harness/tools/read.js +18 -3
  7. package/dist/harness/tools/read.js.map +1 -1
  8. package/dist/harness/utils/read-folders/brace-scanner.d.ts +4 -0
  9. package/dist/harness/utils/read-folders/brace-scanner.d.ts.map +1 -0
  10. package/dist/harness/utils/read-folders/brace-scanner.js +268 -0
  11. package/dist/harness/utils/read-folders/brace-scanner.js.map +1 -0
  12. package/dist/harness/utils/read-folders/header-protection.d.ts +18 -0
  13. package/dist/harness/utils/read-folders/header-protection.d.ts.map +1 -0
  14. package/dist/harness/utils/read-folders/header-protection.js +69 -0
  15. package/dist/harness/utils/read-folders/header-protection.js.map +1 -0
  16. package/dist/harness/utils/read-folders/index.d.ts +27 -0
  17. package/dist/harness/utils/read-folders/index.d.ts.map +1 -0
  18. package/dist/harness/utils/read-folders/index.js +118 -0
  19. package/dist/harness/utils/read-folders/index.js.map +1 -0
  20. package/dist/harness/utils/read-folders/lexical-context.d.ts +21 -0
  21. package/dist/harness/utils/read-folders/lexical-context.d.ts.map +1 -0
  22. package/dist/harness/utils/read-folders/lexical-context.js +24 -0
  23. package/dist/harness/utils/read-folders/lexical-context.js.map +1 -0
  24. package/dist/harness/utils/read-folders/lexical-spans.d.ts +12 -0
  25. package/dist/harness/utils/read-folders/lexical-spans.d.ts.map +1 -0
  26. package/dist/harness/utils/read-folders/lexical-spans.js +92 -0
  27. package/dist/harness/utils/read-folders/lexical-spans.js.map +1 -0
  28. package/dist/harness/utils/read-folders/types.d.ts +47 -0
  29. package/dist/harness/utils/read-folders/types.d.ts.map +1 -0
  30. package/dist/harness/utils/read-folders/types.js +8 -0
  31. package/dist/harness/utils/read-folders/types.js.map +1 -0
  32. package/dist/harness/utils/segmented-read-view.d.ts +52 -0
  33. package/dist/harness/utils/segmented-read-view.d.ts.map +1 -0
  34. package/dist/harness/utils/segmented-read-view.js +155 -0
  35. package/dist/harness/utils/segmented-read-view.js.map +1 -0
  36. package/dist/index.d.ts +2 -0
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +2 -0
  39. package/dist/index.js.map +1 -1
  40. package/package.json +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"empty-assistant-recovery.d.ts","sourceRoot":"","sources":["../src/empty-assistant-recovery.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,GAAG,EAQR,KAAK,KAAK,EAEV,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAoH3C,wBAAgB,0BAA0B,CAAC,IAAI,SAAS,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,cAAc,EAAE,QAAQ,GAAG,QAAQ,CAMnH"}
1
+ {"version":3,"file":"empty-assistant-recovery.d.ts","sourceRoot":"","sources":["../src/empty-assistant-recovery.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,GAAG,EAaR,KAAK,KAAK,EAEV,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AA4K3C,wBAAgB,0BAA0B,CAAC,IAAI,SAAS,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,cAAc,EAAE,QAAQ,GAAG,QAAQ,CAOnH"}
@@ -1,31 +1,60 @@
1
- import { createAssistantMessageEventStream, getToolCallFormat, hasVisibleAssistantContent, hasVisibleText, shouldRecoverTextToolCalls, } from "@earendil-works/pi-ai";
2
- const EMPTY_RESPONSE_ERROR = "Model returned an empty response twice";
3
- const EMPTY_TOOL_USE_ERROR = "Model returned tool_use without a tool call twice";
1
+ import { createAssistantMessageEventStream, EMPTY_RESPONSE_ERROR, EMPTY_TOOL_USE_ERROR, FORWARDED_EMPTY_RESPONSE_ERROR, FORWARDED_EMPTY_TOOL_USE_ERROR, getToolCallFormat, hasKimiTextToolCallRecovery, hasVisibleAssistantContent, hasVisibleText, shouldRecoverTextToolCalls, } from "@earendil-works/pi-ai";
4
2
  function isEmptyStop(message) {
5
3
  return message.stopReason === "stop" && !hasVisibleAssistantContent(message);
6
4
  }
7
5
  function isEmptyToolUse(message) {
8
6
  return message.stopReason === "toolUse" && !message.content.some((block) => block.type === "toolCall");
9
7
  }
10
- function eventStartsVisibleContent(event) {
11
- return event.type === "toolcall_start" || (event.type === "text_delta" && hasVisibleText(event.delta));
8
+ /** Zero-width or whitespace-only deltas never commit, so format-only noise stays buffered. */
9
+ function isMeaningfulContentEvent(event, policy) {
10
+ switch (event.type) {
11
+ case "toolcall_start":
12
+ return true;
13
+ case "text_delta":
14
+ return hasVisibleText(event.delta);
15
+ case "text_end":
16
+ return hasVisibleText(event.content);
17
+ case "thinking_delta":
18
+ return policy.thinkingCommits && hasVisibleText(event.delta);
19
+ case "thinking_end":
20
+ return policy.thinkingCommits && hasVisibleText(event.content);
21
+ default:
22
+ return false;
23
+ }
12
24
  }
13
- function appendRetryDiagnostic(message, type = "empty_assistant_response_recovery") {
25
+ function appendRetryDiagnostic(message, type = "empty_assistant_response_recovery", details = { retries: 1 }) {
14
26
  return {
15
27
  ...message,
16
- diagnostics: [...(message.diagnostics ?? []), { type, timestamp: Date.now(), details: { retries: 1 } }],
28
+ diagnostics: [...(message.diagnostics ?? []), { type, timestamp: Date.now(), details }],
17
29
  };
18
30
  }
31
+ function recoveryDiagnosticType(toolUse) {
32
+ return toolUse ? "empty_tool_use_response_recovery" : "empty_assistant_response_recovery";
33
+ }
19
34
  function createEmptyResponseFailure(message, toolUse = false) {
20
35
  const errorMessage = toolUse ? EMPTY_TOOL_USE_ERROR : EMPTY_RESPONSE_ERROR;
21
36
  return {
22
- ...appendRetryDiagnostic(message, toolUse ? "empty_tool_use_response_recovery" : undefined),
37
+ ...appendRetryDiagnostic(message, recoveryDiagnosticType(toolUse)),
23
38
  content: [{ type: "text", text: errorMessage }],
24
39
  stopReason: "error",
25
40
  errorMessage,
26
41
  };
27
42
  }
28
- function createRetryingStream(firstStream, createStream) {
43
+ /**
44
+ * An attempt whose reasoning already streamed cannot be replayed here: a second `start` would
45
+ * duplicate the partial message, and stitching this attempt's thinking onto a retry's content
46
+ * would break provider replay of signed thinking blocks. The turn ends as a retryable error
47
+ * (see the classifier in pi-ai's retry.ts) and keeps the content the user already saw, so the
48
+ * session's turn retry re-requests it with the failed attempt dropped from the provider context.
49
+ */
50
+ function createForwardedEmptyFailure(message, toolUse) {
51
+ return {
52
+ ...appendRetryDiagnostic(message, recoveryDiagnosticType(toolUse), { retries: 0, forwarded: true }),
53
+ stopReason: "error",
54
+ errorMessage: toolUse ? FORWARDED_EMPTY_TOOL_USE_ERROR : FORWARDED_EMPTY_RESPONSE_ERROR,
55
+ };
56
+ }
57
+ function createRetryingStream(firstStream, createStream, policy) {
29
58
  const outerStream = createAssistantMessageEventStream();
30
59
  void (async () => {
31
60
  try {
@@ -39,6 +68,15 @@ function createRetryingStream(firstStream, createStream) {
39
68
  if (event.type === "done") {
40
69
  const emptyToolUse = isEmptyToolUse(event.message);
41
70
  if (isEmptyStop(event.message) || emptyToolUse) {
71
+ if (forwarding) {
72
+ outerStream.push({
73
+ type: "error",
74
+ reason: "error",
75
+ error: createForwardedEmptyFailure(event.message, emptyToolUse),
76
+ });
77
+ outerStream.end();
78
+ return;
79
+ }
42
80
  if (!retrying) {
43
81
  retry = true;
44
82
  break;
@@ -51,7 +89,7 @@ function createRetryingStream(firstStream, createStream) {
51
89
  const terminal = retrying
52
90
  ? {
53
91
  ...event,
54
- message: appendRetryDiagnostic(event.message, isEmptyToolUse(event.message) ? "empty_tool_use_response_recovery" : undefined),
92
+ message: appendRetryDiagnostic(event.message, recoveryDiagnosticType(isEmptyToolUse(event.message))),
55
93
  }
56
94
  : event;
57
95
  if (!forwarding) {
@@ -76,7 +114,7 @@ function createRetryingStream(firstStream, createStream) {
76
114
  continue;
77
115
  }
78
116
  buffered.push(event);
79
- if (eventStartsVisibleContent(event)) {
117
+ if (isMeaningfulContentEvent(event, policy)) {
80
118
  for (const pending of buffered)
81
119
  outerStream.push(pending);
82
120
  forwarding = true;
@@ -103,9 +141,10 @@ function createRetryingStream(firstStream, createStream) {
103
141
  export function withEmptyAssistantRecovery(model, streamFunction) {
104
142
  if (!shouldRecoverTextToolCalls(model) && getToolCallFormat(model) === undefined)
105
143
  return streamFunction;
144
+ const policy = { thinkingCommits: !hasKimiTextToolCallRecovery(model) };
106
145
  return async (requestedModel, context, options) => {
107
146
  const createStream = () => streamFunction(requestedModel, context, options);
108
- return createRetryingStream(await createStream(), createStream);
147
+ return createRetryingStream(await createStream(), createStream, policy);
109
148
  };
110
149
  }
111
150
  //# sourceMappingURL=empty-assistant-recovery.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"empty-assistant-recovery.js","sourceRoot":"","sources":["../src/empty-assistant-recovery.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\ttype Api,\n\ttype AssistantMessage,\n\ttype AssistantMessageEvent,\n\ttype AssistantMessageEventStream,\n\tcreateAssistantMessageEventStream,\n\tgetToolCallFormat,\n\thasVisibleAssistantContent,\n\thasVisibleText,\n\ttype Model,\n\tshouldRecoverTextToolCalls,\n} from \"@earendil-works/pi-ai\";\nimport type { StreamFn } from \"./types.ts\";\n\ntype StreamFactory = () => AssistantMessageEventStream | Promise<AssistantMessageEventStream>;\n\nconst EMPTY_RESPONSE_ERROR = \"Model returned an empty response twice\";\nconst EMPTY_TOOL_USE_ERROR = \"Model returned tool_use without a tool call twice\";\n\nfunction isEmptyStop(message: AssistantMessage): boolean {\n\treturn message.stopReason === \"stop\" && !hasVisibleAssistantContent(message);\n}\n\nfunction isEmptyToolUse(message: AssistantMessage): boolean {\n\treturn message.stopReason === \"toolUse\" && !message.content.some((block) => block.type === \"toolCall\");\n}\n\nfunction eventStartsVisibleContent(event: AssistantMessageEvent): boolean {\n\treturn event.type === \"toolcall_start\" || (event.type === \"text_delta\" && hasVisibleText(event.delta));\n}\n\nfunction appendRetryDiagnostic(\n\tmessage: AssistantMessage,\n\ttype = \"empty_assistant_response_recovery\",\n): AssistantMessage {\n\treturn {\n\t\t...message,\n\t\tdiagnostics: [...(message.diagnostics ?? []), { type, timestamp: Date.now(), details: { retries: 1 } }],\n\t};\n}\n\nfunction createEmptyResponseFailure(message: AssistantMessage, toolUse = false): AssistantMessage {\n\tconst errorMessage = toolUse ? EMPTY_TOOL_USE_ERROR : EMPTY_RESPONSE_ERROR;\n\treturn {\n\t\t...appendRetryDiagnostic(message, toolUse ? \"empty_tool_use_response_recovery\" : undefined),\n\t\tcontent: [{ type: \"text\", text: errorMessage }],\n\t\tstopReason: \"error\",\n\t\terrorMessage,\n\t};\n}\n\nfunction createRetryingStream(firstStream: AssistantMessageEventStream, createStream: StreamFactory) {\n\tconst outerStream = createAssistantMessageEventStream();\n\n\tvoid (async (): Promise<void> => {\n\t\ttry {\n\t\t\tlet stream = firstStream;\n\t\t\tlet retrying = false;\n\t\t\tfor (;;) {\n\t\t\t\tconst buffered: AssistantMessageEvent[] = [];\n\t\t\t\tlet forwarding = false;\n\t\t\t\tlet retry = false;\n\t\t\t\tfor await (const event of stream) {\n\t\t\t\t\tif (event.type === \"done\") {\n\t\t\t\t\t\tconst emptyToolUse = isEmptyToolUse(event.message);\n\t\t\t\t\t\tif (isEmptyStop(event.message) || emptyToolUse) {\n\t\t\t\t\t\t\tif (!retrying) {\n\t\t\t\t\t\t\t\tretry = true;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst error = createEmptyResponseFailure(event.message, emptyToolUse);\n\t\t\t\t\t\t\touterStream.push({ type: \"error\", reason: \"error\", error });\n\t\t\t\t\t\t\touterStream.end();\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst terminal = retrying\n\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\t...event,\n\t\t\t\t\t\t\t\t\tmessage: appendRetryDiagnostic(\n\t\t\t\t\t\t\t\t\t\tevent.message,\n\t\t\t\t\t\t\t\t\t\tisEmptyToolUse(event.message) ? \"empty_tool_use_response_recovery\" : undefined,\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t: event;\n\t\t\t\t\t\tif (!forwarding) {\n\t\t\t\t\t\t\tfor (const pending of buffered) outerStream.push(pending);\n\t\t\t\t\t\t}\n\t\t\t\t\t\touterStream.push(terminal);\n\t\t\t\t\t\touterStream.end();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (event.type === \"error\") {\n\t\t\t\t\t\tif (!forwarding) {\n\t\t\t\t\t\t\tfor (const pending of buffered) outerStream.push(pending);\n\t\t\t\t\t\t}\n\t\t\t\t\t\touterStream.push(event);\n\t\t\t\t\t\touterStream.end();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (forwarding) {\n\t\t\t\t\t\touterStream.push(event);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tbuffered.push(event);\n\t\t\t\t\tif (eventStartsVisibleContent(event)) {\n\t\t\t\t\t\tfor (const pending of buffered) outerStream.push(pending);\n\t\t\t\t\t\tforwarding = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!retry) {\n\t\t\t\t\touterStream.end(retrying ? appendRetryDiagnostic(await stream.result()) : await stream.result());\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tretrying = true;\n\t\t\t\tstream = await createStream();\n\t\t\t}\n\t\t} catch (error) {\n\t\t\touterStream.fail(error);\n\t\t}\n\t})();\n\n\treturn outerStream;\n}\n\n// Wrapping replaces the provider stream with a buffering proxy, which does not carry the\n// underlying stream's liveness surface (trackLocalWork/hasPendingLocalWork) that the loop's\n// idle watchdog reads. Only wrap models that actually need stream-level recovery; the\n// empty-tool_use contradiction is normalized for every model by the agent loop instead.\nexport function withEmptyAssistantRecovery<TApi extends Api>(model: Model<TApi>, streamFunction: StreamFn): StreamFn {\n\tif (!shouldRecoverTextToolCalls(model) && getToolCallFormat(model) === undefined) return streamFunction;\n\treturn async (requestedModel, context, options) => {\n\t\tconst createStream = (): ReturnType<StreamFn> => streamFunction(requestedModel, context, options);\n\t\treturn createRetryingStream(await createStream(), createStream);\n\t};\n}\n"]}
1
+ {"version":3,"file":"empty-assistant-recovery.js","sourceRoot":"","sources":["../src/empty-assistant-recovery.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\ttype Api,\n\ttype AssistantMessage,\n\ttype AssistantMessageEvent,\n\ttype AssistantMessageEventStream,\n\tcreateAssistantMessageEventStream,\n\tEMPTY_RESPONSE_ERROR,\n\tEMPTY_TOOL_USE_ERROR,\n\tFORWARDED_EMPTY_RESPONSE_ERROR,\n\tFORWARDED_EMPTY_TOOL_USE_ERROR,\n\tgetToolCallFormat,\n\thasKimiTextToolCallRecovery,\n\thasVisibleAssistantContent,\n\thasVisibleText,\n\ttype Model,\n\tshouldRecoverTextToolCalls,\n} from \"@earendil-works/pi-ai\";\nimport type { StreamFn } from \"./types.ts\";\n\ntype StreamFactory = () => AssistantMessageEventStream | Promise<AssistantMessageEventStream>;\n\nfunction isEmptyStop(message: AssistantMessage): boolean {\n\treturn message.stopReason === \"stop\" && !hasVisibleAssistantContent(message);\n}\n\nfunction isEmptyToolUse(message: AssistantMessage): boolean {\n\treturn message.stopReason === \"toolUse\" && !message.content.some((block) => block.type === \"toolCall\");\n}\n\ninterface CommitPolicy {\n\t/**\n\t * Whether reasoning commits the attempt. True for models whose thinking channel is native\n\t * (Claude, antml/other text formats): a reasoning model shows its work for seconds before the\n\t * first text or tool call, and holding that back blanks the transcript for the whole phase.\n\t * False for the Kimi XTML lane: its thinking channel is the documented misrouting vector for\n\t * text tool calls and `recoverKimiXtmlThinking` only rewrites the finished message, so a\n\t * leaked protocol fragment forwarded live could never be retracted (#759).\n\t */\n\treadonly thinkingCommits: boolean;\n}\n\n/** Zero-width or whitespace-only deltas never commit, so format-only noise stays buffered. */\nfunction isMeaningfulContentEvent(event: AssistantMessageEvent, policy: CommitPolicy): boolean {\n\tswitch (event.type) {\n\t\tcase \"toolcall_start\":\n\t\t\treturn true;\n\t\tcase \"text_delta\":\n\t\t\treturn hasVisibleText(event.delta);\n\t\tcase \"text_end\":\n\t\t\treturn hasVisibleText(event.content);\n\t\tcase \"thinking_delta\":\n\t\t\treturn policy.thinkingCommits && hasVisibleText(event.delta);\n\t\tcase \"thinking_end\":\n\t\t\treturn policy.thinkingCommits && hasVisibleText(event.content);\n\t\tdefault:\n\t\t\treturn false;\n\t}\n}\n\nfunction appendRetryDiagnostic(\n\tmessage: AssistantMessage,\n\ttype = \"empty_assistant_response_recovery\",\n\tdetails: Record<string, unknown> = { retries: 1 },\n): AssistantMessage {\n\treturn {\n\t\t...message,\n\t\tdiagnostics: [...(message.diagnostics ?? []), { type, timestamp: Date.now(), details }],\n\t};\n}\n\nfunction recoveryDiagnosticType(toolUse: boolean): string {\n\treturn toolUse ? \"empty_tool_use_response_recovery\" : \"empty_assistant_response_recovery\";\n}\n\nfunction createEmptyResponseFailure(message: AssistantMessage, toolUse = false): AssistantMessage {\n\tconst errorMessage = toolUse ? EMPTY_TOOL_USE_ERROR : EMPTY_RESPONSE_ERROR;\n\treturn {\n\t\t...appendRetryDiagnostic(message, recoveryDiagnosticType(toolUse)),\n\t\tcontent: [{ type: \"text\", text: errorMessage }],\n\t\tstopReason: \"error\",\n\t\terrorMessage,\n\t};\n}\n\n/**\n * An attempt whose reasoning already streamed cannot be replayed here: a second `start` would\n * duplicate the partial message, and stitching this attempt's thinking onto a retry's content\n * would break provider replay of signed thinking blocks. The turn ends as a retryable error\n * (see the classifier in pi-ai's retry.ts) and keeps the content the user already saw, so the\n * session's turn retry re-requests it with the failed attempt dropped from the provider context.\n */\nfunction createForwardedEmptyFailure(message: AssistantMessage, toolUse: boolean): AssistantMessage {\n\treturn {\n\t\t...appendRetryDiagnostic(message, recoveryDiagnosticType(toolUse), { retries: 0, forwarded: true }),\n\t\tstopReason: \"error\",\n\t\terrorMessage: toolUse ? FORWARDED_EMPTY_TOOL_USE_ERROR : FORWARDED_EMPTY_RESPONSE_ERROR,\n\t};\n}\n\nfunction createRetryingStream(\n\tfirstStream: AssistantMessageEventStream,\n\tcreateStream: StreamFactory,\n\tpolicy: CommitPolicy,\n) {\n\tconst outerStream = createAssistantMessageEventStream();\n\n\tvoid (async (): Promise<void> => {\n\t\ttry {\n\t\t\tlet stream = firstStream;\n\t\t\tlet retrying = false;\n\t\t\tfor (;;) {\n\t\t\t\tconst buffered: AssistantMessageEvent[] = [];\n\t\t\t\tlet forwarding = false;\n\t\t\t\tlet retry = false;\n\t\t\t\tfor await (const event of stream) {\n\t\t\t\t\tif (event.type === \"done\") {\n\t\t\t\t\t\tconst emptyToolUse = isEmptyToolUse(event.message);\n\t\t\t\t\t\tif (isEmptyStop(event.message) || emptyToolUse) {\n\t\t\t\t\t\t\tif (forwarding) {\n\t\t\t\t\t\t\t\touterStream.push({\n\t\t\t\t\t\t\t\t\ttype: \"error\",\n\t\t\t\t\t\t\t\t\treason: \"error\",\n\t\t\t\t\t\t\t\t\terror: createForwardedEmptyFailure(event.message, emptyToolUse),\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\touterStream.end();\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (!retrying) {\n\t\t\t\t\t\t\t\tretry = true;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst error = createEmptyResponseFailure(event.message, emptyToolUse);\n\t\t\t\t\t\t\touterStream.push({ type: \"error\", reason: \"error\", error });\n\t\t\t\t\t\t\touterStream.end();\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst terminal = retrying\n\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\t...event,\n\t\t\t\t\t\t\t\t\tmessage: appendRetryDiagnostic(\n\t\t\t\t\t\t\t\t\t\tevent.message,\n\t\t\t\t\t\t\t\t\t\trecoveryDiagnosticType(isEmptyToolUse(event.message)),\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t: event;\n\t\t\t\t\t\tif (!forwarding) {\n\t\t\t\t\t\t\tfor (const pending of buffered) outerStream.push(pending);\n\t\t\t\t\t\t}\n\t\t\t\t\t\touterStream.push(terminal);\n\t\t\t\t\t\touterStream.end();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (event.type === \"error\") {\n\t\t\t\t\t\tif (!forwarding) {\n\t\t\t\t\t\t\tfor (const pending of buffered) outerStream.push(pending);\n\t\t\t\t\t\t}\n\t\t\t\t\t\touterStream.push(event);\n\t\t\t\t\t\touterStream.end();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (forwarding) {\n\t\t\t\t\t\touterStream.push(event);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tbuffered.push(event);\n\t\t\t\t\tif (isMeaningfulContentEvent(event, policy)) {\n\t\t\t\t\t\tfor (const pending of buffered) outerStream.push(pending);\n\t\t\t\t\t\tforwarding = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!retry) {\n\t\t\t\t\touterStream.end(retrying ? appendRetryDiagnostic(await stream.result()) : await stream.result());\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tretrying = true;\n\t\t\t\tstream = await createStream();\n\t\t\t}\n\t\t} catch (error) {\n\t\t\touterStream.fail(error);\n\t\t}\n\t})();\n\n\treturn outerStream;\n}\n\n// Wrapping replaces the provider stream with a buffering proxy, which does not carry the\n// underlying stream's liveness surface (trackLocalWork/hasPendingLocalWork) that the loop's\n// idle watchdog reads. Only wrap models that actually need stream-level recovery; the\n// empty-tool_use contradiction is normalized for every model by the agent loop instead.\nexport function withEmptyAssistantRecovery<TApi extends Api>(model: Model<TApi>, streamFunction: StreamFn): StreamFn {\n\tif (!shouldRecoverTextToolCalls(model) && getToolCallFormat(model) === undefined) return streamFunction;\n\tconst policy: CommitPolicy = { thinkingCommits: !hasKimiTextToolCallRecovery(model) };\n\treturn async (requestedModel, context, options) => {\n\t\tconst createStream = (): ReturnType<StreamFn> => streamFunction(requestedModel, context, options);\n\t\treturn createRetryingStream(await createStream(), createStream, policy);\n\t};\n}\n"]}
@@ -1,6 +1,7 @@
1
1
  import { type Static, Type } from "typebox";
2
2
  import type { Context } from "../context.ts";
3
3
  import type { AgentHarnessTool } from "../types.ts";
4
+ import { type ReadFolder } from "../utils/read-folders/index.ts";
4
5
  import { type TruncationResult } from "../utils/truncate.ts";
5
6
  import type { ExecutionToolContext } from "./tool-context.ts";
6
7
  declare const readSchema: Type.TObject<{
@@ -29,6 +30,8 @@ export interface ReadToolOptions {
29
30
  autoResizeImages?: boolean;
30
31
  /** Optional image conversion/resizing implementation. */
31
32
  imageProcessor?: ReadImageProcessor;
33
+ /** Structural folder. Default options select the measured folder; omit here for verbatim reads. */
34
+ folder?: ReadFolder;
32
35
  }
33
36
  export declare function createReadTool<TContext extends ExecutionToolContext = ExecutionToolContext>(options?: ReadToolOptions): AgentHarnessTool<TContext, typeof readSchema, ReadToolDetails | undefined>;
34
37
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"read.d.ts","sourceRoot":"","sources":["../../../src/harness/tools/read.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD,OAAO,EAIN,KAAK,gBAAgB,EAErB,MAAM,sBAAsB,CAAC;AAG9B,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAE9D,QAAA,MAAM,UAAU;;;;EAId,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO,UAAU,CAAC,CAAC;AAEtD,MAAM,WAAW,eAAe;IAC/B,UAAU,CAAC,EAAE,gBAAgB,CAAC;CAC9B;AAED,MAAM,MAAM,wBAAwB,GACjC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,GAC7D;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAElC,MAAM,MAAM,kBAAkB,GAAG,CAChC,KAAK,EAAE,UAAU,EACjB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE;IAAE,gBAAgB,EAAE,OAAO,CAAA;CAAE,EACtC,OAAO,EAAE,OAAO,KACZ,OAAO,CAAC,wBAAwB,CAAC,CAAC;AAEvC,MAAM,WAAW,eAAe;IAC/B,+EAA+E;IAC/E,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,yDAAyD;IACzD,cAAc,CAAC,EAAE,kBAAkB,CAAC;CACpC;AAED,wBAAgB,cAAc,CAAC,QAAQ,SAAS,oBAAoB,GAAG,oBAAoB,EAC1F,OAAO,CAAC,EAAE,eAAe,GACvB,gBAAgB,CAAC,QAAQ,EAAE,OAAO,UAAU,EAAE,eAAe,GAAG,SAAS,CAAC,CAoG5E"}
1
+ {"version":3,"file":"read.d.ts","sourceRoot":"","sources":["../../../src/harness/tools/read.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD,OAAO,EAAE,KAAK,UAAU,EAAsB,MAAM,gCAAgC,CAAC;AAErF,OAAO,EAIN,KAAK,gBAAgB,EAErB,MAAM,sBAAsB,CAAC;AAG9B,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAE9D,QAAA,MAAM,UAAU;;;;EAId,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO,UAAU,CAAC,CAAC;AAEtD,MAAM,WAAW,eAAe;IAC/B,UAAU,CAAC,EAAE,gBAAgB,CAAC;CAC9B;AAED,MAAM,MAAM,wBAAwB,GACjC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,GAC7D;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAElC,MAAM,MAAM,kBAAkB,GAAG,CAChC,KAAK,EAAE,UAAU,EACjB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE;IAAE,gBAAgB,EAAE,OAAO,CAAA;CAAE,EACtC,OAAO,EAAE,OAAO,KACZ,OAAO,CAAC,wBAAwB,CAAC,CAAC;AAEvC,MAAM,WAAW,eAAe;IAC/B,+EAA+E;IAC/E,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,yDAAyD;IACzD,cAAc,CAAC,EAAE,kBAAkB,CAAC;IACpC,mGAAmG;IACnG,MAAM,CAAC,EAAE,UAAU,CAAC;CACpB;AAED,wBAAgB,cAAc,CAAC,QAAQ,SAAS,oBAAoB,GAAG,oBAAoB,EAC1F,OAAO,GAAE,eAAgD,GACvD,gBAAgB,CAAC,QAAQ,EAAE,OAAO,UAAU,EAAE,eAAe,GAAG,SAAS,CAAC,CA+G5E"}
@@ -1,5 +1,7 @@
1
1
  import { Type } from "typebox";
2
- import { getOrThrow } from "../types.js";
2
+ import { FileError, getOrThrow } from "../types.js";
3
+ import { selectedReadFolder } from "../utils/read-folders/index.js";
4
+ import { createDefaultReadSummary } from "../utils/segmented-read-view.js";
3
5
  import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize, truncateHead, } from "../utils/truncate.js";
4
6
  import { detectSupportedImageMimeType, encodeBase64 } from "./image.js";
5
7
  import { resolveReadToolPath } from "./path-utils.js";
@@ -8,7 +10,7 @@ const readSchema = Type.Object({
8
10
  offset: Type.Optional(Type.Number({ description: "Line number to start reading from (1-indexed)" })),
9
11
  limit: Type.Optional(Type.Number({ description: "Maximum number of lines to read" })),
10
12
  });
11
- export function createReadTool(options) {
13
+ export function createReadTool(options = { folder: selectedReadFolder }) {
12
14
  return {
13
15
  name: "read",
14
16
  label: "read",
@@ -17,6 +19,8 @@ export function createReadTool(options) {
17
19
  async execute(_toolCallId, { path, offset, limit }, _onUpdate, { env }, _invocation, context) {
18
20
  const absolutePath = await resolveReadToolPath(env, path, context);
19
21
  const bytes = getOrThrow(await env.readBinaryFile(absolutePath, context));
22
+ if (context.abortSignal?.aborted)
23
+ throw new FileError("aborted", "Operation aborted", absolutePath);
20
24
  const mimeType = detectSupportedImageMimeType(bytes);
21
25
  if (mimeType) {
22
26
  if (options?.imageProcessor) {
@@ -74,9 +78,20 @@ export function createReadTool(options) {
74
78
  selectedContent = allLines.slice(startLine).join("\n");
75
79
  }
76
80
  const truncation = truncateHead(selectedContent);
81
+ const summary = createDefaultReadSummary({
82
+ path: absolutePath,
83
+ text: textContent,
84
+ offset,
85
+ limit,
86
+ folder: options.folder,
87
+ truncated: truncation.truncated,
88
+ });
77
89
  let outputText;
78
90
  let details;
79
- if (truncation.firstLineExceedsLimit) {
91
+ if (summary) {
92
+ outputText = summary.text;
93
+ }
94
+ else if (truncation.firstLineExceedsLimit) {
80
95
  const firstLineSize = formatSize(new TextEncoder().encode(allLines[startLine]).byteLength);
81
96
  outputText = `[Line ${startLineDisplay} is ${firstLineSize}, exceeds ${formatSize(DEFAULT_MAX_BYTES)} limit. Use bash: sed -n '${startLineDisplay}p' ${path} | head -c ${DEFAULT_MAX_BYTES}]`;
82
97
  details = { truncation };
@@ -1 +1 @@
1
- {"version":3,"file":"read.js","sourceRoot":"","sources":["../../../src/harness/tools/read.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ImageContent, TextContent } from \"@earendil-works/pi-ai\";\nimport { type Static, Type } from \"typebox\";\nimport type { Context } from \"../context.ts\";\nimport type { AgentHarnessTool } from \"../types.ts\";\nimport { getOrThrow } from \"../types.ts\";\nimport {\n\tDEFAULT_MAX_BYTES,\n\tDEFAULT_MAX_LINES,\n\tformatSize,\n\ttype TruncationResult,\n\ttruncateHead,\n} from \"../utils/truncate.ts\";\nimport { detectSupportedImageMimeType, encodeBase64 } from \"./image.ts\";\nimport { resolveReadToolPath } from \"./path-utils.ts\";\nimport type { ExecutionToolContext } from \"./tool-context.ts\";\n\nconst readSchema = Type.Object({\n\tpath: Type.String({ description: \"Path to the file to read (relative or absolute)\" }),\n\toffset: Type.Optional(Type.Number({ description: \"Line number to start reading from (1-indexed)\" })),\n\tlimit: Type.Optional(Type.Number({ description: \"Maximum number of lines to read\" })),\n});\n\nexport type ReadToolInput = Static<typeof readSchema>;\n\nexport interface ReadToolDetails {\n\ttruncation?: TruncationResult;\n}\n\nexport type ReadImageProcessorResult =\n\t| { ok: true; data: string; mimeType: string; hints: string[] }\n\t| { ok: false; message: string };\n\nexport type ReadImageProcessor = (\n\tbytes: Uint8Array,\n\tmimeType: string,\n\toptions: { autoResizeImages: boolean },\n\tcontext: Context,\n) => Promise<ReadImageProcessorResult>;\n\nexport interface ReadToolOptions {\n\t/** Whether an injected image processor should resize images. Default: true. */\n\tautoResizeImages?: boolean;\n\t/** Optional image conversion/resizing implementation. */\n\timageProcessor?: ReadImageProcessor;\n}\n\nexport function createReadTool<TContext extends ExecutionToolContext = ExecutionToolContext>(\n\toptions?: ReadToolOptions,\n): AgentHarnessTool<TContext, typeof readSchema, ReadToolDetails | undefined> {\n\treturn {\n\t\tname: \"read\",\n\t\tlabel: \"read\",\n\t\tdescription: `Read the contents of a file. Supports text files and images (jpg, png, gif, webp, bmp). Images are sent as attachments. For text files, output is truncated to ${DEFAULT_MAX_LINES} lines or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first). Use offset/limit for large files. When you need the full file, continue with offset until complete.`,\n\t\tparameters: readSchema,\n\t\tasync execute(_toolCallId, { path, offset, limit }, _onUpdate, { env }, _invocation, context) {\n\t\t\tconst absolutePath = await resolveReadToolPath(env, path, context);\n\t\t\tconst bytes = getOrThrow(await env.readBinaryFile(absolutePath, context));\n\t\t\tconst mimeType = detectSupportedImageMimeType(bytes);\n\t\t\tif (mimeType) {\n\t\t\t\tif (options?.imageProcessor) {\n\t\t\t\t\tconst processed = await options.imageProcessor(\n\t\t\t\t\t\tbytes,\n\t\t\t\t\t\tmimeType,\n\t\t\t\t\t\t{ autoResizeImages: options.autoResizeImages ?? true },\n\t\t\t\t\t\tcontext,\n\t\t\t\t\t);\n\t\t\t\t\tif (!processed.ok) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tcontent: [{ type: \"text\", text: `Read image file [${mimeType}]\\n${processed.message}` }],\n\t\t\t\t\t\t\tdetails: undefined,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\tconst hints = processed.hints.length > 0 ? `\\n${processed.hints.join(\"\\n\")}` : \"\";\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t{ type: \"text\", text: `Read image file [${processed.mimeType}]${hints}` },\n\t\t\t\t\t\t\t{ type: \"image\", data: processed.data, mimeType: processed.mimeType },\n\t\t\t\t\t\t] satisfies Array<TextContent | ImageContent>,\n\t\t\t\t\t\tdetails: undefined,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\tif (mimeType === \"image/bmp\") {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\ttext: \"Read image file [image/bmp]\\n[Image omitted: configure an imageProcessor to convert BMP images.]\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t\tdetails: undefined,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{ type: \"text\", text: `Read image file [${mimeType}]` },\n\t\t\t\t\t\t{ type: \"image\", data: encodeBase64(bytes), mimeType },\n\t\t\t\t\t] satisfies Array<TextContent | ImageContent>,\n\t\t\t\t\tdetails: undefined,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst textContent = new TextDecoder().decode(bytes);\n\t\t\tconst allLines = textContent.split(\"\\n\");\n\t\t\tconst totalFileLines = allLines.length;\n\t\t\tconst startLine = offset ? Math.max(0, offset - 1) : 0;\n\t\t\tconst startLineDisplay = startLine + 1;\n\t\t\tif (startLine >= allLines.length) {\n\t\t\t\tthrow new Error(`Offset ${offset} is beyond end of file (${allLines.length} lines total)`);\n\t\t\t}\n\n\t\t\tlet selectedContent: string;\n\t\t\tlet userLimitedLines: number | undefined;\n\t\t\tif (limit !== undefined) {\n\t\t\t\tconst endLine = Math.min(startLine + limit, allLines.length);\n\t\t\t\tselectedContent = allLines.slice(startLine, endLine).join(\"\\n\");\n\t\t\t\tuserLimitedLines = endLine - startLine;\n\t\t\t} else {\n\t\t\t\tselectedContent = allLines.slice(startLine).join(\"\\n\");\n\t\t\t}\n\n\t\t\tconst truncation = truncateHead(selectedContent);\n\t\t\tlet outputText: string;\n\t\t\tlet details: ReadToolDetails | undefined;\n\t\t\tif (truncation.firstLineExceedsLimit) {\n\t\t\t\tconst firstLineSize = formatSize(new TextEncoder().encode(allLines[startLine]).byteLength);\n\t\t\t\toutputText = `[Line ${startLineDisplay} is ${firstLineSize}, exceeds ${formatSize(DEFAULT_MAX_BYTES)} limit. Use bash: sed -n '${startLineDisplay}p' ${path} | head -c ${DEFAULT_MAX_BYTES}]`;\n\t\t\t\tdetails = { truncation };\n\t\t\t} else if (truncation.truncated) {\n\t\t\t\tconst endLineDisplay = startLineDisplay + truncation.outputLines - 1;\n\t\t\t\tconst nextOffset = endLineDisplay + 1;\n\t\t\t\toutputText = truncation.content;\n\t\t\t\tif (truncation.truncatedBy === \"lines\") {\n\t\t\t\t\toutputText += `\\n\\n[Showing lines ${startLineDisplay}-${endLineDisplay} of ${totalFileLines}. Use offset=${nextOffset} to continue.]`;\n\t\t\t\t} else {\n\t\t\t\t\toutputText += `\\n\\n[Showing lines ${startLineDisplay}-${endLineDisplay} of ${totalFileLines} (${formatSize(DEFAULT_MAX_BYTES)} limit). Use offset=${nextOffset} to continue.]`;\n\t\t\t\t}\n\t\t\t\tdetails = { truncation };\n\t\t\t} else if (userLimitedLines !== undefined && startLine + userLimitedLines < allLines.length) {\n\t\t\t\tconst remaining = allLines.length - (startLine + userLimitedLines);\n\t\t\t\tconst nextOffset = startLine + userLimitedLines + 1;\n\t\t\t\toutputText = `${truncation.content}\\n\\n[${remaining} more lines in file. Use offset=${nextOffset} to continue.]`;\n\t\t\t} else {\n\t\t\t\toutputText = truncation.content;\n\t\t\t}\n\n\t\t\treturn { content: [{ type: \"text\", text: outputText }], details };\n\t\t},\n\t};\n}\n"]}
1
+ {"version":3,"file":"read.js","sourceRoot":"","sources":["../../../src/harness/tools/read.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ImageContent, TextContent } from \"@earendil-works/pi-ai\";\nimport { type Static, Type } from \"typebox\";\nimport type { Context } from \"../context.ts\";\nimport type { AgentHarnessTool } from \"../types.ts\";\nimport { FileError, getOrThrow } from \"../types.ts\";\nimport { type ReadFolder, selectedReadFolder } from \"../utils/read-folders/index.ts\";\nimport { createDefaultReadSummary } from \"../utils/segmented-read-view.ts\";\nimport {\n\tDEFAULT_MAX_BYTES,\n\tDEFAULT_MAX_LINES,\n\tformatSize,\n\ttype TruncationResult,\n\ttruncateHead,\n} from \"../utils/truncate.ts\";\nimport { detectSupportedImageMimeType, encodeBase64 } from \"./image.ts\";\nimport { resolveReadToolPath } from \"./path-utils.ts\";\nimport type { ExecutionToolContext } from \"./tool-context.ts\";\n\nconst readSchema = Type.Object({\n\tpath: Type.String({ description: \"Path to the file to read (relative or absolute)\" }),\n\toffset: Type.Optional(Type.Number({ description: \"Line number to start reading from (1-indexed)\" })),\n\tlimit: Type.Optional(Type.Number({ description: \"Maximum number of lines to read\" })),\n});\n\nexport type ReadToolInput = Static<typeof readSchema>;\n\nexport interface ReadToolDetails {\n\ttruncation?: TruncationResult;\n}\n\nexport type ReadImageProcessorResult =\n\t| { ok: true; data: string; mimeType: string; hints: string[] }\n\t| { ok: false; message: string };\n\nexport type ReadImageProcessor = (\n\tbytes: Uint8Array,\n\tmimeType: string,\n\toptions: { autoResizeImages: boolean },\n\tcontext: Context,\n) => Promise<ReadImageProcessorResult>;\n\nexport interface ReadToolOptions {\n\t/** Whether an injected image processor should resize images. Default: true. */\n\tautoResizeImages?: boolean;\n\t/** Optional image conversion/resizing implementation. */\n\timageProcessor?: ReadImageProcessor;\n\t/** Structural folder. Default options select the measured folder; omit here for verbatim reads. */\n\tfolder?: ReadFolder;\n}\n\nexport function createReadTool<TContext extends ExecutionToolContext = ExecutionToolContext>(\n\toptions: ReadToolOptions = { folder: selectedReadFolder },\n): AgentHarnessTool<TContext, typeof readSchema, ReadToolDetails | undefined> {\n\treturn {\n\t\tname: \"read\",\n\t\tlabel: \"read\",\n\t\tdescription: `Read the contents of a file. Supports text files and images (jpg, png, gif, webp, bmp). Images are sent as attachments. For text files, output is truncated to ${DEFAULT_MAX_LINES} lines or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first). Use offset/limit for large files. When you need the full file, continue with offset until complete.`,\n\t\tparameters: readSchema,\n\t\tasync execute(_toolCallId, { path, offset, limit }, _onUpdate, { env }, _invocation, context) {\n\t\t\tconst absolutePath = await resolveReadToolPath(env, path, context);\n\t\t\tconst bytes = getOrThrow(await env.readBinaryFile(absolutePath, context));\n\t\t\tif (context.abortSignal?.aborted) throw new FileError(\"aborted\", \"Operation aborted\", absolutePath);\n\t\t\tconst mimeType = detectSupportedImageMimeType(bytes);\n\t\t\tif (mimeType) {\n\t\t\t\tif (options?.imageProcessor) {\n\t\t\t\t\tconst processed = await options.imageProcessor(\n\t\t\t\t\t\tbytes,\n\t\t\t\t\t\tmimeType,\n\t\t\t\t\t\t{ autoResizeImages: options.autoResizeImages ?? true },\n\t\t\t\t\t\tcontext,\n\t\t\t\t\t);\n\t\t\t\t\tif (!processed.ok) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tcontent: [{ type: \"text\", text: `Read image file [${mimeType}]\\n${processed.message}` }],\n\t\t\t\t\t\t\tdetails: undefined,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\tconst hints = processed.hints.length > 0 ? `\\n${processed.hints.join(\"\\n\")}` : \"\";\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t{ type: \"text\", text: `Read image file [${processed.mimeType}]${hints}` },\n\t\t\t\t\t\t\t{ type: \"image\", data: processed.data, mimeType: processed.mimeType },\n\t\t\t\t\t\t] satisfies Array<TextContent | ImageContent>,\n\t\t\t\t\t\tdetails: undefined,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\tif (mimeType === \"image/bmp\") {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\ttext: \"Read image file [image/bmp]\\n[Image omitted: configure an imageProcessor to convert BMP images.]\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t\tdetails: undefined,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{ type: \"text\", text: `Read image file [${mimeType}]` },\n\t\t\t\t\t\t{ type: \"image\", data: encodeBase64(bytes), mimeType },\n\t\t\t\t\t] satisfies Array<TextContent | ImageContent>,\n\t\t\t\t\tdetails: undefined,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst textContent = new TextDecoder().decode(bytes);\n\t\t\tconst allLines = textContent.split(\"\\n\");\n\t\t\tconst totalFileLines = allLines.length;\n\t\t\tconst startLine = offset ? Math.max(0, offset - 1) : 0;\n\t\t\tconst startLineDisplay = startLine + 1;\n\t\t\tif (startLine >= allLines.length) {\n\t\t\t\tthrow new Error(`Offset ${offset} is beyond end of file (${allLines.length} lines total)`);\n\t\t\t}\n\n\t\t\tlet selectedContent: string;\n\t\t\tlet userLimitedLines: number | undefined;\n\t\t\tif (limit !== undefined) {\n\t\t\t\tconst endLine = Math.min(startLine + limit, allLines.length);\n\t\t\t\tselectedContent = allLines.slice(startLine, endLine).join(\"\\n\");\n\t\t\t\tuserLimitedLines = endLine - startLine;\n\t\t\t} else {\n\t\t\t\tselectedContent = allLines.slice(startLine).join(\"\\n\");\n\t\t\t}\n\n\t\t\tconst truncation = truncateHead(selectedContent);\n\t\t\tconst summary = createDefaultReadSummary({\n\t\t\t\tpath: absolutePath,\n\t\t\t\ttext: textContent,\n\t\t\t\toffset,\n\t\t\t\tlimit,\n\t\t\t\tfolder: options.folder,\n\t\t\t\ttruncated: truncation.truncated,\n\t\t\t});\n\t\t\tlet outputText: string;\n\t\t\tlet details: ReadToolDetails | undefined;\n\t\t\tif (summary) {\n\t\t\t\toutputText = summary.text;\n\t\t\t} else if (truncation.firstLineExceedsLimit) {\n\t\t\t\tconst firstLineSize = formatSize(new TextEncoder().encode(allLines[startLine]).byteLength);\n\t\t\t\toutputText = `[Line ${startLineDisplay} is ${firstLineSize}, exceeds ${formatSize(DEFAULT_MAX_BYTES)} limit. Use bash: sed -n '${startLineDisplay}p' ${path} | head -c ${DEFAULT_MAX_BYTES}]`;\n\t\t\t\tdetails = { truncation };\n\t\t\t} else if (truncation.truncated) {\n\t\t\t\tconst endLineDisplay = startLineDisplay + truncation.outputLines - 1;\n\t\t\t\tconst nextOffset = endLineDisplay + 1;\n\t\t\t\toutputText = truncation.content;\n\t\t\t\tif (truncation.truncatedBy === \"lines\") {\n\t\t\t\t\toutputText += `\\n\\n[Showing lines ${startLineDisplay}-${endLineDisplay} of ${totalFileLines}. Use offset=${nextOffset} to continue.]`;\n\t\t\t\t} else {\n\t\t\t\t\toutputText += `\\n\\n[Showing lines ${startLineDisplay}-${endLineDisplay} of ${totalFileLines} (${formatSize(DEFAULT_MAX_BYTES)} limit). Use offset=${nextOffset} to continue.]`;\n\t\t\t\t}\n\t\t\t\tdetails = { truncation };\n\t\t\t} else if (userLimitedLines !== undefined && startLine + userLimitedLines < allLines.length) {\n\t\t\t\tconst remaining = allLines.length - (startLine + userLimitedLines);\n\t\t\t\tconst nextOffset = startLine + userLimitedLines + 1;\n\t\t\t\toutputText = `${truncation.content}\\n\\n[${remaining} more lines in file. Use offset=${nextOffset} to continue.]`;\n\t\t\t} else {\n\t\t\t\toutputText = truncation.content;\n\t\t\t}\n\n\t\t\treturn { content: [{ type: \"text\", text: outputText }], details };\n\t\t},\n\t};\n}\n"]}
@@ -0,0 +1,4 @@
1
+ import type { ReadBraceScan, ReadFoldSettings } from "./types.ts";
2
+ /** Measured row-17 brace lexer, restricted to the three selected languages. */
3
+ export declare function scanBraces(source: string, language: "ts" | "js" | "json", settings: ReadFoldSettings): ReadBraceScan;
4
+ //# sourceMappingURL=brace-scanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brace-scanner.d.ts","sourceRoot":"","sources":["../../../../src/harness/utils/read-folders/brace-scanner.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAiB,MAAM,YAAY,CAAC;AAEjF,+EAA+E;AAC/E,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,EAAE,QAAQ,EAAE,gBAAgB,GAAG,aAAa,CAqPpH"}
@@ -0,0 +1,268 @@
1
+ import { HeaderProtection } from "./header-protection.js";
2
+ import { controls, expressionKeywords, isCallCallee, signatureDeclarations } from "./lexical-context.js";
3
+ import { commentSpan, lineCommentEnd, regexSpan, stringSpan, typeArgumentsSpan } from "./lexical-spans.js";
4
+ /** Measured row-17 brace lexer, restricted to the three selected languages. */
5
+ export function scanBraces(source, language, settings) {
6
+ const ranges = [];
7
+ const stack = [];
8
+ const headers = new HeaderProtection();
9
+ const fail = (reason) => ({ status: "parse_failure", reason });
10
+ let line = 1;
11
+ let i = 0;
12
+ let template = false;
13
+ let templateDepth = 0;
14
+ let previous = "";
15
+ let beforeWord = "";
16
+ let wordLine = 1;
17
+ let valueArrow = false;
18
+ let expressionEnd = false;
19
+ let importClause = false;
20
+ let ambiguousAngleDepth;
21
+ let signatureDeclaration = false;
22
+ if (source.startsWith("#!")) {
23
+ const end = source.indexOf("\n");
24
+ i = end < 0 ? source.length : end;
25
+ }
26
+ while (i < source.length) {
27
+ const char = source[i];
28
+ const next = source[i + 1];
29
+ if (char === "\n")
30
+ line++;
31
+ if (template) {
32
+ if (char === "\\") {
33
+ if (next === "\n")
34
+ line++;
35
+ i += 2;
36
+ continue;
37
+ }
38
+ if (char === "`") {
39
+ template = false;
40
+ templateDepth--;
41
+ expressionEnd = true;
42
+ previous = "literal";
43
+ }
44
+ else if (char === "$" && next === "{") {
45
+ stack.push({
46
+ char: "{",
47
+ line,
48
+ foldable: false,
49
+ protected: true,
50
+ signature: false,
51
+ interpolation: true,
52
+ control: false,
53
+ call: false,
54
+ valueParameters: false,
55
+ declaration: false,
56
+ });
57
+ template = false;
58
+ expressionEnd = false;
59
+ previous = "{";
60
+ i += 2;
61
+ continue;
62
+ }
63
+ i++;
64
+ continue;
65
+ }
66
+ if (/\s/.test(char)) {
67
+ i++;
68
+ continue;
69
+ }
70
+ if (char === "/" && next === "/") {
71
+ i = lineCommentEnd(source, i);
72
+ continue;
73
+ }
74
+ if (char === "/" && next === "*") {
75
+ const span = commentSpan(source, i + 2);
76
+ if (!span)
77
+ return fail("unterminated_comment");
78
+ // Keep declaration docs and every delimiter line with the source signature.
79
+ if (span.newlines + 1 >= settings.minCommentLines &&
80
+ templateDepth === 0 &&
81
+ !stack.some((open) => open.protected) &&
82
+ !source.startsWith("/**", i) &&
83
+ !source.startsWith("/*!", i)) {
84
+ ranges.push({ startLine: line + 1, endLine: line + span.newlines - 1 });
85
+ }
86
+ if (source.startsWith("/**", i) || source.startsWith("/*!", i))
87
+ headers.protect(line, line + span.newlines);
88
+ line += span.newlines;
89
+ i = span.end;
90
+ continue;
91
+ }
92
+ if (char === '"' || char === "'") {
93
+ const span = stringSpan(source, i + 1, char);
94
+ if (!span)
95
+ return fail("unterminated_string");
96
+ line += span.newlines;
97
+ i = span.end;
98
+ previous = "literal";
99
+ expressionEnd = true;
100
+ if (!stack.length)
101
+ importClause = false;
102
+ continue;
103
+ }
104
+ if (char === "`") {
105
+ template = true;
106
+ templateDepth++;
107
+ i++;
108
+ continue;
109
+ }
110
+ if (char === "/") {
111
+ if (expressionEnd === null)
112
+ return fail("ambiguous_regex_literal");
113
+ if (!expressionEnd) {
114
+ const span = regexSpan(source, i + 1);
115
+ if (!span)
116
+ return fail("ambiguous_or_unterminated_regex");
117
+ i = span.end;
118
+ previous = "literal";
119
+ expressionEnd = true;
120
+ continue;
121
+ }
122
+ i += next === "=" ? 2 : 1;
123
+ expressionEnd = false;
124
+ previous = "/";
125
+ continue;
126
+ }
127
+ if (/[A-Za-z_$]/.test(char)) {
128
+ const start = i++;
129
+ while (/[A-Za-z_$0-9]/.test(source[i] ?? ""))
130
+ i++;
131
+ beforeWord = previous;
132
+ previous = source.slice(start, i);
133
+ wordLine = line;
134
+ if (!headers.word(previous, beforeWord, stack.length, line, language === "ts"))
135
+ return fail("unproved_header");
136
+ expressionEnd = !expressionKeywords.has(previous);
137
+ if (language === "ts" && signatureDeclarations.has(previous))
138
+ signatureDeclaration = true;
139
+ if (previous === "import")
140
+ importClause = true;
141
+ if (previous === "from")
142
+ importClause = false;
143
+ continue;
144
+ }
145
+ if (/[0-9]/.test(char)) {
146
+ i++;
147
+ while (/[A-Za-z_0-9.]/.test(source[i] ?? ""))
148
+ i++;
149
+ expressionEnd = true;
150
+ previous = "number";
151
+ continue;
152
+ }
153
+ if (char === "{" || char === "[" || char === "(") {
154
+ if (char === "{" && ambiguousAngleDepth !== undefined)
155
+ return fail("ambiguous_angle_syntax");
156
+ if (language !== "json" &&
157
+ char !== "(" &&
158
+ previous === "," &&
159
+ !stack.some((open) => open.protected) &&
160
+ stack.at(-1)?.char !== "[" &&
161
+ !stack.at(-1)?.call)
162
+ return fail("ambiguous_binding");
163
+ const classBody = headers.open(char, stack.length, previous, line);
164
+ if (!headers.punctuation(char, stack.length, line))
165
+ return fail("unproved_header");
166
+ const call = char === "(" && isCallCallee(previous, beforeWord);
167
+ const declaration = signatureDeclaration && !stack.some((open) => open.declaration);
168
+ const signature = declaration ||
169
+ headers.active ||
170
+ stack.some((open) => open.signature) ||
171
+ (language !== "json" && char === "[" && stack.at(-1)?.char === "{") ||
172
+ importClause ||
173
+ ["const", "let", "var", "export", "type", "#", "!"].includes(previous) ||
174
+ (language !== "json" && [":", "<", "&", "|"].includes(previous)) ||
175
+ (language === "ts" && previous === "=>" && !valueArrow);
176
+ const protectedRange = signature ||
177
+ (char === "(" && !call && !(previous === "=>" && valueArrow)) ||
178
+ stack.some((open) => open.protected);
179
+ const foldable = templateDepth === 0 &&
180
+ char !== "(" &&
181
+ !classBody &&
182
+ !protectedRange &&
183
+ (char === "{" || !expressionEnd || language === "json");
184
+ stack.push({
185
+ char,
186
+ line,
187
+ headerLine: char === "(" ? wordLine : line,
188
+ target: char !== "(" && !expressionEnd,
189
+ foldable,
190
+ protected: protectedRange,
191
+ signature,
192
+ interpolation: false,
193
+ control: char === "(" && controls.has(previous),
194
+ call,
195
+ valueParameters: char === "(" && stack.at(-1)?.call === true && ["(", ","].includes(previous),
196
+ declaration,
197
+ });
198
+ valueArrow = false;
199
+ expressionEnd = false;
200
+ previous = char;
201
+ i++;
202
+ continue;
203
+ }
204
+ if (char === "}" || char === "]" || char === ")") {
205
+ const open = stack.pop();
206
+ if (ambiguousAngleDepth !== undefined && stack.length < ambiguousAngleDepth)
207
+ ambiguousAngleDepth = undefined;
208
+ if (!open || { "{": "}", "[": "]", "(": ")" }[open.char] !== char)
209
+ return fail("unbalanced_delimiters");
210
+ if (open.interpolation) {
211
+ template = true;
212
+ i++;
213
+ continue;
214
+ }
215
+ headers.close(open, stack.length, line);
216
+ if (open.foldable && line - open.line - 1 >= settings.minBodyLines)
217
+ ranges.push({ startLine: open.line + 1, endLine: line - 1 });
218
+ valueArrow = open.valueParameters;
219
+ if (open.declaration)
220
+ signatureDeclaration = false;
221
+ expressionEnd = open.control ? false : char === "}" ? null : true;
222
+ if (char === "}")
223
+ importClause = false;
224
+ previous = char;
225
+ i++;
226
+ continue;
227
+ }
228
+ if ((char === "+" || char === "-") && next === char) {
229
+ previous = char + char;
230
+ i += 2;
231
+ continue;
232
+ }
233
+ // JSX, escaped identifiers and unknown lexical tokens cannot establish safe delimiters.
234
+ if (char === "<" && language === "ts" && expressionEnd) {
235
+ const span = typeArgumentsSpan(source, i + 1);
236
+ if (span) {
237
+ line += span.newlines;
238
+ i = span.end;
239
+ previous = ">";
240
+ continue;
241
+ }
242
+ // A comparison can finish at its enclosing delimiter; an unresolved generic
243
+ // reaching a brace must fail rather than fold constraint/signature members.
244
+ if (!/^\s*\{/.test(source.slice(i + 1)))
245
+ ambiguousAngleDepth = stack.length;
246
+ }
247
+ if (char === "<" && !expressionEnd && /^<\/?[A-Za-z]/.test(source.slice(i)))
248
+ return fail("ambiguous_angle_syntax");
249
+ if (!";:,.?=><!~+-*%&|^".includes(char))
250
+ return fail("unknown_token");
251
+ if (char === ";") {
252
+ importClause = false;
253
+ signatureDeclaration = false;
254
+ if (ambiguousAngleDepth === stack.length)
255
+ ambiguousAngleDepth = undefined;
256
+ }
257
+ if (!headers.punctuation(char === "=" && next === ">" ? "=>" : char, stack.length, line))
258
+ return fail("unproved_header");
259
+ valueArrow = valueArrow && previous === ")" && char === "=" && next === ">";
260
+ previous = char === "=" && next === ">" ? "=>" : char;
261
+ i += previous === "=>" ? 2 : 1;
262
+ expressionEnd = char === "." ? null : false;
263
+ }
264
+ return template || templateDepth || stack.length || headers.unfinished
265
+ ? fail("unbalanced_or_unproved_header")
266
+ : { status: "parsed", ranges: headers.filter(ranges) };
267
+ }
268
+ //# sourceMappingURL=brace-scanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brace-scanner.js","sourceRoot":"","sources":["../../../../src/harness/utils/read-folders/brace-scanner.ts"],"names":[],"mappings":"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","sourcesContent":["import { HeaderProtection } from \"./header-protection.ts\";\nimport { controls, expressionKeywords, isCallCallee, type Open, signatureDeclarations } from \"./lexical-context.ts\";\nimport { commentSpan, lineCommentEnd, regexSpan, stringSpan, typeArgumentsSpan } from \"./lexical-spans.ts\";\nimport type { ReadBraceScan, ReadFoldSettings, ReadLineRange } from \"./types.ts\";\n\n/** Measured row-17 brace lexer, restricted to the three selected languages. */\nexport function scanBraces(source: string, language: \"ts\" | \"js\" | \"json\", settings: ReadFoldSettings): ReadBraceScan {\n\tconst ranges: ReadLineRange[] = [];\n\tconst stack: Open[] = [];\n\tconst headers = new HeaderProtection();\n\tconst fail = (reason: string): ReadBraceScan => ({ status: \"parse_failure\", reason });\n\tlet line = 1;\n\tlet i = 0;\n\tlet template = false;\n\tlet templateDepth = 0;\n\tlet previous = \"\";\n\tlet beforeWord = \"\";\n\tlet wordLine = 1;\n\tlet valueArrow = false;\n\tlet expressionEnd: boolean | null = false;\n\tlet importClause = false;\n\tlet ambiguousAngleDepth: number | undefined;\n\tlet signatureDeclaration = false;\n\tif (source.startsWith(\"#!\")) {\n\t\tconst end = source.indexOf(\"\\n\");\n\t\ti = end < 0 ? source.length : end;\n\t}\n\twhile (i < source.length) {\n\t\tconst char = source[i];\n\t\tconst next = source[i + 1];\n\t\tif (char === \"\\n\") line++;\n\t\tif (template) {\n\t\t\tif (char === \"\\\\\") {\n\t\t\t\tif (next === \"\\n\") line++;\n\t\t\t\ti += 2;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (char === \"`\") {\n\t\t\t\ttemplate = false;\n\t\t\t\ttemplateDepth--;\n\t\t\t\texpressionEnd = true;\n\t\t\t\tprevious = \"literal\";\n\t\t\t} else if (char === \"$\" && next === \"{\") {\n\t\t\t\tstack.push({\n\t\t\t\t\tchar: \"{\",\n\t\t\t\t\tline,\n\t\t\t\t\tfoldable: false,\n\t\t\t\t\tprotected: true,\n\t\t\t\t\tsignature: false,\n\t\t\t\t\tinterpolation: true,\n\t\t\t\t\tcontrol: false,\n\t\t\t\t\tcall: false,\n\t\t\t\t\tvalueParameters: false,\n\t\t\t\t\tdeclaration: false,\n\t\t\t\t});\n\t\t\t\ttemplate = false;\n\t\t\t\texpressionEnd = false;\n\t\t\t\tprevious = \"{\";\n\t\t\t\ti += 2;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\ti++;\n\t\t\tcontinue;\n\t\t}\n\t\tif (/\\s/.test(char)) {\n\t\t\ti++;\n\t\t\tcontinue;\n\t\t}\n\t\tif (char === \"/\" && next === \"/\") {\n\t\t\ti = lineCommentEnd(source, i);\n\t\t\tcontinue;\n\t\t}\n\t\tif (char === \"/\" && next === \"*\") {\n\t\t\tconst span = commentSpan(source, i + 2);\n\t\t\tif (!span) return fail(\"unterminated_comment\");\n\t\t\t// Keep declaration docs and every delimiter line with the source signature.\n\t\t\tif (\n\t\t\t\tspan.newlines + 1 >= settings.minCommentLines &&\n\t\t\t\ttemplateDepth === 0 &&\n\t\t\t\t!stack.some((open) => open.protected) &&\n\t\t\t\t!source.startsWith(\"/**\", i) &&\n\t\t\t\t!source.startsWith(\"/*!\", i)\n\t\t\t) {\n\t\t\t\tranges.push({ startLine: line + 1, endLine: line + span.newlines - 1 });\n\t\t\t}\n\t\t\tif (source.startsWith(\"/**\", i) || source.startsWith(\"/*!\", i)) headers.protect(line, line + span.newlines);\n\t\t\tline += span.newlines;\n\t\t\ti = span.end;\n\t\t\tcontinue;\n\t\t}\n\t\tif (char === '\"' || char === \"'\") {\n\t\t\tconst span = stringSpan(source, i + 1, char);\n\t\t\tif (!span) return fail(\"unterminated_string\");\n\t\t\tline += span.newlines;\n\t\t\ti = span.end;\n\t\t\tprevious = \"literal\";\n\t\t\texpressionEnd = true;\n\t\t\tif (!stack.length) importClause = false;\n\t\t\tcontinue;\n\t\t}\n\t\tif (char === \"`\") {\n\t\t\ttemplate = true;\n\t\t\ttemplateDepth++;\n\t\t\ti++;\n\t\t\tcontinue;\n\t\t}\n\t\tif (char === \"/\") {\n\t\t\tif (expressionEnd === null) return fail(\"ambiguous_regex_literal\");\n\t\t\tif (!expressionEnd) {\n\t\t\t\tconst span = regexSpan(source, i + 1);\n\t\t\t\tif (!span) return fail(\"ambiguous_or_unterminated_regex\");\n\t\t\t\ti = span.end;\n\t\t\t\tprevious = \"literal\";\n\t\t\t\texpressionEnd = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\ti += next === \"=\" ? 2 : 1;\n\t\t\texpressionEnd = false;\n\t\t\tprevious = \"/\";\n\t\t\tcontinue;\n\t\t}\n\t\tif (/[A-Za-z_$]/.test(char)) {\n\t\t\tconst start = i++;\n\t\t\twhile (/[A-Za-z_$0-9]/.test(source[i] ?? \"\")) i++;\n\t\t\tbeforeWord = previous;\n\t\t\tprevious = source.slice(start, i);\n\t\t\twordLine = line;\n\t\t\tif (!headers.word(previous, beforeWord, stack.length, line, language === \"ts\")) return fail(\"unproved_header\");\n\t\t\texpressionEnd = !expressionKeywords.has(previous);\n\t\t\tif (language === \"ts\" && signatureDeclarations.has(previous)) signatureDeclaration = true;\n\t\t\tif (previous === \"import\") importClause = true;\n\t\t\tif (previous === \"from\") importClause = false;\n\t\t\tcontinue;\n\t\t}\n\t\tif (/[0-9]/.test(char)) {\n\t\t\ti++;\n\t\t\twhile (/[A-Za-z_0-9.]/.test(source[i] ?? \"\")) i++;\n\t\t\texpressionEnd = true;\n\t\t\tprevious = \"number\";\n\t\t\tcontinue;\n\t\t}\n\t\tif (char === \"{\" || char === \"[\" || char === \"(\") {\n\t\t\tif (char === \"{\" && ambiguousAngleDepth !== undefined) return fail(\"ambiguous_angle_syntax\");\n\t\t\tif (\n\t\t\t\tlanguage !== \"json\" &&\n\t\t\t\tchar !== \"(\" &&\n\t\t\t\tprevious === \",\" &&\n\t\t\t\t!stack.some((open) => open.protected) &&\n\t\t\t\tstack.at(-1)?.char !== \"[\" &&\n\t\t\t\t!stack.at(-1)?.call\n\t\t\t)\n\t\t\t\treturn fail(\"ambiguous_binding\");\n\t\t\tconst classBody = headers.open(char, stack.length, previous, line);\n\t\t\tif (!headers.punctuation(char, stack.length, line)) return fail(\"unproved_header\");\n\t\t\tconst call = char === \"(\" && isCallCallee(previous, beforeWord);\n\t\t\tconst declaration = signatureDeclaration && !stack.some((open) => open.declaration);\n\t\t\tconst signature =\n\t\t\t\tdeclaration ||\n\t\t\t\theaders.active ||\n\t\t\t\tstack.some((open) => open.signature) ||\n\t\t\t\t(language !== \"json\" && char === \"[\" && stack.at(-1)?.char === \"{\") ||\n\t\t\t\timportClause ||\n\t\t\t\t[\"const\", \"let\", \"var\", \"export\", \"type\", \"#\", \"!\"].includes(previous) ||\n\t\t\t\t(language !== \"json\" && [\":\", \"<\", \"&\", \"|\"].includes(previous)) ||\n\t\t\t\t(language === \"ts\" && previous === \"=>\" && !valueArrow);\n\t\t\tconst protectedRange =\n\t\t\t\tsignature ||\n\t\t\t\t(char === \"(\" && !call && !(previous === \"=>\" && valueArrow)) ||\n\t\t\t\tstack.some((open) => open.protected);\n\t\t\tconst foldable =\n\t\t\t\ttemplateDepth === 0 &&\n\t\t\t\tchar !== \"(\" &&\n\t\t\t\t!classBody &&\n\t\t\t\t!protectedRange &&\n\t\t\t\t(char === \"{\" || !expressionEnd || language === \"json\");\n\t\t\tstack.push({\n\t\t\t\tchar,\n\t\t\t\tline,\n\t\t\t\theaderLine: char === \"(\" ? wordLine : line,\n\t\t\t\ttarget: char !== \"(\" && !expressionEnd,\n\t\t\t\tfoldable,\n\t\t\t\tprotected: protectedRange,\n\t\t\t\tsignature,\n\t\t\t\tinterpolation: false,\n\t\t\t\tcontrol: char === \"(\" && controls.has(previous),\n\t\t\t\tcall,\n\t\t\t\tvalueParameters: char === \"(\" && stack.at(-1)?.call === true && [\"(\", \",\"].includes(previous),\n\t\t\t\tdeclaration,\n\t\t\t});\n\t\t\tvalueArrow = false;\n\t\t\texpressionEnd = false;\n\t\t\tprevious = char;\n\t\t\ti++;\n\t\t\tcontinue;\n\t\t}\n\t\tif (char === \"}\" || char === \"]\" || char === \")\") {\n\t\t\tconst open = stack.pop();\n\t\t\tif (ambiguousAngleDepth !== undefined && stack.length < ambiguousAngleDepth) ambiguousAngleDepth = undefined;\n\t\t\tif (!open || { \"{\": \"}\", \"[\": \"]\", \"(\": \")\" }[open.char] !== char) return fail(\"unbalanced_delimiters\");\n\t\t\tif (open.interpolation) {\n\t\t\t\ttemplate = true;\n\t\t\t\ti++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\theaders.close(open, stack.length, line);\n\t\t\tif (open.foldable && line - open.line - 1 >= settings.minBodyLines)\n\t\t\t\tranges.push({ startLine: open.line + 1, endLine: line - 1 });\n\t\t\tvalueArrow = open.valueParameters;\n\t\t\tif (open.declaration) signatureDeclaration = false;\n\t\t\texpressionEnd = open.control ? false : char === \"}\" ? null : true;\n\t\t\tif (char === \"}\") importClause = false;\n\t\t\tprevious = char;\n\t\t\ti++;\n\t\t\tcontinue;\n\t\t}\n\t\tif ((char === \"+\" || char === \"-\") && next === char) {\n\t\t\tprevious = char + char;\n\t\t\ti += 2;\n\t\t\tcontinue;\n\t\t}\n\t\t// JSX, escaped identifiers and unknown lexical tokens cannot establish safe delimiters.\n\t\tif (char === \"<\" && language === \"ts\" && expressionEnd) {\n\t\t\tconst span = typeArgumentsSpan(source, i + 1);\n\t\t\tif (span) {\n\t\t\t\tline += span.newlines;\n\t\t\t\ti = span.end;\n\t\t\t\tprevious = \">\";\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// A comparison can finish at its enclosing delimiter; an unresolved generic\n\t\t\t// reaching a brace must fail rather than fold constraint/signature members.\n\t\t\tif (!/^\\s*\\{/.test(source.slice(i + 1))) ambiguousAngleDepth = stack.length;\n\t\t}\n\t\tif (char === \"<\" && !expressionEnd && /^<\\/?[A-Za-z]/.test(source.slice(i)))\n\t\t\treturn fail(\"ambiguous_angle_syntax\");\n\t\tif (!\";:,.?=><!~+-*%&|^\".includes(char)) return fail(\"unknown_token\");\n\t\tif (char === \";\") {\n\t\t\timportClause = false;\n\t\t\tsignatureDeclaration = false;\n\t\t\tif (ambiguousAngleDepth === stack.length) ambiguousAngleDepth = undefined;\n\t\t}\n\t\tif (!headers.punctuation(char === \"=\" && next === \">\" ? \"=>\" : char, stack.length, line))\n\t\t\treturn fail(\"unproved_header\");\n\t\tvalueArrow = valueArrow && previous === \")\" && char === \"=\" && next === \">\";\n\t\tprevious = char === \"=\" && next === \">\" ? \"=>\" : char;\n\t\ti += previous === \"=>\" ? 2 : 1;\n\t\texpressionEnd = char === \".\" ? null : false;\n\t}\n\treturn template || templateDepth || stack.length || headers.unfinished\n\t\t? fail(\"unbalanced_or_unproved_header\")\n\t\t: { status: \"parsed\", ranges: headers.filter(ranges) };\n}\n"]}