@remembrance-ai/opencode-plugin 0.2.2 → 0.2.4

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.
@@ -738,8 +738,8 @@ var ZodIssueCode = util.arrayToEnum([
738
738
  "not_finite"
739
739
  ]);
740
740
  var quotelessJson = (obj) => {
741
- const json = JSON.stringify(obj, null, 2);
742
- return json.replace(/"([^"]+)":/g, "$1:");
741
+ const json2 = JSON.stringify(obj, null, 2);
742
+ return json2.replace(/"([^"]+)":/g, "$1:");
743
743
  };
744
744
  var ZodError = class _ZodError extends Error {
745
745
  get errors() {
@@ -12058,7 +12058,7 @@ function findIndependentlyEncodedPrivateKeyFrameProof(tokens, metrics) {
12058
12058
  const recoverableLength = recoverableProof?.length ?? null;
12059
12059
  const length = completeLength ?? recoverableLength;
12060
12060
  if (length === null) continue;
12061
- const continuation = recoverableProof?.basis === "factors" ? independentlyEncodedRsaCrtContinuationRanges(
12061
+ const continuation2 = recoverableProof?.basis === "factors" ? independentlyEncodedRsaCrtContinuationRanges(
12062
12062
  tokens,
12063
12063
  frames,
12064
12064
  bytes,
@@ -12073,10 +12073,10 @@ function findIndependentlyEncodedPrivateKeyFrameProof(tokens, metrics) {
12073
12073
  bytes,
12074
12074
  encoding,
12075
12075
  frames,
12076
- continuationRanges: continuation.ranges,
12076
+ continuationRanges: continuation2.ranges,
12077
12077
  start: frame.start,
12078
12078
  end: frame.start + length,
12079
- ambiguous: continuation.ambiguous,
12079
+ ambiguous: continuation2.ambiguous,
12080
12080
  complete: completeLength !== null,
12081
12081
  factorBacked: recoverableProof?.basis === "factors",
12082
12082
  recoverable: completeLength === null
@@ -14713,19 +14713,19 @@ function recordCryptographicMatchedTerminalOwnership(tokens, assessment, metrics
14713
14713
  }
14714
14714
  function matchedTerminalSourcePieceRanges(tokens, assessment) {
14715
14715
  const sourceStart = assessment.sourcePieceRanges[0].start;
14716
- const continuation = assessment.rsaCrtContinuations.filter(
14716
+ const continuation2 = assessment.rsaCrtContinuations.filter(
14717
14717
  (candidate) => candidate.start === sourceStart && candidate.status === "match" && candidate.mismatchAt === null
14718
14718
  ).sort((left, right) => right.matchedEnd - left.matchedEnd)[0];
14719
- if (!continuation) return [];
14719
+ if (!continuation2) return [];
14720
14720
  const ranges = [];
14721
14721
  for (const [pieceIndex, token] of tokens.entries()) {
14722
14722
  const source = assessment.sourcePieceRanges[pieceIndex];
14723
- if (source.end <= continuation.matchedEnd) {
14723
+ if (source.end <= continuation2.matchedEnd) {
14724
14724
  ranges.push({ pieceIndex, start: 0, end: token.value.length });
14725
14725
  continue;
14726
14726
  }
14727
- if (source.start >= continuation.matchedEnd) continue;
14728
- const matchedLength = continuation.matchedEnd - source.start;
14727
+ if (source.start >= continuation2.matchedEnd) continue;
14728
+ const matchedLength = continuation2.matchedEnd - source.start;
14729
14729
  if (matchedLength > 0 && /^=+$/u.test(token.value.slice(matchedLength))) {
14730
14730
  ranges.push({ pieceIndex, start: 0, end: token.value.length });
14731
14731
  }
@@ -15604,13 +15604,13 @@ function assessEncodedPieceSequence(pieces, textOf, metrics, requireBoundedRecov
15604
15604
  (completeRange) => completeRange.start < end && start < completeRange.end
15605
15605
  );
15606
15606
  const authenticatedFactorRange = scanned.rsaCrtContinuations.some(
15607
- (continuation) => {
15608
- const ownedEnd = Math.min(end, continuation.matchedEnd);
15609
- if (continuation.start >= ownedEnd || start >= continuation.matchedEnd) {
15607
+ (continuation2) => {
15608
+ const ownedEnd = Math.min(end, continuation2.matchedEnd);
15609
+ if (continuation2.start >= ownedEnd || start >= continuation2.matchedEnd) {
15610
15610
  return false;
15611
15611
  }
15612
- if (position.start < continuation.factorEnd) return true;
15613
- return ownedEnd - Math.max(position.start, continuation.factorEnd) >= continuation.minimumAuthenticatedCharacters;
15612
+ if (position.start < continuation2.factorEnd) return true;
15613
+ return ownedEnd - Math.max(position.start, continuation2.factorEnd) >= continuation2.minimumAuthenticatedCharacters;
15614
15614
  }
15615
15615
  );
15616
15616
  if (!authoritativeComplete2 && !authenticatedFactorRange) continue;
@@ -15774,7 +15774,7 @@ function encodedAssessmentHasMatchedCrtContinuationThroughEnd(assessment, pieceC
15774
15774
  const sourceStart = assessment.sourcePieceRanges[0].start;
15775
15775
  const sourceEnd = assessment.sourcePieceRanges[pieceCount - 1].end;
15776
15776
  return assessment.rsaCrtContinuations.some(
15777
- (continuation) => continuation.start === sourceStart && continuation.status === "match" && continuation.mismatchAt === null && continuation.matchedEnd <= sourceEnd && sourceEnd - continuation.matchedEnd <= MAX_ADJACENT_ENCODED_PREFIX_CHARACTERS
15777
+ (continuation2) => continuation2.start === sourceStart && continuation2.status === "match" && continuation2.mismatchAt === null && continuation2.matchedEnd <= sourceEnd && sourceEnd - continuation2.matchedEnd <= MAX_ADJACENT_ENCODED_PREFIX_CHARACTERS
15778
15778
  );
15779
15779
  }
15780
15780
  function encodedAssessmentOwnsEverySourcePiece(assessment, pieceCount) {
@@ -15796,7 +15796,7 @@ function encodedAssessmentOwnsEverySourcePiece(assessment, pieceCount) {
15796
15796
  }
15797
15797
  const terminalSource = assessment.sourcePieceRanges[terminalIndex];
15798
15798
  return assessment.rsaCrtContinuations.some(
15799
- (continuation) => continuation.status === "match" && continuation.mismatchAt === null && continuation.factorEnd < terminalSource.end && continuation.matchedEnd === terminalSource.end
15799
+ (continuation2) => continuation2.status === "match" && continuation2.mismatchAt === null && continuation2.factorEnd < terminalSource.end && continuation2.matchedEnd === terminalSource.end
15800
15800
  );
15801
15801
  }
15802
15802
  function encodedAssessmentAllowsFactorOmission(assessment, pieceIndex) {
@@ -15807,11 +15807,11 @@ function encodedAssessmentAllowsFactorOmission(assessment, pieceIndex) {
15807
15807
  )) {
15808
15808
  return false;
15809
15809
  }
15810
- return assessment.rsaCrtContinuations.every((continuation) => {
15811
- if (piece.end <= continuation.start || piece.start >= continuation.end) {
15810
+ return assessment.rsaCrtContinuations.every((continuation2) => {
15811
+ if (piece.end <= continuation2.start || piece.start >= continuation2.end) {
15812
15812
  return true;
15813
15813
  }
15814
- return continuation.mismatchAt !== null && continuation.mismatchAt < piece.end && continuation.matchedEnd <= piece.start;
15814
+ return continuation2.mismatchAt !== null && continuation2.mismatchAt < piece.end && continuation2.matchedEnd <= piece.start;
15815
15815
  });
15816
15816
  }
15817
15817
  function encodedAssessmentAllowsRecognizableFactorOmission(assessment, pieceIndex) {
@@ -15819,11 +15819,11 @@ function encodedAssessmentAllowsRecognizableFactorOmission(assessment, pieceInde
15819
15819
  return true;
15820
15820
  }
15821
15821
  const piece = assessment.sourcePieceRanges[pieceIndex];
15822
- return assessment.rsaCrtContinuations.every((continuation) => {
15823
- if (piece.end <= continuation.start || piece.start >= continuation.end) {
15822
+ return assessment.rsaCrtContinuations.every((continuation2) => {
15823
+ if (piece.end <= continuation2.start || piece.start >= continuation2.end) {
15824
15824
  return true;
15825
15825
  }
15826
- return continuation.factorEnd <= piece.start && (continuation.matchedEnd <= piece.start || continuation.mismatchAt !== null && continuation.mismatchAt < piece.end);
15826
+ return continuation2.factorEnd <= piece.start && (continuation2.matchedEnd <= piece.start || continuation2.mismatchAt !== null && continuation2.mismatchAt < piece.end);
15827
15827
  });
15828
15828
  }
15829
15829
  function encodedAssessmentStartsAtSource(assessment) {
@@ -17368,7 +17368,7 @@ function scanEncodedPrivateKeyRanges(value, metrics, allowAdjacentPrefix = true,
17368
17368
  if (recoverableProof?.basis === "factors") {
17369
17369
  const sourceStart = offsets ? offsets[cursor] : cursor;
17370
17370
  const sourceEnd = offsets ? offsets[segment.end - 1] + 1 : segment.end;
17371
- const continuation = rsaPrivateKeyCrtContinuationConsistency(
17371
+ const continuation2 = rsaPrivateKeyCrtContinuationConsistency(
17372
17372
  recoverableProof,
17373
17373
  text,
17374
17374
  cursor,
@@ -17385,9 +17385,9 @@ function scanEncodedPrivateKeyRanges(value, metrics, allowAdjacentPrefix = true,
17385
17385
  bitOffset
17386
17386
  );
17387
17387
  const sourceFactorEnd = offsets ? offsets[exactEnd - 1] + 1 : exactEnd;
17388
- const sourceMatchedEnd = offsets ? offsets[continuation.matchedEnd - 1] + 1 : continuation.matchedEnd;
17388
+ const sourceMatchedEnd = offsets ? offsets[continuation2.matchedEnd - 1] + 1 : continuation2.matchedEnd;
17389
17389
  const minimumAuthenticatedCharacters = minimumRsaCrtContinuationSourceCharacters(framing);
17390
- const authenticatedMatchedEnd = continuation.matchedEnd - exactEnd >= minimumAuthenticatedCharacters ? continuation.matchedEnd : exactEnd;
17390
+ const authenticatedMatchedEnd = continuation2.matchedEnd - exactEnd >= minimumAuthenticatedCharacters ? continuation2.matchedEnd : exactEnd;
17391
17391
  matchedFactorEnd = Math.max(exactEnd, authenticatedMatchedEnd);
17392
17392
  rsaCrtContinuations.push({
17393
17393
  start: sourceStart,
@@ -17395,8 +17395,8 @@ function scanEncodedPrivateKeyRanges(value, metrics, allowAdjacentPrefix = true,
17395
17395
  factorEnd: sourceFactorEnd,
17396
17396
  matchedEnd: sourceMatchedEnd,
17397
17397
  minimumAuthenticatedCharacters,
17398
- mismatchAt: continuation.mismatchAt === null ? null : offsets ? offsets[continuation.mismatchAt] : continuation.mismatchAt,
17399
- status: continuation.status
17398
+ mismatchAt: continuation2.mismatchAt === null ? null : offsets ? offsets[continuation2.mismatchAt] : continuation2.mismatchAt,
17399
+ status: continuation2.status
17400
17400
  });
17401
17401
  }
17402
17402
  const exactFactorEnd = recoverableProof?.basis === "factors" && recoverableLength !== null ? encodedPrivateKeyInteriorEnd(
@@ -20857,8 +20857,8 @@ function formattedEncodedPrivateKeyRanges(value, metrics, directStrongRanges, ha
20857
20857
  ranges.push(
20858
20858
  ...subtractTextRanges(jsonArrays.failClosed, excludedAssemblyRanges)
20859
20859
  );
20860
- for (const json of jsonArrays.groups) {
20861
- const candidates = assemblyPieces(json, true);
20860
+ for (const json2 of jsonArrays.groups) {
20861
+ const candidates = assemblyPieces(json2, true);
20862
20862
  append(
20863
20863
  strongFormattedPieceRanges(
20864
20864
  candidates,
@@ -21891,7 +21891,7 @@ function strictIndependentPrivateSourceRanges(scope, metrics, document) {
21891
21891
  );
21892
21892
  claims.push({ token: frame.token, ...range });
21893
21893
  }
21894
- const continuation = complete ? completeRsaPrivateKeyCopies(
21894
+ const continuation2 = complete ? completeRsaPrivateKeyCopies(
21895
21895
  scope,
21896
21896
  frames,
21897
21897
  bytes,
@@ -21912,16 +21912,16 @@ function strictIndependentPrivateSourceRanges(scope, metrics, document) {
21912
21912
  tokenIndexes,
21913
21913
  metrics
21914
21914
  ) : completion;
21915
- if (!continuation) continue;
21916
- if (continuation.ambiguous || detachedRsaContinuationReadsSpent(metrics) > MAX_DETACHED_RSA_CONTINUATION_READS) {
21915
+ if (!continuation2) continue;
21916
+ if (continuation2.ambiguous || detachedRsaContinuationReadsSpent(metrics) > MAX_DETACHED_RSA_CONTINUATION_READS) {
21917
21917
  for (const frame of frames) {
21918
21918
  metrics.ownershipSteps += 1;
21919
21919
  claims.push(wholeTokenSourceRange(frame.token));
21920
21920
  }
21921
- claims.push(...continuation.closed);
21922
- if (continuation.ambiguous) continue;
21921
+ claims.push(...continuation2.closed);
21922
+ if (continuation2.ambiguous) continue;
21923
21923
  }
21924
- for (const range of continuation.ranges) {
21924
+ for (const range of continuation2.ranges) {
21925
21925
  if (range.token === detached) continue;
21926
21926
  claims.push(range);
21927
21927
  }
@@ -23727,23 +23727,197 @@ function remembranceMcpToolSecurity(name) {
23727
23727
  return REMEMBRANCE_MCP_TOOL_SECURITY[name];
23728
23728
  }
23729
23729
 
23730
+ // ../core/src/private-submission-approval.ts
23731
+ var tools = REMEMBRANCE_MCP_PRIVATE_LEARNING_NETWORK_ACTIONS;
23732
+ var PRIVATE_SUBMISSION_LOCAL_CAPTURE_TOOL = "capture_learning";
23733
+ var PRIVATE_SUBMISSION_LOCAL_CAPTURE_NOTE = "capture_learning stays on this machine and sends nothing";
23734
+ var capture = PRIVATE_SUBMISSION_LOCAL_CAPTURE_TOOL;
23735
+ var withCapture = [...tools, capture];
23736
+ var hostedTools = tools.filter(
23737
+ (tool) => !REMEMBRANCE_MCP_LOCAL_ONLY_TOOLS.includes(tool)
23738
+ );
23739
+ var localOnlyComment = `# Local only: ${PRIVATE_SUBMISSION_LOCAL_CAPTURE_NOTE}.`;
23740
+ var PRIVATE_SUBMISSION_CONFIRMED_DELETE_TOOLS = Object.freeze([
23741
+ "delete_private_lesson_candidate",
23742
+ "delete_private_skill_candidate"
23743
+ ]);
23744
+ var isConfirmedDelete = (tool) => PRIVATE_SUBMISSION_CONFIRMED_DELETE_TOOLS.includes(
23745
+ tool
23746
+ );
23747
+ var GEMINI_ADMIN_ALLOWED_TOOLS = Object.freeze(
23748
+ REMEMBRANCE_MCP_RECOMMENDED_LOCAL_ORG_TOOLS.filter(
23749
+ (tool) => !isConfirmedDelete(tool)
23750
+ )
23751
+ );
23752
+ var json = (value) => JSON.stringify(value, null, 2);
23753
+ var approveTables = (server, names = tools) => names.map((tool) => `[${server}.tools.${tool}]
23754
+ approval_mode = "approve"`).join("\n\n");
23755
+ var geminiRule = (tool, decision = "allow") => `[[rule]]
23756
+ mcpName = "remembrance"
23757
+ toolName = "${tool}"
23758
+ decision = "${decision}"
23759
+ priority = 200`;
23760
+ var claudeCodePluginTools = withCapture.map(
23761
+ (tool) => `mcp__plugin_remembrance_remembrance__${tool}`
23762
+ );
23763
+ var codexPluginServer = 'plugins."remembrance@remembrance".mcp_servers.remembrance';
23764
+ var PRIVATE_SUBMISSION_APPROVAL_SNIPPETS = Object.freeze({
23765
+ claudeCodePlugin: json({ permissions: { allow: claudeCodePluginTools } }),
23766
+ // Managed approval for the plugin's local server: the force-enable and the
23767
+ // allow rules only. The optional lockdown keys are separate because they
23768
+ // change much more than approval.
23769
+ claudeCodeManagedPlugin: json({
23770
+ enabledPlugins: { "remembrance@remembrance": true },
23771
+ permissions: { allow: claudeCodePluginTools }
23772
+ }),
23773
+ claudeCodeManagedLockdown: json({
23774
+ strictKnownMarketplaces: [
23775
+ { source: "github", repo: "dreamarkinc/remembrance-skills" }
23776
+ ],
23777
+ allowManagedHooksOnly: true
23778
+ }),
23779
+ // A local standalone server named `remembrance`.
23780
+ claudeCodeServer: json({
23781
+ permissions: {
23782
+ allow: withCapture.map((tool) => `mcp__remembrance__${tool}`)
23783
+ }
23784
+ }),
23785
+ // Hosted MCP, including the managed-mcp.json path.
23786
+ claudeCodeHosted: json({
23787
+ permissions: {
23788
+ allow: hostedTools.map((tool) => `mcp__remembrance__${tool}`)
23789
+ }
23790
+ }),
23791
+ codexPlugin: approveTables(codexPluginServer),
23792
+ codexManualServer: approveTables("mcp_servers.remembrance"),
23793
+ // Optional and separate: Codex asks before capture_learning only under the
23794
+ // managed `writes` mode, and an `approve` table skips Auto-review.
23795
+ codexPluginCapture: [
23796
+ localOnlyComment,
23797
+ "# Approving it also skips Auto-review for the lesson it captures.",
23798
+ approveTables(codexPluginServer, [capture])
23799
+ ].join("\n"),
23800
+ cursorIde: json({
23801
+ mcpAllowlist: withCapture.map((tool) => `remembrance:${tool}`)
23802
+ }),
23803
+ cursorCli: json({
23804
+ permissions: {
23805
+ allow: withCapture.map((tool) => `Mcp(remembrance:${tool})`)
23806
+ }
23807
+ }),
23808
+ openCode: json({
23809
+ permission: Object.fromEntries(
23810
+ withCapture.map((tool) => [`remembrance_${tool}`, "allow"])
23811
+ )
23812
+ }),
23813
+ openClaw: json({
23814
+ plugins: {
23815
+ entries: {
23816
+ remembrance: {
23817
+ config: {
23818
+ privateLearningEgressApprovals: Object.fromEntries(
23819
+ tools.map((tool) => [
23820
+ REMEMBRANCE_MCP_TOOL_SECURITY[tool].consent_class,
23821
+ true
23822
+ ])
23823
+ )
23824
+ }
23825
+ }
23826
+ }
23827
+ }
23828
+ }),
23829
+ geminiCli: [
23830
+ ...tools.map((tool) => geminiRule(tool)),
23831
+ `${localOnlyComment}
23832
+ ${geminiRule(capture)}`
23833
+ ].join("\n\n"),
23834
+ // The managed recipe's admin policy file. It holds the step's own rules
23835
+ // above exactly once, so administrators use it instead of them.
23836
+ geminiCliAdmin: [
23837
+ [
23838
+ "# One exact rule per tool in includeTools: allow, except the two",
23839
+ "# confirmed-delete tools, which ask. To make a tool ask, change its",
23840
+ '# decision to "ask_user" rather than deleting its rule: an admin ask',
23841
+ "# outranks users' own allow rules, and without one a user's allow decides."
23842
+ ].join("\n"),
23843
+ ...REMEMBRANCE_MCP_RECOMMENDED_LOCAL_ORG_TOOLS.map((tool) => {
23844
+ if (isConfirmedDelete(tool)) return geminiRule(tool, "ask_user");
23845
+ if (tool === capture) return `${localOnlyComment}
23846
+ ${geminiRule(tool)}`;
23847
+ return geminiRule(tool);
23848
+ })
23849
+ ].join("\n\n")
23850
+ });
23851
+
23852
+ // ../core/src/private-skill-completion.ts
23853
+ var PRIVATE_SKILL_COMPLETION_VERSION = "private-skill-completion-v1";
23854
+ var opaqueId = external_exports.string().min(1).max(MAX_SHORT_TEXT_LENGTH);
23855
+ var privateSkillCompletionRevisionIdSchema = external_exports.string().regex(/^pscr_[a-f0-9]{18}$/u);
23856
+ var privateSkillCompletionRequestIdSchema = external_exports.string().regex(/^pscc_[a-f0-9]{32}$/u);
23857
+ var privateSkillCompletionStatusRequestSchema = external_exports.object({
23858
+ contract_version: external_exports.literal(PRIVATE_SKILL_COMPLETION_VERSION),
23859
+ public_id: opaqueId
23860
+ }).strict();
23861
+ var privateSkillCompletionSourceRequestSchema = privateSkillCompletionStatusRequestSchema.extend({
23862
+ revision_id: privateSkillCompletionRevisionIdSchema
23863
+ }).strict();
23864
+ var privateSkillCompletionApprovedSourceSchema = privateSkillIdeaRequestSchema.omit({ agent: true, idempotency_key: true, enterprise_encryption: true }).strict();
23865
+ var privateSkillCompletionSourceResponseSchema = privateSkillCompletionSourceRequestSchema.extend({
23866
+ capture: privateSkillCompletionApprovedSourceSchema.describe("Current approved submission; proposed_skill_md may be null or absent. New revisions still require a complete skill body.")
23867
+ }).strict();
23868
+ var privateSkillCompletionRequestStatusSchema = privateSkillCompletionStatusRequestSchema.extend({
23869
+ request_id: privateSkillCompletionRequestIdSchema
23870
+ }).strict();
23871
+ var continuation = privateSkillCompletionRequestStatusSchema.extend({
23872
+ expected: external_exports.object({
23873
+ completion_id: opaqueId,
23874
+ revision_id: privateSkillCompletionRevisionIdSchema,
23875
+ version: external_exports.number().int().nonnegative()
23876
+ }).strict()
23877
+ });
23878
+ var revisedCapture = privateSkillCaptureSchema.superRefine((capture2, context) => {
23879
+ const pending = [{ value: capture2.proposed_metadata, path: ["proposed_metadata"] }];
23880
+ while (pending.length) {
23881
+ const { value, path } = pending.pop();
23882
+ if (!value || typeof value !== "object") continue;
23883
+ for (const [key, entry] of Object.entries(value)) {
23884
+ if (key.startsWith("_remembrance_")) {
23885
+ context.addIssue({ code: "custom", path: [...path, key], message: "Server provenance fields are reserved." });
23886
+ return;
23887
+ }
23888
+ pending.push({ value: entry, path: [...path, key] });
23889
+ }
23890
+ }
23891
+ });
23892
+ var privateSkillCompletionContinuationSchema = external_exports.discriminatedUnion("operation", [
23893
+ continuation.extend({ operation: external_exports.literal("declare_intent"), intent: external_exports.literal("skill_output") }).strict(),
23894
+ continuation.extend({ operation: external_exports.literal("revise"), capture: revisedCapture }).strict(),
23895
+ continuation.extend({ operation: external_exports.literal("resume"), mode: external_exports.literal("changed_revision"), deadline_at: external_exports.number().int().positive() }).strict()
23896
+ ]);
23897
+
23730
23898
  // ../core/src/seed.ts
23731
23899
  var siteUrl = "https://remembrance.dev";
23732
23900
  var feedbackUrl = `${siteUrl}/api/v1/agent/remembrances`;
23733
- var recommendedOrganizationToolToml = REMEMBRANCE_MCP_RECOMMENDED_ORG_TOOLS.map(
23734
- (tool) => ` ${JSON.stringify(tool)},`
23735
- ).join("\n");
23736
- var recommendedOrganizationToolJson = REMEMBRANCE_MCP_RECOMMENDED_ORG_TOOLS.map(
23737
- (tool) => ` ${JSON.stringify(tool)},`
23738
- ).join("\n");
23739
- var recommendedClaudeToolJson = REMEMBRANCE_MCP_RECOMMENDED_ORG_TOOLS.map(
23740
- (tool) => ` ${JSON.stringify(`mcp__remembrance__${tool}`)},`
23741
- ).join("\n");
23901
+ var jsonItems = (values, indent) => values.map((value) => `${indent}${JSON.stringify(value)}`).join(",\n");
23902
+ var recommendedClaudeToolJson = jsonItems(
23903
+ REMEMBRANCE_MCP_RECOMMENDED_ORG_TOOLS.map(
23904
+ (tool) => `mcp__remembrance__${tool}`
23905
+ ),
23906
+ " "
23907
+ );
23742
23908
  var recommendedLocalOrganizationToolToml = REMEMBRANCE_MCP_RECOMMENDED_LOCAL_ORG_TOOLS.map(
23743
23909
  (tool) => ` ${JSON.stringify(tool)},`
23744
23910
  ).join("\n");
23745
- var recommendedLocalOrganizationToolJson = REMEMBRANCE_MCP_RECOMMENDED_LOCAL_ORG_TOOLS.map(
23746
- (tool) => ` ${JSON.stringify(tool)},`
23911
+ var geminiIncludeToolJson = jsonItems(
23912
+ REMEMBRANCE_MCP_RECOMMENDED_LOCAL_ORG_TOOLS,
23913
+ " "
23914
+ );
23915
+ var openClawToolFilterJson = jsonItems(
23916
+ REMEMBRANCE_MCP_RECOMMENDED_LOCAL_ORG_TOOLS,
23917
+ " "
23918
+ );
23919
+ var privateSubmissionToolList = REMEMBRANCE_MCP_PRIVATE_LEARNING_NETWORK_ACTIONS.map(
23920
+ (tool) => `- \`${tool}\``
23747
23921
  ).join("\n");
23748
23922
  var SKILL_INSTALL_COMMAND = "npx skills add dreamarkinc/remembrance-skills --skill remembrancer";
23749
23923
  var skillInstallCommand = SKILL_INSTALL_COMMAND;
@@ -23828,7 +24002,7 @@ var seedSkills = [
23828
24002
  summary: "Operational setup and troubleshooting workflow for Remembrance across Claude Code, Codex, OpenClaw, Cursor, Gemini, MCP, REST, skill-only installs, enterprise keys, and local agent identity.",
23829
24003
  status: "active",
23830
24004
  visibility: "public",
23831
- version: "0.1.38",
24005
+ version: "0.1.39",
23832
24006
  domains: ["agent-skills", "mcp", "resource-discovery"],
23833
24007
  tags: [
23834
24008
  "remembrance",
@@ -23879,7 +24053,7 @@ var seedSkills = [
23879
24053
  "gemini",
23880
24054
  "rest"
23881
24055
  ],
23882
- version: "0.1.38",
24056
+ version: "0.1.39",
23883
24057
  status: "active",
23884
24058
  visibility: "public",
23885
24059
  providers: ["codex", "claude", "cursor", "openclaw", "generic"],
@@ -23922,7 +24096,8 @@ var seedSkills = [
23922
24096
  },
23923
24097
  contraindications: [
23924
24098
  "Do not ask the user to paste raw API keys, private key material, tokens, cookies, or full config files containing secrets.",
23925
- "Do not store org keys in public project files or submit them back to Remembrance."
24099
+ "Do not store org keys in public project files or submit them back to Remembrance.",
24100
+ "Do not change host approval or permission settings, or answer the host's approval of your own pending action, without the user's consent; ask the user (for managed settings, the administrator) first."
23926
24101
  ],
23927
24102
  feedback_url: feedbackUrl,
23928
24103
  install_command: skillInstallCommand,
@@ -23942,7 +24117,9 @@ var seedSkills = [
23942
24117
  "OpenClaw ClawHub search can return unrelated remembrance-named packages; verify the official package points to dreamarkinc/remembrance-skills before installing.",
23943
24118
  "Until allowConversationAccess is enabled, OpenClaw holds only the plugin's completion hook; the prompt, tool and subagent-end hooks already run, and the prompt hook can query the registry.",
23944
24119
  "Cursor local and cloud distribution are separate: configure the team plugin and Team MCP, then verify hook and receipt behavior on each enabled surface.",
23945
- "MCP tools can be missing when the client uses the wrong config shape for its runtime."
24120
+ "MCP tools can be missing when the client uses the wrong config shape for its runtime.",
24121
+ "Without persisted host approval for the four private submit and retry tools, an organization install on a host that asks before them stops every private lesson or skill submission for a manual approval, and unattended or headless runs deny it.",
24122
+ "On hosts that also ask before the local-only capture_learning, a headless run denies the capture itself unless its entry is approved, so no draft reaches the submit tools."
23946
24123
  ],
23947
24124
  suggested_patches: [
23948
24125
  "Add runtime-specific screenshots once each agent's plugin manager UI stabilizes.",
@@ -23969,6 +24146,8 @@ REST/HTTPS, skill-only installs, enterprise org keys, local identity, and common
23969
24146
  propose_private_skill because it has no durable device outbox.
23970
24147
  - A native plugin appears installed but hooks, trust prompts, or MCP tools do
23971
24148
  not work.
24149
+ - An organization install stops for approval at every private lesson or skill
24150
+ submission, or an unattended or headless run denies it.
23972
24151
  - A request fails with 401, 403, 404, 413, 422, 429, or a missing-key error.
23973
24152
 
23974
24153
  ## First decision
@@ -23982,6 +24161,11 @@ REST/HTTPS, skill-only installs, enterprise org keys, local identity, and common
23982
24161
  5. Use the skills.sh entry skill only when the runtime can load filesystem
23983
24162
  skills but not native plugins.
23984
24163
 
24164
+ With an organization key, a plugin or MCP install also needs the step in
24165
+ "Highly Recommended: approve private submissions" below; ask the user to apply
24166
+ it rather than changing host settings yourself. REST and skill-only installs
24167
+ have no MCP tools to approve.
24168
+
23985
24169
  Raw MCP, REST, and skill-only paths do not have native Stop hooks. They must
23986
24170
  self-check before finishing and submit \`type: "failure_report"\` remembrances
23987
24171
  for reusable self-corrections, user-caught mistakes, CI/deploy failures, and
@@ -24085,51 +24269,6 @@ show the same review screen again; never use the automation-only trust bypass
24085
24269
  for normal installation. Fully restart Codex, submit one prompt, use one
24086
24270
  Remembrance tool, complete one turn, and run \`run_connection_doctor\`.
24087
24271
 
24088
- ### Optional unattended routine feedback in Codex
24089
-
24090
- After the user explicitly opts in, add only these two tool overrides to the
24091
- user-level \`~/.codex/config.toml\`. This approval persists across future
24092
- sessions on this installation until removed; installation alone does not grant
24093
- it. Merge with existing tables rather than duplicating them:
24094
-
24095
- ~~~toml
24096
- [plugins."remembrance@remembrance".mcp_servers.remembrance.tools.submit_query_feedback]
24097
- approval_mode = "approve"
24098
-
24099
- [plugins."remembrance@remembrance".mcp_servers.remembrance.tools.submit_feedback]
24100
- approval_mode = "approve"
24101
- ~~~
24102
-
24103
- Use this opt-in for an organization-authenticated local plugin whose
24104
- \`submit_feedback\` schema is categorical and rejects \`lesson\`. Query-fit
24105
- ratings send Remembrance-issued query/result IDs, categorical verdicts, and
24106
- required reasons; post-use ratings send the skill slug, those correlation IDs, usefulness/rating,
24107
- and stable idempotency. Keep optional notes and evidence out of routine
24108
- ratings: do not automatically attach internal receipt hashes, task prose,
24109
- source content, raw logs, or private URLs. Public substantive reports retain
24110
- their separate evidence requirements; reusable private lessons use
24111
- \`capture_learning\` and its exact returned action.
24112
-
24113
- These are real writes, not read-only diagnostics. Post-use feedback can enter
24114
- verification and incur processing cost under the existing organization policy.
24115
- The two overrides do not approve private lesson/skill submission, publishing,
24116
- deletion, reset, preference changes, or other tools. They do not widen the
24117
- server's enabled tool set or override managed policy, guardian decisions,
24118
- network restrictions, or organization processing grants. Do not set the whole
24119
- server's approval default to \`approve\` for this purpose.
24120
-
24121
- Tool overrides persist when authentication or schemas change. Remove or
24122
- re-review both approvals before switching to anonymous or hosted transport,
24123
- enabling legacy broad writes, or changing the organization or destination.
24124
-
24125
- To revoke this opt-in, remove only these two tool-level approval entries;
24126
- the inherited approval policy applies again. Reconnect the plugin or start a
24127
- fresh Codex session if the current host does not reload config, then verify
24128
- with a minimal eligible rating. A healthy connection doctor verifies the
24129
- connection, not that host approval policy permits feedback. A denial means
24130
- that rating was not submitted; acknowledge it locally and do not retry through
24131
- another transport. See the [official installed-server policy guide](https://developers.openai.com/plugins/build/plugins#configure-installed-server-policy).
24132
-
24133
24272
  OpenClaw:
24134
24273
 
24135
24274
  ~~~bash
@@ -24177,25 +24316,300 @@ Cursor plugin installs this Remembrancer skill, an always-apply Cursor rule, a
24177
24316
  plugin-managed MCP server config, and hooks that ask for feedback only after
24178
24317
  actual Remembrance MCP use.
24179
24318
 
24180
- For local plugin testing before marketplace approval:
24319
+ For local plugin testing before marketplace approval, copy the plugin into its
24320
+ own folder under Cursor's local plugin folder, then restart Cursor or run
24321
+ **Developer: Reload Window**:
24181
24322
 
24182
24323
  ~~~bash
24183
- mkdir -p ~/.cursor/plugins/local
24184
- ln -s /absolute/path/to/remembrance/packages/cursor-plugin ~/.cursor/plugins/local/remembrance
24324
+ mkdir -p ~/.cursor/plugins/local/remembrance
24325
+ cp -R /absolute/path/to/remembrance/packages/cursor-plugin/. ~/.cursor/plugins/local/remembrance/
24185
24326
  ~~~
24186
24327
 
24328
+ Copy the files rather than linking them: Cursor skips a symbolic link that
24329
+ points to a plugin elsewhere on disk. On Teams and Enterprise, local plugins
24330
+ load only when an administrator allows **Allow Local Plugin Imports**, which is
24331
+ off by default on Enterprise.
24332
+
24187
24333
  Cursor now documents conversation hooks for cloud agents, but a local plugin
24188
24334
  install does not automatically provision the cloud surface. Distribute the
24189
- plugin through the team marketplace, register Remembrance under **Dashboard >
24190
- Integrations & MCP**, and verify query, invocation, feedback, and contribution
24191
- receipts in both local and cloud runs.
24335
+ plugin through the team marketplace, register Remembrance as a Team MCP server
24336
+ under **Dashboard > Plugins & MCPs**, and verify query, invocation, feedback,
24337
+ and contribution receipts in both local and cloud runs.
24192
24338
 
24193
24339
  After installing any native plugin, restart the agent app/session and approve
24194
24340
  the runtime's trust request when one appears. For Codex, complete the hook
24195
24341
  review above if Codex requests it; unchanged, previously trusted definitions
24196
24342
  may not show another review screen. Changed hook definitions show the review
24197
24343
  screen again. A currently running Codex or Claude thread usually cannot
24198
- hot-load newly installed plugin tools.
24344
+ hot-load newly installed plugin tools. With an organization key, then ask the
24345
+ user to complete the Highly Recommended step below.
24346
+
24347
+ ### Optional unattended routine feedback in Codex
24348
+
24349
+ After the user explicitly opts in, add only these two tool overrides to the
24350
+ user-level \`~/.codex/config.toml\`. This approval persists across future
24351
+ sessions on this installation until removed; installation alone does not grant
24352
+ it. Merge with existing tables rather than duplicating them:
24353
+
24354
+ ~~~toml
24355
+ [plugins."remembrance@remembrance".mcp_servers.remembrance.tools.submit_query_feedback]
24356
+ approval_mode = "approve"
24357
+
24358
+ [plugins."remembrance@remembrance".mcp_servers.remembrance.tools.submit_feedback]
24359
+ approval_mode = "approve"
24360
+ ~~~
24361
+
24362
+ Use this opt-in for an organization-authenticated local plugin whose
24363
+ \`submit_feedback\` schema is categorical and rejects \`lesson\`. Query-fit
24364
+ ratings send Remembrance-issued query/result IDs, categorical verdicts, and
24365
+ required reasons; post-use ratings send the skill slug, those correlation IDs, usefulness/rating,
24366
+ and stable idempotency. Keep optional notes and evidence out of routine
24367
+ ratings: do not automatically attach internal receipt hashes, task prose,
24368
+ source content, raw logs, or private URLs. Public substantive reports retain
24369
+ their separate evidence requirements; reusable private lessons use
24370
+ \`capture_learning\` and its exact returned action.
24371
+
24372
+ These are real writes, not read-only diagnostics. Post-use feedback can enter
24373
+ verification and incur processing cost under the existing organization policy.
24374
+ The two overrides do not approve private lesson/skill submission (the Highly
24375
+ Recommended step below covers that), publishing, deletion, reset, preference
24376
+ changes, or other tools. They do not widen the server's enabled tool set or
24377
+ override managed policy, network restrictions, or organization processing
24378
+ grants. They do skip the Codex approval prompt and its Auto-review (guardian)
24379
+ evaluation for these two tools. Do not set the whole server's approval default
24380
+ to \`approve\` for this purpose.
24381
+
24382
+ Tool overrides persist when authentication or schemas change. Remove or
24383
+ re-review both approvals before switching to anonymous or hosted transport,
24384
+ enabling legacy broad writes, or changing the organization or destination.
24385
+
24386
+ To revoke this opt-in, remove only these two tool-level approval entries;
24387
+ the inherited approval policy applies again. Reconnect the plugin or start a
24388
+ fresh Codex session if the current host does not reload config, then verify
24389
+ with a minimal eligible rating. A healthy connection doctor verifies the
24390
+ connection, not that host approval policy permits feedback. A denial means
24391
+ that rating was not submitted; acknowledge it locally and do not retry through
24392
+ another transport. See the [official installed-server policy guide](https://developers.openai.com/plugins/build/plugins#configure-installed-server-policy).
24393
+
24394
+ ## Highly Recommended: approve private submissions
24395
+
24396
+ For organizations that use Remembrance with a private organization key, this
24397
+ step is required for Remembrance to do its job fully. Without it, on hosts that
24398
+ ask before these tools, every private lesson or skill submission stops for a
24399
+ manual approval, and unattended or headless runs deny it. Public-only use,
24400
+ without an organization key, does not need it.
24401
+
24402
+ Ask the user (for managed settings, the administrator) before applying this
24403
+ step. An agent never edits host permission or settings files without the
24404
+ user's consent, and never answers the host's approval of its own pending
24405
+ action.
24406
+
24407
+ Persist the host's approval for exactly these four local tools, the private
24408
+ submit and retry actions:
24409
+
24410
+ ${privateSubmissionToolList}
24411
+
24412
+ Hosted MCP exposes only \`submit_private_lesson_candidate\` of the four, so
24413
+ approve only that one there. Each approval lets the agent send to the
24414
+ organization's private review queue without asking. On a local server that is
24415
+ normally a draft that \`capture_learning\` already prepared, although the
24416
+ lesson submit also accepts a lesson candidate the agent writes into the call;
24417
+ on hosted MCP, which has no \`capture_learning\`, the approved submit sends the
24418
+ lesson candidate the agent writes. These actions cannot publish public content.
24419
+ Never pre-approve \`delete_private_lesson_candidate\` or
24420
+ \`delete_private_skill_candidate\`, a whole server (\`mcp__<server>\` or
24421
+ \`mcp__<server>__*\`), \`bypassPermissions\`, Gemini CLI \`trust: true\` or
24422
+ \`mcp.allowed\`, or any global auto-approve.
24423
+
24424
+ Every local private submission starts with \`capture_learning\`, which writes
24425
+ the encrypted draft. It is local only: it stays on this machine and sends
24426
+ nothing. On hosts that ask before it (Claude Code, which asks before every MCP
24427
+ tool; Gemini CLI; VS Code; and Cursor, depending on its Run Mode), the steps
24428
+ below add it as a labelled local-only entry, which is not one of the four
24429
+ submit and retry tools. Without it the capture prompts, and a headless run
24430
+ denies it before any draft exists. Codex under the managed \`writes\` mode also
24431
+ asks before it, but there approving it is an optional administrator decision:
24432
+ with it, Auto-review never sees the lesson content (see "Approve private
24433
+ repository contributions in managed Codex"). On these hosts a run that nobody
24434
+ can answer, such as a headless run, denies every tool that has no approval, so
24435
+ it also needs approvals for the other Remembrance tools it calls, such as
24436
+ \`query_skills\`; this step covers only private learning.
24437
+
24438
+ Check the step by behavior: capture one disposable private lesson and confirm
24439
+ that neither the capture nor its submission prompts. Where the organization
24440
+ leaves \`capture_learning\` to Auto-review or a person, as managed Codex may,
24441
+ only the capture is reviewed; its submission still must not prompt.
24442
+ \`run_connection_doctor\` cannot see a persisted approval; its \`host_approval\`
24443
+ field reports only recent denials.
24444
+
24445
+ ### Claude Code
24446
+
24447
+ Merge these entries into \`permissions.allow\` in \`~/.claude/settings.json\`,
24448
+ which applies to every project. The first four are the plugin's private submit
24449
+ and retry tools; the last is the local-only \`capture_learning\` entry:
24450
+
24451
+ ~~~json
24452
+ ${PRIVATE_SUBMISSION_APPROVAL_SNIPPETS.claudeCodePlugin}
24453
+ ~~~
24454
+
24455
+ Choosing "Yes, and don't ask again" at the prompt saves the rule only for the
24456
+ current project, in \`.claude/settings.local.json\`. A local standalone server
24457
+ named \`remembrance\`, whether a user or the organization registers it, uses
24458
+ \`mcp__remembrance__<tool>\` names instead:
24459
+
24460
+ ~~~json
24461
+ ${PRIVATE_SUBMISSION_APPROVAL_SNIPPETS.claudeCodeServer}
24462
+ ~~~
24463
+
24464
+ Hosted MCP, including the \`managed-mcp.json\` path in "Approve Claude Code",
24465
+ exposes only \`submit_private_lesson_candidate\` of these and has no
24466
+ \`capture_learning\`, so a hosted server named \`remembrance\` needs only this
24467
+ entry:
24468
+
24469
+ ~~~json
24470
+ ${PRIVATE_SUBMISSION_APPROVAL_SNIPPETS.claudeCodeHosted}
24471
+ ~~~
24472
+
24473
+ Headless runs such as \`claude -p\` deny every tool without an allow rule, so
24474
+ they also need rules for the other Remembrance tools they call. Plan mode still
24475
+ asks before these tools even when a rule allows them. When managed settings set
24476
+ \`allowManagedPermissionRulesOnly\`, Claude Code ignores user rules, so an
24477
+ administrator must ship them (see "Approve Claude Code").
24478
+
24479
+ ### Codex
24480
+
24481
+ Add one table per tool to the user-level \`~/.codex/config.toml\`, separate from
24482
+ the optional routine-feedback opt-in above:
24483
+
24484
+ ~~~toml
24485
+ ${PRIVATE_SUBMISSION_APPROVAL_SNIPPETS.codexPlugin}
24486
+ ~~~
24487
+
24488
+ Current Codex source does not prompt for these four tools, or for
24489
+ \`capture_learning\`, under the default \`auto\` tool approval mode, because
24490
+ Remembrance marks them non-destructive and closed-world; older releases and
24491
+ other modes can, so add the tables anyway. Under the managed
24492
+ \`default_tools_approval_mode = "writes"\`, Codex asks before every call, from a
24493
+ person or from Auto-review where it is the reviewer, and cannot remember the
24494
+ answer, so a per-tool \`approve\` entry is the only way to persist approval.
24495
+ \`approve\` also skips Auto-review for that tool, so in a managed organization
24496
+ add these tables only with your administrator ("Approve private repository
24497
+ contributions in managed Codex" covers the trade-off and \`capture_learning\`).
24498
+ For a manually registered server named \`remembrance\`, use the same tables
24499
+ under \`mcp_servers.remembrance\`:
24500
+
24501
+ ~~~toml
24502
+ ${PRIVATE_SUBMISSION_APPROVAL_SNIPPETS.codexManualServer}
24503
+ ~~~
24504
+
24505
+ ### Cursor
24506
+
24507
+ In the IDE, merge these entries into the \`mcpAllowlist\` array of
24508
+ \`~/.cursor/permissions.json\` (create the file if it is missing); the last is
24509
+ the local-only \`capture_learning\` entry:
24510
+
24511
+ ~~~json
24512
+ ${PRIVATE_SUBMISSION_APPROVAL_SNIPPETS.cursorIde}
24513
+ ~~~
24514
+
24515
+ \`mcpAllowlist\` replaces the whole in-app MCP allowlist, so keep the entries
24516
+ you already rely on in the same array, keep the file's other keys such as
24517
+ \`terminalAllowlist\`, \`approvalMode\`, and \`autoReview\`, and use this
24518
+ user-level file. Cursor applies it under its Run Modes (Auto-review is the
24519
+ default); a team Run Mode configuration from an administrator overrides it. For
24520
+ the Cursor CLI, merge the same tools into \`permissions.allow\` in
24521
+ \`~/.cursor/cli-config.json\`:
24522
+
24523
+ ~~~json
24524
+ ${PRIVATE_SUBMISSION_APPROVAL_SNIPPETS.cursorCli}
24525
+ ~~~
24526
+
24527
+ If Cursor shows the plugin's server under a name other than \`remembrance\`, use
24528
+ that exact name instead. Do not replace the server name with \`*\`, which
24529
+ approves the tool on every server.
24530
+
24531
+ ### VS Code
24532
+
24533
+ VS Code has no settings key or policy that pre-approves a single tool; use its
24534
+ interface. Run **Chat: Manage Tool Approval**, expand the Remembrance server,
24535
+ tick only the four tools and the local-only \`capture_learning\`, and turn off
24536
+ **Configure for this workspace only** so the approval applies in every
24537
+ workspace. Do not tick the server's own box or choose **Always Allow Tools
24538
+ from** the server: both approve the whole server. Alternatively, choose
24539
+ **Always Allow** in the prompt's Allow menu the first time each of those tools
24540
+ asks.
24541
+
24542
+ ### OpenCode
24543
+
24544
+ OpenCode runs MCP tools without asking by default, so private submissions
24545
+ already complete. Add explicit entries to the \`permission\` map in
24546
+ \`~/.config/opencode/opencode.json\` so the four tools and the local-only
24547
+ \`capture_learning\` keep working under a broader ask rule that comes before
24548
+ them, which would otherwise stop every private lesson or skill submission:
24549
+
24550
+ ~~~json
24551
+ ${PRIVATE_SUBMISSION_APPROVAL_SNIPPETS.openCode}
24552
+ ~~~
24553
+
24554
+ Put them after any broader \`"*"\` or \`"remembrance_*"\` rule in the same
24555
+ \`permission\` map, because OpenCode applies the last matching rule. A broader ask
24556
+ rule set elsewhere, such as in an agent's own \`permission\` map or, depending on
24557
+ the OpenCode version, in another configuration file, can still take precedence;
24558
+ if the capture or its submission still prompts, find that rule and make sure the
24559
+ same entries come after it. In OpenCode 2.x, answering "always" at a prompt
24560
+ saves an allow rule for the current project across sessions (1.x keeps it until
24561
+ OpenCode restarts); never answer "always" on a \`delete_private_*\` prompt.
24562
+
24563
+ ### OpenClaw
24564
+
24565
+ In native runs OpenClaw documents no prompt of its own for MCP tools; the
24566
+ Remembrance plugin asks before each private submission. To persist approval,
24567
+ answer **allow-always** once for each lane when it asks to "Submit private
24568
+ Remembrance lesson" or "Submit private Remembrance skill", for example with
24569
+ \`/approve <id> allow-always\` in chat or
24570
+ \`openclaw approvals resolve <id> allow-always\`. One decision covers that lane's
24571
+ submit and retry actions, and the plugin saves it in its config.
24572
+
24573
+ To pre-set both lanes instead, Remembrance for OpenClaw 0.2.3 or later accepts
24574
+ this in \`~/.openclaw/openclaw.json\`:
24575
+
24576
+ ~~~json
24577
+ ${PRIVATE_SUBMISSION_APPROVAL_SNIPPETS.openClaw}
24578
+ ~~~
24579
+
24580
+ Check the installed version first, for example with
24581
+ \`openclaw plugins inspect remembrance\`. Earlier versions reject the key: the
24582
+ Gateway then refuses to start until it is removed, and those versions cannot
24583
+ keep an allow-always decision across a restart either. On older or
24584
+ mixed-version installs, use allow-always.
24585
+
24586
+ ### Gemini CLI
24587
+
24588
+ Create \`~/.gemini/policies/remembrance.toml\` with one rule per tool; the last
24589
+ rule is the local-only \`capture_learning\` entry:
24590
+
24591
+ ~~~toml
24592
+ ${PRIVATE_SUBMISSION_APPROVAL_SNIPPETS.geminiCli}
24593
+ ~~~
24594
+
24595
+ Gemini CLI asks before every MCP tool without an allow rule, and
24596
+ non-interactive runs treat that prompt as a denial. These rules cover private
24597
+ learning only, so a headless run also needs rules for the other Remembrance
24598
+ tools it calls, such as \`query_skills\`. Keep \`trust\` false and do not list
24599
+ the server in \`mcp.allowed\`: Gemini CLI turns \`trust: true\` and an
24600
+ \`mcp.allowed\` entry alike into an allow rule for every tool of the server.
24601
+ Administrators use the admin policy file in "Approve Gemini CLI" instead, which
24602
+ already contains these rules.
24603
+
24604
+ ### Claude Desktop and other MCP clients
24605
+
24606
+ Claude Desktop offers a per-tool **Always allow**, in the approval prompt or in
24607
+ the connector's tool permissions, but it has no config key and users report
24608
+ that it resets when the app updates, so do not rely on it for unattended runs.
24609
+ For other MCP clients, use the client's per-tool approval for the four exact
24610
+ tool names (only \`submit_private_lesson_candidate\` on hosted MCP), plus the
24611
+ local-only \`capture_learning\` on a local server that asks before it. If a
24612
+ client can approve only a whole server, keep its prompts.
24199
24613
 
24200
24614
  ## Enterprise/org key setup
24201
24615
 
@@ -24282,8 +24696,10 @@ while malformed or privacy-sensitive tags hold the draft instead of being
24282
24696
  silently removed.
24283
24697
 
24284
24698
  The plugin then calls \`submit_private_lesson_candidate\` with that draft ID.
24285
- This is the only host-visible network action in the flow. Approve or persist
24286
- approval for this exact action, not every Remembrance write. The server derives
24699
+ This is the host-visible network action in the flow, and
24700
+ \`retry_private_lesson_candidate\` later resends a retained draft through the
24701
+ same lane. Persist approval for these exact actions, as in "Highly Recommended:
24702
+ approve private submissions", not for every Remembrance write. The server derives
24287
24703
  the organization and private visibility from the authenticated key, verifies a
24288
24704
  signed organization policy plus purpose-bound attestation, and returns a signed
24289
24705
  content-free receipt. The action always submits privately and never falls back
@@ -24341,16 +24757,17 @@ agent task remain available when capture or submission is blocked.
24341
24757
  ### Approve private repository contributions in managed Codex
24342
24758
 
24343
24759
  An API key authorizes Remembrance; it does not authorize Codex to export
24344
- repository-derived content. Codex Auto-review separately evaluates MCP and
24345
- network actions for data exfiltration. A chat approval may not override an
24346
- enterprise deny rule, and no Remembrance plugin setting can weaken that host
24347
- boundary.
24760
+ repository-derived content. Codex Auto-review evaluates the approval requests
24761
+ that would otherwise pause for a person, including MCP calls that need approval
24762
+ under their annotations or configured approval mode; a tool set to \`approve\`
24763
+ never reaches it. A chat approval may not override an enterprise deny rule, and
24764
+ no Remembrance plugin setting can weaken that host boundary.
24348
24765
 
24349
24766
  For an organization that has approved Remembrance as an operational-memory
24350
24767
  processor, a Codex administrator should do all of the following:
24351
24768
 
24352
- 1. Allow the exact Remembrance MCP server identity and
24353
- \`https://remembrance.dev/api/mcp\` in managed Codex requirements.
24769
+ 1. Allow the Remembrance marketplace source in managed Codex requirements and
24770
+ enable the plugin's bundled \`remembrance\` server in managed configuration.
24354
24771
  2. Merge a narrow Remembrance destination rule into the existing
24355
24772
  \`guardian_policy_config\`. Do not replace the rest of the tenant policy or
24356
24773
  remove its credential, secret, raw-log, or unrelated-source-code denies.
@@ -24371,9 +24788,12 @@ key to force a public candidate. Submit privately, then use the reviewed public-
24371
24788
  propagation flow for a redacted public-safe version when the organization wants
24372
24789
  to share it.
24373
24790
 
24374
- The exact plugin MCP identity belongs in managed \`requirements.toml\`. The
24375
- tool allowlist belongs in managed configuration. This keeps reads and normal
24376
- skill use available while prompting for non-read-only writes:
24791
+ Managed \`requirements.toml\` restricts the marketplace source; the tool
24792
+ allowlist and approval mode belong in managed configuration. The bundled server
24793
+ is a local stdio server, so never give it the hosted URL as its identity. If the
24794
+ requirements also carry an \`mcp_servers\` allowlist, approve this server by its
24795
+ configured command (\`sh\`): Codex disables a server whose name and identity do
24796
+ not match an approved entry.
24377
24797
 
24378
24798
  Add these entries to \`requirements.toml\`:
24379
24799
 
@@ -24381,9 +24801,6 @@ Add these entries to \`requirements.toml\`:
24381
24801
  [features]
24382
24802
  hooks = true
24383
24803
 
24384
- [plugins."remembrance@remembrance".mcp_servers.remembrance]
24385
- identity = { url = "https://remembrance.dev/api/mcp" }
24386
-
24387
24804
  [marketplaces]
24388
24805
  restrict_to_allowed_sources = true
24389
24806
 
@@ -24401,8 +24818,37 @@ enabled_tools = [
24401
24818
  ${recommendedLocalOrganizationToolToml}
24402
24819
  ]
24403
24820
  default_tools_approval_mode = "writes"
24821
+
24822
+ ${PRIVATE_SUBMISSION_APPROVAL_SNIPPETS.codexPlugin}
24404
24823
  ~~~
24405
24824
 
24825
+ \`default_tools_approval_mode = "writes"\` asks before every call of every tool
24826
+ that is not marked read-only, including \`query_skills\`, \`get_skill\`,
24827
+ \`get_resource\`, \`invoke_skill\`, \`capture_learning\`, and the feedback tools.
24828
+ The answer comes from a person, or from Auto-review where it is the reviewer;
24829
+ Codex cannot remember it, and a run that cannot ask denies the call.
24830
+
24831
+ The four \`approve\` tables are the Highly Recommended step: they let the
24832
+ private submit and retry calls run without that per-call review. The trade-off
24833
+ is that Auto-review never evaluates a tool set to \`approve\`, so the guardian
24834
+ policy's denies no longer apply to those four calls. They normally carry only a
24835
+ draft ID, so the lesson or skill content passes through \`capture_learning\`,
24836
+ which a person or Auto-review still reviews; but the lesson submit also accepts
24837
+ a lesson candidate written into the call, which then reaches the
24838
+ organization's private review queue without that review. Leave the tables out
24839
+ if the organization wants Auto-review to evaluate every private submission.
24840
+
24841
+ Runs that cannot ask can capture only if \`capture_learning\` is approved too.
24842
+ This optional table is local only, but with it Auto-review never sees the
24843
+ lesson content at all:
24844
+
24845
+ ~~~toml
24846
+ ${PRIVATE_SUBMISSION_APPROVAL_SNIPPETS.codexPluginCapture}
24847
+ ~~~
24848
+
24849
+ Those runs also need approval for every other tool they call, so unattended
24850
+ work under \`writes\` usually relies on Auto-review as the reviewer instead.
24851
+
24406
24852
  If \`allow_managed_hooks_only = true\`, Codex skips plugin hooks. Either leave
24407
24853
  the vetted Remembrance plugin hooks permitted or deploy equivalent managed
24408
24854
  query/completion hooks; otherwise MCP calls may work while the query and
@@ -24512,10 +24958,45 @@ optional local-only tools and never belong in a hosted MCP allowlist.
24512
24958
  ### Approve Claude Code
24513
24959
 
24514
24960
  Force-enable \`remembrance@remembrance\` in managed settings so its vetted hooks
24515
- still run when \`allowManagedHooksOnly\` is enabled. If the organization deploys
24516
- \`managed-mcp.json\`, define Remembrance there because exclusive managed MCP
24517
- suppresses every plugin-provided MCP server. Use the exact URL as the security
24518
- boundary; a server name alone is not sufficient.
24961
+ still run when \`allowManagedHooksOnly\` is enabled. Then choose one MCP path.
24962
+
24963
+ To keep the plugin's local server, with all four private submit and retry tools
24964
+ and the encrypted device outbox, deploy neither \`managed-mcp.json\` nor the
24965
+ URL-only \`allowedMcpServers\` list of the hosted path below. Exclusive managed
24966
+ MCP suppresses every plugin-provided server, and Claude Code also checks plugin
24967
+ servers against \`allowedMcpServers\`, where a list with only \`serverUrl\`
24968
+ entries blocks every stdio server. Merge this approval into managed settings;
24969
+ the plugin's tool names carry the \`mcp__plugin_remembrance_remembrance__\`
24970
+ prefix, and the last entry is the local-only \`capture_learning\`:
24971
+
24972
+ ~~~json
24973
+ ${PRIVATE_SUBMISSION_APPROVAL_SNIPPETS.claudeCodeManagedPlugin}
24974
+ ~~~
24975
+
24976
+ Managed rules also cover organizations that set
24977
+ \`allowManagedPermissionRulesOnly\`, which makes Claude Code ignore user rules.
24978
+ For command or REST fallbacks under a sandbox, also merge \`remembrance.dev\`
24979
+ into \`sandbox.network.allowedDomains\`.
24980
+
24981
+ Optional lockdown, separate from that approval: Remembrance needs neither of
24982
+ these keys, and both restrict much more than Remembrance.
24983
+ \`allowManagedHooksOnly\` makes Claude Code ignore every user, project, and
24984
+ local hook, and turns off \`/goal\` and command-sourced plugins.
24985
+ \`strictKnownMarketplaces\` blocks plugins from every marketplace it does not
24986
+ list, and also the \`~/.claude/skills\` auto-load unless the list includes
24987
+ \`{"source": "skills-dir"}\`. Add them only when the organization wants that
24988
+ lockdown; the hosted path below carries the same two keys:
24989
+
24990
+ ~~~json
24991
+ ${PRIVATE_SUBMISSION_APPROVAL_SNIPPETS.claudeCodeManagedLockdown}
24992
+ ~~~
24993
+
24994
+ To use hosted MCP instead, define Remembrance in \`managed-mcp.json\`, because
24995
+ exclusive managed MCP suppresses every plugin-provided MCP server. The plugin
24996
+ keeps its hooks and skills, but its tools then come from the hosted server named
24997
+ \`remembrance\`, which exposes only \`submit_private_lesson_candidate\` of the four
24998
+ private actions. Use the exact URL as the security boundary; a server name alone
24999
+ is not sufficient.
24519
25000
 
24520
25001
  Do not put a real key directly in \`managed-mcp.json\`; every local user can read
24521
25002
  that file. Reference each user's process environment instead:
@@ -24534,7 +25015,7 @@ that file. Reference each user's process environment instead:
24534
25015
  }
24535
25016
  ~~~
24536
25017
 
24537
- Merge this into managed settings:
25018
+ Merge this into managed settings for the hosted path:
24538
25019
 
24539
25020
  ~~~json
24540
25021
  {
@@ -24575,13 +25056,11 @@ work. See the official
24575
25056
  ### Approve Gemini CLI
24576
25057
 
24577
25058
  Define the canonical server and its tool allowlist in the system override
24578
- settings, not only user or workspace settings. Leave \`trust\` false unless the
24579
- organization has deliberately chosen to bypass every confirmation for this
24580
- narrow server/tool set:
25059
+ settings, not only user or workspace settings. Keep \`trust\` false;
25060
+ \`trust: true\` bypasses every confirmation for the server:
24581
25061
 
24582
25062
  ~~~json
24583
25063
  {
24584
- "mcp": { "allowed": ["remembrance"] },
24585
25064
  "mcpServers": {
24586
25065
  "remembrance": {
24587
25066
  "command": "npx",
@@ -24591,7 +25070,7 @@ narrow server/tool set:
24591
25070
  "REMEMBRANCE_API_KEY": "\${REMEMBRANCE_API_KEY}"
24592
25071
  },
24593
25072
  "includeTools": [
24594
- ${recommendedLocalOrganizationToolJson}
25073
+ ${geminiIncludeToolJson}
24595
25074
  ],
24596
25075
  "trust": false
24597
25076
  }
@@ -24599,14 +25078,54 @@ ${recommendedLocalOrganizationToolJson}
24599
25078
  }
24600
25079
  ~~~
24601
25080
 
25081
+ Approve tools only with exact per-tool policy rules. Do not list the server in
25082
+ \`mcp.allowed\`: the server runs without it, and current Gemini CLI source turns
25083
+ each \`mcp.allowed\` entry into an allow rule for every tool of that server.
25084
+ Instead, save this file as \`remembrance.toml\` in the admin policy folder:
25085
+ \`/Library/Application Support/GeminiCli/policies\` on macOS,
25086
+ \`/etc/gemini-cli/policies\` on Linux, or \`C:\\ProgramData\\gemini-cli\\policies\`
25087
+ on Windows. Gemini CLI ignores admin policy files that are not root-owned.
25088
+
25089
+ ~~~toml
25090
+ ${PRIVATE_SUBMISSION_APPROVAL_SNIPPETS.geminiCliAdmin}
25091
+ ~~~
25092
+
25093
+ The file has one exact rule for every tool in \`includeTools\`: an allow rule
25094
+ for each tool except the two confirmed-delete tools, whose admin \`ask_user\`
25095
+ rules keep them asking even where a user allowed them, so a headless run denies
25096
+ them. Apart from those two, this reproduces the earlier behavior of this
25097
+ recipe, whose \`mcp.allowed\` entry let every included tool run, with exact
25098
+ rules instead of a whole-server rule. The file already contains the rules from
25099
+ "Highly Recommended: approve private submissions" for the four private submit
25100
+ and retry tools and the local-only \`capture_learning\`, so do not add those
25101
+ again. To make a tool ask, change its rule to \`decision = "ask_user"\` instead
25102
+ of deleting the rule: an admin-tier ask outranks users' own allow rules, while
25103
+ without an admin rule a user's allow rule decides. A headless run then denies
25104
+ that tool.
25105
+
25106
+ If the organization already restricts servers with \`mcp.allowed\`, Remembrance
25107
+ must be listed there to run, and that entry creates the whole-server allow rule.
25108
+ Then also place this rule in the admin policy folder. It creates no allow rule:
25109
+ admin rules outrank the one \`mcp.allowed\` creates, so a Remembrance tool
25110
+ without its own admin rule asks again, while the per-tool rules above, at
25111
+ priority 200, still win over it for their tools:
25112
+
25113
+ ~~~toml
25114
+ [[rule]]
25115
+ mcpName = "remembrance"
25116
+ decision = "ask_user"
25117
+ priority = 100
25118
+ ~~~
25119
+
24602
25120
  System settings live at \`/Library/Application Support/GeminiCli/settings.json\`
24603
25121
  on macOS, \`/etc/gemini-cli/settings.json\` on Linux, and
24604
25122
  \`C:\\ProgramData\\gemini-cli\\settings.json\` on Windows. Also allow
24605
25123
  \`remembrance.dev\` in the organization's egress policy. Restart Gemini CLI,
24606
25124
  inspect the registered server, then run \`run_connection_doctor\` and require
24607
25125
  organization scope before contribution work. See the official
24608
- [enterprise configuration](https://google-gemini.github.io/gemini-cli/docs/cli/enterprise.html)
24609
- and [MCP settings](https://google-gemini.github.io/gemini-cli/docs/tools/mcp-server.html).
25126
+ [enterprise configuration](https://geminicli.com/docs/cli/enterprise/),
25127
+ [MCP settings](https://geminicli.com/docs/tools/mcp-server/), and
25128
+ [policy engine](https://geminicli.com/docs/reference/policy-engine/).
24610
25129
 
24611
25130
  ### Approve OpenClaw
24612
25131
 
@@ -24632,7 +25151,7 @@ MCP tools, and \`tools.deny: ["bundle-mcp"]\` disables them:
24632
25151
  "remembrance": {
24633
25152
  "toolFilter": {
24634
25153
  "include": [
24635
- ${recommendedLocalOrganizationToolJson}
25154
+ ${openClawToolFilterJson}
24636
25155
  ]
24637
25156
  }
24638
25157
  }
@@ -24641,8 +25160,17 @@ ${recommendedLocalOrganizationToolJson}
24641
25160
  }
24642
25161
  ~~~
24643
25162
 
24644
- Use a normal \`coding\` or \`messaging\` tool profile. Then verify cold config,
24645
- the active Gateway plugin, and live MCP capabilities separately:
25163
+ Use a normal \`coding\` or \`messaging\` tool profile; both already include
25164
+ \`bundle-mcp\`, so the MCP tools need no \`tools.allow\` or \`tools.alsoAllow\`
25165
+ entry. The plugin asks before each private submission. For unattended runs,
25166
+ have each user answer allow-always once per lane. On Remembrance for OpenClaw
25167
+ 0.2.3 or later only, you can instead pre-set both private lanes by merging the
25168
+ \`privateLearningEgressApprovals\` object from "Highly Recommended: approve
25169
+ private submissions" into \`plugins.entries.remembrance.config\`. Earlier
25170
+ versions reject that key and the Gateway then refuses to start, so check the
25171
+ installed version first and keep allow-always on older or mixed-version
25172
+ fleets. Then verify cold config, the active Gateway plugin, and live MCP
25173
+ capabilities separately:
24646
25174
 
24647
25175
  ~~~bash
24648
25176
  openclaw plugins inspect remembrance --runtime --json
@@ -24677,18 +25205,27 @@ approved secret source, not in \`policy.jsonc\`. After the probes, run
24677
25205
 
24678
25206
  For local agents, publish Remembrance in the team marketplace and choose
24679
25207
  \`Required\` or \`Default On\`. For cloud agents, also register the exact
24680
- Remembrance endpoint under **Dashboard > Integrations & MCP** so the same Team
24681
- MCP is distributed across cloud agents, the Agents window, IDE, and CLI. Add
24682
- \`remembrance.dev\` to the enterprise sandbox network allowlist for command-
24683
- based fallback paths.
25208
+ Remembrance endpoint as a Team MCP server under **Dashboard > Plugins & MCPs**
25209
+ so the same Team MCP is distributed across cloud agents, the Agents window,
25210
+ IDE, and CLI. Add \`remembrance.dev\` to the enterprise sandbox network
25211
+ allowlist for command-based fallback paths.
24684
25212
 
24685
- Cursor's current public enterprise documentation does not define a managed
24686
- per-MCP-tool allowlist equivalent to Codex, Claude Code, Gemini CLI, or
24687
- OpenClaw. Keep Cursor's normal tool approvals enabled and do not claim an
24688
- undocumented control exists. Run \`run_connection_doctor\` and require
24689
- organization scope on every enabled surface, then verify query, invocation,
24690
- feedback, and private-contribution receipts; local plugin hooks and cloud-agent
24691
- hooks can differ. See the official
25213
+ Cursor's managed per-tool allowlist is \`~/.cursor/permissions.json\`,
25214
+ distributed through MDM: ship the five \`mcpAllowlist\` entries from "Highly
25215
+ Recommended: approve private submissions", the four private submit and retry
25216
+ tools and the local-only \`capture_learning\`. A distributed file replaces each
25217
+ user's file and its in-app MCP allowlist, so merge these entries with every
25218
+ other entry and key the organization relies on, such as \`terminalAllowlist\`,
25219
+ \`approvalMode\`, and \`autoReview\`. A team Run Mode configuration overrides
25220
+ local \`permissions.json\` files, and the per-server tools field under
25221
+ **Team Settings > MCP Configuration** limits which tools may run but does not
25222
+ run them without asking. Keep the Auto-review or Allowlist Run Mode rather
25223
+ than Run Everything. Run \`run_connection_doctor\` and
25224
+ require organization scope on every enabled surface, then verify query,
25225
+ invocation, feedback, and private-contribution receipts; local plugin hooks and
25226
+ cloud-agent hooks can differ. See the official
25227
+ [permissions file](https://cursor.com/docs/reference/permissions),
25228
+ [Run Modes](https://cursor.com/docs/agent/security/run-modes),
24692
25229
  [team plugin modes](https://cursor.com/changelog/05-01-26),
24693
25230
  [Team MCP distribution](https://cursor.com/changelog/team-marketplace-updates),
24694
25231
  [sandbox network controls](https://cursor.com/changelog/2-5), and
@@ -24698,12 +25235,15 @@ hooks can differ. See the official
24698
25235
 
24699
25236
  Register exactly \`https://remembrance.dev/api/mcp\` or the exact local
24700
25237
  \`npx @remembrance-ai/mcp-server\` command. Use the recommended organization
24701
- tool list above when the client supports tool filtering. Keep normal approval
24702
- behavior for non-read-only calls unless unattended organization-private
24703
- contribution is explicitly approved. A client with no server/tool policy must
24704
- use its existing destination control or the zero-network handoff. Supply the
25238
+ tool list above when the client supports tool filtering. Supply the
24705
25239
  organization key through the client's secret/header mechanism, then run
24706
25240
  \`run_connection_doctor\` and require organization scope before private writes.
25241
+ Keep normal approval behavior for other non-read-only calls, and persist
25242
+ approval only for the four private submit and retry tools (on hosted MCP, the
25243
+ lesson submit), plus the local-only \`capture_learning\` on a local server that
25244
+ asks before it, as in the Highly Recommended step. A client with no
25245
+ server/tool policy must use its existing destination control or the
25246
+ zero-network handoff.
24707
25247
 
24708
25248
  If any host still denies the export, that denial remains authoritative. The
24709
25249
  portable local handoff and dashboard import work identically for Codex, Claude
@@ -24813,6 +25353,13 @@ not inherit shell exports:
24813
25353
  }
24814
25354
  ~~~
24815
25355
 
25356
+ A server you register yourself under the name \`remembrance\`, local or hosted,
25357
+ takes the standalone approval forms from "Highly Recommended: approve private
25358
+ submissions": \`[mcp_servers.remembrance.tools.<tool>]\` tables in Codex,
25359
+ \`mcp__remembrance__<tool>\` rules in Claude Code (for example after
25360
+ \`claude mcp add\`), and \`remembrance:<tool>\` entries in Cursor. A hosted server
25361
+ needs only \`submit_private_lesson_candidate\`.
25362
+
24816
25363
  ## Skill-only install
24817
25364
 
24818
25365
  For skills.sh-compatible runtimes (or any Agent Skills provider) that can load
@@ -24895,14 +25442,20 @@ session skills.
24895
25442
  \`capture_learning.lesson\` followed by the exact returned action and a
24896
25443
  private draft receipt. On hosted/legacy MCP, require a \`failure_report\`
24897
25444
  through submit_remembrance. This validates both capability states without
24898
- mixing their schemas.
25445
+ mixing their schemas. With an organization key and the Highly Recommended
25446
+ approval in place, neither the capture nor the private submission may
25447
+ prompt. A prompt for a submit or retry tool means its approval is missing or
25448
+ uses the wrong tool names; one for \`capture_learning\` means only that its
25449
+ local-only entry is missing, and is expected where the organization leaves
25450
+ the capture to Auto-review or a person, as managed Codex may.
24899
25451
  12. If using an org key, list and invoke an org-only skill or private overlay
24900
25452
  that should not appear anonymously.
24901
25453
  13. With an organization submission key, submit one disposable redacted skill.
24902
25454
  Current local MCP must use \`capture_learning.private_skill\` followed by
24903
25455
  submit_private_skill_candidate; hosted/legacy MCP must use
24904
25456
  propose_private_skill. Verify it appears only in that organization's review
24905
- queue. In a separate negative scenario, simulate a
25457
+ queue and, on local MCP with the approval in place, that it needed no host
25458
+ prompt. In a separate negative scenario, simulate a
24906
25459
  host-policy denial and verify the plugin reports the fixed content-free alert
24907
25460
  once, persists no blocked content, performs no retry, and creates no handoff.
24908
25461
  Test queue_private_skill_import only as a separate administrator-requested
@@ -24962,6 +25515,27 @@ session skills.
24962
25515
  --probe", and restart OpenClaw after plugin install/config changes.
24963
25516
  - "Claude desktop ignores env vars": put env in the user-scoped Claude settings
24964
25517
  that the desktop app reads, then fully quit and relaunch.
25518
+ - "Every private submission asks for approval" or "a headless run denies
25519
+ private submissions": report the blocked submission once as not submitted.
25520
+ Then ask the user (for managed settings, the administrator) to apply or fix
25521
+ the Highly Recommended approval; never change host settings or answer the
25522
+ host's approval yourself, and only after they confirm, send the retained
25523
+ draft through its exact submit or retry action.
25524
+ \`inspect_private_learning_outbox\` lists each draft with its \`state\`,
25525
+ \`attempts\`, and \`next_retry_at\`: a draft the host stopped before sending,
25526
+ a lesson still \`awaiting_authorization\` with 0 attempts or a skill still
25527
+ \`ready\`, needs its submit action, and a \`retry_scheduled\` draft its retry
25528
+ action once \`next_retry_at\` has passed. A prompt or denial naming a
25529
+ submit or retry tool means its approval is missing or names the wrong tools:
25530
+ a Claude Code plugin install needs
25531
+ \`mcp__plugin_remembrance_remembrance__<tool>\` rules, not
25532
+ \`mcp__remembrance__<tool>\`. One naming \`capture_learning\` means only that
25533
+ its local-only entry is missing, and is expected where the organization
25534
+ leaves the capture to Auto-review or a person, as managed Codex may: under
25535
+ managed Codex \`writes\`, approving \`capture_learning\` is an optional
25536
+ administrator decision (with it, Auto-review never sees the lesson content),
25537
+ not a fix to ask for. \`run_connection_doctor\` cannot see a persisted
25538
+ approval.
24965
25539
  - "Host policy denied private repository export": this is not a Remembrance
24966
25540
  API failure. An administrator must narrowly approve the Remembrance MCP
24967
25541
  server, destination, and private contribution action. Otherwise use the
@@ -25946,6 +26520,34 @@ var verifierOutputSchema = external_exports.object({
25946
26520
  topology_assessment: skillTopologyAssessmentSchema.nullable().default(null).catch(null)
25947
26521
  });
25948
26522
 
26523
+ // src/private-skill-protection-recovery.ts
26524
+ var PRIVATE_SKILL_CONFIGURATION_REFUSAL = "private_admission_configuration_error";
26525
+ var PRIVATE_SKILL_PERMANENT_SERVICE_REFUSAL = "private_skill_permanent_service_refusal";
26526
+ function isPrivateSkillConfigurationHold(draft) {
26527
+ return draft.state === "held_validation" && draft.last_error_code === PRIVATE_SKILL_CONFIGURATION_REFUSAL;
26528
+ }
26529
+ function privateSkillPermanentRefusalGuidance(draft) {
26530
+ if (isPrivateSkillConfigurationHold(draft)) {
26531
+ return {
26532
+ next_action: null,
26533
+ action_required: "Private protection configuration refused this submission. Keep the exact encrypted draft. Ask the organization administrator to correct the selected protection configuration; customer-held submissions require a valid customer-encrypted envelope. Do not change key ownership, protection mode, destination, or consent automatically. Only after an authorized correction, explicitly retry this same draft once with retry_private_skill_candidate. An unchanged refusal remains held; no replacement or acceptance is claimed.",
26534
+ recovery_after_correction: {
26535
+ automatic_retry: false,
26536
+ condition: "organization_administrator_corrected_private_protection_configuration",
26537
+ tool: "retry_private_skill_candidate",
26538
+ arguments: { draft_id: draft.draft_id }
26539
+ }
26540
+ };
26541
+ }
26542
+ if (draft.state === "held_validation" && draft.last_error_code === PRIVATE_SKILL_PERMANENT_SERVICE_REFUSAL) {
26543
+ return {
26544
+ next_action: null,
26545
+ action_required: "The private service explicitly refused unchanged retries. Keep the encrypted draft and inspect this retained hold. No automatic retry, alternate transport, replacement, or successful recovery is authorized by this unknown refusal."
26546
+ };
26547
+ }
26548
+ return {};
26549
+ }
26550
+
25949
26551
  // src/server.ts
25950
26552
  import { existsSync as existsSync6 } from "node:fs";
25951
26553
  import { mkdir as mkdir5, writeFile as writeFile2 } from "node:fs/promises";
@@ -27658,7 +28260,8 @@ var pendingActionDeclineSchema = external_exports.object({
27658
28260
  "not_observed_by_this_agent",
27659
28261
  "agent_exited",
27660
28262
  "not_reusable",
27661
- "duplicate"
28263
+ "duplicate",
28264
+ "host_policy_denied"
27662
28265
  ]).describe("Why this agent cannot complete the action.")
27663
28266
  }).strict();
27664
28267
  var pendingActionPayloadSchema = external_exports.object({
@@ -27793,7 +28396,7 @@ var declaredToolDefinitions = [
27793
28396
  ),
27794
28397
  localTool(
27795
28398
  "decline_pending_action",
27796
- "Decline one pending Remembrance action that a hook reminder listed and you cannot complete. Pass the action_id shown under it and one reason: not_observed_by_this_agent (you never received the result or payload it refers to), agent_exited (the subagent that earned it has exited), not_reusable (it holds nothing reusable), or duplicate (it was already completed another way). It is local-only: it sends nothing, stores nothing, grants no approval, and never counts as feedback. The local completion hook closes that action in your own pending list when it observes this call.",
28399
+ "Decline one pending Remembrance action that a hook reminder listed and you cannot complete. Pass the action_id shown under it and one reason: not_observed_by_this_agent (you never received the result or payload it refers to), agent_exited (the subagent that earned it has exited), not_reusable (it holds nothing reusable), or duplicate (it was already completed another way); host_policy_denied only for a private-learning line or notice whose exact action the host denied. It is local-only: it sends nothing, stores nothing, grants no approval, and never counts as feedback. The local completion hook closes that action in your own pending list when it observes this call; a declined private draft stays retained and returns later only as a user decision.",
27797
28400
  "decline_pending_action",
27798
28401
  pendingActionDeclineSchema
27799
28402
  ),
@@ -27862,7 +28465,7 @@ var declaredToolDefinitions = [
27862
28465
  ),
27863
28466
  localNetworkTool(
27864
28467
  "retry_private_skill_candidate",
27865
- "Retry one retained private-skill draft through the same private_skill approval lane as submit_private_skill_candidate. Network access is allowed only for authorization-pending or due transient retry states, and for a draft an older server refused only for length: that waits out any delay the server set, then reads its signed capability and resends the exact draft only if it admits it. A fresh ready draft is rejected locally. It never switches transport or visibility.",
28468
+ "Retry one retained private-skill draft through the same private_skill approval lane as submit_private_skill_candidate. Network access is allowed only for authorization-pending or due transient retry states, an explicit same-draft retry after an administrator corrected a private-protection configuration hold, and for a draft an older server refused only for length: that waits out any delay the server set, then reads its signed capability and resends the exact draft only if it admits it. Configuration holds never schedule automatic retries; an unchanged refusal remains held. A fresh ready draft is rejected locally. It never switches transport, visibility, key ownership, or protection mode.",
27866
28469
  "retry_private_skill_candidate",
27867
28470
  privateSkillDraftReferenceSchema,
27868
28471
  "content_write"
@@ -42844,27 +43447,27 @@ var require_stringifyString = __commonJS({
42844
43447
  return true;
42845
43448
  }
42846
43449
  function doubleQuotedString(value, ctx) {
42847
- const json = JSON.stringify(value);
43450
+ const json2 = JSON.stringify(value);
42848
43451
  if (ctx.options.doubleQuotedAsJSON)
42849
- return json;
43452
+ return json2;
42850
43453
  const { implicitKey } = ctx;
42851
43454
  const minMultiLineLength = ctx.options.doubleQuotedMinMultiLineLength;
42852
43455
  const indent = ctx.indent || (containsDocumentMarker(value) ? " " : "");
42853
43456
  let str = "";
42854
43457
  let start = 0;
42855
- for (let i = 0, ch = json[i]; ch; ch = json[++i]) {
42856
- if (ch === " " && json[i + 1] === "\\" && json[i + 2] === "n") {
42857
- str += json.slice(start, i) + "\\ ";
43458
+ for (let i = 0, ch = json2[i]; ch; ch = json2[++i]) {
43459
+ if (ch === " " && json2[i + 1] === "\\" && json2[i + 2] === "n") {
43460
+ str += json2.slice(start, i) + "\\ ";
42858
43461
  i += 1;
42859
43462
  start = i;
42860
43463
  ch = "\\";
42861
43464
  }
42862
43465
  if (ch === "\\")
42863
- switch (json[i + 1]) {
43466
+ switch (json2[i + 1]) {
42864
43467
  case "u":
42865
43468
  {
42866
- str += json.slice(start, i);
42867
- const code = json.substr(i + 2, 4);
43469
+ str += json2.slice(start, i);
43470
+ const code = json2.substr(i + 2, 4);
42868
43471
  switch (code) {
42869
43472
  case "0000":
42870
43473
  str += "\\0";
@@ -42894,23 +43497,23 @@ var require_stringifyString = __commonJS({
42894
43497
  if (code.substr(0, 2) === "00")
42895
43498
  str += "\\x" + code.substr(2);
42896
43499
  else
42897
- str += json.substr(i, 6);
43500
+ str += json2.substr(i, 6);
42898
43501
  }
42899
43502
  i += 5;
42900
43503
  start = i + 1;
42901
43504
  }
42902
43505
  break;
42903
43506
  case "n":
42904
- if (implicitKey || json[i + 2] === '"' || json.length < minMultiLineLength) {
43507
+ if (implicitKey || json2[i + 2] === '"' || json2.length < minMultiLineLength) {
42905
43508
  i += 1;
42906
43509
  } else {
42907
- str += json.slice(start, i) + "\n\n";
42908
- while (json[i + 2] === "\\" && json[i + 3] === "n" && json[i + 4] !== '"') {
43510
+ str += json2.slice(start, i) + "\n\n";
43511
+ while (json2[i + 2] === "\\" && json2[i + 3] === "n" && json2[i + 4] !== '"') {
42909
43512
  str += "\n";
42910
43513
  i += 2;
42911
43514
  }
42912
43515
  str += indent;
42913
- if (json[i + 2] === " ")
43516
+ if (json2[i + 2] === " ")
42914
43517
  str += "\\";
42915
43518
  i += 1;
42916
43519
  start = i + 1;
@@ -42920,7 +43523,7 @@ var require_stringifyString = __commonJS({
42920
43523
  i += 1;
42921
43524
  }
42922
43525
  }
42923
- str = start ? str + json.slice(start) : json;
43526
+ str = start ? str + json2.slice(start) : json2;
42924
43527
  return implicitKey ? str : foldFlowLines.foldFlowLines(str, indent, foldFlowLines.FOLD_QUOTED, getFoldOptions(ctx, false));
42925
43528
  }
42926
43529
  function singleQuotedString(value, ctx) {
@@ -45278,11 +45881,11 @@ var require_Document = __commonJS({
45278
45881
  throw new Error(`With a null YAML version, the { schema: Schema } option is required`);
45279
45882
  }
45280
45883
  // json & jsonArg are only used from toJSON()
45281
- toJS({ json, jsonArg, mapAsMap, maxAliasCount, onAnchor, reviver } = {}) {
45884
+ toJS({ json: json2, jsonArg, mapAsMap, maxAliasCount, onAnchor, reviver } = {}) {
45282
45885
  const ctx = {
45283
45886
  anchors: /* @__PURE__ */ new Map(),
45284
45887
  doc: this,
45285
- keep: !json,
45888
+ keep: !json2,
45286
45889
  mapAsMap: mapAsMap === true,
45287
45890
  mapKeyWarned: false,
45288
45891
  maxAliasCount: typeof maxAliasCount === "number" ? maxAliasCount : 100
@@ -61932,7 +62535,8 @@ async function reinspectPrivateSkillSubmissionCandidate(draftId, options = {}) {
61932
62535
  const resumingReinspectionHold = isPrivateSkillSubmissionReinspectionHold(current);
61933
62536
  const resumingDestinationHold = isPrivateSkillDestinationHold(current) && !isPrivateSkillDestinationRecoveryBlocked(current, destination);
61934
62537
  const resumingResolution = current.delivery_resolution !== null;
61935
- const resumingHold = resumingReinspectionHold || resumingDestinationHold || resumingResolution;
62538
+ const resumingConfigurationHold = options.resumeConfigurationHold === true && isPrivateSkillConfigurationHold(current);
62539
+ const resumingHold = resumingReinspectionHold || resumingDestinationHold || resumingResolution || resumingConfigurationHold;
61936
62540
  if (resumingReinspectionHold && !resumingResolution && current.inspection?.inspector_digest === PRIVATE_LEARNING_INSPECTOR_DIGEST && current.inspection.outcome !== "unchanged" && current.inspection.outcome !== "redacted") {
61937
62541
  return Object.freeze({ status: "held", draft: current });
61938
62542
  }
@@ -64799,6 +65403,7 @@ async function inspectPrivateLearningOutbox(input, persistence) {
64799
65403
  status: skillResult.value.recovery_items.length > 0 ? "recovery_required" : "ok",
64800
65404
  drafts: skillResult.value.drafts.map((draft) => ({
64801
65405
  ...draft,
65406
+ ...privateSkillPermanentRefusalGuidance(draft),
64802
65407
  recovery_context: { status: "context_unavailable" },
64803
65408
  agent_recovery: privateSkillExactInspectionRecovery(draft)
64804
65409
  })),
@@ -65104,6 +65709,7 @@ function privateSkillSelectedDraftResult(draft) {
65104
65709
  draft,
65105
65710
  PRIVATE_LEARNING_CAPTURE_VERSION
65106
65711
  ),
65712
+ ...privateSkillPermanentRefusalGuidance(draft),
65107
65713
  recovery_context: recoveryContext,
65108
65714
  native_recovery_context: nativeContext ? {
65109
65715
  ...nativeContext,
@@ -67339,8 +67945,8 @@ function inspectPrivateLearningOutbox2(input, dependencies = {}) {
67339
67945
  }
67340
67946
  var MAX_REMOTE_RESPONSE_BYTES = 4 * 1024 * 1024;
67341
67947
  var DOCTOR_PROBE_TIMEOUT_MS = 7500;
67342
- var SERVER_VERSION = true ? "0.2.2" : "0.0.0-dev";
67343
- var tools = toolDefinitions;
67948
+ var SERVER_VERSION = true ? "0.2.4" : "0.0.0-dev";
67949
+ var tools2 = toolDefinitions;
67344
67950
  var doctorCliRequested = process.argv[2] === "doctor";
67345
67951
  var nativeContextCliRequested = process.argv[2] === "--native-context";
67346
67952
  function legacyBroadWritesEnabled(env = process.env) {
@@ -68041,6 +68647,10 @@ async function deliverPrivateSkillDraft(draftId, access, mode, transport) {
68041
68647
  });
68042
68648
  }
68043
68649
  let draft = await readPrivateSkillDraft(draftId);
68650
+ const resumingConfigurationHold = mode === "retry" && isPrivateSkillConfigurationHold(draft);
68651
+ if (isPrivateSkillConfigurationHold(draft) && !resumingConfigurationHold) {
68652
+ return privateSkillDraftActionResult2(draft, { status: "not_submitted" });
68653
+ }
68044
68654
  const destination = privateSkillDestinationForAccess(access);
68045
68655
  const resumingReinspectionHold = isPrivateSkillSubmissionReinspectionHold(draft);
68046
68656
  const resumingDestinationHold = isPrivateSkillDestinationHold(draft) && !isPrivateSkillDestinationRecoveryBlocked(draft, destination);
@@ -68053,10 +68663,10 @@ async function deliverPrivateSkillDraft(draftId, access, mode, transport) {
68053
68663
  next_action: null
68054
68664
  });
68055
68665
  }
68056
- const resumingLocalHold = resumingReinspectionHold || resumingDestinationHold || resumingResolution;
68666
+ const resumingLocalHold = resumingReinspectionHold || resumingDestinationHold || resumingResolution || resumingConfigurationHold;
68057
68667
  const deferredDestinationProof = draft.state === "retry_scheduled" && draft.last_error_code === "private_skill_destination_audience_proof_unavailable";
68058
68668
  const awaitingDestinationCapability = isPrivateSkillDestinationCapabilityHold(draft) && !resumingResolution;
68059
- if ((mode === "retry" && !resumingResolution || deferredDestinationProof || resumingResolution && draft.state === "retry_scheduled") && !resumingReinspectionHold && !resumingDestinationHold && !awaitingDestinationCapability) {
68669
+ if ((mode === "retry" && !resumingResolution || deferredDestinationProof || resumingResolution && draft.state === "retry_scheduled") && !resumingReinspectionHold && !resumingDestinationHold && !resumingConfigurationHold && !awaitingDestinationCapability) {
68060
68670
  const retryGate = privateSkillRetryGate(draft);
68061
68671
  if (retryGate) {
68062
68672
  return privateSkillDraftActionResult2(draft, retryGate);
@@ -68085,7 +68695,8 @@ async function deliverPrivateSkillDraft(draftId, access, mode, transport) {
68085
68695
  });
68086
68696
  }
68087
68697
  const reinspection = await reinspectPrivateSkillSubmissionCandidate(draftId, {
68088
- destination
68698
+ destination,
68699
+ resumeConfigurationHold: resumingConfigurationHold
68089
68700
  });
68090
68701
  draft = reinspection.draft;
68091
68702
  if (reinspection.status === "held") {
@@ -68398,6 +69009,16 @@ async function holdPrivateSkillDestination(draft, errorCode) {
68398
69009
  async function persistPrivateSkillFailure(draft, result, destinationBodyLimit) {
68399
69010
  return updatePrivateSkillDraft(draft.draft_id, (current) => {
68400
69011
  const attempts = current.attempts;
69012
+ const failure = remembranceMcpToolFailure(result, "propose_private_skill");
69013
+ if (failure && failure.http_status >= 500 && failure.http_status <= 599 && !failure.retryable) {
69014
+ return {
69015
+ ...current,
69016
+ state: "held_validation",
69017
+ next_retry_at: null,
69018
+ last_error_code: failure.code === PRIVATE_SKILL_CONFIGURATION_REFUSAL ? PRIVATE_SKILL_CONFIGURATION_REFUSAL : PRIVATE_SKILL_PERMANENT_SERVICE_REFUSAL,
69019
+ updated_at: (/* @__PURE__ */ new Date()).toISOString()
69020
+ };
69021
+ }
68401
69022
  if (destinationBodyLimit !== null) {
68402
69023
  const nextRetryAt = privateSkillRetryAt(attempts);
68403
69024
  return {
@@ -68726,7 +69347,8 @@ function privateSkillDraftActionResult2(draft, extra = {}) {
68726
69347
  last_error_code: draft.last_error_code,
68727
69348
  retained_locally: true,
68728
69349
  next_action: privateSkillDraftNextAction(draft),
68729
- ...extra
69350
+ ...extra,
69351
+ ...privateSkillPermanentRefusalGuidance(draft)
68730
69352
  };
68731
69353
  }
68732
69354
  function privateSkillDraftNextAction(draft) {
@@ -71183,7 +71805,7 @@ function boundedLocalProfileField(value) {
71183
71805
  return normalized2 ? normalized2.slice(0, 512) : null;
71184
71806
  }
71185
71807
  function mustFindTool(name) {
71186
- const tool = tools.find((item) => item.name === name);
71808
+ const tool = tools2.find((item) => item.name === name);
71187
71809
  if (!tool) {
71188
71810
  throw new Error(`Missing internal tool definition: ${name}`);
71189
71811
  }