poe-code 3.0.257 → 3.0.259-beta.1

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 (76) hide show
  1. package/dist/cli/commands/gaslight.js +84 -1
  2. package/dist/cli/commands/gaslight.js.map +1 -1
  3. package/dist/cli/poe-theme.d.ts +1 -0
  4. package/dist/cli/poe-theme.js +5 -0
  5. package/dist/cli/poe-theme.js.map +1 -0
  6. package/dist/cli/program.js +1 -0
  7. package/dist/cli/program.js.map +1 -1
  8. package/dist/index.d.ts +1 -1
  9. package/dist/index.js +2753 -1123
  10. package/dist/index.js.map +4 -4
  11. package/dist/metafile.json +1 -1
  12. package/dist/sdk/gaslight.d.ts +1 -1
  13. package/dist/sdk/gaslight.js +1 -1
  14. package/dist/sdk/gaslight.js.map +1 -1
  15. package/package.json +3 -1
  16. package/packages/agent-gaslight/dist/config.d.ts +5 -1
  17. package/packages/agent-gaslight/dist/config.js +32 -11
  18. package/packages/agent-gaslight/dist/index.d.ts +3 -2
  19. package/packages/agent-gaslight/dist/index.js +2 -1
  20. package/packages/agent-gaslight/dist/ingest.d.ts +2 -0
  21. package/packages/agent-gaslight/dist/ingest.js +486 -0
  22. package/packages/agent-gaslight/dist/run.js +1 -1
  23. package/packages/agent-gaslight/dist/types.d.ts +41 -6
  24. package/packages/agent-harness/dist/loader/run.js +6 -21
  25. package/packages/agent-script/dist/cli.d.ts +2 -3
  26. package/packages/agent-script/dist/cli.js +70 -36
  27. package/packages/agent-script/dist/example-runner.d.ts +2 -3
  28. package/packages/agent-script/dist/example-runner.js +69 -56
  29. package/packages/agent-script/dist/interp/exceptions.js +1 -1
  30. package/packages/agent-script/dist/interp/globals/object-array.d.ts +1 -1
  31. package/packages/agent-script/dist/interp/globals/object-array.js +39 -20
  32. package/packages/agent-script/dist/interp/host-bridge.js +1 -1
  33. package/packages/agent-script/dist/interp/interpreter.js +83 -17
  34. package/packages/agent-script/dist/interp/methods/array.js +25 -2
  35. package/packages/agent-script/dist/interp/methods/regex.js +1 -1
  36. package/packages/agent-script/dist/interp/promise-tracker.d.ts +16 -0
  37. package/packages/agent-script/dist/interp/promise-tracker.js +58 -0
  38. package/packages/agent-script/dist/interp/promise.js +38 -7
  39. package/packages/agent-script/dist/interp/scope.d.ts +1 -0
  40. package/packages/agent-script/dist/interp/scope.js +3 -0
  41. package/packages/agent-script/dist/interp/values.js +2 -0
  42. package/packages/agent-script/dist/lint/index.d.ts +2 -0
  43. package/packages/agent-script/dist/lint/index.js +2 -0
  44. package/packages/agent-script/dist/lint/rules/AS-export-import-meta.d.ts +6 -1
  45. package/packages/agent-script/dist/lint/rules/AS-export-import-meta.js +33 -4
  46. package/packages/agent-script/dist/modules/agent.js +10 -1
  47. package/packages/agent-script/dist/modules/log.js +5 -1
  48. package/packages/agent-script/dist/modules/registry.js +9 -3
  49. package/packages/agent-script/dist/output-stream.d.ts +12 -0
  50. package/packages/agent-script/dist/output-stream.js +50 -0
  51. package/packages/agent-script/dist/parse/parser.d.ts +1 -1
  52. package/packages/agent-script/dist/parse/parser.js +151 -45
  53. package/packages/agent-script/dist/parse/tokenizer.js +26 -3
  54. package/packages/agent-script/dist/run.js +14 -3
  55. package/packages/agent-script/dist/runner/run-harness.js +28 -5
  56. package/packages/agent-traces/dist/collect.d.ts +4 -0
  57. package/packages/agent-traces/dist/collect.js +102 -0
  58. package/packages/agent-traces/dist/index.d.ts +4 -0
  59. package/packages/agent-traces/dist/index.js +3 -0
  60. package/packages/agent-traces/dist/jsonl.d.ts +2 -0
  61. package/packages/agent-traces/dist/jsonl.js +7 -0
  62. package/packages/agent-traces/dist/line-json.d.ts +4 -0
  63. package/packages/agent-traces/dist/line-json.js +40 -0
  64. package/packages/agent-traces/dist/readers/claude.d.ts +2 -0
  65. package/packages/agent-traces/dist/readers/claude.js +192 -0
  66. package/packages/agent-traces/dist/readers/codex.d.ts +2 -0
  67. package/packages/agent-traces/dist/readers/codex.js +266 -0
  68. package/packages/agent-traces/dist/readers/index.d.ts +5 -0
  69. package/packages/agent-traces/dist/readers/index.js +4 -0
  70. package/packages/agent-traces/dist/types.d.ts +84 -0
  71. package/packages/agent-traces/dist/types.js +1 -0
  72. package/packages/package-lint/dist/model.js +5 -1
  73. package/packages/package-lint/dist/source-imports.d.ts +11 -1
  74. package/packages/package-lint/dist/source-imports.js +30 -4
  75. package/packages/tiny-stdio-mcp-test-server/dist/cli.js +41 -0
  76. package/packages/tiny-stdio-mcp-test-server/dist/index.js +8 -0
@@ -1,3 +1,4 @@
1
+ import { addBrokenPipeListener, createBrokenPipeState, createSafeOutputStream } from "../output-stream.js";
1
2
  export function makeLogModule(sink = createJsonlStdoutSink()) {
2
3
  const normalizedSink = readSink(sink);
3
4
  return {
@@ -26,8 +27,11 @@ export function makeLogModule(sink = createJsonlStdoutSink()) {
26
27
  };
27
28
  }
28
29
  function createJsonlStdoutSink() {
30
+ const brokenPipe = createBrokenPipeState();
31
+ const stdout = createSafeOutputStream(process.stdout, brokenPipe);
32
+ addBrokenPipeListener(process.stdout, brokenPipe);
29
33
  return (entry) => {
30
- process.stdout.write(`${JSON.stringify(toJsonValue(entry, new WeakSet()))}\n`);
34
+ stdout.write(`${JSON.stringify(toJsonValue(entry, new WeakSet()))}\n`);
31
35
  };
32
36
  }
33
37
  function readNonEmptyString(value, label) {
@@ -1,3 +1,4 @@
1
+ import { attachErrorSpan } from "../error/shape.js";
1
2
  import { wrapCancelableBindings } from "../interp/cancel.js";
2
3
  import { readHostOperationPolicy, wrapCallerInjectedBindings } from "../interp/host-bridge.js";
3
4
  import { registerPendingHostCallPolicy } from "../snapshot/policy.js";
@@ -29,7 +30,7 @@ function bindImportDeclaration(declaration, registry, wrappedModules, bindings,
29
30
  const moduleName = declaration.source.value;
30
31
  const moduleExports = registry.get(moduleName);
31
32
  if (moduleExports === undefined) {
32
- throw new Error(createUnknownModuleMessage(moduleName, [...registry.keys()]));
33
+ throw createModuleImportError(createUnknownModuleMessage(moduleName, [...registry.keys()]), declaration.source.span);
33
34
  }
34
35
  const wrappedExports = wrappedModules.get(moduleName) ??
35
36
  wrapCancelableBindings(wrapCallerInjectedBindings(Object.fromEntries(moduleExports), {
@@ -42,7 +43,7 @@ function bindImportDeclaration(declaration, registry, wrappedModules, bindings,
42
43
  for (const specifier of declaration.specifiers) {
43
44
  const localName = specifier.local.name;
44
45
  if (Object.hasOwn(bindings, localName)) {
45
- throw new Error(`Cannot redeclare imported binding '${localName}'.`);
46
+ throw createModuleImportError(`Cannot redeclare imported binding '${localName}'.`, specifier.local.span);
46
47
  }
47
48
  bindings[localName] = resolveImportSpecifier(moduleName, specifier, wrappedExports);
48
49
  }
@@ -56,7 +57,12 @@ function resolveImportSpecifier(moduleName, specifier, wrappedExports) {
56
57
  if (exportedValue !== undefined || Object.hasOwn(wrappedExports, exportName)) {
57
58
  return exportedValue;
58
59
  }
59
- throw new Error(createUnknownExportMessage(moduleName, exportName, Object.keys(wrappedExports).sort()));
60
+ throw createModuleImportError(createUnknownExportMessage(moduleName, exportName, Object.keys(wrappedExports).sort()), specifier.span);
61
+ }
62
+ function createModuleImportError(message, span) {
63
+ const error = new Error(message);
64
+ attachErrorSpan(error, span);
65
+ return error;
60
66
  }
61
67
  function normalizeModuleRegistry(modules) {
62
68
  if (modules === undefined) {
@@ -0,0 +1,12 @@
1
+ export type OutputStream = {
2
+ off?(event: "error", listener: (error: unknown) => void): void;
3
+ on?(event: "error", listener: (error: unknown) => void): void;
4
+ write(chunk: string): void;
5
+ };
6
+ export type BrokenPipeState = {
7
+ closed: boolean;
8
+ };
9
+ export declare function createBrokenPipeState(): BrokenPipeState;
10
+ export declare function createSafeOutputStream(stream: OutputStream, brokenPipe: BrokenPipeState): OutputStream;
11
+ export declare function withBrokenPipeGuard<TResult>(streams: readonly OutputStream[], brokenPipe: BrokenPipeState, callback: () => Promise<TResult>): Promise<TResult>;
12
+ export declare function addBrokenPipeListener(stream: OutputStream, brokenPipe: BrokenPipeState): () => void;
@@ -0,0 +1,50 @@
1
+ import { hasOwnErrorCode } from "./error-codes.js";
2
+ export function createBrokenPipeState() {
3
+ return { closed: false };
4
+ }
5
+ export function createSafeOutputStream(stream, brokenPipe) {
6
+ return {
7
+ write(chunk) {
8
+ if (brokenPipe.closed) {
9
+ return;
10
+ }
11
+ try {
12
+ stream.write(chunk);
13
+ }
14
+ catch (error) {
15
+ if (isBrokenPipeError(error)) {
16
+ brokenPipe.closed = true;
17
+ return;
18
+ }
19
+ throw error;
20
+ }
21
+ }
22
+ };
23
+ }
24
+ export async function withBrokenPipeGuard(streams, brokenPipe, callback) {
25
+ const removeListeners = streams.map((stream) => addBrokenPipeListener(stream, brokenPipe));
26
+ try {
27
+ return await callback();
28
+ }
29
+ finally {
30
+ for (const removeListener of removeListeners) {
31
+ removeListener();
32
+ }
33
+ }
34
+ }
35
+ export function addBrokenPipeListener(stream, brokenPipe) {
36
+ const onError = (error) => {
37
+ if (isBrokenPipeError(error)) {
38
+ brokenPipe.closed = true;
39
+ return;
40
+ }
41
+ throw error;
42
+ };
43
+ stream.on?.("error", onError);
44
+ return () => {
45
+ stream.off?.("error", onError);
46
+ };
47
+ }
48
+ function isBrokenPipeError(error) {
49
+ return hasOwnErrorCode(error, "EPIPE");
50
+ }
@@ -55,7 +55,7 @@ export type TemplateElement = BaseNode & {
55
55
  tail: boolean;
56
56
  value: {
57
57
  raw: string;
58
- cooked: string;
58
+ cooked: string | undefined;
59
59
  };
60
60
  };
61
61
  export type TemplateLiteral = BaseNode & {
@@ -4,6 +4,8 @@ import { formatParseError } from "./format-error.js";
4
4
  import { createExportDefaultDeclaration, createExportNamedDeclaration } from "./parse-export.js";
5
5
  import { createImportMeta, isImportMetaTokenSequence } from "./parse-import-meta.js";
6
6
  import { parseRegex } from "../interp/regex/parse.js";
7
+ const MAX_CONDITIONAL_EXPRESSION_DEPTH = 256;
8
+ const MAX_IF_STATEMENT_DEPTH = 2_048;
7
9
  export class DisallowedSyntaxError extends Error {
8
10
  constructor(syntax, position) {
9
11
  super(`Disallowed syntax '${syntax}' at line ${position.line}, column ${position.column}.`);
@@ -18,6 +20,7 @@ const MULTIPLICATIVE_OPERATORS = new Set(["*", "/", "%"]);
18
20
  const BITWISE_OR_OPERATORS = new Set(["|"]);
19
21
  const BITWISE_XOR_OPERATORS = new Set(["^"]);
20
22
  const BITWISE_AND_OPERATORS = new Set(["&"]);
23
+ const MAX_UNICODE_CODE_POINT = 0x10ffff;
21
24
  const TOP_LEVEL_STATEMENT_KEYWORDS = new Set([
22
25
  "break",
23
26
  "const",
@@ -96,6 +99,8 @@ class Parser {
96
99
  tokens;
97
100
  index = 0;
98
101
  breakableDepth = 0;
102
+ conditionalExpressionDepth = 0;
103
+ ifStatementDepth = 0;
99
104
  loopDepth = 0;
100
105
  generatorBody = false;
101
106
  scopes = [new Map()];
@@ -254,23 +259,33 @@ class Parser {
254
259
  };
255
260
  }
256
261
  parseConditionalExpression() {
257
- const test = this.parseCoalesceExpression();
258
- if (this.consumePunctuator("?") === undefined) {
259
- return test;
262
+ if (this.conditionalExpressionDepth >= MAX_CONDITIONAL_EXPRESSION_DEPTH) {
263
+ const token = this.currentToken();
264
+ throw new Error(`Conditional expression nesting limit exceeded at line ${token.start.line}, column ${token.start.column}.`);
265
+ }
266
+ this.conditionalExpressionDepth += 1;
267
+ try {
268
+ const test = this.parseCoalesceExpression();
269
+ if (this.consumePunctuator("?") === undefined) {
270
+ return test;
271
+ }
272
+ const consequent = this.parseExpression();
273
+ this.expectPunctuator(":");
274
+ const alternate = this.parseConditionalExpression();
275
+ return {
276
+ node: {
277
+ type: "ConditionalExpression",
278
+ test: test.node,
279
+ consequent: consequent.node,
280
+ alternate: alternate.node,
281
+ span: createSpan(test.node.span.start, alternate.node.span.end)
282
+ },
283
+ parenthesized: false
284
+ };
285
+ }
286
+ finally {
287
+ this.conditionalExpressionDepth -= 1;
260
288
  }
261
- const consequent = this.parseExpression();
262
- this.expectPunctuator(":");
263
- const alternate = this.parseConditionalExpression();
264
- return {
265
- node: {
266
- type: "ConditionalExpression",
267
- test: test.node,
268
- consequent: consequent.node,
269
- alternate: alternate.node,
270
- span: createSpan(test.node.span.start, alternate.node.span.end)
271
- },
272
- parenthesized: false
273
- };
274
289
  }
275
290
  parseArrowFunctionBody() {
276
291
  if (this.currentToken().type === "punctuator" && this.currentToken().value === "{") {
@@ -436,28 +451,38 @@ class Parser {
436
451
  throw new DisallowedSyntaxError("label", firstLabelToken.start);
437
452
  }
438
453
  parseIfStatement() {
439
- const ifToken = this.expectKeyword("if");
440
- this.expectPunctuator("(");
441
- const test = this.parseExpression({ allowSequence: true }).node;
442
- this.expectPunctuator(")");
443
- const consequent = this.parseStatement();
444
- if (consequent.type !== "BlockStatement") {
445
- while (this.currentToken().type === "punctuator" &&
446
- this.currentToken().value === ";" &&
447
- this.peekToken(1).type === "keyword" &&
448
- this.peekToken(1).value === "else") {
449
- this.index += 1;
454
+ if (this.ifStatementDepth >= MAX_IF_STATEMENT_DEPTH) {
455
+ const token = this.currentToken();
456
+ throw new Error(`If statement nesting limit exceeded at line ${token.start.line}, column ${token.start.column}.`);
457
+ }
458
+ this.ifStatementDepth += 1;
459
+ try {
460
+ const ifToken = this.expectKeyword("if");
461
+ this.expectPunctuator("(");
462
+ const test = this.parseExpression({ allowSequence: true }).node;
463
+ this.expectPunctuator(")");
464
+ const consequent = this.parseStatement();
465
+ if (consequent.type !== "BlockStatement") {
466
+ while (this.currentToken().type === "punctuator" &&
467
+ this.currentToken().value === ";" &&
468
+ this.peekToken(1).type === "keyword" &&
469
+ this.peekToken(1).value === "else") {
470
+ this.index += 1;
471
+ }
450
472
  }
473
+ const elseToken = this.consumeKeyword("else");
474
+ const alternate = elseToken === undefined ? undefined : this.parseStatement();
475
+ return {
476
+ type: "IfStatement",
477
+ test,
478
+ consequent,
479
+ alternate,
480
+ span: createSpan(ifToken.start, alternate?.span.end ?? consequent.span.end)
481
+ };
482
+ }
483
+ finally {
484
+ this.ifStatementDepth -= 1;
451
485
  }
452
- const elseToken = this.consumeKeyword("else");
453
- const alternate = elseToken === undefined ? undefined : this.parseStatement();
454
- return {
455
- type: "IfStatement",
456
- test,
457
- consequent,
458
- alternate,
459
- span: createSpan(ifToken.start, alternate?.span.end ?? consequent.span.end)
460
- };
461
486
  }
462
487
  parseSwitchStatement() {
463
488
  const switchToken = this.currentToken();
@@ -784,7 +809,7 @@ class Parser {
784
809
  }
785
810
  parseExportDefaultDeclaration(exportToken) {
786
811
  this.index += 1;
787
- if (this.currentToken().value === "function" || this.currentToken().value === "class") {
812
+ if (this.currentToken().value === "class") {
788
813
  throw new DisallowedSyntaxError(`export default ${this.currentToken().value}`, this.currentToken().start);
789
814
  }
790
815
  const declaration = this.parseExpression().node;
@@ -1447,7 +1472,7 @@ class Parser {
1447
1472
  const next = this.currentToken();
1448
1473
  const hasArgument = delegate ||
1449
1474
  (!hasLineBreakBetween(token, next) &&
1450
- !(next.type === "punctuator" && (next.value === ";" || next.value === "}")) &&
1475
+ !(next.type === "punctuator" && isYieldArgumentTerminator(next.value)) &&
1451
1476
  next.type !== "eof");
1452
1477
  const argument = hasArgument ? this.parseAssignmentExpression().node : undefined;
1453
1478
  if (delegate && argument === undefined) {
@@ -1591,7 +1616,7 @@ class Parser {
1591
1616
  continue;
1592
1617
  }
1593
1618
  if (this.currentToken().type === "template") {
1594
- const quasi = createTemplateLiteral(this.currentToken());
1619
+ const quasi = createTemplateLiteral(this.currentToken(), { allowMalformedEscapes: true });
1595
1620
  this.index += 1;
1596
1621
  expression = {
1597
1622
  node: {
@@ -1679,7 +1704,7 @@ class Parser {
1679
1704
  if (token.type === "template") {
1680
1705
  this.index += 1;
1681
1706
  return {
1682
- node: createTemplateLiteral(token),
1707
+ node: createTemplateLiteral(token, { allowMalformedEscapes: false }),
1683
1708
  parenthesized: false
1684
1709
  };
1685
1710
  }
@@ -2834,6 +2859,9 @@ function isAssignmentOperator(value) {
2834
2859
  return false;
2835
2860
  }
2836
2861
  }
2862
+ function isYieldArgumentTerminator(value) {
2863
+ return value === ";" || value === "}" || value === ")" || value === "]" || value === ",";
2864
+ }
2837
2865
  function createNumericLiteral(token) {
2838
2866
  return {
2839
2867
  type: "NumericLiteral",
@@ -2915,7 +2943,7 @@ function createLiteralFromToken(token) {
2915
2943
  }
2916
2944
  return createKeywordLiteral(token);
2917
2945
  }
2918
- function createTemplateLiteral(token) {
2946
+ function createTemplateLiteral(token, options = { allowMalformedEscapes: false }) {
2919
2947
  const raw = token.value;
2920
2948
  const expressions = [];
2921
2949
  const quasis = [];
@@ -2928,7 +2956,7 @@ function createTemplateLiteral(token) {
2928
2956
  continue;
2929
2957
  }
2930
2958
  if (char === "$" && raw[cursor + 1] === "{") {
2931
- quasis.push(createTemplateElement(token.start, raw, quasiStart, cursor, false));
2959
+ quasis.push(createTemplateElement(token.start, raw, quasiStart, cursor, false, options));
2932
2960
  const expressionStart = cursor + 2;
2933
2961
  const expressionEnd = findTemplateExpressionEnd(raw, expressionStart);
2934
2962
  expressions.push(parseEmbeddedExpression(raw.slice(expressionStart, expressionEnd), positionWithinRaw(token.start, raw, expressionStart)));
@@ -2938,7 +2966,7 @@ function createTemplateLiteral(token) {
2938
2966
  }
2939
2967
  cursor += 1;
2940
2968
  }
2941
- quasis.push(createTemplateElement(token.start, raw, quasiStart, raw.length - 1, true));
2969
+ quasis.push(createTemplateElement(token.start, raw, quasiStart, raw.length - 1, true, options));
2942
2970
  return {
2943
2971
  type: "TemplateLiteral",
2944
2972
  expressions,
@@ -2997,18 +3025,96 @@ function isDecimalDigit(value) {
2997
3025
  function isHexDigit(value) {
2998
3026
  return isDecimalDigit(value) || (value >= "a" && value <= "f") || (value >= "A" && value <= "F");
2999
3027
  }
3000
- function createTemplateElement(templateStart, rawTemplate, rawStart, rawEnd, tail) {
3028
+ function isOctalDigit(value) {
3029
+ return value >= "0" && value <= "7";
3030
+ }
3031
+ function createTemplateElement(templateStart, rawTemplate, rawStart, rawEnd, tail, options) {
3001
3032
  const rawValue = rawTemplate.slice(rawStart, rawEnd);
3033
+ const cooked = decodeTemplateElementCooked(rawValue, options.allowMalformedEscapes);
3034
+ if (cooked.invalid !== undefined && !options.allowMalformedEscapes) {
3035
+ const position = positionWithinRaw(templateStart, rawTemplate, rawStart + cooked.invalid.index);
3036
+ throw new Error(`${cooked.invalid.message} at line ${position.line}, column ${position.column}.`);
3037
+ }
3002
3038
  return {
3003
3039
  type: "TemplateElement",
3004
3040
  tail,
3005
3041
  value: {
3006
3042
  raw: rawValue,
3007
- cooked: decodeEscapedText(normalizeTemplateLineTerminators(rawValue))
3043
+ cooked: cooked.invalid === undefined ? cooked.value : undefined
3008
3044
  },
3009
3045
  span: createSpan(positionWithinRaw(templateStart, rawTemplate, rawStart), positionWithinRaw(templateStart, rawTemplate, rawEnd))
3010
3046
  };
3011
3047
  }
3048
+ function decodeTemplateElementCooked(value, allowMalformedEscapes) {
3049
+ const normalized = normalizeTemplateLineTerminators(value);
3050
+ const invalid = findMalformedTemplateEscape(normalized);
3051
+ if (invalid !== undefined) {
3052
+ return allowMalformedEscapes ? { invalid } : { invalid };
3053
+ }
3054
+ return {
3055
+ value: decodeEscapedText(normalized)
3056
+ };
3057
+ }
3058
+ function findMalformedTemplateEscape(value) {
3059
+ let index = 0;
3060
+ while (index < value.length) {
3061
+ if (value[index] !== "\\") {
3062
+ index += 1;
3063
+ continue;
3064
+ }
3065
+ const next = value[index + 1];
3066
+ if (next === undefined || next === "\n") {
3067
+ index += 2;
3068
+ continue;
3069
+ }
3070
+ if (next === "u") {
3071
+ if (!isValidUnicodeEscape(value, index)) {
3072
+ return { index, message: "Invalid unicode escape" };
3073
+ }
3074
+ index += 2;
3075
+ continue;
3076
+ }
3077
+ if (next === "x") {
3078
+ const hex = value.slice(index + 2, index + 4);
3079
+ if (hex.length !== 2 || ![...hex].every(isHexDigit)) {
3080
+ return { index, message: "Invalid hex escape" };
3081
+ }
3082
+ index += 4;
3083
+ continue;
3084
+ }
3085
+ if (next === "0") {
3086
+ if (isDecimalDigit(value[index + 2] ?? "")) {
3087
+ return { index, message: "Legacy octal escape sequences are not supported" };
3088
+ }
3089
+ index += 2;
3090
+ continue;
3091
+ }
3092
+ if (isOctalDigit(next)) {
3093
+ return { index, message: "Legacy octal escape sequences are not supported" };
3094
+ }
3095
+ index += 2;
3096
+ }
3097
+ return undefined;
3098
+ }
3099
+ function isValidUnicodeEscape(value, start) {
3100
+ let index = start + 2;
3101
+ if (value[index] === "{") {
3102
+ index += 1;
3103
+ const codePointStart = index;
3104
+ while (index < value.length && value[index] !== "}") {
3105
+ if (!isHexDigit(value[index] ?? "")) {
3106
+ return false;
3107
+ }
3108
+ index += 1;
3109
+ }
3110
+ if (index === codePointStart || value[index] !== "}") {
3111
+ return false;
3112
+ }
3113
+ return Number.parseInt(value.slice(codePointStart, index), 16) <= MAX_UNICODE_CODE_POINT;
3114
+ }
3115
+ const hex = value.slice(index, index + 4);
3116
+ return hex.length === 4 && [...hex].every(isHexDigit);
3117
+ }
3012
3118
  function findTemplateExpressionEnd(raw, start) {
3013
3119
  let depth = 1;
3014
3120
  let index = start;
@@ -164,6 +164,10 @@ class Lexer {
164
164
  skipTrivia() {
165
165
  while (!this.isAtEnd()) {
166
166
  const char = this.currentChar();
167
+ if (this.isHashbangCommentStart()) {
168
+ this.skipLineComment();
169
+ continue;
170
+ }
167
171
  if (isWhitespace(char) || isLineBreak(char)) {
168
172
  this.advance();
169
173
  continue;
@@ -244,7 +248,7 @@ class Lexer {
244
248
  return;
245
249
  }
246
250
  if (char === "\\") {
247
- this.readEscapedLiteralCharacter();
251
+ this.skipEscapedTemplateCharacter();
248
252
  continue;
249
253
  }
250
254
  if (char === "$" && this.peekChar(1) === "{") {
@@ -405,7 +409,7 @@ class Lexer {
405
409
  return;
406
410
  }
407
411
  if (char === "\\") {
408
- this.readEscapedLiteralCharacter();
412
+ this.skipEscapedTemplateCharacter();
409
413
  continue;
410
414
  }
411
415
  if (char === "$" && this.peekChar(1) === "{") {
@@ -706,6 +710,10 @@ class Lexer {
706
710
  isHtmlStyleCommentDelimiter() {
707
711
  return this.source.startsWith("<!--", this.index) || this.source.startsWith("-->", this.index);
708
712
  }
713
+ isHashbangCommentStart() {
714
+ return (this.source.startsWith("#!", this.index) &&
715
+ (this.index === 0 || (this.index === 1 && this.source[0] === "\uFEFF")));
716
+ }
709
717
  readUnicodeEscape() {
710
718
  const escapeStart = this.position();
711
719
  this.advance();
@@ -760,6 +768,21 @@ class Lexer {
760
768
  }
761
769
  this.advance();
762
770
  }
771
+ skipEscapedTemplateCharacter() {
772
+ this.advance();
773
+ if (this.isAtEnd()) {
774
+ return;
775
+ }
776
+ const escaped = this.currentChar();
777
+ if (escaped === "\r") {
778
+ this.advance();
779
+ if (this.currentChar() === "\n") {
780
+ this.advance();
781
+ }
782
+ return;
783
+ }
784
+ this.advance();
785
+ }
763
786
  readExtendedUnicodeEscape(escapeStart) {
764
787
  this.advance();
765
788
  const codePointStart = this.position();
@@ -852,7 +875,7 @@ function isOctalDigit(char) {
852
875
  return char >= "0" && char <= "7";
853
876
  }
854
877
  function isWhitespace(char) {
855
- return char === " " || char === "\t" || char === "\v" || char === "\f";
878
+ return char === " " || char === "\t" || char === "\v" || char === "\f" || char === "\uFEFF";
856
879
  }
857
880
  function isLineBreak(char) {
858
881
  return char === "\n" || char === "\r";
@@ -5,6 +5,7 @@ import { restore } from "./restore.js";
5
5
  import { Budget } from "./interp/budget.js";
6
6
  import { wrapCancelableBindings } from "./interp/cancel.js";
7
7
  import { enterSnapshotRun } from "./interp/running-state.js";
8
+ import { createSandboxPromiseRejectionTracker, observeSandboxPromise, withSandboxPromiseRejectionTracker } from "./interp/promise-tracker.js";
8
9
  import { HostCallJournal } from "./interp/host-call.js";
9
10
  import { createConsoleJsonGlobals } from "./interp/globals/console-json.js";
10
11
  import { createCollectionGlobals } from "./interp/globals/collections.js";
@@ -33,10 +34,12 @@ export class UnhandledRejectionError extends Error {
33
34
  replaceErrorStack(this);
34
35
  }
35
36
  }
37
+ const DEFAULT_MAX_CALL_DEPTH = 1_000;
36
38
  export function run(source, options = {}) {
37
39
  const lifecycle = { hostCallbackDepth: 0 };
38
40
  const dumpController = createDumpController(lifecycle);
39
- const result = (async () => {
41
+ const promiseTracker = createSandboxPromiseRejectionTracker();
42
+ const result = withSandboxPromiseRejectionTracker(promiseTracker, async () => {
40
43
  const deactivateOtelSink = activateOtelSink(options.otelSink);
41
44
  let leaveSnapshotRun;
42
45
  let createFailureSnapshot;
@@ -46,7 +49,7 @@ export function run(source, options = {}) {
46
49
  if (restoredSnapshot !== undefined) {
47
50
  leaveSnapshotRun = enterSnapshotRun(restoredSnapshot);
48
51
  }
49
- const budget = options.budget ?? new Budget();
52
+ const budget = options.budget ?? new Budget({ maxCallDepth: DEFAULT_MAX_CALL_DEPTH });
50
53
  budget.reset();
51
54
  const filename = options.filename ?? "<input>";
52
55
  const module = parseExecutableModule(source, filename);
@@ -189,6 +192,7 @@ export function run(source, options = {}) {
189
192
  });
190
193
  dumpController.finalize(snapshot);
191
194
  await throwIfReturnedPromiseRejected(result);
195
+ await throwIfUnhandledPromiseRejected(promiseTracker);
192
196
  return {
193
197
  ...result,
194
198
  snapshot
@@ -218,13 +222,14 @@ export function run(source, options = {}) {
218
222
  leaveSnapshotRun?.();
219
223
  deactivateOtelSink();
220
224
  }
221
- })();
225
+ });
222
226
  return attachDumpController(result, dumpController);
223
227
  }
224
228
  async function throwIfReturnedPromiseRejected(result) {
225
229
  if (!result.ok || !isSandboxPromise(result.returnValue)) {
226
230
  return;
227
231
  }
232
+ observeSandboxPromise(result.returnValue);
228
233
  let rejected = false;
229
234
  let rejectionReason;
230
235
  result.returnValue.promise.then(() => undefined, (reason) => {
@@ -240,6 +245,12 @@ async function throwIfReturnedPromiseRejected(result) {
240
245
  throw new UnhandledRejectionError(rejectionReason, result.returnValue.span);
241
246
  }
242
247
  }
248
+ async function throwIfUnhandledPromiseRejected(tracker) {
249
+ const unhandled = await tracker.findUnhandledRejection();
250
+ if (unhandled !== undefined) {
251
+ throw new UnhandledRejectionError(unhandled.reason, unhandled.span);
252
+ }
253
+ }
243
254
  async function callEntryPoint(input) {
244
255
  const defaultExport = input.scope.lookup("default");
245
256
  if (!defaultExport.found) {
@@ -2,6 +2,7 @@ import { readFile, stat } from "node:fs/promises";
2
2
  import { extname } from "node:path";
3
3
  import { supportsSpawnMode } from "@poe-code/agent-spawn/configs";
4
4
  import { SPAWN_MODES } from "@poe-code/agent-spawn/types";
5
+ import { hasOwnErrorCode } from "../error-codes.js";
5
6
  import { extractBlock } from "../loader/extract-block.js";
6
7
  import { splitFrontmatter } from "../loader/frontmatter.js";
7
8
  import { lint } from "../lint.js";
@@ -86,6 +87,10 @@ export async function runHarnessPair(filepath, options) {
86
87
  const modules = options.modulesFor(frontmatter, meta);
87
88
  const diagnostics = lint(executableSource, {
88
89
  allowedExportNames: ["schema"],
90
+ defaultExport: {
91
+ parameters: ["frontmatter"],
92
+ required: true
93
+ },
89
94
  filename: pair.scriptPath,
90
95
  frontmatterFields: Object.keys(frontmatter),
91
96
  modules: createLintModulesFromRuntimeRegistry(modules)
@@ -112,11 +117,19 @@ export async function runHarnessPair(filepath, options) {
112
117
  });
113
118
  }
114
119
  async function readHarnessFile(filepath) {
115
- const stats = await stat(filepath);
116
- if (!stats.isFile()) {
117
- throw new Error(`Harness path must point to a file: ${filepath}`);
120
+ try {
121
+ const stats = await stat(filepath);
122
+ if (!stats.isFile()) {
123
+ throw new Error(`Harness path must point to a file: ${filepath}`);
124
+ }
125
+ return await readFile(filepath, "utf8");
126
+ }
127
+ catch (error) {
128
+ if (hasOwnErrorCode(error, "ENOENT") || hasOwnErrorCode(error, "ENOTDIR")) {
129
+ throw new Error(`Harness file not found: ${filepath}`);
130
+ }
131
+ throw error;
118
132
  }
119
- return readFile(filepath, "utf8");
120
133
  }
121
134
  function loadExecutableSource(filepath, source) {
122
135
  if (extname(filepath) === ".ajs") {
@@ -136,7 +149,7 @@ function loadExecutableSource(filepath, source) {
136
149
  const { source: executableBlock, lineOffset } = extractBlock(body);
137
150
  const absoluteLineOffset = countLineBreaks(source.slice(0, source.length - body.length)) + lineOffset;
138
151
  return {
139
- executableSource: createLineOffsetSource(executableBlock, absoluteLineOffset),
152
+ executableSource: createLineOffsetSource(stripHashbang(executableBlock), absoluteLineOffset),
140
153
  frontmatter,
141
154
  isRawScript: false
142
155
  };
@@ -202,6 +215,16 @@ function excludeHarnessModule(modules, isRawScript) {
202
215
  function stripByteOrderMark(source) {
203
216
  return source.startsWith("\uFEFF") ? source.slice(1) : source;
204
217
  }
218
+ function stripHashbang(source) {
219
+ if (!source.startsWith("#!")) {
220
+ return source;
221
+ }
222
+ const lineBreakIndex = source.indexOf("\n");
223
+ if (lineBreakIndex === -1) {
224
+ return "";
225
+ }
226
+ return source.slice(lineBreakIndex + 1);
227
+ }
205
228
  function countLineBreaks(source) {
206
229
  let count = 0;
207
230
  for (const character of source) {
@@ -0,0 +1,4 @@
1
+ import type { CollectHumanPromptsOptions, CollectHumanPromptsResult, HumanPromptRecord, TraceReader } from "./types.js";
2
+ export declare function collectHumanPromptsFromReaders(readers: TraceReader[], options?: CollectHumanPromptsOptions): Promise<CollectHumanPromptsResult>;
3
+ export declare function collectHumanPrompts(options?: CollectHumanPromptsOptions): Promise<HumanPromptRecord[]>;
4
+ export declare function collectHumanPromptsWithStats(options?: CollectHumanPromptsOptions): Promise<CollectHumanPromptsResult>;