@remembrance-ai/opencode-plugin 0.2.4 → 0.2.5

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/opencode.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "command": [
7
7
  "npx",
8
8
  "-y",
9
- "@remembrance-ai/mcp-server@0.2.4"
9
+ "@remembrance-ai/mcp-server@0.2.5"
10
10
  ],
11
11
  "enabled": true,
12
12
  "environment": {
@@ -16,6 +16,6 @@
16
16
  }
17
17
  },
18
18
  "plugin": [
19
- "@remembrance-ai/opencode-plugin@0.2.4"
19
+ "@remembrance-ai/opencode-plugin@0.2.5"
20
20
  ]
21
21
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remembrance-ai/opencode-plugin",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
4
  "license": "Apache-2.0",
5
5
  "description": "opencode plugin for Remembrance session hooks, tool observation, and contribution nudges.",
6
6
  "type": "module",
@@ -4798,8 +4798,8 @@ function parseSurfaceReleases(value) {
4798
4798
  );
4799
4799
  }
4800
4800
  function isCanonicalIsoTimestamp(value) {
4801
- const timestamp = Date.parse(value);
4802
- return Number.isFinite(timestamp) && new Date(timestamp).toISOString() === value;
4801
+ const timestamp2 = Date.parse(value);
4802
+ return Number.isFinite(timestamp2) && new Date(timestamp2).toISOString() === value;
4803
4803
  }
4804
4804
 
4805
4805
  // ../core/src/client-health-alerts.ts
@@ -9900,6 +9900,9 @@ var MAX_RSA_FACTOR_SEARCH_MODULUS = 1n << 4096n;
9900
9900
  var MAX_RSA_ARITHMETIC_WORK = 2 ** 32;
9901
9901
  var MAX_REQUEST_RSA_ARITHMETIC_WORK = 2 ** 34;
9902
9902
  var currentRedactionRequest = () => void 0;
9903
+ function trackRedactionRequests(current) {
9904
+ currentRedactionRequest = current;
9905
+ }
9903
9906
  var MIN_DETACHED_RSA_CONTINUATION_BYTES = 16;
9904
9907
  var MAX_DETACHED_RSA_CONTINUATION_READS = 1 << 20;
9905
9908
  var detachedRsaContinuationReads = /* @__PURE__ */ new WeakMap();
@@ -10506,9 +10509,137 @@ function serializedPublicRsaKeyValueIsValid2(value, metrics) {
10506
10509
  function rsaPublicNumbersArePlausible2(modulus, exponent, metrics) {
10507
10510
  return rsaPublicNumbersArePlausible(modulus, exponent, metrics);
10508
10511
  }
10512
+ function redactPayload(payload) {
10513
+ const analysis = analyzeSensitivePayload(payload);
10514
+ return redactAnalyzedPayload(payload, analysis);
10515
+ }
10516
+ function redactAnalyzedPayload(payload, analysis) {
10517
+ if (!payload || typeof payload !== "object") {
10518
+ if (typeof payload !== "string") return payload;
10519
+ const baseline = redactStringValue(payload, analysis.metrics);
10520
+ const ranges = lateStringOverlapRanges(payload, analysis.metrics);
10521
+ analysis.sensitiveMaterial ||= ranges.length > 0;
10522
+ return applyLateOverlapRanges(baseline, ranges, analysis.metrics);
10523
+ }
10524
+ const valuePlans = /* @__PURE__ */ new Map();
10525
+ const keyPlans = /* @__PURE__ */ new Map();
10526
+ const outputKeys = /* @__PURE__ */ new Map();
10527
+ const clones = analysis.graph.containers.map(
10528
+ (container) => analysis.numericPrivateContainers.has(container) ? "[redacted]" : container.kind === "array" ? new Array(container.value.length) : {}
10529
+ );
10530
+ for (const container of analysis.graph.containers) {
10531
+ if (analysis.numericPrivateContainers.has(container)) continue;
10532
+ const target = clones[container.id];
10533
+ if (container.kind === "array") {
10534
+ for (const edge of container.edges) {
10535
+ target[edge.index] = redactedEdgeValue(
10536
+ edge,
10537
+ analysis,
10538
+ clones,
10539
+ valuePlans
10540
+ );
10541
+ }
10542
+ continue;
10543
+ }
10544
+ const objectTarget = target;
10545
+ const redactedKeys = analysis.sensitiveObjectKeys.get(container.value);
10546
+ const reservedKeys = new Set(
10547
+ container.edges.filter((edge) => !redactedKeys?.has(edge.key)).map((edge) => edge.key)
10548
+ );
10549
+ const usedKeys = /* @__PURE__ */ new Set();
10550
+ const nextSuffix = /* @__PURE__ */ new Map();
10551
+ for (const edge of container.edges) {
10552
+ const keyPlan = redactedKeys?.has(edge.key) ? redactStringValue(edge.key, analysis.metrics) : unchangedStringProjection(edge.key);
10553
+ const forcedKey = redactedKeys?.has(edge.key) && !keyPlan.changed;
10554
+ keyPlans.set(edge, forcedKey ? fullyRedactedStringProjection() : keyPlan);
10555
+ const outputKey = redactedKeys?.has(edge.key) ? uniqueRedactedObjectKey(
10556
+ reservedKeys,
10557
+ usedKeys,
10558
+ nextSuffix,
10559
+ forcedKey ? "[redacted]" : keyPlan.value
10560
+ ) : edge.key;
10561
+ outputKeys.set(edge, outputKey);
10562
+ usedKeys.add(outputKey);
10563
+ const value = isSensitiveKey(edge.key) && edge.value !== null && edge.value !== void 0 ? "[redacted]" : redactedEdgeValue(edge, analysis, clones, valuePlans);
10564
+ Object.defineProperty(objectTarget, outputKey, {
10565
+ value,
10566
+ enumerable: true,
10567
+ configurable: true,
10568
+ writable: true
10569
+ });
10570
+ }
10571
+ }
10572
+ const claims = latePayloadOverlapRanges(analysis, valuePlans, keyPlans);
10573
+ for (const container of analysis.graph.containers) {
10574
+ if (analysis.numericPrivateContainers.has(container)) continue;
10575
+ const target = clones[container.id];
10576
+ const renamed = /* @__PURE__ */ new Map();
10577
+ for (const edge of container.edges) {
10578
+ const valuePlan = valuePlans.get(edge);
10579
+ const valueRanges = edge.valueToken && claims.get(edge.valueToken);
10580
+ if (valuePlan && valueRanges) {
10581
+ const key = container.kind === "array" ? edge.index : outputKeys.get(edge);
10582
+ target[key] = applyLateOverlapRanges(
10583
+ valuePlan,
10584
+ valueRanges,
10585
+ analysis.metrics
10586
+ );
10587
+ }
10588
+ const keyPlan = keyPlans.get(edge);
10589
+ const keyRanges = edge.keyToken && claims.get(edge.keyToken);
10590
+ if (keyPlan && keyRanges) {
10591
+ const oldKey = outputKeys.get(edge);
10592
+ const candidate = applyLateOverlapRanges(
10593
+ { ...keyPlan, value: oldKey },
10594
+ keyRanges,
10595
+ analysis.metrics
10596
+ );
10597
+ if (candidate !== oldKey) renamed.set(edge, candidate);
10598
+ }
10599
+ }
10600
+ if (renamed.size === 0) continue;
10601
+ const reserved = new Set(
10602
+ container.edges.filter((edge) => !renamed.has(edge)).map((edge) => outputKeys.get(edge))
10603
+ );
10604
+ const used = /* @__PURE__ */ new Set();
10605
+ const suffixes = /* @__PURE__ */ new Map();
10606
+ const entries = container.edges.map((edge) => {
10607
+ const oldKey = outputKeys.get(edge);
10608
+ const candidate = renamed.get(edge);
10609
+ const key = candidate === void 0 ? oldKey : uniqueRedactedObjectKey(reserved, used, suffixes, candidate);
10610
+ used.add(key);
10611
+ return { oldKey, key, value: target[oldKey] };
10612
+ });
10613
+ for (const { oldKey } of entries) delete target[oldKey];
10614
+ for (const { key, value } of entries)
10615
+ Object.defineProperty(target, key, {
10616
+ value,
10617
+ enumerable: true,
10618
+ configurable: true,
10619
+ writable: true
10620
+ });
10621
+ }
10622
+ return clones[analysis.graph.root.id];
10623
+ }
10509
10624
  function containsSensitiveMaterial(payload) {
10510
10625
  return inspectPayloadForSensitiveMaterial(payload).sensitiveMaterial;
10511
10626
  }
10627
+ function redactedEdgeValue(edge, analysis, clones, plans) {
10628
+ if (edge.child) return clones[edge.child.id];
10629
+ if (typeof edge.value !== "string") return edge.value;
10630
+ const ranges = analysis.privateKeyBodyRanges.get(edge.owner.value)?.get(edge.key);
10631
+ const native = ranges ? projectedTextRanges(edge.value, ranges, analysis.metrics) : unchangedStringProjection(edge.value);
10632
+ const lexical = redactStringValue(native.value, analysis.metrics);
10633
+ plans.set(edge, {
10634
+ ...lexical,
10635
+ retained: composeRetainedSourceSpans(
10636
+ native.retained,
10637
+ lexical.retained,
10638
+ analysis.metrics
10639
+ )
10640
+ });
10641
+ return lexical.value;
10642
+ }
10512
10643
  function inspectPayloadForSensitiveMaterial(payload) {
10513
10644
  const analysis = analyzeSensitivePayload(payload);
10514
10645
  latePayloadOverlapRanges(analysis);
@@ -20513,6 +20644,16 @@ function isShortPemBoundaryFragment(value) {
20513
20644
  if (isCompletePemBoundary(trimmed)) return true;
20514
20645
  return trimmed.length <= 32 && /^-{5}(?:BEGIN|END)[A-Z0-9_-]{0,18}$/iu.test(trimmed) && exactPrivateKeyMarker2(trimmed) === null;
20515
20646
  }
20647
+ function uniqueRedactedObjectKey(reserved, used, nextSuffixByBase, candidate) {
20648
+ const base = candidate === "[redacted]" ? "[redacted-key]" : candidate;
20649
+ if (!reserved.has(base) && !used.has(base)) return base;
20650
+ let suffix = nextSuffixByBase.get(base) ?? 2;
20651
+ while (reserved.has(`${base}-${suffix}`) || used.has(`${base}-${suffix}`)) {
20652
+ suffix += 1;
20653
+ }
20654
+ nextSuffixByBase.set(base, suffix + 1);
20655
+ return `${base}-${suffix}`;
20656
+ }
20516
20657
  function createRedactionWorkMetrics() {
20517
20658
  return {
20518
20659
  nodesVisited: 0,
@@ -22612,6 +22753,9 @@ function unchangedStringProjection(value) {
22612
22753
  retained: value.length ? [{ start: 0, end: value.length, outputStart: 0 }] : []
22613
22754
  };
22614
22755
  }
22756
+ function fullyRedactedStringProjection() {
22757
+ return { value: "[redacted]", changed: true, retained: [] };
22758
+ }
22615
22759
  function appendRetainedSourceSpan(spans, span) {
22616
22760
  spans.push(span);
22617
22761
  }
@@ -22651,6 +22795,67 @@ function retainedAfterRedactionRanges(retained, ranges, metrics, replacementLeng
22651
22795
  }
22652
22796
  return result;
22653
22797
  }
22798
+ function projectedTextRanges(value, ranges, metrics) {
22799
+ if (ranges.length === 1 && ranges[0].start === 0 && ranges[0].end === value.length) {
22800
+ return fullyRedactedStringProjection();
22801
+ }
22802
+ const original = unchangedStringProjection(value);
22803
+ return {
22804
+ value: redactTextRanges(value, ranges),
22805
+ changed: ranges.length > 0,
22806
+ retained: retainedAfterRedactionRanges(original.retained, ranges, metrics)
22807
+ };
22808
+ }
22809
+ function composeRetainedSourceSpans(first, second, metrics) {
22810
+ if (first.length === 0 || second.length === 0) return [];
22811
+ const result = [];
22812
+ let leftIndex = 0;
22813
+ let rightIndex = 0;
22814
+ while (leftIndex < first.length && rightIndex < second.length) {
22815
+ metrics.ownershipSteps += 1;
22816
+ const left = first[leftIndex];
22817
+ const right = second[rightIndex];
22818
+ const leftEnd = left.outputStart + left.end - left.start;
22819
+ const start = Math.max(left.outputStart, right.start);
22820
+ const end = Math.min(leftEnd, right.end);
22821
+ if (start < end)
22822
+ appendRetainedSourceSpan(result, {
22823
+ start: left.start + start - left.outputStart,
22824
+ end: left.start + end - left.outputStart,
22825
+ outputStart: right.outputStart + start - right.start
22826
+ });
22827
+ if (leftEnd <= right.end) leftIndex += 1;
22828
+ if (right.end <= leftEnd) rightIndex += 1;
22829
+ }
22830
+ return result;
22831
+ }
22832
+ function applyLateOverlapRanges(baseline, claims, metrics) {
22833
+ if (claims.length === 0 || baseline.retained.length === 0)
22834
+ return baseline.value;
22835
+ const projected = [];
22836
+ let claimIndex = 0;
22837
+ for (const [index, span] of baseline.retained.entries()) {
22838
+ metrics.ownershipSteps += 1;
22839
+ while (claimIndex < claims.length && claims[claimIndex].end <= span.start) {
22840
+ metrics.ownershipSteps += 1;
22841
+ claimIndex += 1;
22842
+ }
22843
+ let current = claimIndex;
22844
+ while (current < claims.length && claims[current].start < span.end) {
22845
+ metrics.ownershipSteps += 1;
22846
+ const claim = claims[current++];
22847
+ const start = Math.max(span.start, claim.start);
22848
+ const end = Math.min(span.end, claim.end);
22849
+ const previous = baseline.retained[index - 1];
22850
+ const next = baseline.retained[index + 1];
22851
+ projected.push({
22852
+ start: start === span.start ? previous ? previous.outputStart + previous.end - previous.start : 0 : span.outputStart + start - span.start,
22853
+ end: end === span.end ? next ? next.outputStart : baseline.value.length : span.outputStart + end - span.start
22854
+ });
22855
+ }
22856
+ }
22857
+ return projected.length === 0 ? baseline.value : redactTextRanges(baseline.value, mergeOrderedTextRanges([], projected));
22858
+ }
22654
22859
  function latePayloadOverlapRanges(analysis, valuePlans, keyPlans) {
22655
22860
  const { graph, metrics } = analysis;
22656
22861
  const claims = /* @__PURE__ */ new Map();
@@ -23446,7 +23651,8 @@ var remembrance_mcp_consent_lanes_default = {
23446
23651
  propose_skill_idea: "skill_candidate",
23447
23652
  submit_suggestion: "skill_suggestion",
23448
23653
  submit_resource: "resource_submission",
23449
- submit_resource_review: "resource_review"
23654
+ submit_resource_review: "resource_review",
23655
+ repair_review_draft: "admin_review_repair"
23450
23656
  };
23451
23657
 
23452
23658
  // ../core/src/remembrance-mcp-policy.ts
@@ -23487,6 +23693,10 @@ var REMEMBRANCE_MCP_OPTIONAL_SHARED_CONTRIBUTION_TOOLS = Object.freeze(
23487
23693
  "register_agent_key"
23488
23694
  ]
23489
23695
  );
23696
+ var REMEMBRANCE_MCP_OPTIONAL_ADMIN_REVIEW_TOOLS = Object.freeze([
23697
+ "get_review_comparison",
23698
+ "repair_review_draft"
23699
+ ]);
23490
23700
  var REMEMBRANCE_MCP_LOCAL_ONLY_TOOLS = Object.freeze([
23491
23701
  "bootstrap_agent_identity",
23492
23702
  "acknowledge_feedback_denial",
@@ -23526,6 +23736,7 @@ var REMEMBRANCE_MCP_RECOMMENDED_LOCAL_ORG_TOOLS = Object.freeze([
23526
23736
  var REMEMBRANCE_MCP_ALL_TOOLS = Object.freeze([
23527
23737
  ...REMEMBRANCE_MCP_RECOMMENDED_ORG_TOOLS,
23528
23738
  ...REMEMBRANCE_MCP_OPTIONAL_SHARED_CONTRIBUTION_TOOLS,
23739
+ ...REMEMBRANCE_MCP_OPTIONAL_ADMIN_REVIEW_TOOLS,
23529
23740
  ...REMEMBRANCE_MCP_LOCAL_ONLY_TOOLS
23530
23741
  ]);
23531
23742
  var REMEMBRANCE_MCP_BOUNDED_WRITE_POLICY_MAX_BYTES_BY_PAYLOAD_CLASS = Object.freeze({
@@ -23621,6 +23832,8 @@ var REMEMBRANCE_MCP_TOOL_SECURITY = freezeMcpToolSecurityManifest({
23621
23832
  link_current_installation: bounded("bounded_metadata"),
23622
23833
  get_skill: read(),
23623
23834
  get_resource: read(),
23835
+ get_review_comparison: read(),
23836
+ repair_review_draft: content("repair_review_draft", "private_skill"),
23624
23837
  bootstrap_agent_identity: bounded("bounded_metadata"),
23625
23838
  acknowledge_feedback_denial: local("bounded_feedback"),
23626
23839
  decline_pending_action: local("bounded_metadata"),
@@ -23895,6 +24108,455 @@ var privateSkillCompletionContinuationSchema = external_exports.discriminatedUni
23895
24108
  continuation.extend({ operation: external_exports.literal("resume"), mode: external_exports.literal("changed_revision"), deadline_at: external_exports.number().int().positive() }).strict()
23896
24109
  ]);
23897
24110
 
24111
+ // ../core/src/verifier.ts
24112
+ var duplicateCandidateSchema = external_exports.object({
24113
+ type: external_exports.enum(["skill", "resource", "remembrance"]),
24114
+ id: external_exports.string().min(1).max(MAX_SHORT_TEXT_LENGTH),
24115
+ slug: external_exports.string().min(1).max(MAX_SHORT_TEXT_LENGTH).optional(),
24116
+ similarity: external_exports.number().min(0).max(1)
24117
+ });
24118
+ var PROPOSED_METADATA_MAX_KEYWORDS = 25;
24119
+ var PROPOSED_METADATA_MAX_TAGS = 15;
24120
+ var PROPOSED_METADATA_MAX_DOMAINS = 8;
24121
+ var PROPOSED_METADATA_MAX_TERM_LENGTH = 64;
24122
+ var PROPOSED_METADATA_MAX_RATIONALE_LENGTH = 2e3;
24123
+ var PROPOSED_METADATA_MAX_MODEL_LENGTH = MAX_SHORT_TEXT_LENGTH;
24124
+ var LOW_VALUE_PROPOSED_METADATA_EXACT_TERMS = /* @__PURE__ */ new Set([
24125
+ "agent",
24126
+ "agents",
24127
+ "agent-skill",
24128
+ "agent-skills",
24129
+ "backfill",
24130
+ "candidate",
24131
+ "candidates",
24132
+ "leaderboard",
24133
+ "import",
24134
+ "imported",
24135
+ "rank",
24136
+ "ranking",
24137
+ "skill",
24138
+ "skills",
24139
+ "skills-sh",
24140
+ "skill-candidate",
24141
+ "skill-candidates"
24142
+ ]);
24143
+ var LOW_VALUE_PROPOSED_METADATA_PATTERNS = [
24144
+ /\bagent\s+skills?\b/i,
24145
+ /\bagentspace\b/i,
24146
+ /\bskill\s+candidates?\b/i,
24147
+ /\bskills?[\s.-]*sh\b/i,
24148
+ /\bleaderboard\s+rank\b/i,
24149
+ /\bnpx\s+skills\s+add\b/i
24150
+ ];
24151
+ function sanitizeProposedMetadataTerm(value) {
24152
+ if (typeof value !== "string") {
24153
+ return null;
24154
+ }
24155
+ const trimmed = value.trim().slice(0, PROPOSED_METADATA_MAX_TERM_LENGTH);
24156
+ if (!trimmed) {
24157
+ return null;
24158
+ }
24159
+ const normalized2 = trimmed.toLowerCase().replace(/[_/.]+/g, "-").replace(/\s+/g, " ").replace(/^-+|-+$/g, "");
24160
+ if (LOW_VALUE_PROPOSED_METADATA_EXACT_TERMS.has(normalized2)) {
24161
+ return null;
24162
+ }
24163
+ if (LOW_VALUE_PROPOSED_METADATA_PATTERNS.some(
24164
+ (pattern) => pattern.test(trimmed)
24165
+ )) {
24166
+ return null;
24167
+ }
24168
+ return trimmed;
24169
+ }
24170
+ var proposedMetadataTermArraySchema = (max) => external_exports.preprocess(
24171
+ (value) => {
24172
+ if (!Array.isArray(value)) {
24173
+ return value;
24174
+ }
24175
+ const seen = /* @__PURE__ */ new Set();
24176
+ const terms = [];
24177
+ for (const entry of value) {
24178
+ const trimmed = sanitizeProposedMetadataTerm(entry);
24179
+ if (!trimmed) {
24180
+ continue;
24181
+ }
24182
+ const key = trimmed.toLowerCase();
24183
+ if (seen.has(key)) {
24184
+ continue;
24185
+ }
24186
+ seen.add(key);
24187
+ terms.push(trimmed);
24188
+ }
24189
+ return terms.slice(0, max);
24190
+ },
24191
+ external_exports.array(external_exports.string().min(1).max(PROPOSED_METADATA_MAX_TERM_LENGTH)).max(max)
24192
+ );
24193
+ var proposedSkillMetadataSchema = external_exports.object({
24194
+ keywords: proposedMetadataTermArraySchema(PROPOSED_METADATA_MAX_KEYWORDS),
24195
+ tags: proposedMetadataTermArraySchema(PROPOSED_METADATA_MAX_TAGS),
24196
+ domains: proposedMetadataTermArraySchema(PROPOSED_METADATA_MAX_DOMAINS),
24197
+ rationale: external_exports.string().trim().min(1).max(PROPOSED_METADATA_MAX_RATIONALE_LENGTH),
24198
+ model: external_exports.string().trim().min(1).max(PROPOSED_METADATA_MAX_MODEL_LENGTH)
24199
+ });
24200
+ var verifierOutputSchema = external_exports.object({
24201
+ recommended_action: verifierActionSchema,
24202
+ confidence: external_exports.number().min(0).max(1),
24203
+ duplicate_candidates: external_exports.array(duplicateCandidateSchema).max(20).default([]),
24204
+ safety_flags: external_exports.array(external_exports.string().min(1).max(MAX_SHORT_TEXT_LENGTH)).max(40).default([]),
24205
+ summary: external_exports.string().min(1).max(MAX_LONG_TEXT_LENGTH),
24206
+ proposed_patch: external_exports.string().max(MAX_LONG_TEXT_LENGTH).nullable().default(null),
24207
+ score_updates: external_exports.record(external_exports.unknown()).superRefine((value, ctx) => {
24208
+ if (serializedJsonBytes(value) > MAX_JSON_FIELD_BYTES) {
24209
+ addSerializedJsonBytesIssue(
24210
+ ctx,
24211
+ MAX_JSON_FIELD_BYTES,
24212
+ `score_updates must be ${MAX_JSON_FIELD_BYTES} bytes or smaller`
24213
+ );
24214
+ }
24215
+ }).nullable().default(null),
24216
+ // Advisory retrieval enrichment (see proposedSkillMetadataSchema). Optional
24217
+ // and defaults to null so existing verifier outputs remain valid.
24218
+ proposed_metadata: proposedSkillMetadataSchema.nullable().default(null),
24219
+ // Advisory evidence-routing assessment. Deterministic routing and the
24220
+ // existing safety/review gates decide whether it can create a candidate.
24221
+ // Topology is advisory and newer than the base verifier contract. A legacy
24222
+ // or malformed topology fragment must not discard an otherwise valid safety
24223
+ // verdict; it degrades to null, which the topology router retains as
24224
+ // evidence-only rather than allowing an unclassified mutation.
24225
+ topology_assessment: skillTopologyAssessmentSchema.nullable().default(null).catch(null)
24226
+ });
24227
+
24228
+ // ../core/src/verification-policy.ts
24229
+ var verificationReviewReasonCodeSchema = external_exports.enum([
24230
+ "safe_useful",
24231
+ "insufficient_evidence",
24232
+ "duplicate",
24233
+ "strategy_divergence",
24234
+ "quality_regression",
24235
+ "out_of_scope",
24236
+ "policy_preference",
24237
+ "sensitive_data",
24238
+ "prompt_injection",
24239
+ "malware_or_phishing",
24240
+ "spam",
24241
+ "invalid_submission",
24242
+ "unsupported_operation",
24243
+ "other"
24244
+ ]);
24245
+ var verifierAssessmentSchema = external_exports.object({
24246
+ evidence_quality: external_exports.number().min(0).max(1),
24247
+ safety_risk: external_exports.number().min(0).max(1),
24248
+ quality_confidence: external_exports.number().min(0).max(1),
24249
+ duplicate_likelihood: external_exports.number().min(0).max(1),
24250
+ applicability: external_exports.number().min(0).max(1),
24251
+ novelty: external_exports.number().min(0).max(1)
24252
+ }).strict();
24253
+ var verificationRubricPackSchema = external_exports.object({
24254
+ version: external_exports.string().min(1).max(MAX_SHORT_TEXT_LENGTH),
24255
+ evidence_standard: external_exports.enum(["strict", "balanced", "permissive"]),
24256
+ duplicate_strategy: external_exports.enum(["human", "merge_when_clear"]),
24257
+ quality_regression_tolerance: external_exports.enum(["none", "minor"]),
24258
+ external_dependency_handling: external_exports.enum(["human", "verified_only"])
24259
+ }).strict();
24260
+ var verificationPolicyVerifierOutputSchema = verifierOutputSchema.extend({
24261
+ // Existing and external approvers remain compatible when this is absent.
24262
+ assessment: verifierAssessmentSchema.nullable().default(null)
24263
+ });
24264
+ var verificationAdminReviewActionSchema = adminReviewActionSchema.extend({
24265
+ reason_code: verificationReviewReasonCodeSchema.optional(),
24266
+ topology_action: skillTopologyActionSchema.optional()
24267
+ }).superRefine((value, ctx) => {
24268
+ if (value.reason_code === "other" && !value.note) {
24269
+ ctx.addIssue({
24270
+ code: external_exports.ZodIssueCode.custom,
24271
+ message: "A note is required when the review reason is other",
24272
+ path: ["note"]
24273
+ });
24274
+ }
24275
+ if (value.topology_action && value.action !== "accept" && value.action !== "merge" && value.action !== "fork") {
24276
+ ctx.addIssue({
24277
+ code: external_exports.ZodIssueCode.custom,
24278
+ message: "A topology route may be selected only when accepting, merging, or forking the reviewed evidence",
24279
+ path: ["topology_action"]
24280
+ });
24281
+ }
24282
+ });
24283
+ var BENIGN_REVIEW_FLAGS = /* @__PURE__ */ new Set([
24284
+ "public_skill_change_requires_human_review",
24285
+ "public_skill_change_duplicate_requires_human_review",
24286
+ "public_evidence_requires_human_review",
24287
+ "score_updates_discarded"
24288
+ ]);
24289
+ var SAFE_VERIFICATION_POLICY_FLAG_CATEGORIES = /* @__PURE__ */ new Set([
24290
+ ...BENIGN_REVIEW_FLAGS,
24291
+ "verification_unavailable",
24292
+ "prompt_injection",
24293
+ "sensitive_data",
24294
+ "malware_or_phishing",
24295
+ "spam",
24296
+ "other_safety_signal"
24297
+ ]);
24298
+
24299
+ // ../core/src/admin-review-decision-contract.ts
24300
+ var identifier = external_exports.string().min(1).max(MAX_SHORT_TEXT_LENGTH);
24301
+ var nullableIdentifier = identifier.nullable();
24302
+ var timestamp = external_exports.string().datetime();
24303
+ var targetTypes = external_exports.enum([
24304
+ "remembrance",
24305
+ "resource",
24306
+ "resource_review",
24307
+ "skill_idea",
24308
+ "suggestion"
24309
+ ]);
24310
+ var reviewDecisionPreviewSelectorSchema = external_exports.object({
24311
+ action: adminReviewActionValueSchema.nullable().default(null),
24312
+ topology_action: skillTopologyActionSchema.nullable().default(null),
24313
+ reason_code: verificationReviewReasonCodeSchema.nullable().default(null)
24314
+ }).strict().superRefine((value, ctx) => {
24315
+ if (value.topology_action && !["accept", "merge", "fork"].includes(value.action ?? "")) {
24316
+ ctx.addIssue({
24317
+ code: external_exports.ZodIssueCode.custom,
24318
+ path: ["topology_action"],
24319
+ message: "A topology route requires an accepting, merging, or forking preview."
24320
+ });
24321
+ }
24322
+ });
24323
+ var overlayRevisionSchema = external_exports.object({
24324
+ id: identifier,
24325
+ status: identifier,
24326
+ version: identifier,
24327
+ revision: external_exports.number().int().nonnegative().nullable(),
24328
+ updated_at: timestamp,
24329
+ private_content_ref: nullableIdentifier,
24330
+ private_catalog_ref: nullableIdentifier,
24331
+ private_catalog_revision_id: nullableIdentifier,
24332
+ private_catalog_version_id: nullableIdentifier,
24333
+ evolution_mode: nullableIdentifier
24334
+ }).strict();
24335
+ var skillRevisionSchema = external_exports.object({
24336
+ id: identifier,
24337
+ slug: identifier,
24338
+ status: identifier,
24339
+ visibility: identifier,
24340
+ version: identifier,
24341
+ current_version_id: nullableIdentifier,
24342
+ updated_at: timestamp
24343
+ }).strict();
24344
+ var versionRevisionSchema = external_exports.object({
24345
+ id: identifier,
24346
+ skill_id: identifier,
24347
+ version: identifier,
24348
+ status: identifier,
24349
+ updated_at: timestamp,
24350
+ private_content_ref: nullableIdentifier,
24351
+ private_content_revision_id: nullableIdentifier,
24352
+ private_content_org_id: nullableIdentifier
24353
+ }).strict();
24354
+ var consumedBaselineSchema = external_exports.object({
24355
+ kind: external_exports.enum(["bundled_seed", "skill_metadata", "public_sync"]),
24356
+ digest: identifier
24357
+ }).strict();
24358
+ var reviewDecisionSnapshotSchema = external_exports.object({
24359
+ schema_version: external_exports.literal("admin-review-decision-snapshot-v1"),
24360
+ org_id: identifier,
24361
+ public_skill_access_policy: external_exports.enum(["allow", "org_only"]),
24362
+ selected_route: reviewDecisionPreviewSelectorSchema,
24363
+ actor: external_exports.object({
24364
+ type: identifier,
24365
+ id: identifier,
24366
+ org_id: identifier,
24367
+ api_key_id: nullableIdentifier,
24368
+ token_id: nullableIdentifier
24369
+ }).strict(),
24370
+ job: external_exports.object({
24371
+ id: identifier,
24372
+ target_type: targetTypes,
24373
+ target_id: identifier,
24374
+ status: identifier,
24375
+ updated_at: timestamp,
24376
+ verifier_input_hash: nullableIdentifier,
24377
+ private_verification_artifact_id: nullableIdentifier,
24378
+ private_verification_result_digest: nullableIdentifier,
24379
+ private_semantic_context_ref: nullableIdentifier,
24380
+ private_context_ref: nullableIdentifier,
24381
+ private_context_revision_id: nullableIdentifier
24382
+ }).strict(),
24383
+ target: external_exports.object({
24384
+ id: identifier,
24385
+ updated_at: timestamp,
24386
+ payload_ref: nullableIdentifier,
24387
+ superseded_by_submission_id: nullableIdentifier,
24388
+ disposition_job_id: nullableIdentifier
24389
+ }).strict(),
24390
+ private_payload: external_exports.object({
24391
+ id: identifier,
24392
+ updated_at: timestamp,
24393
+ encryption_mode: identifier,
24394
+ inspection_status: nullableIdentifier,
24395
+ inspection_updated_at: timestamp.nullable(),
24396
+ inspection_projection_digest: nullableIdentifier,
24397
+ inspection_inspector_digest: nullableIdentifier
24398
+ }).strict().nullable(),
24399
+ protected_source_binding: external_exports.object({
24400
+ schema_version: external_exports.literal("protected-submission-source-v1"),
24401
+ source_type: external_exports.enum(["skill_idea", "suggestion", "remembrance"]),
24402
+ source_id: identifier,
24403
+ source_public_id: identifier,
24404
+ content_digest: identifier,
24405
+ inline_job_digest: identifier.nullable()
24406
+ }).strict().optional(),
24407
+ legacy_public_payload: external_exports.object({
24408
+ schema_version: external_exports.literal("legacy-public-remembrance-payload-v1"),
24409
+ id: identifier,
24410
+ updated_at: timestamp,
24411
+ source_public_id: identifier,
24412
+ raw_digest: identifier,
24413
+ redacted_digest: identifier,
24414
+ target_digest: identifier,
24415
+ inline_job_digest: identifier.nullable()
24416
+ }).strict().optional(),
24417
+ current_skill: external_exports.object({
24418
+ slug: identifier,
24419
+ overlay: overlayRevisionSchema.nullable(),
24420
+ version: versionRevisionSchema.nullable(),
24421
+ consumed_baseline: consumedBaselineSchema.nullable()
24422
+ }).strict().nullable(),
24423
+ source_skill: external_exports.object({
24424
+ skill: skillRevisionSchema,
24425
+ version: versionRevisionSchema.nullable(),
24426
+ consumed_baseline: consumedBaselineSchema.nullable()
24427
+ }).strict().nullable(),
24428
+ parent_skill: external_exports.object({
24429
+ skill: skillRevisionSchema,
24430
+ version: versionRevisionSchema,
24431
+ overlay: overlayRevisionSchema.nullable()
24432
+ }).strict().nullable()
24433
+ }).strict();
24434
+
24435
+ // ../core/src/review-workflows.ts
24436
+ var PRIVATE_SKILL_REVIEW_REPAIR_VERSION = "private-skill-review-repair-v1";
24437
+ var REVIEW_COMPARISON_TOOL_DESCRIPTION = "Organization admin:review only. Read complete exact source, parent and current skill versions already linked to one pending review job. Supply no action, route, organization or arbitrary skill ID. This does not select a route or grant decision authority: inspect the current review detail and its fresh snapshot separately before any decision. An unavailable, held, changed or foreign reference refuses the whole comparison; nothing is truncated or silently substituted.";
24438
+ var REVIEW_DRAFT_REPAIR_TOOL_DESCRIPTION = "Organization admin:review only; explicit delegated AI review authority, not routine learning permission. Create one complete protected replacement for a pending private skill draft using its current unselected review snapshot. Use a stable psrr_ plus 32 lowercase hex request_id; the transport sets Idempotency-Key to that exact ID. Exact replay returns the original replacement receipt; changed input under that ID conflicts. All normal secret inspection, protection, routing, verification and review checks apply. The local client requires inspection to leave the authored capture unchanged before sending. A local hold returns content-free recovery, never an alternate transport or a rewritten original. Creation preserves the original and never approves, rejects or supersedes it. Review the replacement normally, then close the original separately only after proving an accepted, tenant-readable active replacement skill. No public or cross-organization fallback.";
24439
+ var REVIEW_DRAFT_REPAIR_NEXT_INSTRUCTION = "Review the replacement normally. Keep the original open until an accepted, tenant-readable active skill version proves the replacement usable.";
24440
+ var reviewWorkflowJobIdSchema = external_exports.string().regex(/^job_[a-f0-9]{18}$/u);
24441
+ var privateSkillReviewRepairRequestIdSchema = external_exports.string().regex(/^psrr_[a-f0-9]{32}$/u);
24442
+ var referenceIdentifier = external_exports.string().min(1).max(MAX_SHORT_TEXT_LENGTH);
24443
+ var MAX_REVIEW_REFERENCE_BODY_BYTES = 1048576;
24444
+ var reviewReferenceSkillSchema = external_exports.object({
24445
+ skill_id: referenceIdentifier,
24446
+ slug: referenceIdentifier,
24447
+ version_id: referenceIdentifier,
24448
+ version: referenceIdentifier,
24449
+ skill_md: external_exports.string().min(1).max(MAX_REVIEW_REFERENCE_BODY_BYTES).refine((value) => new TextEncoder().encode(value).length <= MAX_REVIEW_REFERENCE_BODY_BYTES),
24450
+ content_hash: referenceIdentifier
24451
+ }).strict();
24452
+ var reviewReferenceComparisonSchema = external_exports.object({
24453
+ schema_version: external_exports.literal("review-reference-comparison-v1"),
24454
+ job_id: reviewWorkflowJobIdSchema,
24455
+ source_skill: reviewReferenceSkillSchema.nullable(),
24456
+ parent_skill: reviewReferenceSkillSchema.nullable(),
24457
+ current_skill: reviewReferenceSkillSchema.nullable()
24458
+ }).strict();
24459
+ function reviewWorkflowResponseBound(tool, body, request) {
24460
+ if (tool === "get_review_comparison") {
24461
+ const parsed2 = reviewReferenceComparisonSchema.safeParse(body);
24462
+ return parsed2.success && parsed2.data.job_id === request.job_id;
24463
+ }
24464
+ if (tool !== "repair_review_draft") return false;
24465
+ const parsed = external_exports.object({
24466
+ contract_version: external_exports.literal(PRIVATE_SKILL_REVIEW_REPAIR_VERSION),
24467
+ request_id: privateSkillReviewRepairRequestIdSchema,
24468
+ original_job_id: reviewWorkflowJobIdSchema,
24469
+ replacement: external_exports.object({
24470
+ public_id: external_exports.string().regex(/^ipub_[a-f0-9]{18}$/u),
24471
+ job_id: reviewWorkflowJobIdSchema,
24472
+ status: external_exports.enum(["submitted", "duplicate", "accepted", "rejected", "merged", "forked", "needs_review", "quarantined"]),
24473
+ visibility: external_exports.literal("organization_private")
24474
+ }).strict(),
24475
+ replayed: external_exports.boolean(),
24476
+ original_disposition: external_exports.literal("unchanged"),
24477
+ verification: external_exports.object({
24478
+ verification_job: external_exports.object({
24479
+ id: reviewWorkflowJobIdSchema,
24480
+ status: verificationJobStatusSchema,
24481
+ target_type: external_exports.literal("skill_idea"),
24482
+ target_id: external_exports.string().regex(/^idea_[a-f0-9]{18}$/u)
24483
+ }).strict(),
24484
+ queue: external_exports.object({ queued: external_exports.boolean(), queueJobId: reviewWorkflowJobIdSchema.nullable() }).strict()
24485
+ }).strict().nullable(),
24486
+ verification_status: verificationJobStatusSchema,
24487
+ dispatch_state: external_exports.enum(["existing_persisted_job", "queued", "mongo_poll_fallback"]),
24488
+ next_action: external_exports.object({
24489
+ method: external_exports.literal("GET"),
24490
+ path: external_exports.literal("/api/v1/admin/review-queue"),
24491
+ query: external_exports.object({ job_id: reviewWorkflowJobIdSchema, review_provenance: external_exports.literal("delegated_ai") }).strict(),
24492
+ instruction: external_exports.literal(REVIEW_DRAFT_REPAIR_NEXT_INSTRUCTION)
24493
+ }).strict()
24494
+ }).strict().safeParse(body);
24495
+ if (!parsed.success) return false;
24496
+ const receipt = parsed.data;
24497
+ if (receipt.request_id !== request.request_id || receipt.original_job_id !== request.job_id || receipt.replacement.job_id === receipt.original_job_id || receipt.next_action.query.job_id !== receipt.replacement.job_id) return false;
24498
+ if (receipt.replayed) return receipt.verification === null && receipt.dispatch_state === "existing_persisted_job";
24499
+ const verification = receipt.verification;
24500
+ return verification !== null && verification.verification_job.id === receipt.replacement.job_id && verification.verification_job.status === receipt.verification_status && (verification.queue.queued ? receipt.dispatch_state === "queued" && verification.queue.queueJobId === receipt.replacement.job_id : receipt.dispatch_state === "mongo_poll_fallback" && verification.queue.queueJobId === null);
24501
+ }
24502
+ var reviewReferenceComparisonRequestSchema = external_exports.object({
24503
+ job_id: reviewWorkflowJobIdSchema,
24504
+ review_provenance: external_exports.literal("delegated_ai").default("delegated_ai")
24505
+ }).strict();
24506
+ function checkJsonGraph(value, context, reservedKeys) {
24507
+ const pending = [{ value, depth: 0 }];
24508
+ const seen = /* @__PURE__ */ new Set();
24509
+ let nodes = 0;
24510
+ while (pending.length) {
24511
+ const entry = pending.pop();
24512
+ if (++nodes > 1e4 || entry.depth > 32) {
24513
+ context.addIssue({ code: "custom", message: "JSON structure exceeds the bounded review request limits." });
24514
+ return false;
24515
+ }
24516
+ const current = entry.value;
24517
+ if (current === null || typeof current === "string" || typeof current === "boolean" || typeof current === "number" && Number.isFinite(current)) continue;
24518
+ if (typeof current !== "object" || seen.has(current)) {
24519
+ context.addIssue({ code: "custom", message: "Review requests must contain finite, acyclic JSON values." });
24520
+ return false;
24521
+ }
24522
+ seen.add(current);
24523
+ for (const [key, child] of Object.entries(current)) {
24524
+ if (reservedKeys && key.startsWith("_remembrance_")) {
24525
+ context.addIssue({ code: "custom", message: "Server provenance fields are reserved." });
24526
+ return false;
24527
+ }
24528
+ pending.push({ value: child, depth: entry.depth + 1 });
24529
+ }
24530
+ }
24531
+ return true;
24532
+ }
24533
+ var reviewSnapshot = reviewDecisionSnapshotSchema.superRefine((value, context) => {
24534
+ if (!checkJsonGraph(value, context, false)) return;
24535
+ if (serializedJsonBytes(value) > MAX_JSON_FIELD_BYTES) {
24536
+ addSerializedJsonBytesIssue(context, MAX_JSON_FIELD_BYTES, "Review snapshot exceeds its JSON byte limit.");
24537
+ }
24538
+ });
24539
+ var repairCapture = privateSkillCaptureSchema.superRefine((capture2, context) => {
24540
+ checkJsonGraph(capture2.proposed_metadata, context, true);
24541
+ });
24542
+ var privateSkillReviewRepairRequest = external_exports.object({
24543
+ contract_version: external_exports.literal(PRIVATE_SKILL_REVIEW_REPAIR_VERSION),
24544
+ job_id: reviewWorkflowJobIdSchema,
24545
+ request_id: privateSkillReviewRepairRequestIdSchema,
24546
+ review_provenance: external_exports.literal("delegated_ai").default("delegated_ai"),
24547
+ expected_review_snapshot: reviewSnapshot,
24548
+ capture: repairCapture
24549
+ }).strict().superRefine((value, context) => {
24550
+ if (!checkJsonGraph(value, context, false)) return;
24551
+ if (serializedJsonBytes(value) > DEFAULT_MUTATION_BODY_LIMIT_BYTES) {
24552
+ addSerializedJsonBytesIssue(context, DEFAULT_MUTATION_BODY_LIMIT_BYTES, "Review repair exceeds its JSON byte limit; nothing is truncated.");
24553
+ }
24554
+ });
24555
+ var privateSkillReviewRepairRequestSchema = external_exports.preprocess((value, context) => {
24556
+ if (!checkJsonGraph(value, context, false)) return external_exports.NEVER;
24557
+ return value;
24558
+ }, privateSkillReviewRepairRequest);
24559
+
23898
24560
  // ../core/src/seed.ts
23899
24561
  var siteUrl = "https://remembrance.dev";
23900
24562
  var feedbackUrl = `${siteUrl}/api/v1/agent/remembrances`;
@@ -26403,123 +27065,6 @@ function invalidToolArgumentsMessage(error, schema) {
26403
27065
  ).join("; ")}${more > 0 ? ` (+${more} more)` : ""}.`;
26404
27066
  }
26405
27067
 
26406
- // ../core/src/verifier.ts
26407
- var duplicateCandidateSchema = external_exports.object({
26408
- type: external_exports.enum(["skill", "resource", "remembrance"]),
26409
- id: external_exports.string().min(1).max(MAX_SHORT_TEXT_LENGTH),
26410
- slug: external_exports.string().min(1).max(MAX_SHORT_TEXT_LENGTH).optional(),
26411
- similarity: external_exports.number().min(0).max(1)
26412
- });
26413
- var PROPOSED_METADATA_MAX_KEYWORDS = 25;
26414
- var PROPOSED_METADATA_MAX_TAGS = 15;
26415
- var PROPOSED_METADATA_MAX_DOMAINS = 8;
26416
- var PROPOSED_METADATA_MAX_TERM_LENGTH = 64;
26417
- var PROPOSED_METADATA_MAX_RATIONALE_LENGTH = 2e3;
26418
- var PROPOSED_METADATA_MAX_MODEL_LENGTH = MAX_SHORT_TEXT_LENGTH;
26419
- var LOW_VALUE_PROPOSED_METADATA_EXACT_TERMS = /* @__PURE__ */ new Set([
26420
- "agent",
26421
- "agents",
26422
- "agent-skill",
26423
- "agent-skills",
26424
- "backfill",
26425
- "candidate",
26426
- "candidates",
26427
- "leaderboard",
26428
- "import",
26429
- "imported",
26430
- "rank",
26431
- "ranking",
26432
- "skill",
26433
- "skills",
26434
- "skills-sh",
26435
- "skill-candidate",
26436
- "skill-candidates"
26437
- ]);
26438
- var LOW_VALUE_PROPOSED_METADATA_PATTERNS = [
26439
- /\bagent\s+skills?\b/i,
26440
- /\bagentspace\b/i,
26441
- /\bskill\s+candidates?\b/i,
26442
- /\bskills?[\s.-]*sh\b/i,
26443
- /\bleaderboard\s+rank\b/i,
26444
- /\bnpx\s+skills\s+add\b/i
26445
- ];
26446
- function sanitizeProposedMetadataTerm(value) {
26447
- if (typeof value !== "string") {
26448
- return null;
26449
- }
26450
- const trimmed = value.trim().slice(0, PROPOSED_METADATA_MAX_TERM_LENGTH);
26451
- if (!trimmed) {
26452
- return null;
26453
- }
26454
- const normalized2 = trimmed.toLowerCase().replace(/[_/.]+/g, "-").replace(/\s+/g, " ").replace(/^-+|-+$/g, "");
26455
- if (LOW_VALUE_PROPOSED_METADATA_EXACT_TERMS.has(normalized2)) {
26456
- return null;
26457
- }
26458
- if (LOW_VALUE_PROPOSED_METADATA_PATTERNS.some(
26459
- (pattern) => pattern.test(trimmed)
26460
- )) {
26461
- return null;
26462
- }
26463
- return trimmed;
26464
- }
26465
- var proposedMetadataTermArraySchema = (max) => external_exports.preprocess(
26466
- (value) => {
26467
- if (!Array.isArray(value)) {
26468
- return value;
26469
- }
26470
- const seen = /* @__PURE__ */ new Set();
26471
- const terms = [];
26472
- for (const entry of value) {
26473
- const trimmed = sanitizeProposedMetadataTerm(entry);
26474
- if (!trimmed) {
26475
- continue;
26476
- }
26477
- const key = trimmed.toLowerCase();
26478
- if (seen.has(key)) {
26479
- continue;
26480
- }
26481
- seen.add(key);
26482
- terms.push(trimmed);
26483
- }
26484
- return terms.slice(0, max);
26485
- },
26486
- external_exports.array(external_exports.string().min(1).max(PROPOSED_METADATA_MAX_TERM_LENGTH)).max(max)
26487
- );
26488
- var proposedSkillMetadataSchema = external_exports.object({
26489
- keywords: proposedMetadataTermArraySchema(PROPOSED_METADATA_MAX_KEYWORDS),
26490
- tags: proposedMetadataTermArraySchema(PROPOSED_METADATA_MAX_TAGS),
26491
- domains: proposedMetadataTermArraySchema(PROPOSED_METADATA_MAX_DOMAINS),
26492
- rationale: external_exports.string().trim().min(1).max(PROPOSED_METADATA_MAX_RATIONALE_LENGTH),
26493
- model: external_exports.string().trim().min(1).max(PROPOSED_METADATA_MAX_MODEL_LENGTH)
26494
- });
26495
- var verifierOutputSchema = external_exports.object({
26496
- recommended_action: verifierActionSchema,
26497
- confidence: external_exports.number().min(0).max(1),
26498
- duplicate_candidates: external_exports.array(duplicateCandidateSchema).max(20).default([]),
26499
- safety_flags: external_exports.array(external_exports.string().min(1).max(MAX_SHORT_TEXT_LENGTH)).max(40).default([]),
26500
- summary: external_exports.string().min(1).max(MAX_LONG_TEXT_LENGTH),
26501
- proposed_patch: external_exports.string().max(MAX_LONG_TEXT_LENGTH).nullable().default(null),
26502
- score_updates: external_exports.record(external_exports.unknown()).superRefine((value, ctx) => {
26503
- if (serializedJsonBytes(value) > MAX_JSON_FIELD_BYTES) {
26504
- addSerializedJsonBytesIssue(
26505
- ctx,
26506
- MAX_JSON_FIELD_BYTES,
26507
- `score_updates must be ${MAX_JSON_FIELD_BYTES} bytes or smaller`
26508
- );
26509
- }
26510
- }).nullable().default(null),
26511
- // Advisory retrieval enrichment (see proposedSkillMetadataSchema). Optional
26512
- // and defaults to null so existing verifier outputs remain valid.
26513
- proposed_metadata: proposedSkillMetadataSchema.nullable().default(null),
26514
- // Advisory evidence-routing assessment. Deterministic routing and the
26515
- // existing safety/review gates decide whether it can create a candidate.
26516
- // Topology is advisory and newer than the base verifier contract. A legacy
26517
- // or malformed topology fragment must not discard an otherwise valid safety
26518
- // verdict; it degrades to null, which the topology router retains as
26519
- // evidence-only rather than allowing an unclassified mutation.
26520
- topology_assessment: skillTopologyAssessmentSchema.nullable().default(null).catch(null)
26521
- });
26522
-
26523
27068
  // src/private-skill-protection-recovery.ts
26524
27069
  var PRIVATE_SKILL_CONFIGURATION_REFUSAL = "private_admission_configuration_error";
26525
27070
  var PRIVATE_SKILL_PERMANENT_SERVICE_REFUSAL = "private_skill_permanent_service_refusal";
@@ -28303,6 +28848,19 @@ var remembranceToolSchema = remembrancePayloadSchema.extend({
28303
28848
  )
28304
28849
  });
28305
28850
  var declaredToolDefinitions = [
28851
+ readTool(
28852
+ "get_review_comparison",
28853
+ REVIEW_COMPARISON_TOOL_DESCRIPTION,
28854
+ "/api/v1/admin/review-queue/comparison",
28855
+ reviewReferenceComparisonRequestSchema,
28856
+ "GET"
28857
+ ),
28858
+ contentWriteTool(
28859
+ "repair_review_draft",
28860
+ REVIEW_DRAFT_REPAIR_TOOL_DESCRIPTION,
28861
+ "/api/v1/admin/review-queue/repairs",
28862
+ privateSkillReviewRepairRequestSchema
28863
+ ),
28306
28864
  readTool(
28307
28865
  "get_connection_status",
28308
28866
  REMEMBRANCE_CONNECTION_STATUS_TOOL_DESCRIPTION,
@@ -29052,13 +29610,13 @@ function validateServedToolDefinition(definition) {
29052
29610
  canonicalToolDefinitionForDispatch(definition);
29053
29611
  }
29054
29612
  function annotationsForTool(name) {
29055
- const readOnly = name === "list_skills" || name === "get_effective_preferences" || name === "get_value_proof" || name === "run_connection_doctor" || name === "get_private_lesson_policy" || name === "inspect_private_lesson_outbox" || name === "inspect_private_learning_outbox" || name === "get_pending_action_payload";
29613
+ const readOnly = name === "get_review_comparison" || name === "list_skills" || name === "get_effective_preferences" || name === "get_value_proof" || name === "run_connection_doctor" || name === "get_private_lesson_policy" || name === "inspect_private_lesson_outbox" || name === "inspect_private_learning_outbox" || name === "get_pending_action_payload";
29056
29614
  const openWorld = name === "submit_feedback" || name === "submit_remembrance" || name === "propose_skill_idea" || name === "submit_suggestion" || name === "submit_resource" || name === "submit_resource_review";
29057
29615
  return {
29058
29616
  readOnlyHint: readOnly,
29059
29617
  openWorldHint: openWorld,
29060
29618
  destructiveHint: name === "delete_private_lesson_candidate" || name === "delete_private_skill_candidate",
29061
- ...readOnly || name === "queue_private_skill_import" || name === "acknowledge_feedback_denial" || name === "decline_pending_action" || name === "submit_private_lesson_candidate" || name === "retry_private_lesson_candidate" || name === "submit_private_skill_candidate" || name === "retry_private_skill_candidate" ? { idempotentHint: true } : {}
29619
+ ...readOnly || name === "queue_private_skill_import" || name === "acknowledge_feedback_denial" || name === "decline_pending_action" || name === "submit_private_lesson_candidate" || name === "retry_private_lesson_candidate" || name === "submit_private_skill_candidate" || name === "retry_private_skill_candidate" || name === "repair_review_draft" ? { idempotentHint: true } : {}
29062
29620
  };
29063
29621
  }
29064
29622
  var ROUTINE_PRIVATE_CAPTURE_REPLACED_TOOLS = /* @__PURE__ */ new Set([
@@ -45344,7 +45902,7 @@ var require_timestamp = __commonJS({
45344
45902
  resolve: (str) => parseSexagesimal(str, false),
45345
45903
  stringify: stringifySexagesimal
45346
45904
  };
45347
- var timestamp = {
45905
+ var timestamp2 = {
45348
45906
  identify: (value) => value instanceof Date,
45349
45907
  default: true,
45350
45908
  tag: "tag:yaml.org,2002:timestamp",
@@ -45353,7 +45911,7 @@ var require_timestamp = __commonJS({
45353
45911
  // assumed to be 00:00:00Z (start of day, UTC).
45354
45912
  test: RegExp("^([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})(?:(?:t|T|[ \\t]+)([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2}(\\.[0-9]+)?)(?:[ \\t]*(Z|[-+][012]?[0-9](?::[0-9]{2})?))?)?$"),
45355
45913
  resolve(str) {
45356
- const match = str.match(timestamp.test);
45914
+ const match = str.match(timestamp2.test);
45357
45915
  if (!match)
45358
45916
  throw new Error("!!timestamp expects a date, starting with yyyy-mm-dd");
45359
45917
  const [, year, month, day, hour, minute, second] = match.map(Number);
@@ -45372,7 +45930,7 @@ var require_timestamp = __commonJS({
45372
45930
  };
45373
45931
  exports.floatTime = floatTime;
45374
45932
  exports.intTime = intTime;
45375
- exports.timestamp = timestamp;
45933
+ exports.timestamp = timestamp2;
45376
45934
  }
45377
45935
  });
45378
45936
  var require_schema3 = __commonJS({
@@ -45390,7 +45948,7 @@ var require_schema3 = __commonJS({
45390
45948
  var omap = require_omap();
45391
45949
  var pairs = require_pairs();
45392
45950
  var set = require_set();
45393
- var timestamp = require_timestamp();
45951
+ var timestamp2 = require_timestamp();
45394
45952
  var schema = [
45395
45953
  map.map,
45396
45954
  seq.seq,
@@ -45410,9 +45968,9 @@ var require_schema3 = __commonJS({
45410
45968
  omap.omap,
45411
45969
  pairs.pairs,
45412
45970
  set.set,
45413
- timestamp.intTime,
45414
- timestamp.floatTime,
45415
- timestamp.timestamp
45971
+ timestamp2.intTime,
45972
+ timestamp2.floatTime,
45973
+ timestamp2.timestamp
45416
45974
  ];
45417
45975
  exports.schema = schema;
45418
45976
  }
@@ -45435,7 +45993,7 @@ var require_tags = __commonJS({
45435
45993
  var pairs = require_pairs();
45436
45994
  var schema$2 = require_schema3();
45437
45995
  var set = require_set();
45438
- var timestamp = require_timestamp();
45996
+ var timestamp2 = require_timestamp();
45439
45997
  var schemas = /* @__PURE__ */ new Map([
45440
45998
  ["core", schema.schema],
45441
45999
  ["failsafe", [map.map, seq.seq, string.string]],
@@ -45449,11 +46007,11 @@ var require_tags = __commonJS({
45449
46007
  float: float.float,
45450
46008
  floatExp: float.floatExp,
45451
46009
  floatNaN: float.floatNaN,
45452
- floatTime: timestamp.floatTime,
46010
+ floatTime: timestamp2.floatTime,
45453
46011
  int: int.int,
45454
46012
  intHex: int.intHex,
45455
46013
  intOct: int.intOct,
45456
- intTime: timestamp.intTime,
46014
+ intTime: timestamp2.intTime,
45457
46015
  map: map.map,
45458
46016
  merge: merge.merge,
45459
46017
  null: _null.nullTag,
@@ -45461,7 +46019,7 @@ var require_tags = __commonJS({
45461
46019
  pairs: pairs.pairs,
45462
46020
  seq: seq.seq,
45463
46021
  set: set.set,
45464
- timestamp: timestamp.timestamp
46022
+ timestamp: timestamp2.timestamp
45465
46023
  };
45466
46024
  var coreKnownTags = {
45467
46025
  "tag:yaml.org,2002:binary": binary.binary,
@@ -45469,7 +46027,7 @@ var require_tags = __commonJS({
45469
46027
  "tag:yaml.org,2002:omap": omap.omap,
45470
46028
  "tag:yaml.org,2002:pairs": pairs.pairs,
45471
46029
  "tag:yaml.org,2002:set": set.set,
45472
- "tag:yaml.org,2002:timestamp": timestamp.timestamp
46030
+ "tag:yaml.org,2002:timestamp": timestamp2.timestamp
45473
46031
  };
45474
46032
  function getTags(customTags, schemaName, addMergeTag) {
45475
46033
  const schemaTags = schemas.get(schemaName);
@@ -66230,9 +66788,68 @@ function createLocalPersistenceCapabilities(dependencies = {}) {
66230
66788
  });
66231
66789
  }
66232
66790
 
66233
- // src/network-transport.ts
66791
+ // ../core/src/redaction-request-budget.ts
66234
66792
  import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
66235
- var networkExecutionAuthority = new AsyncLocalStorage2();
66793
+ var requests = new AsyncLocalStorage2();
66794
+ trackRedactionRequests(() => requests.getStore());
66795
+ function withRedactionRequestBudget(run) {
66796
+ return requests.getStore() ? run() : requests.run({}, run);
66797
+ }
66798
+
66799
+ // src/review-repair-inspection.ts
66800
+ function inspectReviewRepairForTransport(value) {
66801
+ const request = privateSkillReviewRepairRequestSchema.parse(value);
66802
+ const inspection = inspectPrivateSkillCandidate(request.capture);
66803
+ if (inspection.status === "complete" && inspection.decision.outcome === "unchanged") {
66804
+ const values = [];
66805
+ const pending = [request.expected_review_snapshot];
66806
+ while (pending.length) {
66807
+ const entry = pending.pop();
66808
+ if (entry !== null && typeof entry === "object") pending.push(...Object.values(entry));
66809
+ else values.push(entry);
66810
+ }
66811
+ try {
66812
+ if (canonicalJson(withRedactionRequestBudget(() => redactPayload(values))) !== canonicalJson(values)) {
66813
+ return { ready: false, refusal: {
66814
+ status: "held",
66815
+ code: "review_repair_wire_inspection_hold",
66816
+ delivery: "not_delivered",
66817
+ candidate_sent: false,
66818
+ candidate_stored: false,
66819
+ original_disposition: "unchanged",
66820
+ next_action: "Retain the original. Read a fresh normal content-free review snapshot; do not send suspected secrets, rewrite the held request, or use another transport."
66821
+ } };
66822
+ }
66823
+ } catch {
66824
+ return { ready: false, refusal: {
66825
+ status: "held",
66826
+ code: "review_repair_wire_inspection_failure",
66827
+ delivery: "not_delivered",
66828
+ candidate_sent: false,
66829
+ candidate_stored: false,
66830
+ original_disposition: "unchanged",
66831
+ next_action: "Retain the original. Correct the local inspection failure before retrying; do not reformulate around an operational failure."
66832
+ } };
66833
+ }
66834
+ return { ready: true, request };
66835
+ }
66836
+ const reason = inspection.status === "complete" ? "authored_capture_changed_by_inspection" : inspection.status === "incomplete" ? inspection.reason : inspection.error_code;
66837
+ return { ready: false, refusal: {
66838
+ status: "held",
66839
+ code: "review_repair_local_inspection_hold",
66840
+ delivery: "not_delivered",
66841
+ candidate_sent: false,
66842
+ candidate_stored: false,
66843
+ original_disposition: "unchanged",
66844
+ reason_code: reason,
66845
+ inspection: inspection.decision,
66846
+ next_action: inspection.status === "invalid" ? "Retain the original. Correct the local inspection failure before retrying the same unchanged request; do not reformulate around an operational failure or use another transport." : "Retain the original. Prepare a complete safe replacement from authenticated task context and inspect it again. Do not recover suspected secrets, invent missing instructions, or bypass a host denial."
66847
+ } };
66848
+ }
66849
+
66850
+ // src/network-transport.ts
66851
+ import { AsyncLocalStorage as AsyncLocalStorage3 } from "node:async_hooks";
66852
+ var networkExecutionAuthority = new AsyncLocalStorage3();
66236
66853
  var guardedFetches = /* @__PURE__ */ new WeakSet();
66237
66854
  function installRuntimeFetchGuard() {
66238
66855
  const delegate = globalThis.fetch;
@@ -67945,7 +68562,7 @@ function inspectPrivateLearningOutbox2(input, dependencies = {}) {
67945
68562
  }
67946
68563
  var MAX_REMOTE_RESPONSE_BYTES = 4 * 1024 * 1024;
67947
68564
  var DOCTOR_PROBE_TIMEOUT_MS = 7500;
67948
- var SERVER_VERSION = true ? "0.2.4" : "0.0.0-dev";
68565
+ var SERVER_VERSION = true ? "0.2.5" : "0.0.0-dev";
67949
68566
  var tools2 = toolDefinitions;
67950
68567
  var doctorCliRequested = process.argv[2] === "doctor";
67951
68568
  var nativeContextCliRequested = process.argv[2] === "--native-context";
@@ -68304,6 +68921,11 @@ async function callToolWithinExecution(runtimeDefinition, rawArguments, dependen
68304
68921
  );
68305
68922
  }
68306
68923
  async function callNetworkToolWithinExecution(definition, payload, access, transport) {
68924
+ if (definition.name === "repair_review_draft") {
68925
+ const inspection = inspectReviewRepairForTransport(payload);
68926
+ if (!inspection.ready) return inspection.refusal;
68927
+ payload = inspection.request;
68928
+ }
68307
68929
  if (definition.local === "bootstrap_agent_identity") {
68308
68930
  return bootstrapAgentIdentity(payload, access, transport);
68309
68931
  }
@@ -68365,7 +68987,11 @@ async function callNetworkToolWithinExecution(definition, payload, access, trans
68365
68987
  access.credential.source,
68366
68988
  access.configuration.issue
68367
68989
  );
68368
- const result = definition.name === "get_connection_status" && localConfigurationError ? { ok: false, status: 0, error: localConfigurationError } : await callRemembrance(definition, payload, { access, transport });
68990
+ const result = definition.name === "get_connection_status" && localConfigurationError ? { ok: false, status: 0, error: localConfigurationError } : await callRemembrance(definition, payload, {
68991
+ access,
68992
+ transport,
68993
+ ...["get_review_comparison", "repair_review_draft"].includes(definition.name) ? { redirect: "manual" } : {}
68994
+ });
68369
68995
  if (definition.name === "get_connection_status") {
68370
68996
  const status = localConnectionStatus(result, {
68371
68997
  apiBase: access.configuration.baseUrl,
@@ -68580,11 +69206,11 @@ function retryAfterDelayMs(value, nowMs = Date.now()) {
68580
69206
  Number(trimmed) * 1e3
68581
69207
  );
68582
69208
  }
68583
- const timestamp = Date.parse(trimmed);
68584
- if (!Number.isFinite(timestamp)) return null;
69209
+ const timestamp2 = Date.parse(trimmed);
69210
+ if (!Number.isFinite(timestamp2)) return null;
68585
69211
  return Math.min(
68586
69212
  PRIVATE_LEARNING_MAX_RETRY_DELAY_MS,
68587
- Math.max(0, timestamp - nowMs)
69213
+ Math.max(0, timestamp2 - nowMs)
68588
69214
  );
68589
69215
  }
68590
69216
  function privateLessonDestinationForAccess(access) {
@@ -69786,8 +70412,8 @@ function privateLessonRetryGate(draft) {
69786
70412
  }
69787
70413
  function retryDue(nextRetryAt) {
69788
70414
  if (!nextRetryAt) return true;
69789
- const timestamp = Date.parse(nextRetryAt);
69790
- return !Number.isFinite(timestamp) || timestamp <= Date.now();
70415
+ const timestamp2 = Date.parse(nextRetryAt);
70416
+ return !Number.isFinite(timestamp2) || timestamp2 <= Date.now();
69791
70417
  }
69792
70418
  async function submitPrivateLessonHoldTelemetry(initialDraft, access, allowPolicyRefresh, transport) {
69793
70419
  let draft = initialDraft;
@@ -70949,6 +71575,9 @@ async function callRemembrance(definition, rawArguments, options) {
70949
71575
  throw new McpPublicError(configurationError);
70950
71576
  }
70951
71577
  const apiKey = credential.apiKey;
71578
+ if (definition.name === "repair_review_draft") {
71579
+ headers["Idempotency-Key"] = String(parsed.request_id);
71580
+ }
70952
71581
  if (apiKey) {
70953
71582
  headers["x-remembrance-api-key"] = apiKey;
70954
71583
  }
@@ -70992,6 +71621,7 @@ async function callRemembrance(definition, rawArguments, options) {
70992
71621
  let response;
70993
71622
  let body;
70994
71623
  let redirectRejected = false;
71624
+ let reviewWorkflowStatus = 0;
70995
71625
  try {
70996
71626
  const execute = async () => {
70997
71627
  const nextResponse = await transport.request(
@@ -71004,6 +71634,7 @@ async function callRemembrance(definition, rawArguments, options) {
71004
71634
  signal: controller.signal
71005
71635
  }
71006
71636
  );
71637
+ reviewWorkflowStatus = nextResponse.status;
71007
71638
  if (options.redirect === "manual" && (nextResponse.redirected || nextResponse.status >= 300 && nextResponse.status < 400)) {
71008
71639
  void nextResponse.body?.cancel().catch(() => void 0);
71009
71640
  return {
@@ -71018,7 +71649,13 @@ async function callRemembrance(definition, rawArguments, options) {
71018
71649
  let nextBody;
71019
71650
  try {
71020
71651
  nextBody = await readBoundedResponseJson(nextResponse);
71652
+ if (nextResponse.ok && ["get_review_comparison", "repair_review_draft"].includes(definition.name) && !reviewWorkflowResponseBound(definition.name, nextBody, parsed)) {
71653
+ throw new McpPublicError("The review response did not match this request.");
71654
+ }
71021
71655
  } catch {
71656
+ if (["get_review_comparison", "repair_review_draft"].includes(definition.name)) {
71657
+ throw new McpPublicError("The review response is unreadable or unbound.");
71658
+ }
71022
71659
  if (controller.signal.aborted) {
71023
71660
  throw new DOMException("Request timed out", "AbortError");
71024
71661
  }
@@ -71057,6 +71694,26 @@ async function callRemembrance(definition, rawArguments, options) {
71057
71694
  }
71058
71695
  }
71059
71696
  } catch (error) {
71697
+ if (definition.name === "repair_review_draft") {
71698
+ return {
71699
+ ok: false,
71700
+ status: reviewWorkflowStatus,
71701
+ error: "The review repair outcome is unknown. Reconcile by repeating only this exact request with the same request_id; do not create another replacement.",
71702
+ body: {
71703
+ code: "review_repair_outcome_unknown",
71704
+ delivery: "unknown",
71705
+ request_id: parsed.request_id,
71706
+ next_action: "Keep the original and repeat only the identical request_id, capture and snapshot to recover the committed receipt. Do not change input or use a new ID while this outcome is unknown."
71707
+ }
71708
+ };
71709
+ }
71710
+ if (definition.name === "get_review_comparison") {
71711
+ return {
71712
+ ok: false,
71713
+ status: reviewWorkflowStatus,
71714
+ body: { code: "review_comparison_unreadable", error: "No valid comparison was returned for this exact review job. Keep it unchanged and read its current references again." }
71715
+ };
71716
+ }
71060
71717
  return {
71061
71718
  ok: false,
71062
71719
  status: 0,
@@ -71065,6 +71722,25 @@ async function callRemembrance(definition, rawArguments, options) {
71065
71722
  } finally {
71066
71723
  clearTimeout(timeout);
71067
71724
  }
71725
+ if (["get_review_comparison", "repair_review_draft"].includes(definition.name) && (!response.ok || redirectRejected)) {
71726
+ const failure = remembranceMcpToolFailure({
71727
+ ok: false,
71728
+ status: response.status,
71729
+ body,
71730
+ retry_after: response.headers.get("retry-after"),
71731
+ redirect_rejected: redirectRejected
71732
+ }, definition.name);
71733
+ body = {
71734
+ error: failure?.message ?? "The review operation failed.",
71735
+ code: failure?.code ?? "review_workflow_failed",
71736
+ retryable: failure?.retryable ?? false,
71737
+ delivery: failure?.delivery ?? "unknown",
71738
+ ...definition.name === "repair_review_draft" ? {
71739
+ request_id: parsed.request_id,
71740
+ next_action: "If delivery is unknown, repeat only the identical request_id, capture and snapshot to reconcile it. Never create a new replacement for an unresolved write."
71741
+ } : {}
71742
+ };
71743
+ }
71068
71744
  const verifiedBody = definition.name === "get_value_proof" && response.ok ? await verifySignedValueProofWithKeyRefresh(
71069
71745
  body,
71070
71746
  (forceRefresh) => fetchValueProofKeySet(forceRefresh, access, transport)
@@ -71583,7 +72259,7 @@ function endpointFor(definition, payload) {
71583
72259
  const query2 = privateLessonPolicyQuery(payload);
71584
72260
  return query2 ? `${endpoint}?${query2}` : endpoint;
71585
72261
  }
71586
- const queryKeys = definition.name === "list_skills" ? ["q", "slug", "cursor", "limit"] : ["query_id", "result_id"];
72262
+ const queryKeys = definition.name === "list_skills" ? ["q", "slug", "cursor", "limit"] : definition.name === "get_review_comparison" ? ["job_id", "review_provenance"] : ["query_id", "result_id"];
71587
72263
  for (const key of queryKeys) {
71588
72264
  const value = payload[key];
71589
72265
  if (typeof value === "string" && value || typeof value === "number" && Number.isFinite(value)) {