@remembrance-ai/opencode-plugin 0.1.74 → 0.1.76

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.
@@ -5570,9 +5570,9 @@ function builtInPreferenceDefinition(key) {
5570
5570
 
5571
5571
  // ../core/src/agent-guidance.ts
5572
5572
  var REMEMBRANCE_QUERY_TOOL_DESCRIPTION = "Call before non-trivial service, API, tool, library, workflow, UI, review, test, security, or deployment work to find relevant Remembrance skills and resources. For short context-dependent follow-ups, infer the concrete task from the full conversation, preserve any plugin-supplied client_context.directive_id/runtime/trigger_reason, and query anyway. Compare each candidate's bounded why_matched evidence and applicability conditions before opening it; discard stated unlikely or corner-case mismatches. High matches should be fetched before custom work; possible and exploratory matches remain optional. Do not use for broad web search or one-off facts.";
5573
- var REMEMBRANCE_CONNECTION_STATUS_TOOL_DESCRIPTION = "Inspect the active Remembrance transport, credential source, and authenticated registry scope without exposing credentials. Call this before diagnosing authentication, checking environment variables, or making anonymous REST/browser probes. Local or bundled MCP can read REMEMBRANCE_API_KEY or ~/.config/remembrance/config.json; hosted MCP cannot read local files and authenticates from its request header. When a declared native lifecycle is degraded, local MCP may submit bounded, content-free health evidence unless REMEMBRANCE_HEALTH_REPORTING=0. Never infer the plugin's scope from REMEMBRANCE_API_KEY alone.";
5573
+ var REMEMBRANCE_CONNECTION_STATUS_TOOL_DESCRIPTION = "Inspect the active Remembrance transport, credential source, authenticated registry scope, and content-free recent contribution outcomes for the current installation or API key without exposing credentials or lesson content. Call this before diagnosing authentication, checking environment variables, or making anonymous REST/browser probes. Local or bundled MCP can read REMEMBRANCE_API_KEY or ~/.config/remembrance/config.json; hosted MCP cannot read local files and authenticates from its request header. When a declared native lifecycle is degraded, local MCP may submit bounded, content-free health evidence unless REMEMBRANCE_HEALTH_REPORTING=0. Never infer the plugin's scope from REMEMBRANCE_API_KEY alone.";
5574
5574
  var REMEMBRANCE_CONNECTION_DOCTOR_TOOL_DESCRIPTION = "Run a bounded, non-mutating Remembrance connection diagnostic. It verifies the active transport, a redacted registry destination, authenticated scope, query authorization, a real catalog read, and organization submission capability. Local MCP also checks shared-config permissions, destination consistency, native plugin lifecycle, and signing identity; hosted MCP marks local-only checks unavailable. It never submits content, creates query demand, opens a review item, or returns credentials, absolute paths, private registry URLs, prompts, or repository data.";
5575
- var REMEMBRANCE_MCP_SERVER_INSTRUCTIONS = "Remembrance is shared operational memory for agents. Before claiming that Remembrance is anonymous, unconfigured, partially active, or missing an organization key, call run_connection_doctor for the transport you will actually use. It performs a safe catalog read and returns exact bounded remediation without submitting content. Use get_connection_status only when you need the underlying transport and lifecycle fields. An unset REMEMBRANCE_API_KEY does not prove that a local plugin is anonymous because local hooks and bundled MCP may read ~/.config/remembrance/config.json. Conversely, hosted MCP cannot read that local file. Anonymous curl or browser probes describe only those requests, not plugin authentication. Never print or request the raw key. When the user explicitly names an authorized Remembrance skill or supplies a remembrance://skills/{slug} URI, resolve ambiguous names with list_skills using its normalized slug-prefix filter, then call invoke_skill with an exact returned slug. Never guess a slug. list_skills is catalog resolution, not relevance search; use query_skills when the user wants relevant candidates rather than a known selection. invoke_skill rechecks authorization and organization policy, resolves the current reviewed version, and returns the only full instruction body. Catalog listings and MCP resource reads are lightweight handles, not skill use. Do not run query_skills merely to rediscover that explicit selection, and do not submit query-fit feedback for it. After meaningful direct use, follow the returned task-outcome and post-use feedback instructions once. BEFORE non-trivial service/API/tool/library/workflow/UI/review/test/security/deployment work, call query_skills to reuse proven skills and resources instead of rediscovering them. A local repository change still qualifies when it involves a reusable workflow or lesson; skip only genuinely trivial throwaway edits and one-off facts. For short follow-ups such as 'fix these issues', 'continue', or 'try again', infer the concrete task, domain, and constraints from the full conversation and still call query_skills; do not wait for the current prompt to repeat trigger keywords. Treat match_tier as a decision aid, then inspect why_matched and applicability before opening a result. These fields show bounded matched terms/capabilities, satisfied and missed constraints, qualitative lexical/semantic evidence, declared scope, use conditions, and exclusions without exposing unstable raw ranking scores. Discard an unlikely or irrelevant corner-case result and report fit: poor; do not force its use. For a remaining high match, call get_skill or get_resource before custom work and pass the returned query_id/result_id so the surfaced-to-fetch funnel closes; possible and exploratory matches are optional. The response also includes approximate context tokens, verified uses, risk, tags, permissions, dependencies, and contraindications so you can weigh the detour safely. Honor query_skills.skill_access on every response. When policy is org_only, use only returned organization skills and never substitute bundled or live public skill references; if an organization key is configured and the query is unavailable, fail closed because the policy cannot be confirmed. AFTER query_skills, call submit_query_feedback once with one complete set of explicit good/partial/poor judgments using the returned query_id and result_id values; leave unrated results neutral, and remember that a poor query match is not the same as a globally bad skill. Reuse the same organization or anonymous auth scope as the query. AFTER you actually use a skill or resource, close the post-use loop with submit_feedback (useful true/false plus a one-line lesson and the originating query_id/result_id; it returns a ready submit_remembrance payload), then submit_remembrance if the lesson is reusable. If nothing fit and you built a new workflow, use propose_private_skill for an organization-private candidate. With an organization key, propose_skill_idea also remains organization-private. Never remove, suppress, or bypass that key to force a public submission; submit privately, then use the reviewed public-propagation flow when the organization chooses to share it. When delegating, pass the selected slug, exact version, query_id, and result_id to the subagent; it should invoke/fetch that result or run its own full-context query. The parent reports the terminal outcome unless the subagent creates its own invocation. Before finishing any reusable task, self-check for a missed query. If you catch your own mistake, the user catches one, CI/deploy fails, a security issue appears, or you fix a release/versioning miss, submit a failure_report remembrance even if no skill was used; raw MCP clients have no plugin Stop hook to remind you later. A host privacy or tenant-policy denial happens before Remembrance receives the request. Tell the user: 'Remembrance was blocked by host policy before reaching Remembrance. Nothing was sent. Querying remains available.' Do not retry the blocked content through another transport or claim submission. Attach evidence (reproduction detail, artifact hashes, or an attestation); evidence-less public reports wait in unverified intake until corroborated. Redact secrets, private URLs, credentials, raw logs, and proprietary content; submit summaries and hashes, not raw traces.";
5575
+ var REMEMBRANCE_MCP_SERVER_INSTRUCTIONS = "Remembrance is shared operational memory for agents. Before claiming that Remembrance is anonymous, unconfigured, partially active, or missing an organization key, call run_connection_doctor for the transport you will actually use. It performs a safe catalog read and returns exact bounded remediation without submitting content. Use get_connection_status only when you need the underlying transport and lifecycle fields. An unset REMEMBRANCE_API_KEY does not prove that a local plugin is anonymous because local hooks and bundled MCP may read ~/.config/remembrance/config.json. Conversely, hosted MCP cannot read that local file. Anonymous curl or browser probes describe only those requests, not plugin authentication. Never print or request the raw key. When the user explicitly names an authorized Remembrance skill or supplies a remembrance://skills/{slug} URI, resolve ambiguous names with list_skills using its normalized slug-prefix filter, then call invoke_skill with an exact returned slug. Never guess a slug. list_skills is catalog resolution, not relevance search; use query_skills when the user wants relevant candidates rather than a known selection. invoke_skill rechecks authorization and organization policy, resolves the current reviewed version, and returns the only full instruction body. Catalog listings and MCP resource reads are lightweight handles, not skill use. Do not run query_skills merely to rediscover that explicit selection, and do not submit query-fit feedback for it. After meaningful direct use, follow the returned task-outcome and post-use feedback instructions once. BEFORE non-trivial service/API/tool/library/workflow/UI/review/test/security/deployment work, call query_skills to reuse proven skills and resources instead of rediscovering them. A local repository change still qualifies when it involves a reusable workflow or lesson; skip only genuinely trivial throwaway edits and one-off facts. For short follow-ups such as 'fix these issues', 'continue', or 'try again', infer the concrete task, domain, and constraints from the full conversation and still call query_skills; do not wait for the current prompt to repeat trigger keywords. Treat match_tier as a decision aid, then inspect why_matched and applicability before opening a result. These fields show bounded matched terms/capabilities, satisfied and missed constraints, qualitative lexical/semantic evidence, declared scope, use conditions, and exclusions without exposing unstable raw ranking scores. Discard an unlikely or irrelevant corner-case result and report fit: poor; do not force its use. For a remaining high match, call get_skill or get_resource before custom work and pass the returned query_id/result_id so the surfaced-to-fetch funnel closes; possible and exploratory matches are optional. The response also includes approximate context tokens, verified uses, risk, tags, permissions, dependencies, and contraindications so you can weigh the detour safely. Honor query_skills.skill_access on every response. When policy is org_only, use only returned organization skills and never substitute bundled or live public skill references; if an organization key is configured and the query is unavailable, fail closed because the policy cannot be confirmed. AFTER query_skills, call submit_query_feedback once with one complete set of explicit good/partial/poor judgments using the returned query_id and result_id values; leave unrated results neutral, and remember that a poor query match is not the same as a globally bad skill. Reuse the same organization or anonymous auth scope as the query. AFTER you actually use a skill or resource, close the post-use loop with submit_feedback (useful true/false plus a one-line lesson and the originating query_id/result_id; it returns a ready submit_remembrance payload), then submit_remembrance if the lesson is reusable. If nothing fit and you built a new workflow, use propose_private_skill for an organization-private candidate. With an organization key, propose_skill_idea also remains organization-private. Never remove, suppress, or bypass that key to force a public submission; submit privately, then use the reviewed public-propagation flow when the organization chooses to share it. When delegating, pass the selected slug, exact version, query_id, and result_id to the subagent; it should invoke/fetch that result or run its own full-context query. The parent reports the terminal outcome unless the subagent creates its own invocation. Route memory deliberately: host-local memory is for facts about this human, repository, or machine; Remembrance is for generalized lessons another approved organization agent would otherwise rediscover. A local-memory write does not satisfy organization-shared capture. Before finishing any reusable task, self-check for a missed query. If you catch your own mistake, the user catches one, CI/deploy fails, a security issue appears, or you fix a release/versioning miss, submit a failure_report remembrance even if no skill was used; raw MCP clients have no plugin Stop hook to remind you later. A host privacy or tenant-policy denial happens before Remembrance receives the request. Tell the user: 'Remembrance was blocked by host policy before reaching Remembrance. Nothing was sent. Querying remains available.' Do not retry the blocked content through another transport or claim submission. Attach evidence (reproduction detail, artifact hashes, or an attestation); evidence-less public reports wait in unverified intake until corroborated. Redact secrets, private URLs, credentials, raw logs, and proprietary content; submit summaries and hashes, not raw traces.";
5576
5576
 
5577
5577
  // ../core/src/redaction.ts
5578
5578
  var SECRET_PATTERN_SPECS = [
@@ -5731,6 +5731,20 @@ var PRIVATE_LESSON_TAG_IDENTITY_HINTS = /* @__PURE__ */ new Set([
5731
5731
  "tenant",
5732
5732
  "user"
5733
5733
  ]);
5734
+ var PRIVATE_LESSON_TAG_IDENTITY_LABEL_SUFFIXES = /* @__PURE__ */ new Set([
5735
+ "corp",
5736
+ "org",
5737
+ "organization"
5738
+ ]);
5739
+ var PRIVATE_LESSON_TAG_RELATIONAL_PREFIXES = /* @__PURE__ */ new Set([
5740
+ "cross",
5741
+ "inter",
5742
+ "intra",
5743
+ "multi",
5744
+ "per",
5745
+ "single",
5746
+ "sub"
5747
+ ]);
5734
5748
  var privateLessonCanonicalTagSchema = privateLessonTagSyntaxSchema.superRefine((tag, ctx) => {
5735
5749
  const inspected = inspectPrivateLessonTag(tag);
5736
5750
  if (inspected.tag !== tag || inspected.held_reasons.length > 0) {
@@ -6125,8 +6139,14 @@ function computePrivateLessonRedactorDigest() {
6125
6139
  metadata: {
6126
6140
  correlation_ids: "domain-separated-sha256-v1",
6127
6141
  tag_syntax: PRIVATE_LESSON_TAG_PATTERN.source,
6128
- tag_safety: "redaction-plus-identity-hints-v1",
6142
+ tag_safety: "redaction-plus-identity-hints-v2",
6129
6143
  tag_identity_hints: [...PRIVATE_LESSON_TAG_IDENTITY_HINTS].sort(),
6144
+ tag_identity_label_suffixes: [
6145
+ ...PRIVATE_LESSON_TAG_IDENTITY_LABEL_SUFFIXES
6146
+ ].sort(),
6147
+ tag_relational_prefixes: [
6148
+ ...PRIVATE_LESSON_TAG_RELATIONAL_PREFIXES
6149
+ ].sort(),
6130
6150
  tag_aliases: PRIVATE_LESSON_SAFE_TAG_ALIASES
6131
6151
  },
6132
6152
  rules: REPLACEMENTS.map(({ category, pattern, hold }) => ({
@@ -6150,7 +6170,10 @@ function inspectPrivateLessonTag(value) {
6150
6170
  const identityHints = new Set(
6151
6171
  aliased.split(/[-_]/u).filter((segment) => PRIVATE_LESSON_TAG_IDENTITY_HINTS.has(segment))
6152
6172
  );
6153
- if (!privateLessonTagSyntaxSchema.safeParse(aliased).success || redacted.text !== aliased || identityHints.size >= 2) {
6173
+ const segments = aliased.split(/[-_]/u);
6174
+ const isGenericOrganizationRelationship = segments.length === 2 && PRIVATE_LESSON_TAG_RELATIONAL_PREFIXES.has(segments[0] ?? "") && PRIVATE_LESSON_TAG_IDENTITY_LABEL_SUFFIXES.has(segments[1] ?? "");
6175
+ const hasLikelyIdentityLabel = segments.length >= 2 && !isGenericOrganizationRelationship && PRIVATE_LESSON_TAG_IDENTITY_LABEL_SUFFIXES.has(segments.at(-1) ?? "") && segments.slice(0, -1).some((segment) => !PRIVATE_LESSON_TAG_IDENTITY_HINTS.has(segment));
6176
+ if (!privateLessonTagSyntaxSchema.safeParse(aliased).success || redacted.text !== aliased || identityHints.size >= 2 || hasLikelyIdentityLabel) {
6154
6177
  if (held.size === 0) {
6155
6178
  counts.identifier = Math.min(999, (counts.identifier ?? 0) + 1);
6156
6179
  held.add("identifier");
@@ -6177,12 +6200,18 @@ function pseudonymizeCorrelationId(value) {
6177
6200
  return `corr_${digest.slice(0, 48)}`;
6178
6201
  }
6179
6202
  var CURRENT_PRIVATE_LESSON_REDACTOR_DIGEST = computePrivateLessonRedactorDigest();
6203
+ var PRIVATE_LESSON_LEGACY_V2_REDACTOR_DIGEST = "sha256:bca83ac50b0124a0401f806b3498bbb1ec977c02c1975d5345cb8d0b85c00b75";
6180
6204
  var PRIVATE_LESSON_SUPPORTED_REDACTOR_PROFILES = Object.freeze({
6181
6205
  [PRIVATE_LESSON_REDACTION_VERSION]: Object.freeze({
6182
6206
  [CURRENT_PRIVATE_LESSON_REDACTOR_DIGEST]: Object.freeze({
6183
6207
  version: PRIVATE_LESSON_REDACTION_VERSION,
6184
6208
  digest: CURRENT_PRIVATE_LESSON_REDACTOR_DIGEST,
6185
6209
  normalization: "unicode-nfc-lf-space-v1"
6210
+ }),
6211
+ [PRIVATE_LESSON_LEGACY_V2_REDACTOR_DIGEST]: Object.freeze({
6212
+ version: PRIVATE_LESSON_REDACTION_VERSION,
6213
+ digest: PRIVATE_LESSON_LEGACY_V2_REDACTOR_DIGEST,
6214
+ normalization: "unicode-nfc-lf-space-v1"
6186
6215
  })
6187
6216
  })
6188
6217
  });
@@ -7610,7 +7639,7 @@ var seedSkills = [
7610
7639
  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.",
7611
7640
  status: "active",
7612
7641
  visibility: "public",
7613
- version: "0.1.24",
7642
+ version: "0.1.25",
7614
7643
  domains: ["agent-skills", "mcp", "resource-discovery"],
7615
7644
  tags: [
7616
7645
  "remembrance",
@@ -7661,7 +7690,7 @@ var seedSkills = [
7661
7690
  "gemini",
7662
7691
  "rest"
7663
7692
  ],
7664
- version: "0.1.24",
7693
+ version: "0.1.25",
7665
7694
  status: "active",
7666
7695
  visibility: "public",
7667
7696
  providers: ["codex", "claude", "cursor", "openclaw", "generic"],
@@ -8016,8 +8045,12 @@ This is the only host-visible network action in the flow. Approve or persist
8016
8045
  approval for this exact action, not every Remembrance write. The server derives
8017
8046
  the organization and private visibility from the authenticated key, verifies a
8018
8047
  signed organization policy plus purpose-bound attestation, and returns a signed
8019
- content-free receipt. The endpoint can never create or automatically propagate
8020
- public content.
8048
+ content-free receipt. The action always submits privately and never falls back
8049
+ to anonymous or public submission. After an accepted private outcome, a
8050
+ separate server-owned process may create a freshly redacted public candidate
8051
+ only when the organization admin enabled the **Contribution propagation**
8052
+ setting; the submitting agent neither chooses nor sees that process, and the
8053
+ derivative must pass the complete public pipeline.
8021
8054
 
8022
8055
  If the host denies the action, no candidate content was sent. The encrypted
8023
8056
  draft remains on the device in \`awaiting_authorization\`; do not retry it
@@ -10859,7 +10892,7 @@ var toolDefinitions = [
10859
10892
  ),
10860
10893
  tool(
10861
10894
  "submit_private_lesson_candidate",
10862
- "Submit exactly one canonical, challenge-attested organization-private lesson candidate or one content-free held-safety telemetry event. Local plugin clients normally pass only the draft_id returned by prepare_private_lesson_candidate; raw or hosted clients pass the strict candidate-or-hold union. The action can never create or automatically promote public content. Held telemetry contains no lesson prose and can never enter verification, review, topology, or skill materialization. Exact replay is idempotent; host denial leaves the local draft untouched.",
10895
+ "Submit exactly one canonical, challenge-attested organization-private lesson candidate or one content-free held-safety telemetry event. Local plugin clients normally pass only the draft_id returned by prepare_private_lesson_candidate; raw or hosted clients pass the strict candidate-or-hold union. The action always creates only an organization-private submission and never falls back to anonymous or public submission. Held telemetry contains no lesson prose and can never enter verification, review, topology, or skill materialization. Exact replay is idempotent; host denial leaves the local draft untouched.",
10863
10896
  "/api/v1/agent/private-lessons",
10864
10897
  privateLessonSubmissionToolSchema
10865
10898
  ),
@@ -12419,6 +12452,44 @@ async function readPrivateLessonSubmittedRecord(draftId, options = {}) {
12419
12452
  );
12420
12453
  });
12421
12454
  }
12455
+ async function readPrivateLessonSubmissionSource(draftId, options = {}) {
12456
+ assertDraftId(draftId);
12457
+ const outbox = await readyOutbox(options);
12458
+ return withPrivateLessonOutboxLock(outbox.directory, async () => {
12459
+ await reconcileSubmittedRecordsUnlocked(
12460
+ outbox.directory,
12461
+ options.now ?? /* @__PURE__ */ new Date()
12462
+ );
12463
+ if (existsSync4(tombstonePath(outbox.directory, draftId))) {
12464
+ throw new Error("Private lesson draft was explicitly deleted.");
12465
+ }
12466
+ const completionPath = submittedRecordPath(outbox.directory, draftId);
12467
+ if (existsSync4(completionPath)) {
12468
+ await assertRegularPrivateFile(
12469
+ completionPath,
12470
+ PRIVATE_LESSON_SUBMITTED_RECORD_MAX_BYTES
12471
+ );
12472
+ return {
12473
+ kind: "submitted",
12474
+ completion: parseSubmittedRecord(
12475
+ await readBoundedFile(
12476
+ completionPath,
12477
+ PRIVATE_LESSON_SUBMITTED_RECORD_MAX_BYTES
12478
+ ),
12479
+ draftId
12480
+ )
12481
+ };
12482
+ }
12483
+ return {
12484
+ kind: "draft",
12485
+ draft: await readPrivateLessonDraft(draftId, {
12486
+ ...options,
12487
+ outboxDirectory: outbox.directory,
12488
+ encryption: outbox.encryption
12489
+ })
12490
+ };
12491
+ });
12492
+ }
12422
12493
  async function reconcilePrivateLessonOutboxProfiles(options = {}) {
12423
12494
  const outbox = await readyOutbox(options);
12424
12495
  return withPrivateLessonOutboxLock(outbox.directory, async () => {
@@ -13529,7 +13600,7 @@ async function readConcurrentPrivateLessonFallbackKey(path) {
13529
13600
  // src/server.ts
13530
13601
  var MAX_REMOTE_RESPONSE_BYTES = 4 * 1024 * 1024;
13531
13602
  var DOCTOR_PROBE_TIMEOUT_MS = 7500;
13532
- var SERVER_VERSION = true ? "0.1.74" : "0.0.0-dev";
13603
+ var SERVER_VERSION = true ? "0.1.76" : "0.0.0-dev";
13533
13604
  var tools = toolDefinitions;
13534
13605
  var doctorCliRequested = process.argv[2] === "doctor";
13535
13606
  var inputBuffer = Buffer.alloc(0);
@@ -14016,13 +14087,13 @@ async function inspectPrivateLessonOutbox(input) {
14016
14087
  };
14017
14088
  }
14018
14089
  async function submitPrivateLessonDraft(draftId, access, allowPolicyRefresh) {
14019
- const completion = await readPrivateLessonSubmittedRecord(draftId);
14020
- if (completion) {
14021
- return privateLessonSubmittedActionResult(completion, {
14090
+ const source = await readPrivateLessonSubmissionSource(draftId);
14091
+ if (source.kind === "submitted") {
14092
+ return privateLessonSubmittedActionResult(source.completion, {
14022
14093
  status: "already_submitted"
14023
14094
  });
14024
14095
  }
14025
- let draft = await readPrivateLessonDraft(draftId);
14096
+ let draft = source.draft;
14026
14097
  if (draft.state === "superseded_redactor") {
14027
14098
  return privateLessonDraftActionResult(draft, {
14028
14099
  status: "superseded_redactor",
@@ -14209,7 +14280,7 @@ async function submitPrivateLessonDraft(draftId, access, allowPolicyRefresh) {
14209
14280
  "Private lesson submission omitted its processing state."
14210
14281
  );
14211
14282
  }
14212
- const completion2 = await completePrivateLessonSubmission(draft.draft_id, {
14283
+ const completion = await completePrivateLessonSubmission(draft.draft_id, {
14213
14284
  receipt_digest: receiptDigest,
14214
14285
  remote_status: status,
14215
14286
  processing_state: completionProcessingState
@@ -14217,18 +14288,18 @@ async function submitPrivateLessonDraft(draftId, access, allowPolicyRefresh) {
14217
14288
  draft = {
14218
14289
  ...draft,
14219
14290
  state: "submitted",
14220
- attempts: completion2.attempts,
14291
+ attempts: completion.attempts,
14221
14292
  next_retry_at: null,
14222
14293
  last_error_code: null,
14223
- submitted_at: completion2.submitted_at,
14224
- receipt_digest: completion2.receipt_digest,
14294
+ submitted_at: completion.submitted_at,
14295
+ receipt_digest: completion.receipt_digest,
14225
14296
  policy: receiptPolicy,
14226
- updated_at: completion2.updated_at
14297
+ updated_at: completion.updated_at
14227
14298
  };
14228
14299
  return {
14229
14300
  ...privateLessonDraftActionResult(draft, {
14230
14301
  status,
14231
- completion_expires_at: completion2.expires_at
14302
+ completion_expires_at: completion.expires_at
14232
14303
  }),
14233
14304
  receipt,
14234
14305
  processing_state: processingState
@@ -97,6 +97,20 @@ Also use this skill when:
97
97
 
98
98
  Do not directly mutate shared skills. Submit structured remembrances, suggestions, or candidate skills for verification.
99
99
 
100
+ Route memory by audience, not by which write tool is most convenient:
101
+
102
+ - Host-local memory is for facts about this human, repository, installation, or machine.
103
+ - Remembrance is for a generalized lesson another approved organization agent would otherwise rediscover the hard way.
104
+ - A local-memory write does not satisfy organization-shared capture. Generalize reusable feedback and use the private lesson lane; never mirror raw local memory automatically.
105
+
106
+ `get_connection_status` exposes content-free recent contribution outcomes for
107
+ the current installation principal, or for the current API key when an older
108
+ client has no principal. Use it when the user asks whether prior contributions
109
+ were accepted, recognized as duplicates, remain pending, need review, or were
110
+ rejected. It covers remembrances, private skill ideas, skill suggestions,
111
+ resource submissions, and resource reviews. It never returns lesson text,
112
+ titles, rejection prose, actor identity, or private payload data.
113
+
100
114
  Do not use this skill when:
101
115
 
102
116
  - The user asks for general web search, research, or current facts.
@@ -190,14 +204,18 @@ terminal count, retained bytes, reason, and explicit deletion guidance without
190
204
  returning draft content or a local path.
191
205
 
192
206
  The private lesson endpoint derives its organization and private visibility
193
- from the authenticated key. It cannot create public content and never falls
194
- back to public submission. Accepted candidates enter the existing verifier,
195
- topology, review-policy, encrypted-storage, and audit pipelines. Rich content,
196
- raw traces, URLs, code blocks, attachments, secrets, paths, identifiers, or
197
- ambiguous material stays local and must not be forced through by confirmation.
198
- Use `inspect_private_lesson_outbox` to inspect content-free state, retry a
199
- selected draft with `retry_private_lesson_candidate`, and delete only with
200
- `delete_private_lesson_candidate` plus explicit confirmation.
207
+ from the authenticated key. The action always submits privately and never
208
+ falls back to anonymous or public submission. Accepted candidates enter the
209
+ existing verifier, topology, review-policy, encrypted-storage, and audit
210
+ pipelines. After an accepted private outcome, a separate server-owned process
211
+ may create a freshly redacted public candidate only when the organization admin
212
+ enabled **Contribution propagation**; the agent neither chooses nor sees that
213
+ process, and the derivative must pass the complete public pipeline. Rich
214
+ content, raw traces, URLs, code blocks, attachments, secrets, paths,
215
+ identifiers, or ambiguous material stays local and must not be forced through
216
+ by confirmation. Use `inspect_private_lesson_outbox` to inspect content-free
217
+ state, retry a selected draft with `retry_private_lesson_candidate`, and delete
218
+ only with `delete_private_lesson_candidate` plus explicit confirmation.
201
219
 
202
220
  When health reporting is enabled, a held draft may use the same exact submit
203
221
  action to send only a `held_safety_event`: event type, held category counts,
@@ -284,8 +284,12 @@ This is the only host-visible network action in the flow. Approve or persist
284
284
  approval for this exact action, not every Remembrance write. The server derives
285
285
  the organization and private visibility from the authenticated key, verifies a
286
286
  signed organization policy plus purpose-bound attestation, and returns a signed
287
- content-free receipt. The endpoint can never create or automatically propagate
288
- public content.
287
+ content-free receipt. The action always submits privately and never falls back
288
+ to anonymous or public submission. After an accepted private outcome, a
289
+ separate server-owned process may create a freshly redacted public candidate
290
+ only when the organization admin enabled the **Contribution propagation**
291
+ setting; the submitting agent neither chooses nor sees that process, and the
292
+ derivative must pass the complete public pipeline.
289
293
 
290
294
  If the host denies the action, no candidate content was sent. The encrypted
291
295
  draft remains on the device in `awaiting_authorization`; do not retry it
package/src/index.mjs CHANGED
@@ -45,21 +45,21 @@ import {
45
45
  decideStop,
46
46
  directSelectionFromResponse,
47
47
  highMatchFromResponse,
48
- markCurrentEngagementHandled,
49
48
  markHostPolicyAlertDelivered,
49
+ observeSuccessfulCompletionTool,
50
50
  recordDirectiveFollowThroughForTool,
51
51
  recordDirectSelectionSurface,
52
52
  recordDirectiveSurface,
53
53
  recordHighMatchSurface,
54
54
  recordPluginLifecycleHealth,
55
55
  recordHostPolicyDenial,
56
+ recordQueryFeedbackObligation,
56
57
  recordRegistryUse,
57
58
  recordTaskEligibility,
58
59
  recordValueEpisodeSurface,
59
60
  queryResponseHasMatches,
60
61
  reportDirectiveEvent,
61
62
  reportTaskOutcomesOnStop,
62
- responseRequestsRemembranceFollowup,
63
63
  resolveApiCredential,
64
64
  runPromptHook,
65
65
  toolResponseIndicatesFailure,
@@ -76,6 +76,8 @@ const CONTRIBUTION_TOOLS = [
76
76
  "submit_remembrance",
77
77
  "propose_skill_idea",
78
78
  "propose_private_skill",
79
+ "submit_private_lesson_candidate",
80
+ "retry_private_lesson_candidate",
79
81
  "submit_suggestion",
80
82
  "submit_resource",
81
83
  "submit_resource_review",
@@ -371,6 +373,7 @@ export const Remembrance = async (context = {}) => {
371
373
  highMatch: result?.highMatch ?? null,
372
374
  text: guidance.trim(),
373
375
  countsAsUse: Boolean(result?.consumed && result?.matched),
376
+ queryFeedback: result?.queryFeedback ?? null,
374
377
  valueEpisode: result?.valueEpisode ?? null,
375
378
  });
376
379
  }
@@ -412,6 +415,11 @@ export const Remembrance = async (context = {}) => {
412
415
  recordHighMatchSurface(sessionId, guidance.highMatch, env);
413
416
  recordValueEpisodeSurface(sessionId, guidance.valueEpisode, env);
414
417
  }
418
+ recordQueryFeedbackObligation(
419
+ sessionId,
420
+ guidance.queryFeedback,
421
+ env,
422
+ );
415
423
  recordDirectiveSurface(sessionId, guidance.directive, env);
416
424
  if (guidance.directive) {
417
425
  await reportDirectiveEvent(
@@ -487,7 +495,6 @@ export const Remembrance = async (context = {}) => {
487
495
  });
488
496
  const decision = decideStop({ session_id: sessionId }, { env });
489
497
  if (decision?.allow !== false || !decision?.reason) return;
490
- markCurrentEngagementHandled(sessionId, env);
491
498
  writePromptedCount(sessionId, decision.useCount ?? 1, env);
492
499
  await notify(client, decision.reason);
493
500
  } catch (error) {
@@ -555,6 +562,7 @@ export const Remembrance = async (context = {}) => {
555
562
  const tool = toolNameFromEvent(input, output);
556
563
  if (!tool) return;
557
564
  const normalizedTool = tool.toLowerCase();
565
+ const toolArguments = toolArgumentsFromEvent(input);
558
566
  if (toolResponseIndicatesFailure(output)) {
559
567
  await observeHostPolicyDenial({
560
568
  eventType: "tool.execute.after",
@@ -575,6 +583,13 @@ export const Remembrance = async (context = {}) => {
575
583
  valueEpisodeFromResponse(output),
576
584
  env,
577
585
  );
586
+ observeSuccessfulCompletionTool(
587
+ sessionId,
588
+ tool,
589
+ toolArguments,
590
+ output,
591
+ env,
592
+ );
578
593
  await recordDirectiveFollowThroughForTool(sessionId, tool, output, {
579
594
  env,
580
595
  });
@@ -599,6 +614,23 @@ export const Remembrance = async (context = {}) => {
599
614
  selection.slug,
600
615
  env,
601
616
  );
617
+ observeSuccessfulCompletionTool(
618
+ sessionId,
619
+ tool,
620
+ toolArguments,
621
+ output,
622
+ env,
623
+ );
624
+ return;
625
+ }
626
+ if (normalizedTool.endsWith("prepare_private_lesson_candidate")) {
627
+ observeSuccessfulCompletionTool(
628
+ sessionId,
629
+ tool,
630
+ toolArguments,
631
+ output,
632
+ env,
633
+ );
602
634
  return;
603
635
  }
604
636
  if (
@@ -613,19 +645,26 @@ export const Remembrance = async (context = {}) => {
613
645
  normalizedTool.endsWith(candidate),
614
646
  )
615
647
  ) {
616
- if (
617
- normalizedTool.endsWith("submit_feedback") &&
618
- responseRequestsRemembranceFollowup(output)
619
- ) {
620
- return;
621
- }
622
- markCurrentEngagementHandled(sessionId, env);
648
+ observeSuccessfulCompletionTool(
649
+ sessionId,
650
+ tool,
651
+ toolArguments,
652
+ output,
653
+ env,
654
+ );
623
655
  return;
624
656
  }
625
657
  clearHighMatchSurfaceIfOpened(
626
658
  sessionId,
627
659
  tool,
628
- toolArgumentsFromEvent(input),
660
+ toolArguments,
661
+ env,
662
+ );
663
+ observeSuccessfulCompletionTool(
664
+ sessionId,
665
+ tool,
666
+ toolArguments,
667
+ output,
629
668
  env,
630
669
  );
631
670
  } catch (error) {