@digitalforgestudios/openclaw-sulcus 6.6.6 → 7.2.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.
package/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ "use strict";
1
2
  var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -35,10 +36,10 @@ module.exports = __toCommonJS(index_exports);
35
36
  var import_node_path = require("node:path");
36
37
  var import_node_fs = require("node:fs");
37
38
  var https = __toESM(require("node:https"));
38
- var http = __toESM(require("node:http"));
39
- var import_node_url = require("node:url");
39
+ var http2 = __toESM(require("node:http"));
40
+ var import_node_url2 = require("node:url");
40
41
 
41
- // node_modules/@sinclair/typebox/build/esm/type/guard/value.mjs
42
+ // ../../node_modules/@sinclair/typebox/build/esm/type/guard/value.mjs
42
43
  var value_exports = {};
43
44
  __export(value_exports, {
44
45
  HasPropertyKey: () => HasPropertyKey,
@@ -107,7 +108,7 @@ function IsUndefined(value) {
107
108
  return value === void 0;
108
109
  }
109
110
 
110
- // node_modules/@sinclair/typebox/build/esm/type/clone/value.mjs
111
+ // ../../node_modules/@sinclair/typebox/build/esm/type/clone/value.mjs
111
112
  function ArrayType(value) {
112
113
  return value.map((value2) => Visit(value2));
113
114
  }
@@ -137,12 +138,12 @@ function Clone(value) {
137
138
  return Visit(value);
138
139
  }
139
140
 
140
- // node_modules/@sinclair/typebox/build/esm/type/clone/type.mjs
141
+ // ../../node_modules/@sinclair/typebox/build/esm/type/clone/type.mjs
141
142
  function CloneType(schema, options) {
142
143
  return options === void 0 ? Clone(schema) : Clone({ ...options, ...schema });
143
144
  }
144
145
 
145
- // node_modules/@sinclair/typebox/build/esm/value/guard/guard.mjs
146
+ // ../../node_modules/@sinclair/typebox/build/esm/value/guard/guard.mjs
146
147
  function IsObject2(value) {
147
148
  return value !== null && typeof value === "object";
148
149
  }
@@ -156,7 +157,7 @@ function IsNumber2(value) {
156
157
  return typeof value === "number";
157
158
  }
158
159
 
159
- // node_modules/@sinclair/typebox/build/esm/system/policy.mjs
160
+ // ../../node_modules/@sinclair/typebox/build/esm/system/policy.mjs
160
161
  var TypeSystemPolicy;
161
162
  (function(TypeSystemPolicy2) {
162
163
  TypeSystemPolicy2.InstanceMode = "default";
@@ -188,7 +189,7 @@ var TypeSystemPolicy;
188
189
  TypeSystemPolicy2.IsVoidLike = IsVoidLike;
189
190
  })(TypeSystemPolicy || (TypeSystemPolicy = {}));
190
191
 
191
- // node_modules/@sinclair/typebox/build/esm/type/create/immutable.mjs
192
+ // ../../node_modules/@sinclair/typebox/build/esm/type/create/immutable.mjs
192
193
  function ImmutableArray(value) {
193
194
  return globalThis.Object.freeze(value).map((value2) => Immutable(value2));
194
195
  }
@@ -215,7 +216,7 @@ function Immutable(value) {
215
216
  return IsArray(value) ? ImmutableArray(value) : IsDate(value) ? ImmutableDate(value) : IsUint8Array(value) ? ImmutableUint8Array(value) : IsRegExp(value) ? ImmutableRegExp(value) : IsObject(value) ? ImmutableObject(value) : value;
216
217
  }
217
218
 
218
- // node_modules/@sinclair/typebox/build/esm/type/create/type.mjs
219
+ // ../../node_modules/@sinclair/typebox/build/esm/type/create/type.mjs
219
220
  function CreateType(schema, options) {
220
221
  const result = options !== void 0 ? { ...options, ...schema } : schema;
221
222
  switch (TypeSystemPolicy.InstanceMode) {
@@ -228,21 +229,21 @@ function CreateType(schema, options) {
228
229
  }
229
230
  }
230
231
 
231
- // node_modules/@sinclair/typebox/build/esm/type/error/error.mjs
232
+ // ../../node_modules/@sinclair/typebox/build/esm/type/error/error.mjs
232
233
  var TypeBoxError = class extends Error {
233
234
  constructor(message) {
234
235
  super(message);
235
236
  }
236
237
  };
237
238
 
238
- // node_modules/@sinclair/typebox/build/esm/type/symbols/symbols.mjs
239
+ // ../../node_modules/@sinclair/typebox/build/esm/type/symbols/symbols.mjs
239
240
  var TransformKind = /* @__PURE__ */ Symbol.for("TypeBox.Transform");
240
241
  var ReadonlyKind = /* @__PURE__ */ Symbol.for("TypeBox.Readonly");
241
242
  var OptionalKind = /* @__PURE__ */ Symbol.for("TypeBox.Optional");
242
243
  var Hint = /* @__PURE__ */ Symbol.for("TypeBox.Hint");
243
244
  var Kind = /* @__PURE__ */ Symbol.for("TypeBox.Kind");
244
245
 
245
- // node_modules/@sinclair/typebox/build/esm/type/guard/kind.mjs
246
+ // ../../node_modules/@sinclair/typebox/build/esm/type/guard/kind.mjs
246
247
  function IsReadonly(value) {
247
248
  return IsObject(value) && value[ReadonlyKind] === "Readonly";
248
249
  }
@@ -373,7 +374,7 @@ function IsSchema(value) {
373
374
  return IsAny(value) || IsArgument(value) || IsArray3(value) || IsBoolean2(value) || IsBigInt2(value) || IsAsyncIterator2(value) || IsComputed(value) || IsConstructor(value) || IsDate2(value) || IsFunction2(value) || IsInteger(value) || IsIntersect(value) || IsIterator2(value) || IsLiteral(value) || IsMappedKey(value) || IsMappedResult(value) || IsNever(value) || IsNot(value) || IsNull2(value) || IsNumber3(value) || IsObject3(value) || IsPromise(value) || IsRecord(value) || IsRef(value) || IsRegExp2(value) || IsString2(value) || IsSymbol2(value) || IsTemplateLiteral(value) || IsThis(value) || IsTuple(value) || IsUndefined3(value) || IsUnion(value) || IsUint8Array2(value) || IsUnknown(value) || IsUnsafe(value) || IsVoid(value) || IsKind(value);
374
375
  }
375
376
 
376
- // node_modules/@sinclair/typebox/build/esm/type/guard/type.mjs
377
+ // ../../node_modules/@sinclair/typebox/build/esm/type/guard/type.mjs
377
378
  var type_exports = {};
378
379
  __export(type_exports, {
379
380
  IsAny: () => IsAny2,
@@ -665,7 +666,7 @@ function IsSchema2(value) {
665
666
  return IsObject(value) && (IsAny2(value) || IsArgument2(value) || IsArray4(value) || IsBoolean3(value) || IsBigInt3(value) || IsAsyncIterator3(value) || IsComputed2(value) || IsConstructor2(value) || IsDate3(value) || IsFunction3(value) || IsInteger2(value) || IsIntersect2(value) || IsIterator3(value) || IsLiteral2(value) || IsMappedKey2(value) || IsMappedResult2(value) || IsNever2(value) || IsNot2(value) || IsNull3(value) || IsNumber4(value) || IsObject4(value) || IsPromise2(value) || IsRecord2(value) || IsRef2(value) || IsRegExp3(value) || IsString3(value) || IsSymbol3(value) || IsTemplateLiteral2(value) || IsThis2(value) || IsTuple2(value) || IsUndefined4(value) || IsUnion2(value) || IsUint8Array3(value) || IsUnknown2(value) || IsUnsafe2(value) || IsVoid2(value) || IsKind2(value));
666
667
  }
667
668
 
668
- // node_modules/@sinclair/typebox/build/esm/type/patterns/patterns.mjs
669
+ // ../../node_modules/@sinclair/typebox/build/esm/type/patterns/patterns.mjs
669
670
  var PatternBoolean = "(true|false)";
670
671
  var PatternNumber = "(0|[1-9][0-9]*)";
671
672
  var PatternString = "(.*)";
@@ -675,7 +676,7 @@ var PatternNumberExact = `^${PatternNumber}$`;
675
676
  var PatternStringExact = `^${PatternString}$`;
676
677
  var PatternNeverExact = `^${PatternNever}$`;
677
678
 
678
- // node_modules/@sinclair/typebox/build/esm/type/sets/set.mjs
679
+ // ../../node_modules/@sinclair/typebox/build/esm/type/sets/set.mjs
679
680
  function SetIncludes(T, S) {
680
681
  return T.includes(S);
681
682
  }
@@ -700,32 +701,32 @@ function SetUnionMany(T) {
700
701
  return Acc;
701
702
  }
702
703
 
703
- // node_modules/@sinclair/typebox/build/esm/type/any/any.mjs
704
+ // ../../node_modules/@sinclair/typebox/build/esm/type/any/any.mjs
704
705
  function Any(options) {
705
706
  return CreateType({ [Kind]: "Any" }, options);
706
707
  }
707
708
 
708
- // node_modules/@sinclair/typebox/build/esm/type/array/array.mjs
709
+ // ../../node_modules/@sinclair/typebox/build/esm/type/array/array.mjs
709
710
  function Array2(items, options) {
710
711
  return CreateType({ [Kind]: "Array", type: "array", items }, options);
711
712
  }
712
713
 
713
- // node_modules/@sinclair/typebox/build/esm/type/argument/argument.mjs
714
+ // ../../node_modules/@sinclair/typebox/build/esm/type/argument/argument.mjs
714
715
  function Argument(index) {
715
716
  return CreateType({ [Kind]: "Argument", index });
716
717
  }
717
718
 
718
- // node_modules/@sinclair/typebox/build/esm/type/async-iterator/async-iterator.mjs
719
+ // ../../node_modules/@sinclair/typebox/build/esm/type/async-iterator/async-iterator.mjs
719
720
  function AsyncIterator(items, options) {
720
721
  return CreateType({ [Kind]: "AsyncIterator", type: "AsyncIterator", items }, options);
721
722
  }
722
723
 
723
- // node_modules/@sinclair/typebox/build/esm/type/computed/computed.mjs
724
+ // ../../node_modules/@sinclair/typebox/build/esm/type/computed/computed.mjs
724
725
  function Computed(target, parameters, options) {
725
726
  return CreateType({ [Kind]: "Computed", target, parameters }, options);
726
727
  }
727
728
 
728
- // node_modules/@sinclair/typebox/build/esm/type/discard/discard.mjs
729
+ // ../../node_modules/@sinclair/typebox/build/esm/type/discard/discard.mjs
729
730
  function DiscardKey(value, key) {
730
731
  const { [key]: _, ...rest } = value;
731
732
  return rest;
@@ -734,12 +735,12 @@ function Discard(value, keys) {
734
735
  return keys.reduce((acc, key) => DiscardKey(acc, key), value);
735
736
  }
736
737
 
737
- // node_modules/@sinclair/typebox/build/esm/type/never/never.mjs
738
+ // ../../node_modules/@sinclair/typebox/build/esm/type/never/never.mjs
738
739
  function Never(options) {
739
740
  return CreateType({ [Kind]: "Never", not: {} }, options);
740
741
  }
741
742
 
742
- // node_modules/@sinclair/typebox/build/esm/type/mapped/mapped-result.mjs
743
+ // ../../node_modules/@sinclair/typebox/build/esm/type/mapped/mapped-result.mjs
743
744
  function MappedResult(properties) {
744
745
  return CreateType({
745
746
  [Kind]: "MappedResult",
@@ -747,22 +748,22 @@ function MappedResult(properties) {
747
748
  });
748
749
  }
749
750
 
750
- // node_modules/@sinclair/typebox/build/esm/type/constructor/constructor.mjs
751
+ // ../../node_modules/@sinclair/typebox/build/esm/type/constructor/constructor.mjs
751
752
  function Constructor(parameters, returns, options) {
752
753
  return CreateType({ [Kind]: "Constructor", type: "Constructor", parameters, returns }, options);
753
754
  }
754
755
 
755
- // node_modules/@sinclair/typebox/build/esm/type/function/function.mjs
756
+ // ../../node_modules/@sinclair/typebox/build/esm/type/function/function.mjs
756
757
  function Function(parameters, returns, options) {
757
758
  return CreateType({ [Kind]: "Function", type: "Function", parameters, returns }, options);
758
759
  }
759
760
 
760
- // node_modules/@sinclair/typebox/build/esm/type/union/union-create.mjs
761
+ // ../../node_modules/@sinclair/typebox/build/esm/type/union/union-create.mjs
761
762
  function UnionCreate(T, options) {
762
763
  return CreateType({ [Kind]: "Union", anyOf: T }, options);
763
764
  }
764
765
 
765
- // node_modules/@sinclair/typebox/build/esm/type/union/union-evaluated.mjs
766
+ // ../../node_modules/@sinclair/typebox/build/esm/type/union/union-evaluated.mjs
766
767
  function IsUnionOptional(types) {
767
768
  return types.some((type) => IsOptional(type));
768
769
  }
@@ -780,12 +781,12 @@ function UnionEvaluated(T, options) {
780
781
  return T.length === 1 ? CreateType(T[0], options) : T.length === 0 ? Never(options) : ResolveUnion(T, options);
781
782
  }
782
783
 
783
- // node_modules/@sinclair/typebox/build/esm/type/union/union.mjs
784
+ // ../../node_modules/@sinclair/typebox/build/esm/type/union/union.mjs
784
785
  function Union(types, options) {
785
786
  return types.length === 0 ? Never(options) : types.length === 1 ? CreateType(types[0], options) : UnionCreate(types, options);
786
787
  }
787
788
 
788
- // node_modules/@sinclair/typebox/build/esm/type/template-literal/parse.mjs
789
+ // ../../node_modules/@sinclair/typebox/build/esm/type/template-literal/parse.mjs
789
790
  var TemplateLiteralParserError = class extends TypeBoxError {
790
791
  };
791
792
  function Unescape(pattern) {
@@ -909,7 +910,7 @@ function TemplateLiteralParseExact(pattern) {
909
910
  return TemplateLiteralParse(pattern.slice(1, pattern.length - 1));
910
911
  }
911
912
 
912
- // node_modules/@sinclair/typebox/build/esm/type/template-literal/finite.mjs
913
+ // ../../node_modules/@sinclair/typebox/build/esm/type/template-literal/finite.mjs
913
914
  var TemplateLiteralFiniteError = class extends TypeBoxError {
914
915
  };
915
916
  function IsNumberExpression(expression) {
@@ -931,7 +932,7 @@ function IsTemplateLiteralFinite(schema) {
931
932
  return IsTemplateLiteralExpressionFinite(expression);
932
933
  }
933
934
 
934
- // node_modules/@sinclair/typebox/build/esm/type/template-literal/generate.mjs
935
+ // ../../node_modules/@sinclair/typebox/build/esm/type/template-literal/generate.mjs
935
936
  var TemplateLiteralGenerateError = class extends TypeBoxError {
936
937
  };
937
938
  function* GenerateReduce(buffer) {
@@ -963,7 +964,7 @@ function TemplateLiteralGenerate(schema) {
963
964
  return IsTemplateLiteralExpressionFinite(expression) ? [...TemplateLiteralExpressionGenerate(expression)] : [];
964
965
  }
965
966
 
966
- // node_modules/@sinclair/typebox/build/esm/type/literal/literal.mjs
967
+ // ../../node_modules/@sinclair/typebox/build/esm/type/literal/literal.mjs
967
968
  function Literal(value, options) {
968
969
  return CreateType({
969
970
  [Kind]: "Literal",
@@ -972,27 +973,27 @@ function Literal(value, options) {
972
973
  }, options);
973
974
  }
974
975
 
975
- // node_modules/@sinclair/typebox/build/esm/type/boolean/boolean.mjs
976
+ // ../../node_modules/@sinclair/typebox/build/esm/type/boolean/boolean.mjs
976
977
  function Boolean2(options) {
977
978
  return CreateType({ [Kind]: "Boolean", type: "boolean" }, options);
978
979
  }
979
980
 
980
- // node_modules/@sinclair/typebox/build/esm/type/bigint/bigint.mjs
981
+ // ../../node_modules/@sinclair/typebox/build/esm/type/bigint/bigint.mjs
981
982
  function BigInt(options) {
982
983
  return CreateType({ [Kind]: "BigInt", type: "bigint" }, options);
983
984
  }
984
985
 
985
- // node_modules/@sinclair/typebox/build/esm/type/number/number.mjs
986
+ // ../../node_modules/@sinclair/typebox/build/esm/type/number/number.mjs
986
987
  function Number2(options) {
987
988
  return CreateType({ [Kind]: "Number", type: "number" }, options);
988
989
  }
989
990
 
990
- // node_modules/@sinclair/typebox/build/esm/type/string/string.mjs
991
+ // ../../node_modules/@sinclair/typebox/build/esm/type/string/string.mjs
991
992
  function String2(options) {
992
993
  return CreateType({ [Kind]: "String", type: "string" }, options);
993
994
  }
994
995
 
995
- // node_modules/@sinclair/typebox/build/esm/type/template-literal/syntax.mjs
996
+ // ../../node_modules/@sinclair/typebox/build/esm/type/template-literal/syntax.mjs
996
997
  function* FromUnion(syntax) {
997
998
  const trim = syntax.trim().replace(/"|'/g, "");
998
999
  return trim === "boolean" ? yield Boolean2() : trim === "number" ? yield Number2() : trim === "bigint" ? yield BigInt() : trim === "string" ? yield String2() : yield (() => {
@@ -1029,7 +1030,7 @@ function TemplateLiteralSyntax(syntax) {
1029
1030
  return [...FromSyntax(syntax)];
1030
1031
  }
1031
1032
 
1032
- // node_modules/@sinclair/typebox/build/esm/type/template-literal/pattern.mjs
1033
+ // ../../node_modules/@sinclair/typebox/build/esm/type/template-literal/pattern.mjs
1033
1034
  var TemplateLiteralPatternError = class extends TypeBoxError {
1034
1035
  };
1035
1036
  function Escape(value) {
@@ -1044,20 +1045,20 @@ function TemplateLiteralPattern(kinds) {
1044
1045
  return `^${kinds.map((schema) => Visit2(schema, "")).join("")}$`;
1045
1046
  }
1046
1047
 
1047
- // node_modules/@sinclair/typebox/build/esm/type/template-literal/union.mjs
1048
+ // ../../node_modules/@sinclair/typebox/build/esm/type/template-literal/union.mjs
1048
1049
  function TemplateLiteralToUnion(schema) {
1049
1050
  const R = TemplateLiteralGenerate(schema);
1050
1051
  const L = R.map((S) => Literal(S));
1051
1052
  return UnionEvaluated(L);
1052
1053
  }
1053
1054
 
1054
- // node_modules/@sinclair/typebox/build/esm/type/template-literal/template-literal.mjs
1055
+ // ../../node_modules/@sinclair/typebox/build/esm/type/template-literal/template-literal.mjs
1055
1056
  function TemplateLiteral(unresolved, options) {
1056
1057
  const pattern = IsString(unresolved) ? TemplateLiteralPattern(TemplateLiteralSyntax(unresolved)) : TemplateLiteralPattern(unresolved);
1057
1058
  return CreateType({ [Kind]: "TemplateLiteral", type: "string", pattern }, options);
1058
1059
  }
1059
1060
 
1060
- // node_modules/@sinclair/typebox/build/esm/type/indexed/indexed-property-keys.mjs
1061
+ // ../../node_modules/@sinclair/typebox/build/esm/type/indexed/indexed-property-keys.mjs
1061
1062
  function FromTemplateLiteral(templateLiteral) {
1062
1063
  const keys = TemplateLiteralGenerate(templateLiteral);
1063
1064
  return keys.map((key) => key.toString());
@@ -1075,7 +1076,7 @@ function IndexPropertyKeys(type) {
1075
1076
  return [...new Set(IsTemplateLiteral(type) ? FromTemplateLiteral(type) : IsUnion(type) ? FromUnion2(type.anyOf) : IsLiteral(type) ? FromLiteral(type.const) : IsNumber3(type) ? ["[number]"] : IsInteger(type) ? ["[number]"] : [])];
1076
1077
  }
1077
1078
 
1078
- // node_modules/@sinclair/typebox/build/esm/type/indexed/indexed-from-mapped-result.mjs
1079
+ // ../../node_modules/@sinclair/typebox/build/esm/type/indexed/indexed-from-mapped-result.mjs
1079
1080
  function FromProperties(type, properties, options) {
1080
1081
  const result = {};
1081
1082
  for (const K2 of Object.getOwnPropertyNames(properties)) {
@@ -1091,7 +1092,7 @@ function IndexFromMappedResult(type, mappedResult, options) {
1091
1092
  return MappedResult(properties);
1092
1093
  }
1093
1094
 
1094
- // node_modules/@sinclair/typebox/build/esm/type/indexed/indexed.mjs
1095
+ // ../../node_modules/@sinclair/typebox/build/esm/type/indexed/indexed.mjs
1095
1096
  function FromRest(types, key) {
1096
1097
  return types.map((type) => IndexFromPropertyKey(type, key));
1097
1098
  }
@@ -1139,7 +1140,7 @@ function Index(type, key, options) {
1139
1140
  return CreateType(IsSchema(key) ? FromSchema(type, IndexPropertyKeys(key)) : FromSchema(type, key), options);
1140
1141
  }
1141
1142
 
1142
- // node_modules/@sinclair/typebox/build/esm/type/indexed/indexed-from-mapped-key.mjs
1143
+ // ../../node_modules/@sinclair/typebox/build/esm/type/indexed/indexed-from-mapped-key.mjs
1143
1144
  function MappedIndexPropertyKey(type, key, options) {
1144
1145
  return { [key]: Index(type, [key], Clone(options)) };
1145
1146
  }
@@ -1156,12 +1157,12 @@ function IndexFromMappedKey(type, mappedKey, options) {
1156
1157
  return MappedResult(properties);
1157
1158
  }
1158
1159
 
1159
- // node_modules/@sinclair/typebox/build/esm/type/iterator/iterator.mjs
1160
+ // ../../node_modules/@sinclair/typebox/build/esm/type/iterator/iterator.mjs
1160
1161
  function Iterator(items, options) {
1161
1162
  return CreateType({ [Kind]: "Iterator", type: "Iterator", items }, options);
1162
1163
  }
1163
1164
 
1164
- // node_modules/@sinclair/typebox/build/esm/type/object/object.mjs
1165
+ // ../../node_modules/@sinclair/typebox/build/esm/type/object/object.mjs
1165
1166
  function RequiredArray(properties) {
1166
1167
  return globalThis.Object.keys(properties).filter((key) => !IsOptional(properties[key]));
1167
1168
  }
@@ -1172,12 +1173,12 @@ function _Object(properties, options) {
1172
1173
  }
1173
1174
  var Object2 = _Object;
1174
1175
 
1175
- // node_modules/@sinclair/typebox/build/esm/type/promise/promise.mjs
1176
+ // ../../node_modules/@sinclair/typebox/build/esm/type/promise/promise.mjs
1176
1177
  function Promise2(item, options) {
1177
1178
  return CreateType({ [Kind]: "Promise", type: "Promise", item }, options);
1178
1179
  }
1179
1180
 
1180
- // node_modules/@sinclair/typebox/build/esm/type/readonly/readonly.mjs
1181
+ // ../../node_modules/@sinclair/typebox/build/esm/type/readonly/readonly.mjs
1181
1182
  function RemoveReadonly(schema) {
1182
1183
  return CreateType(Discard(schema, [ReadonlyKind]));
1183
1184
  }
@@ -1192,7 +1193,7 @@ function Readonly(schema, enable) {
1192
1193
  return IsMappedResult(schema) ? ReadonlyFromMappedResult(schema, F) : ReadonlyWithFlag(schema, F);
1193
1194
  }
1194
1195
 
1195
- // node_modules/@sinclair/typebox/build/esm/type/readonly/readonly-from-mapped-result.mjs
1196
+ // ../../node_modules/@sinclair/typebox/build/esm/type/readonly/readonly-from-mapped-result.mjs
1196
1197
  function FromProperties2(K, F) {
1197
1198
  const Acc = {};
1198
1199
  for (const K2 of globalThis.Object.getOwnPropertyNames(K))
@@ -1207,12 +1208,12 @@ function ReadonlyFromMappedResult(R, F) {
1207
1208
  return MappedResult(P);
1208
1209
  }
1209
1210
 
1210
- // node_modules/@sinclair/typebox/build/esm/type/tuple/tuple.mjs
1211
+ // ../../node_modules/@sinclair/typebox/build/esm/type/tuple/tuple.mjs
1211
1212
  function Tuple(types, options) {
1212
1213
  return CreateType(types.length > 0 ? { [Kind]: "Tuple", type: "array", items: types, additionalItems: false, minItems: types.length, maxItems: types.length } : { [Kind]: "Tuple", type: "array", minItems: types.length, maxItems: types.length }, options);
1213
1214
  }
1214
1215
 
1215
- // node_modules/@sinclair/typebox/build/esm/type/mapped/mapped.mjs
1216
+ // ../../node_modules/@sinclair/typebox/build/esm/type/mapped/mapped.mjs
1216
1217
  function FromMappedResult3(K, P) {
1217
1218
  return K in P ? FromSchemaType(K, P[K]) : MappedResult(P);
1218
1219
  }
@@ -1267,7 +1268,7 @@ function Mapped(key, map, options) {
1267
1268
  return Object2(R, options);
1268
1269
  }
1269
1270
 
1270
- // node_modules/@sinclair/typebox/build/esm/type/optional/optional.mjs
1271
+ // ../../node_modules/@sinclair/typebox/build/esm/type/optional/optional.mjs
1271
1272
  function RemoveOptional(schema) {
1272
1273
  return CreateType(Discard(schema, [OptionalKind]));
1273
1274
  }
@@ -1282,7 +1283,7 @@ function Optional(schema, enable) {
1282
1283
  return IsMappedResult(schema) ? OptionalFromMappedResult(schema, F) : OptionalWithFlag(schema, F);
1283
1284
  }
1284
1285
 
1285
- // node_modules/@sinclair/typebox/build/esm/type/optional/optional-from-mapped-result.mjs
1286
+ // ../../node_modules/@sinclair/typebox/build/esm/type/optional/optional-from-mapped-result.mjs
1286
1287
  function FromProperties4(P, F) {
1287
1288
  const Acc = {};
1288
1289
  for (const K2 of globalThis.Object.getOwnPropertyNames(P))
@@ -1297,14 +1298,14 @@ function OptionalFromMappedResult(R, F) {
1297
1298
  return MappedResult(P);
1298
1299
  }
1299
1300
 
1300
- // node_modules/@sinclair/typebox/build/esm/type/intersect/intersect-create.mjs
1301
+ // ../../node_modules/@sinclair/typebox/build/esm/type/intersect/intersect-create.mjs
1301
1302
  function IntersectCreate(T, options = {}) {
1302
1303
  const allObjects = T.every((schema) => IsObject3(schema));
1303
1304
  const clonedUnevaluatedProperties = IsSchema(options.unevaluatedProperties) ? { unevaluatedProperties: options.unevaluatedProperties } : {};
1304
1305
  return CreateType(options.unevaluatedProperties === false || IsSchema(options.unevaluatedProperties) || allObjects ? { ...clonedUnevaluatedProperties, [Kind]: "Intersect", type: "object", allOf: T } : { ...clonedUnevaluatedProperties, [Kind]: "Intersect", allOf: T }, options);
1305
1306
  }
1306
1307
 
1307
- // node_modules/@sinclair/typebox/build/esm/type/intersect/intersect-evaluated.mjs
1308
+ // ../../node_modules/@sinclair/typebox/build/esm/type/intersect/intersect-evaluated.mjs
1308
1309
  function IsIntersectOptional(types) {
1309
1310
  return types.every((left) => IsOptional(left));
1310
1311
  }
@@ -1327,7 +1328,7 @@ function IntersectEvaluated(types, options = {}) {
1327
1328
  return ResolveIntersect(types, options);
1328
1329
  }
1329
1330
 
1330
- // node_modules/@sinclair/typebox/build/esm/type/intersect/intersect.mjs
1331
+ // ../../node_modules/@sinclair/typebox/build/esm/type/intersect/intersect.mjs
1331
1332
  function Intersect(types, options) {
1332
1333
  if (types.length === 1)
1333
1334
  return CreateType(types[0], options);
@@ -1338,7 +1339,7 @@ function Intersect(types, options) {
1338
1339
  return IntersectCreate(types, options);
1339
1340
  }
1340
1341
 
1341
- // node_modules/@sinclair/typebox/build/esm/type/ref/ref.mjs
1342
+ // ../../node_modules/@sinclair/typebox/build/esm/type/ref/ref.mjs
1342
1343
  function Ref(...args) {
1343
1344
  const [$ref, options] = typeof args[0] === "string" ? [args[0], args[1]] : [args[0].$id, args[1]];
1344
1345
  if (typeof $ref !== "string")
@@ -1346,7 +1347,7 @@ function Ref(...args) {
1346
1347
  return CreateType({ [Kind]: "Ref", $ref }, options);
1347
1348
  }
1348
1349
 
1349
- // node_modules/@sinclair/typebox/build/esm/type/awaited/awaited.mjs
1350
+ // ../../node_modules/@sinclair/typebox/build/esm/type/awaited/awaited.mjs
1350
1351
  function FromComputed(target, parameters) {
1351
1352
  return Computed("Awaited", [Computed(target, parameters)]);
1352
1353
  }
@@ -1369,7 +1370,7 @@ function Awaited(type, options) {
1369
1370
  return CreateType(IsComputed(type) ? FromComputed(type.target, type.parameters) : IsIntersect(type) ? FromIntersect2(type.allOf) : IsUnion(type) ? FromUnion4(type.anyOf) : IsPromise(type) ? FromPromise(type.item) : IsRef(type) ? FromRef(type.$ref) : type, options);
1370
1371
  }
1371
1372
 
1372
- // node_modules/@sinclair/typebox/build/esm/type/keyof/keyof-property-keys.mjs
1373
+ // ../../node_modules/@sinclair/typebox/build/esm/type/keyof/keyof-property-keys.mjs
1373
1374
  function FromRest4(types) {
1374
1375
  const result = [];
1375
1376
  for (const L of types)
@@ -1408,7 +1409,7 @@ function KeyOfPropertyKeys(type) {
1408
1409
  }
1409
1410
  var includePatternProperties = false;
1410
1411
 
1411
- // node_modules/@sinclair/typebox/build/esm/type/keyof/keyof.mjs
1412
+ // ../../node_modules/@sinclair/typebox/build/esm/type/keyof/keyof.mjs
1412
1413
  function FromComputed2(target, parameters) {
1413
1414
  return Computed("KeyOf", [Computed(target, parameters)]);
1414
1415
  }
@@ -1428,7 +1429,7 @@ function KeyOf(type, options) {
1428
1429
  return IsComputed(type) ? FromComputed2(type.target, type.parameters) : IsRef(type) ? FromRef2(type.$ref) : IsMappedResult(type) ? KeyOfFromMappedResult(type, options) : KeyOfFromType(type, options);
1429
1430
  }
1430
1431
 
1431
- // node_modules/@sinclair/typebox/build/esm/type/keyof/keyof-from-mapped-result.mjs
1432
+ // ../../node_modules/@sinclair/typebox/build/esm/type/keyof/keyof-from-mapped-result.mjs
1432
1433
  function FromProperties6(properties, options) {
1433
1434
  const result = {};
1434
1435
  for (const K2 of globalThis.Object.getOwnPropertyNames(properties))
@@ -1443,7 +1444,7 @@ function KeyOfFromMappedResult(mappedResult, options) {
1443
1444
  return MappedResult(properties);
1444
1445
  }
1445
1446
 
1446
- // node_modules/@sinclair/typebox/build/esm/type/composite/composite.mjs
1447
+ // ../../node_modules/@sinclair/typebox/build/esm/type/composite/composite.mjs
1447
1448
  function CompositeKeys(T) {
1448
1449
  const Acc = [];
1449
1450
  for (const L of T)
@@ -1473,37 +1474,37 @@ function Composite(T, options) {
1473
1474
  return R;
1474
1475
  }
1475
1476
 
1476
- // node_modules/@sinclair/typebox/build/esm/type/date/date.mjs
1477
+ // ../../node_modules/@sinclair/typebox/build/esm/type/date/date.mjs
1477
1478
  function Date2(options) {
1478
1479
  return CreateType({ [Kind]: "Date", type: "Date" }, options);
1479
1480
  }
1480
1481
 
1481
- // node_modules/@sinclair/typebox/build/esm/type/null/null.mjs
1482
+ // ../../node_modules/@sinclair/typebox/build/esm/type/null/null.mjs
1482
1483
  function Null(options) {
1483
1484
  return CreateType({ [Kind]: "Null", type: "null" }, options);
1484
1485
  }
1485
1486
 
1486
- // node_modules/@sinclair/typebox/build/esm/type/symbol/symbol.mjs
1487
+ // ../../node_modules/@sinclair/typebox/build/esm/type/symbol/symbol.mjs
1487
1488
  function Symbol2(options) {
1488
1489
  return CreateType({ [Kind]: "Symbol", type: "symbol" }, options);
1489
1490
  }
1490
1491
 
1491
- // node_modules/@sinclair/typebox/build/esm/type/undefined/undefined.mjs
1492
+ // ../../node_modules/@sinclair/typebox/build/esm/type/undefined/undefined.mjs
1492
1493
  function Undefined(options) {
1493
1494
  return CreateType({ [Kind]: "Undefined", type: "undefined" }, options);
1494
1495
  }
1495
1496
 
1496
- // node_modules/@sinclair/typebox/build/esm/type/uint8array/uint8array.mjs
1497
+ // ../../node_modules/@sinclair/typebox/build/esm/type/uint8array/uint8array.mjs
1497
1498
  function Uint8Array2(options) {
1498
1499
  return CreateType({ [Kind]: "Uint8Array", type: "Uint8Array" }, options);
1499
1500
  }
1500
1501
 
1501
- // node_modules/@sinclair/typebox/build/esm/type/unknown/unknown.mjs
1502
+ // ../../node_modules/@sinclair/typebox/build/esm/type/unknown/unknown.mjs
1502
1503
  function Unknown(options) {
1503
1504
  return CreateType({ [Kind]: "Unknown" }, options);
1504
1505
  }
1505
1506
 
1506
- // node_modules/@sinclair/typebox/build/esm/type/const/const.mjs
1507
+ // ../../node_modules/@sinclair/typebox/build/esm/type/const/const.mjs
1507
1508
  function FromArray3(T) {
1508
1509
  return T.map((L) => FromValue(L, false));
1509
1510
  }
@@ -1523,12 +1524,12 @@ function Const(T, options) {
1523
1524
  return CreateType(FromValue(T, true), options);
1524
1525
  }
1525
1526
 
1526
- // node_modules/@sinclair/typebox/build/esm/type/constructor-parameters/constructor-parameters.mjs
1527
+ // ../../node_modules/@sinclair/typebox/build/esm/type/constructor-parameters/constructor-parameters.mjs
1527
1528
  function ConstructorParameters(schema, options) {
1528
1529
  return IsConstructor(schema) ? Tuple(schema.parameters, options) : Never(options);
1529
1530
  }
1530
1531
 
1531
- // node_modules/@sinclair/typebox/build/esm/type/enum/enum.mjs
1532
+ // ../../node_modules/@sinclair/typebox/build/esm/type/enum/enum.mjs
1532
1533
  function Enum(item, options) {
1533
1534
  if (IsUndefined(item))
1534
1535
  throw new Error("Enum undefined or empty");
@@ -1538,7 +1539,7 @@ function Enum(item, options) {
1538
1539
  return Union(anyOf, { ...options, [Hint]: "Enum" });
1539
1540
  }
1540
1541
 
1541
- // node_modules/@sinclair/typebox/build/esm/type/extends/extends-check.mjs
1542
+ // ../../node_modules/@sinclair/typebox/build/esm/type/extends/extends-check.mjs
1542
1543
  var ExtendsResolverError = class extends TypeBoxError {
1543
1544
  };
1544
1545
  var ExtendsResult;
@@ -1789,7 +1790,7 @@ function ExtendsCheck(left, right) {
1789
1790
  return Visit3(left, right);
1790
1791
  }
1791
1792
 
1792
- // node_modules/@sinclair/typebox/build/esm/type/extends/extends-from-mapped-result.mjs
1793
+ // ../../node_modules/@sinclair/typebox/build/esm/type/extends/extends-from-mapped-result.mjs
1793
1794
  function FromProperties8(P, Right, True, False, options) {
1794
1795
  const Acc = {};
1795
1796
  for (const K2 of globalThis.Object.getOwnPropertyNames(P))
@@ -1804,7 +1805,7 @@ function ExtendsFromMappedResult(Left, Right, True, False, options) {
1804
1805
  return MappedResult(P);
1805
1806
  }
1806
1807
 
1807
- // node_modules/@sinclair/typebox/build/esm/type/extends/extends.mjs
1808
+ // ../../node_modules/@sinclair/typebox/build/esm/type/extends/extends.mjs
1808
1809
  function ExtendsResolve(left, right, trueType, falseType) {
1809
1810
  const R = ExtendsCheck(left, right);
1810
1811
  return R === ExtendsResult.Union ? Union([trueType, falseType]) : R === ExtendsResult.True ? trueType : falseType;
@@ -1813,7 +1814,7 @@ function Extends(L, R, T, F, options) {
1813
1814
  return IsMappedResult(L) ? ExtendsFromMappedResult(L, R, T, F, options) : IsMappedKey(L) ? CreateType(ExtendsFromMappedKey(L, R, T, F, options)) : CreateType(ExtendsResolve(L, R, T, F), options);
1814
1815
  }
1815
1816
 
1816
- // node_modules/@sinclair/typebox/build/esm/type/extends/extends-from-mapped-key.mjs
1817
+ // ../../node_modules/@sinclair/typebox/build/esm/type/extends/extends-from-mapped-key.mjs
1817
1818
  function FromPropertyKey(K, U, L, R, options) {
1818
1819
  return {
1819
1820
  [K]: Extends(Literal(K), U, L, R, Clone(options))
@@ -1832,12 +1833,12 @@ function ExtendsFromMappedKey(T, U, L, R, options) {
1832
1833
  return MappedResult(P);
1833
1834
  }
1834
1835
 
1835
- // node_modules/@sinclair/typebox/build/esm/type/exclude/exclude-from-template-literal.mjs
1836
+ // ../../node_modules/@sinclair/typebox/build/esm/type/exclude/exclude-from-template-literal.mjs
1836
1837
  function ExcludeFromTemplateLiteral(L, R) {
1837
1838
  return Exclude(TemplateLiteralToUnion(L), R);
1838
1839
  }
1839
1840
 
1840
- // node_modules/@sinclair/typebox/build/esm/type/exclude/exclude.mjs
1841
+ // ../../node_modules/@sinclair/typebox/build/esm/type/exclude/exclude.mjs
1841
1842
  function ExcludeRest(L, R) {
1842
1843
  const excluded = L.filter((inner) => ExtendsCheck(inner, R) === ExtendsResult.False);
1843
1844
  return excluded.length === 1 ? excluded[0] : Union(excluded);
@@ -1850,7 +1851,7 @@ function Exclude(L, R, options = {}) {
1850
1851
  return CreateType(IsUnion(L) ? ExcludeRest(L.anyOf, R) : ExtendsCheck(L, R) !== ExtendsResult.False ? Never() : L, options);
1851
1852
  }
1852
1853
 
1853
- // node_modules/@sinclair/typebox/build/esm/type/exclude/exclude-from-mapped-result.mjs
1854
+ // ../../node_modules/@sinclair/typebox/build/esm/type/exclude/exclude-from-mapped-result.mjs
1854
1855
  function FromProperties9(P, U) {
1855
1856
  const Acc = {};
1856
1857
  for (const K2 of globalThis.Object.getOwnPropertyNames(P))
@@ -1865,12 +1866,12 @@ function ExcludeFromMappedResult(R, T) {
1865
1866
  return MappedResult(P);
1866
1867
  }
1867
1868
 
1868
- // node_modules/@sinclair/typebox/build/esm/type/extract/extract-from-template-literal.mjs
1869
+ // ../../node_modules/@sinclair/typebox/build/esm/type/extract/extract-from-template-literal.mjs
1869
1870
  function ExtractFromTemplateLiteral(L, R) {
1870
1871
  return Extract(TemplateLiteralToUnion(L), R);
1871
1872
  }
1872
1873
 
1873
- // node_modules/@sinclair/typebox/build/esm/type/extract/extract.mjs
1874
+ // ../../node_modules/@sinclair/typebox/build/esm/type/extract/extract.mjs
1874
1875
  function ExtractRest(L, R) {
1875
1876
  const extracted = L.filter((inner) => ExtendsCheck(inner, R) !== ExtendsResult.False);
1876
1877
  return extracted.length === 1 ? extracted[0] : Union(extracted);
@@ -1883,7 +1884,7 @@ function Extract(L, R, options) {
1883
1884
  return CreateType(IsUnion(L) ? ExtractRest(L.anyOf, R) : ExtendsCheck(L, R) !== ExtendsResult.False ? L : Never(), options);
1884
1885
  }
1885
1886
 
1886
- // node_modules/@sinclair/typebox/build/esm/type/extract/extract-from-mapped-result.mjs
1887
+ // ../../node_modules/@sinclair/typebox/build/esm/type/extract/extract-from-mapped-result.mjs
1887
1888
  function FromProperties10(P, T) {
1888
1889
  const Acc = {};
1889
1890
  for (const K2 of globalThis.Object.getOwnPropertyNames(P))
@@ -1898,17 +1899,17 @@ function ExtractFromMappedResult(R, T) {
1898
1899
  return MappedResult(P);
1899
1900
  }
1900
1901
 
1901
- // node_modules/@sinclair/typebox/build/esm/type/instance-type/instance-type.mjs
1902
+ // ../../node_modules/@sinclair/typebox/build/esm/type/instance-type/instance-type.mjs
1902
1903
  function InstanceType(schema, options) {
1903
1904
  return IsConstructor(schema) ? CreateType(schema.returns, options) : Never(options);
1904
1905
  }
1905
1906
 
1906
- // node_modules/@sinclair/typebox/build/esm/type/readonly-optional/readonly-optional.mjs
1907
+ // ../../node_modules/@sinclair/typebox/build/esm/type/readonly-optional/readonly-optional.mjs
1907
1908
  function ReadonlyOptional(schema) {
1908
1909
  return Readonly(Optional(schema));
1909
1910
  }
1910
1911
 
1911
- // node_modules/@sinclair/typebox/build/esm/type/record/record.mjs
1912
+ // ../../node_modules/@sinclair/typebox/build/esm/type/record/record.mjs
1912
1913
  function RecordCreateFromPattern(pattern, T, options) {
1913
1914
  return CreateType({ [Kind]: "Record", type: "object", patternProperties: { [pattern]: T } }, options);
1914
1915
  }
@@ -1963,7 +1964,7 @@ function RecordValue2(type) {
1963
1964
  return type.patternProperties[RecordPattern(type)];
1964
1965
  }
1965
1966
 
1966
- // node_modules/@sinclair/typebox/build/esm/type/instantiate/instantiate.mjs
1967
+ // ../../node_modules/@sinclair/typebox/build/esm/type/instantiate/instantiate.mjs
1967
1968
  function FromConstructor2(args, type) {
1968
1969
  type.parameters = FromTypes(args, type.parameters);
1969
1970
  type.returns = FromType(args, type.returns);
@@ -2038,12 +2039,12 @@ function Instantiate(type, args) {
2038
2039
  return FromType(args, CloneType(type));
2039
2040
  }
2040
2041
 
2041
- // node_modules/@sinclair/typebox/build/esm/type/integer/integer.mjs
2042
+ // ../../node_modules/@sinclair/typebox/build/esm/type/integer/integer.mjs
2042
2043
  function Integer(options) {
2043
2044
  return CreateType({ [Kind]: "Integer", type: "integer" }, options);
2044
2045
  }
2045
2046
 
2046
- // node_modules/@sinclair/typebox/build/esm/type/intrinsic/intrinsic-from-mapped-key.mjs
2047
+ // ../../node_modules/@sinclair/typebox/build/esm/type/intrinsic/intrinsic-from-mapped-key.mjs
2047
2048
  function MappedIntrinsicPropertyKey(K, M, options) {
2048
2049
  return {
2049
2050
  [K]: Intrinsic(Literal(K), M, Clone(options))
@@ -2063,7 +2064,7 @@ function IntrinsicFromMappedKey(T, M, options) {
2063
2064
  return MappedResult(P);
2064
2065
  }
2065
2066
 
2066
- // node_modules/@sinclair/typebox/build/esm/type/intrinsic/intrinsic.mjs
2067
+ // ../../node_modules/@sinclair/typebox/build/esm/type/intrinsic/intrinsic.mjs
2067
2068
  function ApplyUncapitalize(value) {
2068
2069
  const [first, rest] = [value.slice(0, 1), value.slice(1)];
2069
2070
  return [first.toLowerCase(), rest].join("");
@@ -2108,27 +2109,27 @@ function Intrinsic(schema, mode, options = {}) {
2108
2109
  );
2109
2110
  }
2110
2111
 
2111
- // node_modules/@sinclair/typebox/build/esm/type/intrinsic/capitalize.mjs
2112
+ // ../../node_modules/@sinclair/typebox/build/esm/type/intrinsic/capitalize.mjs
2112
2113
  function Capitalize(T, options = {}) {
2113
2114
  return Intrinsic(T, "Capitalize", options);
2114
2115
  }
2115
2116
 
2116
- // node_modules/@sinclair/typebox/build/esm/type/intrinsic/lowercase.mjs
2117
+ // ../../node_modules/@sinclair/typebox/build/esm/type/intrinsic/lowercase.mjs
2117
2118
  function Lowercase(T, options = {}) {
2118
2119
  return Intrinsic(T, "Lowercase", options);
2119
2120
  }
2120
2121
 
2121
- // node_modules/@sinclair/typebox/build/esm/type/intrinsic/uncapitalize.mjs
2122
+ // ../../node_modules/@sinclair/typebox/build/esm/type/intrinsic/uncapitalize.mjs
2122
2123
  function Uncapitalize(T, options = {}) {
2123
2124
  return Intrinsic(T, "Uncapitalize", options);
2124
2125
  }
2125
2126
 
2126
- // node_modules/@sinclair/typebox/build/esm/type/intrinsic/uppercase.mjs
2127
+ // ../../node_modules/@sinclair/typebox/build/esm/type/intrinsic/uppercase.mjs
2127
2128
  function Uppercase(T, options = {}) {
2128
2129
  return Intrinsic(T, "Uppercase", options);
2129
2130
  }
2130
2131
 
2131
- // node_modules/@sinclair/typebox/build/esm/type/omit/omit-from-mapped-result.mjs
2132
+ // ../../node_modules/@sinclair/typebox/build/esm/type/omit/omit-from-mapped-result.mjs
2132
2133
  function FromProperties12(properties, propertyKeys, options) {
2133
2134
  const result = {};
2134
2135
  for (const K2 of globalThis.Object.getOwnPropertyNames(properties))
@@ -2143,7 +2144,7 @@ function OmitFromMappedResult(mappedResult, propertyKeys, options) {
2143
2144
  return MappedResult(properties);
2144
2145
  }
2145
2146
 
2146
- // node_modules/@sinclair/typebox/build/esm/type/omit/omit.mjs
2147
+ // ../../node_modules/@sinclair/typebox/build/esm/type/omit/omit.mjs
2147
2148
  function FromIntersect6(types, propertyKeys) {
2148
2149
  return types.map((type) => OmitResolve(type, propertyKeys));
2149
2150
  }
@@ -2177,7 +2178,7 @@ function Omit(type, key, options) {
2177
2178
  return IsMappedResult(type) ? OmitFromMappedResult(type, propertyKeys, options) : IsMappedKey(key) ? OmitFromMappedKey(type, key, options) : isTypeRef && isKeyRef ? Computed("Omit", [type, typeKey], options) : !isTypeRef && isKeyRef ? Computed("Omit", [type, typeKey], options) : isTypeRef && !isKeyRef ? Computed("Omit", [type, typeKey], options) : CreateType({ ...OmitResolve(type, propertyKeys), ...options });
2178
2179
  }
2179
2180
 
2180
- // node_modules/@sinclair/typebox/build/esm/type/omit/omit-from-mapped-key.mjs
2181
+ // ../../node_modules/@sinclair/typebox/build/esm/type/omit/omit-from-mapped-key.mjs
2181
2182
  function FromPropertyKey2(type, key, options) {
2182
2183
  return { [key]: Omit(type, [key], Clone(options)) };
2183
2184
  }
@@ -2194,7 +2195,7 @@ function OmitFromMappedKey(type, mappedKey, options) {
2194
2195
  return MappedResult(properties);
2195
2196
  }
2196
2197
 
2197
- // node_modules/@sinclair/typebox/build/esm/type/pick/pick-from-mapped-result.mjs
2198
+ // ../../node_modules/@sinclair/typebox/build/esm/type/pick/pick-from-mapped-result.mjs
2198
2199
  function FromProperties14(properties, propertyKeys, options) {
2199
2200
  const result = {};
2200
2201
  for (const K2 of globalThis.Object.getOwnPropertyNames(properties))
@@ -2209,7 +2210,7 @@ function PickFromMappedResult(mappedResult, propertyKeys, options) {
2209
2210
  return MappedResult(properties);
2210
2211
  }
2211
2212
 
2212
- // node_modules/@sinclair/typebox/build/esm/type/pick/pick.mjs
2213
+ // ../../node_modules/@sinclair/typebox/build/esm/type/pick/pick.mjs
2213
2214
  function FromIntersect7(types, propertyKeys) {
2214
2215
  return types.map((type) => PickResolve(type, propertyKeys));
2215
2216
  }
@@ -2243,7 +2244,7 @@ function Pick(type, key, options) {
2243
2244
  return IsMappedResult(type) ? PickFromMappedResult(type, propertyKeys, options) : IsMappedKey(key) ? PickFromMappedKey(type, key, options) : isTypeRef && isKeyRef ? Computed("Pick", [type, typeKey], options) : !isTypeRef && isKeyRef ? Computed("Pick", [type, typeKey], options) : isTypeRef && !isKeyRef ? Computed("Pick", [type, typeKey], options) : CreateType({ ...PickResolve(type, propertyKeys), ...options });
2244
2245
  }
2245
2246
 
2246
- // node_modules/@sinclair/typebox/build/esm/type/pick/pick-from-mapped-key.mjs
2247
+ // ../../node_modules/@sinclair/typebox/build/esm/type/pick/pick-from-mapped-key.mjs
2247
2248
  function FromPropertyKey3(type, key, options) {
2248
2249
  return {
2249
2250
  [key]: Pick(type, [key], Clone(options))
@@ -2262,7 +2263,7 @@ function PickFromMappedKey(type, mappedKey, options) {
2262
2263
  return MappedResult(properties);
2263
2264
  }
2264
2265
 
2265
- // node_modules/@sinclair/typebox/build/esm/type/partial/partial.mjs
2266
+ // ../../node_modules/@sinclair/typebox/build/esm/type/partial/partial.mjs
2266
2267
  function FromComputed3(target, parameters) {
2267
2268
  return Computed("Partial", [Computed(target, parameters)]);
2268
2269
  }
@@ -2303,7 +2304,7 @@ function Partial(type, options) {
2303
2304
  }
2304
2305
  }
2305
2306
 
2306
- // node_modules/@sinclair/typebox/build/esm/type/partial/partial-from-mapped-result.mjs
2307
+ // ../../node_modules/@sinclair/typebox/build/esm/type/partial/partial-from-mapped-result.mjs
2307
2308
  function FromProperties17(K, options) {
2308
2309
  const Acc = {};
2309
2310
  for (const K2 of globalThis.Object.getOwnPropertyNames(K))
@@ -2318,7 +2319,7 @@ function PartialFromMappedResult(R, options) {
2318
2319
  return MappedResult(P);
2319
2320
  }
2320
2321
 
2321
- // node_modules/@sinclair/typebox/build/esm/type/required/required.mjs
2322
+ // ../../node_modules/@sinclair/typebox/build/esm/type/required/required.mjs
2322
2323
  function FromComputed4(target, parameters) {
2323
2324
  return Computed("Required", [Computed(target, parameters)]);
2324
2325
  }
@@ -2359,7 +2360,7 @@ function Required(type, options) {
2359
2360
  }
2360
2361
  }
2361
2362
 
2362
- // node_modules/@sinclair/typebox/build/esm/type/required/required-from-mapped-result.mjs
2363
+ // ../../node_modules/@sinclair/typebox/build/esm/type/required/required-from-mapped-result.mjs
2363
2364
  function FromProperties19(P, options) {
2364
2365
  const Acc = {};
2365
2366
  for (const K2 of globalThis.Object.getOwnPropertyNames(P))
@@ -2374,7 +2375,7 @@ function RequiredFromMappedResult(R, options) {
2374
2375
  return MappedResult(P);
2375
2376
  }
2376
2377
 
2377
- // node_modules/@sinclair/typebox/build/esm/type/module/compute.mjs
2378
+ // ../../node_modules/@sinclair/typebox/build/esm/type/module/compute.mjs
2378
2379
  function DereferenceParameters(moduleProperties, types) {
2379
2380
  return types.map((type) => {
2380
2381
  return IsRef(type) ? Dereference(moduleProperties, type.$ref) : FromType2(moduleProperties, type);
@@ -2470,7 +2471,7 @@ function ComputeModuleProperties(moduleProperties) {
2470
2471
  }, {});
2471
2472
  }
2472
2473
 
2473
- // node_modules/@sinclair/typebox/build/esm/type/module/module.mjs
2474
+ // ../../node_modules/@sinclair/typebox/build/esm/type/module/module.mjs
2474
2475
  var TModule = class {
2475
2476
  constructor($defs) {
2476
2477
  const computed = ComputeModuleProperties($defs);
@@ -2493,17 +2494,17 @@ function Module(properties) {
2493
2494
  return new TModule(properties);
2494
2495
  }
2495
2496
 
2496
- // node_modules/@sinclair/typebox/build/esm/type/not/not.mjs
2497
+ // ../../node_modules/@sinclair/typebox/build/esm/type/not/not.mjs
2497
2498
  function Not(type, options) {
2498
2499
  return CreateType({ [Kind]: "Not", not: type }, options);
2499
2500
  }
2500
2501
 
2501
- // node_modules/@sinclair/typebox/build/esm/type/parameters/parameters.mjs
2502
+ // ../../node_modules/@sinclair/typebox/build/esm/type/parameters/parameters.mjs
2502
2503
  function Parameters(schema, options) {
2503
2504
  return IsFunction2(schema) ? Tuple(schema.parameters, options) : Never();
2504
2505
  }
2505
2506
 
2506
- // node_modules/@sinclair/typebox/build/esm/type/recursive/recursive.mjs
2507
+ // ../../node_modules/@sinclair/typebox/build/esm/type/recursive/recursive.mjs
2507
2508
  var Ordinal = 0;
2508
2509
  function Recursive(callback, options = {}) {
2509
2510
  if (IsUndefined(options.$id))
@@ -2513,13 +2514,13 @@ function Recursive(callback, options = {}) {
2513
2514
  return CreateType({ [Hint]: "Recursive", ...thisType }, options);
2514
2515
  }
2515
2516
 
2516
- // node_modules/@sinclair/typebox/build/esm/type/regexp/regexp.mjs
2517
+ // ../../node_modules/@sinclair/typebox/build/esm/type/regexp/regexp.mjs
2517
2518
  function RegExp2(unresolved, options) {
2518
2519
  const expr = IsString(unresolved) ? new globalThis.RegExp(unresolved) : unresolved;
2519
2520
  return CreateType({ [Kind]: "RegExp", type: "RegExp", source: expr.source, flags: expr.flags }, options);
2520
2521
  }
2521
2522
 
2522
- // node_modules/@sinclair/typebox/build/esm/type/rest/rest.mjs
2523
+ // ../../node_modules/@sinclair/typebox/build/esm/type/rest/rest.mjs
2523
2524
  function RestResolve(T) {
2524
2525
  return IsIntersect(T) ? T.allOf : IsUnion(T) ? T.anyOf : IsTuple(T) ? T.items ?? [] : [];
2525
2526
  }
@@ -2527,12 +2528,12 @@ function Rest(T) {
2527
2528
  return RestResolve(T);
2528
2529
  }
2529
2530
 
2530
- // node_modules/@sinclair/typebox/build/esm/type/return-type/return-type.mjs
2531
+ // ../../node_modules/@sinclair/typebox/build/esm/type/return-type/return-type.mjs
2531
2532
  function ReturnType(schema, options) {
2532
2533
  return IsFunction2(schema) ? CreateType(schema.returns, options) : Never(options);
2533
2534
  }
2534
2535
 
2535
- // node_modules/@sinclair/typebox/build/esm/type/transform/transform.mjs
2536
+ // ../../node_modules/@sinclair/typebox/build/esm/type/transform/transform.mjs
2536
2537
  var TransformDecodeBuilder = class {
2537
2538
  constructor(schema) {
2538
2539
  this.schema = schema;
@@ -2564,17 +2565,17 @@ function Transform(schema) {
2564
2565
  return new TransformDecodeBuilder(schema);
2565
2566
  }
2566
2567
 
2567
- // node_modules/@sinclair/typebox/build/esm/type/unsafe/unsafe.mjs
2568
+ // ../../node_modules/@sinclair/typebox/build/esm/type/unsafe/unsafe.mjs
2568
2569
  function Unsafe(options = {}) {
2569
2570
  return CreateType({ [Kind]: options[Kind] ?? "Unsafe" }, options);
2570
2571
  }
2571
2572
 
2572
- // node_modules/@sinclair/typebox/build/esm/type/void/void.mjs
2573
+ // ../../node_modules/@sinclair/typebox/build/esm/type/void/void.mjs
2573
2574
  function Void(options) {
2574
2575
  return CreateType({ [Kind]: "Void", type: "void" }, options);
2575
2576
  }
2576
2577
 
2577
- // node_modules/@sinclair/typebox/build/esm/type/type/type.mjs
2578
+ // ../../node_modules/@sinclair/typebox/build/esm/type/type/type.mjs
2578
2579
  var type_exports2 = {};
2579
2580
  __export(type_exports2, {
2580
2581
  Any: () => Any,
@@ -2641,9 +2642,1453 @@ __export(type_exports2, {
2641
2642
  Void: () => Void
2642
2643
  });
2643
2644
 
2644
- // node_modules/@sinclair/typebox/build/esm/type/type/index.mjs
2645
+ // ../../node_modules/@sinclair/typebox/build/esm/type/type/index.mjs
2645
2646
  var Type = type_exports2;
2646
2647
 
2648
+ // src/local-client.ts
2649
+ var http = __toESM(require("node:http"));
2650
+ var import_node_url = require("node:url");
2651
+ var SulcusLocalClient = class {
2652
+ endpoint;
2653
+ apiKey;
2654
+ timeoutMs;
2655
+ maxConsecutiveFailures;
2656
+ cooldownMs;
2657
+ logger;
2658
+ // Health tracking
2659
+ consecutiveFailures = 0;
2660
+ lastFailureAt = 0;
2661
+ lastSuccessAt = 0;
2662
+ constructor(opts) {
2663
+ this.endpoint = opts.endpoint.replace(/\/+$/, "");
2664
+ this.apiKey = opts.apiKey ?? "";
2665
+ this.timeoutMs = opts.timeoutMs ?? 2e3;
2666
+ this.maxConsecutiveFailures = opts.maxConsecutiveFailures ?? 3;
2667
+ this.cooldownMs = opts.cooldownMs ?? 3e4;
2668
+ this.logger = opts.logger ?? { info: () => {
2669
+ }, warn: () => {
2670
+ }, debug: () => {
2671
+ } };
2672
+ }
2673
+ /**
2674
+ * Whether the local sidecar is considered available.
2675
+ * False if too many consecutive failures and cooldown hasn't elapsed.
2676
+ */
2677
+ isAvailable() {
2678
+ if (this.consecutiveFailures < this.maxConsecutiveFailures) return true;
2679
+ if (Date.now() - this.lastFailureAt > this.cooldownMs) return true;
2680
+ return false;
2681
+ }
2682
+ /** Reset health state (e.g. on config change or manual intervention). */
2683
+ resetHealth() {
2684
+ this.consecutiveFailures = 0;
2685
+ this.lastFailureAt = 0;
2686
+ }
2687
+ /** Mark a successful request. */
2688
+ markSuccess() {
2689
+ this.consecutiveFailures = 0;
2690
+ this.lastSuccessAt = Date.now();
2691
+ }
2692
+ /** Mark a failed request. */
2693
+ markFailure() {
2694
+ this.consecutiveFailures++;
2695
+ this.lastFailureAt = Date.now();
2696
+ }
2697
+ /** Health summary for diagnostics. */
2698
+ healthSummary() {
2699
+ return {
2700
+ available: this.isAvailable(),
2701
+ consecutiveFailures: this.consecutiveFailures,
2702
+ lastSuccessAt: this.lastSuccessAt,
2703
+ lastFailureAt: this.lastFailureAt
2704
+ };
2705
+ }
2706
+ // ── HTTP transport ──────────────────────────────────────────────────────
2707
+ /**
2708
+ * Make an HTTP request to the local sidecar. No retries — fail fast.
2709
+ */
2710
+ request(method, path, body) {
2711
+ if (!this.isAvailable()) {
2712
+ return Promise.reject(new Error(`sulcus-local: sidecar unavailable (${this.consecutiveFailures} consecutive failures, cooldown active)`));
2713
+ }
2714
+ let parsedUrl;
2715
+ try {
2716
+ parsedUrl = new import_node_url.URL(this.endpoint + path);
2717
+ } catch (e) {
2718
+ const msg = e instanceof Error ? e.message : String(e);
2719
+ return Promise.reject(new Error(`sulcus-local: invalid URL ${this.endpoint}${path}: ${msg}`));
2720
+ }
2721
+ const bodyStr = body !== void 0 ? JSON.stringify(body) : void 0;
2722
+ return new Promise((resolve2, reject) => {
2723
+ const headers = {
2724
+ Accept: "application/json"
2725
+ };
2726
+ if (this.apiKey) {
2727
+ headers["Authorization"] = `Bearer ${this.apiKey}`;
2728
+ }
2729
+ if (bodyStr !== void 0) {
2730
+ headers["Content-Type"] = "application/json";
2731
+ headers["Content-Length"] = String(Buffer.byteLength(bodyStr));
2732
+ }
2733
+ const req = http.request(
2734
+ {
2735
+ hostname: parsedUrl.hostname,
2736
+ port: parsedUrl.port ? parseInt(parsedUrl.port, 10) : 80,
2737
+ path: parsedUrl.pathname + parsedUrl.search,
2738
+ method,
2739
+ headers,
2740
+ timeout: this.timeoutMs
2741
+ },
2742
+ (res) => {
2743
+ const chunks = [];
2744
+ res.on("data", (chunk) => chunks.push(chunk));
2745
+ res.on("end", () => {
2746
+ const raw = Buffer.concat(chunks).toString("utf-8");
2747
+ if (!res.statusCode || res.statusCode >= 400) {
2748
+ this.markFailure();
2749
+ return reject(new Error(`sulcus-local: HTTP ${res.statusCode} for ${method} ${path}: ${raw.substring(0, 200)}`));
2750
+ }
2751
+ this.markSuccess();
2752
+ if (!raw || raw.trim() === "") return resolve2(null);
2753
+ try {
2754
+ resolve2(JSON.parse(raw));
2755
+ } catch {
2756
+ resolve2(raw);
2757
+ }
2758
+ });
2759
+ }
2760
+ );
2761
+ req.on("timeout", () => {
2762
+ req.destroy();
2763
+ this.markFailure();
2764
+ reject(new Error(`sulcus-local: timeout (${this.timeoutMs}ms) for ${method} ${path}`));
2765
+ });
2766
+ req.on("error", (e) => {
2767
+ this.markFailure();
2768
+ reject(new Error(`sulcus-local: network error for ${method} ${path}: ${e.message}`));
2769
+ });
2770
+ if (bodyStr !== void 0) req.write(bodyStr);
2771
+ req.end();
2772
+ });
2773
+ }
2774
+ // ── API methods (mirror SulcusCloudClient's interface) ──────────────────
2775
+ /**
2776
+ * Search memory by semantic query.
2777
+ * Uses the same `/api/v1/agent/search` endpoint as the cloud client.
2778
+ */
2779
+ async search_memory(query, limit, namespace) {
2780
+ const body = { query };
2781
+ if (limit !== void 0) body.limit = limit;
2782
+ if (namespace !== void 0) body.namespace = namespace;
2783
+ const res = await this.request("POST", "/api/v1/agent/search", body);
2784
+ const results = res?.results ?? res?.items ?? res?.nodes ?? (Array.isArray(res) ? res : []);
2785
+ return { results };
2786
+ }
2787
+ /**
2788
+ * Store a new memory node.
2789
+ * Uses the same `/api/v1/agent/nodes` endpoint as the cloud client.
2790
+ */
2791
+ async add_memory(content, memoryType, hints) {
2792
+ const body = { label: content };
2793
+ if (memoryType) body.memory_type = memoryType;
2794
+ if (hints) body.extraction_hints = hints;
2795
+ const res = await this.request("POST", "/api/v1/agent/nodes", body);
2796
+ return res ?? { id: "unknown" };
2797
+ }
2798
+ /**
2799
+ * Get a single memory node by ID.
2800
+ */
2801
+ async get_memory(id) {
2802
+ try {
2803
+ const res = await this.request("GET", `/api/v1/agent/nodes/${id}`);
2804
+ return res;
2805
+ } catch {
2806
+ return null;
2807
+ }
2808
+ }
2809
+ /**
2810
+ * Update an existing memory node.
2811
+ */
2812
+ async update_memory(id, updates) {
2813
+ const res = await this.request("PATCH", `/api/v1/agent/nodes/${id}`, updates);
2814
+ return res;
2815
+ }
2816
+ /**
2817
+ * List hot nodes (most active memories).
2818
+ */
2819
+ async list_hot_nodes(limit) {
2820
+ const q = limit ? `?limit=${limit}` : "";
2821
+ const res = await this.request("GET", `/api/v1/agent/hot_nodes${q}`);
2822
+ const nodes = Array.isArray(res) ? res : res?.hot_nodes ?? res?.nodes ?? [];
2823
+ return { nodes };
2824
+ }
2825
+ /**
2826
+ * Delete a memory node.
2827
+ */
2828
+ async delete_memory(id) {
2829
+ return this.request("DELETE", `/api/v1/agent/nodes/${id}`);
2830
+ }
2831
+ /**
2832
+ * Health probe — check if sidecar is reachable.
2833
+ * Returns true if endpoint responds, false otherwise.
2834
+ */
2835
+ async probe() {
2836
+ try {
2837
+ await this.request("GET", "/api/v1/agent/hot_nodes?limit=1");
2838
+ return true;
2839
+ } catch {
2840
+ return false;
2841
+ }
2842
+ }
2843
+ /**
2844
+ * Batch heat boost — boost multiple nodes at once.
2845
+ */
2846
+ async boost_batch(boosts) {
2847
+ try {
2848
+ await this.request("POST", "/api/v1/agent/boost_batch", { boosts });
2849
+ return true;
2850
+ } catch {
2851
+ return false;
2852
+ }
2853
+ }
2854
+ };
2855
+
2856
+ // src/retry-queue.ts
2857
+ var RetryQueue = class {
2858
+ items = /* @__PURE__ */ new Map();
2859
+ maxItems;
2860
+ maxRetries;
2861
+ logger;
2862
+ flushing = false;
2863
+ constructor(opts = {}) {
2864
+ this.maxItems = opts.maxItems ?? 500;
2865
+ this.maxRetries = opts.maxRetries ?? 5;
2866
+ this.logger = opts.logger ?? { info: () => {
2867
+ }, warn: () => {
2868
+ }, debug: () => {
2869
+ } };
2870
+ }
2871
+ /** Number of items currently queued. */
2872
+ get size() {
2873
+ return this.items.size;
2874
+ }
2875
+ /** Whether a flush is currently in progress. */
2876
+ get isFlushing() {
2877
+ return this.flushing;
2878
+ }
2879
+ /**
2880
+ * Enqueue an operation for retry.
2881
+ * If an item with the same key already exists, it's updated (latest payload wins).
2882
+ */
2883
+ enqueue(key, operation, payload) {
2884
+ const existing = this.items.get(key);
2885
+ if (existing) {
2886
+ existing.payload = payload;
2887
+ existing.operation = operation;
2888
+ this.logger.debug(`sulcus-retry: updated existing item ${key} (attempts: ${existing.attempts})`);
2889
+ return;
2890
+ }
2891
+ if (this.items.size >= this.maxItems) {
2892
+ const oldestKey = this.items.keys().next().value;
2893
+ if (oldestKey !== void 0) {
2894
+ this.items.delete(oldestKey);
2895
+ this.logger.warn(`sulcus-retry: queue full (${this.maxItems}), evicted oldest item ${oldestKey}`);
2896
+ }
2897
+ }
2898
+ this.items.set(key, {
2899
+ key,
2900
+ operation,
2901
+ payload,
2902
+ attempts: 0,
2903
+ enqueuedAt: Date.now(),
2904
+ lastAttemptAt: 0
2905
+ });
2906
+ this.logger.debug(`sulcus-retry: enqueued ${operation} for ${key} (queue size: ${this.items.size})`);
2907
+ }
2908
+ /**
2909
+ * Flush the queue — attempt all pending retries.
2910
+ *
2911
+ * Calls `executor` for each item. If the executor succeeds, the item is removed.
2912
+ * If it throws, the item's attempt count is incremented; items exceeding
2913
+ * `maxRetries` are dropped.
2914
+ *
2915
+ * Returns the number of successfully flushed items.
2916
+ */
2917
+ async flush(executor) {
2918
+ if (this.flushing) {
2919
+ this.logger.debug("sulcus-retry: flush already in progress, skipping");
2920
+ return { flushed: 0, failed: 0, dropped: 0 };
2921
+ }
2922
+ if (this.items.size === 0) {
2923
+ return { flushed: 0, failed: 0, dropped: 0 };
2924
+ }
2925
+ this.flushing = true;
2926
+ let flushed = 0;
2927
+ let failed = 0;
2928
+ let dropped = 0;
2929
+ const keys = [...this.items.keys()];
2930
+ for (const key of keys) {
2931
+ const item = this.items.get(key);
2932
+ if (!item) continue;
2933
+ item.attempts++;
2934
+ item.lastAttemptAt = Date.now();
2935
+ try {
2936
+ await executor(item);
2937
+ this.items.delete(key);
2938
+ flushed++;
2939
+ } catch (err) {
2940
+ const msg = err instanceof Error ? err.message : String(err);
2941
+ item.lastError = msg;
2942
+ if (item.attempts >= this.maxRetries) {
2943
+ this.items.delete(key);
2944
+ dropped++;
2945
+ this.logger.warn(`sulcus-retry: dropped ${item.operation} for ${key} after ${item.attempts} attempts: ${msg}`);
2946
+ } else {
2947
+ failed++;
2948
+ this.logger.debug(`sulcus-retry: ${item.operation} for ${key} failed (attempt ${item.attempts}/${this.maxRetries}): ${msg}`);
2949
+ }
2950
+ }
2951
+ }
2952
+ this.flushing = false;
2953
+ if (flushed > 0 || dropped > 0) {
2954
+ this.logger.info(`sulcus-retry: flush complete \u2014 flushed: ${flushed}, failed: ${failed}, dropped: ${dropped}, remaining: ${this.items.size}`);
2955
+ }
2956
+ return { flushed, failed, dropped };
2957
+ }
2958
+ /** Clear all items from the queue. */
2959
+ clear() {
2960
+ this.items.clear();
2961
+ }
2962
+ /** Get a diagnostic snapshot of the queue state. */
2963
+ snapshot() {
2964
+ return {
2965
+ size: this.items.size,
2966
+ flushing: this.flushing,
2967
+ items: [...this.items.values()]
2968
+ };
2969
+ }
2970
+ };
2971
+
2972
+ // src/context-engine.ts
2973
+ var DEFAULT_THRESHOLDS = {
2974
+ compactionTriggerRatio: 0.75,
2975
+ trimTriggerRatio: 0.65,
2976
+ largeResultChars: 3e3,
2977
+ trimHeadChars: 1500,
2978
+ trimTailChars: 1500,
2979
+ emergencyHeadChars: 500,
2980
+ emergencyTailChars: 500,
2981
+ emergencyBrakeRatio: 0.9,
2982
+ minTurnsBetweenCompaction: 3,
2983
+ highGrowthRateThreshold: 1e4,
2984
+ charsPerToken: 4,
2985
+ captureMinChars: 4e3,
2986
+ maxCapturesPerTurn: 3,
2987
+ captureTriggerRatio: 0.55,
2988
+ cumulativeToolCharsThreshold: 5e4,
2989
+ cumulativePressureRatio: 0.5,
2990
+ knowledgeCaptureInterval: 8,
2991
+ knowledgeCaptureRatio: 0.4,
2992
+ constructiveMinRecentTurns: 4,
2993
+ assemblyInjectRatio: 0.85,
2994
+ assemblyRecallRatio: 0.7,
2995
+ assemblyRecallCapRatio: 0.8,
2996
+ constructiveRecentBudgetRatio: 0.6,
2997
+ compressSentenceMaxChars: 200,
2998
+ compressMaxChars: 600,
2999
+ sessionTtlMs: 2 * 60 * 60 * 1e3
3000
+ // 2 hours
3001
+ };
3002
+ var DECISION_MARKERS = ["decided", "will use", "going to", "plan is", "the fix", "conclusion", "recommend", "approach"];
3003
+ var SulcusContextEngine = class {
3004
+ info;
3005
+ logger;
3006
+ delegateCompaction;
3007
+ assemblyMode;
3008
+ compactMode;
3009
+ memoryClient;
3010
+ namespace;
3011
+ /** Merged thresholds (defaults + user overrides). */
3012
+ t;
3013
+ // Compaction tracking per session
3014
+ lastCompactionTurn = /* @__PURE__ */ new Map();
3015
+ turnCounter = /* @__PURE__ */ new Map();
3016
+ // Phase 4: Track message IDs already captured to Sulcus
3017
+ capturedMsgIds = /* @__PURE__ */ new Map();
3018
+ // Phase 5: Track session knowledge capture turns
3019
+ lastKnowledgeCaptureTurn = /* @__PURE__ */ new Map();
3020
+ // Phase 5.5: Growth rate tracking (tokens at previous turn, for delta)
3021
+ lastTokenCount = /* @__PURE__ */ new Map();
3022
+ growthRate = /* @__PURE__ */ new Map();
3023
+ // tokens/turn EMA
3024
+ // Phase 5.5: Cumulative tool result chars per session
3025
+ cumulativeToolChars = /* @__PURE__ */ new Map();
3026
+ // Bug fix: Track which message IDs have been counted for cumulative tool chars
3027
+ countedToolMsgIds = /* @__PURE__ */ new Map();
3028
+ // Phase 6: Working memory cache per session — summaries of tool results
3029
+ workingMemory = /* @__PURE__ */ new Map();
3030
+ // Bug fix: Track last-scanned message index for knowledge capture per session
3031
+ lastKnowledgeScanIndex = /* @__PURE__ */ new Map();
3032
+ // Bug fix: Track last activity timestamp per session for TTL eviction
3033
+ sessionLastActivity = /* @__PURE__ */ new Map();
3034
+ constructor(config) {
3035
+ this.logger = config.logger;
3036
+ this.delegateCompaction = config.delegateCompaction;
3037
+ this.assemblyMode = config.assemblyMode;
3038
+ this.compactMode = config.compactMode ?? "smart";
3039
+ this.memoryClient = config.memoryClient ?? null;
3040
+ this.namespace = config.namespace ?? "default";
3041
+ this.t = { ...DEFAULT_THRESHOLDS, ...config.thresholds };
3042
+ this.info = {
3043
+ id: "openclaw-sulcus",
3044
+ name: "Sulcus Context Engine",
3045
+ version: config.version,
3046
+ ownsCompaction: true,
3047
+ turnMaintenanceMode: "foreground"
3048
+ };
3049
+ this.logger.info(
3050
+ `sulcus-context-engine: initialized v${config.version} (assembly=${this.assemblyMode}, compact=${this.compactMode}, capture=unified, overflow=hardened)`
3051
+ );
3052
+ this.logger.info(
3053
+ `sulcus-context-engine: thresholds: ${JSON.stringify(this.t)}`
3054
+ );
3055
+ }
3056
+ // ---------------------------------------------------------------------------
3057
+ // Bootstrap / Maintain / Ingest — still no-op
3058
+ // ---------------------------------------------------------------------------
3059
+ async bootstrap(_params) {
3060
+ return { bootstrapped: false, reason: "phase-5" };
3061
+ }
3062
+ async maintain(_params) {
3063
+ return { changed: false, bytesFreed: 0, rewrittenEntries: 0, reason: "phase-5" };
3064
+ }
3065
+ /**
3066
+ * Ingest a single message into working memory.
3067
+ * For tool results: cache metadata, store full content to Sulcus (SILU generates pointer_summary).
3068
+ * Uses unified capturedMsgIds dedup guard to prevent triple-ingestion.
3069
+ */
3070
+ async ingest(params) {
3071
+ const { sessionId, message } = params;
3072
+ if (!message || message.role !== "tool" || typeof message.content !== "string") {
3073
+ return { ingested: false };
3074
+ }
3075
+ if (!message.id || message.content.length < 200) {
3076
+ return { ingested: false };
3077
+ }
3078
+ const sessionCache = this.getSessionCache(sessionId);
3079
+ if (sessionCache.has(message.id)) {
3080
+ return { ingested: false };
3081
+ }
3082
+ const sessionCaptured = this.getSessionCapturedIds(sessionId);
3083
+ const toolName = message.name || "tool";
3084
+ const turn = this.turnCounter.get(sessionId) ?? 0;
3085
+ const entry = {
3086
+ messageId: message.id,
3087
+ toolName,
3088
+ originalLength: message.content.length,
3089
+ turn
3090
+ };
3091
+ if (this.memoryClient && message.content.length >= this.t.captureMinChars && !sessionCaptured.has(message.id)) {
3092
+ try {
3093
+ const res = await this.memoryClient.add_memory(
3094
+ `[Tool: ${toolName}]
3095
+ ${message.content}`,
3096
+ "episodic",
3097
+ { key_points: [`tool-result:${toolName}`, `session:${sessionId}`, `msg:${message.id}`] }
3098
+ );
3099
+ entry.sulcusNodeId = res?.id;
3100
+ sessionCaptured.add(message.id);
3101
+ this.logger.debug(`sulcus-ce: ingested tool result to memory (${toolName}, ${message.content.length} chars) [msg=${message.id}]`);
3102
+ } catch {
3103
+ this.logger.debug(`sulcus-ce: memory store failed for ingest [msg=${message.id}]`);
3104
+ }
3105
+ }
3106
+ sessionCache.set(message.id, entry);
3107
+ return { ingested: true };
3108
+ }
3109
+ /**
3110
+ * Batch ingest messages into working memory.
3111
+ */
3112
+ async ingestBatch(params) {
3113
+ const { sessionId, messages } = params;
3114
+ if (!messages || !Array.isArray(messages)) return { ingestedCount: 0 };
3115
+ let count = 0;
3116
+ for (const msg of messages) {
3117
+ const res = await this.ingest({ sessionId, message: msg });
3118
+ if (res.ingested) count++;
3119
+ }
3120
+ return { ingestedCount: count };
3121
+ }
3122
+ /** Get or create the working memory cache for a session. */
3123
+ getSessionCache(sessionId) {
3124
+ let cache = this.workingMemory.get(sessionId);
3125
+ if (!cache) {
3126
+ cache = /* @__PURE__ */ new Map();
3127
+ this.workingMemory.set(sessionId, cache);
3128
+ }
3129
+ return cache;
3130
+ }
3131
+ /** Get or create the captured message ID set for a session (unified dedup guard). */
3132
+ getSessionCapturedIds(sessionId) {
3133
+ let ids = this.capturedMsgIds.get(sessionId);
3134
+ if (!ids) {
3135
+ ids = /* @__PURE__ */ new Set();
3136
+ this.capturedMsgIds.set(sessionId, ids);
3137
+ }
3138
+ return ids;
3139
+ }
3140
+ /** Get or create the counted tool message ID set for a session. */
3141
+ getSessionCountedIds(sessionId) {
3142
+ let ids = this.countedToolMsgIds.get(sessionId);
3143
+ if (!ids) {
3144
+ ids = /* @__PURE__ */ new Set();
3145
+ this.countedToolMsgIds.set(sessionId, ids);
3146
+ }
3147
+ return ids;
3148
+ }
3149
+ // ---------------------------------------------------------------------------
3150
+ // Session lifecycle — cleanup + TTL eviction
3151
+ // ---------------------------------------------------------------------------
3152
+ /**
3153
+ * Clear all per-session state for a given session.
3154
+ * Call when a session ends (e.g. from onSubagentEnded or dispose).
3155
+ */
3156
+ clearSession(sessionId) {
3157
+ this.turnCounter.delete(sessionId);
3158
+ this.lastCompactionTurn.delete(sessionId);
3159
+ this.capturedMsgIds.delete(sessionId);
3160
+ this.countedToolMsgIds.delete(sessionId);
3161
+ this.lastKnowledgeCaptureTurn.delete(sessionId);
3162
+ this.lastKnowledgeScanIndex.delete(sessionId);
3163
+ this.lastTokenCount.delete(sessionId);
3164
+ this.growthRate.delete(sessionId);
3165
+ this.cumulativeToolChars.delete(sessionId);
3166
+ this.workingMemory.delete(sessionId);
3167
+ this.sessionLastActivity.delete(sessionId);
3168
+ this.logger.debug(`sulcus-ce: cleared session state [session=${sessionId}]`);
3169
+ }
3170
+ /**
3171
+ * Evict stale sessions that haven't been active within the TTL window.
3172
+ * Called periodically from afterTurn to prevent unbounded memory growth.
3173
+ */
3174
+ evictStaleSessions() {
3175
+ const now = Date.now();
3176
+ const ttl = this.t.sessionTtlMs;
3177
+ const stale = [];
3178
+ for (const [sessionId, lastActivity] of this.sessionLastActivity) {
3179
+ if (now - lastActivity > ttl) {
3180
+ stale.push(sessionId);
3181
+ }
3182
+ }
3183
+ for (const sessionId of stale) {
3184
+ this.clearSession(sessionId);
3185
+ }
3186
+ if (stale.length > 0) {
3187
+ this.logger.info(`sulcus-ce: evicted ${stale.length} stale session(s) (TTL=${ttl}ms)`);
3188
+ }
3189
+ }
3190
+ // ---------------------------------------------------------------------------
3191
+ // afterTurn — THE OVERFLOW PREVENTION + KNOWLEDGE CAPTURE
3192
+ // ---------------------------------------------------------------------------
3193
+ async afterTurn(params) {
3194
+ const {
3195
+ sessionId,
3196
+ sessionFile,
3197
+ messages,
3198
+ tokenBudget,
3199
+ runtimeContext
3200
+ } = params;
3201
+ const turn = (this.turnCounter.get(sessionId) ?? 0) + 1;
3202
+ this.turnCounter.set(sessionId, turn);
3203
+ this.sessionLastActivity.set(sessionId, Date.now());
3204
+ if (turn % 10 === 0) {
3205
+ this.evictStaleSessions();
3206
+ }
3207
+ const budget = tokenBudget ?? runtimeContext?.tokenBudget;
3208
+ const currentTokens = runtimeContext?.currentTokenCount;
3209
+ if (!budget || !currentTokens) return;
3210
+ const usage = currentTokens / budget;
3211
+ const usagePct = (usage * 100).toFixed(1);
3212
+ const prevTokens = this.lastTokenCount.get(sessionId);
3213
+ const hasPrevious = prevTokens !== void 0;
3214
+ this.lastTokenCount.set(sessionId, currentTokens);
3215
+ let newGrowth;
3216
+ if (!hasPrevious) {
3217
+ newGrowth = 0;
3218
+ } else {
3219
+ const tokensAddedThisTurn = Math.max(0, currentTokens - prevTokens);
3220
+ const prevGrowth = this.growthRate.get(sessionId) ?? 0;
3221
+ newGrowth = Math.round(0.3 * tokensAddedThisTurn + 0.7 * prevGrowth);
3222
+ }
3223
+ this.growthRate.set(sessionId, newGrowth);
3224
+ let sessionToolChars = this.cumulativeToolChars.get(sessionId) ?? 0;
3225
+ const countedIds = this.getSessionCountedIds(sessionId);
3226
+ for (const msg of messages) {
3227
+ if (msg.role === "tool" && typeof msg.content === "string" && msg.id) {
3228
+ if (countedIds.has(msg.id)) continue;
3229
+ if (!msg.content.includes("[\u2026 trimmed by sulcus-ce") && !msg.content.includes("[captured by sulcus-ce")) {
3230
+ sessionToolChars += msg.content.length;
3231
+ countedIds.add(msg.id);
3232
+ }
3233
+ }
3234
+ }
3235
+ this.cumulativeToolChars.set(sessionId, sessionToolChars);
3236
+ if (usage > 0.5) {
3237
+ this.logger.debug(
3238
+ `sulcus-ce: context pressure ${usagePct}% (${currentTokens}/${budget} tokens, growth: ${newGrowth} tok/turn, cumToolChars: ${sessionToolChars}) [session=${sessionId}, turn=${turn}]`
3239
+ );
3240
+ }
3241
+ if (usage >= this.t.captureTriggerRatio && this.memoryClient) {
3242
+ await this.captureToolResults(messages, sessionId, turn);
3243
+ }
3244
+ if (usage >= this.t.knowledgeCaptureRatio && this.memoryClient && turn - (this.lastKnowledgeCaptureTurn.get(sessionId) ?? 0) >= this.t.knowledgeCaptureInterval) {
3245
+ await this.captureSessionKnowledge(messages, sessionId, turn, usagePct);
3246
+ }
3247
+ if (this.assemblyMode === "constructive") {
3248
+ const sessionCache = this.getSessionCache(sessionId);
3249
+ const sessionCapturedCtv = this.getSessionCapturedIds(sessionId);
3250
+ let newCached = 0;
3251
+ for (const msg of messages) {
3252
+ if (msg.role !== "tool" || typeof msg.content !== "string" || !msg.id) continue;
3253
+ if (sessionCache.has(msg.id)) continue;
3254
+ if (msg.content.length < 200) continue;
3255
+ const toolName = msg.name || "tool";
3256
+ const entry = {
3257
+ messageId: msg.id,
3258
+ toolName,
3259
+ originalLength: msg.content.length,
3260
+ turn
3261
+ };
3262
+ if (this.memoryClient && msg.content.length >= this.t.captureMinChars && !sessionCapturedCtv.has(msg.id)) {
3263
+ this.memoryClient.add_memory(
3264
+ `[Tool: ${toolName}]
3265
+ ${msg.content}`,
3266
+ "episodic",
3267
+ { key_points: [`tool-result:${toolName}`, `session:${sessionId}`, `msg:${msg.id}`] }
3268
+ ).then((res) => {
3269
+ entry.sulcusNodeId = res?.id;
3270
+ sessionCapturedCtv.add(msg.id);
3271
+ }).catch(() => {
3272
+ });
3273
+ }
3274
+ sessionCache.set(msg.id, entry);
3275
+ newCached++;
3276
+ }
3277
+ if (newCached > 0) {
3278
+ this.logger.debug(`sulcus-ce: cached ${newCached} tool results (total: ${sessionCache.size}) [turn=${turn}]`);
3279
+ }
3280
+ if (usage >= this.t.compactionTriggerRatio) {
3281
+ const lastCompaction = this.lastCompactionTurn.get(sessionId) ?? 0;
3282
+ const turnsSinceCompaction = turn - lastCompaction;
3283
+ const minTurns = newGrowth >= this.t.highGrowthRateThreshold ? 1 : this.t.minTurnsBetweenCompaction;
3284
+ if (turnsSinceCompaction >= minTurns) {
3285
+ this.logger.info(
3286
+ `sulcus-ce: CONSTRUCTIVE COMPACTION at ${usagePct}% (${currentTokens}/${budget}). Growth: ${newGrowth} tok/turn [session=${sessionId}]`
3287
+ );
3288
+ try {
3289
+ const result = await this.delegateCompaction({
3290
+ sessionId,
3291
+ sessionFile: params.sessionFile,
3292
+ tokenBudget: budget,
3293
+ currentTokenCount: currentTokens,
3294
+ force: false,
3295
+ runtimeContext
3296
+ });
3297
+ this.lastCompactionTurn.set(sessionId, turn);
3298
+ if (result.compacted) {
3299
+ const saved = (result.result?.tokensBefore ?? 0) - (result.result?.tokensAfter ?? 0);
3300
+ this.logger.info(`sulcus-ce: constructive compaction succeeded \u2014 saved ~${saved} tokens`);
3301
+ }
3302
+ } catch (e) {
3303
+ this.logger.warn(`sulcus-ce: constructive compaction failed: ${e}`);
3304
+ }
3305
+ }
3306
+ }
3307
+ return;
3308
+ }
3309
+ if (usage >= this.t.emergencyBrakeRatio && runtimeContext?.rewriteTranscriptEntries) {
3310
+ this.logger.warn(
3311
+ `sulcus-ce: \u26A0\uFE0F EMERGENCY BRAKE at ${usagePct}% (${currentTokens}/${budget}) \u2014 aggressively trimming ALL tool results [session=${sessionId}, turn=${turn}]`
3312
+ );
3313
+ await this.emergencyTrimAllToolResults(messages, runtimeContext.rewriteTranscriptEntries, sessionId);
3314
+ }
3315
+ if (usage >= this.t.cumulativePressureRatio && sessionToolChars >= this.t.cumulativeToolCharsThreshold && runtimeContext?.rewriteTranscriptEntries && usage < this.t.emergencyBrakeRatio) {
3316
+ this.logger.info(
3317
+ `sulcus-ce: cumulative pressure trim \u2014 ${sessionToolChars} total tool chars, ${usagePct}% budget [session=${sessionId}]`
3318
+ );
3319
+ await this.trimCumulativePressure(messages, runtimeContext.rewriteTranscriptEntries, sessionId);
3320
+ }
3321
+ if (usage >= this.t.trimTriggerRatio && usage < this.t.emergencyBrakeRatio && // emergency already handled
3322
+ runtimeContext?.rewriteTranscriptEntries) {
3323
+ await this.trimLargeToolResults(messages, runtimeContext.rewriteTranscriptEntries, sessionId);
3324
+ }
3325
+ if (usage >= this.t.compactionTriggerRatio) {
3326
+ const lastCompaction = this.lastCompactionTurn.get(sessionId) ?? 0;
3327
+ const turnsSinceCompaction = turn - lastCompaction;
3328
+ const minTurns = newGrowth >= this.t.highGrowthRateThreshold ? 1 : this.t.minTurnsBetweenCompaction;
3329
+ if (turnsSinceCompaction >= minTurns) {
3330
+ this.logger.info(
3331
+ `sulcus-ce: PREEMPTIVE COMPACTION at ${usagePct}% (${currentTokens}/${budget}). Growth: ${newGrowth} tok/turn, interval: ${minTurns}, turns since last: ${turnsSinceCompaction}. [session=${sessionId}]`
3332
+ );
3333
+ try {
3334
+ const result = await this.delegateCompaction({
3335
+ sessionId,
3336
+ sessionFile,
3337
+ tokenBudget: budget,
3338
+ currentTokenCount: currentTokens,
3339
+ force: false,
3340
+ runtimeContext
3341
+ });
3342
+ this.lastCompactionTurn.set(sessionId, turn);
3343
+ if (result.compacted) {
3344
+ const saved = (result.result?.tokensBefore ?? 0) - (result.result?.tokensAfter ?? 0);
3345
+ this.logger.info(`sulcus-ce: compaction succeeded \u2014 saved ~${saved} tokens`);
3346
+ } else {
3347
+ this.logger.debug(`sulcus-ce: compaction declined: ${result.reason ?? "unknown"}`);
3348
+ }
3349
+ } catch (e) {
3350
+ this.logger.warn(`sulcus-ce: preemptive compaction failed: ${e}`);
3351
+ }
3352
+ } else {
3353
+ this.logger.debug(
3354
+ `sulcus-ce: skipping compaction (only ${turnsSinceCompaction} turns since last, need ${minTurns})`
3355
+ );
3356
+ }
3357
+ }
3358
+ }
3359
+ // ---------------------------------------------------------------------------
3360
+ // Phase 4: Capture tool results to Sulcus before trimming
3361
+ // ---------------------------------------------------------------------------
3362
+ async captureToolResults(messages, sessionId, turn) {
3363
+ const sessionCaptured = this.getSessionCapturedIds(sessionId);
3364
+ let captureCount = 0;
3365
+ for (const msg of messages) {
3366
+ if (captureCount >= this.t.maxCapturesPerTurn) break;
3367
+ if (msg.role !== "tool" || typeof msg.content !== "string") continue;
3368
+ if (!msg.id || msg.content.length < this.t.captureMinChars) continue;
3369
+ if (sessionCaptured.has(msg.id)) continue;
3370
+ if (msg.content.includes("[\u2026 trimmed by sulcus-ce")) continue;
3371
+ if (msg.content.includes("[captured by sulcus-ce")) continue;
3372
+ try {
3373
+ const toolName = msg.name || "tool-result";
3374
+ const firstLine = msg.content.slice(0, 200).split("\n")[0];
3375
+ const captureContent = [
3376
+ `[Tool result: ${toolName}] ${firstLine}`,
3377
+ "",
3378
+ msg.content
3379
+ ].join("\n");
3380
+ await this.memoryClient.add_memory(captureContent, "episodic", {
3381
+ key_points: [`tool-result:${toolName}`, `session:${sessionId}`, `msg:${msg.id}`]
3382
+ });
3383
+ sessionCaptured.add(msg.id);
3384
+ captureCount++;
3385
+ this.logger.debug(
3386
+ `sulcus-ce: captured tool result to memory (${toolName}, ${msg.content.length} chars) [msg=${msg.id}]`
3387
+ );
3388
+ } catch (e) {
3389
+ this.logger.warn(`sulcus-ce: memory capture failed for msg ${msg.id}: ${e}`);
3390
+ }
3391
+ }
3392
+ if (captureCount > 0) {
3393
+ this.logger.info(`sulcus-ce: captured ${captureCount} tool results to Sulcus memory [session=${sessionId}, turn=${turn}]`);
3394
+ }
3395
+ }
3396
+ // ---------------------------------------------------------------------------
3397
+ // Phase 5: Continuous session knowledge capture
3398
+ // ---------------------------------------------------------------------------
3399
+ async captureSessionKnowledge(messages, sessionId, turn, usagePct) {
3400
+ this.lastKnowledgeCaptureTurn.set(sessionId, turn);
3401
+ try {
3402
+ const lastScanIdx = this.lastKnowledgeScanIndex.get(sessionId) ?? 0;
3403
+ const messagesToScan = messages.slice(lastScanIdx);
3404
+ this.lastKnowledgeScanIndex.set(sessionId, messages.length);
3405
+ const decisions = [];
3406
+ const filesModified = [];
3407
+ const commandsRun = [];
3408
+ const userIntents = [];
3409
+ for (const msg of messagesToScan) {
3410
+ const role = msg.role;
3411
+ const content = typeof msg.content === "string" ? msg.content : "";
3412
+ if (role === "user" && content.length > 10) {
3413
+ userIntents.push(content.substring(0, 150));
3414
+ }
3415
+ if (role === "assistant" && content.length > 20) {
3416
+ const lc = content.toLowerCase();
3417
+ if (DECISION_MARKERS.some((m) => lc.includes(m))) {
3418
+ const sentences = content.split(/[.!?\n]/).filter((s) => s.trim().length > 10);
3419
+ for (const s of sentences) {
3420
+ if (DECISION_MARKERS.some((m) => s.toLowerCase().includes(m)) && !decisions.includes(s.trim())) {
3421
+ decisions.push(s.trim().substring(0, 200));
3422
+ if (decisions.length >= 5) break;
3423
+ }
3424
+ }
3425
+ }
3426
+ }
3427
+ const toolCalls = Array.isArray(msg.tool_calls) ? msg.tool_calls : [];
3428
+ for (const tc of toolCalls) {
3429
+ const name = tc.name ?? tc.function;
3430
+ if (name === "Write" || name === "Edit" || name === "write" || name === "edit") {
3431
+ const input = tc.input ?? tc.arguments ?? {};
3432
+ const fp = input?.file_path ?? input?.path;
3433
+ if (fp && typeof fp === "string" && !filesModified.includes(fp)) filesModified.push(fp);
3434
+ }
3435
+ if (name === "Bash" || name === "bash" || name === "exec" || name === "shell") {
3436
+ const input = tc.input ?? tc.arguments ?? {};
3437
+ const cmd = input?.command ?? input?.cmd;
3438
+ if (cmd && typeof cmd === "string" && commandsRun.length < 5) {
3439
+ commandsRun.push(cmd.substring(0, 100));
3440
+ }
3441
+ }
3442
+ }
3443
+ }
3444
+ const storePromises = [];
3445
+ if (decisions.length > 0) {
3446
+ const decisionText = `Session decisions (turn ${turn}): ${decisions.join(" | ")}`;
3447
+ storePromises.push(
3448
+ this.memoryClient.add_memory(decisionText, "semantic", {
3449
+ key_points: [`session:${sessionId}`, "decisions", `turn:${turn}`]
3450
+ }).catch((e) => this.logger.debug(`sulcus-ce: decision capture failed: ${e}`))
3451
+ );
3452
+ }
3453
+ if (this.memoryClient.store_episode) {
3454
+ const firstUser = messages.find((m) => m.role === "user" && typeof m.content === "string");
3455
+ const episode = {
3456
+ topic: typeof firstUser?.content === "string" ? firstUser.content.substring(0, 200) : "(none)",
3457
+ decisions: decisions.slice(0, 5),
3458
+ files_modified: filesModified.slice(0, 10),
3459
+ commands_run: commandsRun.slice(0, 5),
3460
+ outcome: "in-progress",
3461
+ duration_turns: messages.length,
3462
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
3463
+ };
3464
+ storePromises.push(
3465
+ this.memoryClient.store_episode(episode).catch((e) => this.logger.debug(`sulcus-ce: episode capture failed: ${e}`))
3466
+ );
3467
+ }
3468
+ await Promise.allSettled(storePromises);
3469
+ if (storePromises.length > 0) {
3470
+ this.logger.info(`sulcus-ce: session knowledge capture \u2014 stored ${storePromises.length} memories (turn ${turn}, ${usagePct}% budget)`);
3471
+ }
3472
+ } catch (e) {
3473
+ this.logger.warn(`sulcus-ce: session knowledge capture failed: ${e}`);
3474
+ }
3475
+ }
3476
+ // ---------------------------------------------------------------------------
3477
+ // Trim large tool results (capture-aware)
3478
+ // ---------------------------------------------------------------------------
3479
+ async trimLargeToolResults(messages, rewriteTranscript, sessionId) {
3480
+ const sessionCaptured = this.getSessionCapturedIds(sessionId);
3481
+ const replacements = [];
3482
+ for (const msg of messages) {
3483
+ if (msg.role !== "tool" || typeof msg.content !== "string") continue;
3484
+ if (msg.content.length <= this.t.largeResultChars) continue;
3485
+ if (!msg.id) continue;
3486
+ if (msg.content.includes("[\u2026 trimmed by sulcus-ce")) continue;
3487
+ if (msg.content.includes("[captured by sulcus-ce")) continue;
3488
+ const wasCaptured = sessionCaptured.has(msg.id);
3489
+ const toolName = msg.name || "tool-result";
3490
+ const head = msg.content.slice(0, this.t.trimHeadChars);
3491
+ const tail = msg.content.slice(-this.t.trimTailChars);
3492
+ const trimmed = msg.content.length - this.t.trimHeadChars - this.t.trimTailChars;
3493
+ const marker = wasCaptured ? `[captured by sulcus-ce \u2014 full content stored in memory, use memory_recall for "${toolName}" to retrieve]` : `[\u2026 trimmed by sulcus-ce: ${trimmed} chars removed \u2026]`;
3494
+ replacements.push({
3495
+ entryId: msg.id,
3496
+ message: { ...msg, content: `${head}
3497
+
3498
+ ${marker}
3499
+
3500
+ ${tail}` }
3501
+ });
3502
+ }
3503
+ if (replacements.length > 0) {
3504
+ try {
3505
+ const result = await rewriteTranscript({ replacements });
3506
+ if (result.changed) {
3507
+ this.logger.info(
3508
+ `sulcus-ce: trimmed ${result.rewrittenEntries} large tool results, freed ~${result.bytesFreed} bytes [session=${sessionId}]`
3509
+ );
3510
+ }
3511
+ } catch (e) {
3512
+ this.logger.warn(`sulcus-ce: transcript rewrite failed: ${e}`);
3513
+ }
3514
+ }
3515
+ }
3516
+ // ---------------------------------------------------------------------------
3517
+ // Emergency brake: aggressively trim ALL tool results at 90%+ budget.
3518
+ // Last-resort guard before context overflow. 500 char head + 500 char tail.
3519
+ // ---------------------------------------------------------------------------
3520
+ async emergencyTrimAllToolResults(messages, rewriteTranscript, sessionId) {
3521
+ const replacements = [];
3522
+ for (const msg of messages) {
3523
+ if (msg.role !== "tool" || typeof msg.content !== "string") continue;
3524
+ if (!msg.id) continue;
3525
+ if (msg.content.length <= this.t.emergencyHeadChars + this.t.emergencyTailChars + 200) continue;
3526
+ if (msg.content.includes("[\u26A0\uFE0F EMERGENCY trimmed by sulcus-ce")) continue;
3527
+ const head = msg.content.slice(0, this.t.emergencyHeadChars);
3528
+ const tail = msg.content.slice(-this.t.emergencyTailChars);
3529
+ const trimmed = msg.content.length - this.t.emergencyHeadChars - this.t.emergencyTailChars;
3530
+ replacements.push({
3531
+ entryId: msg.id,
3532
+ message: {
3533
+ ...msg,
3534
+ content: `${head}
3535
+
3536
+ [\u26A0\uFE0F EMERGENCY trimmed by sulcus-ce: ${trimmed} chars removed \u2014 context at 90%+ budget]
3537
+
3538
+ ${tail}`
3539
+ }
3540
+ });
3541
+ }
3542
+ if (replacements.length > 0) {
3543
+ try {
3544
+ const result = await rewriteTranscript({ replacements });
3545
+ if (result.changed) {
3546
+ this.logger.warn(
3547
+ `sulcus-ce: \u26A0\uFE0F EMERGENCY trimmed ${result.rewrittenEntries} tool results, freed ~${result.bytesFreed} bytes [session=${sessionId}]`
3548
+ );
3549
+ }
3550
+ } catch (e) {
3551
+ this.logger.warn(`sulcus-ce: emergency transcript rewrite failed: ${e}`);
3552
+ }
3553
+ }
3554
+ }
3555
+ // ---------------------------------------------------------------------------
3556
+ // Cumulative pressure trimming: when total tool output exceeds 50k chars AND
3557
+ // budget usage is >50%, trim the oldest/largest tool results even if they’re
3558
+ // individually under LARGE_RESULT_CHARS. Targets the biggest offenders first.
3559
+ // ---------------------------------------------------------------------------
3560
+ async trimCumulativePressure(messages, rewriteTranscript, sessionId) {
3561
+ const toolMsgs = [];
3562
+ for (let i = 0; i < messages.length; i++) {
3563
+ const msg = messages[i];
3564
+ if (msg.role !== "tool" || typeof msg.content !== "string" || !msg.id) continue;
3565
+ if (msg.content.includes("[\u2026 trimmed by sulcus-ce")) continue;
3566
+ if (msg.content.includes("[captured by sulcus-ce")) continue;
3567
+ if (msg.content.includes("[\u26A0\uFE0F EMERGENCY trimmed")) continue;
3568
+ if (msg.content.length < 1e3) continue;
3569
+ toolMsgs.push({ idx: i, msg, size: msg.content.length });
3570
+ }
3571
+ if (toolMsgs.length === 0) return;
3572
+ toolMsgs.sort((a, b) => b.size - a.size);
3573
+ const trimCount = Math.max(1, Math.ceil(toolMsgs.length / 2));
3574
+ const sessionCaptured = this.getSessionCapturedIds(sessionId);
3575
+ const replacements = [];
3576
+ for (let i = 0; i < trimCount; i++) {
3577
+ const { msg } = toolMsgs[i];
3578
+ const wasCaptured = sessionCaptured.has(msg.id);
3579
+ const toolName = msg.name || "tool-result";
3580
+ const head = msg.content.slice(0, this.t.trimHeadChars);
3581
+ const tail = msg.content.slice(-this.t.trimTailChars);
3582
+ const trimmed = msg.content.length - this.t.trimHeadChars - this.t.trimTailChars;
3583
+ if (trimmed <= 0) continue;
3584
+ const marker = wasCaptured ? `[captured by sulcus-ce \u2014 full content stored in memory, use memory_recall for "${toolName}" to retrieve]` : `[\u2026 trimmed by sulcus-ce (cumulative pressure): ${trimmed} chars removed \u2026]`;
3585
+ replacements.push({
3586
+ entryId: msg.id,
3587
+ message: { ...msg, content: `${head}
3588
+
3589
+ ${marker}
3590
+
3591
+ ${tail}` }
3592
+ });
3593
+ }
3594
+ if (replacements.length > 0) {
3595
+ try {
3596
+ const result = await rewriteTranscript({ replacements });
3597
+ if (result.changed) {
3598
+ this.logger.info(
3599
+ `sulcus-ce: cumulative pressure trimmed ${result.rewrittenEntries} tool results, freed ~${result.bytesFreed} bytes [session=${sessionId}]`
3600
+ );
3601
+ }
3602
+ } catch (e) {
3603
+ this.logger.warn(`sulcus-ce: cumulative pressure rewrite failed: ${e}`);
3604
+ }
3605
+ }
3606
+ }
3607
+ // ---------------------------------------------------------------------------
3608
+ // Assemble — Phase 5: Memory-Aware Assembly with Full Recall
3609
+ // ---------------------------------------------------------------------------
3610
+ async assemble(params) {
3611
+ if (this.assemblyMode === "constructive") {
3612
+ return this.assembleConstructive(params);
3613
+ }
3614
+ if (this.assemblyMode !== "memory-aware" || !this.memoryClient) {
3615
+ return { messages: params.messages, estimatedTokens: 0 };
3616
+ }
3617
+ const { messages, tokenBudget, sessionId } = params;
3618
+ try {
3619
+ const recentUserMsgs = messages.filter((m) => m.role === "user" && typeof m.content === "string").slice(-3);
3620
+ if (recentUserMsgs.length === 0) {
3621
+ return { messages, estimatedTokens: 0 };
3622
+ }
3623
+ const topicText = recentUserMsgs.map((m) => m.content).join(" ").slice(0, 500);
3624
+ const searchRes = await this.memoryClient.search_memory(topicText, 8, this.namespace);
3625
+ const memories = searchRes?.results ?? [];
3626
+ if (memories.length === 0) {
3627
+ this.logger.debug(`sulcus-ce: assemble \u2014 no relevant memories found`);
3628
+ return { messages, estimatedTokens: 0 };
3629
+ }
3630
+ const memoryIndex = memories.map((m) => {
3631
+ const heat = typeof m.heat === "number" ? m.heat.toFixed(2) : "?";
3632
+ const type = m.memory_type || "unknown";
3633
+ const preview = typeof m.content === "string" ? m.content.slice(0, 120).replace(/\n/g, " ") : "(no preview)";
3634
+ return `- [${type}|h:${heat}] ${preview}${m.content?.length > 120 ? "\u2026" : ""}`;
3635
+ }).join("\n");
3636
+ const indexMessage = {
3637
+ role: "system",
3638
+ content: `<sulcus_memory_index count="${memories.length}" note="These memories are stored in Sulcus and recoverable via memory_recall. If context is tight, content already stored here can be safely summarized.">
3639
+ ${memoryIndex}
3640
+ </sulcus_memory_index>`
3641
+ };
3642
+ let totalChars = 0;
3643
+ for (const msg of messages) {
3644
+ if (typeof msg.content === "string") totalChars += msg.content.length;
3645
+ else if (Array.isArray(msg.content)) {
3646
+ for (const part of msg.content) {
3647
+ if (typeof part === "string") totalChars += part.length;
3648
+ else if (part?.text) totalChars += part.text.length;
3649
+ }
3650
+ }
3651
+ }
3652
+ totalChars += indexMessage.content.length;
3653
+ const estimatedTokens = Math.ceil(totalChars / this.t.charsPerToken);
3654
+ if (!tokenBudget || estimatedTokens < tokenBudget * this.t.assemblyInjectRatio) {
3655
+ const injections = [indexMessage];
3656
+ if (tokenBudget && estimatedTokens < tokenBudget * this.t.assemblyRecallRatio) {
3657
+ const topMemories = memories.slice(0, 3).filter(
3658
+ (m) => typeof m.content === "string" && m.content.length > 50 && m.content.length < 2e3
3659
+ );
3660
+ if (topMemories.length > 0) {
3661
+ const recalledContent = topMemories.map((m) => {
3662
+ const type = m.memory_type || "unknown";
3663
+ const heat = typeof m.heat === "number" ? m.heat.toFixed(2) : "?";
3664
+ return `[${type}|h:${heat}] ${m.content}`;
3665
+ }).join("\n---\n");
3666
+ const recalledChars = recalledContent.length;
3667
+ const recalledTokens = Math.ceil(recalledChars / this.t.charsPerToken);
3668
+ if (estimatedTokens + recalledTokens < tokenBudget * this.t.assemblyRecallCapRatio) {
3669
+ injections.push({
3670
+ role: "system",
3671
+ content: `<sulcus_recalled_context note="Relevant memories recalled from Sulcus for this conversation.">
3672
+ ${recalledContent}
3673
+ </sulcus_recalled_context>`
3674
+ });
3675
+ totalChars += recalledChars;
3676
+ this.logger.debug(`sulcus-ce: assemble \u2014 injected ${topMemories.length} full recalled memories (+${recalledTokens} tokens)`);
3677
+ }
3678
+ }
3679
+ }
3680
+ const firstNonSystem2 = messages.findIndex((m) => m.role !== "system");
3681
+ const insertAt2 = firstNonSystem2 === -1 ? messages.length : firstNonSystem2;
3682
+ const finalTokens = Math.ceil(totalChars / this.t.charsPerToken);
3683
+ const assembled2 = [
3684
+ ...messages.slice(0, insertAt2),
3685
+ ...injections,
3686
+ ...messages.slice(insertAt2)
3687
+ ];
3688
+ this.logger.debug(`sulcus-ce: assemble \u2014 injected ${memories.length} memory refs (${finalTokens} est tokens, under budget)`);
3689
+ return { messages: assembled2, estimatedTokens: finalTokens };
3690
+ }
3691
+ const storedFingerprints = /* @__PURE__ */ new Set();
3692
+ for (const m of memories) {
3693
+ if (typeof m.content === "string" && m.content.length > 50) {
3694
+ storedFingerprints.add(m.content.slice(0, 100).trim().toLowerCase());
3695
+ }
3696
+ }
3697
+ const compressed = messages.map((msg) => {
3698
+ if (msg.role === "system" || msg.role === "user") return msg;
3699
+ if (typeof msg.content !== "string" || msg.content.length < 500) return msg;
3700
+ const fingerprint = msg.content.slice(0, 100).trim().toLowerCase();
3701
+ if (storedFingerprints.has(fingerprint)) {
3702
+ const summary = msg.content.slice(0, 200).replace(/\n/g, " ");
3703
+ return {
3704
+ ...msg,
3705
+ content: `[stored in sulcus \u2014 use memory_recall to retrieve] ${summary}\u2026`
3706
+ };
3707
+ }
3708
+ return msg;
3709
+ });
3710
+ let compressedChars = 0;
3711
+ for (const msg of compressed) {
3712
+ if (typeof msg.content === "string") compressedChars += msg.content.length;
3713
+ }
3714
+ compressedChars += indexMessage.content.length;
3715
+ const compressedTokens = Math.ceil(compressedChars / this.t.charsPerToken);
3716
+ const firstNonSystem = compressed.findIndex((m) => m.role !== "system");
3717
+ const insertAt = firstNonSystem === -1 ? compressed.length : firstNonSystem;
3718
+ const assembled = [
3719
+ ...compressed.slice(0, insertAt),
3720
+ indexMessage,
3721
+ ...compressed.slice(insertAt)
3722
+ ];
3723
+ const savedTokens = estimatedTokens - compressedTokens;
3724
+ this.logger.info(`sulcus-ce: assemble \u2014 memory-aware compression saved ~${savedTokens} tokens (${estimatedTokens} \u2192 ${compressedTokens})`);
3725
+ return { messages: assembled, estimatedTokens: compressedTokens };
3726
+ } catch (e) {
3727
+ this.logger.warn(`sulcus-ce: assemble memory-aware failed: ${e} \u2014 falling back to passthrough`);
3728
+ return { messages: params.messages, estimatedTokens: 0 };
3729
+ }
3730
+ }
3731
+ // ---------------------------------------------------------------------------
3732
+ // Constructive Assembly — Phase 6
3733
+ // Builds context deterministically: system messages + memory injection +
3734
+ // recent turns at full fidelity + older turns with summaries. Always fits
3735
+ // within tokenBudget. No transcript patching.
3736
+ // ---------------------------------------------------------------------------
3737
+ /**
3738
+ * Build context from working memory cache + recent turns.
3739
+ *
3740
+ * - System messages: pass through unchanged
3741
+ * - Recent N turns: pass through at full fidelity (agent needs recent context verbatim)
3742
+ * - Older tool results: replaced with their cached summary
3743
+ * - Older assistant messages: keep decisions/actions, compress verbose reasoning
3744
+ * - Memory injection: relevant recalled memories woven in
3745
+ * - N is budget-driven: calculated from tokenBudget minus system + memory overhead
3746
+ */
3747
+ async assembleConstructive(params) {
3748
+ const { messages, tokenBudget, sessionId } = params;
3749
+ if (!messages || messages.length === 0) {
3750
+ return { messages: [], estimatedTokens: 0 };
3751
+ }
3752
+ if (!tokenBudget) {
3753
+ return { messages, estimatedTokens: 0 };
3754
+ }
3755
+ const sessionCache = this.getSessionCache(sessionId);
3756
+ try {
3757
+ const systemMsgs = messages.filter((m) => m.role === "system");
3758
+ const conversationMsgs = messages.filter((m) => m.role !== "system");
3759
+ let systemChars = 0;
3760
+ for (const msg of systemMsgs) {
3761
+ systemChars += this.estimateMessageChars(msg);
3762
+ }
3763
+ const systemTokens = Math.ceil(systemChars / this.t.charsPerToken);
3764
+ let memoryBlock = null;
3765
+ let memoryTokens = 0;
3766
+ if (this.memoryClient) {
3767
+ try {
3768
+ const recentUser = conversationMsgs.filter((m) => m.role === "user" && typeof m.content === "string").slice(-3);
3769
+ if (recentUser.length > 0) {
3770
+ const topicText = recentUser.map((m) => m.content).join(" ").slice(0, 500);
3771
+ const searchRes = await this.memoryClient.search_memory(topicText, 5, this.namespace);
3772
+ const memories = searchRes?.results ?? [];
3773
+ if (memories.length > 0) {
3774
+ const memoryContent = memories.map((m) => {
3775
+ const type = m.memory_type || "?";
3776
+ const heat = typeof m.heat === "number" ? m.heat.toFixed(2) : "?";
3777
+ const content = typeof m.content === "string" ? m.content.slice(0, 300) : "";
3778
+ return `[${type}|h:${heat}] ${content}`;
3779
+ }).join("\n");
3780
+ memoryBlock = {
3781
+ role: "system",
3782
+ content: `<sulcus_context note="Relevant memories recalled from Sulcus.">
3783
+ ${memoryContent}
3784
+ </sulcus_context>`
3785
+ };
3786
+ memoryTokens = Math.ceil(memoryBlock.content.length / this.t.charsPerToken);
3787
+ }
3788
+ }
3789
+ } catch {
3790
+ }
3791
+ }
3792
+ const conversationBudget = tokenBudget - systemTokens - memoryTokens;
3793
+ if (conversationBudget <= 0) {
3794
+ this.logger.warn(`sulcus-ce: constructive \u2014 budget exhausted by system messages (${systemTokens} tokens)`);
3795
+ const assembled2 = [...systemMsgs, ...memoryBlock ? [memoryBlock] : [], ...conversationMsgs.slice(-2)];
3796
+ return { messages: assembled2, estimatedTokens: systemTokens + memoryTokens };
3797
+ }
3798
+ const turns = [];
3799
+ let currentTurnStart = 0;
3800
+ for (let i = 0; i < conversationMsgs.length; i++) {
3801
+ if (i > 0 && conversationMsgs[i].role === "user") {
3802
+ turns.push({
3803
+ startIdx: currentTurnStart,
3804
+ endIdx: i - 1,
3805
+ messages: conversationMsgs.slice(currentTurnStart, i)
3806
+ });
3807
+ currentTurnStart = i;
3808
+ }
3809
+ }
3810
+ if (currentTurnStart < conversationMsgs.length) {
3811
+ turns.push({
3812
+ startIdx: currentTurnStart,
3813
+ endIdx: conversationMsgs.length - 1,
3814
+ messages: conversationMsgs.slice(currentTurnStart)
3815
+ });
3816
+ }
3817
+ if (turns.length === 0) {
3818
+ const assembled2 = [...systemMsgs, ...memoryBlock ? [memoryBlock] : []];
3819
+ return { messages: assembled2, estimatedTokens: systemTokens + memoryTokens };
3820
+ }
3821
+ const recentBudgetRatio = this.t.constructiveRecentBudgetRatio;
3822
+ const recentBudgetChars = conversationBudget * this.t.charsPerToken * recentBudgetRatio;
3823
+ let recentChars = 0;
3824
+ let recentTurnCount = 0;
3825
+ for (let i = turns.length - 1; i >= 0; i--) {
3826
+ let turnChars = 0;
3827
+ for (const msg of turns[i].messages) {
3828
+ turnChars += this.estimateMessageChars(msg);
3829
+ }
3830
+ if (recentChars + turnChars > recentBudgetChars && recentTurnCount >= this.t.constructiveMinRecentTurns) {
3831
+ break;
3832
+ }
3833
+ recentChars += turnChars;
3834
+ recentTurnCount++;
3835
+ }
3836
+ recentTurnCount = Math.max(recentTurnCount, Math.min(this.t.constructiveMinRecentTurns, turns.length));
3837
+ const olderTurns = turns.slice(0, turns.length - recentTurnCount);
3838
+ const recentTurns = turns.slice(turns.length - recentTurnCount);
3839
+ const pointerSummaries = /* @__PURE__ */ new Map();
3840
+ if (this.memoryClient && olderTurns.length > 0) {
3841
+ try {
3842
+ const searchRes = await this.memoryClient.search_memory(
3843
+ `tool-result session:${sessionId}`,
3844
+ 20,
3845
+ this.namespace
3846
+ );
3847
+ for (const r of searchRes?.results ?? []) {
3848
+ const summary = r.pointer_summary || r.label;
3849
+ if (!summary || typeof summary !== "string") continue;
3850
+ const keyPoints = r.key_points ?? [];
3851
+ for (const kp of keyPoints) {
3852
+ if (typeof kp === "string" && kp.startsWith("msg:")) {
3853
+ pointerSummaries.set(kp.slice(4), summary);
3854
+ }
3855
+ }
3856
+ }
3857
+ if (pointerSummaries.size > 0) {
3858
+ this.logger.debug(`sulcus-ce: constructive \u2014 fetched ${pointerSummaries.size} pointer summaries from Sulcus`);
3859
+ }
3860
+ } catch {
3861
+ }
3862
+ }
3863
+ const remainingBudgetChars = conversationBudget * this.t.charsPerToken - recentChars;
3864
+ const summarizedOlder = [];
3865
+ let olderChars = 0;
3866
+ for (const turn of olderTurns) {
3867
+ for (const msg of turn.messages) {
3868
+ let processed = msg;
3869
+ if (msg.role === "tool" && typeof msg.content === "string" && msg.id) {
3870
+ const cached = sessionCache.get(msg.id);
3871
+ if (cached?.sulcusNodeId && pointerSummaries.has(msg.id)) {
3872
+ processed = {
3873
+ ...msg,
3874
+ content: `[${cached.toolName} summary] ${pointerSummaries.get(msg.id)}`
3875
+ };
3876
+ } else if (cached) {
3877
+ if (cached.sulcusNodeId) {
3878
+ this.logger.warn(
3879
+ `sulcus-ce: constructive \u2014 pointer_summary not found for cached tool result (${cached.toolName}, node=${cached.sulcusNodeId}, msg=${msg.id}). Falling back to truncation.`
3880
+ );
3881
+ }
3882
+ const preview = msg.content.length > 500 ? msg.content.slice(0, 250) + "\n\u2026\n" + msg.content.slice(-250) : msg.content;
3883
+ processed = {
3884
+ ...msg,
3885
+ content: `[${cached.toolName}] ${preview}`
3886
+ };
3887
+ } else if (msg.content.length > 500) {
3888
+ const toolName = msg.name || "tool";
3889
+ const preview = msg.content.slice(0, 250) + "\n\u2026\n" + msg.content.slice(-250);
3890
+ processed = {
3891
+ ...msg,
3892
+ content: `[${toolName}] ${preview}`
3893
+ };
3894
+ }
3895
+ } else if (msg.role === "assistant" && typeof msg.content === "string" && msg.content.length > 1e3) {
3896
+ processed = {
3897
+ ...msg,
3898
+ content: this.compressAssistantMessage(msg.content)
3899
+ };
3900
+ }
3901
+ const processedChars = this.estimateMessageChars(processed);
3902
+ if (olderChars + processedChars > remainingBudgetChars) {
3903
+ break;
3904
+ }
3905
+ summarizedOlder.push(processed);
3906
+ olderChars += processedChars;
3907
+ }
3908
+ if (olderChars >= remainingBudgetChars) break;
3909
+ }
3910
+ const recentMessages = recentTurns.flatMap((t) => t.messages);
3911
+ const assembled = [
3912
+ ...systemMsgs,
3913
+ ...memoryBlock ? [memoryBlock] : [],
3914
+ ...summarizedOlder,
3915
+ ...recentMessages
3916
+ ];
3917
+ const totalChars = systemChars + (memoryBlock ? memoryBlock.content.length : 0) + olderChars + recentChars;
3918
+ const estimatedTokens = Math.ceil(totalChars / this.t.charsPerToken);
3919
+ this.logger.info(
3920
+ `sulcus-ce: constructive assembly \u2014 ${assembled.length} messages, ${estimatedTokens}/${tokenBudget} tokens, ${recentTurnCount} recent turns (full), ${olderTurns.length} older (summarized), ${sessionCache.size} cached summaries`
3921
+ );
3922
+ return { messages: assembled, estimatedTokens };
3923
+ } catch (e) {
3924
+ this.logger.warn(`sulcus-ce: constructive assembly failed: ${e} \u2014 falling back to passthrough`);
3925
+ return { messages, estimatedTokens: 0 };
3926
+ }
3927
+ }
3928
+ /** Estimate character count of a message (handles string + multipart content). */
3929
+ estimateMessageChars(msg) {
3930
+ if (typeof msg.content === "string") return msg.content.length;
3931
+ if (Array.isArray(msg.content)) {
3932
+ let total = 0;
3933
+ for (const part of msg.content) {
3934
+ if (typeof part === "string") total += part.length;
3935
+ else if (part?.text) total += part.text.length;
3936
+ }
3937
+ return total;
3938
+ }
3939
+ return 0;
3940
+ }
3941
+ /**
3942
+ * Compress a verbose assistant message: keep decision sentences, trim reasoning.
3943
+ * Returns the compressed content (200–600 chars).
3944
+ */
3945
+ compressAssistantMessage(content) {
3946
+ const sentences = content.split(/(?<=[.!?\n])\s+/).filter((s) => s.trim().length > 10);
3947
+ const decisionSentences = sentences.filter((s) => {
3948
+ const lc = s.toLowerCase();
3949
+ return DECISION_MARKERS.some((m) => lc.includes(m));
3950
+ });
3951
+ const maxSentence = this.t.compressSentenceMaxChars;
3952
+ const maxOutput = this.t.compressMaxChars;
3953
+ const kept = [];
3954
+ if (sentences.length > 0) kept.push(sentences[0].slice(0, maxSentence));
3955
+ for (const ds of decisionSentences.slice(0, 3)) {
3956
+ if (!kept.includes(ds.slice(0, maxSentence))) kept.push(ds.slice(0, maxSentence));
3957
+ }
3958
+ if (sentences.length > 1) {
3959
+ const last = sentences[sentences.length - 1].slice(0, maxSentence);
3960
+ if (!kept.includes(last)) kept.push(last);
3961
+ }
3962
+ if (kept.length === 0) {
3963
+ return content.slice(0, maxSentence * 2) + "\u2026";
3964
+ }
3965
+ const compressed = kept.join(" ");
3966
+ if (compressed.length > maxOutput) return compressed.slice(0, maxOutput - 3) + "\u2026";
3967
+ return compressed;
3968
+ }
3969
+ // ---------------------------------------------------------------------------
3970
+ // Compact — Phase 5: Unified Compaction (Capture + Enrich + Delegate)
3971
+ // ---------------------------------------------------------------------------
3972
+ async compact(params) {
3973
+ if (this.compactMode !== "smart" || !this.memoryClient) {
3974
+ this.logger.debug("sulcus-ce: compact() \u2014 no memory client or passthrough mode, delegating plain");
3975
+ try {
3976
+ return await this.delegateCompaction(params);
3977
+ } catch (e) {
3978
+ return { ok: false, compacted: false, reason: `delegation-error: ${e}` };
3979
+ }
3980
+ }
3981
+ try {
3982
+ try {
3983
+ const runtimeMessages = params.runtimeContext?.messages;
3984
+ if (Array.isArray(runtimeMessages) && runtimeMessages.length > 0) {
3985
+ const firstUser = runtimeMessages.find((m) => m.role === "user" && typeof m.content === "string");
3986
+ const lastAssistant = [...runtimeMessages].reverse().find((m) => m.role === "assistant" && typeof m.content === "string");
3987
+ const summaryParts = [
3988
+ `Pre-compaction capture (${runtimeMessages.length} messages)`,
3989
+ `Topic: ${typeof firstUser?.content === "string" ? firstUser.content.substring(0, 200) : "(none)"}`,
3990
+ `Last output: ${typeof lastAssistant?.content === "string" ? lastAssistant.content.substring(0, 200) : "(none)"}`
3991
+ ];
3992
+ await this.memoryClient.add_memory(summaryParts.join("\n"), "episodic", {
3993
+ key_points: [`session:${params.sessionId}`, "compaction-capture"]
3994
+ });
3995
+ this.logger.info(`sulcus-ce: compact \u2014 pre-compaction capture stored (${runtimeMessages.length} messages)`);
3996
+ }
3997
+ } catch (captureErr) {
3998
+ this.logger.debug(`sulcus-ce: compact \u2014 pre-compaction capture failed: ${captureErr}`);
3999
+ }
4000
+ const searchRes = await this.memoryClient.search_memory(
4001
+ "recent conversation context decisions tasks",
4002
+ 12,
4003
+ this.namespace
4004
+ );
4005
+ const storedMemories = searchRes?.results ?? [];
4006
+ if (storedMemories.length === 0) {
4007
+ this.logger.debug("sulcus-ce: compact \u2014 no stored memories, delegating plain");
4008
+ return await this.delegateCompaction(params);
4009
+ }
4010
+ const storedSummary = storedMemories.map((m) => {
4011
+ const type = m.memory_type || "unknown";
4012
+ const heat = typeof m.heat === "number" ? m.heat.toFixed(2) : "?";
4013
+ const preview = typeof m.content === "string" ? m.content.slice(0, 150).replace(/\n/g, " ") : "(no content)";
4014
+ return ` - [${type}, heat=${heat}] ${preview}`;
4015
+ }).join("\n");
4016
+ const existingInstructions = params.customInstructions || "";
4017
+ const smartInstructions = [
4018
+ existingInstructions,
4019
+ "",
4020
+ "=== SULCUS MEMORY CONTEXT ===",
4021
+ "The following content is ALREADY stored in the agent's persistent memory (Sulcus)",
4022
+ "and is recoverable via memory_recall. You do NOT need to preserve this content",
4023
+ "in the summary \u2014 it will survive compaction through memory.",
4024
+ "",
4025
+ storedSummary,
4026
+ "",
4027
+ "=== COMPACTION GUIDANCE ===",
4028
+ "Focus the summary on:",
4029
+ "1. ACTIVE TASK STATE \u2014 what the agent is currently working on, next steps",
4030
+ "2. PENDING DECISIONS \u2014 anything awaiting input or approval",
4031
+ "3. UNFINISHED WORK \u2014 partial progress, blockers, what remains",
4032
+ "4. CONVERSATION DYNAMICS \u2014 who asked what, tone, important agreements",
4033
+ "",
4034
+ "DO NOT re-summarize content already listed above as stored in memory.",
4035
+ "Instead, note: '[stored in Sulcus \u2014 recallable via memory_recall]'",
4036
+ "",
4037
+ "Structure the summary with clear sections when appropriate:",
4038
+ "- Active Context (what's happening now)",
4039
+ "- Stored in Memory (brief note of what's recallable)",
4040
+ "- Key Decisions & Agreements",
4041
+ "- Next Steps"
4042
+ ].filter(Boolean).join("\n");
4043
+ this.logger.info(
4044
+ `sulcus-ce: smart compaction \u2014 ${storedMemories.length} memories in context, enriched instructions (${smartInstructions.length} chars)`
4045
+ );
4046
+ const result = await this.delegateCompaction({
4047
+ ...params,
4048
+ customInstructions: smartInstructions
4049
+ });
4050
+ if (result.compacted) {
4051
+ const saved = (result.result?.tokensBefore ?? 0) - (result.result?.tokensAfter ?? 0);
4052
+ this.logger.info(`sulcus-ce: smart compaction saved ~${saved} tokens`);
4053
+ }
4054
+ return result;
4055
+ } catch (e) {
4056
+ this.logger.warn(`sulcus-ce: smart compaction failed: ${e} \u2014 falling back to plain delegation`);
4057
+ try {
4058
+ return await this.delegateCompaction(params);
4059
+ } catch (e2) {
4060
+ return { ok: false, compacted: false, reason: `delegation-error: ${e2}` };
4061
+ }
4062
+ }
4063
+ }
4064
+ // ---------------------------------------------------------------------------
4065
+ // Subagent lifecycle — no-op
4066
+ // ---------------------------------------------------------------------------
4067
+ async prepareSubagentSpawn(_params) {
4068
+ return void 0;
4069
+ }
4070
+ async onSubagentEnded(params) {
4071
+ const sessionId = params?.sessionId;
4072
+ if (sessionId && this.turnCounter.has(sessionId)) {
4073
+ this.clearSession(sessionId);
4074
+ }
4075
+ }
4076
+ // ---------------------------------------------------------------------------
4077
+ // Cleanup
4078
+ // ---------------------------------------------------------------------------
4079
+ async dispose() {
4080
+ const allSessions = /* @__PURE__ */ new Set([
4081
+ ...this.turnCounter.keys(),
4082
+ ...this.workingMemory.keys(),
4083
+ ...this.sessionLastActivity.keys()
4084
+ ]);
4085
+ for (const sessionId of allSessions) {
4086
+ this.clearSession(sessionId);
4087
+ }
4088
+ this.logger.info("sulcus-ce: disposed");
4089
+ }
4090
+ };
4091
+
2647
4092
  // index.ts
2648
4093
  function generateSessionId() {
2649
4094
  const ts = Date.now().toString(36);
@@ -2878,6 +4323,7 @@ var recallQM = {
2878
4323
  scoreTurns: 0
2879
4324
  };
2880
4325
  var wasJustCompacted = false;
4326
+ var contextEngineActive = false;
2881
4327
  var REBUILD_TOKEN_BUDGET = 1e4;
2882
4328
  var CORE_MEMORY_MAX_CHARS = 4e3;
2883
4329
  var coreMemoryCache = void 0;
@@ -3325,6 +4771,10 @@ ${contextParts.join("\n")}
3325
4771
  const messages = Array.isArray(event?.messages) ? event.messages : [];
3326
4772
  if (messages.length === 0) return;
3327
4773
  wasJustCompacted = true;
4774
+ if (contextEngineActive) {
4775
+ logger.info("sulcus: pre_compaction_capture \u2014 skipped (context engine handles capture). Rebuild flag SET.");
4776
+ return;
4777
+ }
3328
4778
  logger.info("sulcus: pre_compaction_capture \u2014 rebuild flag SET (next turn will inject full Sulcus context)");
3329
4779
  const firstUser = messages.find((m) => m.role === "user" || m.type === "human");
3330
4780
  const lastAssistant = [...messages].reverse().find((m) => m.role === "assistant" || m.type === "ai");
@@ -3335,7 +4785,7 @@ ${contextParts.join("\n")}
3335
4785
  const decisions = [];
3336
4786
  const errors = [];
3337
4787
  const userIntents = [];
3338
- const DECISION_MARKERS = ["decided", "will use", "going to", "plan is", "the fix", "conclusion", "recommend", "approach"];
4788
+ const DECISION_MARKERS2 = ["decided", "will use", "going to", "plan is", "the fix", "conclusion", "recommend", "approach"];
3339
4789
  const ERROR_MARKERS = ["error:", "failed:", "exception", "traceback", "panicked", "stack trace"];
3340
4790
  for (const msg of messages) {
3341
4791
  const role = msg.role ?? msg.type;
@@ -3345,10 +4795,10 @@ ${contextParts.join("\n")}
3345
4795
  }
3346
4796
  if ((role === "assistant" || role === "ai") && rawContent.length > 20) {
3347
4797
  const lc = rawContent.toLowerCase();
3348
- if (DECISION_MARKERS.some((m) => lc.includes(m))) {
4798
+ if (DECISION_MARKERS2.some((m) => lc.includes(m))) {
3349
4799
  const sentences = rawContent.split(/[.!?\n]/).filter((s) => s.trim().length > 10);
3350
4800
  for (const s of sentences) {
3351
- if (DECISION_MARKERS.some((m) => s.toLowerCase().includes(m)) && !decisions.includes(s.trim())) {
4801
+ if (DECISION_MARKERS2.some((m) => s.toLowerCase().includes(m)) && !decisions.includes(s.trim())) {
3352
4802
  decisions.push(s.trim().substring(0, 200));
3353
4803
  if (decisions.length >= 5) break;
3354
4804
  }
@@ -3520,7 +4970,7 @@ var SulcusCloudClient = class _SulcusCloudClient {
3520
4970
  _rawRequest(method, path, bodyStr, parsedUrl) {
3521
4971
  return new Promise((resolveP, rejectP) => {
3522
4972
  const isHttps = parsedUrl.protocol === "https:";
3523
- const transport = isHttps ? https : http;
4973
+ const transport = isHttps ? https : http2;
3524
4974
  const headers = {
3525
4975
  "Authorization": `Bearer ${this.apiKey}`,
3526
4976
  "Accept": "application/json"
@@ -3572,7 +5022,7 @@ var SulcusCloudClient = class _SulcusCloudClient {
3572
5022
  request(method, path, body) {
3573
5023
  let parsedUrl;
3574
5024
  try {
3575
- parsedUrl = new import_node_url.URL(this.serverUrl + path);
5025
+ parsedUrl = new import_node_url2.URL(this.serverUrl + path);
3576
5026
  } catch (e) {
3577
5027
  const msg = e instanceof Error ? e.message : String(e);
3578
5028
  return Promise.reject(new Error(`SulcusCloudClient: invalid URL ${this.serverUrl}${path}: ${msg}`));
@@ -4163,7 +5613,7 @@ var ASSISTANT_CAPTURE_MAX_DIRECT = 1500;
4163
5613
  function summarizeForCapture(text, namespace) {
4164
5614
  const paragraphs = text.split(/\n{2,}/).map((p) => p.trim()).filter((p) => p.length > 20);
4165
5615
  if (paragraphs.length === 0) return text.substring(0, ASSISTANT_CAPTURE_MAX_DIRECT);
4166
- const DECISION_MARKERS = [
5616
+ const DECISION_MARKERS2 = [
4167
5617
  "decided",
4168
5618
  "recommend",
4169
5619
  "conclusion",
@@ -4185,7 +5635,7 @@ function summarizeForCapture(text, namespace) {
4185
5635
  if (paragraphs[0]) keyParagraphs.push(paragraphs[0]);
4186
5636
  for (let i = 1; i < paragraphs.length - 1; i++) {
4187
5637
  const pLower = paragraphs[i].toLowerCase();
4188
- if (DECISION_MARKERS.some((m) => pLower.includes(m))) {
5638
+ if (DECISION_MARKERS2.some((m) => pLower.includes(m))) {
4189
5639
  keyParagraphs.push(paragraphs[i]);
4190
5640
  if (keyParagraphs.length >= 3) break;
4191
5641
  }
@@ -4627,7 +6077,40 @@ async function expandQueryWithEntities(client, originalQuery, namespace, logger)
4627
6077
  return { extraMemories, expandedQuery };
4628
6078
  }
4629
6079
  var THIN_RECALL_THRESHOLD = 3;
4630
- function buildSdkRecallHandler(sulcusMem, namespace, maxResults, profileFrequency, logger, boostOnRecall = true, tokenBudget = 1e4, contextRebuild = true, contextWindowSize = 2e5) {
6080
+ function buildSdkRecallHandler(sulcusMem, namespace, maxResults, profileFrequency, logger, boostOnRecall = true, tokenBudget = 1e4, contextRebuild = true, contextWindowSize = 2e5, localClient = null) {
6081
+ async function localFirstSearch(query, limit, ns) {
6082
+ if (!localClient || !localClient.isAvailable()) {
6083
+ const res2 = await sulcusMem.search_memory(query, limit, ns);
6084
+ return { results: res2?.results ?? [], source: "cloud" };
6085
+ }
6086
+ try {
6087
+ const localRes = await localClient.search_memory(query, limit, ns);
6088
+ const localResults = localRes?.results ?? [];
6089
+ if (localResults.length >= Math.ceil(limit * 0.7)) {
6090
+ sulcusMem.search_memory(query, limit, ns).catch(() => {
6091
+ });
6092
+ return { results: localResults, source: "local" };
6093
+ }
6094
+ if (localResults.length > 0) {
6095
+ try {
6096
+ const remoteRes = await sulcusMem.search_memory(query, limit, ns);
6097
+ const remoteResults = remoteRes?.results ?? [];
6098
+ const remoteById = new Map(remoteResults.map((r) => [r.id, r]));
6099
+ const mergedMap = new Map(remoteById);
6100
+ for (const [, node] of new Map(localResults.map((r) => [r.id, r]))) {
6101
+ if (!mergedMap.has(node.id)) mergedMap.set(node.id, node);
6102
+ }
6103
+ return { results: [...mergedMap.values()].slice(0, limit), source: "local+cloud" };
6104
+ } catch {
6105
+ return { results: localResults, source: "local" };
6106
+ }
6107
+ }
6108
+ } catch {
6109
+ logger.debug?.("sulcus: autoRecall local search failed, falling back to cloud");
6110
+ }
6111
+ const res = await sulcusMem.search_memory(query, limit, ns);
6112
+ return { results: res?.results ?? [], source: "cloud" };
6113
+ }
4631
6114
  let turnCount = 0;
4632
6115
  let profileCache = null;
4633
6116
  let recallCache = null;
@@ -4767,7 +6250,7 @@ ${mutedComment}` : mutedComment };
4767
6250
  logger.info(`sulcus: TOPIC SHIFT detected (overlap=${overlap.toFixed(2)}) \u2014 fresh recall (turn ${turnCount})`);
4768
6251
  }
4769
6252
  try {
4770
- const searchRes = await sulcusMem.search_memory(recallQuery, effectiveMax, effectiveNamespace);
6253
+ const searchRes = await localFirstSearch(recallQuery, effectiveMax, effectiveNamespace);
4771
6254
  const vectorResults = searchRes?.results ?? [];
4772
6255
  let sdkExpanded = vectorResults;
4773
6256
  if (vectorResults.length < THIN_RECALL_THRESHOLD) {
@@ -5144,6 +6627,7 @@ function buildPromptSection(params) {
5144
6627
  lines.push("Memory types: episodic (events, fast decay), semantic (knowledge, slow), preference (opinions, slower), procedural (how-tos, slowest), fact (data, slow)");
5145
6628
  return lines;
5146
6629
  }
6630
+ var toolRecallCache = { results: [], cachedAt: 0 };
5147
6631
  var toolDefinitions = {
5148
6632
  memory_recall: {
5149
6633
  schema: {
@@ -5157,14 +6641,70 @@ var toolDefinitions = {
5157
6641
  })
5158
6642
  },
5159
6643
  options: { name: "memory_recall" },
5160
- makeExecute: ({ sulcusMem, backendMode, namespace, nativeLoader, isAvailable }) => async (_id, params) => {
6644
+ makeExecute: ({ sulcusMem, localClient, backendMode, namespace, nativeLoader, isAvailable, logger }) => async (_id, params) => {
5161
6645
  if (!isAvailable || !sulcusMem) throw new Error(`Sulcus unavailable: ${nativeLoader.error || "not loaded"}`);
5162
6646
  const searchNamespace = params.namespace ?? namespace;
5163
- const res = await sulcusMem.search_memory(params.query, params.limit ?? 5, searchNamespace);
5164
- const results = res?.results ?? [];
6647
+ const query = params.query;
6648
+ const limit = params.limit ?? 5;
6649
+ let results = [];
6650
+ let source = "cloud";
6651
+ if (localClient && localClient.isAvailable()) {
6652
+ try {
6653
+ const localRes = await localClient.search_memory(query, limit, searchNamespace);
6654
+ const localResults = localRes?.results ?? [];
6655
+ if (localResults.length >= Math.ceil(limit * 0.7)) {
6656
+ results = localResults;
6657
+ source = "local";
6658
+ logger.debug?.(`sulcus: recall served from local (${localResults.length} results)`);
6659
+ sulcusMem.search_memory(query, limit, searchNamespace).catch(() => {
6660
+ });
6661
+ } else if (localResults.length > 0) {
6662
+ source = "local+cloud";
6663
+ try {
6664
+ const remoteRes = await sulcusMem.search_memory(query, limit, searchNamespace);
6665
+ const remoteResults = remoteRes?.results ?? [];
6666
+ const remoteById = new Map(remoteResults.map((r) => [r.id, r]));
6667
+ const localById = new Map(localResults.map((r) => [r.id, r]));
6668
+ const mergedMap = new Map(remoteById);
6669
+ for (const [id, node] of localById) {
6670
+ if (!mergedMap.has(id)) mergedMap.set(id, node);
6671
+ }
6672
+ const merged = [...mergedMap.values()];
6673
+ results = merged.slice(0, limit);
6674
+ logger.debug?.(`sulcus: recall merged local(${localResults.length}) + cloud(${remoteResults.length}) \u2192 ${results.length}`);
6675
+ } catch {
6676
+ results = localResults;
6677
+ source = "local";
6678
+ logger.debug?.(`sulcus: remote recall failed, using ${localResults.length} local results`);
6679
+ }
6680
+ } else {
6681
+ logger.debug?.("sulcus: local recall empty, falling back to cloud");
6682
+ }
6683
+ } catch (localErr) {
6684
+ logger.debug?.(`sulcus: local recall failed (${localErr}), falling back to cloud`);
6685
+ }
6686
+ }
6687
+ if (results.length === 0) {
6688
+ try {
6689
+ const res = await sulcusMem.search_memory(query, limit, searchNamespace);
6690
+ results = res?.results ?? [];
6691
+ source = "cloud";
6692
+ } catch (cloudErr) {
6693
+ if (toolRecallCache.results.length > 0) {
6694
+ results = toolRecallCache.results;
6695
+ source = "stale-cache";
6696
+ logger.warn?.(`sulcus: both local and cloud recall failed, serving stale cache (${results.length} items)`);
6697
+ } else {
6698
+ throw cloudErr;
6699
+ }
6700
+ }
6701
+ }
6702
+ if (results.length > 0 && source !== "stale-cache") {
6703
+ toolRecallCache = { results, cachedAt: Date.now() };
6704
+ }
5165
6705
  return {
5166
6706
  content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
5167
- details: { results, backend: backendMode, namespace: searchNamespace }
6707
+ details: { results, backend: backendMode, namespace: searchNamespace, source }
5168
6708
  };
5169
6709
  }
5170
6710
  },
@@ -5186,7 +6726,7 @@ var toolDefinitions = {
5186
6726
  })
5187
6727
  },
5188
6728
  options: { name: "memory_store" },
5189
- makeExecute: ({ sulcusMem, backendMode, namespace, nativeLoader, isAvailable, logger }) => async (_id, params) => {
6729
+ makeExecute: ({ sulcusMem, localClient, retryQueue, backendMode, namespace, nativeLoader, isAvailable, logger }) => async (_id, params) => {
5190
6730
  const content = params.content;
5191
6731
  if (isJunkMemory(content)) {
5192
6732
  logger.debug?.(`sulcus: filtered junk memory: "${content.substring(0, 50)}..."`);
@@ -5195,8 +6735,33 @@ var toolDefinitions = {
5195
6735
  if (!isAvailable || !sulcusMem) throw new Error(`Sulcus unavailable: ${nativeLoader.error || "not loaded"}`);
5196
6736
  const mtype = params.memory_type || "episodic";
5197
6737
  const storeHints = buildExtractionHints(mtype, namespace, "user_capture", content.substring(0, 200));
5198
- const res = await sulcusMem.add_memory(content, mtype, storeHints);
5199
- const nodeId = res?.id ?? "unknown";
6738
+ let nodeId = "unknown";
6739
+ let res = {};
6740
+ let storeSource = "cloud";
6741
+ if (localClient && localClient.isAvailable()) {
6742
+ try {
6743
+ const localRes = await localClient.add_memory(content, mtype, storeHints);
6744
+ nodeId = localRes?.id ?? "unknown";
6745
+ res = localRes;
6746
+ storeSource = "local";
6747
+ logger.debug?.(`sulcus: stored to local sidecar (id: ${nodeId})`);
6748
+ sulcusMem.add_memory(content, mtype, storeHints).then((remoteRes) => {
6749
+ logger.debug?.(`sulcus: remote store synced (local: ${nodeId}, remote: ${remoteRes?.id ?? "?"})`);
6750
+ }).catch((err) => {
6751
+ const errMsg = err instanceof Error ? err.message : String(err);
6752
+ logger.warn(`sulcus: remote store failed for ${nodeId} (will retry): ${errMsg}`);
6753
+ retryQueue.enqueue(nodeId, "store", { content, memory_type: mtype, extraction_hints: storeHints });
6754
+ });
6755
+ } catch (localErr) {
6756
+ const errMsg = localErr instanceof Error ? localErr.message : String(localErr);
6757
+ logger.warn(`sulcus: local store failed (${errMsg}), falling back to cloud`);
6758
+ }
6759
+ }
6760
+ if (storeSource === "cloud") {
6761
+ const cloudRes = await sulcusMem.add_memory(content, mtype, storeHints);
6762
+ nodeId = cloudRes?.id ?? "unknown";
6763
+ res = cloudRes;
6764
+ }
5200
6765
  let trainResult = null;
5201
6766
  if (params.train === true) {
5202
6767
  try {
@@ -5216,8 +6781,8 @@ var toolDefinitions = {
5216
6781
  }
5217
6782
  }
5218
6783
  return {
5219
- content: [{ type: "text", text: `Stored [${mtype}] memory (id: ${nodeId}) \u2192 backend: ${backendMode}, namespace: ${namespace}${trainResult ? ` | SIU: ${trainResult}` : ""}` }],
5220
- details: { ...res, id: nodeId, memory_type: mtype, backend: backendMode, namespace, train: trainResult }
6784
+ content: [{ type: "text", text: `Stored [${mtype}] memory (id: ${nodeId}) \u2192 backend: ${backendMode}, namespace: ${namespace}, source: ${storeSource}${trainResult ? ` | SIU: ${trainResult}` : ""}` }],
6785
+ details: { ...res, id: nodeId, memory_type: mtype, backend: backendMode, namespace, source: storeSource, train: trainResult }
5221
6786
  };
5222
6787
  }
5223
6788
  },
@@ -5229,9 +6794,14 @@ var toolDefinitions = {
5229
6794
  parameters: Type.Object({})
5230
6795
  },
5231
6796
  options: { name: "memory_status" },
5232
- makeExecute: ({ sulcusMem, backendMode, namespace, nativeLoader, storeLibPath, vectorsLibPath, wasmDir, isAvailable }) => async (_id, _params) => {
6797
+ makeExecute: ({ sulcusMem, localClient, retryQueue, backendMode, namespace, nativeLoader, storeLibPath, vectorsLibPath, wasmDir, isAvailable }) => async (_id, _params) => {
6798
+ const localStatus = localClient ? {
6799
+ endpoint: localClient.endpoint,
6800
+ ...localClient.healthSummary(),
6801
+ retry_queue_size: retryQueue.size
6802
+ } : null;
5233
6803
  if (!isAvailable || !sulcusMem) {
5234
- return { content: [{ type: "text", text: JSON.stringify({ status: "unavailable", backend: backendMode, namespace, error: nativeLoader.error || "not loaded", storeLib: storeLibPath, vectorsLib: vectorsLibPath, wasmDir }, null, 2) }] };
6804
+ return { content: [{ type: "text", text: JSON.stringify({ status: "unavailable", backend: backendMode, namespace, error: nativeLoader.error || "not loaded", storeLib: storeLibPath, vectorsLib: vectorsLibPath, wasmDir, local: localStatus }, null, 2) }] };
5235
6805
  }
5236
6806
  try {
5237
6807
  const [statusInfo, hotNodes] = await Promise.all([
@@ -5287,7 +6857,7 @@ var toolDefinitions = {
5287
6857
  };
5288
6858
  }
5289
6859
  return {
5290
- content: [{ type: "text", text: JSON.stringify({ status: "ok", backend: backendMode, namespace, ...si?.capabilities ? { capabilities: si.capabilities } : {}, ...si?.stats ? { stats: si.stats } : {}, hot_node_count: nodeList.length, hot_nodes: nodeList, recall_quality: recallQuality, last_injection: lastInjection }, null, 2) }],
6860
+ content: [{ type: "text", text: JSON.stringify({ status: "ok", backend: backendMode, namespace, ...si?.capabilities ? { capabilities: si.capabilities } : {}, ...si?.stats ? { stats: si.stats } : {}, hot_node_count: nodeList.length, hot_nodes: nodeList, recall_quality: recallQuality, last_injection: lastInjection, ...localStatus ? { local: localStatus } : {} }, null, 2) }],
5291
6861
  details: { status: "ok", backend: backendMode, namespace, count: nodeList.length }
5292
6862
  };
5293
6863
  } catch (e) {
@@ -5365,13 +6935,32 @@ var toolDefinitions = {
5365
6935
  })
5366
6936
  },
5367
6937
  options: { name: "memory_delete" },
5368
- makeExecute: ({ sulcusMem, backendMode, namespace, nativeLoader, isAvailable }) => async (_id, params) => {
6938
+ makeExecute: ({ sulcusMem, localClient, retryQueue, backendMode, namespace, nativeLoader, isAvailable, logger }) => async (_id, params) => {
5369
6939
  if (!isAvailable || !sulcusMem) throw new Error(`Sulcus unavailable: ${nativeLoader.error || "not loaded"}`);
6940
+ const memId = params.id;
5370
6941
  const train = params.train !== false;
5371
- const res = await sulcusMem.delete_memory(params.id, train);
6942
+ if (localClient && localClient.isAvailable()) {
6943
+ try {
6944
+ await localClient.delete_memory(memId);
6945
+ logger.debug?.(`sulcus: deleted from local sidecar (${memId})`);
6946
+ } catch (localErr) {
6947
+ logger.debug?.(`sulcus: local delete failed (${localErr})`);
6948
+ }
6949
+ }
6950
+ let res;
6951
+ try {
6952
+ res = await sulcusMem.delete_memory(memId, train);
6953
+ } catch (remoteErr) {
6954
+ retryQueue.enqueue(memId, "delete", { id: memId });
6955
+ logger.debug?.(`sulcus: remote delete failed, enqueued for retry (${memId})`);
6956
+ return {
6957
+ content: [{ type: "text", text: `Deleted memory ${memId} locally. Remote sync pending.` }],
6958
+ details: { id: memId, trained: train, backend: backendMode, namespace, source: "local" }
6959
+ };
6960
+ }
5372
6961
  return {
5373
- content: [{ type: "text", text: `Deleted memory ${params.id}${train ? " (trained SIVU to reject similar)" : ""}. Backend: ${backendMode}, namespace: ${namespace}` }],
5374
- details: { id: params.id, trained: train, result: res, backend: backendMode, namespace }
6962
+ content: [{ type: "text", text: `Deleted memory ${memId}${train ? " (trained SIVU to reject similar)" : ""}. Backend: ${backendMode}, namespace: ${namespace}` }],
6963
+ details: { id: memId, trained: train, result: res, backend: backendMode, namespace }
5375
6964
  };
5376
6965
  }
5377
6966
  },
@@ -5385,15 +6974,31 @@ var toolDefinitions = {
5385
6974
  })
5386
6975
  },
5387
6976
  options: { name: "memory_get" },
5388
- makeExecute: ({ sulcusMem, backendMode, namespace, nativeLoader, isAvailable }) => async (_id, params) => {
6977
+ makeExecute: ({ sulcusMem, localClient, backendMode, namespace, nativeLoader, isAvailable, logger }) => async (_id, params) => {
5389
6978
  if (!isAvailable || !sulcusMem) throw new Error(`Sulcus unavailable: ${nativeLoader.error || "not loaded"}`);
5390
6979
  if (!(sulcusMem instanceof SulcusCloudClient)) throw new Error("memory_get requires cloud backend");
5391
6980
  const memId = params.id;
6981
+ let source = "cloud";
6982
+ if (localClient && localClient.isAvailable()) {
6983
+ try {
6984
+ const localRes = await localClient.get_memory(memId);
6985
+ if (localRes) {
6986
+ source = "local";
6987
+ logger.debug?.(`sulcus: memory_get served from local (${memId})`);
6988
+ return {
6989
+ content: [{ type: "text", text: JSON.stringify(localRes, null, 2) }],
6990
+ details: { ...localRes, backend: backendMode, namespace, source }
6991
+ };
6992
+ }
6993
+ } catch {
6994
+ logger.debug?.(`sulcus: local get failed for ${memId}, falling back to cloud`);
6995
+ }
6996
+ }
5392
6997
  const res = await sulcusMem.get_memory(memId);
5393
6998
  if (!res) return { content: [{ type: "text", text: `Memory ${memId} not found.` }], details: { found: false, id: memId } };
5394
6999
  return {
5395
7000
  content: [{ type: "text", text: JSON.stringify(res, null, 2) }],
5396
- details: { ...res, backend: backendMode, namespace }
7001
+ details: { ...res, backend: backendMode, namespace, source }
5397
7002
  };
5398
7003
  }
5399
7004
  },
@@ -5466,7 +7071,7 @@ var toolDefinitions = {
5466
7071
  })
5467
7072
  },
5468
7073
  options: { name: "memory_update" },
5469
- makeExecute: ({ sulcusMem, backendMode, namespace, nativeLoader, isAvailable, logger }) => async (_id, params) => {
7074
+ makeExecute: ({ sulcusMem, localClient, retryQueue, backendMode, namespace, nativeLoader, isAvailable, logger }) => async (_id, params) => {
5470
7075
  if (!isAvailable || !sulcusMem) throw new Error(`Sulcus unavailable: ${nativeLoader.error || "not loaded"}`);
5471
7076
  if (!(sulcusMem instanceof SulcusCloudClient)) throw new Error("memory_update requires cloud backend");
5472
7077
  const memId = params.id;
@@ -5478,8 +7083,26 @@ var toolDefinitions = {
5478
7083
  if (Object.keys(updates).length === 0) {
5479
7084
  return { content: [{ type: "text", text: "No fields to update. Provide at least one of: content, memory_type, is_pinned, heat." }] };
5480
7085
  }
5481
- const res = await sulcusMem.update_memory(memId, updates);
7086
+ if (localClient && localClient.isAvailable()) {
7087
+ try {
7088
+ await localClient.update_memory(memId, updates);
7089
+ logger.debug?.(`sulcus: updated local sidecar (${memId})`);
7090
+ } catch (localErr) {
7091
+ logger.debug?.(`sulcus: local update failed (${localErr})`);
7092
+ }
7093
+ }
7094
+ let res;
5482
7095
  const fields = Object.keys(updates).join(", ");
7096
+ try {
7097
+ res = await sulcusMem.update_memory(memId, updates);
7098
+ } catch (remoteErr) {
7099
+ retryQueue.enqueue(memId, "update", updates);
7100
+ logger.debug?.(`sulcus: remote update failed, enqueued for retry (${memId})`);
7101
+ return {
7102
+ content: [{ type: "text", text: `Updated memory ${memId} locally (fields: ${fields}). Remote sync pending.` }],
7103
+ details: { id: memId, updated_fields: Object.keys(updates), backend: backendMode, namespace, source: "local" }
7104
+ };
7105
+ }
5483
7106
  logger.info(`sulcus: memory_update \u2014 updated ${memId} (fields: ${fields})`);
5484
7107
  return {
5485
7108
  content: [{ type: "text", text: `Updated memory ${memId} (fields: ${fields}). Backend: ${backendMode}, namespace: ${namespace}` }],
@@ -6508,6 +8131,11 @@ var sulcusPlugin = {
6508
8131
  const wasmDir = pluginConfig?.wasmDir ? (0, import_node_path.resolve)(pluginConfig.wasmDir) : (0, import_node_path.resolve)(__dirname, "wasm");
6509
8132
  const serverUrl = pluginConfig?.serverUrl;
6510
8133
  const apiKey = pluginConfig?.apiKey;
8134
+ const localConfig = pluginConfig?.local;
8135
+ const localEndpoint = localConfig?.endpoint ?? process.env.SULCUS_LOCAL_URL;
8136
+ const localApiKey = localConfig?.apiKey ?? process.env.SULCUS_LOCAL_API_KEY;
8137
+ const localTimeoutMs = localConfig?.timeoutMs ?? 2e3;
8138
+ const localEnabled = localEndpoint !== void 0 && localConfig?.enabled !== false;
6511
8139
  const agentId = pluginConfig?.agentId;
6512
8140
  const namespace = pluginConfig?.namespace === "default" && agentId ? agentId : pluginConfig?.namespace || agentId || "default";
6513
8141
  const autoRecall = pluginConfig?.autoRecall ?? false;
@@ -6558,10 +8186,33 @@ var sulcusPlugin = {
6558
8186
  }
6559
8187
  const isAvailable = sulcusMem !== null;
6560
8188
  const isCloudBackend = backendMode === "cloud" && sulcusMem instanceof SulcusCloudClient;
6561
- STATIC_AWARENESS = buildStaticAwareness(backendMode, namespace);
8189
+ let localClient = null;
8190
+ const retryQueue = new RetryQueue({ maxItems: 500, maxRetries: 5, logger });
8191
+ if (localEnabled && localEndpoint) {
8192
+ localClient = new SulcusLocalClient({
8193
+ endpoint: localEndpoint,
8194
+ apiKey: localApiKey,
8195
+ timeoutMs: localTimeoutMs,
8196
+ logger
8197
+ });
8198
+ logger.info(`sulcus: local sidecar client created (endpoint: ${localEndpoint}, timeout: ${localTimeoutMs}ms)`);
8199
+ localClient.probe().then((ok) => {
8200
+ if (ok) logger.info("sulcus: local sidecar probe OK \u2705");
8201
+ else logger.warn("sulcus: local sidecar probe failed \u2014 will retry on first use");
8202
+ }).catch(() => {
8203
+ logger.warn("sulcus: local sidecar probe failed \u2014 will retry on first use");
8204
+ });
8205
+ } else if (localConfig?.enabled === true && !localEndpoint) {
8206
+ logger.warn("sulcus: local.enabled=true but no local.endpoint or SULCUS_LOCAL_URL set \u2014 local-first disabled");
8207
+ }
8208
+ const hasLocalClient = localClient !== null;
8209
+ const effectiveBackendMode = hasLocalClient && isCloudBackend ? "dual" : backendMode;
8210
+ STATIC_AWARENESS = buildStaticAwareness(effectiveBackendMode, namespace);
6562
8211
  REBUILD_TOKEN_BUDGET = contextRebuildBudget;
6563
8212
  if (isAvailable) {
6564
- logger.info(`sulcus: ready \u2705 (backend: ${backendMode}, namespace: ${namespace}, autoRecall: ${autoRecall}, autoCapture: ${autoCapture}, captureFromAssistant: ${captureFromAssistant}, contextRebuild: ${contextRebuildEnabled})`);
8213
+ const localTag = hasLocalClient ? `, local: ${localEndpoint}` : "";
8214
+ const retryTag = hasLocalClient ? ", retryQueue: enabled" : "";
8215
+ logger.info(`sulcus: ready \u2705 (backend: ${effectiveBackendMode}, namespace: ${namespace}, autoRecall: ${autoRecall}, autoCapture: ${autoCapture}, captureFromAssistant: ${captureFromAssistant}, contextRebuild: ${contextRebuildEnabled}${localTag}${retryTag})`);
6565
8216
  } else {
6566
8217
  const hints = [];
6567
8218
  if (!serverUrl && !apiKey) {
@@ -6582,7 +8233,9 @@ var sulcusPlugin = {
6582
8233
  const siuRequestFn = isCloudBackend && sulcusMem ? (method, path, body) => sulcusMem.request(method, path, body) : null;
6583
8234
  const toolDeps = {
6584
8235
  sulcusMem,
6585
- backendMode,
8236
+ localClient,
8237
+ retryQueue,
8238
+ backendMode: effectiveBackendMode,
6586
8239
  namespace,
6587
8240
  nativeLoader,
6588
8241
  storeLibPath,
@@ -6626,7 +8279,9 @@ var sulcusPlugin = {
6626
8279
  try {
6627
8280
  api.registerMemoryFlushPlan(() => {
6628
8281
  if (!isAvailable || !sulcusMem) return null;
8282
+ const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
6629
8283
  return {
8284
+ relativePath: `memory/${today}.md`,
6630
8285
  softThresholdTokens: 15e3,
6631
8286
  forceFlushTranscriptBytes: "2mb",
6632
8287
  reserveTokensFloor: 3e4,
@@ -6672,7 +8327,7 @@ var sulcusPlugin = {
6672
8327
  const errorsHit = [];
6673
8328
  const userIntents = [];
6674
8329
  const assistantWork = [];
6675
- const DECISION_MARKERS = ["decided", "will use", "going to", "plan is", "the fix", "conclusion", "recommend", "approach"];
8330
+ const DECISION_MARKERS2 = ["decided", "will use", "going to", "plan is", "the fix", "conclusion", "recommend", "approach"];
6676
8331
  for (const msg of msgs) {
6677
8332
  const role = msg.role ?? msg.type;
6678
8333
  const text = typeof msg.content === "string" ? msg.content : typeof msg.text === "string" ? msg.text : Array.isArray(msg.content) ? msg.content.filter((c) => c.type === "text").map((c) => c.text).join("\n") : "";
@@ -6682,10 +8337,10 @@ var sulcusPlugin = {
6682
8337
  }
6683
8338
  if ((role === "assistant" || role === "ai") && text.length > 50) {
6684
8339
  const lc = text.toLowerCase();
6685
- if (DECISION_MARKERS.some((m) => lc.includes(m))) {
8340
+ if (DECISION_MARKERS2.some((m) => lc.includes(m))) {
6686
8341
  const sentences = text.split(/[.!?\n]/).filter((s) => s.trim().length > 15);
6687
8342
  for (const s of sentences) {
6688
- if (DECISION_MARKERS.some((m) => s.toLowerCase().includes(m))) {
8343
+ if (DECISION_MARKERS2.some((m) => s.toLowerCase().includes(m))) {
6689
8344
  decisions.push(s.trim().substring(0, 300));
6690
8345
  if (decisions.length >= 8) break;
6691
8346
  }
@@ -6833,6 +8488,46 @@ ${activity.join("\n")}`);
6833
8488
  logger.warn("sulcus: registerService failed: " + (e instanceof Error ? e.message : String(e)));
6834
8489
  }
6835
8490
  }
8491
+ if (hasLocalClient && isCloudBackend && sulcusMem) {
8492
+ const retryApiOn = api.on;
8493
+ const retryCloudClient = sulcusMem;
8494
+ retryApiOn("before_prompt_build", async () => {
8495
+ if (retryQueue.size === 0) return;
8496
+ try {
8497
+ await retryQueue.flush(async (item) => {
8498
+ switch (item.operation) {
8499
+ case "store": {
8500
+ const p = item.payload;
8501
+ await retryCloudClient.add_memory(
8502
+ p.content,
8503
+ p.memory_type,
8504
+ p.extraction_hints
8505
+ );
8506
+ break;
8507
+ }
8508
+ case "update": {
8509
+ await retryCloudClient.update_memory(item.key, item.payload);
8510
+ break;
8511
+ }
8512
+ case "delete": {
8513
+ await retryCloudClient.delete_memory(item.key);
8514
+ break;
8515
+ }
8516
+ case "boost": {
8517
+ const boosts = item.payload.boosts;
8518
+ if (boosts) await retryCloudClient.boost_batch(boosts);
8519
+ break;
8520
+ }
8521
+ default:
8522
+ logger.warn(`sulcus-retry: unknown operation ${item.operation}`);
8523
+ }
8524
+ });
8525
+ } catch (flushErr) {
8526
+ logger.debug?.(`sulcus: retry queue flush error: ${flushErr}`);
8527
+ }
8528
+ });
8529
+ logger.info("sulcus: registered retry queue flush hook");
8530
+ }
6836
8531
  if (isCloudBackend && sulcusMem) {
6837
8532
  if (autoRecall) {
6838
8533
  const sdkRecallHandler = buildSdkRecallHandler(
@@ -6844,7 +8539,8 @@ ${activity.join("\n")}`);
6844
8539
  boostOnRecallEnabled,
6845
8540
  tokenBudget,
6846
8541
  contextRebuildEnabled,
6847
- contextWindowSize
8542
+ contextWindowSize,
8543
+ hasLocalClient ? localClient : null
6848
8544
  );
6849
8545
  const apiOn = api.on;
6850
8546
  apiOn("before_prompt_build", async (event, ctx) => {
@@ -6930,17 +8626,17 @@ ${activity.join("\n")}`);
6930
8626
  const commandsRun = [];
6931
8627
  const decisions = [];
6932
8628
  const errors = [];
6933
- const DECISION_MARKERS = ["decided", "will use", "going to", "plan is", "the fix", "conclusion", "recommend", "approach"];
8629
+ const DECISION_MARKERS2 = ["decided", "will use", "going to", "plan is", "the fix", "conclusion", "recommend", "approach"];
6934
8630
  const ERROR_MARKERS = ["error:", "failed:", "exception", "traceback", "panicked", "stack trace"];
6935
8631
  for (const msg of messages) {
6936
8632
  const role = msg.role ?? msg.type;
6937
8633
  const rawContent = typeof msg.content === "string" ? msg.content : typeof msg.text === "string" ? msg.text : "";
6938
8634
  if ((role === "assistant" || role === "ai") && rawContent.length > 20) {
6939
8635
  const lc = rawContent.toLowerCase();
6940
- if (DECISION_MARKERS.some((m) => lc.includes(m))) {
8636
+ if (DECISION_MARKERS2.some((m) => lc.includes(m))) {
6941
8637
  const sentences = rawContent.split(/[.!?\n]/).filter((s) => s.trim().length > 10);
6942
8638
  for (const s of sentences) {
6943
- if (DECISION_MARKERS.some((m) => s.toLowerCase().includes(m)) && !decisions.includes(s.trim())) {
8639
+ if (DECISION_MARKERS2.some((m) => s.toLowerCase().includes(m)) && !decisions.includes(s.trim())) {
6944
8640
  decisions.push(s.trim().substring(0, 200));
6945
8641
  if (decisions.length >= 5) break;
6946
8642
  }
@@ -7031,7 +8727,7 @@ ${activity.join("\n")}`);
7031
8727
  const dreamMinMemories = pluginConfig?.dreamMinMemories ?? 50;
7032
8728
  const dreamMinHeat = pluginConfig?.dreamConsolidateMinHeat ?? 0.1;
7033
8729
  if (dreamEnabled && isAvailable && sulcusMem instanceof SulcusCloudClient) {
7034
- let readDreamState = function() {
8730
+ let readDreamState2 = function() {
7035
8731
  try {
7036
8732
  if ((0, import_node_fs.existsSync)(dreamStateFile)) {
7037
8733
  const raw = (0, import_node_fs.readFileSync)(dreamStateFile, "utf-8");
@@ -7044,12 +8740,12 @@ ${activity.join("\n")}`);
7044
8740
  } catch {
7045
8741
  }
7046
8742
  return { lastDreamMs: 0, lastSessionCount: 0 };
7047
- }, writeDreamState = function(state) {
8743
+ }, writeDreamState2 = function(state) {
7048
8744
  try {
7049
8745
  (0, import_node_fs.writeFileSync)(dreamStateFile, JSON.stringify(state));
7050
8746
  } catch {
7051
8747
  }
7052
- }, acquireDreamLock = function() {
8748
+ }, acquireDreamLock2 = function() {
7053
8749
  try {
7054
8750
  if ((0, import_node_fs.existsSync)(dreamLockFile)) {
7055
8751
  const lockAge = Date.now() - (JSON.parse((0, import_node_fs.readFileSync)(dreamLockFile, "utf-8")).ts ?? 0);
@@ -7060,12 +8756,13 @@ ${activity.join("\n")}`);
7060
8756
  } catch {
7061
8757
  return false;
7062
8758
  }
7063
- }, releaseDreamLock = function() {
8759
+ }, releaseDreamLock2 = function() {
7064
8760
  try {
7065
8761
  if ((0, import_node_fs.existsSync)(dreamLockFile)) require("node:fs").unlinkSync(dreamLockFile);
7066
8762
  } catch {
7067
8763
  }
7068
8764
  };
8765
+ var readDreamState = readDreamState2, writeDreamState = writeDreamState2, acquireDreamLock = acquireDreamLock2, releaseDreamLock = releaseDreamLock2;
7069
8766
  const stateDir = (0, import_node_path.resolve)(__dirname, ".sulcus-state");
7070
8767
  if (!(0, import_node_fs.existsSync)(stateDir)) (0, import_node_fs.mkdirSync)(stateDir, { recursive: true });
7071
8768
  const dreamStateFile = (0, import_node_path.resolve)(stateDir, "dream-state.json");
@@ -7079,7 +8776,7 @@ ${activity.join("\n")}`);
7079
8776
  dreamApiOn("agent_end", async () => {
7080
8777
  if (dreamSessionCount % dreamSessionInterval !== 0) return;
7081
8778
  if (dreamSessionCount === 0) return;
7082
- const state = readDreamState();
8779
+ const state = readDreamState2();
7083
8780
  const elapsed = Date.now() - state.lastDreamMs;
7084
8781
  if (elapsed < dreamMinGapMs) {
7085
8782
  logger.info(`sulcus/dream: gate 2 skip \u2014 ${Math.round(elapsed / 36e5)}h since last dream (need ${Math.round(dreamMinGapMs / 36e5)}h)`);
@@ -7098,18 +8795,18 @@ ${activity.join("\n")}`);
7098
8795
  logger.warn(`sulcus/dream: gate 3 error \u2014 ${e instanceof Error ? e.message : e}`);
7099
8796
  return;
7100
8797
  }
7101
- if (!acquireDreamLock()) {
8798
+ if (!acquireDreamLock2()) {
7102
8799
  logger.info("sulcus/dream: lock held \u2014 another consolidation in progress");
7103
8800
  return;
7104
8801
  }
7105
8802
  logger.info(`sulcus/dream: triggering consolidation (minHeat=${dreamMinHeat})`);
7106
8803
  sulcusMem.consolidate(dreamMinHeat).then((result) => {
7107
- writeDreamState({ lastDreamMs: Date.now(), lastSessionCount: dreamSessionCount });
8804
+ writeDreamState2({ lastDreamMs: Date.now(), lastSessionCount: dreamSessionCount });
7108
8805
  logger.info(`sulcus/dream: consolidation complete \u2014 ${JSON.stringify(result)}`);
7109
8806
  }).catch((e) => {
7110
8807
  logger.warn(`sulcus/dream: consolidation failed \u2014 ${e instanceof Error ? e.message : e}`);
7111
8808
  }).finally(() => {
7112
- releaseDreamLock();
8809
+ releaseDreamLock2();
7113
8810
  });
7114
8811
  });
7115
8812
  logger.info(`sulcus: dream auto-trigger enabled (every ${dreamSessionInterval} sessions, ${Math.round(dreamMinGapMs / 36e5)}h gap, min ${dreamMinMemories} memories)`);
@@ -7898,6 +9595,40 @@ ${res.items.length} shown${res.total ? ` of ${res.total}` : ""}`);
7898
9595
  logger.warn(`sulcus: registerCommand failed: ${e instanceof Error ? e.message : e}`);
7899
9596
  }
7900
9597
  }
9598
+ const contextEngineEnabled = pluginConfig?.contextEngine?.enabled === true;
9599
+ const assemblyMode = pluginConfig?.contextEngine?.assemblyMode ?? "passthrough";
9600
+ const compactMode = pluginConfig?.contextEngine?.compactMode ?? "smart";
9601
+ if (contextEngineEnabled && typeof api.registerContextEngine === "function") {
9602
+ const ceVersion = "7.2.0";
9603
+ const ceThresholds = pluginConfig?.contextEngine?.thresholds ?? {};
9604
+ api.registerContextEngine("openclaw-sulcus", async () => {
9605
+ let delegateCompaction;
9606
+ try {
9607
+ const sdk = await import("openclaw/plugin-sdk");
9608
+ delegateCompaction = sdk.delegateCompactionToRuntime;
9609
+ } catch {
9610
+ const sdk = await import("@anthropic-ai/openclaw-plugin-sdk");
9611
+ delegateCompaction = sdk.delegateCompactionToRuntime;
9612
+ }
9613
+ const engine = new SulcusContextEngine({
9614
+ version: ceVersion,
9615
+ assemblyMode,
9616
+ compactMode,
9617
+ logger,
9618
+ delegateCompaction,
9619
+ memoryClient: sulcusMem && sulcusMem instanceof SulcusCloudClient ? sulcusMem : null,
9620
+ namespace,
9621
+ thresholds: ceThresholds
9622
+ });
9623
+ return engine;
9624
+ });
9625
+ contextEngineActive = true;
9626
+ logger.info(`sulcus: context engine registered (v7.2.0, ownsCompaction: true, assembly: ${assemblyMode})`);
9627
+ } else if (contextEngineEnabled) {
9628
+ logger.warn("sulcus: contextEngine.enabled=true but api.registerContextEngine not available \u2014 skipping");
9629
+ } else {
9630
+ logger.info("sulcus: context engine disabled (set contextEngine.enabled: true to opt in)");
9631
+ }
7901
9632
  if (isAvailable && sulcusMem instanceof SulcusCloudClient) {
7902
9633
  importOpenClawHistory(sulcusMem, logger).catch((e) => {
7903
9634
  logger.warn(`sulcus: history import failed: ${e instanceof Error ? e.message : String(e)}`);