@garygentry/system1 0.4.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,15 +1,13 @@
1
1
  import { createRequire as __decideRequire } from "node:module"; const require = __decideRequire(import.meta.url);
2
2
  import {
3
- CLI_PACKAGE,
4
3
  DecisionsError,
5
4
  ProviderError,
6
- VERSION,
7
5
  __commonJS,
8
6
  __export,
9
7
  __require,
10
8
  __toESM,
11
9
  isDecisionsError
12
- } from "./chunk-B62DSXC3.mjs";
10
+ } from "./chunk-MH6THLCO.mjs";
13
11
 
14
12
  // ../../node_modules/.pnpm/yaml@2.9.1/node_modules/yaml/dist/nodes/identity.js
15
13
  var require_identity = __commonJS({
@@ -24,9 +22,9 @@ var require_identity = __commonJS({
24
22
  var NODE_TYPE = /* @__PURE__ */ Symbol.for("yaml.node.type");
25
23
  var isAlias = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === ALIAS;
26
24
  var isDocument = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === DOC;
27
- var isMap = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === MAP;
25
+ var isMap2 = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === MAP;
28
26
  var isPair = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === PAIR;
29
- var isScalar = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === SCALAR;
27
+ var isScalar2 = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === SCALAR;
30
28
  var isSeq = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === SEQ;
31
29
  function isCollection(node) {
32
30
  if (node && typeof node === "object")
@@ -48,7 +46,7 @@ var require_identity = __commonJS({
48
46
  }
49
47
  return false;
50
48
  }
51
- var hasAnchor = (node) => (isScalar(node) || isCollection(node)) && !!node.anchor;
49
+ var hasAnchor = (node) => (isScalar2(node) || isCollection(node)) && !!node.anchor;
52
50
  exports.ALIAS = ALIAS;
53
51
  exports.DOC = DOC;
54
52
  exports.MAP = MAP;
@@ -60,10 +58,10 @@ var require_identity = __commonJS({
60
58
  exports.isAlias = isAlias;
61
59
  exports.isCollection = isCollection;
62
60
  exports.isDocument = isDocument;
63
- exports.isMap = isMap;
61
+ exports.isMap = isMap2;
64
62
  exports.isNode = isNode;
65
63
  exports.isPair = isPair;
66
- exports.isScalar = isScalar;
64
+ exports.isScalar = isScalar2;
67
65
  exports.isSeq = isSeq;
68
66
  }
69
67
  });
@@ -3764,29 +3762,29 @@ var require_resolve_props = __commonJS({
3764
3762
  let comma = null;
3765
3763
  let found2 = null;
3766
3764
  let start = null;
3767
- for (const token of tokens) {
3765
+ for (const token2 of tokens) {
3768
3766
  if (reqSpace) {
3769
- if (token.type !== "space" && token.type !== "newline" && token.type !== "comma")
3770
- onError(token.offset, "MISSING_CHAR", "Tags and anchors must be separated from the next token by white space");
3767
+ if (token2.type !== "space" && token2.type !== "newline" && token2.type !== "comma")
3768
+ onError(token2.offset, "MISSING_CHAR", "Tags and anchors must be separated from the next token by white space");
3771
3769
  reqSpace = false;
3772
3770
  }
3773
3771
  if (tab) {
3774
- if (atNewline && token.type !== "comment" && token.type !== "newline") {
3772
+ if (atNewline && token2.type !== "comment" && token2.type !== "newline") {
3775
3773
  onError(tab, "TAB_AS_INDENT", "Tabs are not allowed as indentation");
3776
3774
  }
3777
3775
  tab = null;
3778
3776
  }
3779
- switch (token.type) {
3777
+ switch (token2.type) {
3780
3778
  case "space":
3781
- if (!flow && (indicator !== "doc-start" || next?.type !== "flow-collection") && token.source.includes(" ")) {
3782
- tab = token;
3779
+ if (!flow && (indicator !== "doc-start" || next?.type !== "flow-collection") && token2.source.includes(" ")) {
3780
+ tab = token2;
3783
3781
  }
3784
3782
  hasSpace = true;
3785
3783
  break;
3786
3784
  case "comment": {
3787
3785
  if (!hasSpace)
3788
- onError(token, "MISSING_CHAR", "Comments must be separated from other tokens by white space characters");
3789
- const cb = token.source.substring(1) || " ";
3786
+ onError(token2, "MISSING_CHAR", "Comments must be separated from other tokens by white space characters");
3787
+ const cb = token2.source.substring(1) || " ";
3790
3788
  if (!comment)
3791
3789
  comment = cb;
3792
3790
  else
@@ -3798,33 +3796,33 @@ var require_resolve_props = __commonJS({
3798
3796
  case "newline":
3799
3797
  if (atNewline) {
3800
3798
  if (comment)
3801
- comment += token.source;
3799
+ comment += token2.source;
3802
3800
  else if (!found2 || indicator !== "seq-item-ind")
3803
3801
  spaceBefore = true;
3804
3802
  } else
3805
- commentSep += token.source;
3803
+ commentSep += token2.source;
3806
3804
  atNewline = true;
3807
3805
  hasNewline = true;
3808
3806
  if (anchor || tag)
3809
- newlineAfterProp = token;
3807
+ newlineAfterProp = token2;
3810
3808
  hasSpace = true;
3811
3809
  break;
3812
3810
  case "anchor":
3813
3811
  if (anchor)
3814
- onError(token, "MULTIPLE_ANCHORS", "A node can have at most one anchor");
3815
- if (token.source.endsWith(":"))
3816
- onError(token.offset + token.source.length - 1, "BAD_ALIAS", "Anchor ending in : is ambiguous", true);
3817
- anchor = token;
3818
- start ?? (start = token.offset);
3812
+ onError(token2, "MULTIPLE_ANCHORS", "A node can have at most one anchor");
3813
+ if (token2.source.endsWith(":"))
3814
+ onError(token2.offset + token2.source.length - 1, "BAD_ALIAS", "Anchor ending in : is ambiguous", true);
3815
+ anchor = token2;
3816
+ start ?? (start = token2.offset);
3819
3817
  atNewline = false;
3820
3818
  hasSpace = false;
3821
3819
  reqSpace = true;
3822
3820
  break;
3823
3821
  case "tag": {
3824
3822
  if (tag)
3825
- onError(token, "MULTIPLE_TAGS", "A node can have at most one tag");
3826
- tag = token;
3827
- start ?? (start = token.offset);
3823
+ onError(token2, "MULTIPLE_TAGS", "A node can have at most one tag");
3824
+ tag = token2;
3825
+ start ?? (start = token2.offset);
3828
3826
  atNewline = false;
3829
3827
  hasSpace = false;
3830
3828
  reqSpace = true;
@@ -3832,25 +3830,25 @@ var require_resolve_props = __commonJS({
3832
3830
  }
3833
3831
  case indicator:
3834
3832
  if (anchor || tag)
3835
- onError(token, "BAD_PROP_ORDER", `Anchors and tags must be after the ${token.source} indicator`);
3833
+ onError(token2, "BAD_PROP_ORDER", `Anchors and tags must be after the ${token2.source} indicator`);
3836
3834
  if (found2)
3837
- onError(token, "UNEXPECTED_TOKEN", `Unexpected ${token.source} in ${flow ?? "collection"}`);
3838
- found2 = token;
3835
+ onError(token2, "UNEXPECTED_TOKEN", `Unexpected ${token2.source} in ${flow ?? "collection"}`);
3836
+ found2 = token2;
3839
3837
  atNewline = indicator === "seq-item-ind" || indicator === "explicit-key-ind";
3840
3838
  hasSpace = false;
3841
3839
  break;
3842
3840
  case "comma":
3843
3841
  if (flow) {
3844
3842
  if (comma)
3845
- onError(token, "UNEXPECTED_TOKEN", `Unexpected , in ${flow}`);
3846
- comma = token;
3843
+ onError(token2, "UNEXPECTED_TOKEN", `Unexpected , in ${flow}`);
3844
+ comma = token2;
3847
3845
  atNewline = false;
3848
3846
  hasSpace = false;
3849
3847
  break;
3850
3848
  }
3851
3849
  // else fallthrough
3852
3850
  default:
3853
- onError(token, "UNEXPECTED_TOKEN", `Unexpected ${token.type} token`);
3851
+ onError(token2, "UNEXPECTED_TOKEN", `Unexpected ${token2.type} token`);
3854
3852
  atNewline = false;
3855
3853
  hasSpace = false;
3856
3854
  }
@@ -4123,15 +4121,15 @@ var require_resolve_end = __commonJS({
4123
4121
  if (end) {
4124
4122
  let hasSpace = false;
4125
4123
  let sep2 = "";
4126
- for (const token of end) {
4127
- const { source, type } = token;
4124
+ for (const token2 of end) {
4125
+ const { source, type } = token2;
4128
4126
  switch (type) {
4129
4127
  case "space":
4130
4128
  hasSpace = true;
4131
4129
  break;
4132
4130
  case "comment": {
4133
4131
  if (reqSpace && !hasSpace)
4134
- onError(token, "MISSING_CHAR", "Comments must be separated from other tokens by white space characters");
4132
+ onError(token2, "MISSING_CHAR", "Comments must be separated from other tokens by white space characters");
4135
4133
  const cb = source.substring(1) || " ";
4136
4134
  if (!comment)
4137
4135
  comment = cb;
@@ -4146,7 +4144,7 @@ var require_resolve_end = __commonJS({
4146
4144
  hasSpace = true;
4147
4145
  break;
4148
4146
  default:
4149
- onError(token, "UNEXPECTED_TOKEN", `Unexpected ${type} at node end`);
4147
+ onError(token2, "UNEXPECTED_TOKEN", `Unexpected ${type} at node end`);
4150
4148
  }
4151
4149
  offset += source.length;
4152
4150
  }
@@ -4170,11 +4168,11 @@ var require_resolve_flow_collection = __commonJS({
4170
4168
  var utilContainsNewline = require_util_contains_newline();
4171
4169
  var utilMapIncludes = require_util_map_includes();
4172
4170
  var blockMsg = "Block collections are not allowed within flow collections";
4173
- var isBlock = (token) => token && (token.type === "block-map" || token.type === "block-seq");
4171
+ var isBlock = (token2) => token2 && (token2.type === "block-map" || token2.type === "block-seq");
4174
4172
  function resolveFlowCollection({ composeNode, composeEmptyNode }, ctx, fc, onError, tag) {
4175
- const isMap = fc.start.source === "{";
4176
- const fcName = isMap ? "flow map" : "flow sequence";
4177
- const NodeClass = tag?.nodeClass ?? (isMap ? YAMLMap.YAMLMap : YAMLSeq.YAMLSeq);
4173
+ const isMap2 = fc.start.source === "{";
4174
+ const fcName = isMap2 ? "flow map" : "flow sequence";
4175
+ const NodeClass = tag?.nodeClass ?? (isMap2 ? YAMLMap.YAMLMap : YAMLSeq.YAMLSeq);
4178
4176
  const coll = new NodeClass(ctx.schema);
4179
4177
  coll.flow = true;
4180
4178
  const atRoot = ctx.atRoot;
@@ -4210,7 +4208,7 @@ var require_resolve_flow_collection = __commonJS({
4210
4208
  offset = props.end;
4211
4209
  continue;
4212
4210
  }
4213
- if (!isMap && ctx.options.strict && utilContainsNewline.containsNewline(key))
4211
+ if (!isMap2 && ctx.options.strict && utilContainsNewline.containsNewline(key))
4214
4212
  onError(
4215
4213
  key,
4216
4214
  // checked by containsNewline()
@@ -4250,7 +4248,7 @@ var require_resolve_flow_collection = __commonJS({
4250
4248
  }
4251
4249
  }
4252
4250
  }
4253
- if (!isMap && !sep2 && !props.found) {
4251
+ if (!isMap2 && !sep2 && !props.found) {
4254
4252
  const valueNode = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end, sep2, null, props, onError);
4255
4253
  coll.items.push(valueNode);
4256
4254
  offset = valueNode.range[2];
@@ -4273,7 +4271,7 @@ var require_resolve_flow_collection = __commonJS({
4273
4271
  startOnNewline: false
4274
4272
  });
4275
4273
  if (valueProps.found) {
4276
- if (!isMap && !props.found && ctx.options.strict) {
4274
+ if (!isMap2 && !props.found && ctx.options.strict) {
4277
4275
  if (sep2)
4278
4276
  for (const st of sep2) {
4279
4277
  if (st === valueProps.found)
@@ -4305,7 +4303,7 @@ var require_resolve_flow_collection = __commonJS({
4305
4303
  const pair = new Pair.Pair(keyNode, valueNode);
4306
4304
  if (ctx.options.keepSourceTokens)
4307
4305
  pair.srcToken = collItem;
4308
- if (isMap) {
4306
+ if (isMap2) {
4309
4307
  const map = coll;
4310
4308
  if (utilMapIncludes.mapIncludes(ctx, map.items, keyNode))
4311
4309
  onError(keyStart, "DUPLICATE_KEY", "Map keys must be unique");
@@ -4321,7 +4319,7 @@ var require_resolve_flow_collection = __commonJS({
4321
4319
  offset = valueNode ? valueNode.range[2] : valueProps.end;
4322
4320
  }
4323
4321
  }
4324
- const expectedEnd = isMap ? "}" : "]";
4322
+ const expectedEnd = isMap2 ? "}" : "]";
4325
4323
  const [ce, ...ee] = fc.end;
4326
4324
  let cePos = offset;
4327
4325
  if (ce?.source === expectedEnd)
@@ -4362,8 +4360,8 @@ var require_compose_collection = __commonJS({
4362
4360
  var resolveBlockMap = require_resolve_block_map();
4363
4361
  var resolveBlockSeq = require_resolve_block_seq();
4364
4362
  var resolveFlowCollection = require_resolve_flow_collection();
4365
- function resolveCollection(CN, ctx, token, onError, tagName, tag) {
4366
- const coll = token.type === "block-map" ? resolveBlockMap.resolveBlockMap(CN, ctx, token, onError, tag) : token.type === "block-seq" ? resolveBlockSeq.resolveBlockSeq(CN, ctx, token, onError, tag) : resolveFlowCollection.resolveFlowCollection(CN, ctx, token, onError, tag);
4363
+ function resolveCollection(CN, ctx, token2, onError, tagName, tag) {
4364
+ const coll = token2.type === "block-map" ? resolveBlockMap.resolveBlockMap(CN, ctx, token2, onError, tag) : token2.type === "block-seq" ? resolveBlockSeq.resolveBlockSeq(CN, ctx, token2, onError, tag) : resolveFlowCollection.resolveFlowCollection(CN, ctx, token2, onError, tag);
4367
4365
  const Coll = coll.constructor;
4368
4366
  if (tagName === "!" || tagName === Coll.tagName) {
4369
4367
  coll.tag = Coll.tagName;
@@ -4373,10 +4371,10 @@ var require_compose_collection = __commonJS({
4373
4371
  coll.tag = tagName;
4374
4372
  return coll;
4375
4373
  }
4376
- function composeCollection(CN, ctx, token, props, onError) {
4374
+ function composeCollection(CN, ctx, token2, props, onError) {
4377
4375
  const tagToken = props.tag;
4378
4376
  const tagName = !tagToken ? null : ctx.directives.tagName(tagToken.source, (msg) => onError(tagToken, "TAG_RESOLVE_FAILED", msg));
4379
- if (token.type === "block-seq") {
4377
+ if (token2.type === "block-seq") {
4380
4378
  const { anchor, newlineAfterProp: nl } = props;
4381
4379
  const lastProp = anchor && tagToken ? anchor.offset > tagToken.offset ? anchor : tagToken : anchor ?? tagToken;
4382
4380
  if (lastProp && (!nl || nl.offset < lastProp.offset)) {
@@ -4384,9 +4382,9 @@ var require_compose_collection = __commonJS({
4384
4382
  onError(lastProp, "MISSING_CHAR", message);
4385
4383
  }
4386
4384
  }
4387
- const expType = token.type === "block-map" ? "map" : token.type === "block-seq" ? "seq" : token.start.source === "{" ? "map" : "seq";
4385
+ const expType = token2.type === "block-map" ? "map" : token2.type === "block-seq" ? "seq" : token2.start.source === "{" ? "map" : "seq";
4388
4386
  if (!tagToken || !tagName || tagName === "!" || tagName === YAMLMap.YAMLMap.tagName && expType === "map" || tagName === YAMLSeq.YAMLSeq.tagName && expType === "seq") {
4389
- return resolveCollection(CN, ctx, token, onError, tagName);
4387
+ return resolveCollection(CN, ctx, token2, onError, tagName);
4390
4388
  }
4391
4389
  let tag = ctx.schema.tags.find((t) => t.tag === tagName && t.collection === expType);
4392
4390
  if (!tag) {
@@ -4400,10 +4398,10 @@ var require_compose_collection = __commonJS({
4400
4398
  } else {
4401
4399
  onError(tagToken, "TAG_RESOLVE_FAILED", `Unresolved tag: ${tagName}`, true);
4402
4400
  }
4403
- return resolveCollection(CN, ctx, token, onError, tagName);
4401
+ return resolveCollection(CN, ctx, token2, onError, tagName);
4404
4402
  }
4405
4403
  }
4406
- const coll = resolveCollection(CN, ctx, token, onError, tagName, tag);
4404
+ const coll = resolveCollection(CN, ctx, token2, onError, tagName, tag);
4407
4405
  const res = tag.resolve?.(coll, (msg) => onError(tagToken, "TAG_RESOLVE_FAILED", msg), ctx.options) ?? coll;
4408
4406
  const node = identity.isNode(res) ? res : new Scalar.Scalar(res);
4409
4407
  node.range = coll.range;
@@ -4553,31 +4551,31 @@ var require_resolve_block_scalar = __commonJS({
4553
4551
  let comment = "";
4554
4552
  let length = source.length;
4555
4553
  for (let i = 1; i < props.length; ++i) {
4556
- const token = props[i];
4557
- switch (token.type) {
4554
+ const token2 = props[i];
4555
+ switch (token2.type) {
4558
4556
  case "space":
4559
4557
  hasSpace = true;
4560
4558
  // fallthrough
4561
4559
  case "newline":
4562
- length += token.source.length;
4560
+ length += token2.source.length;
4563
4561
  break;
4564
4562
  case "comment":
4565
4563
  if (strict2 && !hasSpace) {
4566
4564
  const message = "Comments must be separated from other tokens by white space characters";
4567
- onError(token, "MISSING_CHAR", message);
4565
+ onError(token2, "MISSING_CHAR", message);
4568
4566
  }
4569
- length += token.source.length;
4570
- comment = token.source.substring(1);
4567
+ length += token2.source.length;
4568
+ comment = token2.source.substring(1);
4571
4569
  break;
4572
4570
  case "error":
4573
- onError(token, "UNEXPECTED_TOKEN", token.message);
4574
- length += token.source.length;
4571
+ onError(token2, "UNEXPECTED_TOKEN", token2.message);
4572
+ length += token2.source.length;
4575
4573
  break;
4576
4574
  /* istanbul ignore next should not happen */
4577
4575
  default: {
4578
- const message = `Unexpected token in block scalar header: ${token.type}`;
4579
- onError(token, "UNEXPECTED_TOKEN", message);
4580
- const ts = token.source;
4576
+ const message = `Unexpected token in block scalar header: ${token2.type}`;
4577
+ onError(token2, "UNEXPECTED_TOKEN", message);
4578
+ const ts = token2.source;
4581
4579
  if (ts && typeof ts === "string")
4582
4580
  length += ts.length;
4583
4581
  }
@@ -4828,25 +4826,25 @@ var require_compose_scalar = __commonJS({
4828
4826
  var Scalar = require_Scalar();
4829
4827
  var resolveBlockScalar = require_resolve_block_scalar();
4830
4828
  var resolveFlowScalar = require_resolve_flow_scalar();
4831
- function composeScalar(ctx, token, tagToken, onError) {
4832
- const { value, type, comment, range } = token.type === "block-scalar" ? resolveBlockScalar.resolveBlockScalar(ctx, token, onError) : resolveFlowScalar.resolveFlowScalar(token, ctx.options.strict, onError);
4829
+ function composeScalar(ctx, token2, tagToken, onError) {
4830
+ const { value, type, comment, range } = token2.type === "block-scalar" ? resolveBlockScalar.resolveBlockScalar(ctx, token2, onError) : resolveFlowScalar.resolveFlowScalar(token2, ctx.options.strict, onError);
4833
4831
  const tagName = tagToken ? ctx.directives.tagName(tagToken.source, (msg) => onError(tagToken, "TAG_RESOLVE_FAILED", msg)) : null;
4834
4832
  let tag;
4835
4833
  if (ctx.options.stringKeys && ctx.atKey) {
4836
4834
  tag = ctx.schema[identity.SCALAR];
4837
4835
  } else if (tagName)
4838
4836
  tag = findScalarTagByName(ctx.schema, value, tagName, tagToken, onError);
4839
- else if (token.type === "scalar")
4840
- tag = findScalarTagByTest(ctx, value, token, onError);
4837
+ else if (token2.type === "scalar")
4838
+ tag = findScalarTagByTest(ctx, value, token2, onError);
4841
4839
  else
4842
4840
  tag = ctx.schema[identity.SCALAR];
4843
4841
  let scalar;
4844
4842
  try {
4845
- const res = tag.resolve(value, (msg) => onError(tagToken ?? token, "TAG_RESOLVE_FAILED", msg), ctx.options);
4843
+ const res = tag.resolve(value, (msg) => onError(tagToken ?? token2, "TAG_RESOLVE_FAILED", msg), ctx.options);
4846
4844
  scalar = identity.isScalar(res) ? res : new Scalar.Scalar(res);
4847
4845
  } catch (error) {
4848
4846
  const msg = error instanceof Error ? error.message : String(error);
4849
- onError(tagToken ?? token, "TAG_RESOLVE_FAILED", msg);
4847
+ onError(tagToken ?? token2, "TAG_RESOLVE_FAILED", msg);
4850
4848
  scalar = new Scalar.Scalar(value);
4851
4849
  }
4852
4850
  scalar.range = range;
@@ -4884,7 +4882,7 @@ var require_compose_scalar = __commonJS({
4884
4882
  onError(tagToken, "TAG_RESOLVE_FAILED", `Unresolved tag: ${tagName}`, tagName !== "tag:yaml.org,2002:str");
4885
4883
  return schema[identity.SCALAR];
4886
4884
  }
4887
- function findScalarTagByTest({ atKey, directives, schema }, value, token, onError) {
4885
+ function findScalarTagByTest({ atKey, directives, schema }, value, token2, onError) {
4888
4886
  const tag = schema.tags.find((tag2) => (tag2.default === true || atKey && tag2.default === "key") && tag2.test?.test(value)) || schema[identity.SCALAR];
4889
4887
  if (schema.compat) {
4890
4888
  const compat = schema.compat.find((tag2) => tag2.default && tag2.test?.test(value)) ?? schema[identity.SCALAR];
@@ -4892,7 +4890,7 @@ var require_compose_scalar = __commonJS({
4892
4890
  const ts = directives.tagString(tag.tag);
4893
4891
  const cs = directives.tagString(compat.tag);
4894
4892
  const msg = `Value may be parsed as either ${ts} or ${cs}`;
4895
- onError(token, "TAG_RESOLVE_FAILED", msg, true);
4893
+ onError(token2, "TAG_RESOLVE_FAILED", msg, true);
4896
4894
  }
4897
4895
  }
4898
4896
  return tag;
@@ -4942,22 +4940,22 @@ var require_compose_node = __commonJS({
4942
4940
  var resolveEnd = require_resolve_end();
4943
4941
  var utilEmptyScalarPosition = require_util_empty_scalar_position();
4944
4942
  var CN = { composeNode, composeEmptyNode };
4945
- function composeNode(ctx, token, props, onError) {
4943
+ function composeNode(ctx, token2, props, onError) {
4946
4944
  const atKey = ctx.atKey;
4947
4945
  const { spaceBefore, comment, anchor, tag } = props;
4948
4946
  let node;
4949
4947
  let isSrcToken = true;
4950
- switch (token.type) {
4948
+ switch (token2.type) {
4951
4949
  case "alias":
4952
- node = composeAlias(ctx, token, onError);
4950
+ node = composeAlias(ctx, token2, onError);
4953
4951
  if (anchor || tag)
4954
- onError(token, "ALIAS_PROPS", "An alias node must not specify any properties");
4952
+ onError(token2, "ALIAS_PROPS", "An alias node must not specify any properties");
4955
4953
  break;
4956
4954
  case "scalar":
4957
4955
  case "single-quoted-scalar":
4958
4956
  case "double-quoted-scalar":
4959
4957
  case "block-scalar":
4960
- node = composeScalar.composeScalar(ctx, token, tag, onError);
4958
+ node = composeScalar.composeScalar(ctx, token2, tag, onError);
4961
4959
  if (anchor)
4962
4960
  node.anchor = anchor.source.substring(1);
4963
4961
  break;
@@ -4965,47 +4963,47 @@ var require_compose_node = __commonJS({
4965
4963
  case "block-seq":
4966
4964
  case "flow-collection":
4967
4965
  try {
4968
- node = composeCollection.composeCollection(CN, ctx, token, props, onError);
4966
+ node = composeCollection.composeCollection(CN, ctx, token2, props, onError);
4969
4967
  if (anchor)
4970
4968
  node.anchor = anchor.source.substring(1);
4971
4969
  } catch (error) {
4972
4970
  const message = error instanceof Error ? error.message : String(error);
4973
- onError(token, "RESOURCE_EXHAUSTION", message);
4971
+ onError(token2, "RESOURCE_EXHAUSTION", message);
4974
4972
  }
4975
4973
  break;
4976
4974
  default: {
4977
- const message = token.type === "error" ? token.message : `Unsupported token (type: ${token.type})`;
4978
- onError(token, "UNEXPECTED_TOKEN", message);
4975
+ const message = token2.type === "error" ? token2.message : `Unsupported token (type: ${token2.type})`;
4976
+ onError(token2, "UNEXPECTED_TOKEN", message);
4979
4977
  isSrcToken = false;
4980
4978
  }
4981
4979
  }
4982
- node ?? (node = composeEmptyNode(ctx, token.offset, void 0, null, props, onError));
4980
+ node ?? (node = composeEmptyNode(ctx, token2.offset, void 0, null, props, onError));
4983
4981
  if (anchor && node.anchor === "")
4984
4982
  onError(anchor, "BAD_ALIAS", "Anchor cannot be an empty string");
4985
4983
  if (atKey && ctx.options.stringKeys && (!identity.isScalar(node) || typeof node.value !== "string" || node.tag && node.tag !== "tag:yaml.org,2002:str")) {
4986
4984
  const msg = "With stringKeys, all keys must be strings";
4987
- onError(tag ?? token, "NON_STRING_KEY", msg);
4985
+ onError(tag ?? token2, "NON_STRING_KEY", msg);
4988
4986
  }
4989
4987
  if (spaceBefore)
4990
4988
  node.spaceBefore = true;
4991
4989
  if (comment) {
4992
- if (token.type === "scalar" && token.source === "")
4990
+ if (token2.type === "scalar" && token2.source === "")
4993
4991
  node.comment = comment;
4994
4992
  else
4995
4993
  node.commentBefore = comment;
4996
4994
  }
4997
4995
  if (ctx.options.keepSourceTokens && isSrcToken)
4998
- node.srcToken = token;
4996
+ node.srcToken = token2;
4999
4997
  return node;
5000
4998
  }
5001
4999
  function composeEmptyNode(ctx, offset, before, pos, { spaceBefore, comment, anchor, tag, end }, onError) {
5002
- const token = {
5000
+ const token2 = {
5003
5001
  type: "scalar",
5004
5002
  offset: utilEmptyScalarPosition.emptyScalarPosition(offset, before, pos),
5005
5003
  indent: -1,
5006
5004
  source: ""
5007
5005
  };
5008
- const node = composeScalar.composeScalar(ctx, token, tag, onError);
5006
+ const node = composeScalar.composeScalar(ctx, token2, tag, onError);
5009
5007
  if (anchor) {
5010
5008
  node.anchor = anchor.source.substring(1);
5011
5009
  if (node.anchor === "")
@@ -5196,28 +5194,28 @@ ${cb}` : comment;
5196
5194
  * @param endOffset - Should be set if `forceDoc` is also set, to set the document range end and to indicate errors correctly.
5197
5195
  */
5198
5196
  *compose(tokens, forceDoc = false, endOffset = -1) {
5199
- for (const token of tokens)
5200
- yield* this.next(token);
5197
+ for (const token2 of tokens)
5198
+ yield* this.next(token2);
5201
5199
  yield* this.end(forceDoc, endOffset);
5202
5200
  }
5203
5201
  /** Advance the composer by one CST token. */
5204
- *next(token) {
5202
+ *next(token2) {
5205
5203
  if (node_process.env.LOG_STREAM)
5206
- console.dir(token, { depth: null });
5207
- switch (token.type) {
5204
+ console.dir(token2, { depth: null });
5205
+ switch (token2.type) {
5208
5206
  case "directive":
5209
- this.directives.add(token.source, (offset, message, warning) => {
5210
- const pos = getErrorPos(token);
5207
+ this.directives.add(token2.source, (offset, message, warning) => {
5208
+ const pos = getErrorPos(token2);
5211
5209
  pos[0] += offset;
5212
5210
  this.onError(pos, "BAD_DIRECTIVE", message, warning);
5213
5211
  });
5214
- this.prelude.push(token.source);
5212
+ this.prelude.push(token2.source);
5215
5213
  this.atDirectives = true;
5216
5214
  break;
5217
5215
  case "document": {
5218
- const doc = composeDoc.composeDoc(this.options, this.directives, token, this.onError);
5216
+ const doc = composeDoc.composeDoc(this.options, this.directives, token2, this.onError);
5219
5217
  if (this.atDirectives && !doc.directives.docStart)
5220
- this.onError(token, "MISSING_CHAR", "Missing directives-end/doc-start indicator line");
5218
+ this.onError(token2, "MISSING_CHAR", "Missing directives-end/doc-start indicator line");
5221
5219
  this.decorate(doc, false);
5222
5220
  if (this.doc)
5223
5221
  yield this.doc;
@@ -5230,11 +5228,11 @@ ${cb}` : comment;
5230
5228
  break;
5231
5229
  case "comment":
5232
5230
  case "newline":
5233
- this.prelude.push(token.source);
5231
+ this.prelude.push(token2.source);
5234
5232
  break;
5235
5233
  case "error": {
5236
- const msg = token.source ? `${token.message}: ${JSON.stringify(token.source)}` : token.message;
5237
- const error = new errors.YAMLParseError(getErrorPos(token), "UNEXPECTED_TOKEN", msg);
5234
+ const msg = token2.source ? `${token2.message}: ${JSON.stringify(token2.source)}` : token2.message;
5235
+ const error = new errors.YAMLParseError(getErrorPos(token2), "UNEXPECTED_TOKEN", msg);
5238
5236
  if (this.atDirectives || !this.doc)
5239
5237
  this.errors.push(error);
5240
5238
  else
@@ -5244,11 +5242,11 @@ ${cb}` : comment;
5244
5242
  case "doc-end": {
5245
5243
  if (!this.doc) {
5246
5244
  const msg = "Unexpected doc-end without preceding document";
5247
- this.errors.push(new errors.YAMLParseError(getErrorPos(token), "UNEXPECTED_TOKEN", msg));
5245
+ this.errors.push(new errors.YAMLParseError(getErrorPos(token2), "UNEXPECTED_TOKEN", msg));
5248
5246
  break;
5249
5247
  }
5250
5248
  this.doc.directives.docEnd = true;
5251
- const end = resolveEnd.resolveEnd(token.end, token.offset + token.source.length, this.doc.options.strict, this.onError);
5249
+ const end = resolveEnd.resolveEnd(token2.end, token2.offset + token2.source.length, this.doc.options.strict, this.onError);
5252
5250
  this.decorate(this.doc, true);
5253
5251
  if (end.comment) {
5254
5252
  const dc = this.doc.comment;
@@ -5259,7 +5257,7 @@ ${end.comment}` : end.comment;
5259
5257
  break;
5260
5258
  }
5261
5259
  default:
5262
- this.errors.push(new errors.YAMLParseError(getErrorPos(token), "UNEXPECTED_TOKEN", `Unsupported token ${token.type}`));
5260
+ this.errors.push(new errors.YAMLParseError(getErrorPos(token2), "UNEXPECTED_TOKEN", `Unsupported token ${token2.type}`));
5263
5261
  }
5264
5262
  }
5265
5263
  /**
@@ -5296,8 +5294,8 @@ var require_cst_scalar = __commonJS({
5296
5294
  var resolveFlowScalar = require_resolve_flow_scalar();
5297
5295
  var errors = require_errors();
5298
5296
  var stringifyString = require_stringifyString();
5299
- function resolveAsScalar(token, strict2 = true, onError) {
5300
- if (token) {
5297
+ function resolveAsScalar(token2, strict2 = true, onError) {
5298
+ if (token2) {
5301
5299
  const _onError = (pos, code, message) => {
5302
5300
  const offset = typeof pos === "number" ? pos : Array.isArray(pos) ? pos[0] : pos.offset;
5303
5301
  if (onError)
@@ -5305,13 +5303,13 @@ var require_cst_scalar = __commonJS({
5305
5303
  else
5306
5304
  throw new errors.YAMLParseError([offset, offset + 1], code, message);
5307
5305
  };
5308
- switch (token.type) {
5306
+ switch (token2.type) {
5309
5307
  case "scalar":
5310
5308
  case "single-quoted-scalar":
5311
5309
  case "double-quoted-scalar":
5312
- return resolveFlowScalar.resolveFlowScalar(token, strict2, _onError);
5310
+ return resolveFlowScalar.resolveFlowScalar(token2, strict2, _onError);
5313
5311
  case "block-scalar":
5314
- return resolveBlockScalar.resolveBlockScalar({ options: { strict: strict2 } }, token, _onError);
5312
+ return resolveBlockScalar.resolveBlockScalar({ options: { strict: strict2 } }, token2, _onError);
5315
5313
  }
5316
5314
  }
5317
5315
  return null;
@@ -5348,13 +5346,13 @@ var require_cst_scalar = __commonJS({
5348
5346
  return { type: "scalar", offset, indent, source, end };
5349
5347
  }
5350
5348
  }
5351
- function setScalarValue(token, value, context = {}) {
5349
+ function setScalarValue(token2, value, context = {}) {
5352
5350
  let { afterKey = false, implicitKey = false, inFlow = false, type } = context;
5353
- let indent = "indent" in token ? token.indent : null;
5351
+ let indent = "indent" in token2 ? token2.indent : null;
5354
5352
  if (afterKey && typeof indent === "number")
5355
5353
  indent += 2;
5356
5354
  if (!type)
5357
- switch (token.type) {
5355
+ switch (token2.type) {
5358
5356
  case "single-quoted-scalar":
5359
5357
  type = "QUOTE_SINGLE";
5360
5358
  break;
@@ -5362,7 +5360,7 @@ var require_cst_scalar = __commonJS({
5362
5360
  type = "QUOTE_DOUBLE";
5363
5361
  break;
5364
5362
  case "block-scalar": {
5365
- const header = token.props[0];
5363
+ const header = token2.props[0];
5366
5364
  if (header.type !== "block-scalar-header")
5367
5365
  throw new Error("Invalid block scalar header");
5368
5366
  type = header.source[0] === ">" ? "BLOCK_FOLDED" : "BLOCK_LITERAL";
@@ -5380,40 +5378,40 @@ var require_cst_scalar = __commonJS({
5380
5378
  switch (source[0]) {
5381
5379
  case "|":
5382
5380
  case ">":
5383
- setBlockScalarValue(token, source);
5381
+ setBlockScalarValue(token2, source);
5384
5382
  break;
5385
5383
  case '"':
5386
- setFlowScalarValue(token, source, "double-quoted-scalar");
5384
+ setFlowScalarValue(token2, source, "double-quoted-scalar");
5387
5385
  break;
5388
5386
  case "'":
5389
- setFlowScalarValue(token, source, "single-quoted-scalar");
5387
+ setFlowScalarValue(token2, source, "single-quoted-scalar");
5390
5388
  break;
5391
5389
  default:
5392
- setFlowScalarValue(token, source, "scalar");
5390
+ setFlowScalarValue(token2, source, "scalar");
5393
5391
  }
5394
5392
  }
5395
- function setBlockScalarValue(token, source) {
5393
+ function setBlockScalarValue(token2, source) {
5396
5394
  const he = source.indexOf("\n");
5397
5395
  const head = source.substring(0, he);
5398
5396
  const body = source.substring(he + 1) + "\n";
5399
- if (token.type === "block-scalar") {
5400
- const header = token.props[0];
5397
+ if (token2.type === "block-scalar") {
5398
+ const header = token2.props[0];
5401
5399
  if (header.type !== "block-scalar-header")
5402
5400
  throw new Error("Invalid block scalar header");
5403
5401
  header.source = head;
5404
- token.source = body;
5402
+ token2.source = body;
5405
5403
  } else {
5406
- const { offset } = token;
5407
- const indent = "indent" in token ? token.indent : -1;
5404
+ const { offset } = token2;
5405
+ const indent = "indent" in token2 ? token2.indent : -1;
5408
5406
  const props = [
5409
5407
  { type: "block-scalar-header", offset, indent, source: head }
5410
5408
  ];
5411
- if (!addEndtoBlockProps(props, "end" in token ? token.end : void 0))
5409
+ if (!addEndtoBlockProps(props, "end" in token2 ? token2.end : void 0))
5412
5410
  props.push({ type: "newline", offset: -1, indent, source: "\n" });
5413
- for (const key of Object.keys(token))
5411
+ for (const key of Object.keys(token2))
5414
5412
  if (key !== "type" && key !== "offset")
5415
- delete token[key];
5416
- Object.assign(token, { type: "block-scalar", indent, props, source: body });
5413
+ delete token2[key];
5414
+ Object.assign(token2, { type: "block-scalar", indent, props, source: body });
5417
5415
  }
5418
5416
  }
5419
5417
  function addEndtoBlockProps(props, end) {
@@ -5430,40 +5428,40 @@ var require_cst_scalar = __commonJS({
5430
5428
  }
5431
5429
  return false;
5432
5430
  }
5433
- function setFlowScalarValue(token, source, type) {
5434
- switch (token.type) {
5431
+ function setFlowScalarValue(token2, source, type) {
5432
+ switch (token2.type) {
5435
5433
  case "scalar":
5436
5434
  case "double-quoted-scalar":
5437
5435
  case "single-quoted-scalar":
5438
- token.type = type;
5439
- token.source = source;
5436
+ token2.type = type;
5437
+ token2.source = source;
5440
5438
  break;
5441
5439
  case "block-scalar": {
5442
- const end = token.props.slice(1);
5440
+ const end = token2.props.slice(1);
5443
5441
  let oa = source.length;
5444
- if (token.props[0].type === "block-scalar-header")
5445
- oa -= token.props[0].source.length;
5442
+ if (token2.props[0].type === "block-scalar-header")
5443
+ oa -= token2.props[0].source.length;
5446
5444
  for (const tok of end)
5447
5445
  tok.offset += oa;
5448
- delete token.props;
5449
- Object.assign(token, { type, source, end });
5446
+ delete token2.props;
5447
+ Object.assign(token2, { type, source, end });
5450
5448
  break;
5451
5449
  }
5452
5450
  case "block-map":
5453
5451
  case "block-seq": {
5454
- const offset = token.offset + source.length;
5455
- const nl = { type: "newline", offset, indent: token.indent, source: "\n" };
5456
- delete token.items;
5457
- Object.assign(token, { type, source, end: [nl] });
5452
+ const offset = token2.offset + source.length;
5453
+ const nl = { type: "newline", offset, indent: token2.indent, source: "\n" };
5454
+ delete token2.items;
5455
+ Object.assign(token2, { type, source, end: [nl] });
5458
5456
  break;
5459
5457
  }
5460
5458
  default: {
5461
- const indent = "indent" in token ? token.indent : -1;
5462
- const end = "end" in token && Array.isArray(token.end) ? token.end.filter((st) => st.type === "space" || st.type === "comment" || st.type === "newline") : [];
5463
- for (const key of Object.keys(token))
5459
+ const indent = "indent" in token2 ? token2.indent : -1;
5460
+ const end = "end" in token2 && Array.isArray(token2.end) ? token2.end.filter((st) => st.type === "space" || st.type === "comment" || st.type === "newline") : [];
5461
+ for (const key of Object.keys(token2))
5464
5462
  if (key !== "type" && key !== "offset")
5465
- delete token[key];
5466
- Object.assign(token, { type, indent, source, end });
5463
+ delete token2[key];
5464
+ Object.assign(token2, { type, indent, source, end });
5467
5465
  }
5468
5466
  }
5469
5467
  }
@@ -5478,40 +5476,40 @@ var require_cst_stringify = __commonJS({
5478
5476
  "../../node_modules/.pnpm/yaml@2.9.1/node_modules/yaml/dist/parse/cst-stringify.js"(exports) {
5479
5477
  "use strict";
5480
5478
  var stringify = (cst) => "type" in cst ? stringifyToken(cst) : stringifyItem(cst);
5481
- function stringifyToken(token) {
5482
- switch (token.type) {
5479
+ function stringifyToken(token2) {
5480
+ switch (token2.type) {
5483
5481
  case "block-scalar": {
5484
5482
  let res = "";
5485
- for (const tok of token.props)
5483
+ for (const tok of token2.props)
5486
5484
  res += stringifyToken(tok);
5487
- return res + token.source;
5485
+ return res + token2.source;
5488
5486
  }
5489
5487
  case "block-map":
5490
5488
  case "block-seq": {
5491
5489
  let res = "";
5492
- for (const item of token.items)
5490
+ for (const item of token2.items)
5493
5491
  res += stringifyItem(item);
5494
5492
  return res;
5495
5493
  }
5496
5494
  case "flow-collection": {
5497
- let res = token.start.source;
5498
- for (const item of token.items)
5495
+ let res = token2.start.source;
5496
+ for (const item of token2.items)
5499
5497
  res += stringifyItem(item);
5500
- for (const st of token.end)
5498
+ for (const st of token2.end)
5501
5499
  res += st.source;
5502
5500
  return res;
5503
5501
  }
5504
5502
  case "document": {
5505
- let res = stringifyItem(token);
5506
- if (token.end)
5507
- for (const st of token.end)
5503
+ let res = stringifyItem(token2);
5504
+ if (token2.end)
5505
+ for (const st of token2.end)
5508
5506
  res += st.source;
5509
5507
  return res;
5510
5508
  }
5511
5509
  default: {
5512
- let res = token.source;
5513
- if ("end" in token && token.end)
5514
- for (const st of token.end)
5510
+ let res = token2.source;
5511
+ if ("end" in token2 && token2.end)
5512
+ for (const st of token2.end)
5515
5513
  res += st.source;
5516
5514
  return res;
5517
5515
  }
@@ -5573,16 +5571,16 @@ var require_cst_visit = __commonJS({
5573
5571
  if (typeof ctrl === "symbol")
5574
5572
  return ctrl;
5575
5573
  for (const field of ["key", "value"]) {
5576
- const token = item[field];
5577
- if (token && "items" in token) {
5578
- for (let i = 0; i < token.items.length; ++i) {
5579
- const ci = _visit(Object.freeze(path.concat([[field, i]])), token.items[i], visitor);
5574
+ const token2 = item[field];
5575
+ if (token2 && "items" in token2) {
5576
+ for (let i = 0; i < token2.items.length; ++i) {
5577
+ const ci = _visit(Object.freeze(path.concat([[field, i]])), token2.items[i], visitor);
5580
5578
  if (typeof ci === "number")
5581
5579
  i = ci - 1;
5582
5580
  else if (ci === BREAK)
5583
5581
  return BREAK;
5584
5582
  else if (ci === REMOVE) {
5585
- token.items.splice(i, 1);
5583
+ token2.items.splice(i, 1);
5586
5584
  i -= 1;
5587
5585
  }
5588
5586
  }
@@ -5607,10 +5605,10 @@ var require_cst = __commonJS({
5607
5605
  var DOCUMENT = "";
5608
5606
  var FLOW_END = "";
5609
5607
  var SCALAR = "";
5610
- var isCollection = (token) => !!token && "items" in token;
5611
- var isScalar = (token) => !!token && (token.type === "scalar" || token.type === "single-quoted-scalar" || token.type === "double-quoted-scalar" || token.type === "block-scalar");
5612
- function prettyToken(token) {
5613
- switch (token) {
5608
+ var isCollection = (token2) => !!token2 && "items" in token2;
5609
+ var isScalar2 = (token2) => !!token2 && (token2.type === "scalar" || token2.type === "single-quoted-scalar" || token2.type === "double-quoted-scalar" || token2.type === "block-scalar");
5610
+ function prettyToken(token2) {
5611
+ switch (token2) {
5614
5612
  case BOM:
5615
5613
  return "<BOM>";
5616
5614
  case DOCUMENT:
@@ -5620,7 +5618,7 @@ var require_cst = __commonJS({
5620
5618
  case SCALAR:
5621
5619
  return "<SCALAR>";
5622
5620
  default:
5623
- return JSON.stringify(token);
5621
+ return JSON.stringify(token2);
5624
5622
  }
5625
5623
  }
5626
5624
  function tokenType(source) {
@@ -5692,7 +5690,7 @@ var require_cst = __commonJS({
5692
5690
  exports.FLOW_END = FLOW_END;
5693
5691
  exports.SCALAR = SCALAR;
5694
5692
  exports.isCollection = isCollection;
5695
- exports.isScalar = isScalar;
5693
+ exports.isScalar = isScalar2;
5696
5694
  exports.prettyToken = prettyToken;
5697
5695
  exports.tokenType = tokenType;
5698
5696
  }
@@ -6344,8 +6342,8 @@ var require_parser = __commonJS({
6344
6342
  }
6345
6343
  return -1;
6346
6344
  }
6347
- function isFlowToken(token) {
6348
- switch (token?.type) {
6345
+ function isFlowToken(token2) {
6346
+ switch (token2?.type) {
6349
6347
  case "alias":
6350
6348
  case "scalar":
6351
6349
  case "single-quoted-scalar":
@@ -6552,38 +6550,38 @@ var require_parser = __commonJS({
6552
6550
  return this.stack[this.stack.length - n];
6553
6551
  }
6554
6552
  *pop(error) {
6555
- const token = error ?? this.stack.pop();
6556
- if (!token) {
6553
+ const token2 = error ?? this.stack.pop();
6554
+ if (!token2) {
6557
6555
  const message = "Tried to pop an empty stack";
6558
6556
  yield { type: "error", offset: this.offset, source: "", message };
6559
6557
  } else if (this.stack.length === 0) {
6560
- yield token;
6558
+ yield token2;
6561
6559
  } else {
6562
6560
  const top = this.peek(1);
6563
- if (token.type === "block-scalar") {
6564
- token.indent = "indent" in top ? top.indent : 0;
6565
- } else if (token.type === "flow-collection" && top.type === "document") {
6566
- token.indent = 0;
6561
+ if (token2.type === "block-scalar") {
6562
+ token2.indent = "indent" in top ? top.indent : 0;
6563
+ } else if (token2.type === "flow-collection" && top.type === "document") {
6564
+ token2.indent = 0;
6567
6565
  }
6568
- if (token.type === "flow-collection")
6569
- fixFlowSeqItems(token);
6566
+ if (token2.type === "flow-collection")
6567
+ fixFlowSeqItems(token2);
6570
6568
  switch (top.type) {
6571
6569
  case "document":
6572
- top.value = token;
6570
+ top.value = token2;
6573
6571
  break;
6574
6572
  case "block-scalar":
6575
- top.props.push(token);
6573
+ top.props.push(token2);
6576
6574
  break;
6577
6575
  case "block-map": {
6578
6576
  const it = top.items[top.items.length - 1];
6579
6577
  if (it.value) {
6580
- top.items.push({ start: [], key: token, sep: [] });
6578
+ top.items.push({ start: [], key: token2, sep: [] });
6581
6579
  this.onKeyLine = true;
6582
6580
  return;
6583
6581
  } else if (it.sep) {
6584
- it.value = token;
6582
+ it.value = token2;
6585
6583
  } else {
6586
- Object.assign(it, { key: token, sep: [] });
6584
+ Object.assign(it, { key: token2, sep: [] });
6587
6585
  this.onKeyLine = !it.explicitKey;
6588
6586
  return;
6589
6587
  }
@@ -6592,34 +6590,34 @@ var require_parser = __commonJS({
6592
6590
  case "block-seq": {
6593
6591
  const it = top.items[top.items.length - 1];
6594
6592
  if (it.value)
6595
- top.items.push({ start: [], value: token });
6593
+ top.items.push({ start: [], value: token2 });
6596
6594
  else
6597
- it.value = token;
6595
+ it.value = token2;
6598
6596
  break;
6599
6597
  }
6600
6598
  case "flow-collection": {
6601
6599
  const it = top.items[top.items.length - 1];
6602
6600
  if (!it || it.value)
6603
- top.items.push({ start: [], key: token, sep: [] });
6601
+ top.items.push({ start: [], key: token2, sep: [] });
6604
6602
  else if (it.sep)
6605
- it.value = token;
6603
+ it.value = token2;
6606
6604
  else
6607
- Object.assign(it, { key: token, sep: [] });
6605
+ Object.assign(it, { key: token2, sep: [] });
6608
6606
  return;
6609
6607
  }
6610
6608
  /* istanbul ignore next should not happen */
6611
6609
  default:
6612
6610
  yield* this.pop();
6613
- yield* this.pop(token);
6611
+ yield* this.pop(token2);
6614
6612
  }
6615
- if ((top.type === "document" || top.type === "block-map" || top.type === "block-seq") && (token.type === "block-map" || token.type === "block-seq")) {
6616
- const last = token.items[token.items.length - 1];
6617
- if (last && !last.sep && !last.value && last.start.length > 0 && findNonEmptyIndex(last.start) === -1 && (token.indent === 0 || last.start.every((st) => st.type !== "comment" || st.indent < token.indent))) {
6613
+ if ((top.type === "document" || top.type === "block-map" || top.type === "block-seq") && (token2.type === "block-map" || token2.type === "block-seq")) {
6614
+ const last = token2.items[token2.items.length - 1];
6615
+ if (last && !last.sep && !last.value && last.start.length > 0 && findNonEmptyIndex(last.start) === -1 && (token2.indent === 0 || last.start.every((st) => st.type !== "comment" || st.indent < token2.indent))) {
6618
6616
  if (top.type === "document")
6619
6617
  top.end = last.start;
6620
6618
  else
6621
6619
  top.items.push({ start: last.start });
6622
- token.items.splice(-1, 1);
6620
+ token2.items.splice(-1, 1);
6623
6621
  }
6624
6622
  }
6625
6623
  }
@@ -7162,7 +7160,7 @@ var require_parser = __commonJS({
7162
7160
  yield* this.pop();
7163
7161
  }
7164
7162
  }
7165
- *lineEnd(token) {
7163
+ *lineEnd(token2) {
7166
7164
  switch (this.type) {
7167
7165
  case "comma":
7168
7166
  case "doc-start":
@@ -7179,10 +7177,10 @@ var require_parser = __commonJS({
7179
7177
  case "space":
7180
7178
  case "comment":
7181
7179
  default:
7182
- if (token.end)
7183
- token.end.push(this.sourceToken);
7180
+ if (token2.end)
7181
+ token2.end.push(this.sourceToken);
7184
7182
  else
7185
- token.end = [this.sourceToken];
7183
+ token2.end = [this.sourceToken];
7186
7184
  if (this.type === "newline")
7187
7185
  yield* this.pop();
7188
7186
  }
@@ -7647,9 +7645,9 @@ var require_scan = __commonJS({
7647
7645
  var isPathSeparator = (code) => {
7648
7646
  return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH;
7649
7647
  };
7650
- var depth = (token) => {
7651
- if (token.isPrefix !== true) {
7652
- token.depth = token.isGlobstar ? Infinity : 1;
7648
+ var depth = (token2) => {
7649
+ if (token2.isPrefix !== true) {
7650
+ token2.depth = token2.isGlobstar ? Infinity : 1;
7653
7651
  }
7654
7652
  };
7655
7653
  var scan = (input, options) => {
@@ -7676,7 +7674,7 @@ var require_scan = __commonJS({
7676
7674
  let braces = 0;
7677
7675
  let prev;
7678
7676
  let code;
7679
- let token = { value: "", depth: 0, isGlob: false };
7677
+ let token2 = { value: "", depth: 0, isGlob: false };
7680
7678
  const eos = () => index >= length;
7681
7679
  const peek = () => str.charCodeAt(index + 1);
7682
7680
  const advance = () => {
@@ -7687,7 +7685,7 @@ var require_scan = __commonJS({
7687
7685
  code = advance();
7688
7686
  let next;
7689
7687
  if (code === CHAR_BACKWARD_SLASH) {
7690
- backslashes = token.backslashes = true;
7688
+ backslashes = token2.backslashes = true;
7691
7689
  code = advance();
7692
7690
  if (code === CHAR_LEFT_CURLY_BRACE) {
7693
7691
  braceEscaped = true;
@@ -7698,7 +7696,7 @@ var require_scan = __commonJS({
7698
7696
  braces++;
7699
7697
  while (eos() !== true && (code = advance())) {
7700
7698
  if (code === CHAR_BACKWARD_SLASH) {
7701
- backslashes = token.backslashes = true;
7699
+ backslashes = token2.backslashes = true;
7702
7700
  advance();
7703
7701
  continue;
7704
7702
  }
@@ -7707,8 +7705,8 @@ var require_scan = __commonJS({
7707
7705
  continue;
7708
7706
  }
7709
7707
  if (braceEscaped !== true && code === CHAR_DOT && (code = advance()) === CHAR_DOT) {
7710
- isBrace = token.isBrace = true;
7711
- isGlob = token.isGlob = true;
7708
+ isBrace = token2.isBrace = true;
7709
+ isGlob = token2.isGlob = true;
7712
7710
  finished = true;
7713
7711
  if (scanToEnd === true) {
7714
7712
  continue;
@@ -7716,8 +7714,8 @@ var require_scan = __commonJS({
7716
7714
  break;
7717
7715
  }
7718
7716
  if (braceEscaped !== true && code === CHAR_COMMA) {
7719
- isBrace = token.isBrace = true;
7720
- isGlob = token.isGlob = true;
7717
+ isBrace = token2.isBrace = true;
7718
+ isGlob = token2.isGlob = true;
7721
7719
  finished = true;
7722
7720
  if (scanToEnd === true) {
7723
7721
  continue;
@@ -7728,7 +7726,7 @@ var require_scan = __commonJS({
7728
7726
  braces--;
7729
7727
  if (braces === 0) {
7730
7728
  braceEscaped = false;
7731
- isBrace = token.isBrace = true;
7729
+ isBrace = token2.isBrace = true;
7732
7730
  finished = true;
7733
7731
  break;
7734
7732
  }
@@ -7741,8 +7739,8 @@ var require_scan = __commonJS({
7741
7739
  }
7742
7740
  if (code === CHAR_FORWARD_SLASH) {
7743
7741
  slashes.push(index);
7744
- tokens.push(token);
7745
- token = { value: "", depth: 0, isGlob: false };
7742
+ tokens.push(token2);
7743
+ token2 = { value: "", depth: 0, isGlob: false };
7746
7744
  if (finished === true) continue;
7747
7745
  if (prev === CHAR_DOT && index === start + 1) {
7748
7746
  start += 2;
@@ -7754,8 +7752,8 @@ var require_scan = __commonJS({
7754
7752
  if (opts.noext !== true) {
7755
7753
  const isExtglobChar = code === CHAR_PLUS || code === CHAR_AT || code === CHAR_ASTERISK || code === CHAR_QUESTION_MARK || code === CHAR_EXCLAMATION_MARK;
7756
7754
  if (isExtglobChar === true && peek() === CHAR_LEFT_PARENTHESES) {
7757
- isGlob = token.isGlob = true;
7758
- isExtglob = token.isExtglob = true;
7755
+ isGlob = token2.isGlob = true;
7756
+ isExtglob = token2.isExtglob = true;
7759
7757
  finished = true;
7760
7758
  if (code === CHAR_EXCLAMATION_MARK && index === start) {
7761
7759
  negatedExtglob = true;
@@ -7764,7 +7762,7 @@ var require_scan = __commonJS({
7764
7762
  let parens = 0;
7765
7763
  while (eos() !== true && (code = advance())) {
7766
7764
  if (code === CHAR_BACKWARD_SLASH) {
7767
- backslashes = token.backslashes = true;
7765
+ backslashes = token2.backslashes = true;
7768
7766
  advance();
7769
7767
  continue;
7770
7768
  }
@@ -7783,8 +7781,8 @@ var require_scan = __commonJS({
7783
7781
  }
7784
7782
  }
7785
7783
  if (code === CHAR_ASTERISK) {
7786
- if (prev === CHAR_ASTERISK) isGlobstar = token.isGlobstar = true;
7787
- isGlob = token.isGlob = true;
7784
+ if (prev === CHAR_ASTERISK) isGlobstar = token2.isGlobstar = true;
7785
+ isGlob = token2.isGlob = true;
7788
7786
  finished = true;
7789
7787
  if (scanToEnd === true) {
7790
7788
  continue;
@@ -7792,7 +7790,7 @@ var require_scan = __commonJS({
7792
7790
  break;
7793
7791
  }
7794
7792
  if (code === CHAR_QUESTION_MARK) {
7795
- isGlob = token.isGlob = true;
7793
+ isGlob = token2.isGlob = true;
7796
7794
  finished = true;
7797
7795
  if (scanToEnd === true) {
7798
7796
  continue;
@@ -7802,13 +7800,13 @@ var require_scan = __commonJS({
7802
7800
  if (code === CHAR_LEFT_SQUARE_BRACKET) {
7803
7801
  while (eos() !== true && (next = advance())) {
7804
7802
  if (next === CHAR_BACKWARD_SLASH) {
7805
- backslashes = token.backslashes = true;
7803
+ backslashes = token2.backslashes = true;
7806
7804
  advance();
7807
7805
  continue;
7808
7806
  }
7809
7807
  if (next === CHAR_RIGHT_SQUARE_BRACKET) {
7810
- isBracket = token.isBracket = true;
7811
- isGlob = token.isGlob = true;
7808
+ isBracket = token2.isBracket = true;
7809
+ isGlob = token2.isGlob = true;
7812
7810
  finished = true;
7813
7811
  break;
7814
7812
  }
@@ -7819,17 +7817,17 @@ var require_scan = __commonJS({
7819
7817
  break;
7820
7818
  }
7821
7819
  if (opts.nonegate !== true && code === CHAR_EXCLAMATION_MARK && index === start) {
7822
- negated = token.negated = true;
7820
+ negated = token2.negated = true;
7823
7821
  start++;
7824
7822
  continue;
7825
7823
  }
7826
7824
  if (opts.noparen !== true && code === CHAR_LEFT_PARENTHESES) {
7827
- isGlob = token.isGlob = true;
7825
+ isGlob = token2.isGlob = true;
7828
7826
  if (scanToEnd === true) {
7829
7827
  let parens = 1;
7830
7828
  while (eos() !== true && (code = advance())) {
7831
7829
  if (code === CHAR_BACKWARD_SLASH) {
7832
- backslashes = token.backslashes = true;
7830
+ backslashes = token2.backslashes = true;
7833
7831
  advance();
7834
7832
  continue;
7835
7833
  }
@@ -7903,7 +7901,7 @@ var require_scan = __commonJS({
7903
7901
  if (opts.tokens === true) {
7904
7902
  state.maxDepth = 0;
7905
7903
  if (!isPathSeparator(code)) {
7906
- tokens.push(token);
7904
+ tokens.push(token2);
7907
7905
  }
7908
7906
  state.tokens = tokens;
7909
7907
  }
@@ -7949,7 +7947,7 @@ var require_scan = __commonJS({
7949
7947
  var require_parse = __commonJS({
7950
7948
  "../../node_modules/.pnpm/picomatch@4.0.7/node_modules/picomatch/lib/parse.js"(exports, module) {
7951
7949
  "use strict";
7952
- var constants2 = require_constants();
7950
+ var constants = require_constants();
7953
7951
  var utils = require_utils();
7954
7952
  var {
7955
7953
  MAX_LENGTH,
@@ -7957,7 +7955,7 @@ var require_parse = __commonJS({
7957
7955
  REGEX_NON_SPECIAL_CHARS,
7958
7956
  REGEX_SPECIAL_CHARS_BACKREF,
7959
7957
  REPLACEMENTS
7960
- } = constants2;
7958
+ } = constants;
7961
7959
  var expandRange = (args, options) => {
7962
7960
  if (typeof options.expandRange === "function") {
7963
7961
  return options.expandRange(...args, options);
@@ -8166,7 +8164,7 @@ var require_parse = __commonJS({
8166
8164
  if (options.maxExtglobRecursion === false) {
8167
8165
  return { risky: false };
8168
8166
  }
8169
- const max = typeof options.maxExtglobRecursion === "number" ? options.maxExtglobRecursion : constants2.DEFAULT_MAX_EXTGLOB_RECURSION;
8167
+ const max = typeof options.maxExtglobRecursion === "number" ? options.maxExtglobRecursion : constants.DEFAULT_MAX_EXTGLOB_RECURSION;
8170
8168
  const branches = splitTopLevel(body).map((branch) => branch.trim());
8171
8169
  if (branches.length > 1) {
8172
8170
  if (branches.some((branch) => branch === "") || branches.some((branch) => /^[*?]+$/.test(branch)) || hasRepeatedCharPrefixOverlap(branches)) {
@@ -8212,8 +8210,8 @@ var require_parse = __commonJS({
8212
8210
  const bos = { type: "bos", value: "", output: opts.prepend || "" };
8213
8211
  const tokens = [bos];
8214
8212
  const capture = opts.capture ? "" : "?:";
8215
- const PLATFORM_CHARS = constants2.globChars(opts.windows);
8216
- const EXTGLOB_CHARS = constants2.extglobChars(PLATFORM_CHARS);
8213
+ const PLATFORM_CHARS = constants.globChars(opts.windows);
8214
+ const EXTGLOB_CHARS = constants.extglobChars(PLATFORM_CHARS);
8217
8215
  const {
8218
8216
  DOT_LITERAL,
8219
8217
  PLUS_LITERAL,
@@ -8272,9 +8270,9 @@ var require_parse = __commonJS({
8272
8270
  state.consumed += value2;
8273
8271
  state.index += num2;
8274
8272
  };
8275
- const append = (token) => {
8276
- state.output += token.output != null ? token.output : token.value;
8277
- consume(token.value);
8273
+ const append = (token2) => {
8274
+ state.output += token2.output != null ? token2.output : token2.value;
8275
+ consume(token2.value);
8278
8276
  };
8279
8277
  const negate = () => {
8280
8278
  let count = 1;
@@ -8324,54 +8322,54 @@ var require_parse = __commonJS({
8324
8322
  prev = tok;
8325
8323
  };
8326
8324
  const extglobOpen = (type, value2) => {
8327
- const token = { ...EXTGLOB_CHARS[value2], conditions: 1, inner: "" };
8328
- token.prev = prev;
8329
- token.parens = state.parens;
8330
- token.output = state.output;
8331
- token.startIndex = state.index;
8332
- token.tokensIndex = tokens.length;
8333
- const output = (opts.capture ? "(" : "") + token.open;
8325
+ const token2 = { ...EXTGLOB_CHARS[value2], conditions: 1, inner: "" };
8326
+ token2.prev = prev;
8327
+ token2.parens = state.parens;
8328
+ token2.output = state.output;
8329
+ token2.startIndex = state.index;
8330
+ token2.tokensIndex = tokens.length;
8331
+ const output = (opts.capture ? "(" : "") + token2.open;
8334
8332
  increment("parens");
8335
8333
  push({ type, value: value2, output: state.output ? "" : ONE_CHAR });
8336
8334
  push({ type: "paren", extglob: true, value: advance(), output });
8337
- extglobs.push(token);
8335
+ extglobs.push(token2);
8338
8336
  };
8339
- const extglobClose = (token) => {
8340
- const literal = input.slice(token.startIndex, state.index + 1);
8341
- const body = input.slice(token.startIndex + 2, state.index);
8337
+ const extglobClose = (token2) => {
8338
+ const literal = input.slice(token2.startIndex, state.index + 1);
8339
+ const body = input.slice(token2.startIndex + 2, state.index);
8342
8340
  const analysis = analyzeRepeatedExtglob(body, opts);
8343
- if ((token.type === "plus" || token.type === "star") && analysis.risky) {
8344
- const safeOutput = analysis.safeOutput ? (token.output ? "" : ONE_CHAR) + (opts.capture ? `(${analysis.safeOutput})` : analysis.safeOutput) : void 0;
8345
- const open = tokens[token.tokensIndex];
8341
+ if ((token2.type === "plus" || token2.type === "star") && analysis.risky) {
8342
+ const safeOutput = analysis.safeOutput ? (token2.output ? "" : ONE_CHAR) + (opts.capture ? `(${analysis.safeOutput})` : analysis.safeOutput) : void 0;
8343
+ const open = tokens[token2.tokensIndex];
8346
8344
  open.type = "text";
8347
8345
  open.value = literal;
8348
8346
  open.output = safeOutput || utils.escapeRegex(literal);
8349
- for (let i = token.tokensIndex + 1; i < tokens.length; i++) {
8347
+ for (let i = token2.tokensIndex + 1; i < tokens.length; i++) {
8350
8348
  tokens[i].value = "";
8351
8349
  tokens[i].output = "";
8352
8350
  delete tokens[i].suffix;
8353
8351
  }
8354
- state.output = token.output + open.output;
8352
+ state.output = token2.output + open.output;
8355
8353
  state.backtrack = true;
8356
8354
  push({ type: "paren", extglob: true, value, output: "" });
8357
8355
  decrement("parens");
8358
8356
  return;
8359
8357
  }
8360
- let output = token.close + (opts.capture ? ")" : "");
8358
+ let output = token2.close + (opts.capture ? ")" : "");
8361
8359
  let rest;
8362
- if (token.type === "negate") {
8360
+ if (token2.type === "negate") {
8363
8361
  let extglobStar = star;
8364
- if (token.inner && token.inner.length > 1 && token.inner.includes("/")) {
8362
+ if (token2.inner && token2.inner.length > 1 && token2.inner.includes("/")) {
8365
8363
  extglobStar = globstar(opts);
8366
8364
  }
8367
8365
  if (extglobStar !== star || eos() || /^\)+$/.test(remaining())) {
8368
- output = token.close = `)$))${extglobStar}`;
8366
+ output = token2.close = `)$))${extglobStar}`;
8369
8367
  }
8370
- if (token.inner.includes("*") && (rest = remaining()) && /^\.[^\\/.]+$/.test(rest)) {
8368
+ if (token2.inner.includes("*") && (rest = remaining()) && /^\.[^\\/.]+$/.test(rest)) {
8371
8369
  const expression = parse3(rest, { ...options, fastpaths: false }).output;
8372
- output = token.close = `)${expression})${extglobStar})`;
8370
+ output = token2.close = `)${expression})${extglobStar})`;
8373
8371
  }
8374
- if (token.prev.type === "bos") {
8372
+ if (token2.prev.type === "bos") {
8375
8373
  state.negatedExtglob = true;
8376
8374
  }
8377
8375
  }
@@ -8830,18 +8828,18 @@ var require_parse = __commonJS({
8830
8828
  consume(value);
8831
8829
  continue;
8832
8830
  }
8833
- const token = { type: "star", value, output: star };
8831
+ const token2 = { type: "star", value, output: star };
8834
8832
  if (opts.bash === true) {
8835
- token.output = ".*?";
8833
+ token2.output = ".*?";
8836
8834
  if (prev.type === "bos" || prev.type === "slash") {
8837
- token.output = nodot + token.output;
8835
+ token2.output = nodot + token2.output;
8838
8836
  }
8839
- push(token);
8837
+ push(token2);
8840
8838
  continue;
8841
8839
  }
8842
8840
  if (prev && (prev.type === "bracket" || prev.type === "paren") && opts.regex === true) {
8843
- token.output = value;
8844
- push(token);
8841
+ token2.output = value;
8842
+ push(token2);
8845
8843
  continue;
8846
8844
  }
8847
8845
  if (state.index === state.start || prev.type === "slash" || prev.type === "dot") {
@@ -8860,7 +8858,7 @@ var require_parse = __commonJS({
8860
8858
  prev.output += ONE_CHAR;
8861
8859
  }
8862
8860
  }
8863
- push(token);
8861
+ push(token2);
8864
8862
  }
8865
8863
  while (state.brackets > 0) {
8866
8864
  if (opts.strictBrackets === true) throw new SyntaxError(syntaxError("closing", "]"));
@@ -8882,10 +8880,10 @@ var require_parse = __commonJS({
8882
8880
  }
8883
8881
  if (state.backtrack === true) {
8884
8882
  state.output = "";
8885
- for (const token of state.tokens) {
8886
- state.output += token.output != null ? token.output : token.value;
8887
- if (token.suffix) {
8888
- state.output += token.suffix;
8883
+ for (const token2 of state.tokens) {
8884
+ state.output += token2.output != null ? token2.output : token2.value;
8885
+ if (token2.suffix) {
8886
+ state.output += token2.suffix;
8889
8887
  }
8890
8888
  }
8891
8889
  }
@@ -8909,7 +8907,7 @@ var require_parse = __commonJS({
8909
8907
  NO_DOTS_SLASH,
8910
8908
  STAR,
8911
8909
  START_ANCHOR
8912
- } = constants2.globChars(opts.windows);
8910
+ } = constants.globChars(opts.windows);
8913
8911
  const nodot = opts.dot ? NO_DOTS : NO_DOT;
8914
8912
  const slashDot = opts.dot ? NO_DOTS_SLASH : NO_DOT;
8915
8913
  const capture = opts.capture ? "" : "?:";
@@ -8967,7 +8965,7 @@ var require_picomatch = __commonJS({
8967
8965
  var scan = require_scan();
8968
8966
  var parse3 = require_parse();
8969
8967
  var utils = require_utils();
8970
- var constants2 = require_constants();
8968
+ var constants = require_constants();
8971
8969
  var isObject2 = (val) => val && typeof val === "object" && !Array.isArray(val);
8972
8970
  var picomatch4 = (glob2, options, returnState = false) => {
8973
8971
  if (Array.isArray(glob2)) {
@@ -9095,7 +9093,7 @@ var require_picomatch = __commonJS({
9095
9093
  return /$^/;
9096
9094
  }
9097
9095
  };
9098
- picomatch4.constants = constants2;
9096
+ picomatch4.constants = constants;
9099
9097
  module.exports = picomatch4;
9100
9098
  }
9101
9099
  });
@@ -9117,6 +9115,149 @@ var require_picomatch2 = __commonJS({
9117
9115
  }
9118
9116
  });
9119
9117
 
9118
+ // ../core/dist/guard/packs.js
9119
+ var PACKS = {
9120
+ "done-check": {
9121
+ summary: "At Stop, checks the change against the criteria files' bullets and blocks once on a confidently unmet one",
9122
+ defaults: {
9123
+ latencyMs: 5e3,
9124
+ maxUsdPerSession: 0.01,
9125
+ criteria: ["TASK.md", ".system1/done.md"],
9126
+ evidence: []
9127
+ }
9128
+ }
9129
+ };
9130
+ var PACK_NAMES = Object.keys(PACKS);
9131
+ function isPackName(name) {
9132
+ return Object.hasOwn(PACKS, name);
9133
+ }
9134
+ var PACK_KEYS = [
9135
+ "enabled",
9136
+ "enabledAt",
9137
+ "enabledBy",
9138
+ "latencyMs",
9139
+ "maxUsdPerSession",
9140
+ "criteria",
9141
+ "evidence"
9142
+ ];
9143
+ function readGuard(user, userFile, repo, repoFile, warnings) {
9144
+ const layers2 = [
9145
+ { packs: packSections(repo, repoFile, warnings), file: repoFile, which: "repo" },
9146
+ { packs: packSections(user, userFile, warnings), file: userFile, which: "user" }
9147
+ ];
9148
+ const packs = {};
9149
+ for (const name of PACK_NAMES) {
9150
+ const defaults = PACKS[name].defaults;
9151
+ const pack = {
9152
+ enabled: false,
9153
+ latencyMs: defaults.latencyMs,
9154
+ maxUsdPerSession: defaults.maxUsdPerSession,
9155
+ criteria: [...defaults.criteria],
9156
+ evidence: [...defaults.evidence]
9157
+ };
9158
+ const seen = /* @__PURE__ */ new Set();
9159
+ for (const { packs: sections, file, which } of layers2) {
9160
+ const section2 = sections[name];
9161
+ if (!section2)
9162
+ continue;
9163
+ const set = (key, value) => {
9164
+ if (seen.has(key))
9165
+ return;
9166
+ seen.add(key);
9167
+ pack[key] = value;
9168
+ };
9169
+ for (const [key, value] of Object.entries(section2)) {
9170
+ if (value === void 0 || value === null)
9171
+ continue;
9172
+ const path = `guard.packs.${name}.${key}`;
9173
+ if (!PACK_KEYS.includes(key)) {
9174
+ warnings.push(`${file}: unknown key ${path}`);
9175
+ continue;
9176
+ }
9177
+ if (key.startsWith("enabled") && which === "user") {
9178
+ warnings.push(`${file}: ${path} is read only from the repo file (ignored)`);
9179
+ continue;
9180
+ }
9181
+ const bad = (expected) => warnings.push(`${file}: ${path} must be ${expected} (ignored)`);
9182
+ switch (key) {
9183
+ case "enabled":
9184
+ if (typeof value === "boolean")
9185
+ set("enabled", value);
9186
+ else
9187
+ bad("true or false");
9188
+ break;
9189
+ case "enabledAt":
9190
+ case "enabledBy":
9191
+ if (typeof value === "string" && value.trim())
9192
+ set(key, value);
9193
+ else
9194
+ bad("text");
9195
+ break;
9196
+ case "latencyMs":
9197
+ if (typeof value === "number" && Number.isFinite(value) && value > 0)
9198
+ set(key, value);
9199
+ else
9200
+ bad("a number above 0");
9201
+ break;
9202
+ case "maxUsdPerSession":
9203
+ if (typeof value === "number" && Number.isFinite(value) && value >= 0)
9204
+ set(key, value);
9205
+ else
9206
+ bad("a number of at least 0");
9207
+ break;
9208
+ case "criteria":
9209
+ case "evidence":
9210
+ if (Array.isArray(value) && value.every((v) => typeof v === "string" && v.trim()))
9211
+ set(key, value);
9212
+ else
9213
+ bad("a list of file paths");
9214
+ break;
9215
+ }
9216
+ }
9217
+ }
9218
+ packs[name] = pack;
9219
+ }
9220
+ return { packs };
9221
+ }
9222
+ function packSections(layer, file, warnings) {
9223
+ const guard = layer?.guard;
9224
+ if (guard === void 0 || guard === null)
9225
+ return {};
9226
+ if (!isMapping(guard)) {
9227
+ warnings.push(`${file}: guard must be a mapping (ignored)`);
9228
+ return {};
9229
+ }
9230
+ for (const key of Object.keys(guard)) {
9231
+ if (key !== "packs")
9232
+ warnings.push(`${file}: unknown key guard.${key}`);
9233
+ }
9234
+ const packs = guard.packs;
9235
+ if (packs === void 0 || packs === null)
9236
+ return {};
9237
+ if (!isMapping(packs)) {
9238
+ warnings.push(`${file}: guard.packs must be a mapping (ignored)`);
9239
+ return {};
9240
+ }
9241
+ const out = {};
9242
+ for (const [name, section2] of Object.entries(packs)) {
9243
+ if (!isPackName(name)) {
9244
+ warnings.push(`${file}: unknown guard pack ${name} (known: ${PACK_NAMES.join(", ")})`);
9245
+ continue;
9246
+ }
9247
+ if (section2 === void 0 || section2 === null)
9248
+ continue;
9249
+ if (!isMapping(section2)) {
9250
+ warnings.push(`${file}: guard.packs.${name} must be a mapping (ignored)`);
9251
+ continue;
9252
+ }
9253
+ out[name] = section2;
9254
+ }
9255
+ return out;
9256
+ }
9257
+ function isMapping(value) {
9258
+ return typeof value === "object" && value !== null && !Array.isArray(value);
9259
+ }
9260
+
9120
9261
  // ../core/dist/route/route.js
9121
9262
  var ROUTE_DEFAULTS = {
9122
9263
  enabled: true,
@@ -9589,6 +9730,7 @@ function loadConfig(options = {}) {
9589
9730
  profiles: [...profileList(user, userFile, warnings), ...profileList(repo, repoFile, warnings)],
9590
9731
  route: readRoute(user, userFile, repo, repoFile, env)
9591
9732
  };
9733
+ const guard = readGuard(user, userFile, repo, repoFile, warnings);
9592
9734
  const key = resolveApiKey(env, userDir);
9593
9735
  const session = detectSession(env);
9594
9736
  return {
@@ -9602,6 +9744,7 @@ function loadConfig(options = {}) {
9602
9744
  replay: /^(1|true|yes)$/i.test(env.SYSTEM1_REPLAY ?? ""),
9603
9745
  session: session?.id,
9604
9746
  sessionOrigin: session?.origin,
9747
+ guard,
9605
9748
  layers: {
9606
9749
  ...user ? { user: userFile } : {},
9607
9750
  ...repo ? { repo: repoFile } : {},
@@ -9635,11 +9778,21 @@ function readLayer(file) {
9635
9778
  return parsed;
9636
9779
  }
9637
9780
  var KNOWN = {
9638
- "": ["model", "endpoint", "concurrency", "timeoutMs", "budget", "egress", "profiles", "route"],
9781
+ "": [
9782
+ "model",
9783
+ "endpoint",
9784
+ "concurrency",
9785
+ "timeoutMs",
9786
+ "budget",
9787
+ "egress",
9788
+ "profiles",
9789
+ "route",
9790
+ "guard"
9791
+ ],
9639
9792
  budget: ["maxCalls", "maxUsd"],
9640
9793
  egress: ["consent", "exclude"]
9641
9794
  };
9642
- function unknownKeys(layer, file, which2) {
9795
+ function unknownKeys(layer, file, which) {
9643
9796
  if (!layer)
9644
9797
  return [];
9645
9798
  const warnings = Object.keys(layer).filter((k) => !KNOWN[""]?.includes(k)).map((key) => `${file}: unknown key ${key}`);
@@ -9647,7 +9800,7 @@ function unknownKeys(layer, file, which2) {
9647
9800
  const value = layer[section2];
9648
9801
  if (value === void 0 || value === null)
9649
9802
  continue;
9650
- if (!isMapping(value)) {
9803
+ if (!isMapping2(value)) {
9651
9804
  warnings.push(`${file}: ${section2} must be a mapping (ignored)`);
9652
9805
  continue;
9653
9806
  }
@@ -9657,7 +9810,7 @@ function unknownKeys(layer, file, which2) {
9657
9810
  }
9658
9811
  }
9659
9812
  const egress = layer.egress;
9660
- if (which2 === "user" && isMapping(egress) && egress.consent !== void 0) {
9813
+ if (which === "user" && isMapping2(egress) && egress.consent !== void 0) {
9661
9814
  warnings.push(`${file}: egress.consent is read only from the repo file (ignored)`);
9662
9815
  }
9663
9816
  return warnings;
@@ -9685,7 +9838,7 @@ function pick(path, rule, layers2, warnings) {
9685
9838
  const [head, key] = path.split(".");
9686
9839
  let value = layer?.[head];
9687
9840
  if (key !== void 0)
9688
- value = isMapping(value) ? value[key] : void 0;
9841
+ value = isMapping2(value) ? value[key] : void 0;
9689
9842
  if (value === void 0 || value === null)
9690
9843
  continue;
9691
9844
  if (!rule.ok(value))
@@ -9695,7 +9848,7 @@ function pick(path, rule, layers2, warnings) {
9695
9848
  }
9696
9849
  return found2;
9697
9850
  }
9698
- function isMapping(value) {
9851
+ function isMapping2(value) {
9699
9852
  return typeof value === "object" && value !== null && !Array.isArray(value);
9700
9853
  }
9701
9854
  function readConsent(repo, file) {
@@ -9885,15 +10038,56 @@ var HEADER = `# decisions \u2014 per-repo configuration. See https://github.com/
9885
10038
  # egress.consent records that this repo agreed to send content to the decision
9886
10039
  # model's provider (OpenRouter). Path excludes and secret scrubbing always apply.
9887
10040
  `;
10041
+ function editable(file, path) {
10042
+ const doc = existsSync2(file) ? (0, import_yaml2.parseDocument)(readFileSync2(file, "utf8")) : (0, import_yaml2.parseDocument)(HEADER);
10043
+ if (doc.errors.length > 0) {
10044
+ throw new DecisionsError("config-error", `${file} is not valid YAML: ${doc.errors[0]?.message}`, {
10045
+ file
10046
+ });
10047
+ }
10048
+ if (!doc.contents || (0, import_yaml2.isScalar)(doc.contents) && doc.contents.value == null) {
10049
+ doc.contents = doc.createNode({});
10050
+ }
10051
+ if (!(0, import_yaml2.isMap)(doc.contents)) {
10052
+ throw new DecisionsError("config-error", `${file} must be a YAML mapping`, { file });
10053
+ }
10054
+ for (let i = 1; i <= path.length; i++) {
10055
+ const at = path.slice(0, i);
10056
+ const node = doc.getIn(at, true);
10057
+ if (node === void 0 || (0, import_yaml2.isMap)(node))
10058
+ continue;
10059
+ if (node === null || (0, import_yaml2.isScalar)(node) && node.value == null) {
10060
+ doc.setIn(at, doc.createNode({}));
10061
+ continue;
10062
+ }
10063
+ throw new DecisionsError("config-error", `${file}: ${at.join(".")} must be a mapping. Correct it by hand, then run the command again`, { file });
10064
+ }
10065
+ return doc;
10066
+ }
9888
10067
  function setConsent(repoRoot, granted, by, now = /* @__PURE__ */ new Date()) {
9889
10068
  const file = repoConfigPath(repoRoot);
9890
- const doc = existsSync2(file) ? (0, import_yaml2.parseDocument)(readFileSync2(file, "utf8")) : (0, import_yaml2.parseDocument)(HEADER);
10069
+ const doc = editable(file, ["egress"]);
9891
10070
  const consent = { granted, at: now.toISOString(), ...by ? { by } : {} };
9892
10071
  doc.setIn(["egress", "consent"], consent);
9893
10072
  mkdirSync(dirname2(file), { recursive: true });
9894
10073
  writeFileSync(file, doc.toString());
9895
10074
  return consent;
9896
10075
  }
10076
+ function setGuardEnabled(repoRoot, pack, enabled, by, now = /* @__PURE__ */ new Date()) {
10077
+ const file = repoConfigPath(repoRoot);
10078
+ const path = ["guard", "packs", pack];
10079
+ const doc = editable(file, path);
10080
+ const at = now.toISOString();
10081
+ doc.setIn([...path, "enabled"], enabled);
10082
+ doc.setIn([...path, "enabledAt"], at);
10083
+ if (by)
10084
+ doc.setIn([...path, "enabledBy"], by);
10085
+ else
10086
+ doc.deleteIn([...path, "enabledBy"]);
10087
+ mkdirSync(dirname2(file), { recursive: true });
10088
+ writeFileSync(file, doc.toString());
10089
+ return { enabled, enabledAt: at, ...by ? { enabledBy: by } : {} };
10090
+ }
9897
10091
  function assertConsent(consent, repoRoot) {
9898
10092
  if (consent.granted)
9899
10093
  return;
@@ -9950,59 +10144,9 @@ function isExcluded(path, extra = []) {
9950
10144
  return [...DEFAULT_EXCLUDES, ...extra].find((p) => (0, import_picomatch.default)(p, { dot: true })(path));
9951
10145
  }
9952
10146
 
9953
- // ../core/dist/ping.js
9954
- function probeUrl(config) {
9955
- const origin = new URL(config.endpoint).origin;
9956
- return `${origin}/api/v1/models/${config.model}/endpoints`;
9957
- }
9958
- async function ping(config, options = {}) {
9959
- const doFetch = options.fetch ?? fetch;
9960
- const probe = probeUrl(config);
9961
- const base2 = {
9962
- model: config.model,
9963
- endpoint: config.endpoint,
9964
- probe,
9965
- keyPresent: config.apiKey !== void 0
9966
- };
9967
- const started = performance.now();
9968
- try {
9969
- const response = await doFetch(probe, {
9970
- signal: AbortSignal.timeout(options.timeoutMs ?? 1e4)
9971
- });
9972
- const latencyMs = Math.round(performance.now() - started);
9973
- if (!response.ok) {
9974
- return {
9975
- ...base2,
9976
- ok: false,
9977
- latencyMs,
9978
- httpStatus: response.status,
9979
- error: `HTTP ${response.status}`
9980
- };
9981
- }
9982
- const body = await response.json();
9983
- const contextLength = body.data?.endpoints?.[0]?.context_length;
9984
- return {
9985
- ...base2,
9986
- ok: true,
9987
- latencyMs,
9988
- httpStatus: response.status,
9989
- ...typeof contextLength === "number" ? { contextLength } : {}
9990
- };
9991
- } catch (error) {
9992
- return { ...base2, ok: false, error: describe2(error) };
9993
- }
9994
- }
9995
- function describe2(error) {
9996
- if (error instanceof Error) {
9997
- const cause = error.cause;
9998
- return cause instanceof Error ? `${error.message}: ${cause.message}` : error.message;
9999
- }
10000
- return String(error);
10001
- }
10002
-
10003
- // ../core/dist/spec/spec.js
10004
- import { existsSync as existsSync4, readdirSync as readdirSync2, readFileSync as readFileSync4 } from "node:fs";
10005
- import { basename as basename2, extname, isAbsolute as isAbsolute3, join as join2, relative as relative3, resolve as resolve5 } from "node:path";
10147
+ // ../core/dist/guard/state.js
10148
+ import { existsSync as existsSync3, readFileSync as readFileSync4, statSync as statSync3 } from "node:fs";
10149
+ import { join as join2 } from "node:path";
10006
10150
 
10007
10151
  // ../../node_modules/.pnpm/typebox@1.3.34/node_modules/typebox/build/system/memory/memory.mjs
10008
10152
  var memory_exports = {};
@@ -13132,8 +13276,8 @@ function Compare(left, right) {
13132
13276
  // ../../node_modules/.pnpm/typebox@1.3.34/node_modules/typebox/build/type/engine/evaluate/broaden.mjs
13133
13277
  function BroadenFilter(type, types, result = [], all = types) {
13134
13278
  return guard_exports.ShiftLeft(types, (left, right) => {
13135
- const compare2 = Compare(type, left);
13136
- return guard_exports.IsEqual(compare2, CompareResultLeftInside) || guard_exports.IsEqual(compare2, CompareResultEqual) ? all : guard_exports.IsEqual(compare2, CompareResultDisjoint) ? BroadenFilter(type, right, [...result, left], all) : BroadenFilter(type, right, result, all);
13279
+ const compare = Compare(type, left);
13280
+ return guard_exports.IsEqual(compare, CompareResultLeftInside) || guard_exports.IsEqual(compare, CompareResultEqual) ? all : guard_exports.IsEqual(compare, CompareResultDisjoint) ? BroadenFilter(type, right, [...result, left], all) : BroadenFilter(type, right, result, all);
13137
13281
  }, () => [...result, type]);
13138
13282
  }
13139
13283
  function BroadenType(type, types, result) {
@@ -15016,15 +15160,15 @@ function NextPendingResource(stack, schema) {
15016
15160
  return IsId(schema) ? false : stack.pendingResource;
15017
15161
  }
15018
15162
  function NextLexicalBase(stack, schema) {
15019
- const entry2 = ResourceEntry(stack, schema);
15020
- if (entry2)
15021
- return entry2.base;
15163
+ const entry = ResourceEntry(stack, schema);
15164
+ if (entry)
15165
+ return entry.base;
15022
15166
  return IsId(schema) ? NextUri(schema.$id, stack.lexicalBase).href : stack.lexicalBase;
15023
15167
  }
15024
15168
  function NextResourceBase(stack, schema) {
15025
- const entry2 = ResourceEntry(stack, schema);
15026
- if (entry2)
15027
- return entry2.base;
15169
+ const entry = ResourceEntry(stack, schema);
15170
+ if (entry)
15171
+ return entry.base;
15028
15172
  return IsId(schema) && stack.pendingResource ? NextUri(schema.$id, stack.resourceBase).href : stack.resourceBase;
15029
15173
  }
15030
15174
  function NextUseResourceBaseForReference(stack, schema) {
@@ -15036,9 +15180,9 @@ function NextReferenceBase(stack, schema) {
15036
15180
  return isRetrieved || useResourceBaseForReference ? NextResourceBase(stack, schema) : NextLexicalBase(stack, schema);
15037
15181
  }
15038
15182
  function NextLexicalSchema(stack, schema) {
15039
- const entry2 = ResourceEntry(stack, schema);
15040
- if (entry2)
15041
- return entry2.root;
15183
+ const entry = ResourceEntry(stack, schema);
15184
+ if (entry)
15185
+ return entry.root;
15042
15186
  return IsId(schema) ? schema : stack.lexicalSchema;
15043
15187
  }
15044
15188
  function HasStackKeywords(stack, schema) {
@@ -18204,7 +18348,234 @@ __export(value_exports, {
18204
18348
  Repair: () => Repair
18205
18349
  });
18206
18350
 
18351
+ // ../core/dist/store/file.js
18352
+ import { randomBytes } from "node:crypto";
18353
+ import { closeSync, mkdirSync as mkdirSync2, openSync, readFileSync as readFileSync3, renameSync, rmSync, statSync as statSync2, writeFileSync as writeFileSync2, writeSync } from "node:fs";
18354
+ import { dirname as dirname3 } from "node:path";
18355
+ import { threadId } from "node:worker_threads";
18356
+ function token() {
18357
+ return `${process.pid}.${threadId}.${randomBytes(6).toString("hex")}`;
18358
+ }
18359
+ function readOr(file) {
18360
+ try {
18361
+ return readFileSync3(file, "utf8");
18362
+ } catch {
18363
+ return void 0;
18364
+ }
18365
+ }
18366
+ function withFileLock(file, options, body) {
18367
+ mkdirSync2(dirname3(file), { recursive: true });
18368
+ const lock = `${file}.lock`;
18369
+ const mine = token();
18370
+ const deadline = Date.now() + options.waitMs;
18371
+ for (; ; ) {
18372
+ try {
18373
+ const fd = openSync(lock, "wx");
18374
+ try {
18375
+ writeSync(fd, mine);
18376
+ } finally {
18377
+ closeSync(fd);
18378
+ }
18379
+ break;
18380
+ } catch (error) {
18381
+ if (error.code !== "EEXIST")
18382
+ throw error;
18383
+ takeOverIfStale(lock, options.staleMs);
18384
+ if (Date.now() > deadline)
18385
+ throw options.busy(lock);
18386
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 25);
18387
+ }
18388
+ }
18389
+ try {
18390
+ return body();
18391
+ } finally {
18392
+ if (readOr(lock) === mine)
18393
+ rmSync(lock, { force: true });
18394
+ }
18395
+ }
18396
+ function takeOverIfStale(lock, staleMs) {
18397
+ let judged;
18398
+ try {
18399
+ if (Date.now() - statSync2(lock).mtimeMs <= staleMs)
18400
+ return;
18401
+ judged = readOr(lock);
18402
+ } catch {
18403
+ return;
18404
+ }
18405
+ if (judged === void 0)
18406
+ return;
18407
+ const aside = `${lock}.${token()}.stale`;
18408
+ try {
18409
+ renameSync(lock, aside);
18410
+ } catch {
18411
+ return;
18412
+ }
18413
+ if (readOr(aside) === judged) {
18414
+ rmSync(aside, { force: true });
18415
+ return;
18416
+ }
18417
+ try {
18418
+ const fd = openSync(lock, "wx");
18419
+ closeSync(fd);
18420
+ renameSync(aside, lock);
18421
+ } catch {
18422
+ rmSync(aside, { force: true });
18423
+ }
18424
+ }
18425
+ function writeJsonAtomic(file, value) {
18426
+ writeTextAtomic(file, `${JSON.stringify(value, null, 2)}
18427
+ `);
18428
+ }
18429
+ function writeTextAtomic(file, text) {
18430
+ mkdirSync2(dirname3(file), { recursive: true });
18431
+ const tmp = `${file}.${token()}.tmp`;
18432
+ try {
18433
+ writeFileSync2(tmp, text);
18434
+ renameSync(tmp, file);
18435
+ } catch (error) {
18436
+ rmSync(tmp, { force: true });
18437
+ throw error;
18438
+ }
18439
+ }
18440
+
18441
+ // ../core/dist/guard/state.js
18442
+ var GUARD_STATE_FORMAT = 1;
18443
+ var GUARD_STATE_MAX_BYTES = 1e6;
18444
+ var CRITERIA_SNAPSHOT_MAX_CHARS = 16384;
18445
+ var SESSION_TTL_MS = 14 * 24 * 60 * 60 * 1e3;
18446
+ var MAX_SESSIONS = 200;
18447
+ var CLOSED = { additionalProperties: false };
18448
+ var Sha256 = typebox_exports.String({ pattern: "^[0-9a-f]{64}$" });
18449
+ var Time = typebox_exports.String({ pattern: "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,3})?Z$" });
18450
+ var SESSION_ID_PATTERN = "^(claude|codex):[A-Za-z0-9._-]{1,128}$";
18451
+ var MAX_CRITERIA_FILES = 20;
18452
+ var SessionSchema = typebox_exports.Object({
18453
+ harness: typebox_exports.Union([typebox_exports.Literal("claude"), typebox_exports.Literal("codex")]),
18454
+ /** The commit the session started from; `null` in a repo with no commits yet. */
18455
+ base: typebox_exports.Union([typebox_exports.String({ pattern: "^[0-9a-f]{40}([0-9a-f]{24})?$" }), typebox_exports.Null()]),
18456
+ /** `first-stop`: SessionStart didn't run, so the base is HEAD at the first Stop. */
18457
+ baseSource: typebox_exports.Union([typebox_exports.Literal("session-start"), typebox_exports.Literal("first-stop")]),
18458
+ startedAt: Time,
18459
+ updatedAt: Time,
18460
+ /** The criteria files at the base, by hash; the text too when it fits. */
18461
+ criteria: typebox_exports.Array(typebox_exports.Object({
18462
+ path: typebox_exports.String({ minLength: 1 }),
18463
+ sha256: Sha256,
18464
+ text: typebox_exports.Optional(typebox_exports.String({ maxLength: CRITERIA_SNAPSHOT_MAX_CHARS }))
18465
+ }, CLOSED), { maxItems: MAX_CRITERIA_FILES }),
18466
+ /** The last check: the hash of what it looked at, and what it did. */
18467
+ last: typebox_exports.Optional(typebox_exports.Object({
18468
+ hash: Sha256,
18469
+ at: Time,
18470
+ outcome: typebox_exports.Union([
18471
+ typebox_exports.Literal("allow"),
18472
+ typebox_exports.Literal("block"),
18473
+ typebox_exports.Literal("skipped")
18474
+ ])
18475
+ }, CLOSED))
18476
+ }, CLOSED);
18477
+ var GuardStateSchema = typebox_exports.Object({
18478
+ version: typebox_exports.Literal(GUARD_STATE_FORMAT),
18479
+ sessions: typebox_exports.Record(typebox_exports.String({ pattern: SESSION_ID_PATTERN }), SessionSchema)
18480
+ }, CLOSED);
18481
+ function guardStatePath(repoRoot) {
18482
+ return join2(stateDir(repoRoot), "guard", "state.json");
18483
+ }
18484
+ function guardStateProblems(value) {
18485
+ const problems = [...value_exports.Errors(GuardStateSchema, value)].map((e) => `${e.instancePath || "/"} ${e.message}`);
18486
+ const sessions = value?.sessions;
18487
+ if (typeof sessions === "object" && sessions !== null) {
18488
+ const pattern = new RegExp(SESSION_ID_PATTERN);
18489
+ for (const id of Object.keys(sessions)) {
18490
+ if (!pattern.test(id))
18491
+ problems.push(`/sessions ${JSON.stringify(id)} is not a session id`);
18492
+ }
18493
+ }
18494
+ return problems;
18495
+ }
18496
+ function readGuardState(file) {
18497
+ if (!existsSync3(file))
18498
+ return { version: GUARD_STATE_FORMAT, sessions: {} };
18499
+ let size;
18500
+ let text;
18501
+ try {
18502
+ size = statSync3(file).size;
18503
+ if (size > GUARD_STATE_MAX_BYTES)
18504
+ throw unreadable(file, [`it is ${size} bytes, over the limit`]);
18505
+ text = readFileSync4(file, "utf8");
18506
+ } catch (error) {
18507
+ if (error instanceof DecisionsError)
18508
+ throw error;
18509
+ throw unreadable(file, [`cannot read it: ${error.message}`]);
18510
+ }
18511
+ let value;
18512
+ try {
18513
+ value = JSON.parse(text);
18514
+ } catch (error) {
18515
+ throw unreadable(file, [`not JSON: ${error.message}`]);
18516
+ }
18517
+ const problems = guardStateProblems(value);
18518
+ if (problems.length)
18519
+ throw unreadable(file, problems);
18520
+ return value;
18521
+ }
18522
+ function unreadable(file, problems) {
18523
+ return new DecisionsError("invalid-request", `${file} is not a valid guard state (it was left as it is; delete it to start over):
18524
+ ${problems.join("\n ")}`, { file, problems });
18525
+ }
18526
+ function pruneSessions(state, now) {
18527
+ const cutoff = now.getTime() - SESSION_TTL_MS;
18528
+ const at = (s) => Math.min(Date.parse(s.updatedAt), now.getTime());
18529
+ const kept = Object.entries(state.sessions).filter(([, s]) => at(s) >= cutoff).sort(([, a], [, b]) => at(b) - at(a)).slice(0, MAX_SESSIONS);
18530
+ return { ...state, sessions: Object.fromEntries(kept) };
18531
+ }
18532
+ function updateGuardState(file, change, options = {}) {
18533
+ const now = options.now ?? /* @__PURE__ */ new Date();
18534
+ return withFileLock(file, {
18535
+ waitMs: options.waitMs ?? 200,
18536
+ staleMs: 3e4,
18537
+ busy: (lock) => new DecisionsError("invalid-request", `${file} is locked by another guard hook (${lock}). Delete the lock file if no hook is running.`, { file, lock })
18538
+ }, () => {
18539
+ const { state, result } = change(readGuardState(file));
18540
+ if (state) {
18541
+ const next = pruneSessions(state, now);
18542
+ const problems = guardStateProblems(next);
18543
+ if (problems.length) {
18544
+ throw new DecisionsError("invalid-request", `Refused to write an invalid guard state to ${file} (a bug in decide; the file is unchanged):
18545
+ ${problems.join("\n ")}`, { file, problems });
18546
+ }
18547
+ writeTextAtomic(file, fitToSize(next));
18548
+ }
18549
+ return result;
18550
+ });
18551
+ }
18552
+ function fitToSize(state) {
18553
+ const text = (s) => `${JSON.stringify(s, null, 2)}
18554
+ `;
18555
+ let out = text(state);
18556
+ if (Buffer.byteLength(out) <= GUARD_STATE_MAX_BYTES)
18557
+ return out;
18558
+ const entries = Object.entries(state.sessions).map(([id, s]) => [id, { ...s, criteria: s.criteria.map((c) => ({ ...c })) }]);
18559
+ const current = () => ({ ...state, sessions: Object.fromEntries(entries) });
18560
+ for (let i = entries.length - 1; i >= 0; i--) {
18561
+ for (const c of entries[i]?.[1].criteria ?? [])
18562
+ delete c.text;
18563
+ out = text(current());
18564
+ if (Buffer.byteLength(out) <= GUARD_STATE_MAX_BYTES)
18565
+ return out;
18566
+ }
18567
+ while (entries.length > 0) {
18568
+ entries.pop();
18569
+ out = text(current());
18570
+ if (Buffer.byteLength(out) <= GUARD_STATE_MAX_BYTES)
18571
+ return out;
18572
+ }
18573
+ return out;
18574
+ }
18575
+
18207
18576
  // ../core/dist/spec/spec.js
18577
+ import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync6 } from "node:fs";
18578
+ import { basename as basename2, extname, isAbsolute as isAbsolute3, join as join3, relative as relative3, resolve as resolve5 } from "node:path";
18208
18579
  var import_yaml3 = __toESM(require_dist(), 1);
18209
18580
 
18210
18581
  // ../core/dist/project/project.js
@@ -18294,12 +18665,12 @@ function project(input) {
18294
18665
  const kept = [];
18295
18666
  const undecided = [];
18296
18667
  let dropped = 0;
18297
- for (const row2 of input.rows) {
18298
- const v = verdictOf(row2.answers, input.undecidedOf(row2), keep, keepAny, input.sort?.question);
18668
+ for (const row of input.rows) {
18669
+ const v = verdictOf(row.answers, input.undecidedOf(row), keep, keepAny, input.sort?.question);
18299
18670
  if (v.verdict === "undecided")
18300
- undecided.push({ row: row2, questions: v.questions });
18671
+ undecided.push({ row, questions: v.questions });
18301
18672
  else if (v.verdict === "kept")
18302
- kept.push(row2);
18673
+ kept.push(row);
18303
18674
  else
18304
18675
  dropped += 1;
18305
18676
  }
@@ -18310,13 +18681,13 @@ function project(input) {
18310
18681
  }
18311
18682
  const keptTotal = kept.length;
18312
18683
  const limited = input.limit !== void 0 ? kept.slice(0, input.limit) : kept;
18313
- const withFields = input.fields ? limited.map((row2) => pickAnswers(row2, input.fields)) : limited;
18684
+ const withFields = input.fields ? limited.map((row) => pickAnswers(row, input.fields)) : limited;
18314
18685
  return { kept: withFields, keptTotal, undecided, dropped };
18315
18686
  }
18316
- function pickAnswers(row2, fields) {
18687
+ function pickAnswers(row, fields) {
18317
18688
  return {
18318
- ...row2,
18319
- answers: Object.fromEntries(Object.entries(row2.answers).filter(([k]) => fields.includes(k)))
18689
+ ...row,
18690
+ answers: Object.fromEntries(Object.entries(row.answers).filter(([k]) => fields.includes(k)))
18320
18691
  };
18321
18692
  }
18322
18693
  function read(answer, field) {
@@ -18351,18 +18722,18 @@ function num(value) {
18351
18722
  }
18352
18723
 
18353
18724
  // ../core/dist/sources/read.js
18354
- import { execFileSync } from "node:child_process";
18355
- import { existsSync as existsSync3, readFileSync as readFileSync3, realpathSync as realpathSync2, statSync as statSync3 } from "node:fs";
18725
+ import { execFile, execFileSync } from "node:child_process";
18726
+ import { existsSync as existsSync4, readFileSync as readFileSync5, realpathSync as realpathSync2, statSync as statSync5 } from "node:fs";
18356
18727
  import { isAbsolute as isAbsolute2, relative as relative2, resolve as resolve4 } from "node:path";
18357
18728
 
18358
18729
  // ../../node_modules/.pnpm/tinyglobby@0.2.17/node_modules/tinyglobby/dist/index.mjs
18359
- import { readdir, readdirSync, realpath, realpathSync, stat, statSync as statSync2 } from "fs";
18730
+ import { readdir, readdirSync, realpath, realpathSync, stat, statSync as statSync4 } from "fs";
18360
18731
  import { isAbsolute, posix, resolve as resolve3 } from "path";
18361
18732
  import { fileURLToPath } from "url";
18362
18733
 
18363
18734
  // ../../node_modules/.pnpm/fdir@6.5.0_picomatch@4.0.7/node_modules/fdir/dist/index.mjs
18364
18735
  import { createRequire } from "module";
18365
- import { basename, dirname as dirname3, normalize, relative, resolve as resolve2, sep } from "path";
18736
+ import { basename, dirname as dirname4, normalize, relative, resolve as resolve2, sep } from "path";
18366
18737
  import * as nativeFs from "fs";
18367
18738
  var __require2 = /* @__PURE__ */ createRequire(import.meta.url);
18368
18739
  function cleanPath(path) {
@@ -18433,14 +18804,14 @@ function build$6(root, options) {
18433
18804
  if (relativePaths) return filters && filters.length ? pushDirectoryFilterWithRelativePath(root) : pushDirectoryWithRelativePath(root);
18434
18805
  return filters && filters.length ? pushDirectoryFilter : pushDirectory;
18435
18806
  }
18436
- var pushFileFilterAndCount = (filename, _paths, counts2, filters) => {
18437
- if (filters.every((filter) => filter(filename, false))) counts2.files++;
18807
+ var pushFileFilterAndCount = (filename, _paths, counts, filters) => {
18808
+ if (filters.every((filter) => filter(filename, false))) counts.files++;
18438
18809
  };
18439
18810
  var pushFileFilter = (filename, paths, _counts, filters) => {
18440
18811
  if (filters.every((filter) => filter(filename, false))) paths.push(filename);
18441
18812
  };
18442
- var pushFileCount = (_filename, _paths, counts2, _filters) => {
18443
- counts2.files++;
18813
+ var pushFileCount = (_filename, _paths, counts, _filters) => {
18814
+ counts.files++;
18444
18815
  };
18445
18816
  var pushFile = (filename, paths) => {
18446
18817
  paths.push(filename);
@@ -18506,13 +18877,13 @@ function build$2(options, isSynchronous) {
18506
18877
  }
18507
18878
  function isRecursive(path, resolved, state) {
18508
18879
  if (state.options.useRealPaths) return isRecursiveUsingRealPaths(resolved, state);
18509
- let parent = dirname3(path);
18880
+ let parent = dirname4(path);
18510
18881
  let depth = 1;
18511
18882
  while (parent !== state.root && depth < 2) {
18512
18883
  const resolvedPath = state.symlinks.get(parent);
18513
18884
  const isSameRoot = !!resolvedPath && (resolvedPath === resolved || resolvedPath.startsWith(resolved) || resolved.startsWith(resolvedPath));
18514
18885
  if (isSameRoot) depth++;
18515
- else parent = dirname3(parent);
18886
+ else parent = dirname4(parent);
18516
18887
  }
18517
18888
  state.symlinks.set(path, resolved);
18518
18889
  return depth > 1;
@@ -18681,26 +19052,26 @@ var Walker = class {
18681
19052
  if (controller.aborted || signal && signal.aborted || maxFiles && paths.length > maxFiles) return;
18682
19053
  const files = this.getArray(this.state.paths);
18683
19054
  for (let i = 0; i < entries.length; ++i) {
18684
- const entry2 = entries[i];
18685
- if (entry2.isFile() || entry2.isSymbolicLink() && !resolveSymlinks$1 && !excludeSymlinks) {
18686
- const filename = this.joinPath(entry2.name, directoryPath);
19055
+ const entry = entries[i];
19056
+ if (entry.isFile() || entry.isSymbolicLink() && !resolveSymlinks$1 && !excludeSymlinks) {
19057
+ const filename = this.joinPath(entry.name, directoryPath);
18687
19058
  this.pushFile(filename, files, this.state.counts, filters);
18688
- } else if (entry2.isDirectory()) {
18689
- let path = joinDirectoryPath(entry2.name, directoryPath, this.state.options.pathSeparator);
18690
- if (exclude && exclude(entry2.name, path)) continue;
19059
+ } else if (entry.isDirectory()) {
19060
+ let path = joinDirectoryPath(entry.name, directoryPath, this.state.options.pathSeparator);
19061
+ if (exclude && exclude(entry.name, path)) continue;
18691
19062
  this.pushDirectory(path, paths, filters);
18692
19063
  this.walkDirectory(this.state, path, path, depth - 1, this.walk);
18693
- } else if (this.resolveSymlink && entry2.isSymbolicLink()) {
18694
- let path = joinPathWithBasePath(entry2.name, directoryPath);
19064
+ } else if (this.resolveSymlink && entry.isSymbolicLink()) {
19065
+ let path = joinPathWithBasePath(entry.name, directoryPath);
18695
19066
  this.resolveSymlink(path, this.state, (stat2, resolvedPath) => {
18696
19067
  if (stat2.isDirectory()) {
18697
19068
  resolvedPath = normalizePath(resolvedPath, this.state.options);
18698
- if (exclude && exclude(entry2.name, useRealPaths ? resolvedPath : path + pathSeparator)) return;
19069
+ if (exclude && exclude(entry.name, useRealPaths ? resolvedPath : path + pathSeparator)) return;
18699
19070
  this.walkDirectory(this.state, resolvedPath, useRealPaths ? resolvedPath : path + pathSeparator, depth - 1, this.walk);
18700
19071
  } else {
18701
19072
  resolvedPath = useRealPaths ? resolvedPath : path;
18702
19073
  const filename = basename(resolvedPath);
18703
- const directoryPath$1 = normalizePath(dirname3(resolvedPath), this.state.options);
19074
+ const directoryPath$1 = normalizePath(dirname4(resolvedPath), this.state.options);
18704
19075
  resolvedPath = this.joinPath(filename, directoryPath$1);
18705
19076
  this.pushFile(resolvedPath, files, this.state.counts, filters);
18706
19077
  }
@@ -19111,7 +19482,7 @@ function getOptions(options) {
19111
19482
  realpath: opts.fs.realpath || realpath,
19112
19483
  realpathSync: opts.fs.realpathSync || realpathSync,
19113
19484
  stat: opts.fs.stat || stat,
19114
- statSync: opts.fs.statSync || statSync2
19485
+ statSync: opts.fs.statSync || statSync4
19115
19486
  });
19116
19487
  if (opts.debug) log("globbing with options:", opts);
19117
19488
  return opts;
@@ -19190,7 +19561,7 @@ async function readSource(spec, options) {
19190
19561
  case "stdin":
19191
19562
  return { documents: [{ id: "stdin", kind: "text", text: spec.text }], skipped: [] };
19192
19563
  case "file":
19193
- return readFiles([spec.path], options, spec.range);
19564
+ return readFiles([spec.path], options, spec.range, spec.optional);
19194
19565
  case "glob":
19195
19566
  return readGlob(spec.patterns, options);
19196
19567
  case "jsonl":
@@ -19210,24 +19581,26 @@ function parseFileRef(ref) {
19210
19581
  }
19211
19582
  return { path: match[1], range: { start, end } };
19212
19583
  }
19213
- function readFiles(paths, options, range) {
19584
+ function readFiles(paths, options, range, optional = false) {
19214
19585
  const documents = [];
19215
19586
  const skipped = [];
19216
19587
  const limit = options.maxFileBytes ?? DEFAULT_MAX_FILE_BYTES;
19217
19588
  for (const path of paths) {
19218
19589
  const { absolute, display: rel, real, outside } = resolvePath(options.cwd, path);
19219
- if (!existsSync3(absolute) || !statSync3(absolute).isFile()) {
19590
+ if (!existsSync4(absolute) || !statSync5(absolute).isFile()) {
19591
+ if (optional)
19592
+ continue;
19220
19593
  throw new DecisionsError("source-error", `No such file: ${rel}`, { path: rel });
19221
19594
  }
19222
19595
  if (outside && !options.allowOutside) {
19223
19596
  skipped.push({ path: rel, reason: "outside-repo", detail: real });
19224
19597
  continue;
19225
19598
  }
19226
- if (statSync3(absolute).size > limit) {
19599
+ if (statSync5(absolute).size > limit) {
19227
19600
  skipped.push({ path: rel, reason: "too-large", detail: `> ${limit} bytes` });
19228
19601
  continue;
19229
19602
  }
19230
- const buffer = readFileSync3(absolute);
19603
+ const buffer = readFileSync5(absolute);
19231
19604
  if (buffer.subarray(0, 8e3).includes(0)) {
19232
19605
  skipped.push({ path: rel, reason: "binary" });
19233
19606
  continue;
@@ -19311,13 +19684,13 @@ function insideGitWorkTree(cwd) {
19311
19684
  }
19312
19685
  function readJsonl(path, options) {
19313
19686
  const { absolute, display: rel, real, outside } = resolvePath(options.cwd, path);
19314
- if (!existsSync3(absolute))
19687
+ if (!existsSync4(absolute))
19315
19688
  throw new DecisionsError("source-error", `No such file: ${rel}`, { path: rel });
19316
19689
  if (outside && !options.allowOutside) {
19317
19690
  return { documents: [], skipped: [{ path: rel, reason: "outside-repo", detail: real }] };
19318
19691
  }
19319
19692
  const documents = [];
19320
- readFileSync3(absolute, "utf8").split("\n").forEach((line, index) => {
19693
+ readFileSync5(absolute, "utf8").split("\n").forEach((line, index) => {
19321
19694
  if (line.trim() === "")
19322
19695
  return;
19323
19696
  let data;
@@ -19340,7 +19713,7 @@ function readJsonl(path, options) {
19340
19713
  });
19341
19714
  return { documents, skipped: [] };
19342
19715
  }
19343
- function readDiff(spec, options) {
19716
+ async function readDiff(spec, options) {
19344
19717
  if (spec.range?.startsWith("-")) {
19345
19718
  throw new DecisionsError("invalid-request", `Invalid diff range "${spec.range}"`);
19346
19719
  }
@@ -19348,17 +19721,19 @@ function readDiff(spec, options) {
19348
19721
  if (spec.staged)
19349
19722
  args.push("--cached");
19350
19723
  args.push(spec.range ?? "HEAD", "--", ...spec.paths ?? []);
19351
- let out;
19352
- try {
19353
- out = execFileSync("git", args, {
19724
+ const out = await new Promise((resolve6, reject) => {
19725
+ execFile("git", args, {
19354
19726
  encoding: "utf8",
19355
19727
  maxBuffer: 64 * 1024 * 1024,
19356
- stdio: ["ignore", "pipe", "pipe"]
19728
+ ...options.signal ? { signal: options.signal } : {}
19729
+ }, (error, stdout, stderr) => {
19730
+ if (!error)
19731
+ return resolve6(stdout);
19732
+ if (error.name === "AbortError")
19733
+ return reject(error);
19734
+ reject(new DecisionsError("source-error", `git diff failed: ${String(stderr || error).trim()}`));
19357
19735
  });
19358
- } catch (error) {
19359
- const stderr = String(error.stderr ?? error).trim();
19360
- throw new DecisionsError("source-error", `git diff failed: ${stderr}`);
19361
- }
19736
+ });
19362
19737
  return splitDiffByFile(out);
19363
19738
  }
19364
19739
  function splitDiffByFile(diff) {
@@ -19366,9 +19741,15 @@ function splitDiffByFile(diff) {
19366
19741
  const documents = [];
19367
19742
  const skipped = [];
19368
19743
  for (const text of sections) {
19369
- const raw = /^\+\+\+ (?:b\/)?(.+)$/m.exec(text)?.[1] ?? /^diff --git a\/.+ b\/(.+)$/m.exec(text)?.[1];
19370
- const decoded = raw === void 0 ? void 0 : unquoteGitPath(raw.trim());
19371
- const path = decoded?.replace(/^b\//, "");
19744
+ const side = (marker, prefix) => {
19745
+ const found2 = new RegExp(`^\\${marker[0]}\\${marker[1]}\\${marker[2]} (.+)$`, "m").exec(text)?.[1];
19746
+ if (found2 === void 0 || found2.trim() === "/dev/null")
19747
+ return void 0;
19748
+ return unquoteGitPath(found2.trim())?.replace(new RegExp(`^${prefix}/`), "");
19749
+ };
19750
+ const header = /^diff --git a\/.+ b\/(.+)$/m.exec(text)?.[1];
19751
+ const raw = header ?? /^\+\+\+ (.+)$/m.exec(text)?.[1];
19752
+ const path = side("+++", "b") ?? side("---", "a") ?? (header === void 0 ? void 0 : unquoteGitPath(header.trim())?.replace(/^b\//, ""));
19372
19753
  if (path === void 0 || path === "/dev/null") {
19373
19754
  skipped.push({
19374
19755
  path: raw?.trim() ?? "(unnamed)",
@@ -19605,8 +19986,8 @@ function describeExpectation(expectation) {
19605
19986
  }
19606
19987
 
19607
19988
  // ../core/dist/spec/spec.js
19608
- var CLOSED = { additionalProperties: false };
19609
- var Threshold = typebox_exports.Object({ value: typebox_exports.Number(), why: typebox_exports.String({ minLength: 1 }) }, CLOSED);
19989
+ var CLOSED2 = { additionalProperties: false };
19990
+ var Threshold = typebox_exports.Object({ value: typebox_exports.Number(), why: typebox_exports.String({ minLength: 1 }) }, CLOSED2);
19610
19991
  var SPEC_FORMAT = 1;
19611
19992
  var SpecSchema = typebox_exports.Object({
19612
19993
  /** Spec file format. Absent means 1; a newer one is refused, not guessed. */
@@ -19617,14 +19998,14 @@ var SpecSchema = typebox_exports.Object({
19617
19998
  /** Kept when any of these matches, as well as every `keep`. */
19618
19999
  keepAny: typebox_exports.Optional(typebox_exports.Array(typebox_exports.String())),
19619
20000
  sort: typebox_exports.Optional(typebox_exports.String()),
19620
- policy: typebox_exports.Optional(typebox_exports.Object({ thresholds: typebox_exports.Optional(typebox_exports.Record(typebox_exports.String(), Threshold)) }, CLOSED)),
20001
+ policy: typebox_exports.Optional(typebox_exports.Object({ thresholds: typebox_exports.Optional(typebox_exports.Record(typebox_exports.String(), Threshold)) }, CLOSED2)),
19621
20002
  source: typebox_exports.Optional(typebox_exports.Object({
19622
20003
  glob: typebox_exports.Optional(typebox_exports.Array(typebox_exports.String())),
19623
20004
  file: typebox_exports.Optional(typebox_exports.String()),
19624
20005
  jsonl: typebox_exports.Optional(typebox_exports.String()),
19625
20006
  diff: typebox_exports.Optional(typebox_exports.String()),
19626
20007
  split: typebox_exports.Optional(typebox_exports.String())
19627
- }, CLOSED)),
20008
+ }, CLOSED2)),
19628
20009
  examples: typebox_exports.Optional(typebox_exports.Array(typebox_exports.Object({
19629
20010
  id: typebox_exports.String({ minLength: 1 }),
19630
20011
  /** The text (or a JSON value, sent as its JSON text) to judge. Give this or `file`. */
@@ -19632,11 +20013,11 @@ var SpecSchema = typebox_exports.Object({
19632
20013
  /** A repo file to judge, `path` or `path:START-END`. Give this or `state`. */
19633
20014
  file: typebox_exports.Optional(typebox_exports.String()),
19634
20015
  expect: typebox_exports.Optional(typebox_exports.Record(typebox_exports.String(), typebox_exports.Unknown()))
19635
- }, CLOSED))),
20016
+ }, CLOSED2))),
19636
20017
  provenance: typebox_exports.Optional(typebox_exports.Record(typebox_exports.String(), typebox_exports.Unknown())),
19637
20018
  /** Anything else a team wants to carry, ignored by the engine. */
19638
20019
  meta: typebox_exports.Optional(typebox_exports.Record(typebox_exports.String(), typebox_exports.Unknown()))
19639
- }, CLOSED);
20020
+ }, CLOSED2);
19640
20021
  var NAME = /^[a-z0-9][a-z0-9._-]*$/;
19641
20022
  function parseSpec(text, file, origin) {
19642
20023
  let raw;
@@ -19706,7 +20087,7 @@ function exampleFileProblem(ref, repoRoot) {
19706
20087
  const rel = relative3(repoRoot, full);
19707
20088
  if (isAbsolute3(path) || rel.startsWith("..") || isAbsolute3(rel))
19708
20089
  return "is outside the repo";
19709
- if (!existsSync4(full))
20090
+ if (!existsSync5(full))
19710
20091
  return "does not exist";
19711
20092
  return void 0;
19712
20093
  }
@@ -19737,23 +20118,23 @@ function isQuestion(value) {
19737
20118
  }
19738
20119
  function specDirs(repoRoot, userConfigDir2, bundled) {
19739
20120
  return {
19740
- repo: join2(repoRoot, ".system1", "specs"),
19741
- user: join2(userConfigDir2, "specs"),
20121
+ repo: join3(repoRoot, ".system1", "specs"),
20122
+ user: join3(userConfigDir2, "specs"),
19742
20123
  ...bundled ? { bundled } : {}
19743
20124
  };
19744
20125
  }
19745
20126
  function loadSpec(ref, dirs, cwd) {
19746
20127
  if (ref.includes("/") || ref.endsWith(".yaml") || ref.endsWith(".yml") || ref.endsWith(".json")) {
19747
20128
  const file = isAbsolute3(ref) ? ref : resolve5(cwd, ref);
19748
- if (!existsSync4(file))
20129
+ if (!existsSync5(file))
19749
20130
  throw new DecisionsError("invalid-request", `No spec file at ${ref}`);
19750
- return parseSpec(readFileSync4(file, "utf8"), file, "path");
20131
+ return parseSpec(readFileSync6(file, "utf8"), file, "path");
19751
20132
  }
19752
20133
  for (const [origin, dir] of layers(dirs)) {
19753
20134
  for (const ext of [".yaml", ".yml", ".json"]) {
19754
- const file = join2(dir, `${ref}${ext}`);
19755
- if (existsSync4(file))
19756
- return parseSpec(readFileSync4(file, "utf8"), file, origin);
20135
+ const file = join3(dir, `${ref}${ext}`);
20136
+ if (existsSync5(file))
20137
+ return parseSpec(readFileSync6(file, "utf8"), file, origin);
19757
20138
  }
19758
20139
  }
19759
20140
  const known = listSpecs(dirs).map((s) => s.name);
@@ -19763,17 +20144,17 @@ function listSpecs(dirs) {
19763
20144
  const seen = /* @__PURE__ */ new Set();
19764
20145
  const out = [];
19765
20146
  for (const [origin, dir] of layers(dirs)) {
19766
- if (!existsSync4(dir))
20147
+ if (!existsSync5(dir))
19767
20148
  continue;
19768
- for (const entry2 of readdirSync2(dir).sort()) {
19769
- if (!/\.(ya?ml|json)$/.test(entry2))
20149
+ for (const entry of readdirSync2(dir).sort()) {
20150
+ if (!/\.(ya?ml|json)$/.test(entry))
19770
20151
  continue;
19771
- const file = join2(dir, entry2);
19772
- const name = basename2(entry2, extname(entry2));
20152
+ const file = join3(dir, entry);
20153
+ const name = basename2(entry, extname(entry));
19773
20154
  const shadowed = seen.has(name);
19774
20155
  seen.add(name);
19775
20156
  try {
19776
- const spec = parseSpec(readFileSync4(file, "utf8"), file, origin);
20157
+ const spec = parseSpec(readFileSync6(file, "utf8"), file, origin);
19777
20158
  out.push({
19778
20159
  name,
19779
20160
  origin,
@@ -19833,10 +20214,10 @@ var QuestionSchema = typebox_exports.Union([
19833
20214
 
19834
20215
  // ../core/dist/opportunities/backlog.js
19835
20216
  import { createHash } from "node:crypto";
19836
- import { closeSync, existsSync as existsSync5, mkdirSync as mkdirSync2, openSync, readFileSync as readFileSync5, renameSync, rmSync, statSync as statSync4, writeFileSync as writeFileSync2 } from "node:fs";
19837
- import { dirname as dirname4, join as join3 } from "node:path";
20217
+ import { existsSync as existsSync6, readFileSync as readFileSync7 } from "node:fs";
20218
+ import { join as join4 } from "node:path";
19838
20219
  var BACKLOG_FORMAT = 1;
19839
- var CLOSED2 = { additionalProperties: false };
20220
+ var CLOSED3 = { additionalProperties: false };
19840
20221
  var Text = typebox_exports.String({ minLength: 1 });
19841
20222
  var Usd = typebox_exports.Number({ minimum: 0, maximum: 1e6 });
19842
20223
  var Status = typebox_exports.Union([
@@ -19850,8 +20231,8 @@ var candidateFields = {
19850
20231
  mode: typebox_exports.Union([typebox_exports.Literal("code"), typebox_exports.Literal("agents")]),
19851
20232
  location: typebox_exports.Object({
19852
20233
  path: Text,
19853
- lines: typebox_exports.Optional(typebox_exports.Object({ start: typebox_exports.Integer({ minimum: 1 }), end: typebox_exports.Integer({ minimum: 1 }) }, CLOSED2))
19854
- }, CLOSED2),
20234
+ lines: typebox_exports.Optional(typebox_exports.Object({ start: typebox_exports.Integer({ minimum: 1 }), end: typebox_exports.Integer({ minimum: 1 }) }, CLOSED3))
20235
+ }, CLOSED3),
19855
20236
  /** What does the job today, e.g. "gpt-4o call parsed to an enum". */
19856
20237
  mechanism: Text,
19857
20238
  shape: typebox_exports.Union([
@@ -19883,12 +20264,12 @@ var candidateFields = {
19883
20264
  decisionCostPerItemUsd: Usd,
19884
20265
  /** Where the figures came from. */
19885
20266
  note: typebox_exports.Optional(typebox_exports.String())
19886
- }, CLOSED2),
20267
+ }, CLOSED3),
19887
20268
  risk: typebox_exports.Object({
19888
20269
  level: typebox_exports.Union([typebox_exports.Literal("low"), typebox_exports.Literal("medium"), typebox_exports.Literal("high")]),
19889
20270
  /** What would go wrong if the replacement erred. */
19890
20271
  note: Text
19891
- }, CLOSED2),
20272
+ }, CLOSED3),
19892
20273
  /** The next step, e.g. "save the spec with design, then adopt". */
19893
20274
  next: Text,
19894
20275
  /** Defaults to `new`. `rejected` needs a `statusReason`. */
@@ -19898,9 +20279,9 @@ var candidateFields = {
19898
20279
  source: typebox_exports.Object({
19899
20280
  sweep: Text,
19900
20281
  answers: typebox_exports.Union([typebox_exports.Literal("live"), typebox_exports.Literal("replay")])
19901
- }, CLOSED2)
20282
+ }, CLOSED3)
19902
20283
  };
19903
- var CandidateSchema = typebox_exports.Object(candidateFields, CLOSED2);
20284
+ var CandidateSchema = typebox_exports.Object(candidateFields, CLOSED3);
19904
20285
  var OpportunitySchema = typebox_exports.Object({
19905
20286
  id: typebox_exports.String({ pattern: "^op-[0-9a-f]{12}$" }),
19906
20287
  ...candidateFields,
@@ -19910,16 +20291,16 @@ var OpportunitySchema = typebox_exports.Object({
19910
20291
  basis: typebox_exports.Literal("projected"),
19911
20292
  /** volume × (current − decision), per `per`. Negative means it would cost more. */
19912
20293
  savingUsd: typebox_exports.Number()
19913
- }, CLOSED2),
20294
+ }, CLOSED3),
19914
20295
  firstSeenAt: typebox_exports.String(),
19915
20296
  seenAt: typebox_exports.String()
19916
- }, CLOSED2);
20297
+ }, CLOSED3);
19917
20298
  var BacklogSchema = typebox_exports.Object({
19918
20299
  version: typebox_exports.Literal(BACKLOG_FORMAT),
19919
20300
  opportunities: typebox_exports.Array(OpportunitySchema)
19920
- }, CLOSED2);
20301
+ }, CLOSED3);
19921
20302
  function backlogPath(repoRoot) {
19922
- return join3(stateDir(repoRoot), "opportunities.json");
20303
+ return join4(stateDir(repoRoot), "opportunities.json");
19923
20304
  }
19924
20305
  function opportunityId(mode, evidence) {
19925
20306
  const normal = evidence.replace(/\s+/g, " ").trim();
@@ -19968,11 +20349,11 @@ function candidateProblems(c, at) {
19968
20349
  return problems;
19969
20350
  }
19970
20351
  function readBacklog(file) {
19971
- if (!existsSync5(file))
20352
+ if (!existsSync6(file))
19972
20353
  return { version: BACKLOG_FORMAT, opportunities: [] };
19973
20354
  let text;
19974
20355
  try {
19975
- text = readFileSync5(file, "utf8");
20356
+ text = readFileSync7(file, "utf8");
19976
20357
  } catch (error) {
19977
20358
  throw malformed2(file, [`cannot read it: ${error.message}`]);
19978
20359
  }
@@ -19994,39 +20375,14 @@ function malformed2(file, problems) {
19994
20375
  var LOCK_WAIT_MS = 5e3;
19995
20376
  var LOCK_STALE_MS = 3e4;
19996
20377
  function withBacklogLock(file, body) {
19997
- mkdirSync2(dirname4(file), { recursive: true });
19998
- const lock = `${file}.lock`;
19999
- const deadline = Date.now() + LOCK_WAIT_MS;
20000
- for (; ; ) {
20001
- try {
20002
- closeSync(openSync(lock, "wx"));
20003
- break;
20004
- } catch (error) {
20005
- if (error.code !== "EEXIST")
20006
- throw error;
20007
- try {
20008
- if (Date.now() - statSync4(lock).mtimeMs > LOCK_STALE_MS)
20009
- rmSync(lock, { force: true });
20010
- } catch {
20011
- }
20012
- if (Date.now() > deadline) {
20013
- throw new DecisionsError("invalid-request", `${file} is locked by another \`decide opportunities add\` (${lock}). Retry, or delete the lock file if no add is running.`, { file, lock });
20014
- }
20015
- Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 25);
20016
- }
20017
- }
20018
- try {
20019
- return body();
20020
- } finally {
20021
- rmSync(lock, { force: true });
20022
- }
20378
+ return withFileLock(file, {
20379
+ waitMs: LOCK_WAIT_MS,
20380
+ staleMs: LOCK_STALE_MS,
20381
+ busy: (lock) => new DecisionsError("invalid-request", `${file} is locked by another \`decide opportunities add\` (${lock}). Retry, or delete the lock file if no add is running.`, { file, lock })
20382
+ }, body);
20023
20383
  }
20024
20384
  function writeBacklog(file, backlog) {
20025
- mkdirSync2(dirname4(file), { recursive: true });
20026
- const tmp = `${file}.${process.pid}.tmp`;
20027
- writeFileSync2(tmp, `${JSON.stringify(backlog, null, 2)}
20028
- `);
20029
- renameSync(tmp, file);
20385
+ writeJsonAtomic(file, backlog);
20030
20386
  }
20031
20387
  function mergeCandidates(backlog, candidates, now) {
20032
20388
  const problems = candidates.flatMap((c, i) => candidateProblems(c, `/candidates/${i}`));
@@ -20268,6 +20624,16 @@ var RouteInput = typebox_exports.Object({
20268
20624
  /** The user's prompt, as the harness hook received it. */
20269
20625
  prompt: typebox_exports.String()
20270
20626
  }, strict);
20627
+ var HookEvent = typebox_exports.Object({
20628
+ hook_event_name: typebox_exports.String({ minLength: 1, description: "SessionStart or Stop" }),
20629
+ session_id: typebox_exports.String({ minLength: 1 }),
20630
+ cwd: typebox_exports.String({ minLength: 1 }),
20631
+ source: typebox_exports.Optional(typebox_exports.String({ description: "SessionStart: startup, resume, clear, compact" })),
20632
+ stop_hook_active: typebox_exports.Optional(typebox_exports.Boolean()),
20633
+ transcript_path: typebox_exports.Optional(typebox_exports.Union([typebox_exports.String(), typebox_exports.Null()])),
20634
+ turn_id: typebox_exports.Optional(typebox_exports.String({ description: "Codex only" })),
20635
+ last_assistant_message: typebox_exports.Optional(typebox_exports.Union([typebox_exports.String(), typebox_exports.Null()]))
20636
+ }, { additionalProperties: true });
20271
20637
  var TOOL_SCHEMAS = {
20272
20638
  ask: AskInput,
20273
20639
  many: ManyInput,
@@ -20277,11 +20643,12 @@ var TOOL_SCHEMAS = {
20277
20643
  "opportunities-add": OpportunitiesAddInput,
20278
20644
  "opportunities-list": OpportunitiesListInput,
20279
20645
  "opportunities-check": OpportunitiesCheckInput,
20280
- route: RouteInput
20646
+ route: RouteInput,
20647
+ hook: HookEvent
20281
20648
  };
20282
20649
 
20283
20650
  // ../core/dist/tools/context.js
20284
- import { isAbsolute as isAbsolute4, join as join5, relative as relative4 } from "node:path";
20651
+ import { isAbsolute as isAbsolute4, join as join6, relative as relative4 } from "node:path";
20285
20652
 
20286
20653
  // ../core/dist/egress/scrub.js
20287
20654
  var SECRET_NAME = "[A-Za-z0-9_.-]*(?:api[_-]?key|secret|token|passw(?:or)?d|pwd|credential|private[_-]?key|auth)[A-Za-z0-9_.-]*";
@@ -20332,42 +20699,42 @@ var RULES = [
20332
20699
  pattern: /(?<=\b[A-Z][A-Z0-9_]*(?:KEY|TOKEN|SECRET|PASSWORD|PASS|PAT|CREDENTIALS?)["']?\s*[:=]\s*["']?)(?!\$\{|<|\[REDACTED)(?=[A-Za-z0-9_\-+/=~]*\d)(?=[A-Za-z0-9_\-+/=~]*[A-Za-z])[A-Za-z0-9_\-+/=~]{12,}(?=["'\s,}]|$)/gm
20333
20700
  }
20334
20701
  ];
20335
- function scrubText(text, counts2 = {}) {
20702
+ function scrubText(text, counts = {}) {
20336
20703
  let out = text;
20337
20704
  for (const rule of RULES) {
20338
20705
  out = out.replace(rule.pattern, () => {
20339
- counts2[rule.kind] = (counts2[rule.kind] ?? 0) + 1;
20706
+ counts[rule.kind] = (counts[rule.kind] ?? 0) + 1;
20340
20707
  return `[REDACTED:${rule.kind}]`;
20341
20708
  });
20342
20709
  }
20343
20710
  return out;
20344
20711
  }
20345
- function scrubState(state, counts2 = {}) {
20346
- return walk(state, counts2);
20712
+ function scrubState(state, counts = {}) {
20713
+ return walk(state, counts);
20347
20714
  }
20348
20715
  var SECRET_KEY = new RegExp(`^${SECRET_NAME}$`, "i");
20349
- function walk(value, counts2, key) {
20716
+ function walk(value, counts, key) {
20350
20717
  if (typeof value === "string") {
20351
20718
  if (key !== void 0 && SECRET_KEY.test(key) && value.trim().length >= 4) {
20352
- counts2["assigned-secret"] = (counts2["assigned-secret"] ?? 0) + 1;
20719
+ counts["assigned-secret"] = (counts["assigned-secret"] ?? 0) + 1;
20353
20720
  return "[REDACTED:assigned-secret]";
20354
20721
  }
20355
- return scrubText(value, counts2);
20722
+ return scrubText(value, counts);
20356
20723
  }
20357
20724
  if (Array.isArray(value))
20358
- return value.map((v) => walk(v, counts2, key));
20725
+ return value.map((v) => walk(v, counts, key));
20359
20726
  if (value !== null && typeof value === "object") {
20360
- return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, walk(v, counts2, k)]));
20727
+ return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, walk(v, counts, k)]));
20361
20728
  }
20362
20729
  return value;
20363
20730
  }
20364
- function scrubQuestions(questions, counts2 = {}) {
20731
+ function scrubQuestions(questions, counts = {}) {
20365
20732
  return Object.fromEntries(Object.entries(questions).map(([name, q]) => [
20366
20733
  name,
20367
20734
  {
20368
20735
  ...q,
20369
- instructions: scrubText(q.instructions, counts2),
20370
- ...q.criteria === void 0 ? {} : { criteria: scrubState(q.criteria, counts2) }
20736
+ instructions: scrubText(q.instructions, counts),
20737
+ ...q.criteria === void 0 ? {} : { criteria: scrubState(q.criteria, counts) }
20371
20738
  }
20372
20739
  ]));
20373
20740
  }
@@ -20388,8 +20755,8 @@ function assertStateFits(id, state, questions, profile) {
20388
20755
 
20389
20756
  // ../core/dist/fixtures/store.js
20390
20757
  import { createHash as createHash2 } from "node:crypto";
20391
- import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync6, renameSync as renameSync2, writeFileSync as writeFileSync3 } from "node:fs";
20392
- import { dirname as dirname5, join as join4 } from "node:path";
20758
+ import { existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync8, renameSync as renameSync2, writeFileSync as writeFileSync3 } from "node:fs";
20759
+ import { dirname as dirname5, join as join5 } from "node:path";
20393
20760
  var FIXTURE_VERSION = 1;
20394
20761
  function fixtureKey(request) {
20395
20762
  const { model, state, questions } = request;
@@ -20416,13 +20783,13 @@ var FixtureStore = class {
20416
20783
  if (!NAMESPACE.test(namespace)) {
20417
20784
  throw new Error(`Invalid fixture namespace "${namespace}" (lowercase letters, digits, . _ -)`);
20418
20785
  }
20419
- return join4(this.dir, namespace, `${key}.json`);
20786
+ return join5(this.dir, namespace, `${key}.json`);
20420
20787
  }
20421
20788
  lookup(namespace, request) {
20422
20789
  const file = this.path(namespace, fixtureKey(request));
20423
- if (!existsSync6(file))
20790
+ if (!existsSync7(file))
20424
20791
  return void 0;
20425
- const record = JSON.parse(readFileSync6(file, "utf8"));
20792
+ const record = JSON.parse(readFileSync8(file, "utf8"));
20426
20793
  return record.v === FIXTURE_VERSION ? record : void 0;
20427
20794
  }
20428
20795
  record(namespace, request, response, now = /* @__PURE__ */ new Date()) {
@@ -20447,7 +20814,7 @@ var FixtureStore = class {
20447
20814
  // ../core/dist/decide.js
20448
20815
  var ZERO = { input_tokens: 0, output_tokens: 0, cost: 0 };
20449
20816
  function createDecider(options) {
20450
- const { profile, transport, fixtures, ledger, session } = options;
20817
+ const { profile, transport, fixtures, ledger, session, tag } = options;
20451
20818
  const now = options.now ?? (() => /* @__PURE__ */ new Date());
20452
20819
  const mode = (options.mode ?? "auto") === "auto" ? transport ? "live" : "replay" : options.mode;
20453
20820
  if ((mode === "live" || mode === "record") && !transport) {
@@ -20459,6 +20826,7 @@ function createDecider(options) {
20459
20826
  const log2 = (source, model, usage) => ledger?.append({
20460
20827
  ts: now().toISOString(),
20461
20828
  ...session ? { session } : {},
20829
+ ...tag ? { tag } : {},
20462
20830
  model,
20463
20831
  source,
20464
20832
  calls: 1,
@@ -20517,7 +20885,7 @@ function createDecider(options) {
20517
20885
  }
20518
20886
 
20519
20887
  // ../core/dist/run/spend.js
20520
- import { appendFileSync, existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync7 } from "node:fs";
20888
+ import { appendFileSync, existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as readFileSync9 } from "node:fs";
20521
20889
  import { dirname as dirname6 } from "node:path";
20522
20890
  function sumUsage(usages) {
20523
20891
  const total = usages.reduce((sum, u) => ({
@@ -20532,15 +20900,15 @@ var SpendLedger = class {
20532
20900
  constructor(file) {
20533
20901
  this.file = file;
20534
20902
  }
20535
- append(entry2) {
20903
+ append(entry) {
20536
20904
  mkdirSync4(dirname6(this.file), { recursive: true });
20537
- appendFileSync(this.file, `${JSON.stringify(entry2)}
20905
+ appendFileSync(this.file, `${JSON.stringify(entry)}
20538
20906
  `);
20539
20907
  }
20540
20908
  entries() {
20541
- if (!existsSync7(this.file))
20909
+ if (!existsSync8(this.file))
20542
20910
  return [];
20543
- return readFileSync7(this.file, "utf8").split("\n").filter((line) => line.trim() !== "").flatMap((line) => {
20911
+ return readFileSync9(this.file, "utf8").split("\n").filter((line) => line.trim() !== "").flatMap((line) => {
20544
20912
  try {
20545
20913
  return [JSON.parse(line)];
20546
20914
  } catch {
@@ -20549,7 +20917,7 @@ var SpendLedger = class {
20549
20917
  });
20550
20918
  }
20551
20919
  summary(filter = {}) {
20552
- const rows = this.entries().filter((e) => (filter.session === void 0 || e.session === filter.session) && (filter.since === void 0 || new Date(e.ts) >= filter.since));
20920
+ const rows = this.entries().filter((e) => (filter.session === void 0 || e.session === filter.session) && (filter.tag === void 0 || e.tag === filter.tag) && (filter.since === void 0 || new Date(e.ts) >= filter.since));
20553
20921
  const count = (source) => rows.filter((e) => e.source === source).reduce((n, e) => n + e.calls, 0);
20554
20922
  return {
20555
20923
  ...sumUsage(rows),
@@ -20625,13 +20993,13 @@ function rebasePattern(pattern, cwd, repoRoot) {
20625
20993
  const prefix = relative4(repoRoot, cwd);
20626
20994
  if (prefix === "" || prefix.startsWith("..") || isAbsolute4(prefix) || pattern.startsWith("**/"))
20627
20995
  return pattern;
20628
- return join5(prefix, pattern);
20996
+ return join6(prefix, pattern);
20629
20997
  }
20630
20998
  function rebase(source, cwd, repoRoot) {
20631
20999
  const prefix = relative4(repoRoot, cwd);
20632
21000
  if (prefix === "" || prefix.startsWith("..") || isAbsolute4(prefix))
20633
21001
  return source;
20634
- const move = (path) => isAbsolute4(path) ? path : join5(prefix, path);
21002
+ const move = (path) => isAbsolute4(path) ? path : join6(prefix, path);
20635
21003
  switch (source.kind) {
20636
21004
  case "file":
20637
21005
  return { ...source, path: move(source.path) };
@@ -20656,7 +21024,7 @@ function specSources(spec) {
20656
21024
  ...s.diff !== void 0 ? [{ kind: "diff", ...s.diff ? { range: s.diff } : {} }] : []
20657
21025
  ];
20658
21026
  }
20659
- function deciderFor(ctx, profile, mode = "auto") {
21027
+ function deciderFor(ctx, profile, mode = "auto", extra = {}) {
20660
21028
  const { config } = ctx;
20661
21029
  const dir = stateDir(config.repoRoot);
20662
21030
  return createDecider({
@@ -20671,14 +21039,15 @@ function deciderFor(ctx, profile, mode = "auto") {
20671
21039
  ...ctx.fetch ? { fetch: ctx.fetch } : {}
20672
21040
  })
20673
21041
  } : {},
20674
- fixtures: new FixtureStore(join5(dir, "fixtures")),
20675
- ledger: new SpendLedger(join5(dir, "usage.jsonl")),
21042
+ fixtures: new FixtureStore(join6(dir, "fixtures")),
21043
+ ledger: new SpendLedger(join6(dir, "usage.jsonl")),
20676
21044
  mode: config.replay ? "replay" : mode,
20677
- ...config.session ? { session: config.session } : {}
21045
+ ...config.session ? { session: config.session } : {},
21046
+ ...extra.tag ? { tag: extra.tag } : {}
20678
21047
  });
20679
21048
  }
20680
21049
  function ledgerFor(ctx) {
20681
- return new SpendLedger(join5(stateDir(ctx.config.repoRoot), "usage.jsonl"));
21050
+ return new SpendLedger(join6(stateDir(ctx.config.repoRoot), "usage.jsonl"));
20682
21051
  }
20683
21052
 
20684
21053
  // ../core/dist/run/budget.js
@@ -20724,26 +21093,27 @@ async function prepare(input) {
20724
21093
  cwd: input.cwd,
20725
21094
  ...input.maxFileBytes ? { maxFileBytes: input.maxFileBytes } : {},
20726
21095
  ...input.allowOutside ? { allowOutside: true } : {},
21096
+ ...input.signal ? { signal: input.signal } : {},
20727
21097
  ...input.filter?.length ? { filter: input.filter, withhold: input.exclude ?? [] } : {}
20728
21098
  });
20729
21099
  const byKind = {};
20730
21100
  let redactedSources = 0;
20731
- const count = (counts2) => {
20732
- const found2 = Object.values(counts2).reduce((a, b) => a + b, 0);
20733
- for (const [kind, n] of Object.entries(counts2))
21101
+ const count = (counts) => {
21102
+ const found2 = Object.values(counts).reduce((a, b) => a + b, 0);
21103
+ for (const [kind, n] of Object.entries(counts))
20734
21104
  byKind[kind] = (byKind[kind] ?? 0) + n;
20735
21105
  return found2;
20736
21106
  };
20737
21107
  const allowed = applyExcludes(read2.documents, input.exclude);
20738
21108
  const chosen = applyFilter(allowed.items, input.filter);
20739
21109
  const documents = chosen.items.map((doc) => {
20740
- const counts2 = {};
21110
+ const counts = {};
20741
21111
  const scrubbed = {
20742
21112
  ...doc,
20743
- ...doc.text !== void 0 ? { text: scrubText(doc.text, counts2) } : {},
20744
- ...doc.data !== void 0 ? { data: scrubState(doc.data, counts2) } : {}
21113
+ ...doc.text !== void 0 ? { text: scrubText(doc.text, counts) } : {},
21114
+ ...doc.data !== void 0 ? { data: scrubState(doc.data, counts) } : {}
20745
21115
  };
20746
- if (count(counts2) > 0)
21116
+ if (count(counts) > 0)
20747
21117
  redactedSources += 1;
20748
21118
  return scrubbed;
20749
21119
  });
@@ -20752,9 +21122,9 @@ async function prepare(input) {
20752
21122
  const tokens = [];
20753
21123
  const oversize = [];
20754
21124
  const items = kept.flatMap((item) => {
20755
- const counts2 = {};
20756
- const state = scrubState(item.state, counts2);
20757
- if (count(counts2) > 0)
21125
+ const counts = {};
21126
+ const state = scrubState(item.state, counts);
21127
+ if (count(counts) > 0)
20758
21128
  redactedSources += 1;
20759
21129
  try {
20760
21130
  tokens.push(assertStateFits(item.id, state, input.questions, input.profile));
@@ -20834,512 +21204,6 @@ async function mapWithConcurrency(items, limit, worker, onProgress) {
20834
21204
  return results;
20835
21205
  }
20836
21206
 
20837
- // ../core/dist/tools/many.js
20838
- var EXCERPT = 120;
20839
- var SAMPLE = 5;
20840
- async function runMany(ctx, rawInput) {
20841
- const input = checkInput("many", rawInput);
20842
- const started = performance.now();
20843
- const req = resolveRequest(ctx, input);
20844
- const prepared = await prepare({
20845
- sources: req.sources,
20846
- split: req.split,
20847
- questions: req.questions,
20848
- profile: req.profile,
20849
- cwd: ctx.config.repoRoot,
20850
- exclude: ctx.config.egress.exclude,
20851
- filter: req.exclude,
20852
- oversize: "skip",
20853
- ...input.allowOutside ? { allowOutside: true } : {}
20854
- });
20855
- const base2 = {
20856
- ...req.spec ? { spec: req.spec.name } : {},
20857
- model: req.profile.id,
20858
- skipped: summariseSkipped(prepared.skipped),
20859
- redactions: { total: prepared.redactions.total, items: prepared.redactions.items },
20860
- projection: prepared.projection
20861
- };
20862
- if (input.dryRun) {
20863
- return {
20864
- ...base2,
20865
- dryRun: true,
20866
- counts: counts(prepared.items.length, 0, 0, 0, 0, 0, prepared.skipped.length),
20867
- kept: [],
20868
- undecided: [],
20869
- failed: [],
20870
- usage: sumUsage([]),
20871
- wallClockMs: Math.round(performance.now() - started),
20872
- sampleIds: prepared.items.slice(0, SAMPLE).map((i) => i.id)
20873
- };
20874
- }
20875
- const decider = deciderFor(ctx, req.profile, input.mode ?? "auto");
20876
- if (decider.mode !== "replay") {
20877
- assertConsent(ctx.config.egress.consent, ctx.config.repoRoot);
20878
- checkBudget(prepared.projection, ctx.config.budget, input.confirm ?? false);
20879
- }
20880
- const concurrency = Math.min(input.concurrency ?? ctx.config.concurrency, ctx.config.concurrency);
20881
- const settled = await mapWithConcurrency(prepared.items, concurrency, (item) => decider.decide({ state: item.state, questions: req.questions, namespace: req.namespace }));
20882
- const done = [];
20883
- const failed = [];
20884
- settled.forEach((outcome, i) => {
20885
- const item = prepared.items[i];
20886
- if ("value" in outcome)
20887
- done.push({ item, result: outcome.value });
20888
- else
20889
- failed.push({ id: item.id, ...describe3(outcome.error) });
20890
- });
20891
- const [first] = failed;
20892
- if (done.length === 0 && first && failed.every((f) => f.code === first.code)) {
20893
- const { error } = settled.find((s) => "error" in s);
20894
- const details = isDecisionsError(error) ? error.details : {};
20895
- throw new DecisionsError(first.code === "error" ? "invalid-request" : first.code, failed.length === 1 ? error.message : `All ${failed.length} items failed: ${error.message}`, { ...details, failed: failed.length });
20896
- }
20897
- const rows = done.map(({ item, result }) => ({
20898
- ...row(item, result.answers),
20899
- flat: result.undecided
20900
- }));
20901
- const projected = project({
20902
- rows,
20903
- keep: req.keep,
20904
- keepAny: req.keepAny,
20905
- ...req.sort ? { sort: req.sort } : {},
20906
- ...input.limit !== void 0 ? { limit: input.limit } : {},
20907
- ...input.fields ? { fields: input.fields } : {},
20908
- undecidedOf: (r) => r.flat
20909
- });
20910
- const strip = ({ flat, ...rest }) => ({
20911
- ...rest,
20912
- ...flat.length ? { undecided: [...flat] } : {}
20913
- });
20914
- return {
20915
- ...base2,
20916
- dryRun: false,
20917
- source: decider.mode === "replay" ? "replay" : "live",
20918
- counts: counts(prepared.items.length, projected.kept.length, projected.keptTotal, projected.undecided.length, projected.dropped, failed.length, prepared.skipped.length),
20919
- kept: projected.kept.map(strip),
20920
- undecided: projected.undecided.map(({ row: row2, questions }) => ({ ...strip(row2), questions })),
20921
- failed,
20922
- usage: sumUsage(done.map((d) => d.result.usage)),
20923
- wallClockMs: Math.round(performance.now() - started)
20924
- };
20925
- }
20926
- function row(item, answers) {
20927
- return {
20928
- id: item.id,
20929
- ...item.path ? { path: item.path } : {},
20930
- ...item.lines ? { lines: item.lines } : {},
20931
- ...traceable(item) ? {} : { excerpt: excerpt(item.state) },
20932
- answers
20933
- };
20934
- }
20935
- function traceable(item) {
20936
- if (item.path === void 0)
20937
- return false;
20938
- const oneLine = item.lines !== void 0 && item.lines.start === item.lines.end;
20939
- return item.id === item.path || !oneLine;
20940
- }
20941
- function excerpt(state) {
20942
- const text = (typeof state === "string" ? state : JSON.stringify(state)).replace(/\s+/g, " ").trim();
20943
- return text.length > EXCERPT ? `${text.slice(0, EXCERPT - 1)}\u2026` : text;
20944
- }
20945
- function counts(items, kept, keptTotal, undecided, dropped, failed, skipped) {
20946
- return { items, kept, keptTotal, undecided, dropped, failed, skipped };
20947
- }
20948
- function summariseSkipped(skipped) {
20949
- const byReason = {};
20950
- for (const s of skipped)
20951
- byReason[s.reason] = (byReason[s.reason] ?? 0) + 1;
20952
- const ordered = [...skipped].sort((a, b) => Number(b.reason === "excluded") - Number(a.reason === "excluded"));
20953
- return { total: skipped.length, byReason, sample: ordered.slice(0, SAMPLE) };
20954
- }
20955
- function describe3(error) {
20956
- return isDecisionsError(error) ? { code: error.code, message: error.message } : { code: "error", message: error.message };
20957
- }
20958
-
20959
- // ../core/dist/tools/ask.js
20960
- async function runAsk(ctx, rawInput) {
20961
- const input = checkInput("ask", rawInput);
20962
- const req = resolveRequest(ctx, input);
20963
- const prepared = await prepare({
20964
- sources: req.sources,
20965
- split: req.split,
20966
- questions: req.questions,
20967
- profile: req.profile,
20968
- cwd: ctx.config.repoRoot,
20969
- exclude: ctx.config.egress.exclude,
20970
- filter: req.exclude,
20971
- ...input.allowOutside ? { allowOutside: true } : {}
20972
- });
20973
- const [item, ...rest] = prepared.items;
20974
- if (!item || rest.length > 0) {
20975
- const held = prepared.skipped.filter((s) => s.reason !== "filtered");
20976
- const left = prepared.skipped.length - held.length;
20977
- const withheld = (held.length ? ` (${held.length} withheld: ${held.map((s) => `${s.path} ${s.reason}`).join(", ")})` : "") + (left ? ` (${left} left out by --exclude)` : "");
20978
- throw new DecisionsError("invalid-request", item ? `ask needs exactly one state, but the source gave ${prepared.items.length}. Use \`decide many\` to fan out.` : `The source gave nothing to ask about${withheld}.`, { items: prepared.items.length });
20979
- }
20980
- const result = await deciderFor(ctx, req.profile, input.mode ?? "auto").decide({
20981
- state: item.state,
20982
- questions: req.questions,
20983
- namespace: req.namespace
20984
- });
20985
- const verdict = req.keep.length > 0 || req.keepAny.length > 0 ? verdictOf(result.answers, result.undecided, req.keep, req.keepAny).verdict : void 0;
20986
- return {
20987
- ...req.spec ? { spec: req.spec.name } : {},
20988
- id: item.id,
20989
- ...item.path ? { path: item.path } : {},
20990
- ...item.lines ? { lines: item.lines } : {},
20991
- source: result.source,
20992
- model: result.model,
20993
- servedBy: result.servedBy,
20994
- answers: result.answers,
20995
- undecided: result.undecided,
20996
- ...verdict ? { verdict } : {},
20997
- usage: result.usage,
20998
- latencyMs: result.latencyMs,
20999
- skipped: summariseSkipped(prepared.skipped),
21000
- redactions: { total: prepared.redactions.total, items: prepared.redactions.items }
21001
- };
21002
- }
21003
-
21004
- // ../core/dist/tools/doctor.js
21005
- import { accessSync, constants, existsSync as existsSync8, statSync as statSync5 } from "node:fs";
21006
- import { delimiter, join as join6 } from "node:path";
21007
- function routeCheck(config) {
21008
- try {
21009
- route("", config);
21010
- if (!config.enabled) {
21011
- return {
21012
- name: "route",
21013
- status: "ok",
21014
- detail: "routing hints are off (route.enabled or SYSTEM1_ROUTE)"
21015
- };
21016
- }
21017
- const names = activeTriggers(config).map((t) => t.name);
21018
- return {
21019
- name: "route",
21020
- status: "ok",
21021
- detail: `routing hints on: ${names.length ? names.join(", ") : "no triggers"}`
21022
- };
21023
- } catch (error) {
21024
- return {
21025
- name: "route",
21026
- status: "warn",
21027
- detail: `routing hints are silent: ${error instanceof Error ? error.message : String(error)}`,
21028
- fix: 'correct route: in the config file (test it with `decide route --text "\u2026"`)'
21029
- };
21030
- }
21031
- }
21032
- function backlogProblem(error) {
21033
- const problems = isDecisionsError(error) ? error.details?.problems : void 0;
21034
- const first = Array.isArray(problems) ? problems[0] : void 0;
21035
- const count = Array.isArray(problems) && problems.length > 1 ? ` (+${problems.length - 1} more)` : "";
21036
- return `the backlog doesn't validate: ${first ?? (error instanceof Error ? error.message : String(error))}${count}`;
21037
- }
21038
- function backlogCheck(repoRoot) {
21039
- const file = backlogPath(repoRoot);
21040
- if (!existsSync8(file))
21041
- return { name: "backlog", status: "ok", detail: "no scout backlog yet" };
21042
- try {
21043
- const { opportunities } = readBacklog(file);
21044
- return {
21045
- name: "backlog",
21046
- status: "ok",
21047
- detail: `${opportunities.length} opportunit${opportunities.length === 1 ? "y" : "ies"} in ${file}`
21048
- };
21049
- } catch (error) {
21050
- return {
21051
- name: "backlog",
21052
- status: "warn",
21053
- detail: backlogProblem(error),
21054
- fix: "`decide opportunities check` lists every problem; correct the file by hand or move it aside (decide never repairs it)"
21055
- };
21056
- }
21057
- }
21058
- var CODEX_RULE = 'prefix_rule(pattern = ["decide"], decision = "allow")';
21059
- async function runDoctor(options) {
21060
- const { env } = options;
21061
- const harness = detectHarness(env) ?? null;
21062
- const checks = [
21063
- {
21064
- name: "cli",
21065
- status: "ok",
21066
- detail: `decide ${VERSION} on node ${process.version}${options.cliPath ? ` (${options.cliPath})` : ""}`
21067
- },
21068
- pathCheck(harness, env)
21069
- ];
21070
- const onPath = which("decide", env.PATH ?? "");
21071
- if (onPath && options.probeVersion) {
21072
- checks.push(await versionCheck(onPath, options.probeVersion, env));
21073
- }
21074
- const report2 = (ctx2, live2) => ({
21075
- healthy: !checks.some((c) => c.status === "fail"),
21076
- live: live2,
21077
- harness,
21078
- session: ctx2?.config.session ?? null,
21079
- version: VERSION,
21080
- node: process.version,
21081
- checks
21082
- });
21083
- let ctx;
21084
- try {
21085
- ctx = createContext(options);
21086
- } catch (error) {
21087
- checks.push({
21088
- name: "config",
21089
- status: "fail",
21090
- detail: error instanceof Error ? error.message : String(error),
21091
- fix: "correct the config file or environment variable named above"
21092
- });
21093
- return report2(void 0, false);
21094
- }
21095
- const { config } = ctx;
21096
- if (config.warnings.length > 0) {
21097
- checks.push({
21098
- name: "config-keys",
21099
- status: "warn",
21100
- detail: `ignored: ${config.warnings.join("; ")}`,
21101
- fix: "correct or remove these keys (docs/configuration.md lists every key)"
21102
- });
21103
- }
21104
- const credentials = join6(userConfigDir(env, options.home), "credentials");
21105
- checks.push(config.apiKey && config.apiKeyQuoted ? {
21106
- name: "key",
21107
- status: "warn",
21108
- detail: `OPENROUTER_API_KEY present (${config.apiKeySource}), but wrapped in quotes; they were removed`,
21109
- fix: config.apiKeySource === "env" ? "set OPENROUTER_API_KEY without the surrounding quotes" : `remove the extra quotes around openrouter_api_key in ${credentials}`,
21110
- advisory: true
21111
- } : config.apiKey ? {
21112
- name: "key",
21113
- status: "ok",
21114
- detail: `OPENROUTER_API_KEY present (${config.apiKeySource})`
21115
- } : {
21116
- name: "key",
21117
- status: "warn",
21118
- detail: "no API key: only replay works",
21119
- // The file this shell would read: XDG_CONFIG_HOME moves it.
21120
- fix: `set OPENROUTER_API_KEY, or write \`openrouter_api_key: <key>\` to ${credentials} (create its directory first; chmod 600)`
21121
- });
21122
- checks.push(config.egress.consent.granted ? { name: "consent", status: "ok", detail: `egress consent granted for ${config.repoRoot}` } : {
21123
- name: "consent",
21124
- status: "warn",
21125
- detail: `no egress consent for ${config.repoRoot}: live calls are refused`,
21126
- fix: "if the user agrees, they grant it themselves (an agent must not): `decide config egress allow` in a terminal in this repo, or through their agent prompt's shell escape (not as a chat message) with --confirm added"
21127
- });
21128
- checks.push(routeCheck(config.route));
21129
- checks.push(backlogCheck(config.repoRoot));
21130
- let reach;
21131
- try {
21132
- reach = await ping({
21133
- endpoint: config.endpoint,
21134
- model: config.model,
21135
- apiKey: config.apiKey,
21136
- replay: config.replay
21137
- }, ctx.fetch ? { fetch: ctx.fetch } : {});
21138
- } catch (error) {
21139
- checks.push({
21140
- name: "config",
21141
- status: "fail",
21142
- detail: `endpoint ${config.endpoint}: ${error instanceof Error ? error.message : String(error)}`,
21143
- fix: "correct SYSTEM1_ENDPOINT or `endpoint` in the config file"
21144
- });
21145
- return report2(ctx, false);
21146
- }
21147
- if (reach.ok) {
21148
- checks.push({
21149
- name: "network",
21150
- status: "ok",
21151
- detail: `${config.model} reachable in ${reach.latencyMs} ms`
21152
- });
21153
- } else {
21154
- const sandboxed = env.CODEX_SANDBOX_NETWORK_DISABLED === "1";
21155
- checks.push({
21156
- name: "network",
21157
- // Replay needs no network, so an unreachable endpoint only warns there.
21158
- status: config.replay ? "warn" : "fail",
21159
- detail: `${reach.probe}: ${reach.error ?? "unreachable"}${sandboxed ? " (Codex sandbox has network disabled)" : ""}`,
21160
- fix: networkFix(reach.httpStatus, harness, sandboxed, env)
21161
- });
21162
- }
21163
- const live = !config.replay && config.apiKey !== void 0 && config.egress.consent.granted && reach.ok === true;
21164
- return report2(ctx, live);
21165
- }
21166
- function pathCheck(harness, env) {
21167
- const onPath = which("decide", env.PATH ?? "");
21168
- if (onPath)
21169
- return { name: "path", status: "ok", detail: `decide on PATH: ${onPath}` };
21170
- if (harness === "claude") {
21171
- return {
21172
- name: "path",
21173
- status: "warn",
21174
- detail: "decide is not on PATH, so the System 1 plugin's bin/ is not loaded",
21175
- fix: `npm i -g ${CLI_PACKAGE}@${VERSION} (also puts decide in your own terminal), or install the System 1 plugin from its marketplace`
21176
- };
21177
- }
21178
- return {
21179
- name: "path",
21180
- status: "warn",
21181
- detail: "decide is not on PATH. Codex and Pi do not add plugin bin/ to PATH",
21182
- fix: `npm i -g ${CLI_PACKAGE}@${VERSION}`
21183
- };
21184
- }
21185
- async function versionCheck(path, probe, env) {
21186
- let raw;
21187
- try {
21188
- raw = await probe(path);
21189
- } catch {
21190
- }
21191
- const found2 = raw?.trim() || void 0;
21192
- if (raw !== void 0 && found2 === void 0 && env.CODEX_SANDBOX_NETWORK_DISABLED === "1") {
21193
- return {
21194
- name: "path-version",
21195
- status: "ok",
21196
- detail: "not checked: the Codex sandbox hides a child process's output"
21197
- };
21198
- }
21199
- if (found2 === void 0) {
21200
- return {
21201
- name: "path-version",
21202
- status: "warn",
21203
- detail: `could not read the version of ${path}`,
21204
- fix: `run \`${path} version\` to see why; reinstall it if it fails`
21205
- };
21206
- }
21207
- if (found2 === VERSION) {
21208
- return {
21209
- name: "path-version",
21210
- status: "ok",
21211
- detail: `decide on PATH is ${found2}, same as this one`
21212
- };
21213
- }
21214
- return {
21215
- name: "path-version",
21216
- status: "warn",
21217
- detail: `decide on PATH is ${found2}, but this one is ${VERSION}: agents will run the one on PATH`,
21218
- fix: `npm i -g ${CLI_PACKAGE}@${VERSION}, or remove the other decide (${path})`
21219
- };
21220
- }
21221
- function networkFix(httpStatus, harness, sandboxed, env) {
21222
- if (httpStatus === 404)
21223
- return "the endpoint does not know this model: check SYSTEM1_MODEL / `model` and SYSTEM1_ENDPOINT / `endpoint`";
21224
- if (httpStatus !== void 0 && httpStatus >= 500)
21225
- return `the provider answered HTTP ${httpStatus}: retry later`;
21226
- if (httpStatus !== void 0)
21227
- return `the endpoint answered HTTP ${httpStatus}: check SYSTEM1_ENDPOINT / \`endpoint\``;
21228
- if (sandboxed) {
21229
- return `add \`${CODEX_RULE}\` to ${env.CODEX_HOME?.trim() || "~/.codex"}/rules/system1.rules, then restart Codex (it covers commands that start with decide; a pipe into decide stays offline)`;
21230
- }
21231
- if (harness === "claude")
21232
- return "allow outbound access to openrouter.ai in Claude Code's sandbox settings";
21233
- return "check that this machine can reach openrouter.ai (proxy, firewall, DNS)";
21234
- }
21235
- function which(name, path) {
21236
- for (const dir of path.split(delimiter)) {
21237
- if (!dir)
21238
- continue;
21239
- const candidate = join6(dir, name);
21240
- try {
21241
- accessSync(candidate, constants.X_OK);
21242
- if (statSync5(candidate).isFile())
21243
- return candidate;
21244
- } catch {
21245
- }
21246
- }
21247
- return void 0;
21248
- }
21249
-
21250
- // ../core/dist/tools/opportunities.js
21251
- import { existsSync as existsSync9 } from "node:fs";
21252
- function runOpportunitiesAdd(ctx, rawInput, now = () => /* @__PURE__ */ new Date()) {
21253
- const input = checkInput("opportunities-add", rawInput);
21254
- const file = backlogPath(ctx.config.repoRoot);
21255
- const merged2 = withBacklogLock(file, () => {
21256
- const result = mergeCandidates(readBacklog(file), input.candidates, now().toISOString());
21257
- writeBacklog(file, result.backlog);
21258
- return result;
21259
- });
21260
- return {
21261
- file,
21262
- added: merged2.added,
21263
- updated: merged2.updated,
21264
- staled: merged2.staled,
21265
- total: merged2.backlog.opportunities.length
21266
- };
21267
- }
21268
- function runOpportunitiesList(ctx, rawInput = {}) {
21269
- const input = checkInput("opportunities-list", rawInput);
21270
- const filters = (input.keep ?? []).map(parseRecordFilter);
21271
- const sort = parseRecordSort(input.sort ?? "projected:desc");
21272
- assertFields(input.fields);
21273
- const file = backlogPath(ctx.config.repoRoot);
21274
- const { opportunities } = readBacklog(file);
21275
- const matched = opportunities.filter((o) => filters.every((f) => recordMatches(o, f))).sort((a, b) => compare(readField(a, sort.field), readField(b, sort.field), sort.direction));
21276
- const shown = input.limit === void 0 ? matched : matched.slice(0, input.limit);
21277
- return {
21278
- file,
21279
- total: opportunities.length,
21280
- matched: matched.length,
21281
- basis: "projected",
21282
- opportunities: shown.map((o) => pick2(o, input.fields))
21283
- };
21284
- }
21285
- function runOpportunitiesCheck(ctx, rawInput = {}) {
21286
- checkInput("opportunities-check", rawInput);
21287
- const file = backlogPath(ctx.config.repoRoot);
21288
- const backlog = readBacklog(file);
21289
- const byStatus = {};
21290
- for (const o of backlog.opportunities)
21291
- byStatus[o.status] = (byStatus[o.status] ?? 0) + 1;
21292
- return { file, exists: existsSync9(file), entries: backlog.opportunities.length, byStatus };
21293
- }
21294
- function compare(a, b, direction) {
21295
- if (a === void 0 || b === void 0)
21296
- return a === b ? 0 : a === void 0 ? 1 : -1;
21297
- const order = typeof a === "number" && typeof b === "number" ? a - b : String(a).localeCompare(String(b));
21298
- return direction === "asc" ? order : -order;
21299
- }
21300
- function pick2(o, fields) {
21301
- if (!fields)
21302
- return o;
21303
- const out = { id: o.id };
21304
- for (const f of fields)
21305
- if (Object.hasOwn(o, f))
21306
- out[f] = o[f];
21307
- return out;
21308
- }
21309
- function assertFields(fields = []) {
21310
- const unknown = fields.filter((f) => !ENTRY_FIELDS.includes(f));
21311
- if (unknown.length) {
21312
- throw new DecisionsError("invalid-request", `--fields: no field ${unknown.join(", ")}. Fields: ${ENTRY_FIELDS.join(", ")}`);
21313
- }
21314
- }
21315
-
21316
- // ../core/dist/tools/route.js
21317
- function runRoute(options, rawInput) {
21318
- const problems = checkRouteInput(rawInput);
21319
- if (problems.length > 0) {
21320
- throw new DecisionsError("invalid-request", `Invalid route input:
21321
- ${problems.join("\n ")}`, {
21322
- problems
21323
- });
21324
- }
21325
- const input = rawInput;
21326
- const config = loadConfig(options);
21327
- const { user, repo } = config.layers;
21328
- return {
21329
- ...route(input.prompt, config.route),
21330
- files: { ...user ? { user } : {}, ...repo ? { repo } : {} }
21331
- };
21332
- }
21333
- function checkRouteInput(input) {
21334
- if (typeof input !== "object" || input === null || Array.isArray(input))
21335
- return ["/ must be an object"];
21336
- const extra = Object.keys(input).filter((k) => k !== "prompt");
21337
- return [
21338
- ...typeof input.prompt === "string" ? [] : ["/prompt must be a string"],
21339
- ...extra.map((k) => `/${k} is not allowed`)
21340
- ];
21341
- }
21342
-
21343
21207
  // ../core/dist/spec/lint.js
21344
21208
  var LINT_SEVERITY = {
21345
21209
  "no-way-out": "warning",
@@ -21376,6 +21240,9 @@ function lintQuestions(questions) {
21376
21240
  }
21377
21241
  return out;
21378
21242
  }
21243
+ function lintStatement(text, question) {
21244
+ return unsupported(text, question);
21245
+ }
21379
21246
  function lintSpec(spec) {
21380
21247
  const out = lintQuestions(spec.questions);
21381
21248
  const thresholds = spec.policy?.thresholds ?? {};
@@ -21461,251 +21328,66 @@ function dedupe(findings) {
21461
21328
  });
21462
21329
  }
21463
21330
 
21464
- // ../core/dist/tools/spec-check.js
21465
- async function runSpecCheck(ctx, rawInput) {
21466
- const input = checkInput("spec-check", rawInput);
21467
- const spec = loadSpec(input.spec, ctx.specDirs, ctx.cwd);
21468
- const lint = lintSpec(spec);
21469
- const examples = spec.examples ?? [];
21470
- if (examples.length === 0) {
21471
- throw new DecisionsError("invalid-request", `Spec "${spec.name}" has no examples to check. Add some under examples: (id, state or file, expect).`);
21472
- }
21473
- assertExamples(spec);
21474
- const profile = resolveProfile(input.model ?? ctx.config.model, allProfiles(ctx.config));
21475
- const root = ctx.config.repoRoot;
21476
- const prepared = [];
21477
- for (const example of examples) {
21478
- const expect = example.expect ? parseExpect(example.expect, spec.questions, example.id) : {};
21479
- const refused = example.file !== void 0 ? exampleFileProblem(example.file, root) : void 0;
21480
- if (refused) {
21481
- prepared.push({ id: example.id, expect, reason: `${example.file} ${refused}` });
21482
- continue;
21483
- }
21484
- const source = example.file !== void 0 ? { kind: "file", ...parseFileRef(example.file) } : {
21485
- kind: "text",
21486
- // A structured state is sent as its JSON text.
21487
- text: typeof example.state === "string" ? example.state : JSON.stringify(example.state),
21488
- id: example.id
21489
- };
21490
- try {
21491
- const prep = await prepare({
21492
- sources: [source],
21493
- split: parseSplit("file"),
21494
- questions: spec.questions,
21495
- profile,
21496
- cwd: root,
21497
- exclude: ctx.config.egress.exclude
21498
- });
21499
- const [item, ...rest] = prep.items;
21500
- const why = prep.skipped[0];
21501
- prepared.push(item && rest.length === 0 ? { id: example.id, expect, prep } : {
21502
- id: example.id,
21503
- expect,
21504
- reason: why ? `${why.path} ${why.reason}` : `gave ${prep.items.length} states, not 1`
21505
- });
21506
- } catch (error) {
21507
- if (!isDecisionsError(error) || !["state-too-large", "source-error"].includes(error.code)) {
21508
- throw error;
21509
- }
21510
- prepared.push({ id: example.id, expect, reason: error.message.split("\n")[0] });
21511
- }
21512
- }
21513
- const decider = deciderFor(ctx, profile, input.mode ?? "replay");
21514
- if (decider.mode !== "replay") {
21515
- assertConsent(ctx.config.egress.consent, root);
21516
- const parts = prepared.flatMap((p) => p.prep ? [p.prep.projection] : []);
21517
- checkBudget(combineProjections(profile, parts), ctx.config.budget, input.confirm ?? false);
21518
- }
21519
- const results = [];
21520
- const usages = [];
21521
- let misses = 0;
21522
- for (const { id, expect, prep, reason } of prepared) {
21523
- const item = prep?.items[0];
21524
- if (!prep || !item) {
21525
- results.push({ id, status: "withheld", reason: reason ?? "not sent" });
21526
- continue;
21527
- }
21528
- try {
21529
- const r = await decider.decide({
21530
- state: item.state,
21531
- questions: spec.questions,
21532
- namespace: spec.name
21533
- });
21534
- usages.push(r.usage);
21535
- results.push(judge(id, r.answers, r.undecided, expect));
21536
- } catch (error) {
21537
- if (isDecisionsError(error) && error.code === "replay-miss") {
21538
- misses += 1;
21539
- continue;
21540
- }
21541
- throw error;
21542
- }
21543
- }
21544
- if (misses > 0) {
21545
- throw new DecisionsError("replay-miss", `${misses} of ${examples.length} examples of "${spec.name}" have no recorded answer. Record them with \`decide spec check ${spec.name} --live\` (needs a key and repo consent).`, { misses });
21546
- }
21547
- const counts2 = {
21548
- examples: results.length,
21549
- pass: 0,
21550
- fail: 0,
21551
- undecided: 0,
21552
- captured: 0,
21553
- withheld: 0
21554
- };
21555
- for (const r of results)
21556
- counts2[r.status] += 1;
21557
- return {
21558
- spec: spec.name,
21559
- file: spec.file,
21560
- model: profile.id,
21561
- source: decider.mode === "replay" ? "replay" : "live",
21562
- passed: counts2.fail === 0 && counts2.undecided === 0 && counts2.withheld === 0,
21563
- counts: counts2,
21564
- examples: results,
21565
- usage: sumUsage(usages),
21566
- skipped: summariseSkipped(prepared.flatMap((p) => p.prep?.skipped ?? [])),
21567
- lint
21568
- };
21569
- }
21570
- function judge(id, answers, flat, expect) {
21571
- const entries = Object.entries(expect);
21572
- if (entries.length === 0) {
21573
- return { id, status: "captured", answers, ...flat.length ? { undecided: flat } : {} };
21574
- }
21575
- const failures = [];
21576
- const undecided = [];
21577
- for (const [question, expectation] of entries) {
21578
- const answer = answers[question];
21579
- const isFlat = flat.includes(question);
21580
- if (expectation.kind === "undecided") {
21581
- if (!isFlat)
21582
- failures.push({ question, expected: "undecided" });
21583
- } else if (isFlat) {
21584
- undecided.push(question);
21585
- } else if (!answer || !meets(question, answer, expectation)) {
21586
- failures.push({ question, expected: describeExpectation(expectation) });
21587
- }
21588
- }
21589
- const status = failures.length ? "fail" : undecided.length ? "undecided" : "pass";
21590
- return {
21591
- id,
21592
- status,
21593
- answers,
21594
- ...failures.length ? { failures } : {},
21595
- ...undecided.length ? { undecided } : {}
21596
- };
21597
- }
21598
-
21599
- // ../core/dist/tools/spec-lint.js
21600
- import { basename as basename3, extname as extname2 } from "node:path";
21601
- function runSpecLint(ctx, rawInput = {}) {
21602
- const input = checkInput("spec-lint", rawInput);
21603
- const specs = input.spec ? [named(ctx, input.spec)] : listSpecs(ctx.specDirs).filter((s) => !s.shadowed).map((s) => s.error ? { name: s.name, file: s.file, origin: s.origin, findings: [], invalid: s.error } : (
21604
- // Loaded by path, so keep the layer it was listed from.
21605
- { ...entry(loadSpec(s.file, ctx.specDirs, ctx.cwd)), origin: s.origin }
21606
- ));
21607
- const all = specs.flatMap((s) => s.findings);
21608
- const counts2 = {
21609
- specs: specs.length,
21610
- errors: all.filter((f) => f.severity === "error").length,
21611
- warnings: all.filter((f) => f.severity === "warning").length,
21612
- invalid: specs.filter((s) => s.invalid).length
21613
- };
21614
- return { specs, counts: counts2, passed: counts2.errors === 0 && counts2.invalid === 0 };
21615
- }
21616
- function named(ctx, ref) {
21617
- try {
21618
- return entry(loadSpec(ref, ctx.specDirs, ctx.cwd));
21619
- } catch (error) {
21620
- const file = isDecisionsError(error) ? error.details?.file : void 0;
21621
- if (typeof file !== "string")
21622
- throw error;
21623
- return {
21624
- name: basename3(file, extname2(file)),
21625
- file,
21626
- origin: "path",
21627
- findings: [],
21628
- invalid: error.message
21629
- };
21630
- }
21631
- }
21632
- function entry(spec) {
21633
- return { name: spec.name, file: spec.file, origin: spec.origin, findings: lintSpec(spec) };
21634
- }
21635
-
21636
- // ../core/dist/tools/usage.js
21637
- function runUsage(ctx, rawInput = {}) {
21638
- const input = checkInput("usage", rawInput);
21639
- const since = input.since ? new Date(input.since) : void 0;
21640
- if (since && Number.isNaN(since.getTime())) {
21641
- throw new DecisionsError("invalid-request", `--since "${input.since}" is not a date`);
21642
- }
21643
- const session = input.session === "current" ? currentSession(ctx) : input.session;
21644
- const ledger = ledgerFor(ctx);
21645
- return {
21646
- basis: "measured",
21647
- ...ledger.summary({
21648
- ...session ? { session } : {},
21649
- ...since ? { since } : {}
21650
- }),
21651
- ledger: ledger.file,
21652
- ...session ? { session } : {},
21653
- ...input.since ? { since: input.since } : {}
21654
- };
21655
- }
21656
- function currentSession(ctx) {
21657
- if (ctx.config.session)
21658
- return ctx.config.session;
21659
- throw new DecisionsError("invalid-request", "no current session: not running under Claude Code, Codex or Pi, and SYSTEM1_SESSION is not set");
21660
- }
21661
-
21662
- // ../core/dist/wiring.js
21663
- var DEFAULT_MODEL = DEFAULT_MODEL_ID;
21664
- function resolveConnection(env = process.env) {
21665
- return {
21666
- endpoint: nonEmpty3(env.SYSTEM1_ENDPOINT) ?? DEFAULT_ENDPOINT,
21667
- model: nonEmpty3(env.SYSTEM1_MODEL) ?? DEFAULT_MODEL,
21668
- apiKey: envKey(env),
21669
- replay: /^(1|true|yes)$/i.test(env.SYSTEM1_REPLAY ?? "")
21670
- };
21671
- }
21672
- function envKey(env) {
21673
- try {
21674
- return unquoteKey(env.OPENROUTER_API_KEY)?.value;
21675
- } catch {
21676
- return void 0;
21677
- }
21678
- }
21679
- function nonEmpty3(value) {
21680
- return value === void 0 || value.trim() === "" ? void 0 : value.trim();
21681
- }
21682
-
21683
21331
  export {
21332
+ PACKS,
21333
+ PACK_NAMES,
21334
+ isPackName,
21335
+ DEFAULT_MODEL_ID,
21336
+ resolveProfile,
21684
21337
  activeTriggers,
21338
+ route,
21339
+ DEFAULT_ENDPOINT,
21340
+ detectHarness,
21341
+ userConfigDir,
21685
21342
  repoConfigPath,
21686
21343
  loadConfig,
21687
21344
  allProfiles,
21345
+ unquoteKey,
21688
21346
  setConsent,
21347
+ setGuardEnabled,
21348
+ assertConsent,
21349
+ assertStateFits,
21689
21350
  DEFAULT_EXCLUDES,
21690
- probeUrl,
21691
- ping,
21351
+ CRITERIA_SNAPSHOT_MAX_CHARS,
21352
+ MAX_CRITERIA_FILES,
21353
+ guardStatePath,
21354
+ readGuardState,
21355
+ updateGuardState,
21356
+ verdictOf,
21357
+ project,
21358
+ backlogPath,
21359
+ readBacklog,
21360
+ withBacklogLock,
21361
+ writeBacklog,
21362
+ mergeCandidates,
21363
+ ENTRY_FIELDS,
21364
+ parseRecordFilter,
21365
+ parseRecordSort,
21366
+ readField,
21367
+ recordMatches,
21368
+ project2,
21369
+ combineProjections,
21370
+ checkBudget,
21692
21371
  parseFileRef,
21372
+ parseSplit,
21373
+ prepare,
21374
+ mapWithConcurrency,
21375
+ sumUsage,
21376
+ parseExpect,
21377
+ meets,
21378
+ describeExpectation,
21379
+ lintStatement,
21380
+ lintSpec,
21693
21381
  exampleProblems,
21382
+ exampleFileProblem,
21694
21383
  assertExamples,
21695
21384
  parseQuestionSet,
21696
21385
  loadSpec,
21697
21386
  listSpecs,
21698
21387
  TOOL_SCHEMAS,
21699
21388
  createContext,
21700
- runMany,
21701
- runAsk,
21702
- runDoctor,
21703
- runOpportunitiesAdd,
21704
- runOpportunitiesList,
21705
- runOpportunitiesCheck,
21706
- runRoute,
21707
- runSpecCheck,
21708
- runSpecLint,
21709
- runUsage,
21710
- resolveConnection
21389
+ checkInput,
21390
+ resolveRequest,
21391
+ deciderFor,
21392
+ ledgerFor
21711
21393
  };