@garygentry/system1 0.4.0 → 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-WZIVOY5Q.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 {
@@ -9598,9 +9740,11 @@ function loadConfig(options = {}) {
9598
9740
  repoRoot,
9599
9741
  apiKey: key?.value,
9600
9742
  apiKeySource: key?.source,
9743
+ apiKeyQuoted: key?.quoted ?? false,
9601
9744
  replay: /^(1|true|yes)$/i.test(env.SYSTEM1_REPLAY ?? ""),
9602
9745
  session: session?.id,
9603
9746
  sessionOrigin: session?.origin,
9747
+ guard,
9604
9748
  layers: {
9605
9749
  ...user ? { user: userFile } : {},
9606
9750
  ...repo ? { repo: repoFile } : {},
@@ -9634,11 +9778,21 @@ function readLayer(file) {
9634
9778
  return parsed;
9635
9779
  }
9636
9780
  var KNOWN = {
9637
- "": ["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
+ ],
9638
9792
  budget: ["maxCalls", "maxUsd"],
9639
9793
  egress: ["consent", "exclude"]
9640
9794
  };
9641
- function unknownKeys(layer, file, which2) {
9795
+ function unknownKeys(layer, file, which) {
9642
9796
  if (!layer)
9643
9797
  return [];
9644
9798
  const warnings = Object.keys(layer).filter((k) => !KNOWN[""]?.includes(k)).map((key) => `${file}: unknown key ${key}`);
@@ -9646,7 +9800,7 @@ function unknownKeys(layer, file, which2) {
9646
9800
  const value = layer[section2];
9647
9801
  if (value === void 0 || value === null)
9648
9802
  continue;
9649
- if (!isMapping(value)) {
9803
+ if (!isMapping2(value)) {
9650
9804
  warnings.push(`${file}: ${section2} must be a mapping (ignored)`);
9651
9805
  continue;
9652
9806
  }
@@ -9656,7 +9810,7 @@ function unknownKeys(layer, file, which2) {
9656
9810
  }
9657
9811
  }
9658
9812
  const egress = layer.egress;
9659
- if (which2 === "user" && isMapping(egress) && egress.consent !== void 0) {
9813
+ if (which === "user" && isMapping2(egress) && egress.consent !== void 0) {
9660
9814
  warnings.push(`${file}: egress.consent is read only from the repo file (ignored)`);
9661
9815
  }
9662
9816
  return warnings;
@@ -9684,7 +9838,7 @@ function pick(path, rule, layers2, warnings) {
9684
9838
  const [head, key] = path.split(".");
9685
9839
  let value = layer?.[head];
9686
9840
  if (key !== void 0)
9687
- value = isMapping(value) ? value[key] : void 0;
9841
+ value = isMapping2(value) ? value[key] : void 0;
9688
9842
  if (value === void 0 || value === null)
9689
9843
  continue;
9690
9844
  if (!rule.ok(value))
@@ -9694,7 +9848,7 @@ function pick(path, rule, layers2, warnings) {
9694
9848
  }
9695
9849
  return found2;
9696
9850
  }
9697
- function isMapping(value) {
9851
+ function isMapping2(value) {
9698
9852
  return typeof value === "object" && value !== null && !Array.isArray(value);
9699
9853
  }
9700
9854
  function readConsent(repo, file) {
@@ -9841,18 +9995,36 @@ function triggerList(value, file) {
9841
9995
  });
9842
9996
  }
9843
9997
  function resolveApiKey(env, userDir) {
9844
- const fromEnv = nonEmpty2(env.OPENROUTER_API_KEY);
9998
+ const fromEnv = unquoteKey(env.OPENROUTER_API_KEY);
9845
9999
  if (fromEnv)
9846
- return { value: fromEnv, source: "env" };
10000
+ return { ...fromEnv, source: "env" };
9847
10001
  const file = join(userDir, "credentials");
9848
10002
  if (!existsSync(file))
9849
10003
  return void 0;
9850
10004
  if (process.platform !== "win32" && (statSync(file).mode & 63) !== 0) {
9851
10005
  throw new DecisionsError("config-error", `${file} is readable by other users. Run \`chmod 600 ${file}\`.`, { file });
9852
10006
  }
9853
- const parsed = readLayer(file);
9854
- const value = nonEmpty2(typeof parsed?.openrouter_api_key === "string" ? parsed.openrouter_api_key : void 0);
9855
- return value ? { value, source: "credentials", file } : void 0;
10007
+ let parsed;
10008
+ try {
10009
+ parsed = readLayer(file);
10010
+ } catch {
10011
+ throw new DecisionsError("config-error", `${file} is not a valid YAML mapping; it should hold one line, \`openrouter_api_key: <key>\``, { file });
10012
+ }
10013
+ const key = unquoteKey(typeof parsed?.openrouter_api_key === "string" ? parsed.openrouter_api_key : void 0, `openrouter_api_key in ${file}`);
10014
+ return key ? { ...key, source: "credentials", file } : void 0;
10015
+ }
10016
+ function unquoteKey(raw, source = "OPENROUTER_API_KEY") {
10017
+ const value = nonEmpty2(raw);
10018
+ if (!value)
10019
+ return void 0;
10020
+ const quoted = /^(["']).*\1$/s.test(value);
10021
+ const key = quoted ? nonEmpty2(value.slice(1, -1)) : value;
10022
+ if (!key)
10023
+ return void 0;
10024
+ if (/[^\x21-\x7e]/.test(key)) {
10025
+ throw new DecisionsError("config-error", `${source} contains a space, line break or other character an API key can't have; set it again`);
10026
+ }
10027
+ return { value: key, quoted };
9856
10028
  }
9857
10029
  function nonEmpty2(value) {
9858
10030
  return value === void 0 || value.trim() === "" ? void 0 : value.trim();
@@ -9866,15 +10038,56 @@ var HEADER = `# decisions \u2014 per-repo configuration. See https://github.com/
9866
10038
  # egress.consent records that this repo agreed to send content to the decision
9867
10039
  # model's provider (OpenRouter). Path excludes and secret scrubbing always apply.
9868
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
+ }
9869
10067
  function setConsent(repoRoot, granted, by, now = /* @__PURE__ */ new Date()) {
9870
10068
  const file = repoConfigPath(repoRoot);
9871
- const doc = existsSync2(file) ? (0, import_yaml2.parseDocument)(readFileSync2(file, "utf8")) : (0, import_yaml2.parseDocument)(HEADER);
10069
+ const doc = editable(file, ["egress"]);
9872
10070
  const consent = { granted, at: now.toISOString(), ...by ? { by } : {} };
9873
10071
  doc.setIn(["egress", "consent"], consent);
9874
10072
  mkdirSync(dirname2(file), { recursive: true });
9875
10073
  writeFileSync(file, doc.toString());
9876
10074
  return consent;
9877
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
+ }
9878
10091
  function assertConsent(consent, repoRoot) {
9879
10092
  if (consent.granted)
9880
10093
  return;
@@ -9931,59 +10144,9 @@ function isExcluded(path, extra = []) {
9931
10144
  return [...DEFAULT_EXCLUDES, ...extra].find((p) => (0, import_picomatch.default)(p, { dot: true })(path));
9932
10145
  }
9933
10146
 
9934
- // ../core/dist/ping.js
9935
- function probeUrl(config) {
9936
- const origin = new URL(config.endpoint).origin;
9937
- return `${origin}/api/v1/models/${config.model}/endpoints`;
9938
- }
9939
- async function ping(config, options = {}) {
9940
- const doFetch = options.fetch ?? fetch;
9941
- const probe = probeUrl(config);
9942
- const base2 = {
9943
- model: config.model,
9944
- endpoint: config.endpoint,
9945
- probe,
9946
- keyPresent: config.apiKey !== void 0
9947
- };
9948
- const started = performance.now();
9949
- try {
9950
- const response = await doFetch(probe, {
9951
- signal: AbortSignal.timeout(options.timeoutMs ?? 1e4)
9952
- });
9953
- const latencyMs = Math.round(performance.now() - started);
9954
- if (!response.ok) {
9955
- return {
9956
- ...base2,
9957
- ok: false,
9958
- latencyMs,
9959
- httpStatus: response.status,
9960
- error: `HTTP ${response.status}`
9961
- };
9962
- }
9963
- const body = await response.json();
9964
- const contextLength = body.data?.endpoints?.[0]?.context_length;
9965
- return {
9966
- ...base2,
9967
- ok: true,
9968
- latencyMs,
9969
- httpStatus: response.status,
9970
- ...typeof contextLength === "number" ? { contextLength } : {}
9971
- };
9972
- } catch (error) {
9973
- return { ...base2, ok: false, error: describe2(error) };
9974
- }
9975
- }
9976
- function describe2(error) {
9977
- if (error instanceof Error) {
9978
- const cause = error.cause;
9979
- return cause instanceof Error ? `${error.message}: ${cause.message}` : error.message;
9980
- }
9981
- return String(error);
9982
- }
9983
-
9984
- // ../core/dist/spec/spec.js
9985
- import { existsSync as existsSync4, readdirSync as readdirSync2, readFileSync as readFileSync4 } from "node:fs";
9986
- 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";
9987
10150
 
9988
10151
  // ../../node_modules/.pnpm/typebox@1.3.34/node_modules/typebox/build/system/memory/memory.mjs
9989
10152
  var memory_exports = {};
@@ -13113,8 +13276,8 @@ function Compare(left, right) {
13113
13276
  // ../../node_modules/.pnpm/typebox@1.3.34/node_modules/typebox/build/type/engine/evaluate/broaden.mjs
13114
13277
  function BroadenFilter(type, types, result = [], all = types) {
13115
13278
  return guard_exports.ShiftLeft(types, (left, right) => {
13116
- const compare2 = Compare(type, left);
13117
- 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);
13118
13281
  }, () => [...result, type]);
13119
13282
  }
13120
13283
  function BroadenType(type, types, result) {
@@ -14997,15 +15160,15 @@ function NextPendingResource(stack, schema) {
14997
15160
  return IsId(schema) ? false : stack.pendingResource;
14998
15161
  }
14999
15162
  function NextLexicalBase(stack, schema) {
15000
- const entry2 = ResourceEntry(stack, schema);
15001
- if (entry2)
15002
- return entry2.base;
15163
+ const entry = ResourceEntry(stack, schema);
15164
+ if (entry)
15165
+ return entry.base;
15003
15166
  return IsId(schema) ? NextUri(schema.$id, stack.lexicalBase).href : stack.lexicalBase;
15004
15167
  }
15005
15168
  function NextResourceBase(stack, schema) {
15006
- const entry2 = ResourceEntry(stack, schema);
15007
- if (entry2)
15008
- return entry2.base;
15169
+ const entry = ResourceEntry(stack, schema);
15170
+ if (entry)
15171
+ return entry.base;
15009
15172
  return IsId(schema) && stack.pendingResource ? NextUri(schema.$id, stack.resourceBase).href : stack.resourceBase;
15010
15173
  }
15011
15174
  function NextUseResourceBaseForReference(stack, schema) {
@@ -15017,9 +15180,9 @@ function NextReferenceBase(stack, schema) {
15017
15180
  return isRetrieved || useResourceBaseForReference ? NextResourceBase(stack, schema) : NextLexicalBase(stack, schema);
15018
15181
  }
15019
15182
  function NextLexicalSchema(stack, schema) {
15020
- const entry2 = ResourceEntry(stack, schema);
15021
- if (entry2)
15022
- return entry2.root;
15183
+ const entry = ResourceEntry(stack, schema);
15184
+ if (entry)
15185
+ return entry.root;
15023
15186
  return IsId(schema) ? schema : stack.lexicalSchema;
15024
15187
  }
15025
15188
  function HasStackKeywords(stack, schema) {
@@ -18185,7 +18348,234 @@ __export(value_exports, {
18185
18348
  Repair: () => Repair
18186
18349
  });
18187
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
+
18188
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";
18189
18579
  var import_yaml3 = __toESM(require_dist(), 1);
18190
18580
 
18191
18581
  // ../core/dist/project/project.js
@@ -18275,12 +18665,12 @@ function project(input) {
18275
18665
  const kept = [];
18276
18666
  const undecided = [];
18277
18667
  let dropped = 0;
18278
- for (const row2 of input.rows) {
18279
- 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);
18280
18670
  if (v.verdict === "undecided")
18281
- undecided.push({ row: row2, questions: v.questions });
18671
+ undecided.push({ row, questions: v.questions });
18282
18672
  else if (v.verdict === "kept")
18283
- kept.push(row2);
18673
+ kept.push(row);
18284
18674
  else
18285
18675
  dropped += 1;
18286
18676
  }
@@ -18291,13 +18681,13 @@ function project(input) {
18291
18681
  }
18292
18682
  const keptTotal = kept.length;
18293
18683
  const limited = input.limit !== void 0 ? kept.slice(0, input.limit) : kept;
18294
- 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;
18295
18685
  return { kept: withFields, keptTotal, undecided, dropped };
18296
18686
  }
18297
- function pickAnswers(row2, fields) {
18687
+ function pickAnswers(row, fields) {
18298
18688
  return {
18299
- ...row2,
18300
- 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)))
18301
18691
  };
18302
18692
  }
18303
18693
  function read(answer, field) {
@@ -18332,18 +18722,18 @@ function num(value) {
18332
18722
  }
18333
18723
 
18334
18724
  // ../core/dist/sources/read.js
18335
- import { execFileSync } from "node:child_process";
18336
- 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";
18337
18727
  import { isAbsolute as isAbsolute2, relative as relative2, resolve as resolve4 } from "node:path";
18338
18728
 
18339
18729
  // ../../node_modules/.pnpm/tinyglobby@0.2.17/node_modules/tinyglobby/dist/index.mjs
18340
- import { readdir, readdirSync, realpath, realpathSync, stat, statSync as statSync2 } from "fs";
18730
+ import { readdir, readdirSync, realpath, realpathSync, stat, statSync as statSync4 } from "fs";
18341
18731
  import { isAbsolute, posix, resolve as resolve3 } from "path";
18342
18732
  import { fileURLToPath } from "url";
18343
18733
 
18344
18734
  // ../../node_modules/.pnpm/fdir@6.5.0_picomatch@4.0.7/node_modules/fdir/dist/index.mjs
18345
18735
  import { createRequire } from "module";
18346
- 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";
18347
18737
  import * as nativeFs from "fs";
18348
18738
  var __require2 = /* @__PURE__ */ createRequire(import.meta.url);
18349
18739
  function cleanPath(path) {
@@ -18414,14 +18804,14 @@ function build$6(root, options) {
18414
18804
  if (relativePaths) return filters && filters.length ? pushDirectoryFilterWithRelativePath(root) : pushDirectoryWithRelativePath(root);
18415
18805
  return filters && filters.length ? pushDirectoryFilter : pushDirectory;
18416
18806
  }
18417
- var pushFileFilterAndCount = (filename, _paths, counts2, filters) => {
18418
- 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++;
18419
18809
  };
18420
18810
  var pushFileFilter = (filename, paths, _counts, filters) => {
18421
18811
  if (filters.every((filter) => filter(filename, false))) paths.push(filename);
18422
18812
  };
18423
- var pushFileCount = (_filename, _paths, counts2, _filters) => {
18424
- counts2.files++;
18813
+ var pushFileCount = (_filename, _paths, counts, _filters) => {
18814
+ counts.files++;
18425
18815
  };
18426
18816
  var pushFile = (filename, paths) => {
18427
18817
  paths.push(filename);
@@ -18487,13 +18877,13 @@ function build$2(options, isSynchronous) {
18487
18877
  }
18488
18878
  function isRecursive(path, resolved, state) {
18489
18879
  if (state.options.useRealPaths) return isRecursiveUsingRealPaths(resolved, state);
18490
- let parent = dirname3(path);
18880
+ let parent = dirname4(path);
18491
18881
  let depth = 1;
18492
18882
  while (parent !== state.root && depth < 2) {
18493
18883
  const resolvedPath = state.symlinks.get(parent);
18494
18884
  const isSameRoot = !!resolvedPath && (resolvedPath === resolved || resolvedPath.startsWith(resolved) || resolved.startsWith(resolvedPath));
18495
18885
  if (isSameRoot) depth++;
18496
- else parent = dirname3(parent);
18886
+ else parent = dirname4(parent);
18497
18887
  }
18498
18888
  state.symlinks.set(path, resolved);
18499
18889
  return depth > 1;
@@ -18662,26 +19052,26 @@ var Walker = class {
18662
19052
  if (controller.aborted || signal && signal.aborted || maxFiles && paths.length > maxFiles) return;
18663
19053
  const files = this.getArray(this.state.paths);
18664
19054
  for (let i = 0; i < entries.length; ++i) {
18665
- const entry2 = entries[i];
18666
- if (entry2.isFile() || entry2.isSymbolicLink() && !resolveSymlinks$1 && !excludeSymlinks) {
18667
- 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);
18668
19058
  this.pushFile(filename, files, this.state.counts, filters);
18669
- } else if (entry2.isDirectory()) {
18670
- let path = joinDirectoryPath(entry2.name, directoryPath, this.state.options.pathSeparator);
18671
- 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;
18672
19062
  this.pushDirectory(path, paths, filters);
18673
19063
  this.walkDirectory(this.state, path, path, depth - 1, this.walk);
18674
- } else if (this.resolveSymlink && entry2.isSymbolicLink()) {
18675
- let path = joinPathWithBasePath(entry2.name, directoryPath);
19064
+ } else if (this.resolveSymlink && entry.isSymbolicLink()) {
19065
+ let path = joinPathWithBasePath(entry.name, directoryPath);
18676
19066
  this.resolveSymlink(path, this.state, (stat2, resolvedPath) => {
18677
19067
  if (stat2.isDirectory()) {
18678
19068
  resolvedPath = normalizePath(resolvedPath, this.state.options);
18679
- if (exclude && exclude(entry2.name, useRealPaths ? resolvedPath : path + pathSeparator)) return;
19069
+ if (exclude && exclude(entry.name, useRealPaths ? resolvedPath : path + pathSeparator)) return;
18680
19070
  this.walkDirectory(this.state, resolvedPath, useRealPaths ? resolvedPath : path + pathSeparator, depth - 1, this.walk);
18681
19071
  } else {
18682
19072
  resolvedPath = useRealPaths ? resolvedPath : path;
18683
19073
  const filename = basename(resolvedPath);
18684
- const directoryPath$1 = normalizePath(dirname3(resolvedPath), this.state.options);
19074
+ const directoryPath$1 = normalizePath(dirname4(resolvedPath), this.state.options);
18685
19075
  resolvedPath = this.joinPath(filename, directoryPath$1);
18686
19076
  this.pushFile(resolvedPath, files, this.state.counts, filters);
18687
19077
  }
@@ -19092,7 +19482,7 @@ function getOptions(options) {
19092
19482
  realpath: opts.fs.realpath || realpath,
19093
19483
  realpathSync: opts.fs.realpathSync || realpathSync,
19094
19484
  stat: opts.fs.stat || stat,
19095
- statSync: opts.fs.statSync || statSync2
19485
+ statSync: opts.fs.statSync || statSync4
19096
19486
  });
19097
19487
  if (opts.debug) log("globbing with options:", opts);
19098
19488
  return opts;
@@ -19171,7 +19561,7 @@ async function readSource(spec, options) {
19171
19561
  case "stdin":
19172
19562
  return { documents: [{ id: "stdin", kind: "text", text: spec.text }], skipped: [] };
19173
19563
  case "file":
19174
- return readFiles([spec.path], options, spec.range);
19564
+ return readFiles([spec.path], options, spec.range, spec.optional);
19175
19565
  case "glob":
19176
19566
  return readGlob(spec.patterns, options);
19177
19567
  case "jsonl":
@@ -19191,24 +19581,26 @@ function parseFileRef(ref) {
19191
19581
  }
19192
19582
  return { path: match[1], range: { start, end } };
19193
19583
  }
19194
- function readFiles(paths, options, range) {
19584
+ function readFiles(paths, options, range, optional = false) {
19195
19585
  const documents = [];
19196
19586
  const skipped = [];
19197
19587
  const limit = options.maxFileBytes ?? DEFAULT_MAX_FILE_BYTES;
19198
19588
  for (const path of paths) {
19199
19589
  const { absolute, display: rel, real, outside } = resolvePath(options.cwd, path);
19200
- if (!existsSync3(absolute) || !statSync3(absolute).isFile()) {
19590
+ if (!existsSync4(absolute) || !statSync5(absolute).isFile()) {
19591
+ if (optional)
19592
+ continue;
19201
19593
  throw new DecisionsError("source-error", `No such file: ${rel}`, { path: rel });
19202
19594
  }
19203
19595
  if (outside && !options.allowOutside) {
19204
19596
  skipped.push({ path: rel, reason: "outside-repo", detail: real });
19205
19597
  continue;
19206
19598
  }
19207
- if (statSync3(absolute).size > limit) {
19599
+ if (statSync5(absolute).size > limit) {
19208
19600
  skipped.push({ path: rel, reason: "too-large", detail: `> ${limit} bytes` });
19209
19601
  continue;
19210
19602
  }
19211
- const buffer = readFileSync3(absolute);
19603
+ const buffer = readFileSync5(absolute);
19212
19604
  if (buffer.subarray(0, 8e3).includes(0)) {
19213
19605
  skipped.push({ path: rel, reason: "binary" });
19214
19606
  continue;
@@ -19292,13 +19684,13 @@ function insideGitWorkTree(cwd) {
19292
19684
  }
19293
19685
  function readJsonl(path, options) {
19294
19686
  const { absolute, display: rel, real, outside } = resolvePath(options.cwd, path);
19295
- if (!existsSync3(absolute))
19687
+ if (!existsSync4(absolute))
19296
19688
  throw new DecisionsError("source-error", `No such file: ${rel}`, { path: rel });
19297
19689
  if (outside && !options.allowOutside) {
19298
19690
  return { documents: [], skipped: [{ path: rel, reason: "outside-repo", detail: real }] };
19299
19691
  }
19300
19692
  const documents = [];
19301
- readFileSync3(absolute, "utf8").split("\n").forEach((line, index) => {
19693
+ readFileSync5(absolute, "utf8").split("\n").forEach((line, index) => {
19302
19694
  if (line.trim() === "")
19303
19695
  return;
19304
19696
  let data;
@@ -19321,7 +19713,7 @@ function readJsonl(path, options) {
19321
19713
  });
19322
19714
  return { documents, skipped: [] };
19323
19715
  }
19324
- function readDiff(spec, options) {
19716
+ async function readDiff(spec, options) {
19325
19717
  if (spec.range?.startsWith("-")) {
19326
19718
  throw new DecisionsError("invalid-request", `Invalid diff range "${spec.range}"`);
19327
19719
  }
@@ -19329,17 +19721,19 @@ function readDiff(spec, options) {
19329
19721
  if (spec.staged)
19330
19722
  args.push("--cached");
19331
19723
  args.push(spec.range ?? "HEAD", "--", ...spec.paths ?? []);
19332
- let out;
19333
- try {
19334
- out = execFileSync("git", args, {
19724
+ const out = await new Promise((resolve6, reject) => {
19725
+ execFile("git", args, {
19335
19726
  encoding: "utf8",
19336
19727
  maxBuffer: 64 * 1024 * 1024,
19337
- 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()}`));
19338
19735
  });
19339
- } catch (error) {
19340
- const stderr = String(error.stderr ?? error).trim();
19341
- throw new DecisionsError("source-error", `git diff failed: ${stderr}`);
19342
- }
19736
+ });
19343
19737
  return splitDiffByFile(out);
19344
19738
  }
19345
19739
  function splitDiffByFile(diff) {
@@ -19347,9 +19741,15 @@ function splitDiffByFile(diff) {
19347
19741
  const documents = [];
19348
19742
  const skipped = [];
19349
19743
  for (const text of sections) {
19350
- const raw = /^\+\+\+ (?:b\/)?(.+)$/m.exec(text)?.[1] ?? /^diff --git a\/.+ b\/(.+)$/m.exec(text)?.[1];
19351
- const decoded = raw === void 0 ? void 0 : unquoteGitPath(raw.trim());
19352
- 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\//, ""));
19353
19753
  if (path === void 0 || path === "/dev/null") {
19354
19754
  skipped.push({
19355
19755
  path: raw?.trim() ?? "(unnamed)",
@@ -19586,8 +19986,8 @@ function describeExpectation(expectation) {
19586
19986
  }
19587
19987
 
19588
19988
  // ../core/dist/spec/spec.js
19589
- var CLOSED = { additionalProperties: false };
19590
- 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);
19591
19991
  var SPEC_FORMAT = 1;
19592
19992
  var SpecSchema = typebox_exports.Object({
19593
19993
  /** Spec file format. Absent means 1; a newer one is refused, not guessed. */
@@ -19598,14 +19998,14 @@ var SpecSchema = typebox_exports.Object({
19598
19998
  /** Kept when any of these matches, as well as every `keep`. */
19599
19999
  keepAny: typebox_exports.Optional(typebox_exports.Array(typebox_exports.String())),
19600
20000
  sort: typebox_exports.Optional(typebox_exports.String()),
19601
- 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)),
19602
20002
  source: typebox_exports.Optional(typebox_exports.Object({
19603
20003
  glob: typebox_exports.Optional(typebox_exports.Array(typebox_exports.String())),
19604
20004
  file: typebox_exports.Optional(typebox_exports.String()),
19605
20005
  jsonl: typebox_exports.Optional(typebox_exports.String()),
19606
20006
  diff: typebox_exports.Optional(typebox_exports.String()),
19607
20007
  split: typebox_exports.Optional(typebox_exports.String())
19608
- }, CLOSED)),
20008
+ }, CLOSED2)),
19609
20009
  examples: typebox_exports.Optional(typebox_exports.Array(typebox_exports.Object({
19610
20010
  id: typebox_exports.String({ minLength: 1 }),
19611
20011
  /** The text (or a JSON value, sent as its JSON text) to judge. Give this or `file`. */
@@ -19613,11 +20013,11 @@ var SpecSchema = typebox_exports.Object({
19613
20013
  /** A repo file to judge, `path` or `path:START-END`. Give this or `state`. */
19614
20014
  file: typebox_exports.Optional(typebox_exports.String()),
19615
20015
  expect: typebox_exports.Optional(typebox_exports.Record(typebox_exports.String(), typebox_exports.Unknown()))
19616
- }, CLOSED))),
20016
+ }, CLOSED2))),
19617
20017
  provenance: typebox_exports.Optional(typebox_exports.Record(typebox_exports.String(), typebox_exports.Unknown())),
19618
20018
  /** Anything else a team wants to carry, ignored by the engine. */
19619
20019
  meta: typebox_exports.Optional(typebox_exports.Record(typebox_exports.String(), typebox_exports.Unknown()))
19620
- }, CLOSED);
20020
+ }, CLOSED2);
19621
20021
  var NAME = /^[a-z0-9][a-z0-9._-]*$/;
19622
20022
  function parseSpec(text, file, origin) {
19623
20023
  let raw;
@@ -19687,7 +20087,7 @@ function exampleFileProblem(ref, repoRoot) {
19687
20087
  const rel = relative3(repoRoot, full);
19688
20088
  if (isAbsolute3(path) || rel.startsWith("..") || isAbsolute3(rel))
19689
20089
  return "is outside the repo";
19690
- if (!existsSync4(full))
20090
+ if (!existsSync5(full))
19691
20091
  return "does not exist";
19692
20092
  return void 0;
19693
20093
  }
@@ -19718,23 +20118,23 @@ function isQuestion(value) {
19718
20118
  }
19719
20119
  function specDirs(repoRoot, userConfigDir2, bundled) {
19720
20120
  return {
19721
- repo: join2(repoRoot, ".system1", "specs"),
19722
- user: join2(userConfigDir2, "specs"),
20121
+ repo: join3(repoRoot, ".system1", "specs"),
20122
+ user: join3(userConfigDir2, "specs"),
19723
20123
  ...bundled ? { bundled } : {}
19724
20124
  };
19725
20125
  }
19726
20126
  function loadSpec(ref, dirs, cwd) {
19727
20127
  if (ref.includes("/") || ref.endsWith(".yaml") || ref.endsWith(".yml") || ref.endsWith(".json")) {
19728
20128
  const file = isAbsolute3(ref) ? ref : resolve5(cwd, ref);
19729
- if (!existsSync4(file))
20129
+ if (!existsSync5(file))
19730
20130
  throw new DecisionsError("invalid-request", `No spec file at ${ref}`);
19731
- return parseSpec(readFileSync4(file, "utf8"), file, "path");
20131
+ return parseSpec(readFileSync6(file, "utf8"), file, "path");
19732
20132
  }
19733
20133
  for (const [origin, dir] of layers(dirs)) {
19734
20134
  for (const ext of [".yaml", ".yml", ".json"]) {
19735
- const file = join2(dir, `${ref}${ext}`);
19736
- if (existsSync4(file))
19737
- 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);
19738
20138
  }
19739
20139
  }
19740
20140
  const known = listSpecs(dirs).map((s) => s.name);
@@ -19744,17 +20144,17 @@ function listSpecs(dirs) {
19744
20144
  const seen = /* @__PURE__ */ new Set();
19745
20145
  const out = [];
19746
20146
  for (const [origin, dir] of layers(dirs)) {
19747
- if (!existsSync4(dir))
20147
+ if (!existsSync5(dir))
19748
20148
  continue;
19749
- for (const entry2 of readdirSync2(dir).sort()) {
19750
- if (!/\.(ya?ml|json)$/.test(entry2))
20149
+ for (const entry of readdirSync2(dir).sort()) {
20150
+ if (!/\.(ya?ml|json)$/.test(entry))
19751
20151
  continue;
19752
- const file = join2(dir, entry2);
19753
- const name = basename2(entry2, extname(entry2));
20152
+ const file = join3(dir, entry);
20153
+ const name = basename2(entry, extname(entry));
19754
20154
  const shadowed = seen.has(name);
19755
20155
  seen.add(name);
19756
20156
  try {
19757
- const spec = parseSpec(readFileSync4(file, "utf8"), file, origin);
20157
+ const spec = parseSpec(readFileSync6(file, "utf8"), file, origin);
19758
20158
  out.push({
19759
20159
  name,
19760
20160
  origin,
@@ -19814,10 +20214,10 @@ var QuestionSchema = typebox_exports.Union([
19814
20214
 
19815
20215
  // ../core/dist/opportunities/backlog.js
19816
20216
  import { createHash } from "node:crypto";
19817
- import { closeSync, existsSync as existsSync5, mkdirSync as mkdirSync2, openSync, readFileSync as readFileSync5, renameSync, rmSync, statSync as statSync4, writeFileSync as writeFileSync2 } from "node:fs";
19818
- 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";
19819
20219
  var BACKLOG_FORMAT = 1;
19820
- var CLOSED2 = { additionalProperties: false };
20220
+ var CLOSED3 = { additionalProperties: false };
19821
20221
  var Text = typebox_exports.String({ minLength: 1 });
19822
20222
  var Usd = typebox_exports.Number({ minimum: 0, maximum: 1e6 });
19823
20223
  var Status = typebox_exports.Union([
@@ -19831,8 +20231,8 @@ var candidateFields = {
19831
20231
  mode: typebox_exports.Union([typebox_exports.Literal("code"), typebox_exports.Literal("agents")]),
19832
20232
  location: typebox_exports.Object({
19833
20233
  path: Text,
19834
- lines: typebox_exports.Optional(typebox_exports.Object({ start: typebox_exports.Integer({ minimum: 1 }), end: typebox_exports.Integer({ minimum: 1 }) }, CLOSED2))
19835
- }, CLOSED2),
20234
+ lines: typebox_exports.Optional(typebox_exports.Object({ start: typebox_exports.Integer({ minimum: 1 }), end: typebox_exports.Integer({ minimum: 1 }) }, CLOSED3))
20235
+ }, CLOSED3),
19836
20236
  /** What does the job today, e.g. "gpt-4o call parsed to an enum". */
19837
20237
  mechanism: Text,
19838
20238
  shape: typebox_exports.Union([
@@ -19864,12 +20264,12 @@ var candidateFields = {
19864
20264
  decisionCostPerItemUsd: Usd,
19865
20265
  /** Where the figures came from. */
19866
20266
  note: typebox_exports.Optional(typebox_exports.String())
19867
- }, CLOSED2),
20267
+ }, CLOSED3),
19868
20268
  risk: typebox_exports.Object({
19869
20269
  level: typebox_exports.Union([typebox_exports.Literal("low"), typebox_exports.Literal("medium"), typebox_exports.Literal("high")]),
19870
20270
  /** What would go wrong if the replacement erred. */
19871
20271
  note: Text
19872
- }, CLOSED2),
20272
+ }, CLOSED3),
19873
20273
  /** The next step, e.g. "save the spec with design, then adopt". */
19874
20274
  next: Text,
19875
20275
  /** Defaults to `new`. `rejected` needs a `statusReason`. */
@@ -19879,9 +20279,9 @@ var candidateFields = {
19879
20279
  source: typebox_exports.Object({
19880
20280
  sweep: Text,
19881
20281
  answers: typebox_exports.Union([typebox_exports.Literal("live"), typebox_exports.Literal("replay")])
19882
- }, CLOSED2)
20282
+ }, CLOSED3)
19883
20283
  };
19884
- var CandidateSchema = typebox_exports.Object(candidateFields, CLOSED2);
20284
+ var CandidateSchema = typebox_exports.Object(candidateFields, CLOSED3);
19885
20285
  var OpportunitySchema = typebox_exports.Object({
19886
20286
  id: typebox_exports.String({ pattern: "^op-[0-9a-f]{12}$" }),
19887
20287
  ...candidateFields,
@@ -19891,16 +20291,16 @@ var OpportunitySchema = typebox_exports.Object({
19891
20291
  basis: typebox_exports.Literal("projected"),
19892
20292
  /** volume × (current − decision), per `per`. Negative means it would cost more. */
19893
20293
  savingUsd: typebox_exports.Number()
19894
- }, CLOSED2),
20294
+ }, CLOSED3),
19895
20295
  firstSeenAt: typebox_exports.String(),
19896
20296
  seenAt: typebox_exports.String()
19897
- }, CLOSED2);
20297
+ }, CLOSED3);
19898
20298
  var BacklogSchema = typebox_exports.Object({
19899
20299
  version: typebox_exports.Literal(BACKLOG_FORMAT),
19900
20300
  opportunities: typebox_exports.Array(OpportunitySchema)
19901
- }, CLOSED2);
20301
+ }, CLOSED3);
19902
20302
  function backlogPath(repoRoot) {
19903
- return join3(stateDir(repoRoot), "opportunities.json");
20303
+ return join4(stateDir(repoRoot), "opportunities.json");
19904
20304
  }
19905
20305
  function opportunityId(mode, evidence) {
19906
20306
  const normal = evidence.replace(/\s+/g, " ").trim();
@@ -19949,11 +20349,11 @@ function candidateProblems(c, at) {
19949
20349
  return problems;
19950
20350
  }
19951
20351
  function readBacklog(file) {
19952
- if (!existsSync5(file))
20352
+ if (!existsSync6(file))
19953
20353
  return { version: BACKLOG_FORMAT, opportunities: [] };
19954
20354
  let text;
19955
20355
  try {
19956
- text = readFileSync5(file, "utf8");
20356
+ text = readFileSync7(file, "utf8");
19957
20357
  } catch (error) {
19958
20358
  throw malformed2(file, [`cannot read it: ${error.message}`]);
19959
20359
  }
@@ -19975,39 +20375,14 @@ function malformed2(file, problems) {
19975
20375
  var LOCK_WAIT_MS = 5e3;
19976
20376
  var LOCK_STALE_MS = 3e4;
19977
20377
  function withBacklogLock(file, body) {
19978
- mkdirSync2(dirname4(file), { recursive: true });
19979
- const lock = `${file}.lock`;
19980
- const deadline = Date.now() + LOCK_WAIT_MS;
19981
- for (; ; ) {
19982
- try {
19983
- closeSync(openSync(lock, "wx"));
19984
- break;
19985
- } catch (error) {
19986
- if (error.code !== "EEXIST")
19987
- throw error;
19988
- try {
19989
- if (Date.now() - statSync4(lock).mtimeMs > LOCK_STALE_MS)
19990
- rmSync(lock, { force: true });
19991
- } catch {
19992
- }
19993
- if (Date.now() > deadline) {
19994
- 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 });
19995
- }
19996
- Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 25);
19997
- }
19998
- }
19999
- try {
20000
- return body();
20001
- } finally {
20002
- rmSync(lock, { force: true });
20003
- }
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);
20004
20383
  }
20005
20384
  function writeBacklog(file, backlog) {
20006
- mkdirSync2(dirname4(file), { recursive: true });
20007
- const tmp = `${file}.${process.pid}.tmp`;
20008
- writeFileSync2(tmp, `${JSON.stringify(backlog, null, 2)}
20009
- `);
20010
- renameSync(tmp, file);
20385
+ writeJsonAtomic(file, backlog);
20011
20386
  }
20012
20387
  function mergeCandidates(backlog, candidates, now) {
20013
20388
  const problems = candidates.flatMap((c, i) => candidateProblems(c, `/candidates/${i}`));
@@ -20249,6 +20624,16 @@ var RouteInput = typebox_exports.Object({
20249
20624
  /** The user's prompt, as the harness hook received it. */
20250
20625
  prompt: typebox_exports.String()
20251
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 });
20252
20637
  var TOOL_SCHEMAS = {
20253
20638
  ask: AskInput,
20254
20639
  many: ManyInput,
@@ -20258,11 +20643,12 @@ var TOOL_SCHEMAS = {
20258
20643
  "opportunities-add": OpportunitiesAddInput,
20259
20644
  "opportunities-list": OpportunitiesListInput,
20260
20645
  "opportunities-check": OpportunitiesCheckInput,
20261
- route: RouteInput
20646
+ route: RouteInput,
20647
+ hook: HookEvent
20262
20648
  };
20263
20649
 
20264
20650
  // ../core/dist/tools/context.js
20265
- 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";
20266
20652
 
20267
20653
  // ../core/dist/egress/scrub.js
20268
20654
  var SECRET_NAME = "[A-Za-z0-9_.-]*(?:api[_-]?key|secret|token|passw(?:or)?d|pwd|credential|private[_-]?key|auth)[A-Za-z0-9_.-]*";
@@ -20313,42 +20699,42 @@ var RULES = [
20313
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
20314
20700
  }
20315
20701
  ];
20316
- function scrubText(text, counts2 = {}) {
20702
+ function scrubText(text, counts = {}) {
20317
20703
  let out = text;
20318
20704
  for (const rule of RULES) {
20319
20705
  out = out.replace(rule.pattern, () => {
20320
- counts2[rule.kind] = (counts2[rule.kind] ?? 0) + 1;
20706
+ counts[rule.kind] = (counts[rule.kind] ?? 0) + 1;
20321
20707
  return `[REDACTED:${rule.kind}]`;
20322
20708
  });
20323
20709
  }
20324
20710
  return out;
20325
20711
  }
20326
- function scrubState(state, counts2 = {}) {
20327
- return walk(state, counts2);
20712
+ function scrubState(state, counts = {}) {
20713
+ return walk(state, counts);
20328
20714
  }
20329
20715
  var SECRET_KEY = new RegExp(`^${SECRET_NAME}$`, "i");
20330
- function walk(value, counts2, key) {
20716
+ function walk(value, counts, key) {
20331
20717
  if (typeof value === "string") {
20332
20718
  if (key !== void 0 && SECRET_KEY.test(key) && value.trim().length >= 4) {
20333
- counts2["assigned-secret"] = (counts2["assigned-secret"] ?? 0) + 1;
20719
+ counts["assigned-secret"] = (counts["assigned-secret"] ?? 0) + 1;
20334
20720
  return "[REDACTED:assigned-secret]";
20335
20721
  }
20336
- return scrubText(value, counts2);
20722
+ return scrubText(value, counts);
20337
20723
  }
20338
20724
  if (Array.isArray(value))
20339
- return value.map((v) => walk(v, counts2, key));
20725
+ return value.map((v) => walk(v, counts, key));
20340
20726
  if (value !== null && typeof value === "object") {
20341
- 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)]));
20342
20728
  }
20343
20729
  return value;
20344
20730
  }
20345
- function scrubQuestions(questions, counts2 = {}) {
20731
+ function scrubQuestions(questions, counts = {}) {
20346
20732
  return Object.fromEntries(Object.entries(questions).map(([name, q]) => [
20347
20733
  name,
20348
20734
  {
20349
20735
  ...q,
20350
- instructions: scrubText(q.instructions, counts2),
20351
- ...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) }
20352
20738
  }
20353
20739
  ]));
20354
20740
  }
@@ -20369,8 +20755,8 @@ function assertStateFits(id, state, questions, profile) {
20369
20755
 
20370
20756
  // ../core/dist/fixtures/store.js
20371
20757
  import { createHash as createHash2 } from "node:crypto";
20372
- import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync6, renameSync as renameSync2, writeFileSync as writeFileSync3 } from "node:fs";
20373
- 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";
20374
20760
  var FIXTURE_VERSION = 1;
20375
20761
  function fixtureKey(request) {
20376
20762
  const { model, state, questions } = request;
@@ -20397,13 +20783,13 @@ var FixtureStore = class {
20397
20783
  if (!NAMESPACE.test(namespace)) {
20398
20784
  throw new Error(`Invalid fixture namespace "${namespace}" (lowercase letters, digits, . _ -)`);
20399
20785
  }
20400
- return join4(this.dir, namespace, `${key}.json`);
20786
+ return join5(this.dir, namespace, `${key}.json`);
20401
20787
  }
20402
20788
  lookup(namespace, request) {
20403
20789
  const file = this.path(namespace, fixtureKey(request));
20404
- if (!existsSync6(file))
20790
+ if (!existsSync7(file))
20405
20791
  return void 0;
20406
- const record = JSON.parse(readFileSync6(file, "utf8"));
20792
+ const record = JSON.parse(readFileSync8(file, "utf8"));
20407
20793
  return record.v === FIXTURE_VERSION ? record : void 0;
20408
20794
  }
20409
20795
  record(namespace, request, response, now = /* @__PURE__ */ new Date()) {
@@ -20428,7 +20814,7 @@ var FixtureStore = class {
20428
20814
  // ../core/dist/decide.js
20429
20815
  var ZERO = { input_tokens: 0, output_tokens: 0, cost: 0 };
20430
20816
  function createDecider(options) {
20431
- const { profile, transport, fixtures, ledger, session } = options;
20817
+ const { profile, transport, fixtures, ledger, session, tag } = options;
20432
20818
  const now = options.now ?? (() => /* @__PURE__ */ new Date());
20433
20819
  const mode = (options.mode ?? "auto") === "auto" ? transport ? "live" : "replay" : options.mode;
20434
20820
  if ((mode === "live" || mode === "record") && !transport) {
@@ -20440,6 +20826,7 @@ function createDecider(options) {
20440
20826
  const log2 = (source, model, usage) => ledger?.append({
20441
20827
  ts: now().toISOString(),
20442
20828
  ...session ? { session } : {},
20829
+ ...tag ? { tag } : {},
20443
20830
  model,
20444
20831
  source,
20445
20832
  calls: 1,
@@ -20498,7 +20885,7 @@ function createDecider(options) {
20498
20885
  }
20499
20886
 
20500
20887
  // ../core/dist/run/spend.js
20501
- 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";
20502
20889
  import { dirname as dirname6 } from "node:path";
20503
20890
  function sumUsage(usages) {
20504
20891
  const total = usages.reduce((sum, u) => ({
@@ -20513,15 +20900,15 @@ var SpendLedger = class {
20513
20900
  constructor(file) {
20514
20901
  this.file = file;
20515
20902
  }
20516
- append(entry2) {
20903
+ append(entry) {
20517
20904
  mkdirSync4(dirname6(this.file), { recursive: true });
20518
- appendFileSync(this.file, `${JSON.stringify(entry2)}
20905
+ appendFileSync(this.file, `${JSON.stringify(entry)}
20519
20906
  `);
20520
20907
  }
20521
20908
  entries() {
20522
- if (!existsSync7(this.file))
20909
+ if (!existsSync8(this.file))
20523
20910
  return [];
20524
- 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) => {
20525
20912
  try {
20526
20913
  return [JSON.parse(line)];
20527
20914
  } catch {
@@ -20530,7 +20917,7 @@ var SpendLedger = class {
20530
20917
  });
20531
20918
  }
20532
20919
  summary(filter = {}) {
20533
- 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));
20534
20921
  const count = (source) => rows.filter((e) => e.source === source).reduce((n, e) => n + e.calls, 0);
20535
20922
  return {
20536
20923
  ...sumUsage(rows),
@@ -20606,13 +20993,13 @@ function rebasePattern(pattern, cwd, repoRoot) {
20606
20993
  const prefix = relative4(repoRoot, cwd);
20607
20994
  if (prefix === "" || prefix.startsWith("..") || isAbsolute4(prefix) || pattern.startsWith("**/"))
20608
20995
  return pattern;
20609
- return join5(prefix, pattern);
20996
+ return join6(prefix, pattern);
20610
20997
  }
20611
20998
  function rebase(source, cwd, repoRoot) {
20612
20999
  const prefix = relative4(repoRoot, cwd);
20613
21000
  if (prefix === "" || prefix.startsWith("..") || isAbsolute4(prefix))
20614
21001
  return source;
20615
- const move = (path) => isAbsolute4(path) ? path : join5(prefix, path);
21002
+ const move = (path) => isAbsolute4(path) ? path : join6(prefix, path);
20616
21003
  switch (source.kind) {
20617
21004
  case "file":
20618
21005
  return { ...source, path: move(source.path) };
@@ -20637,7 +21024,7 @@ function specSources(spec) {
20637
21024
  ...s.diff !== void 0 ? [{ kind: "diff", ...s.diff ? { range: s.diff } : {} }] : []
20638
21025
  ];
20639
21026
  }
20640
- function deciderFor(ctx, profile, mode = "auto") {
21027
+ function deciderFor(ctx, profile, mode = "auto", extra = {}) {
20641
21028
  const { config } = ctx;
20642
21029
  const dir = stateDir(config.repoRoot);
20643
21030
  return createDecider({
@@ -20652,14 +21039,15 @@ function deciderFor(ctx, profile, mode = "auto") {
20652
21039
  ...ctx.fetch ? { fetch: ctx.fetch } : {}
20653
21040
  })
20654
21041
  } : {},
20655
- fixtures: new FixtureStore(join5(dir, "fixtures")),
20656
- ledger: new SpendLedger(join5(dir, "usage.jsonl")),
21042
+ fixtures: new FixtureStore(join6(dir, "fixtures")),
21043
+ ledger: new SpendLedger(join6(dir, "usage.jsonl")),
20657
21044
  mode: config.replay ? "replay" : mode,
20658
- ...config.session ? { session: config.session } : {}
21045
+ ...config.session ? { session: config.session } : {},
21046
+ ...extra.tag ? { tag: extra.tag } : {}
20659
21047
  });
20660
21048
  }
20661
21049
  function ledgerFor(ctx) {
20662
- return new SpendLedger(join5(stateDir(ctx.config.repoRoot), "usage.jsonl"));
21050
+ return new SpendLedger(join6(stateDir(ctx.config.repoRoot), "usage.jsonl"));
20663
21051
  }
20664
21052
 
20665
21053
  // ../core/dist/run/budget.js
@@ -20705,26 +21093,27 @@ async function prepare(input) {
20705
21093
  cwd: input.cwd,
20706
21094
  ...input.maxFileBytes ? { maxFileBytes: input.maxFileBytes } : {},
20707
21095
  ...input.allowOutside ? { allowOutside: true } : {},
21096
+ ...input.signal ? { signal: input.signal } : {},
20708
21097
  ...input.filter?.length ? { filter: input.filter, withhold: input.exclude ?? [] } : {}
20709
21098
  });
20710
21099
  const byKind = {};
20711
21100
  let redactedSources = 0;
20712
- const count = (counts2) => {
20713
- const found2 = Object.values(counts2).reduce((a, b) => a + b, 0);
20714
- 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))
20715
21104
  byKind[kind] = (byKind[kind] ?? 0) + n;
20716
21105
  return found2;
20717
21106
  };
20718
21107
  const allowed = applyExcludes(read2.documents, input.exclude);
20719
21108
  const chosen = applyFilter(allowed.items, input.filter);
20720
21109
  const documents = chosen.items.map((doc) => {
20721
- const counts2 = {};
21110
+ const counts = {};
20722
21111
  const scrubbed = {
20723
21112
  ...doc,
20724
- ...doc.text !== void 0 ? { text: scrubText(doc.text, counts2) } : {},
20725
- ...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) } : {}
20726
21115
  };
20727
- if (count(counts2) > 0)
21116
+ if (count(counts) > 0)
20728
21117
  redactedSources += 1;
20729
21118
  return scrubbed;
20730
21119
  });
@@ -20733,9 +21122,9 @@ async function prepare(input) {
20733
21122
  const tokens = [];
20734
21123
  const oversize = [];
20735
21124
  const items = kept.flatMap((item) => {
20736
- const counts2 = {};
20737
- const state = scrubState(item.state, counts2);
20738
- if (count(counts2) > 0)
21125
+ const counts = {};
21126
+ const state = scrubState(item.state, counts);
21127
+ if (count(counts) > 0)
20739
21128
  redactedSources += 1;
20740
21129
  try {
20741
21130
  tokens.push(assertStateFits(item.id, state, input.questions, input.profile));
@@ -20815,501 +21204,6 @@ async function mapWithConcurrency(items, limit, worker, onProgress) {
20815
21204
  return results;
20816
21205
  }
20817
21206
 
20818
- // ../core/dist/tools/many.js
20819
- var EXCERPT = 120;
20820
- var SAMPLE = 5;
20821
- async function runMany(ctx, rawInput) {
20822
- const input = checkInput("many", rawInput);
20823
- const started = performance.now();
20824
- const req = resolveRequest(ctx, input);
20825
- const prepared = await prepare({
20826
- sources: req.sources,
20827
- split: req.split,
20828
- questions: req.questions,
20829
- profile: req.profile,
20830
- cwd: ctx.config.repoRoot,
20831
- exclude: ctx.config.egress.exclude,
20832
- filter: req.exclude,
20833
- oversize: "skip",
20834
- ...input.allowOutside ? { allowOutside: true } : {}
20835
- });
20836
- const base2 = {
20837
- ...req.spec ? { spec: req.spec.name } : {},
20838
- model: req.profile.id,
20839
- skipped: summariseSkipped(prepared.skipped),
20840
- redactions: { total: prepared.redactions.total, items: prepared.redactions.items },
20841
- projection: prepared.projection
20842
- };
20843
- if (input.dryRun) {
20844
- return {
20845
- ...base2,
20846
- dryRun: true,
20847
- counts: counts(prepared.items.length, 0, 0, 0, 0, 0, prepared.skipped.length),
20848
- kept: [],
20849
- undecided: [],
20850
- failed: [],
20851
- usage: sumUsage([]),
20852
- wallClockMs: Math.round(performance.now() - started),
20853
- sampleIds: prepared.items.slice(0, SAMPLE).map((i) => i.id)
20854
- };
20855
- }
20856
- const decider = deciderFor(ctx, req.profile, input.mode ?? "auto");
20857
- if (decider.mode !== "replay") {
20858
- assertConsent(ctx.config.egress.consent, ctx.config.repoRoot);
20859
- checkBudget(prepared.projection, ctx.config.budget, input.confirm ?? false);
20860
- }
20861
- const concurrency = Math.min(input.concurrency ?? ctx.config.concurrency, ctx.config.concurrency);
20862
- const settled = await mapWithConcurrency(prepared.items, concurrency, (item) => decider.decide({ state: item.state, questions: req.questions, namespace: req.namespace }));
20863
- const done = [];
20864
- const failed = [];
20865
- settled.forEach((outcome, i) => {
20866
- const item = prepared.items[i];
20867
- if ("value" in outcome)
20868
- done.push({ item, result: outcome.value });
20869
- else
20870
- failed.push({ id: item.id, ...describe3(outcome.error) });
20871
- });
20872
- const [first] = failed;
20873
- if (done.length === 0 && first && failed.every((f) => f.code === first.code)) {
20874
- const { error } = settled.find((s) => "error" in s);
20875
- const details = isDecisionsError(error) ? error.details : {};
20876
- 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 });
20877
- }
20878
- const rows = done.map(({ item, result }) => ({
20879
- ...row(item, result.answers),
20880
- flat: result.undecided
20881
- }));
20882
- const projected = project({
20883
- rows,
20884
- keep: req.keep,
20885
- keepAny: req.keepAny,
20886
- ...req.sort ? { sort: req.sort } : {},
20887
- ...input.limit !== void 0 ? { limit: input.limit } : {},
20888
- ...input.fields ? { fields: input.fields } : {},
20889
- undecidedOf: (r) => r.flat
20890
- });
20891
- const strip = ({ flat, ...rest }) => ({
20892
- ...rest,
20893
- ...flat.length ? { undecided: [...flat] } : {}
20894
- });
20895
- return {
20896
- ...base2,
20897
- dryRun: false,
20898
- source: decider.mode === "replay" ? "replay" : "live",
20899
- counts: counts(prepared.items.length, projected.kept.length, projected.keptTotal, projected.undecided.length, projected.dropped, failed.length, prepared.skipped.length),
20900
- kept: projected.kept.map(strip),
20901
- undecided: projected.undecided.map(({ row: row2, questions }) => ({ ...strip(row2), questions })),
20902
- failed,
20903
- usage: sumUsage(done.map((d) => d.result.usage)),
20904
- wallClockMs: Math.round(performance.now() - started)
20905
- };
20906
- }
20907
- function row(item, answers) {
20908
- return {
20909
- id: item.id,
20910
- ...item.path ? { path: item.path } : {},
20911
- ...item.lines ? { lines: item.lines } : {},
20912
- ...traceable(item) ? {} : { excerpt: excerpt(item.state) },
20913
- answers
20914
- };
20915
- }
20916
- function traceable(item) {
20917
- if (item.path === void 0)
20918
- return false;
20919
- const oneLine = item.lines !== void 0 && item.lines.start === item.lines.end;
20920
- return item.id === item.path || !oneLine;
20921
- }
20922
- function excerpt(state) {
20923
- const text = (typeof state === "string" ? state : JSON.stringify(state)).replace(/\s+/g, " ").trim();
20924
- return text.length > EXCERPT ? `${text.slice(0, EXCERPT - 1)}\u2026` : text;
20925
- }
20926
- function counts(items, kept, keptTotal, undecided, dropped, failed, skipped) {
20927
- return { items, kept, keptTotal, undecided, dropped, failed, skipped };
20928
- }
20929
- function summariseSkipped(skipped) {
20930
- const byReason = {};
20931
- for (const s of skipped)
20932
- byReason[s.reason] = (byReason[s.reason] ?? 0) + 1;
20933
- const ordered = [...skipped].sort((a, b) => Number(b.reason === "excluded") - Number(a.reason === "excluded"));
20934
- return { total: skipped.length, byReason, sample: ordered.slice(0, SAMPLE) };
20935
- }
20936
- function describe3(error) {
20937
- return isDecisionsError(error) ? { code: error.code, message: error.message } : { code: "error", message: error.message };
20938
- }
20939
-
20940
- // ../core/dist/tools/ask.js
20941
- async function runAsk(ctx, rawInput) {
20942
- const input = checkInput("ask", rawInput);
20943
- const req = resolveRequest(ctx, input);
20944
- const prepared = await prepare({
20945
- sources: req.sources,
20946
- split: req.split,
20947
- questions: req.questions,
20948
- profile: req.profile,
20949
- cwd: ctx.config.repoRoot,
20950
- exclude: ctx.config.egress.exclude,
20951
- filter: req.exclude,
20952
- ...input.allowOutside ? { allowOutside: true } : {}
20953
- });
20954
- const [item, ...rest] = prepared.items;
20955
- if (!item || rest.length > 0) {
20956
- const held = prepared.skipped.filter((s) => s.reason !== "filtered");
20957
- const left = prepared.skipped.length - held.length;
20958
- const withheld = (held.length ? ` (${held.length} withheld: ${held.map((s) => `${s.path} ${s.reason}`).join(", ")})` : "") + (left ? ` (${left} left out by --exclude)` : "");
20959
- 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 });
20960
- }
20961
- const result = await deciderFor(ctx, req.profile, input.mode ?? "auto").decide({
20962
- state: item.state,
20963
- questions: req.questions,
20964
- namespace: req.namespace
20965
- });
20966
- const verdict = req.keep.length > 0 || req.keepAny.length > 0 ? verdictOf(result.answers, result.undecided, req.keep, req.keepAny).verdict : void 0;
20967
- return {
20968
- ...req.spec ? { spec: req.spec.name } : {},
20969
- id: item.id,
20970
- ...item.path ? { path: item.path } : {},
20971
- ...item.lines ? { lines: item.lines } : {},
20972
- source: result.source,
20973
- model: result.model,
20974
- servedBy: result.servedBy,
20975
- answers: result.answers,
20976
- undecided: result.undecided,
20977
- ...verdict ? { verdict } : {},
20978
- usage: result.usage,
20979
- latencyMs: result.latencyMs,
20980
- skipped: summariseSkipped(prepared.skipped),
20981
- redactions: { total: prepared.redactions.total, items: prepared.redactions.items }
20982
- };
20983
- }
20984
-
20985
- // ../core/dist/tools/doctor.js
20986
- import { accessSync, constants, existsSync as existsSync8, statSync as statSync5 } from "node:fs";
20987
- import { delimiter, join as join6 } from "node:path";
20988
- function routeCheck(config) {
20989
- try {
20990
- route("", config);
20991
- if (!config.enabled) {
20992
- return {
20993
- name: "route",
20994
- status: "ok",
20995
- detail: "routing hints are off (route.enabled or SYSTEM1_ROUTE)"
20996
- };
20997
- }
20998
- const names = activeTriggers(config).map((t) => t.name);
20999
- return {
21000
- name: "route",
21001
- status: "ok",
21002
- detail: `routing hints on: ${names.length ? names.join(", ") : "no triggers"}`
21003
- };
21004
- } catch (error) {
21005
- return {
21006
- name: "route",
21007
- status: "warn",
21008
- detail: `routing hints are silent: ${error instanceof Error ? error.message : String(error)}`,
21009
- fix: 'correct route: in the config file (test it with `decide route --text "\u2026"`)'
21010
- };
21011
- }
21012
- }
21013
- function backlogProblem(error) {
21014
- const problems = isDecisionsError(error) ? error.details?.problems : void 0;
21015
- const first = Array.isArray(problems) ? problems[0] : void 0;
21016
- const count = Array.isArray(problems) && problems.length > 1 ? ` (+${problems.length - 1} more)` : "";
21017
- return `the backlog doesn't validate: ${first ?? (error instanceof Error ? error.message : String(error))}${count}`;
21018
- }
21019
- function backlogCheck(repoRoot) {
21020
- const file = backlogPath(repoRoot);
21021
- if (!existsSync8(file))
21022
- return { name: "backlog", status: "ok", detail: "no scout backlog yet" };
21023
- try {
21024
- const { opportunities } = readBacklog(file);
21025
- return {
21026
- name: "backlog",
21027
- status: "ok",
21028
- detail: `${opportunities.length} opportunit${opportunities.length === 1 ? "y" : "ies"} in ${file}`
21029
- };
21030
- } catch (error) {
21031
- return {
21032
- name: "backlog",
21033
- status: "warn",
21034
- detail: backlogProblem(error),
21035
- fix: "`decide opportunities check` lists every problem; correct the file by hand or move it aside (decide never repairs it)"
21036
- };
21037
- }
21038
- }
21039
- var CODEX_RULE = 'prefix_rule(pattern = ["decide"], decision = "allow")';
21040
- async function runDoctor(options) {
21041
- const { env } = options;
21042
- const harness = detectHarness(env) ?? null;
21043
- const checks = [
21044
- {
21045
- name: "cli",
21046
- status: "ok",
21047
- detail: `decide ${VERSION} on node ${process.version}${options.cliPath ? ` (${options.cliPath})` : ""}`
21048
- },
21049
- pathCheck(harness, env)
21050
- ];
21051
- const onPath = which("decide", env.PATH ?? "");
21052
- if (onPath && options.probeVersion) {
21053
- checks.push(await versionCheck(onPath, options.probeVersion, env));
21054
- }
21055
- const report2 = (ctx2, live2) => ({
21056
- healthy: !checks.some((c) => c.status === "fail"),
21057
- live: live2,
21058
- harness,
21059
- session: ctx2?.config.session ?? null,
21060
- version: VERSION,
21061
- node: process.version,
21062
- checks
21063
- });
21064
- let ctx;
21065
- try {
21066
- ctx = createContext(options);
21067
- } catch (error) {
21068
- checks.push({
21069
- name: "config",
21070
- status: "fail",
21071
- detail: error instanceof Error ? error.message : String(error),
21072
- fix: "correct the config file or environment variable named above"
21073
- });
21074
- return report2(void 0, false);
21075
- }
21076
- const { config } = ctx;
21077
- if (config.warnings.length > 0) {
21078
- checks.push({
21079
- name: "config-keys",
21080
- status: "warn",
21081
- detail: `ignored: ${config.warnings.join("; ")}`,
21082
- fix: "correct or remove these keys (docs/configuration.md lists every key)"
21083
- });
21084
- }
21085
- checks.push(config.apiKey ? { name: "key", status: "ok", detail: `OPENROUTER_API_KEY present (${config.apiKeySource})` } : {
21086
- name: "key",
21087
- status: "warn",
21088
- detail: "no API key: only replay works",
21089
- // The file this shell would read: XDG_CONFIG_HOME moves it.
21090
- fix: `set OPENROUTER_API_KEY, or write \`openrouter_api_key: <key>\` to ${join6(userConfigDir(env, options.home), "credentials")} (create its directory first; chmod 600)`
21091
- });
21092
- checks.push(config.egress.consent.granted ? { name: "consent", status: "ok", detail: `egress consent granted for ${config.repoRoot}` } : {
21093
- name: "consent",
21094
- status: "warn",
21095
- detail: `no egress consent for ${config.repoRoot}: live calls are refused`,
21096
- 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"
21097
- });
21098
- checks.push(routeCheck(config.route));
21099
- checks.push(backlogCheck(config.repoRoot));
21100
- let reach;
21101
- try {
21102
- reach = await ping({
21103
- endpoint: config.endpoint,
21104
- model: config.model,
21105
- apiKey: config.apiKey,
21106
- replay: config.replay
21107
- }, ctx.fetch ? { fetch: ctx.fetch } : {});
21108
- } catch (error) {
21109
- checks.push({
21110
- name: "config",
21111
- status: "fail",
21112
- detail: `endpoint ${config.endpoint}: ${error instanceof Error ? error.message : String(error)}`,
21113
- fix: "correct SYSTEM1_ENDPOINT or `endpoint` in the config file"
21114
- });
21115
- return report2(ctx, false);
21116
- }
21117
- if (reach.ok) {
21118
- checks.push({
21119
- name: "network",
21120
- status: "ok",
21121
- detail: `${config.model} reachable in ${reach.latencyMs} ms`
21122
- });
21123
- } else {
21124
- const sandboxed = env.CODEX_SANDBOX_NETWORK_DISABLED === "1";
21125
- checks.push({
21126
- name: "network",
21127
- // Replay needs no network, so an unreachable endpoint only warns there.
21128
- status: config.replay ? "warn" : "fail",
21129
- detail: `${reach.probe}: ${reach.error ?? "unreachable"}${sandboxed ? " (Codex sandbox has network disabled)" : ""}`,
21130
- fix: networkFix(reach.httpStatus, harness, sandboxed, env)
21131
- });
21132
- }
21133
- const live = !config.replay && config.apiKey !== void 0 && config.egress.consent.granted && reach.ok === true;
21134
- return report2(ctx, live);
21135
- }
21136
- function pathCheck(harness, env) {
21137
- const onPath = which("decide", env.PATH ?? "");
21138
- if (onPath)
21139
- return { name: "path", status: "ok", detail: `decide on PATH: ${onPath}` };
21140
- if (harness === "claude") {
21141
- return {
21142
- name: "path",
21143
- status: "warn",
21144
- detail: "decide is not on PATH, so the decisions plugin's bin/ is not loaded",
21145
- fix: "install the decisions plugin from its marketplace, or start Claude Code with `--plugin-dir <checkout>/plugins/system1`"
21146
- };
21147
- }
21148
- return {
21149
- name: "path",
21150
- status: "warn",
21151
- detail: "decide is not on PATH. Codex and Pi do not add plugin bin/ to PATH",
21152
- fix: `npm i -g ${CLI_PACKAGE}@${VERSION}`
21153
- };
21154
- }
21155
- async function versionCheck(path, probe, env) {
21156
- let raw;
21157
- try {
21158
- raw = await probe(path);
21159
- } catch {
21160
- }
21161
- const found2 = raw?.trim() || void 0;
21162
- if (raw !== void 0 && found2 === void 0 && env.CODEX_SANDBOX_NETWORK_DISABLED === "1") {
21163
- return {
21164
- name: "path-version",
21165
- status: "ok",
21166
- detail: "not checked: the Codex sandbox hides a child process's output"
21167
- };
21168
- }
21169
- if (found2 === void 0) {
21170
- return {
21171
- name: "path-version",
21172
- status: "warn",
21173
- detail: `could not read the version of ${path}`,
21174
- fix: `run \`${path} version\` to see why; reinstall it if it fails`
21175
- };
21176
- }
21177
- if (found2 === VERSION) {
21178
- return {
21179
- name: "path-version",
21180
- status: "ok",
21181
- detail: `decide on PATH is ${found2}, same as this one`
21182
- };
21183
- }
21184
- return {
21185
- name: "path-version",
21186
- status: "warn",
21187
- detail: `decide on PATH is ${found2}, but this one is ${VERSION}: agents will run the one on PATH`,
21188
- fix: `npm i -g ${CLI_PACKAGE}@${VERSION}, or remove the other decide (${path})`
21189
- };
21190
- }
21191
- function networkFix(httpStatus, harness, sandboxed, env) {
21192
- if (httpStatus === 404)
21193
- return "the endpoint does not know this model: check SYSTEM1_MODEL / `model` and SYSTEM1_ENDPOINT / `endpoint`";
21194
- if (httpStatus !== void 0 && httpStatus >= 500)
21195
- return `the provider answered HTTP ${httpStatus}: retry later`;
21196
- if (httpStatus !== void 0)
21197
- return `the endpoint answered HTTP ${httpStatus}: check SYSTEM1_ENDPOINT / \`endpoint\``;
21198
- if (sandboxed) {
21199
- 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)`;
21200
- }
21201
- if (harness === "claude")
21202
- return "allow outbound access to openrouter.ai in Claude Code's sandbox settings";
21203
- return "check that this machine can reach openrouter.ai (proxy, firewall, DNS)";
21204
- }
21205
- function which(name, path) {
21206
- for (const dir of path.split(delimiter)) {
21207
- if (!dir)
21208
- continue;
21209
- const candidate = join6(dir, name);
21210
- try {
21211
- accessSync(candidate, constants.X_OK);
21212
- if (statSync5(candidate).isFile())
21213
- return candidate;
21214
- } catch {
21215
- }
21216
- }
21217
- return void 0;
21218
- }
21219
-
21220
- // ../core/dist/tools/opportunities.js
21221
- import { existsSync as existsSync9 } from "node:fs";
21222
- function runOpportunitiesAdd(ctx, rawInput, now = () => /* @__PURE__ */ new Date()) {
21223
- const input = checkInput("opportunities-add", rawInput);
21224
- const file = backlogPath(ctx.config.repoRoot);
21225
- const merged2 = withBacklogLock(file, () => {
21226
- const result = mergeCandidates(readBacklog(file), input.candidates, now().toISOString());
21227
- writeBacklog(file, result.backlog);
21228
- return result;
21229
- });
21230
- return {
21231
- file,
21232
- added: merged2.added,
21233
- updated: merged2.updated,
21234
- staled: merged2.staled,
21235
- total: merged2.backlog.opportunities.length
21236
- };
21237
- }
21238
- function runOpportunitiesList(ctx, rawInput = {}) {
21239
- const input = checkInput("opportunities-list", rawInput);
21240
- const filters = (input.keep ?? []).map(parseRecordFilter);
21241
- const sort = parseRecordSort(input.sort ?? "projected:desc");
21242
- assertFields(input.fields);
21243
- const file = backlogPath(ctx.config.repoRoot);
21244
- const { opportunities } = readBacklog(file);
21245
- 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));
21246
- const shown = input.limit === void 0 ? matched : matched.slice(0, input.limit);
21247
- return {
21248
- file,
21249
- total: opportunities.length,
21250
- matched: matched.length,
21251
- basis: "projected",
21252
- opportunities: shown.map((o) => pick2(o, input.fields))
21253
- };
21254
- }
21255
- function runOpportunitiesCheck(ctx, rawInput = {}) {
21256
- checkInput("opportunities-check", rawInput);
21257
- const file = backlogPath(ctx.config.repoRoot);
21258
- const backlog = readBacklog(file);
21259
- const byStatus = {};
21260
- for (const o of backlog.opportunities)
21261
- byStatus[o.status] = (byStatus[o.status] ?? 0) + 1;
21262
- return { file, exists: existsSync9(file), entries: backlog.opportunities.length, byStatus };
21263
- }
21264
- function compare(a, b, direction) {
21265
- if (a === void 0 || b === void 0)
21266
- return a === b ? 0 : a === void 0 ? 1 : -1;
21267
- const order = typeof a === "number" && typeof b === "number" ? a - b : String(a).localeCompare(String(b));
21268
- return direction === "asc" ? order : -order;
21269
- }
21270
- function pick2(o, fields) {
21271
- if (!fields)
21272
- return o;
21273
- const out = { id: o.id };
21274
- for (const f of fields)
21275
- if (Object.hasOwn(o, f))
21276
- out[f] = o[f];
21277
- return out;
21278
- }
21279
- function assertFields(fields = []) {
21280
- const unknown = fields.filter((f) => !ENTRY_FIELDS.includes(f));
21281
- if (unknown.length) {
21282
- throw new DecisionsError("invalid-request", `--fields: no field ${unknown.join(", ")}. Fields: ${ENTRY_FIELDS.join(", ")}`);
21283
- }
21284
- }
21285
-
21286
- // ../core/dist/tools/route.js
21287
- function runRoute(options, rawInput) {
21288
- const problems = checkRouteInput(rawInput);
21289
- if (problems.length > 0) {
21290
- throw new DecisionsError("invalid-request", `Invalid route input:
21291
- ${problems.join("\n ")}`, {
21292
- problems
21293
- });
21294
- }
21295
- const input = rawInput;
21296
- const config = loadConfig(options);
21297
- const { user, repo } = config.layers;
21298
- return {
21299
- ...route(input.prompt, config.route),
21300
- files: { ...user ? { user } : {}, ...repo ? { repo } : {} }
21301
- };
21302
- }
21303
- function checkRouteInput(input) {
21304
- if (typeof input !== "object" || input === null || Array.isArray(input))
21305
- return ["/ must be an object"];
21306
- const extra = Object.keys(input).filter((k) => k !== "prompt");
21307
- return [
21308
- ...typeof input.prompt === "string" ? [] : ["/prompt must be a string"],
21309
- ...extra.map((k) => `/${k} is not allowed`)
21310
- ];
21311
- }
21312
-
21313
21207
  // ../core/dist/spec/lint.js
21314
21208
  var LINT_SEVERITY = {
21315
21209
  "no-way-out": "warning",
@@ -21346,6 +21240,9 @@ function lintQuestions(questions) {
21346
21240
  }
21347
21241
  return out;
21348
21242
  }
21243
+ function lintStatement(text, question) {
21244
+ return unsupported(text, question);
21245
+ }
21349
21246
  function lintSpec(spec) {
21350
21247
  const out = lintQuestions(spec.questions);
21351
21248
  const thresholds = spec.policy?.thresholds ?? {};
@@ -21374,7 +21271,6 @@ function lintSpec(spec) {
21374
21271
  var NUMERIC = /* @__PURE__ */ new Set([">=", ">", "<=", "<"]);
21375
21272
  function lintQuestion(name, q) {
21376
21273
  const out = [];
21377
- const texts = [q.instructions, ...criteriaTexts(q)];
21378
21274
  if (q.type === "choice") {
21379
21275
  const options = Object.entries(q.criteria);
21380
21276
  const wayOut = ([key, desc]) => key.split(/[_\-\s/]+/).some((t) => WAY_OUT_KEY.test(t)) || WAY_OUT_TEXT.test(desc);
@@ -21432,244 +21328,66 @@ function dedupe(findings) {
21432
21328
  });
21433
21329
  }
21434
21330
 
21435
- // ../core/dist/tools/spec-check.js
21436
- async function runSpecCheck(ctx, rawInput) {
21437
- const input = checkInput("spec-check", rawInput);
21438
- const spec = loadSpec(input.spec, ctx.specDirs, ctx.cwd);
21439
- const lint = lintSpec(spec);
21440
- const examples = spec.examples ?? [];
21441
- if (examples.length === 0) {
21442
- throw new DecisionsError("invalid-request", `Spec "${spec.name}" has no examples to check. Add some under examples: (id, state or file, expect).`);
21443
- }
21444
- assertExamples(spec);
21445
- const profile = resolveProfile(input.model ?? ctx.config.model, allProfiles(ctx.config));
21446
- const root = ctx.config.repoRoot;
21447
- const prepared = [];
21448
- for (const example of examples) {
21449
- const expect = example.expect ? parseExpect(example.expect, spec.questions, example.id) : {};
21450
- const refused = example.file !== void 0 ? exampleFileProblem(example.file, root) : void 0;
21451
- if (refused) {
21452
- prepared.push({ id: example.id, expect, reason: `${example.file} ${refused}` });
21453
- continue;
21454
- }
21455
- const source = example.file !== void 0 ? { kind: "file", ...parseFileRef(example.file) } : {
21456
- kind: "text",
21457
- // A structured state is sent as its JSON text.
21458
- text: typeof example.state === "string" ? example.state : JSON.stringify(example.state),
21459
- id: example.id
21460
- };
21461
- try {
21462
- const prep = await prepare({
21463
- sources: [source],
21464
- split: parseSplit("file"),
21465
- questions: spec.questions,
21466
- profile,
21467
- cwd: root,
21468
- exclude: ctx.config.egress.exclude
21469
- });
21470
- const [item, ...rest] = prep.items;
21471
- const why = prep.skipped[0];
21472
- prepared.push(item && rest.length === 0 ? { id: example.id, expect, prep } : {
21473
- id: example.id,
21474
- expect,
21475
- reason: why ? `${why.path} ${why.reason}` : `gave ${prep.items.length} states, not 1`
21476
- });
21477
- } catch (error) {
21478
- if (!isDecisionsError(error) || !["state-too-large", "source-error"].includes(error.code)) {
21479
- throw error;
21480
- }
21481
- prepared.push({ id: example.id, expect, reason: error.message.split("\n")[0] });
21482
- }
21483
- }
21484
- const decider = deciderFor(ctx, profile, input.mode ?? "replay");
21485
- if (decider.mode !== "replay") {
21486
- assertConsent(ctx.config.egress.consent, root);
21487
- const parts = prepared.flatMap((p) => p.prep ? [p.prep.projection] : []);
21488
- checkBudget(combineProjections(profile, parts), ctx.config.budget, input.confirm ?? false);
21489
- }
21490
- const results = [];
21491
- const usages = [];
21492
- let misses = 0;
21493
- for (const { id, expect, prep, reason } of prepared) {
21494
- const item = prep?.items[0];
21495
- if (!prep || !item) {
21496
- results.push({ id, status: "withheld", reason: reason ?? "not sent" });
21497
- continue;
21498
- }
21499
- try {
21500
- const r = await decider.decide({
21501
- state: item.state,
21502
- questions: spec.questions,
21503
- namespace: spec.name
21504
- });
21505
- usages.push(r.usage);
21506
- results.push(judge(id, r.answers, r.undecided, expect));
21507
- } catch (error) {
21508
- if (isDecisionsError(error) && error.code === "replay-miss") {
21509
- misses += 1;
21510
- continue;
21511
- }
21512
- throw error;
21513
- }
21514
- }
21515
- if (misses > 0) {
21516
- 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 });
21517
- }
21518
- const counts2 = {
21519
- examples: results.length,
21520
- pass: 0,
21521
- fail: 0,
21522
- undecided: 0,
21523
- captured: 0,
21524
- withheld: 0
21525
- };
21526
- for (const r of results)
21527
- counts2[r.status] += 1;
21528
- return {
21529
- spec: spec.name,
21530
- file: spec.file,
21531
- model: profile.id,
21532
- source: decider.mode === "replay" ? "replay" : "live",
21533
- passed: counts2.fail === 0 && counts2.undecided === 0 && counts2.withheld === 0,
21534
- counts: counts2,
21535
- examples: results,
21536
- usage: sumUsage(usages),
21537
- skipped: summariseSkipped(prepared.flatMap((p) => p.prep?.skipped ?? [])),
21538
- lint
21539
- };
21540
- }
21541
- function judge(id, answers, flat, expect) {
21542
- const entries = Object.entries(expect);
21543
- if (entries.length === 0) {
21544
- return { id, status: "captured", answers, ...flat.length ? { undecided: flat } : {} };
21545
- }
21546
- const failures = [];
21547
- const undecided = [];
21548
- for (const [question, expectation] of entries) {
21549
- const answer = answers[question];
21550
- const isFlat = flat.includes(question);
21551
- if (expectation.kind === "undecided") {
21552
- if (!isFlat)
21553
- failures.push({ question, expected: "undecided" });
21554
- } else if (isFlat) {
21555
- undecided.push(question);
21556
- } else if (!answer || !meets(question, answer, expectation)) {
21557
- failures.push({ question, expected: describeExpectation(expectation) });
21558
- }
21559
- }
21560
- const status = failures.length ? "fail" : undecided.length ? "undecided" : "pass";
21561
- return {
21562
- id,
21563
- status,
21564
- answers,
21565
- ...failures.length ? { failures } : {},
21566
- ...undecided.length ? { undecided } : {}
21567
- };
21568
- }
21569
-
21570
- // ../core/dist/tools/spec-lint.js
21571
- import { basename as basename3, extname as extname2 } from "node:path";
21572
- function runSpecLint(ctx, rawInput = {}) {
21573
- const input = checkInput("spec-lint", rawInput);
21574
- 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 } : (
21575
- // Loaded by path, so keep the layer it was listed from.
21576
- { ...entry(loadSpec(s.file, ctx.specDirs, ctx.cwd)), origin: s.origin }
21577
- ));
21578
- const all = specs.flatMap((s) => s.findings);
21579
- const counts2 = {
21580
- specs: specs.length,
21581
- errors: all.filter((f) => f.severity === "error").length,
21582
- warnings: all.filter((f) => f.severity === "warning").length,
21583
- invalid: specs.filter((s) => s.invalid).length
21584
- };
21585
- return { specs, counts: counts2, passed: counts2.errors === 0 && counts2.invalid === 0 };
21586
- }
21587
- function named(ctx, ref) {
21588
- try {
21589
- return entry(loadSpec(ref, ctx.specDirs, ctx.cwd));
21590
- } catch (error) {
21591
- const file = isDecisionsError(error) ? error.details?.file : void 0;
21592
- if (typeof file !== "string")
21593
- throw error;
21594
- return {
21595
- name: basename3(file, extname2(file)),
21596
- file,
21597
- origin: "path",
21598
- findings: [],
21599
- invalid: error.message
21600
- };
21601
- }
21602
- }
21603
- function entry(spec) {
21604
- return { name: spec.name, file: spec.file, origin: spec.origin, findings: lintSpec(spec) };
21605
- }
21606
-
21607
- // ../core/dist/tools/usage.js
21608
- function runUsage(ctx, rawInput = {}) {
21609
- const input = checkInput("usage", rawInput);
21610
- const since = input.since ? new Date(input.since) : void 0;
21611
- if (since && Number.isNaN(since.getTime())) {
21612
- throw new DecisionsError("invalid-request", `--since "${input.since}" is not a date`);
21613
- }
21614
- const session = input.session === "current" ? currentSession(ctx) : input.session;
21615
- const ledger = ledgerFor(ctx);
21616
- return {
21617
- basis: "measured",
21618
- ...ledger.summary({
21619
- ...session ? { session } : {},
21620
- ...since ? { since } : {}
21621
- }),
21622
- ledger: ledger.file,
21623
- ...session ? { session } : {},
21624
- ...input.since ? { since: input.since } : {}
21625
- };
21626
- }
21627
- function currentSession(ctx) {
21628
- if (ctx.config.session)
21629
- return ctx.config.session;
21630
- throw new DecisionsError("invalid-request", "no current session: not running under Claude Code, Codex or Pi, and SYSTEM1_SESSION is not set");
21631
- }
21632
-
21633
- // ../core/dist/wiring.js
21634
- var DEFAULT_MODEL = DEFAULT_MODEL_ID;
21635
- function resolveConnection(env = process.env) {
21636
- return {
21637
- endpoint: nonEmpty3(env.SYSTEM1_ENDPOINT) ?? DEFAULT_ENDPOINT,
21638
- model: nonEmpty3(env.SYSTEM1_MODEL) ?? DEFAULT_MODEL,
21639
- apiKey: nonEmpty3(env.OPENROUTER_API_KEY),
21640
- replay: /^(1|true|yes)$/i.test(env.SYSTEM1_REPLAY ?? "")
21641
- };
21642
- }
21643
- function nonEmpty3(value) {
21644
- return value === void 0 || value.trim() === "" ? void 0 : value.trim();
21645
- }
21646
-
21647
21331
  export {
21332
+ PACKS,
21333
+ PACK_NAMES,
21334
+ isPackName,
21335
+ DEFAULT_MODEL_ID,
21336
+ resolveProfile,
21648
21337
  activeTriggers,
21338
+ route,
21339
+ DEFAULT_ENDPOINT,
21340
+ detectHarness,
21341
+ userConfigDir,
21649
21342
  repoConfigPath,
21650
21343
  loadConfig,
21651
21344
  allProfiles,
21345
+ unquoteKey,
21652
21346
  setConsent,
21347
+ setGuardEnabled,
21348
+ assertConsent,
21349
+ assertStateFits,
21653
21350
  DEFAULT_EXCLUDES,
21654
- probeUrl,
21655
- 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,
21656
21371
  parseFileRef,
21372
+ parseSplit,
21373
+ prepare,
21374
+ mapWithConcurrency,
21375
+ sumUsage,
21376
+ parseExpect,
21377
+ meets,
21378
+ describeExpectation,
21379
+ lintStatement,
21380
+ lintSpec,
21657
21381
  exampleProblems,
21382
+ exampleFileProblem,
21658
21383
  assertExamples,
21659
21384
  parseQuestionSet,
21660
21385
  loadSpec,
21661
21386
  listSpecs,
21662
21387
  TOOL_SCHEMAS,
21663
21388
  createContext,
21664
- runMany,
21665
- runAsk,
21666
- runDoctor,
21667
- runOpportunitiesAdd,
21668
- runOpportunitiesList,
21669
- runOpportunitiesCheck,
21670
- runRoute,
21671
- runSpecCheck,
21672
- runSpecLint,
21673
- runUsage,
21674
- resolveConnection
21389
+ checkInput,
21390
+ resolveRequest,
21391
+ deciderFor,
21392
+ ledgerFor
21675
21393
  };