@pmelab/gtd 35.0.0 → 37.0.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.
Files changed (69) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +45 -29
  3. package/claude/hooks/context.ts +66 -0
  4. package/claude/hooks/drive.ts +3 -0
  5. package/claude/hooks/register.tsx +69 -6
  6. package/dist/chunks/{reserved-C8Fm3s72.mjs → reserved-zAfoJtR7.mjs} +62 -24
  7. package/dist/chunks/{tail-oaYewBN_.mjs → tail-BRC4DGdV.mjs} +1 -1
  8. package/dist/chunks/{wire-D3Rc5r22.mjs → wire-CSsa3Wxd.mjs} +117 -2
  9. package/dist/flows.mjs +15 -10
  10. package/dist/gtd.bundle.mjs +511 -64
  11. package/dist/testkit.mjs +158 -26
  12. package/dist/wire.mjs +2 -2
  13. package/dist/workflows/cleanup.mjs +2 -2
  14. package/dist/workflows/feature.mjs +138 -38
  15. package/dist/workflows/fix.mjs +2 -2
  16. package/dist/workflows/review.mjs +2 -2
  17. package/dist/workflows/rollout.mjs +1 -1
  18. package/dist/workflows.mjs +208 -66
  19. package/package.json +1 -1
  20. package/skills/authoring/SKILL.md +3 -3
  21. package/src/cli/Cli.test.ts +10 -3
  22. package/src/cli/Cli.ts +76 -1
  23. package/src/cli/Program.test.ts +1 -0
  24. package/src/cli/Program.ts +28 -0
  25. package/src/cli/ToolCommands.ts +3 -0
  26. package/src/edge/Edge.ts +11 -0
  27. package/src/flows/runtime.ts +11 -0
  28. package/src/flows/scripts.ts +31 -9
  29. package/src/replay/Replay.ts +51 -2
  30. package/src/testkit/FlowScripts.test.ts +58 -0
  31. package/src/testkit/FlowScripts.ts +61 -24
  32. package/src/testkit/Layers.ts +1 -0
  33. package/src/wire/BeatDocument.ts +12 -2
  34. package/src/wire/BeatStatus.ts +3 -0
  35. package/src/wire/ContextLimit.fixture.ts +30 -0
  36. package/src/wire/ContextLimit.test.ts +96 -0
  37. package/src/wire/ContextLimit.ts +169 -0
  38. package/src/wire/index.ts +1 -0
  39. package/src/wire/types.ts +7 -0
  40. package/src/workflow/Workflow.ts +9 -1
  41. package/src/workflows/feature/acceptance-criteria.feature +5 -1
  42. package/src/workflows/feature/context-limit.feature +37 -0
  43. package/src/workflows/feature/coverage.feature +6 -3
  44. package/src/workflows/feature/coverage.ts +8 -2
  45. package/src/workflows/feature/fast-path.feature +11 -19
  46. package/src/workflows/feature/feature.test.ts +24 -4
  47. package/src/workflows/feature/feature.ts +14 -10
  48. package/src/workflows/feature/flag-mode.feature +17 -9
  49. package/src/workflows/feature/lens-models.feature +7 -1
  50. package/src/workflows/feature/naive-acceptance.feature +2 -2
  51. package/src/workflows/feature/one-step-land.feature +10 -12
  52. package/src/workflows/feature/quality-unresolved.feature +7 -1
  53. package/src/workflows/feature/refactor-absorbs.feature +12 -5
  54. package/src/workflows/feature/refactor-first.feature +11 -2
  55. package/src/workflows/feature/refactor-lens-models.feature +4 -0
  56. package/src/workflows/feature/refactor-rebuild.feature +269 -0
  57. package/src/workflows/feature/refactor-review.feature +45 -1
  58. package/src/workflows/feature/refactor.ts +9 -3
  59. package/src/workflows/feature/risk-tier.feature +12 -4
  60. package/src/workflows/feature/seam-rejection.feature +4 -9
  61. package/src/workflows/feature/seam.feature +29 -26
  62. package/src/workflows/feature/seam.test.ts +117 -6
  63. package/src/workflows/feature/seam.ts +158 -39
  64. package/src/workflows/review/review-door.feature +12 -4
  65. package/src/workflows/shared/options.test.ts +11 -2
  66. package/src/workflows/shared/options.ts +18 -4
  67. package/src/workflows/shared/steps.ts +1 -0
  68. package/src/workflows/shared/text.test.ts +8 -0
  69. package/src/workflows/shared/text.ts +91 -22
@@ -170,7 +170,7 @@ var init_GlobalValue = __esmMin((() => {
170
170
  }));
171
171
  //#endregion
172
172
  //#region node_modules/effect/dist/esm/Predicate.js
173
- var isTruthy, isString, isNumber, isBoolean, isBigInt, isSymbol, isFunction$1, isUndefined, isNever, isRecordOrArray, isObject$3, hasProperty, isTagged, isNullable, isNotNullable, isUint8Array, isDate, isIterable, isRecord$2, isPromiseLike;
173
+ var isTruthy, isString, isNumber, isBoolean, isBigInt, isSymbol, isFunction$1, isUndefined, isNotUndefined, isNotNull, isNever, isRecordOrArray, isObject$3, hasProperty, isTagged, isNullable, isNotNullable, isUint8Array, isDate, isIterable, isRecord$2, isPromiseLike;
174
174
  var init_Predicate = __esmMin((() => {
175
175
  init_Function();
176
176
  isTruthy = (input) => !!input;
@@ -181,6 +181,8 @@ var init_Predicate = __esmMin((() => {
181
181
  isSymbol = (input) => typeof input === "symbol";
182
182
  isFunction$1 = isFunction$2;
183
183
  isUndefined = (input) => input === void 0;
184
+ isNotUndefined = (input) => input !== void 0;
185
+ isNotNull = (input) => input !== null;
184
186
  isNever = (_) => false;
185
187
  isRecordOrArray = (input) => typeof input === "object" && input !== null;
186
188
  isObject$3 = (input) => isRecordOrArray(input) || isFunction$1(input);
@@ -6826,7 +6828,7 @@ var init_label = __esmMin((() => {
6826
6828
  }));
6827
6829
  //#endregion
6828
6830
  //#region node_modules/effect/dist/esm/internal/core-effect.js
6829
- var annotateLogs$1, asSome, try_$1, catchTag$1, diffFiberRefs, bind, bindTo, match$4, fiberRefs, ignore$1, logWithLevel, logWarning$1, logError$1, mapErrorCause, negate$1, orElseSucceed$1, patchFiberRefs, promise$1, provideService$1, provideServiceEffect, sandbox$1, sleep$1, succeedNone, summarized, tapErrorCause$1, tryPromise$1, tryMap$1, updateFiberRefs, when$1, serviceOption$1;
6831
+ var annotateLogs$1, asSome, try_$1, catchAllDefect$1, catchTag$1, diffFiberRefs, bind, bindTo, match$4, fiberRefs, ignore$1, logWithLevel, logWarning$1, logError$1, mapErrorCause, negate$1, orElseSucceed$1, patchFiberRefs, promise$1, provideService$1, provideServiceEffect, sandbox$1, sleep$1, succeedNone, summarized, tapErrorCause$1, tryPromise$1, tryMap$1, updateFiberRefs, when$1, serviceOption$1;
6830
6832
  var init_core_effect = __esmMin((() => {
6831
6833
  init_Clock();
6832
6834
  init_Context();
@@ -6861,6 +6863,13 @@ var init_core_effect = __esmMin((() => {
6861
6863
  }
6862
6864
  });
6863
6865
  };
6866
+ catchAllDefect$1 = /*#__PURE__*/ dual(2, (self, f) => catchAllCause$1(self, (cause) => {
6867
+ const option = find(cause, (_) => isDieType$1(_) ? some(_) : none$4());
6868
+ switch (option._tag) {
6869
+ case "None": return failCause$6(cause);
6870
+ case "Some": return f(option.value.defect);
6871
+ }
6872
+ }));
6864
6873
  catchTag$1 = /*#__PURE__*/ dual((args) => isEffect$1(args[0]), (self, ...args) => {
6865
6874
  const f = args[args.length - 1];
6866
6875
  let predicate;
@@ -11435,7 +11444,7 @@ var init_MutableQueue = __esmMin((() => {
11435
11444
  }));
11436
11445
  //#endregion
11437
11446
  //#region node_modules/effect/dist/esm/Effect.js
11438
- var isEffect, all$1, forEach$2, async, withFiberRuntime, fail$7, failCause$2, die, dieMessage, gen, promise, succeed$3, suspend$5, sync$1, _void, catchAll, catchAllCause, catchIf, catchTag, ignore, sandbox, retry, try_, tryMap, tryPromise, interrupt, interruptible, uninterruptible, uninterruptibleMask, as, asVoid, map$4, mapBoth$1, mapError$1, negate, acquireRelease, acquireUseRelease$2, addFinalizer, ensuring$2, onError, scope$1, scopeWith, scopedWith$1, scoped$3, fiberIdWith, forkDaemon, forkIn, forkScoped, timeout, provide$1, provideService, serviceOption, either, exit$1, intoDeferred, when, flatMap$4, andThen, flatten$5, race, raceWith, tap, tapErrorCause, repeat, match$2, matchCause, matchCauseEffect, matchEffect, logWarning, logError, annotateLogs, orDie, orElse$1, orElseSucceed, runtime, unsafeMakeSemaphore, makeSemaphore, unsafeMakeLatch, runFork$1, runPromise, runSync, zip$1, zipLeft, zipRight$1, zipWith, fnUntraced;
11447
+ var isEffect, all$1, forEach$2, async, withFiberRuntime, fail$7, failCause$2, die, dieMessage, gen, promise, succeed$3, suspend$5, sync$1, _void, catchAll, catchAllCause, catchAllDefect, catchIf, catchTag, ignore, sandbox, retry, try_, tryMap, tryPromise, interrupt, interruptible, uninterruptible, uninterruptibleMask, as, asVoid, map$4, mapBoth$1, mapError$1, negate, acquireRelease, acquireUseRelease$2, addFinalizer, ensuring$2, onError, scope$1, scopeWith, scopedWith$1, scoped$3, fiberIdWith, forkDaemon, forkIn, forkScoped, timeout, provide$1, provideService, serviceOption, either, exit$1, intoDeferred, when, flatMap$4, andThen, flatten$5, race, raceWith, tap, tapErrorCause, repeat, match$2, matchCause, matchCauseEffect, matchEffect, logWarning, logError, annotateLogs, orDie, orElse$1, orElseSucceed, runtime, unsafeMakeSemaphore, makeSemaphore, unsafeMakeLatch, runFork$1, runPromise, runSync, zip$1, zipLeft, zipRight$1, zipWith, fnUntraced;
11439
11448
  var init_Effect = __esmMin((() => {
11440
11449
  init_core_effect();
11441
11450
  init_core();
@@ -11461,6 +11470,7 @@ var init_Effect = __esmMin((() => {
11461
11470
  _void = void_$4;
11462
11471
  catchAll = catchAll$1;
11463
11472
  catchAllCause = catchAllCause$1;
11473
+ catchAllDefect = catchAllDefect$1;
11464
11474
  catchIf = catchIf$1;
11465
11475
  catchTag = catchTag$1;
11466
11476
  ignore = ignore$1;
@@ -14278,7 +14288,7 @@ function getBrands(ast) {
14278
14288
  onSome: (brands) => brands.map((brand) => ` & Brand<${formatUnknown(brand)}>`).join("")
14279
14289
  });
14280
14290
  }
14281
- var TypeConstructorAnnotationId, BrandAnnotationId, SchemaIdAnnotationId, MessageAnnotationId, MissingMessageAnnotationId, IdentifierAnnotationId, TitleAnnotationId, AutoTitleAnnotationId, DescriptionAnnotationId, ExamplesAnnotationId, DefaultAnnotationId, JSONSchemaAnnotationId, ArbitraryAnnotationId, PrettyAnnotationId, EquivalenceAnnotationId, DocumentationAnnotationId, ConcurrencyAnnotationId, BatchingAnnotationId, ParseIssueTitleAnnotationId, ParseOptionsAnnotationId, DecodingFallbackAnnotationId, SurrogateAnnotationId, StableFilterAnnotationId, getAnnotation, getBrandAnnotation, getMessageAnnotation, getMissingMessageAnnotation, getTitleAnnotation, getAutoTitleAnnotation, getIdentifierAnnotation, getDescriptionAnnotation, getConcurrencyAnnotation, getBatchingAnnotation, getParseIssueTitleAnnotation$1, getParseOptionsAnnotation, getDecodingFallbackAnnotation, getSurrogateAnnotation, getStableFilterAnnotation, hasStableFilter, JSONIdentifierAnnotationId, getJSONIdentifierAnnotation, getJSONIdentifier, Declaration, createASTGuard, Literal$1, isLiteral, $null, UniqueSymbol, UndefinedKeyword, undefinedKeyword, NeverKeyword, neverKeyword, UnknownKeyword, unknownKeyword, AnyKeyword, anyKeyword, StringKeyword, stringKeyword, isStringKeyword, NumberKeyword, numberKeyword, isNumberKeyword, BooleanKeyword, booleanKeyword, isBooleanKeyword, BigIntKeyword, bigIntKeyword, SymbolKeyword, symbolKeyword, isSymbolKeyword, Type$1, OptionalType, getRestASTs, TupleType, formatTuple, PropertySignature, isParameter, IndexSignature, TypeLiteral, formatIndexSignatures, formatTypeLiteral, isTypeLiteral, sortCandidates, literalMap, flatten$2, unify, Union$1, mapMembers, isMembers, isUnion, toJSONMemoMap, Suspend, Refinement$1, isRefinement$1, defaultParseOption, Transformation$1, isTransformation$1, FinalTransformation, createTransformationGuard, ComposeTransformation, composeTransformation, PropertySignatureTransformation$1, TypeLiteralTransformation, isTypeLiteralTransformation, annotations, STRING_KEYWORD_PATTERN, NUMBER_KEYWORD_PATTERN, getTemplateLiteralSpanTypePattern, handleTemplateLiteralSpanTypeParens, getTemplateLiteralPattern, getTemplateLiteralRegExp, record, pickAnnotations, omitAnnotations, preserveTransformationAnnotations, typeAST, getTransformationFrom, encodedAST_, encodedAST, toJSONAnnotations, getEncodedParameter, formatKeyword, getOrElseExpected, getExpected;
14291
+ var TypeConstructorAnnotationId, BrandAnnotationId, SchemaIdAnnotationId, MessageAnnotationId, MissingMessageAnnotationId, IdentifierAnnotationId, TitleAnnotationId, AutoTitleAnnotationId, DescriptionAnnotationId, ExamplesAnnotationId, DefaultAnnotationId, JSONSchemaAnnotationId, ArbitraryAnnotationId, PrettyAnnotationId, EquivalenceAnnotationId, DocumentationAnnotationId, ConcurrencyAnnotationId, BatchingAnnotationId, ParseIssueTitleAnnotationId, ParseOptionsAnnotationId, DecodingFallbackAnnotationId, SurrogateAnnotationId, StableFilterAnnotationId, getAnnotation, getBrandAnnotation, getMessageAnnotation, getMissingMessageAnnotation, getTitleAnnotation, getAutoTitleAnnotation, getIdentifierAnnotation, getDescriptionAnnotation, getConcurrencyAnnotation, getBatchingAnnotation, getParseIssueTitleAnnotation$1, getParseOptionsAnnotation, getDecodingFallbackAnnotation, getSurrogateAnnotation, getStableFilterAnnotation, hasStableFilter, JSONIdentifierAnnotationId, getJSONIdentifierAnnotation, getJSONIdentifier, Declaration, createASTGuard, Literal$1, isLiteral, $null, UniqueSymbol, UndefinedKeyword, undefinedKeyword, NeverKeyword, neverKeyword, UnknownKeyword, unknownKeyword, AnyKeyword, anyKeyword, StringKeyword, stringKeyword, isStringKeyword, NumberKeyword, numberKeyword, isNumberKeyword, BooleanKeyword, booleanKeyword, isBooleanKeyword, BigIntKeyword, bigIntKeyword, SymbolKeyword, symbolKeyword, isSymbolKeyword, Type$1, OptionalType, getRestASTs, TupleType, formatTuple, PropertySignature, isParameter, IndexSignature, TypeLiteral, formatIndexSignatures, formatTypeLiteral, isTypeLiteral, sortCandidates, literalMap, flatten$2, unify, Union$1, mapMembers, isMembers, isUnion, toJSONMemoMap, Suspend, Refinement$1, isRefinement$1, defaultParseOption, Transformation$1, isTransformation$1, FinalTransformation, createTransformationGuard, ComposeTransformation, composeTransformation, PropertySignatureTransformation$1, TypeLiteralTransformation, isTypeLiteralTransformation, annotations, STRING_KEYWORD_PATTERN, NUMBER_KEYWORD_PATTERN, getTemplateLiteralSpanTypePattern, handleTemplateLiteralSpanTypeParens, getTemplateLiteralPattern, getTemplateLiteralRegExp, record$1, pickAnnotations, omitAnnotations, preserveTransformationAnnotations, typeAST, getTransformationFrom, encodedAST_, encodedAST, toJSONAnnotations, getEncodedParameter, formatKeyword, getOrElseExpected, getExpected;
14282
14292
  var init_SchemaAST = __esmMin((() => {
14283
14293
  init_Array();
14284
14294
  init_Function();
@@ -15242,7 +15252,7 @@ var init_SchemaAST = __esmMin((() => {
15242
15252
  return pattern;
15243
15253
  };
15244
15254
  getTemplateLiteralRegExp = (ast) => new RegExp(`^${getTemplateLiteralPattern(ast, false, true)}$`);
15245
- record = (key, value) => {
15255
+ record$1 = (key, value) => {
15246
15256
  const propertySignatures = [];
15247
15257
  const indexSignatures = [];
15248
15258
  const go = (key) => {
@@ -16549,7 +16559,7 @@ function causeEncode(cause) {
16549
16559
  };
16550
16560
  }
16551
16561
  }
16552
- var TypeId$7, variance, builtInAnnotations, toASTAnnotations, mergeSchemaAnnotations, format, encodedSchema, typeSchema, decodeUnknown, decodeUnknownEither, isSchema, declareConstructor, declarePrimitive, declare, InstanceOfSchemaId, instanceOf, Undefined, Null, Never, Unknown, BigIntFromSelf, SymbolFromSelf, String$, Number$, Boolean$, getDefaultUnionAST, NullOr, UndefinedOr, element, ElementImpl, getDefaultTupleTypeAST, Array$, formatPropertySignatureToken, PropertySignatureDeclaration, ToPropertySignature, formatPropertyKey, PropertySignatureTransformation, mergeSignatureAnnotations, PropertySignatureTypeId, isPropertySignature, PropertySignatureImpl, makePropertySignature, PropertySignatureWithFromImpl, propertySignature, withConstructorDefault, optional$2, preserveMissingMessageAnnotation, getDefaultTypeLiteralAST, lazilyMergeDefaults, tag$2, TaggedStruct, Record, intersectTypeLiterals, preserveRefinementAnnotations, addRefinementToMembers, extendAST, getTypes, intersectUnionMembers, extend, suspend, RefineSchemaId, fromFilterPredicateReturnTypeItem, toFilterParseIssue, transformOrFail, transform$1, TrimmedSchemaId, trimmed, MinLengthSchemaId, minLength, LengthSchemaId, length, PatternSchemaId, pattern, LowercasedSchemaId, lowercased, Lowercased, UppercasedSchemaId, uppercased, Uppercased, CapitalizedSchemaId, capitalized, Capitalized, UncapitalizedSchemaId, uncapitalized, Uncapitalized, nonEmptyString, Trimmed, NonEmptyTrimmedString, getErrorMessage, UUIDSchemaId, uuidRegexp, ULIDSchemaId, ulidRegexp, URLFromSelf, FiniteSchemaId, finite, GreaterThanSchemaId, greaterThan, GreaterThanOrEqualToSchemaId, greaterThanOrEqualTo, IntSchemaId, int, LessThanSchemaId, lessThan, LessThanOrEqualToSchemaId, lessThanOrEqualTo, BetweenSchemaId, between, NonNaNSchemaId, nonNaN, positive, negative, nonPositive, nonNegative, Int, NonNegative, JsonNumberSchemaId, encodeSymbol, decodeSymbol, GreaterThanOrEqualToBigIntSchemaId, greaterThanOrEqualToBigInt, BetweenBigIntSchemaId, betweenBigInt, nonNegativeBigInt, BigInt$, NonNegativeBigIntFromSelf, toComposite, DurationFromSelf, NonNegativeInt, DurationValueMillis, DurationValueNanos, DurationValueInfinity, durationValueInfinity, DurationValue, HRTime, isDurationValue, Uint8ArrayFromSelf, Uint8, ValidDateSchemaId, validDate, DateFromSelfSchemaId, DateFromSelf, DateFromString, DateTimeUtcFromSelf, decodeDateTimeUtc, timeZoneOffsetArbitrary, TimeZoneOffsetFromSelf, timeZoneNamedArbitrary, TimeZoneNamedFromSelf, TimeZoneFromSelf, timeZoneArbitrary, DateTimeZonedFromSelf, optionDecode, optionArbitrary, optionPretty, optionParse, OptionFromSelf_, OptionFromSelf, bigDecimalPretty, bigDecimalArbitrary, BigDecimalFromSelf, isField, isFields, getFields, getSchemaFromFieldsOr, getFieldsFromFieldsOr, getClassTag, TaggedError, extendFields, astCache, getClassAnnotations, makeClass, FiberIdEncoded, fiberIdArbitrary, fiberIdPretty, FiberIdFromSelf, fiberIdDecode, fiberIdEncode, causeDieEncoded, CauseEmptyEncoded, causeFailEncoded, CauseInterruptEncoded, causeEncodedId, causeEncoded, causeArbitrary, causePretty, causeParse, CauseFromSelf, Cause, Defect, SymbolStruct, SymbolFromStruct, PropertyKey$;
16562
+ var TypeId$7, variance, builtInAnnotations, toASTAnnotations, mergeSchemaAnnotations, format, encodedSchema, typeSchema, decodeUnknown, decodeUnknownEither, isSchema, declareConstructor, declarePrimitive, declare, InstanceOfSchemaId, instanceOf, Undefined, Null, Never, Unknown, BigIntFromSelf, SymbolFromSelf, String$, Number$, Boolean$, getDefaultUnionAST, NullOr, UndefinedOr, NullishOr, element, ElementImpl, getDefaultTupleTypeAST, Array$, formatPropertySignatureToken, PropertySignatureDeclaration, FromPropertySignature, ToPropertySignature, formatPropertyKey, PropertySignatureTransformation, mergeSignatureAnnotations, PropertySignatureTypeId, isPropertySignature, PropertySignatureImpl, makePropertySignature, PropertySignatureWithFromImpl, propertySignature, withConstructorDefault, optionalToRequired, optionalToOptional, optionalPropertySignatureAST, optional$2, optionalWith, preserveMissingMessageAnnotation, getDefaultTypeLiteralAST, lazilyMergeDefaults, tag$2, TaggedStruct, Record, intersectTypeLiterals, preserveRefinementAnnotations, addRefinementToMembers, extendAST, getTypes, intersectUnionMembers, extend, suspend, RefineSchemaId, fromFilterPredicateReturnTypeItem, toFilterParseIssue, transformOrFail, transform$1, TrimmedSchemaId, trimmed, MinLengthSchemaId, minLength, LengthSchemaId, length, PatternSchemaId, pattern, LowercasedSchemaId, lowercased, Lowercased, UppercasedSchemaId, uppercased, Uppercased, CapitalizedSchemaId, capitalized, Capitalized, UncapitalizedSchemaId, uncapitalized, Uncapitalized, nonEmptyString, Trimmed, NonEmptyTrimmedString, getErrorMessage, UUIDSchemaId, uuidRegexp, ULIDSchemaId, ulidRegexp, URLFromSelf, FiniteSchemaId, finite, GreaterThanSchemaId, greaterThan, GreaterThanOrEqualToSchemaId, greaterThanOrEqualTo, IntSchemaId, int, LessThanSchemaId, lessThan, LessThanOrEqualToSchemaId, lessThanOrEqualTo, BetweenSchemaId, between, NonNaNSchemaId, nonNaN, positive, negative, nonPositive, nonNegative, Int, NonNegative, JsonNumberSchemaId, encodeSymbol, decodeSymbol, GreaterThanOrEqualToBigIntSchemaId, greaterThanOrEqualToBigInt, BetweenBigIntSchemaId, betweenBigInt, nonNegativeBigInt, BigInt$, NonNegativeBigIntFromSelf, toComposite, DurationFromSelf, NonNegativeInt, DurationValueMillis, DurationValueNanos, DurationValueInfinity, durationValueInfinity, DurationValue, HRTime, isDurationValue, Uint8ArrayFromSelf, Uint8, ValidDateSchemaId, validDate, DateFromSelfSchemaId, DateFromSelf, DateFromString, DateTimeUtcFromSelf, decodeDateTimeUtc, timeZoneOffsetArbitrary, TimeZoneOffsetFromSelf, timeZoneNamedArbitrary, TimeZoneNamedFromSelf, TimeZoneFromSelf, timeZoneArbitrary, DateTimeZonedFromSelf, optionDecode, optionArbitrary, optionPretty, optionParse, OptionFromSelf_, OptionFromSelf, bigDecimalPretty, bigDecimalArbitrary, BigDecimalFromSelf, isField, isFields, getFields, getSchemaFromFieldsOr, getFieldsFromFieldsOr, getClassTag, TaggedError, extendFields, astCache, getClassAnnotations, makeClass, FiberIdEncoded, fiberIdArbitrary, fiberIdPretty, FiberIdFromSelf, fiberIdDecode, fiberIdEncode, causeDieEncoded, CauseEmptyEncoded, causeFailEncoded, CauseInterruptEncoded, causeEncodedId, causeEncoded, causeArbitrary, causePretty, causeParse, CauseFromSelf, Cause, Defect, SymbolStruct, SymbolFromStruct, PropertyKey$;
16553
16563
  var init_Schema = __esmMin((() => {
16554
16564
  init_Array();
16555
16565
  init_BigDecimal();
@@ -16668,6 +16678,7 @@ var init_Schema = __esmMin((() => {
16668
16678
  getDefaultUnionAST = (members) => Union$1.make(members.map((m) => m.ast));
16669
16679
  NullOr = (self) => Union(self, Null);
16670
16680
  UndefinedOr = (self) => Union(self, Undefined);
16681
+ NullishOr = (self) => Union(self, Null, Undefined);
16671
16682
  element = (self) => new ElementImpl(new OptionalType(self.ast, false), self);
16672
16683
  ElementImpl = class ElementImpl {
16673
16684
  ast;
@@ -16712,6 +16723,15 @@ var init_Schema = __esmMin((() => {
16712
16723
  return `PropertySignature<${token}, ${type}, never, ${token}, ${type}>`;
16713
16724
  }
16714
16725
  };
16726
+ FromPropertySignature = class extends OptionalType {
16727
+ isReadonly;
16728
+ fromKey;
16729
+ constructor(type, isOptional, isReadonly, annotations, fromKey) {
16730
+ super(type, isOptional, annotations);
16731
+ this.isReadonly = isReadonly;
16732
+ this.fromKey = fromKey;
16733
+ }
16734
+ };
16715
16735
  ToPropertySignature = class extends OptionalType {
16716
16736
  isReadonly;
16717
16737
  defaultValue;
@@ -16802,10 +16822,80 @@ var init_Schema = __esmMin((() => {
16802
16822
  case "PropertySignatureTransformation": return makePropertySignature(new PropertySignatureTransformation(ast.from, new ToPropertySignature(ast.to.type, ast.to.isOptional, ast.to.isReadonly, ast.to.annotations, defaultValue), ast.decode, ast.encode));
16803
16823
  }
16804
16824
  });
16825
+ optionalToRequired = (from, to, options) => makePropertySignature(new PropertySignatureTransformation(new FromPropertySignature(from.ast, true, true, {}, void 0), new ToPropertySignature(to.ast, false, true, {}, void 0), (o) => some(options.decode(o)), flatMap$9(options.encode)));
16826
+ optionalToOptional = (from, to, options) => makePropertySignature(new PropertySignatureTransformation(new FromPropertySignature(from.ast, true, true, {}, void 0), new ToPropertySignature(to.ast, true, true, {}, void 0), options.decode, options.encode));
16827
+ optionalPropertySignatureAST = (self, options) => {
16828
+ const isExact = options?.exact;
16829
+ const defaultValue = options?.default;
16830
+ const isNullable = options?.nullable;
16831
+ const asOption = options?.as == "Option";
16832
+ const asOptionEncode = options?.onNoneEncoding ? orElse$3(options.onNoneEncoding) : identity$1;
16833
+ if (isExact) if (defaultValue) if (isNullable) return withConstructorDefault(optionalToRequired(NullOr(self), typeSchema(self), {
16834
+ decode: match$8({
16835
+ onNone: defaultValue,
16836
+ onSome: (a) => a === null ? defaultValue() : a
16837
+ }),
16838
+ encode: some
16839
+ }), defaultValue).ast;
16840
+ else return withConstructorDefault(optionalToRequired(self, typeSchema(self), {
16841
+ decode: match$8({
16842
+ onNone: defaultValue,
16843
+ onSome: identity$1
16844
+ }),
16845
+ encode: some
16846
+ }), defaultValue).ast;
16847
+ else if (asOption) {
16848
+ const to = OptionFromSelf_(typeSchema(self));
16849
+ if (isNullable) return optionalToRequired(NullOr(self), to, {
16850
+ decode: filter$3(isNotNull),
16851
+ encode: asOptionEncode
16852
+ }).ast;
16853
+ else return optionalToRequired(self, to, {
16854
+ decode: identity$1,
16855
+ encode: identity$1
16856
+ }).ast;
16857
+ } else if (isNullable) return optionalToOptional(NullOr(self), typeSchema(self), {
16858
+ decode: filter$3(isNotNull),
16859
+ encode: identity$1
16860
+ }).ast;
16861
+ else return new PropertySignatureDeclaration(self.ast, true, true, {}, void 0);
16862
+ else if (defaultValue) if (isNullable) return withConstructorDefault(optionalToRequired(NullishOr(self), typeSchema(self), {
16863
+ decode: match$8({
16864
+ onNone: defaultValue,
16865
+ onSome: (a) => a == null ? defaultValue() : a
16866
+ }),
16867
+ encode: some
16868
+ }), defaultValue).ast;
16869
+ else return withConstructorDefault(optionalToRequired(UndefinedOr(self), typeSchema(self), {
16870
+ decode: match$8({
16871
+ onNone: defaultValue,
16872
+ onSome: (a) => a === void 0 ? defaultValue() : a
16873
+ }),
16874
+ encode: some
16875
+ }), defaultValue).ast;
16876
+ else if (asOption) {
16877
+ const to = OptionFromSelf_(typeSchema(self));
16878
+ if (isNullable) return optionalToRequired(NullishOr(self), to, {
16879
+ decode: filter$3((a) => a != null),
16880
+ encode: asOptionEncode
16881
+ }).ast;
16882
+ else return optionalToRequired(UndefinedOr(self), to, {
16883
+ decode: filter$3(isNotUndefined),
16884
+ encode: asOptionEncode
16885
+ }).ast;
16886
+ } else if (isNullable) return optionalToOptional(NullishOr(self), UndefinedOr(typeSchema(self)), {
16887
+ decode: filter$3(isNotNull),
16888
+ encode: identity$1
16889
+ }).ast;
16890
+ else return new PropertySignatureDeclaration(UndefinedOr(self).ast, true, true, {}, void 0);
16891
+ };
16805
16892
  optional$2 = (self) => {
16806
16893
  const ast = self.ast === undefinedKeyword || self.ast === neverKeyword ? undefinedKeyword : UndefinedOr(self).ast;
16807
16894
  return new PropertySignatureWithFromImpl(new PropertySignatureDeclaration(ast, true, true, {}, void 0), self);
16808
16895
  };
16896
+ optionalWith = /*#__PURE__*/ dual((args) => isSchema(args[0]), (self, options) => {
16897
+ return new PropertySignatureWithFromImpl(optionalPropertySignatureAST(self, options), self);
16898
+ });
16809
16899
  preserveMissingMessageAnnotation = /*#__PURE__*/ pickAnnotations([MissingMessageAnnotationId]);
16810
16900
  getDefaultTypeLiteralAST = (fields, records) => {
16811
16901
  const ownKeys = Reflect.ownKeys(fields);
@@ -16847,7 +16937,7 @@ var init_Schema = __esmMin((() => {
16847
16937
  const issFrom = [];
16848
16938
  const issTo = [];
16849
16939
  for (const r of records) {
16850
- const { indexSignatures, propertySignatures } = record(r.key.ast, r.value.ast);
16940
+ const { indexSignatures, propertySignatures } = record$1(r.key.ast, r.value.ast);
16851
16941
  propertySignatures.forEach((ps) => {
16852
16942
  from.push(ps);
16853
16943
  to.push(new PropertySignature(ps.name, typeAST(ps.type), ps.isOptional, ps.isReadonly, ps.annotations));
@@ -16862,7 +16952,7 @@ var init_Schema = __esmMin((() => {
16862
16952
  }
16863
16953
  const iss = [];
16864
16954
  for (const r of records) {
16865
- const { indexSignatures, propertySignatures } = record(r.key.ast, r.value.ast);
16955
+ const { indexSignatures, propertySignatures } = record$1(r.key.ast, r.value.ast);
16866
16956
  propertySignatures.forEach((ps) => pss.push(ps));
16867
16957
  indexSignatures.forEach((is) => iss.push(is));
16868
16958
  }
@@ -21831,15 +21921,15 @@ const openQuestions = (text) => ctx().openQuestions(text);
21831
21921
  //#endregion
21832
21922
  //#region src/flows/scripts.ts
21833
21923
  /** POSIX single-quoting: `value` survives the shell as one word, verbatim. */
21834
- const quote = (value) => `'${value.replace(/'/g, "'\\''")}'`;
21835
- const removal = (paths) => paths.length === 0 ? [] : [`rm -rf -- ${paths.map(quote).join(" ")}`];
21924
+ const quote$1 = (value) => `'${value.replace(/'/g, "'\\''")}'`;
21925
+ const removal = (paths) => paths.length === 0 ? [] : [`rm -rf -- ${paths.map(quote$1).join(" ")}`];
21836
21926
  /**
21837
21927
  * Run `command` and keep its outcome in the tree: on failure its output (or
21838
21928
  * a note that there was none) lands in `report`, on success `report` goes.
21839
21929
  */
21840
21930
  const checkScript = (command, options) => {
21841
- const report = quote(options.report);
21842
- const output = quote(`${options.report}.output`);
21931
+ const report = quote$1(options.report);
21932
+ const output = quote$1(`${options.report}.output`);
21843
21933
  return [
21844
21934
  "#!/usr/bin/env sh",
21845
21935
  "set +e",
@@ -21857,7 +21947,7 @@ const checkScript = (command, options) => {
21857
21947
  ` rm -f ${output}`,
21858
21948
  ` printf 'the test command failed with exit code %s and produced no output.' "$code" > ${report}`,
21859
21949
  " fi",
21860
- ` printf '\\n<!-- gtd check %s -->\\n' ${quote(options.stamp)} >> ${report}`,
21950
+ ` printf '\\n<!-- gtd check %s -->\\n' ${quote$1(options.stamp)} >> ${report}`,
21861
21951
  "else",
21862
21952
  ` rm -f ${output} ${report}`,
21863
21953
  ...removal(options.sweepOnGreen ?? []).map((line) => ` ${line}`),
@@ -21865,26 +21955,22 @@ const checkScript = (command, options) => {
21865
21955
  ""
21866
21956
  ].join("\n");
21867
21957
  };
21868
- /**
21869
- * Reverse-apply `commit`'s changes to the working tree. A failure — the
21870
- * patch no longer applies — writes `failure` and git's error to `report`,
21871
- * since a failed revert and one with nothing to undo both leave the tree clean.
21872
- */
21873
- const revertScript = (commit, report, failure) => {
21874
- const range = `${quote(`${commit}^`)} ${quote(commit)}`;
21875
- const error = quote(`${report}.output`);
21958
+ const patchScript = (commit, report, failure, reverse, exclude = []) => {
21959
+ const paths = exclude.length === 0 ? "" : ` . ${exclude.map((p) => quote$1(`:(exclude,literal)${p}`)).join(" ")}`;
21960
+ const range = `${quote$1(`${commit}^`)} ${quote$1(commit)}`;
21961
+ const error = quote$1(`${report}.output`);
21876
21962
  return [
21877
21963
  "#!/usr/bin/env sh",
21878
21964
  "set +e",
21879
- `if ! git diff --quiet ${range} --; then`,
21880
- ` mkdir -p "$(dirname ${quote(report)})"`,
21881
- ` git diff --binary ${range} -- | git apply -R 2> ${error}`,
21965
+ `if ! git diff --quiet ${range} --${paths}; then`,
21966
+ ` mkdir -p "$(dirname ${quote$1(report)})"`,
21967
+ ` git diff --binary --no-color --no-ext-diff --no-textconv --src-prefix=a/ --dst-prefix=b/ ${range} --${paths} | git apply --whitespace=nowarn${reverse ? " -R" : ""} 2> ${error}`,
21882
21968
  " code=$?",
21883
21969
  " if [ \"$code\" -ne 0 ]; then",
21884
21970
  " {",
21885
- ` printf '%s\\n\\n' ${quote(failure)}`,
21886
- ` if [ -s ${error} ]; then cat ${error}; else printf 'Reverting it exited %s and produced no output.\\n' "$code"; fi`,
21887
- ` } > ${quote(report)}`,
21971
+ ` printf '%s\\n\\n' ${quote$1(failure)}`,
21972
+ ` if [ -s ${error} ]; then cat ${error}; else printf '${reverse ? "Reverting" : "Applying"} it exited %s and produced no output.\\n' "$code"; fi`,
21973
+ ` } > ${quote$1(report)}`,
21888
21974
  " fi",
21889
21975
  ` rm -f ${error}`,
21890
21976
  "fi",
@@ -21892,17 +21978,25 @@ const revertScript = (commit, report, failure) => {
21892
21978
  ].join("\n");
21893
21979
  };
21894
21980
  /**
21981
+ * Reverse-apply `commit`'s changes to the working tree. A failure — the
21982
+ * patch no longer applies — writes `failure` and git's error to `report`,
21983
+ * since a failed revert and one with nothing to undo both leave the tree clean.
21984
+ */
21985
+ const revertScript = (commit, report, failure) => patchScript(commit, report, failure, true);
21986
+ /** Apply `commit`'s changes outside `exclude` to the working tree; a failure is reported as `revertScript`'s is. */
21987
+ const applyScript = (commit, report, failure, exclude = []) => patchScript(commit, report, failure, false, exclude);
21988
+ /**
21895
21989
  * Undo the edits one commit made to `paths`: each goes back to its content
21896
21990
  * before `commit` — or away, when `commit` added it — but only while it is
21897
21991
  * still exactly as `commit` left it. A path changed since is left alone.
21898
21992
  */
21899
21993
  const restoreScript = (commit, paths, source) => {
21900
- const unchanged = (path) => `git diff --quiet ${quote(commit)} -- ${quote(path)}`;
21994
+ const unchanged = (path) => `git diff --quiet ${quote$1(commit)} -- ${quote$1(path)}`;
21901
21995
  return [
21902
21996
  "#!/usr/bin/env sh",
21903
21997
  "set +e",
21904
- ...paths.restore.map((path) => `${unchanged(path)} && git restore --source=${quote(source ?? `${commit}~1`)} --worktree -- ${quote(path)}`),
21905
- ...paths.remove.map((path) => `${unchanged(path)} && rm -f -- ${quote(path)}`),
21998
+ ...paths.restore.map((path) => `${unchanged(path)} && git restore --source=${quote$1(source ?? `${commit}~1`)} --worktree -- ${quote$1(path)}`),
21999
+ ...paths.remove.map((path) => `${unchanged(path)} && rm -f -- ${quote$1(path)}`),
21906
22000
  ""
21907
22001
  ].join("\n");
21908
22002
  };
@@ -21916,15 +22010,15 @@ const removeScript = (paths) => [
21916
22010
  const moveScript = (from, to) => [
21917
22011
  "#!/usr/bin/env sh",
21918
22012
  "set -e",
21919
- `mkdir -p "$(dirname ${quote(to)})"`,
21920
- `mv -- ${quote(from)} ${quote(to)}`,
22013
+ `mkdir -p "$(dirname ${quote$1(to)})"`,
22014
+ `mv -- ${quote$1(from)} ${quote$1(to)}`,
21921
22015
  ""
21922
22016
  ].join("\n");
21923
22017
  /** Write `path` as committed at `ref` to `to`. */
21924
22018
  const showScript = (ref, path, to) => [
21925
22019
  "#!/usr/bin/env sh",
21926
22020
  "set -e",
21927
- `git show ${quote(`${ref}:${path}`)} > ${quote(to)}`,
22021
+ `git show ${quote$1(`${ref}:${path}`)} > ${quote$1(to)}`,
21928
22022
  ""
21929
22023
  ].join("\n");
21930
22024
  //#endregion
@@ -22259,6 +22353,7 @@ var flows_exports = /* @__PURE__ */ __exportAll({
22259
22353
  accessFor: () => accessFor,
22260
22354
  agent: () => agent,
22261
22355
  answered: () => answered,
22356
+ applyScript: () => applyScript,
22262
22357
  changes: () => changes,
22263
22358
  changesSince: () => changesSince,
22264
22359
  check: () => check,
@@ -22286,7 +22381,7 @@ var flows_exports = /* @__PURE__ */ __exportAll({
22286
22381
  numeric: () => numeric,
22287
22382
  openQuestions: () => openQuestions,
22288
22383
  protocolMismatch: () => protocolMismatch,
22289
- quote: () => quote,
22384
+ quote: () => quote$1,
22290
22385
  read: () => read,
22291
22386
  refuse: () => refuse,
22292
22387
  removeScript: () => removeScript,
@@ -37370,6 +37465,7 @@ var init_BeatStatus = __esmMin((() => {
37370
37465
  initial,
37371
37466
  model: rendered.model,
37372
37467
  system: rendered.system !== void 0 && rendered.system !== "" ? rendered.system : void 0,
37468
+ context: rendered.context,
37373
37469
  log,
37374
37470
  state: rendered.state,
37375
37471
  actor: rendered.actor,
@@ -37428,6 +37524,7 @@ var init_BeatDocument = __esmMin((() => {
37428
37524
  session,
37429
37525
  model: status.model,
37430
37526
  system: status.system,
37527
+ context: status.context,
37431
37528
  validate,
37432
37529
  log: status.log,
37433
37530
  state: status.state,
@@ -37669,6 +37766,91 @@ var init_Slices = __esmMin((() => {
37669
37766
  renderSlicesJson = (document) => JSON.stringify(document) + "\n";
37670
37767
  }));
37671
37768
  //#endregion
37769
+ //#region src/wire/ContextLimit.ts
37770
+ var DEFAULT_CONTEXT_LIMIT, DEFAULT_WINDOW, LONG_WINDOW, contextWindowFor, parseContextWindow, CONTEXT_STOP_FILE_ENV, contextShapeFault, contextLimitFault, record, count, usageTokens, transcriptTokens, CONTEXT_LIMIT_MARKER, percent, contextHook, hookOutput;
37771
+ var init_ContextLimit = __esmMin((() => {
37772
+ DEFAULT_CONTEXT_LIMIT = {
37773
+ soft: .6,
37774
+ hard: .7
37775
+ };
37776
+ DEFAULT_WINDOW = 2e5;
37777
+ LONG_WINDOW = 1e6;
37778
+ contextWindowFor = (model) => model !== void 0 && /\[1m\]/i.test(model) ? LONG_WINDOW : DEFAULT_WINDOW;
37779
+ parseContextWindow = (raw) => {
37780
+ const n = Number(raw);
37781
+ return raw !== void 0 && raw.trim() !== "" && Number.isInteger(n) && n > 0 ? n : void 0;
37782
+ };
37783
+ CONTEXT_STOP_FILE_ENV = "GTD_LOOP_CONTEXT_STOP_FILE";
37784
+ contextShapeFault = (limit) => {
37785
+ if (typeof limit !== "object" || limit === null || Array.isArray(limit)) return `context must be an object { soft, hard }, got ${JSON.stringify(limit)}`;
37786
+ const unknown = Object.keys(limit).filter((key) => key !== "soft" && key !== "hard");
37787
+ if (unknown.length > 0) return `unknown key(s) ${unknown.join(", ")} in context`;
37788
+ const shares = limit;
37789
+ for (const key of ["soft", "hard"]) {
37790
+ const value = shares[key];
37791
+ if (value === void 0) continue;
37792
+ if (typeof value !== "number" || !(value > 0 && value <= 1)) return `context.${key} must be a share of the context window in (0, 1], got ${JSON.stringify(value)}`;
37793
+ }
37794
+ };
37795
+ contextLimitFault = (limit) => {
37796
+ const shape = contextShapeFault(limit);
37797
+ if (shape !== void 0) return shape;
37798
+ const shares = limit;
37799
+ const soft = shares.soft ?? DEFAULT_CONTEXT_LIMIT.soft;
37800
+ const hard = shares.hard ?? DEFAULT_CONTEXT_LIMIT.hard;
37801
+ return soft < hard ? void 0 : `context.soft (${soft}) must be below context.hard (${hard})`;
37802
+ };
37803
+ record = (value) => typeof value === "object" && value !== null ? value : void 0;
37804
+ count = (value) => typeof value === "number" && value > 0 ? value : 0;
37805
+ usageTokens = (usage) => {
37806
+ const u = record(usage);
37807
+ if (u === void 0) return void 0;
37808
+ return count(u["input_tokens"]) + count(u["cache_read_input_tokens"]) + count(u["cache_creation_input_tokens"]);
37809
+ };
37810
+ transcriptTokens = (transcript) => {
37811
+ const lines = transcript.split("\n");
37812
+ for (let i = lines.length - 1; i >= 0; i--) {
37813
+ const line = lines[i].trim();
37814
+ if (line === "") continue;
37815
+ let entry;
37816
+ try {
37817
+ entry = JSON.parse(line);
37818
+ } catch {
37819
+ continue;
37820
+ }
37821
+ if (record(entry)?.["type"] !== "assistant") continue;
37822
+ const tokens = usageTokens(record(record(entry)?.["message"])?.["usage"]);
37823
+ if (tokens !== void 0) return tokens;
37824
+ }
37825
+ };
37826
+ CONTEXT_LIMIT_MARKER = "gtd: context limit";
37827
+ percent = (share) => `${Math.round(share * 100)}%`;
37828
+ contextHook = (input) => {
37829
+ const { event, tokens, window, limit } = input;
37830
+ if (event === "PreCompact") return {
37831
+ kind: "hard",
37832
+ stopReason: `${CONTEXT_LIMIT_MARKER}: the context is about to be compacted — the run is too big for one turn`
37833
+ };
37834
+ if (tokens === void 0) return void 0;
37835
+ const used = `${tokens} of ${window} tokens`;
37836
+ if (tokens >= limit.hard * window) return {
37837
+ kind: "hard",
37838
+ stopReason: `${CONTEXT_LIMIT_MARKER}: ${used}, past the ${percent(limit.hard)} hard limit`
37839
+ };
37840
+ if (tokens >= limit.soft * window) return {
37841
+ kind: "soft",
37842
+ reason: `Context limit: ${used} used, past the ${percent(limit.soft)} soft limit. Stop working now. Do not start anything new; end your turn with a short handoff note as your answer: what you touched, and what was next.`
37843
+ };
37844
+ };
37845
+ hookOutput = (decision) => decision.kind === "soft" ? {
37846
+ decision: "block",
37847
+ reason: decision.reason
37848
+ } : {
37849
+ continue: false,
37850
+ stopReason: decision.stopReason
37851
+ };
37852
+ }));
37853
+ //#endregion
37672
37854
  //#region src/wire/index.ts
37673
37855
  var init_wire = __esmMin((() => {
37674
37856
  init_constants();
@@ -37684,6 +37866,7 @@ var init_wire = __esmMin((() => {
37684
37866
  init_Sessions();
37685
37867
  init_Glob();
37686
37868
  init_Slices();
37869
+ init_ContextLimit();
37687
37870
  }));
37688
37871
  //#endregion
37689
37872
  //#region src/replay/RefName.ts
@@ -38013,6 +38196,15 @@ const budgeted = (evidence, budgetBytes) => {
38013
38196
  truncated
38014
38197
  };
38015
38198
  };
38199
+ const scopeLabel = (chain) => {
38200
+ const path = chain.flatMap((o) => o.name === void 0 ? [] : [o.name]).join(".");
38201
+ if (chain.at(-1)?.name !== void 0) return `scope "${path}"`;
38202
+ return path === "" ? "an unnamed scope" : `an unnamed scope inside "${path}"`;
38203
+ };
38204
+ const inheritedContext = (layers) => layers.reduce((merged, layer) => layer === void 0 ? merged : {
38205
+ ...merged,
38206
+ ...Object.fromEntries(Object.entries(layer).filter(([, value]) => value !== void 0))
38207
+ }, void 0);
38016
38208
  const sameSkills = (a, b) => (a?.length ?? 0) === (b?.length ?? 0) && (a ?? []).every((skill, i) => skill === b?.[i]);
38017
38209
  const samePersona = (a, b) => a.model === b.model && a.system === b.system && sameSkills(a.skills, b.skills) && sameAccess(a.access, b.access);
38018
38210
  /**
@@ -38032,6 +38224,7 @@ const KNOWN_OPTIONS = {
38032
38224
  ...STEERING_OPTIONS,
38033
38225
  "model",
38034
38226
  "system",
38227
+ "context",
38035
38228
  "allowEmpty",
38036
38229
  "keep"
38037
38230
  ]),
@@ -38146,11 +38339,13 @@ const replay = async (input) => {
38146
38339
  if (s.model !== void 0) persona.model = s.model;
38147
38340
  if (s.system !== void 0) persona.system = s.system;
38148
38341
  }
38342
+ const context = inheritedContext([...scopes.map((s) => s.context), request.options.context]);
38149
38343
  return {
38150
38344
  ...request,
38151
38345
  options: {
38152
38346
  ...persona,
38153
- ...request.options
38347
+ ...request.options,
38348
+ ...context === void 0 ? {} : { context }
38154
38349
  }
38155
38350
  };
38156
38351
  }
@@ -38288,10 +38483,26 @@ const replay = async (input) => {
38288
38483
  });
38289
38484
  return landPending(step, input.pending);
38290
38485
  };
38486
+ const scopeContextFault = () => {
38487
+ for (const [i, s] of scopes.entries()) {
38488
+ const fault = s.context === void 0 ? void 0 : contextShapeFault(s.context);
38489
+ if (fault !== void 0) return `the context of ${scopeLabel(scopes.slice(0, i + 1))}: ${fault}`;
38490
+ }
38491
+ };
38492
+ const resolvedContextFault = (request) => {
38493
+ const own = request.options.context;
38494
+ const layered = scopeContextFault() ?? (own === void 0 ? void 0 : contextShapeFault(own));
38495
+ if (layered !== void 0) return layered;
38496
+ const resolved = resolve(request);
38497
+ const context = resolved.kind === "agent" ? resolved.options.context : void 0;
38498
+ return context === void 0 ? void 0 : contextLimitFault(context);
38499
+ };
38291
38500
  const requestFault = (request) => {
38292
38501
  if (request.kind === "run" && typeof request.body !== "string") return `gtd: step "${scoped(request.name)}": a run() body is a shell script string — decide in flow code, then render the script (see checkScript and friends)`;
38293
38502
  if (request.kind !== "agent") return void 0;
38294
38503
  const name = scoped(request.name);
38504
+ const contextFault = resolvedContextFault(request);
38505
+ if (contextFault !== void 0) return `gtd: step "${name}": ${contextFault}`;
38295
38506
  if (request.options.keep === true) {
38296
38507
  if (request.options.allowEmpty === true) return `gtd: step "${name}": keep: true cannot be combined with allowEmpty — a turn that changes nothing has nothing to keep`;
38297
38508
  const refFault = name.includes("/") ? "it contains \"/\"" : refNameFault(name);
@@ -39912,6 +40123,10 @@ const commonOf = (step) => {
39912
40123
  ...optional$1("mode", options.mode)
39913
40124
  };
39914
40125
  };
40126
+ const stepContext = (context) => context === void 0 ? void 0 : {
40127
+ ...optional$1("soft", context.soft),
40128
+ ...optional$1("hard", context.hard)
40129
+ };
39915
40130
  const promptDef = (common, request, skills, access) => ({
39916
40131
  ...common,
39917
40132
  kind: "prompt",
@@ -39920,6 +40135,7 @@ const promptDef = (common, request, skills, access) => ({
39920
40135
  ...optional$1("system", request.options.system),
39921
40136
  ...optional$1("allowEmpty", request.options.allowEmpty),
39922
40137
  ...optional$1("keep", request.options.keep),
40138
+ ...optional$1("context", stepContext(request.options.context)),
39923
40139
  ...optional$1("skills", skills),
39924
40140
  ...optional$1("access", access)
39925
40141
  });
@@ -40005,7 +40221,8 @@ const hintsOf = (def) => ({
40005
40221
  ...optional$1("system", def.system),
40006
40222
  ...optional$1("mode", def.mode),
40007
40223
  ...optional$1("skills", def.skills),
40008
- ...optional$1("access", def.access)
40224
+ ...optional$1("access", def.access),
40225
+ ...optional$1("context", def.context)
40009
40226
  });
40010
40227
  const normalizeStatus = (raw) => raw === "A" ? "A" : raw === "D" ? "D" : "M";
40011
40228
  /** Per-model token totals, highest-cost first (ties broken by model name). */
@@ -41287,6 +41504,17 @@ const readStdin = () => tryPromise({
41287
41504
  },
41288
41505
  catch: (e) => e instanceof Error ? e : new Error(String(e))
41289
41506
  });
41507
+ /** Stdout is written once: a hook reads one JSON document, or none. */
41508
+ const runContextHookCommand = (command, out) => gen(function* () {
41509
+ const tools = yield* ToolCommands;
41510
+ const input = yield* readStdin();
41511
+ out.write(yield* tools.contextHook({
41512
+ input,
41513
+ ...command.soft !== void 0 ? { soft: command.soft } : {},
41514
+ ...command.hard !== void 0 ? { hard: command.hard } : {},
41515
+ ...command.window !== void 0 ? { window: command.window } : {}
41516
+ }));
41517
+ });
41290
41518
  /** Stdout is written once, after the answerer resolves — a rejection leaves it empty. */
41291
41519
  const runJudgeRunCommand = (command, out) => gen(function* () {
41292
41520
  const host = yield* Host;
@@ -41746,6 +41974,7 @@ const needsOf = (kind) => {
41746
41974
  case "lsp":
41747
41975
  case "install":
41748
41976
  case "loop":
41977
+ case "contextHook":
41749
41978
  case "judgeRun":
41750
41979
  case "strays": return "none";
41751
41980
  case "check":
@@ -41785,6 +42014,7 @@ const dispatchVoidCommand = (command, json, out) => {
41785
42014
  case "uncheck": return runUncheckCommand(command.file);
41786
42015
  case "install": return runInstallCommand(out);
41787
42016
  case "loop": return runLoopCommand();
42017
+ case "contextHook": return runContextHookCommand(command, out);
41788
42018
  case "summary": return runSummaryCommand(out, command.slice);
41789
42019
  case "base": return runBaseCommand(out);
41790
42020
  case "judge": return runJudgeCommand(json, out);
@@ -41828,6 +42058,10 @@ const nonNegativeNumber = (raw, flag) => {
41828
42058
  if (raw.trim() === "" || !Number.isFinite(n) || n < 0) return left(`gtd: ${flag} must be a non-negative number — got "${raw}"`);
41829
42059
  return right(n);
41830
42060
  };
42061
+ const share = (raw, flag) => {
42062
+ const n = Number(raw);
42063
+ return raw.trim() !== "" && n > 0 && n <= 1 ? right(n) : left(`gtd: ${flag} must be a share of the context window in (0, 1] — got "${raw}"`);
42064
+ };
41831
42065
  const FLAGS = [
41832
42066
  {
41833
42067
  name: "--json",
@@ -41955,6 +42189,39 @@ const FLAGS = [
41955
42189
  "GTD_JUDGE_MODEL, then judge.json, then sonnet"
41956
42190
  ]
41957
42191
  },
42192
+ {
42193
+ name: "--soft",
42194
+ arity: 1,
42195
+ repeatable: false,
42196
+ scope: (kind) => kind === "contextHook",
42197
+ decode: ([raw]) => share(raw ?? "", "--soft"),
42198
+ scopeError: "gtd: --soft is only valid for `gtd context-hook`",
42199
+ valueHint: "<share>",
42200
+ help: ["(gtd context-hook only) the soft limit; default 0.6"]
42201
+ },
42202
+ {
42203
+ name: "--hard",
42204
+ arity: 1,
42205
+ repeatable: false,
42206
+ scope: (kind) => kind === "contextHook",
42207
+ decode: ([raw]) => share(raw ?? "", "--hard"),
42208
+ scopeError: "gtd: --hard is only valid for `gtd context-hook`",
42209
+ valueHint: "<share>",
42210
+ help: ["(gtd context-hook only) the hard limit; default 0.7"]
42211
+ },
42212
+ {
42213
+ name: "--window",
42214
+ arity: 1,
42215
+ repeatable: false,
42216
+ scope: (kind) => kind === "contextHook",
42217
+ decode: ([raw]) => {
42218
+ const window = parseContextWindow(raw);
42219
+ return window !== void 0 ? right(window) : left(`gtd: --window must be a positive integer — got "${raw ?? ""}"`);
42220
+ },
42221
+ scopeError: "gtd: --window is only valid for `gtd context-hook`",
42222
+ valueHint: "<tokens>",
42223
+ help: ["(gtd context-hook only) the model's context window in", "tokens; default 200000"]
42224
+ },
41958
42225
  {
41959
42226
  name: "--slice",
41960
42227
  arity: 1,
@@ -42261,6 +42528,22 @@ const COMMAND_ROWS = [
42261
42528
  "~/.config/gtd/loop.json); see docs/driver.md"
42262
42529
  ]
42263
42530
  },
42531
+ {
42532
+ token: "context-hook",
42533
+ kind: "contextHook",
42534
+ arity: "none",
42535
+ details: [
42536
+ "A Claude Code PostToolUse and PreCompact hook that stops an",
42537
+ "agent turn at a share of its context window. Reads the",
42538
+ "hook's input on stdin and the context the transcript's",
42539
+ "latest assistant message used. Past --soft it blocks with a",
42540
+ "request to end the turn with a handoff note; past --hard,",
42541
+ "or at PreCompact, it stops the turn with a stopReason",
42542
+ "starting \"gtd: context limit\". Prints nothing otherwise,",
42543
+ "and for a subagent's tool call. Always exits 0 on valid",
42544
+ "input; see docs/driver.md"
42545
+ ]
42546
+ },
42264
42547
  {
42265
42548
  token: "summary",
42266
42549
  kind: "summary",
@@ -42739,6 +43022,17 @@ const parseArgv = (argv) => {
42739
43022
  verbose
42740
43023
  };
42741
43024
  }
43025
+ if (kind === "contextHook") return {
43026
+ kind: "command",
43027
+ command: {
43028
+ kind: "contextHook",
43029
+ ...bag["--soft"] !== void 0 ? { soft: bag["--soft"] } : {},
43030
+ ...bag["--hard"] !== void 0 ? { hard: bag["--hard"] } : {},
43031
+ ...bag["--window"] !== void 0 ? { window: bag["--window"] } : {}
43032
+ },
43033
+ json,
43034
+ verbose
43035
+ };
42742
43036
  if (kind === "summary") return {
42743
43037
  kind: "command",
42744
43038
  command: {
@@ -58417,8 +58711,9 @@ var init_lsp = __esmMin((() => {
58417
58711
  }));
58418
58712
  //#endregion
58419
58713
  //#region src/loop/Agent.ts
58420
- var optional, parseJson, field, costOf, claude, pi, agentCli;
58714
+ var optional, parseJson, field$1, costOf, claude, hookSettings, pi, agentCli;
58421
58715
  var init_Agent = __esmMin((() => {
58716
+ init_wire();
58422
58717
  optional = (flag, value) => value === void 0 || value === "" ? [] : [flag, value];
58423
58718
  parseJson = (text) => {
58424
58719
  try {
@@ -58427,7 +58722,7 @@ var init_Agent = __esmMin((() => {
58427
58722
  return;
58428
58723
  }
58429
58724
  };
58430
- field = (value, key) => typeof value === "object" && value !== null ? value[key] : void 0;
58725
+ field$1 = (value, key) => typeof value === "object" && value !== null ? value[key] : void 0;
58431
58726
  costOf = (value) => typeof value === "number" && value > 0 ? value : 0;
58432
58727
  claude = {
58433
58728
  command: "claude",
@@ -58437,19 +58732,45 @@ var init_Agent = __esmMin((() => {
58437
58732
  turn.sessionId,
58438
58733
  ...optional("--model", turn.model),
58439
58734
  ...optional("--system-prompt", turn.system),
58735
+ "--settings",
58736
+ JSON.stringify(hookSettings(turn.hook)),
58440
58737
  "--output-format",
58441
- "json",
58738
+ "stream-json",
58739
+ "--verbose",
58442
58740
  "--dangerously-skip-permissions"
58443
58741
  ],
58444
58742
  read: (exit) => {
58445
- const envelope = parseJson(exit.stdout);
58446
- const result = field(envelope, "result");
58743
+ const lines = exit.stdout.split("\n");
58744
+ let envelope;
58745
+ for (let i = lines.length - 1; i >= 0 && envelope === void 0; i--) {
58746
+ const event = parseJson(lines[i]);
58747
+ if (field$1(event, "type") === "result" || field$1(event, "result") !== void 0) envelope = event;
58748
+ }
58749
+ const result = field$1(envelope, "result");
58750
+ const hookStopped = field$1(envelope, "terminal_reason") === "hook_stopped";
58447
58751
  return {
58448
- ok: exit.code === 0 && field(envelope, "is_error") !== true,
58449
- text: typeof result === "string" ? result : exit.stdout,
58450
- cost: costOf(field(envelope, "total_cost_usd"))
58752
+ ok: exit.code === 0 && field$1(envelope, "is_error") !== true && !hookStopped,
58753
+ text: typeof result === "string" ? result : lines.slice(-5).join("\n"),
58754
+ cost: costOf(field$1(envelope, "total_cost_usd")),
58755
+ hookStopped
58451
58756
  };
58452
- }
58757
+ },
58758
+ contextTokens: (line) => {
58759
+ const event = parseJson(line);
58760
+ if (field$1(event, "type") !== "assistant") return void 0;
58761
+ if (field$1(event, "parent_tool_use_id") != null) return void 0;
58762
+ return usageTokens(field$1(field$1(event, "message"), "usage"));
58763
+ }
58764
+ };
58765
+ hookSettings = (command) => {
58766
+ const hook = [{ hooks: [{
58767
+ type: "command",
58768
+ command
58769
+ }] }];
58770
+ return { hooks: {
58771
+ PostToolUse: hook,
58772
+ PreCompact: hook
58773
+ } };
58453
58774
  };
58454
58775
  pi = {
58455
58776
  command: "pi",
@@ -58465,13 +58786,14 @@ var init_Agent = __esmMin((() => {
58465
58786
  ],
58466
58787
  read: (exit) => {
58467
58788
  const events = exit.stdout.split("\n").map(parseJson).filter((event) => event !== void 0);
58468
- const message = field(events.findLast((event) => field(event, "type") === "message_end" && field(field(event, "message"), "role") === "assistant"), "message");
58469
- const content = field(message, "content");
58470
- const text = (Array.isArray(content) ? content : []).filter((part) => field(part, "type") === "text").map((part) => String(field(part, "text"))).join("\n");
58789
+ const message = field$1(events.findLast((event) => field$1(event, "type") === "message_end" && field$1(field$1(event, "message"), "role") === "assistant"), "message");
58790
+ const content = field$1(message, "content");
58791
+ const text = (Array.isArray(content) ? content : []).filter((part) => field$1(part, "type") === "text").map((part) => String(field$1(part, "text"))).join("\n");
58471
58792
  return {
58472
- ok: exit.code === 0 && !events.some((event) => field(event, "type") === "error"),
58793
+ ok: exit.code === 0 && !events.some((event) => field$1(event, "type") === "error"),
58473
58794
  text: text === "" ? exit.stdout : text,
58474
- cost: costOf(field(field(field(message, "usage"), "cost"), "total"))
58795
+ cost: costOf(field$1(field$1(field$1(message, "usage"), "cost"), "total")),
58796
+ hookStopped: false
58475
58797
  };
58476
58798
  }
58477
58799
  };
@@ -58496,6 +58818,10 @@ var init_Documents = __esmMin((() => {
58496
58818
  })),
58497
58819
  model: optional$2(String$),
58498
58820
  system: optional$2(String$),
58821
+ context: optional$2(Struct({
58822
+ soft: optionalWith(Number$, { exact: true }),
58823
+ hard: optionalWith(Number$, { exact: true })
58824
+ })),
58499
58825
  validate: optional$2(String$),
58500
58826
  label: optional$2(String$),
58501
58827
  judge: optional$2(String$)
@@ -58543,9 +58869,30 @@ var init_Host = __esmMin((() => {
58543
58869
  ],
58544
58870
  detached: true
58545
58871
  });
58872
+ const killGroup = (signal) => {
58873
+ try {
58874
+ if (child.pid !== void 0) process.kill(-child.pid, signal);
58875
+ } catch {}
58876
+ };
58546
58877
  let stdout = "";
58547
58878
  let stderr = "";
58548
- child.stdout?.setEncoding("utf-8").on("data", (chunk) => stdout += chunk);
58879
+ let stopped = false;
58880
+ let stopForce;
58881
+ let scanned = 0;
58882
+ child.stdout?.setEncoding("utf-8").on("data", (chunk) => {
58883
+ stdout += chunk;
58884
+ if (request.stopAt === void 0 || stopped) return;
58885
+ for (let end = stdout.indexOf("\n", scanned); end !== -1; end = stdout.indexOf("\n", scanned)) {
58886
+ const line = stdout.slice(scanned, end);
58887
+ scanned = end + 1;
58888
+ if (request.stopAt(line)) {
58889
+ stopped = true;
58890
+ killGroup("SIGTERM");
58891
+ stopForce = setTimeout(() => killGroup("SIGKILL"), KILL_GRACE_MS);
58892
+ return;
58893
+ }
58894
+ }
58895
+ });
58549
58896
  child.stderr?.setEncoding("utf-8").on("data", (chunk) => stderr += chunk);
58550
58897
  child.stdin?.on("error", () => {});
58551
58898
  child.stdin?.end(request.stdin);
@@ -58558,18 +58905,15 @@ var init_Host = __esmMin((() => {
58558
58905
  };
58559
58906
  child.on("error", (error) => settle(fail$7(/* @__PURE__ */ new Error(`could not run ${request.command}: ${error.message}`))));
58560
58907
  child.on("close", (code, signal) => {
58908
+ clearTimeout(stopForce);
58561
58909
  const exit = code ?? 128 + (signal === null ? 0 : constants.signals[signal] ?? 0);
58562
58910
  settle(succeed$3({
58563
58911
  code: exit,
58564
58912
  stdout,
58565
- stderr
58913
+ stderr,
58914
+ ...stopped ? { stopped: true } : {}
58566
58915
  }));
58567
58916
  });
58568
- const killGroup = (signal) => {
58569
- try {
58570
- if (child.pid !== void 0) process.kill(-child.pid, signal);
58571
- } catch {}
58572
- };
58573
58917
  return async((done) => {
58574
58918
  if (child.exitCode !== null || child.signalCode !== null || child.pid === void 0) {
58575
58919
  killGroup("SIGTERM");
@@ -58613,9 +58957,10 @@ var init_Host = __esmMin((() => {
58613
58957
  }));
58614
58958
  //#endregion
58615
58959
  //#region src/loop/Settings.ts
58616
- var Agent, CLAUDE_MODELS, BUNDLED_HINTS, SettingsFile, settingsPath, MODEL_ENV_PREFIX, modelEnvName, decodeFile, decodeAgent, resolveSettings, resolveModel;
58960
+ var Agent, CLAUDE_MODELS, BUNDLED_HINTS, SettingsFile, settingsPath, MODEL_ENV_PREFIX, modelEnvName, decodeFile, decodeAgent, decodeWindow, resolveSettings, resolveModel;
58617
58961
  var init_Settings = __esmMin((() => {
58618
58962
  init_esm();
58963
+ init_wire();
58619
58964
  Agent = Literal("claude", "pi");
58620
58965
  CLAUDE_MODELS = {
58621
58966
  smart: "opus",
@@ -58627,7 +58972,8 @@ var init_Settings = __esmMin((() => {
58627
58972
  models: optional$2(Record({
58628
58973
  key: String$,
58629
58974
  value: String$
58630
- }))
58975
+ })),
58976
+ contextWindow: optional$2(Int.pipe(positive()))
58631
58977
  });
58632
58978
  settingsPath = (env) => {
58633
58979
  const explicit = env["GTD_LOOP_CONFIG"];
@@ -58649,9 +58995,15 @@ var init_Settings = __esmMin((() => {
58649
58995
  return decodeUnknownEither(SettingsFile, { onExcessProperty: "error" })(json).pipe(mapLeft((error) => `${path}: ${error.message}`));
58650
58996
  };
58651
58997
  decodeAgent = (value) => value === void 0 || value === "" ? right(void 0) : decodeUnknownEither(Agent)(value).pipe(mapLeft(() => `GTD_LOOP_AGENT must be "claude" or "pi" — got "${value}"`));
58998
+ decodeWindow = (value) => {
58999
+ if (value === void 0 || value === "") return right(void 0);
59000
+ const window = parseContextWindow(value);
59001
+ return window !== void 0 ? right(window) : left(`GTD_LOOP_CONTEXT_WINDOW must be a positive integer — got "${value}"`);
59002
+ };
58652
59003
  resolveSettings = (env, file) => gen$2(function* () {
58653
59004
  const fromFile = file === void 0 ? {} : yield* decodeFile(file.path, file.text);
58654
59005
  const envAgent = yield* decodeAgent(env["GTD_LOOP_AGENT"]);
59006
+ const envWindow = yield* decodeWindow(env["GTD_LOOP_CONTEXT_WINDOW"]);
58655
59007
  const envModels = Object.fromEntries(Object.entries(env).filter((entry) => entry[0].startsWith(MODEL_ENV_PREFIX) && entry[1] !== void 0 && entry[1] !== ""));
58656
59008
  const agent = envAgent ?? fromFile.agent ?? "claude";
58657
59009
  return {
@@ -58660,7 +59012,8 @@ var init_Settings = __esmMin((() => {
58660
59012
  ...agent === "claude" ? CLAUDE_MODELS : {},
58661
59013
  ...fromFile.models
58662
59014
  },
58663
- envModels
59015
+ envModels,
59016
+ contextWindow: envWindow ?? fromFile.contextWindow
58664
59017
  };
58665
59018
  });
58666
59019
  resolveModel = (settings, hint) => {
@@ -58670,9 +59023,10 @@ var init_Settings = __esmMin((() => {
58670
59023
  }));
58671
59024
  //#endregion
58672
59025
  //#region src/loop/Loop.ts
58673
- var MAX_VALIDATION_FIXES, FAILURE_LINES, LAND, fromEither, paint, firstLines, loadSettings, gitRoot, gtd, readBeat, clock, header, rest, agentFailed, agentTurn, sessionFlags, validateOnce, promptBeat, runScript, stop, judgeGate, messageBeat, kindBeat, runBeat, costFlags, land, runLoop;
59026
+ var MAX_VALIDATION_FIXES, FAILURE_LINES, LAND, fromEither, paint, firstLines, loadSettings, gitRoot, gtd, readBeat, clock, header, rest, agentFailed, quote, contextGuard, contextStop, agentTurn, sessionFlags, validateOnce, promptBeat, runScript, stop, judgeGate, messageBeat, kindBeat, runBeat, costFlags, land, runLoop;
58674
59027
  var init_Loop = __esmMin((() => {
58675
59028
  init_esm();
59029
+ init_wire();
58676
59030
  init_Agent();
58677
59031
  init_Documents();
58678
59032
  init_Host();
@@ -58725,23 +59079,61 @@ var init_Loop = __esmMin((() => {
58725
59079
  ...exit.stderr.trim() === "" ? [] : [firstLines(exit.stderr)],
58726
59080
  ...result.text.trim() === "" ? [] : [firstLines(result.text)]
58727
59081
  ].join("\n") + "\n");
59082
+ quote = (arg) => `'${arg.replaceAll("'", "'\\''")}'`;
59083
+ contextGuard = (ctx, doc, model) => {
59084
+ const limit = {
59085
+ ...DEFAULT_CONTEXT_LIMIT,
59086
+ ...doc.context
59087
+ };
59088
+ const window = ctx.settings.contextWindow ?? contextWindowFor(model);
59089
+ return {
59090
+ limit,
59091
+ window,
59092
+ hook: [...ctx.gtd, "context-hook"].concat([
59093
+ "--soft",
59094
+ String(limit.soft),
59095
+ "--hard",
59096
+ String(limit.hard),
59097
+ "--window",
59098
+ String(window)
59099
+ ]).map(quote).join(" "),
59100
+ kill: (limit.hard + 1) / 2
59101
+ };
59102
+ };
59103
+ contextStop = (ctx, exit, guard, stopFile) => map$4(ctx.host.readFile(stopFile), (recorded) => {
59104
+ const reason = recorded?.trim().split("\n")[0];
59105
+ if (reason !== void 0 && reason !== "") return reason;
59106
+ if (exit.stopped !== true) return void 0;
59107
+ return `${CONTEXT_LIMIT_MARKER}: the stream passed ${Math.round(guard.kill * 100)}% of ${guard.window} tokens with no hook stopping it`;
59108
+ });
58728
59109
  agentTurn = (ctx, doc, model) => (prompt, sessionFlag) => gen(function* () {
58729
59110
  const cli = agentCli(ctx.settings.agent);
59111
+ const guard = contextGuard(ctx, doc, model);
59112
+ const tokensOf = cli.contextTokens;
59113
+ const stopFile = `${ctx.log}.context-stop`;
59114
+ yield* ctx.host.resetLog(stopFile);
58730
59115
  const exit = yield* ctx.host.spawn({
58731
59116
  command: cli.command,
58732
59117
  args: cli.args({
58733
59118
  sessionFlag,
58734
59119
  sessionId: doc.session?.id ?? "",
58735
59120
  model,
58736
- system: doc.system
59121
+ system: doc.system,
59122
+ hook: guard.hook
58737
59123
  }),
58738
59124
  cwd: ctx.root,
58739
59125
  stdin: prompt,
58740
- env: { CLAUDE_CODE_DISABLE_BACKGROUND_TASKS: "1" },
58741
- output: "capture"
59126
+ env: {
59127
+ CLAUDE_CODE_DISABLE_BACKGROUND_TASKS: "1",
59128
+ [CONTEXT_STOP_FILE_ENV]: stopFile
59129
+ },
59130
+ output: "capture",
59131
+ ...tokensOf === void 0 ? {} : { stopAt: (line) => (tokensOf(line) ?? 0) >= guard.kill * guard.window }
58742
59132
  });
58743
59133
  const result = cli.read(exit);
58744
59134
  yield* ctx.host.appendLog(ctx.log, `${exit.stderr}${result.text}\n`);
59135
+ const why = yield* contextStop(ctx, exit, guard, stopFile);
59136
+ if (why !== void 0) return yield* fail$7(/* @__PURE__ */ new Error(`${why} — the turn at "${doc.state}" was stopped and nothing landed; see ${ctx.log}`));
58745
59137
  if (!result.ok) yield* agentFailed(ctx, exit, result);
58746
59138
  return result;
58747
59139
  });
@@ -58871,15 +59263,66 @@ var init_Loop = __esmMin((() => {
58871
59263
  });
58872
59264
  }));
58873
59265
  //#endregion
59266
+ //#region src/loop/ContextHook.ts
59267
+ var field, limitOf, parseInput, tokensOf, recordStop, runContextHook;
59268
+ var init_ContextHook = __esmMin((() => {
59269
+ init_esm();
59270
+ init_wire();
59271
+ init_Host();
59272
+ field = (value, key) => typeof value === "object" && value !== null ? value[key] : void 0;
59273
+ limitOf = (request) => {
59274
+ const limit = {
59275
+ soft: request.soft ?? DEFAULT_CONTEXT_LIMIT.soft,
59276
+ hard: request.hard ?? DEFAULT_CONTEXT_LIMIT.hard
59277
+ };
59278
+ const fault = contextLimitFault(limit);
59279
+ return fault === void 0 ? succeed$3(limit) : fail$7(/* @__PURE__ */ new Error(`gtd context-hook: ${fault}`));
59280
+ };
59281
+ parseInput = (text) => try_({
59282
+ try: () => JSON.parse(text),
59283
+ catch: () => /* @__PURE__ */ new Error("gtd context-hook: stdin is not a hook's JSON input")
59284
+ }).pipe(map$4((input) => {
59285
+ const event = field(input, "hook_event_name");
59286
+ if (event !== "PostToolUse" && event !== "PreCompact") return void 0;
59287
+ if (field(input, "agent_id") !== void 0) return void 0;
59288
+ const path = field(input, "transcript_path");
59289
+ return {
59290
+ event,
59291
+ transcript: typeof path === "string" && path !== "" ? path : void 0
59292
+ };
59293
+ }));
59294
+ tokensOf = (input) => input.event === "PostToolUse" && input.transcript !== void 0 ? flatMap$4(LoopHost, (host) => host.readFile(input.transcript)).pipe(map$4((text) => text === void 0 ? void 0 : transcriptTokens(text))) : succeed$3(void 0);
59295
+ recordStop = (stopReason) => flatMap$4(LoopHost, (host) => {
59296
+ const file = host.env[CONTEXT_STOP_FILE_ENV];
59297
+ return file === void 0 || file === "" ? _void : host.appendLog(file, `${stopReason}\n`).pipe(catchAllDefect(() => _void));
59298
+ });
59299
+ runContextHook = (request) => gen(function* () {
59300
+ const limit = yield* limitOf(request);
59301
+ const input = yield* parseInput(request.input);
59302
+ if (input === void 0) return "";
59303
+ const decision = contextHook({
59304
+ event: input.event,
59305
+ tokens: yield* tokensOf(input),
59306
+ window: request.window ?? contextWindowFor(void 0),
59307
+ limit
59308
+ });
59309
+ if (decision === void 0) return "";
59310
+ if (decision.kind === "hard") yield* recordStop(decision.stopReason);
59311
+ return `${JSON.stringify(hookOutput(decision))}\n`;
59312
+ });
59313
+ }));
59314
+ //#endregion
58874
59315
  //#region src/loop/index.ts
58875
59316
  var loop_exports = /* @__PURE__ */ __exportAll({
58876
59317
  LoopHost: () => LoopHost,
58877
59318
  liveLoopHost: () => liveLoopHost,
59319
+ runContextHook: () => runContextHook,
58878
59320
  runLoop: () => runLoop
58879
59321
  });
58880
59322
  var init_loop = __esmMin((() => {
58881
59323
  init_Loop();
58882
59324
  init_Host();
59325
+ init_ContextHook();
58883
59326
  }));
58884
59327
  //#endregion
58885
59328
  //#region src/judges/providers/io.ts
@@ -59368,6 +59811,10 @@ const liveToolCommands = effect$1(ToolCommands, gen(function* () {
59368
59811
  const { runLoop, liveLoopHost } = await Promise.resolve().then(() => (init_loop(), loop_exports));
59369
59812
  return () => runLoop({ gtd }).pipe(provide$1(liveLoopHost));
59370
59813
  }),
59814
+ contextHook: (request) => lazy("loop", async () => {
59815
+ const { runContextHook, liveLoopHost } = await Promise.resolve().then(() => (init_loop(), loop_exports));
59816
+ return () => runContextHook(request).pipe(provide$1(liveLoopHost));
59817
+ }),
59371
59818
  judge: async (request) => {
59372
59819
  const { runJudge } = await Promise.resolve().then(() => (init_judges(), judges_exports)).catch((error) => {
59373
59820
  throw loadFailure("judge")(error);