@mrciphersmith/keryx 0.3.42 → 0.3.43

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.js +171 -16
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -14119,7 +14119,7 @@ var package_default;
14119
14119
  var init_package = __esm(() => {
14120
14120
  package_default = {
14121
14121
  name: "@mrciphersmith/keryx",
14122
- version: "0.3.42",
14122
+ version: "0.3.43",
14123
14123
  description: "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
14124
14124
  private: false,
14125
14125
  publishConfig: {
@@ -85714,6 +85714,127 @@ var init_contracts = __esm(() => {
85714
85714
  ];
85715
85715
  });
85716
85716
 
85717
+ // src/harness/external/strict-schema.ts
85718
+ function isSchema(value) {
85719
+ return typeof value === "object" && value !== null && !Array.isArray(value);
85720
+ }
85721
+ function mapSchemas(value) {
85722
+ const out = {};
85723
+ if (!isSchema(value))
85724
+ return out;
85725
+ for (const [key, child] of Object.entries(value))
85726
+ out[key] = toStrictNode(child);
85727
+ return out;
85728
+ }
85729
+ function makeNullable(schema) {
85730
+ const type = schema.type;
85731
+ if (typeof type === "string") {
85732
+ if (type === "null")
85733
+ return schema;
85734
+ const out = { ...schema, type: [type, "null"] };
85735
+ if (Array.isArray(schema.enum) && !schema.enum.includes(null))
85736
+ out.enum = [...schema.enum, null];
85737
+ return out;
85738
+ }
85739
+ if (Array.isArray(type)) {
85740
+ if (type.includes("null")) {
85741
+ const out = { ...schema };
85742
+ if (Array.isArray(schema.enum) && !schema.enum.includes(null))
85743
+ out.enum = [...schema.enum, null];
85744
+ return out;
85745
+ }
85746
+ const out = { ...schema, type: [...type, "null"] };
85747
+ if (Array.isArray(schema.enum) && !schema.enum.includes(null))
85748
+ out.enum = [...schema.enum, null];
85749
+ return out;
85750
+ }
85751
+ if (Array.isArray(schema.enum) && schema.anyOf === undefined) {
85752
+ return schema.enum.includes(null) ? schema : { ...schema, enum: [...schema.enum, null] };
85753
+ }
85754
+ if (Array.isArray(schema.anyOf)) {
85755
+ return { ...schema, anyOf: [...schema.anyOf, NULL_SCHEMA] };
85756
+ }
85757
+ return { anyOf: [schema, NULL_SCHEMA] };
85758
+ }
85759
+ function toStrictNode(node) {
85760
+ if (!isSchema(node))
85761
+ return {};
85762
+ const out = {};
85763
+ if (node.type !== undefined)
85764
+ out.type = node.type;
85765
+ if (Array.isArray(node.enum))
85766
+ out.enum = [...node.enum];
85767
+ else if ("const" in node)
85768
+ out.enum = [node.const];
85769
+ if (typeof node.$ref === "string")
85770
+ out.$ref = node.$ref;
85771
+ const union = node.anyOf ?? node.oneOf;
85772
+ if (Array.isArray(union))
85773
+ out.anyOf = union.map(toStrictNode);
85774
+ if (isSchema(node.items))
85775
+ out.items = toStrictNode(node.items);
85776
+ if (isSchema(node.$defs))
85777
+ out.$defs = mapSchemas(node.$defs);
85778
+ const isObject = node.type === "object" || isSchema(node.properties);
85779
+ if (isObject) {
85780
+ const required = new Set(Array.isArray(node.required) ? node.required : []);
85781
+ const properties = {};
85782
+ for (const [key, child] of Object.entries(isSchema(node.properties) ? node.properties : {})) {
85783
+ const strict = toStrictNode(child);
85784
+ properties[key] = required.has(key) ? strict : makeNullable(strict);
85785
+ }
85786
+ out.type ??= "object";
85787
+ out.properties = properties;
85788
+ out.required = Object.keys(properties);
85789
+ out.additionalProperties = false;
85790
+ }
85791
+ return out;
85792
+ }
85793
+ function toStrictOutputSchema(bundled) {
85794
+ return toStrictNode(bundled);
85795
+ }
85796
+ function resolveRef3(node, root) {
85797
+ let current = node;
85798
+ for (let hops = 0;typeof current.$ref === "string" && hops < 16; hops += 1) {
85799
+ const ref = current.$ref;
85800
+ if (!ref.startsWith("#/"))
85801
+ return current;
85802
+ let target = root;
85803
+ for (const part of ref.slice(2).split("/")) {
85804
+ target = isSchema(target) ? target[decodeURIComponent(part)] : undefined;
85805
+ }
85806
+ if (!isSchema(target))
85807
+ return current;
85808
+ current = target;
85809
+ }
85810
+ return current;
85811
+ }
85812
+ function dropNulls(value, node, root) {
85813
+ if (!isSchema(node))
85814
+ return value;
85815
+ const schema = resolveRef3(node, root);
85816
+ if (Array.isArray(value)) {
85817
+ return isSchema(schema.items) ? value.map((item) => dropNulls(item, schema.items, root)) : value;
85818
+ }
85819
+ if (!isSchema(value) || !isSchema(schema.properties))
85820
+ return value;
85821
+ const required = new Set(Array.isArray(schema.required) ? schema.required : []);
85822
+ const out = {};
85823
+ for (const [key, child] of Object.entries(value)) {
85824
+ if (child === null && !required.has(key) && key in schema.properties)
85825
+ continue;
85826
+ out[key] = dropNulls(child, schema.properties[key], root);
85827
+ }
85828
+ return out;
85829
+ }
85830
+ function dropOptionalNulls(value, bundled) {
85831
+ return isSchema(bundled) ? dropNulls(value, bundled, bundled) : value;
85832
+ }
85833
+ var NULL_SCHEMA;
85834
+ var init_strict_schema = __esm(() => {
85835
+ NULL_SCHEMA = { type: "null" };
85836
+ });
85837
+
85717
85838
  // src/harness/external/prompt.ts
85718
85839
  function byteLength(text) {
85719
85840
  return Buffer.byteLength(text, "utf8");
@@ -87661,6 +87782,7 @@ async function superviseExternalRun(input, deps) {
87661
87782
  if (verdict === "drained") {
87662
87783
  return build(reportedExitCode ?? exitCodeFromEvents(events), false);
87663
87784
  }
87785
+ const exitedBeforeKill = reportedExitCode;
87664
87786
  handle.kill();
87665
87787
  const grace = afterMs2(input.killGraceMs ?? DEFAULT_KILL_GRACE_MS, undefined);
87666
87788
  const code = await Promise.race([exited, grace.promise]);
@@ -87668,7 +87790,7 @@ async function superviseExternalRun(input, deps) {
87668
87790
  if (verdict === "timeout") {
87669
87791
  return build(code ?? EXTERNAL_TIMEOUT_EXIT_CODE, true);
87670
87792
  }
87671
- return build(code ?? exitCodeFromEvents(events), false);
87793
+ return build(exitedBeforeKill ?? exitCodeFromEvents(events), false);
87672
87794
  }
87673
87795
  function deferred() {
87674
87796
  let resolve = () => {
@@ -87737,7 +87859,7 @@ function describeError(error) {
87737
87859
  return "unknown error";
87738
87860
  }
87739
87861
  }
87740
- var EXTERNAL_TIMEOUT_EXIT_CODE = 124, STDOUT_HEAD_BYTES, STDOUT_TAIL_BYTES, DEFAULT_KILL_GRACE_MS = 2000, DEFAULT_TERMINAL_SETTLE_MS = 2000, SUPERVISION_TICK_DIVISOR = 4, SUPERVISION_TICK_FLOOR_MS = 5;
87862
+ var EXTERNAL_TIMEOUT_EXIT_CODE = 124, STDOUT_HEAD_BYTES, STDOUT_TAIL_BYTES, DEFAULT_KILL_GRACE_MS = 2000, DEFAULT_TERMINAL_SETTLE_MS = 2000, CODEX_TERMINAL_SETTLE_MS = 1e4, SUPERVISION_TICK_DIVISOR = 4, SUPERVISION_TICK_FLOOR_MS = 5;
87741
87863
  var init_supervise = __esm(() => {
87742
87864
  init_bounded();
87743
87865
  init_bun_spawn_port();
@@ -88705,6 +88827,22 @@ async function runExternalChild(input, deps) {
88705
88827
  return refuse2("Error", schemaPrep.reason);
88706
88828
  const { schema: resultSchema, schemaText: resultSchemaText, schemaPath: resultSchemaPath, schemaDir } = schemaPrep;
88707
88829
  const resultSchemaInline = await prepareInlineSchema(resultSchema);
88830
+ const bundledResultSchema = JSON.parse(resultSchemaInline);
88831
+ const codexStrict = entry.id === "codex-cli";
88832
+ let resultSchemaPathForCodec = resultSchemaPath;
88833
+ if (codexStrict) {
88834
+ resultSchemaPathForCodec = path208.join(schemaDir, STRICT_SCHEMA_FILE);
88835
+ try {
88836
+ await writeFile32(resultSchemaPathForCodec, JSON.stringify(toStrictOutputSchema(bundledResultSchema)), "utf8");
88837
+ } catch (error) {
88838
+ await rm15(schemaDir, { recursive: true, force: true }).catch(() => {
88839
+ return;
88840
+ });
88841
+ const message = error instanceof Error ? error.message : String(error);
88842
+ return refuse2("Error", `failed to prepare the required result schema for validation: ${message}`);
88843
+ }
88844
+ }
88845
+ const normalize = codexStrict ? (value) => dropOptionalNulls(value, bundledResultSchema) : undefined;
88708
88846
  try {
88709
88847
  const assembled = buildExternalPrompt({
88710
88848
  taskTitle: input.taskTitle,
@@ -88731,7 +88869,7 @@ async function runExternalChild(input, deps) {
88731
88869
  write: sandbox === "worktree-write",
88732
88870
  worktreePath: created.path,
88733
88871
  prompt: assembled.prompt,
88734
- validate: (built) => validateStructuredResult(built, resultSchema)
88872
+ validate: (built) => validateStructuredResult(built, resultSchema, normalize)
88735
88873
  });
88736
88874
  }
88737
88875
  const runInput = {
@@ -88741,7 +88879,7 @@ async function runExternalChild(input, deps) {
88741
88879
  ...input.runtime.model === undefined || input.runtime.model === null ? {} : { model: input.runtime.model },
88742
88880
  ...input.sessionId === undefined ? {} : { sessionId: input.sessionId },
88743
88881
  ...input.runtime.maxCostUnits === undefined || input.runtime.maxCostUnits === null ? {} : { maxCostUnits: input.runtime.maxCostUnits },
88744
- resultSchemaPath,
88882
+ resultSchemaPath: resultSchemaPathForCodec,
88745
88883
  resultSchema: resultSchemaInline,
88746
88884
  printTimeoutSeconds: Math.ceil(input.timeoutMs / 1000)
88747
88885
  };
@@ -88762,6 +88900,7 @@ async function runExternalChild(input, deps) {
88762
88900
  timeoutMs: input.timeoutMs,
88763
88901
  ...entry.maxStderrBytes === undefined ? {} : { maxStderrBytes: entry.maxStderrBytes },
88764
88902
  stdin: streaming ? "pipe" : "ignore",
88903
+ ...entry.id === "codex-cli" ? { terminalSettleMs: CODEX_TERMINAL_SETTLE_MS } : {},
88765
88904
  ...streaming ? {
88766
88905
  initialStdin: [
88767
88906
  codec.encodeStdinMessage(assembled.prompt)
@@ -88777,8 +88916,14 @@ async function runExternalChild(input, deps) {
88777
88916
  ...deps.onSupervisionTrigger === undefined ? {} : { onSupervisionTrigger: deps.onSupervisionTrigger }
88778
88917
  });
88779
88918
  const cause = outcome.overflow ?? codec.classifyFailure(outcome);
88780
- const built = buildOutcome({ cause, outcome, argv, worktreePath: created.path });
88781
- return await validateStructuredResult(built, resultSchema);
88919
+ const built = buildOutcome({
88920
+ cause,
88921
+ outcome,
88922
+ argv,
88923
+ worktreePath: created.path,
88924
+ finalMessageOnly: entry.id === "codex-cli"
88925
+ });
88926
+ return await validateStructuredResult(built, resultSchema, normalize);
88782
88927
  } finally {
88783
88928
  await deps.worktree.remove(input.worktreeId).catch(() => {
88784
88929
  return;
@@ -88790,7 +88935,7 @@ async function runExternalChild(input, deps) {
88790
88935
  });
88791
88936
  }
88792
88937
  }
88793
- async function validateStructuredResult(built, resultSchema) {
88938
+ async function validateStructuredResult(built, resultSchema, normalize) {
88794
88939
  if (built.status !== "Completed")
88795
88940
  return built;
88796
88941
  let parsed;
@@ -88800,12 +88945,13 @@ async function validateStructuredResult(built, resultSchema) {
88800
88945
  const message = error instanceof Error ? error.message : String(error);
88801
88946
  return structuredResultError(`structured result is not valid JSON: ${message}`, built);
88802
88947
  }
88803
- const validationErrors = await validateJson(parsed, resultSchema);
88948
+ const normalized = normalize === undefined ? parsed : normalize(parsed);
88949
+ const validationErrors = await validateJson(normalized, resultSchema);
88804
88950
  if (validationErrors.length > 0) {
88805
88951
  const detail = validationErrors.slice(0, 3).map((e) => `${e.path}: ${e.message}`).join("; ");
88806
88952
  return structuredResultError(`structured result failed subagent-result schema validation: ${detail}`, built);
88807
88953
  }
88808
- return built;
88954
+ return normalized === parsed ? built : { ...built, output: JSON.stringify(normalized) };
88809
88955
  }
88810
88956
  async function runAcpBranch(args) {
88811
88957
  const { input, deps, entry, worktreePath } = args;
@@ -88865,10 +89011,10 @@ async function runAcpBranch(args) {
88865
89011
  return { ...final, acp: record };
88866
89012
  }
88867
89013
  function buildOutcome(args) {
88868
- const { cause, outcome, argv, worktreePath } = args;
89014
+ const { cause, outcome, argv, worktreePath, finalMessageOnly } = args;
88869
89015
  const sessionRef = findSessionRef(outcome.events);
88870
89016
  const costUnits = findCostUnits(outcome.events);
88871
- const text = collectAssistantText(outcome.events);
89017
+ const text = collectAssistantText(outcome.events, finalMessageOnly);
88872
89018
  const base = {
88873
89019
  argv,
88874
89020
  worktreePath,
@@ -88903,13 +89049,21 @@ function findCostUnits(events) {
88903
89049
  }
88904
89050
  return cost;
88905
89051
  }
88906
- function collectAssistantText(events) {
89052
+ function collectAssistantText(events, finalMessageOnly) {
88907
89053
  for (let i = events.length - 1;i >= 0; i -= 1) {
88908
89054
  const event = events[i];
88909
89055
  if (event?.kind === "child_finished" && event.text !== undefined && event.text.trim().length > 0) {
88910
89056
  return event.text.trim();
88911
89057
  }
88912
89058
  }
89059
+ if (finalMessageOnly) {
89060
+ for (let i = events.length - 1;i >= 0; i -= 1) {
89061
+ const event = events[i];
89062
+ if (event?.kind === "assistant_text" && event.text.trim().length > 0)
89063
+ return event.text.trim();
89064
+ }
89065
+ return "";
89066
+ }
88913
89067
  const parts = [];
88914
89068
  for (const event of events) {
88915
89069
  if (event.kind === "assistant_text")
@@ -88918,10 +89072,11 @@ function collectAssistantText(events) {
88918
89072
  return parts.join(`
88919
89073
  `).trim();
88920
89074
  }
88921
- var DEFAULT_BUDGET_THRESHOLD_FRACTION = 0.8, DEFAULT_NO_PROGRESS_INTERVAL_MS = 30000, DENIED_CAUSE_MARKERS;
89075
+ var DEFAULT_BUDGET_THRESHOLD_FRACTION = 0.8, DEFAULT_NO_PROGRESS_INTERVAL_MS = 30000, DENIED_CAUSE_MARKERS, STRICT_SCHEMA_FILE = "subagent-result.strict.schema.json";
88922
89076
  var init_runtime2 = __esm(() => {
88923
89077
  init_contracts();
88924
89078
  init_dispatch();
89079
+ init_strict_schema();
88925
89080
  init_env();
88926
89081
  init_prompt2();
88927
89082
  init_registry3();
@@ -92204,7 +92359,7 @@ function describeValue4(value) {
92204
92359
  return "null";
92205
92360
  return typeof value;
92206
92361
  }
92207
- function resolveRef3(ref, rootSchema) {
92362
+ function resolveRef4(ref, rootSchema) {
92208
92363
  if (ref.startsWith("#/$defs/")) {
92209
92364
  const name = ref.replace("#/$defs/", "");
92210
92365
  const schema = rootSchema.$defs?.[name];
@@ -92226,7 +92381,7 @@ function validateAgainstSchema3(value, schema, rootSchema) {
92226
92381
  }
92227
92382
  function walk4(value, schema, valuePath, rootSchema, errors) {
92228
92383
  if (schema.$ref) {
92229
- const resolved = resolveRef3(schema.$ref, rootSchema);
92384
+ const resolved = resolveRef4(schema.$ref, rootSchema);
92230
92385
  walk4(value, resolved.schema, valuePath, resolved.root, errors);
92231
92386
  return;
92232
92387
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrciphersmith/keryx",
3
- "version": "0.3.42",
3
+ "version": "0.3.43",
4
4
  "description": "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
5
5
  "private": false,
6
6
  "publishConfig": {