@remembrance-ai/opencode-plugin 0.1.60 → 0.1.62
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -6
- package/opencode.json +8 -2
- package/package.json +1 -1
- package/scripts/hook-core.mjs +335 -28
- package/servers/remembrance-mcp.mjs +90 -4
- package/skills/remembrancer/references/identity-preferences-topology.md +20 -8
- package/skills/remembrancer/references/remembrance-setup.md +5 -0
- package/src/index.mjs +10 -0
package/README.md
CHANGED
|
@@ -57,12 +57,26 @@ An optional single-use token from **Dashboard > Agents > Instances > Install on
|
|
|
57
57
|
this device** links the installation to the signed-in member so bounded working
|
|
58
58
|
preferences can follow that engineer. The token expires after ten
|
|
59
59
|
minutes and never belongs in reusable key-distribution instructions. Unlinked
|
|
60
|
-
installs remain fully functional with installation-local preferences.
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
60
|
+
installs remain fully functional with installation-local preferences. Private
|
|
61
|
+
working preferences follow each engineer across approved agents and steer
|
|
62
|
+
relevant public and team skills without changing shared instructions or
|
|
63
|
+
weakening organization policy. Profiles, observations, compatibility records,
|
|
64
|
+
and feedback remain private to the organization. Classification runs
|
|
65
|
+
asynchronously against exact skill versions; query and invocation add no
|
|
66
|
+
generative preference call or second embedding request. Material compatibility
|
|
67
|
+
may reorder only already-relevant skills inside one match tier or apply a
|
|
68
|
+
surgical sidecar. Missing or stale coverage is neutral, and no preference can
|
|
69
|
+
weaken safety, authorization, privacy, applicability, required skill steps,
|
|
70
|
+
validation, review, or organization policy. Exact skill-version changes queue
|
|
71
|
+
only that skill for affected organizations; blocked classification resumes
|
|
72
|
+
automatically when the organization becomes eligible.
|
|
73
|
+
|
|
74
|
+
After actually using an exact skill version, the agent may call
|
|
75
|
+
`submit_preference_compatibility_feedback` only with the exact correlation IDs
|
|
76
|
+
and server-issued preference fingerprint in that result's feedback offer. The
|
|
77
|
+
server verifies the fetch and active preference. The operation requires a
|
|
78
|
+
verified principal session, remains private to the organization, and never
|
|
79
|
+
edits the skill.
|
|
66
80
|
|
|
67
81
|
## Organization key
|
|
68
82
|
|
package/opencode.json
CHANGED
|
@@ -3,7 +3,11 @@
|
|
|
3
3
|
"mcp": {
|
|
4
4
|
"remembrance": {
|
|
5
5
|
"type": "local",
|
|
6
|
-
"command": [
|
|
6
|
+
"command": [
|
|
7
|
+
"npx",
|
|
8
|
+
"-y",
|
|
9
|
+
"@remembrance-ai/mcp-server@0.1.62"
|
|
10
|
+
],
|
|
7
11
|
"enabled": true,
|
|
8
12
|
"environment": {
|
|
9
13
|
"REMEMBRANCE_PLUGIN_HOST": "opencode",
|
|
@@ -11,5 +15,7 @@
|
|
|
11
15
|
}
|
|
12
16
|
}
|
|
13
17
|
},
|
|
14
|
-
"plugin": [
|
|
18
|
+
"plugin": [
|
|
19
|
+
"@remembrance-ai/opencode-plugin@0.1.62"
|
|
20
|
+
]
|
|
15
21
|
}
|
package/package.json
CHANGED
package/scripts/hook-core.mjs
CHANGED
|
@@ -58,6 +58,33 @@ const VALUE_EPISODE_MARKER_LIMIT = 20;
|
|
|
58
58
|
const VALUE_EPISODE_MARKER_TTL_MS = 30 * 24 * 60 * 60 * 1000;
|
|
59
59
|
const DIRECT_SELECTION_MARKER_LIMIT = 20;
|
|
60
60
|
const DIRECT_SELECTION_MARKER_TTL_MS = 24 * 60 * 60 * 1000;
|
|
61
|
+
const PREFERENCE_EVIDENCE_SKILL_LIMIT = 2;
|
|
62
|
+
const PREFERENCE_EVIDENCE_SETTING_LIMIT = 4;
|
|
63
|
+
const PREFERENCE_EVIDENCE_CONTEXT_CHARS = 760;
|
|
64
|
+
const EFFECTIVE_PREFERENCE_SOURCES = new Set([
|
|
65
|
+
"mandatory_org",
|
|
66
|
+
"explicit_task",
|
|
67
|
+
"explicit_project",
|
|
68
|
+
"explicit_member_runtime",
|
|
69
|
+
"explicit_member",
|
|
70
|
+
"learned_member_runtime",
|
|
71
|
+
"learned_member",
|
|
72
|
+
"explicit_installation",
|
|
73
|
+
"learned_installation",
|
|
74
|
+
"recommended_org",
|
|
75
|
+
"skill_default",
|
|
76
|
+
]);
|
|
77
|
+
const PREFERENCE_EVIDENCE_SOURCES = new Set([
|
|
78
|
+
"mandatory_org",
|
|
79
|
+
"explicit_project",
|
|
80
|
+
"explicit_member_runtime",
|
|
81
|
+
"explicit_member",
|
|
82
|
+
"learned_member_runtime",
|
|
83
|
+
"learned_member",
|
|
84
|
+
"explicit_installation",
|
|
85
|
+
"learned_installation",
|
|
86
|
+
"recommended_org",
|
|
87
|
+
]);
|
|
61
88
|
const MAX_LOCAL_CONFIG_BYTES = 64 * 1024;
|
|
62
89
|
const MAX_LOCAL_HEALTH_MARKER_BYTES = 16 * 1024;
|
|
63
90
|
const MAX_LOCAL_PLUGIN_ALERT_BYTES = 16 * 1024;
|
|
@@ -338,14 +365,27 @@ export function buildQueryPayload(
|
|
|
338
365
|
redacted.length <= MAX_SUMMARY_CHARS
|
|
339
366
|
? redacted
|
|
340
367
|
: `${redacted.slice(0, MAX_SUMMARY_CHARS - 3).trim()}...`;
|
|
341
|
-
const
|
|
368
|
+
const credential = resolveApiCredential(env);
|
|
369
|
+
const organizationCredentialAvailable =
|
|
370
|
+
Boolean(credential.apiKey) &&
|
|
371
|
+
!isUnusableConfigurationSource(credential.source);
|
|
372
|
+
const projectKey = organizationCredentialAvailable
|
|
373
|
+
? projectKeyForHook(env, projectPath)
|
|
374
|
+
: null;
|
|
342
375
|
const resolvedClientContext = clientContext
|
|
343
|
-
? {
|
|
344
|
-
...
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
376
|
+
? (() => {
|
|
377
|
+
const { project_key: requestedProjectKey, ...publicClientContext } =
|
|
378
|
+
clientContext;
|
|
379
|
+
const authorizedProjectKey = organizationCredentialAvailable
|
|
380
|
+
? requestedProjectKey || projectKey
|
|
381
|
+
: null;
|
|
382
|
+
return {
|
|
383
|
+
...publicClientContext,
|
|
384
|
+
...(authorizedProjectKey
|
|
385
|
+
? { project_key: authorizedProjectKey }
|
|
386
|
+
: {}),
|
|
387
|
+
};
|
|
388
|
+
})()
|
|
349
389
|
: null;
|
|
350
390
|
return {
|
|
351
391
|
agent: {
|
|
@@ -834,7 +874,7 @@ export function formatContext(
|
|
|
834
874
|
);
|
|
835
875
|
if (effectivePreferences.length > 0) {
|
|
836
876
|
lines.push(
|
|
837
|
-
`Apply these persisted working preferences silently: ${effectivePreferences.join("; ")}. A clear current-task preference overrides every non-mandatory entry; a Required organization setting remains authoritative. Use preferences only for discretionary presentation, workflow, or strategy choices among already-applicable options. Never weaken applicability, safety, authorization, privacy, required skill steps, validation, or review.`,
|
|
877
|
+
`Apply these persisted working preferences silently: ${effectivePreferences.join("; ")}. Do not ask the user to reconfirm them or mention routine preference application in the final response. A clear current-task preference overrides every non-mandatory entry; a Required organization setting remains authoritative. Use preferences only for discretionary presentation, workflow, or strategy choices among already-applicable options. Never weaken applicability, safety, authorization, privacy, required skill steps, validation, or review.`,
|
|
838
878
|
);
|
|
839
879
|
}
|
|
840
880
|
const queryId = body?.query_feedback?.query_id ?? body?.query_id;
|
|
@@ -878,6 +918,12 @@ export function formatContext(
|
|
|
878
918
|
`After using Remembrance: ${safeText(contributionDirective, MAX_DIRECTIVE_CHARS)}`,
|
|
879
919
|
);
|
|
880
920
|
}
|
|
921
|
+
const preferenceEvidence = preferenceCompatibilityEvidenceFromResponse(body);
|
|
922
|
+
if (preferenceEvidence.length > 0) {
|
|
923
|
+
tailLines.push(
|
|
924
|
+
preferenceCompatibilityEvidenceInstruction(preferenceEvidence),
|
|
925
|
+
);
|
|
926
|
+
}
|
|
881
927
|
if (skills.length > 0 || resources.length > 0) {
|
|
882
928
|
tailLines.push(
|
|
883
929
|
"Delegating this task? Pass the selected slug, query_id, and result_id to the subagent; it should fetch that result or run its own full-context query before custom work.",
|
|
@@ -1072,6 +1118,162 @@ function sanitizePreferenceApplication(value, effectivePreferences) {
|
|
|
1072
1118
|
: `skill preference defaults ${settings.join("; ")}`;
|
|
1073
1119
|
}
|
|
1074
1120
|
|
|
1121
|
+
export function preferenceCompatibilityEvidenceFromResponse(response) {
|
|
1122
|
+
const body = responseBodyFromToolResponse(response);
|
|
1123
|
+
const candidates = [
|
|
1124
|
+
...(body?.selection_mode === "explicit" && body?.skill
|
|
1125
|
+
? [
|
|
1126
|
+
{
|
|
1127
|
+
...body.skill,
|
|
1128
|
+
result_id: body.result_id,
|
|
1129
|
+
effective_preferences: body.effective_preferences,
|
|
1130
|
+
preference_compatibility_feedback:
|
|
1131
|
+
body.preference_compatibility_feedback,
|
|
1132
|
+
},
|
|
1133
|
+
]
|
|
1134
|
+
: []),
|
|
1135
|
+
...(Array.isArray(body?.skills) ? body.skills : []),
|
|
1136
|
+
];
|
|
1137
|
+
const seen = new Set();
|
|
1138
|
+
return candidates
|
|
1139
|
+
.flatMap((candidate) => {
|
|
1140
|
+
const slug = safePreferenceIdentifier(candidate?.slug, 120);
|
|
1141
|
+
if (!slug || seen.has(slug)) return [];
|
|
1142
|
+
const feedback = candidate?.preference_compatibility_feedback;
|
|
1143
|
+
const queryId = safeCorrelationIdentifier(feedback?.query_id, 160);
|
|
1144
|
+
const resultId = safeCorrelationIdentifier(feedback?.result_id, 160);
|
|
1145
|
+
const skillVersionId = safeCorrelationIdentifier(
|
|
1146
|
+
feedback?.skill_version_id,
|
|
1147
|
+
160,
|
|
1148
|
+
);
|
|
1149
|
+
const preferences = sanitizePreferenceFeedbackSettings(
|
|
1150
|
+
feedback?.preferences,
|
|
1151
|
+
);
|
|
1152
|
+
if (
|
|
1153
|
+
feedback?.available !== true ||
|
|
1154
|
+
!queryId ||
|
|
1155
|
+
!resultId ||
|
|
1156
|
+
!skillVersionId ||
|
|
1157
|
+
preferences.length === 0
|
|
1158
|
+
) {
|
|
1159
|
+
return [];
|
|
1160
|
+
}
|
|
1161
|
+
seen.add(slug);
|
|
1162
|
+
return [
|
|
1163
|
+
{
|
|
1164
|
+
query_id: queryId,
|
|
1165
|
+
result_id: resultId,
|
|
1166
|
+
skill_slug: slug,
|
|
1167
|
+
skill_version_id: skillVersionId,
|
|
1168
|
+
preferences,
|
|
1169
|
+
},
|
|
1170
|
+
];
|
|
1171
|
+
})
|
|
1172
|
+
.slice(0, PREFERENCE_EVIDENCE_SKILL_LIMIT);
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
function preferenceCompatibilityEvidenceInstruction(evidence) {
|
|
1176
|
+
const payload = boundedPreferenceEvidencePayload(evidence);
|
|
1177
|
+
if (!payload) return "";
|
|
1178
|
+
return [
|
|
1179
|
+
"Private preference evidence (silent, after actual use only): if the selected skill clearly supports or conflicts with one server-issued working preference below, call submit_preference_compatibility_feedback using its exact query_id, result_id, preference_fingerprint, skill slug, and version id.",
|
|
1180
|
+
'Judge observed behavior independently: selection and an existing classifier label are not new evidence. Skip neutral or uncertain cases. Use evidence_source "explicit_user" only for the user\'s explicit assessment; otherwise use "agent_observed". Never send task content, and do not ask the user or mention routine telemetry.',
|
|
1181
|
+
`Candidates: ${payload}`,
|
|
1182
|
+
].join(" ");
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
function boundedPreferenceEvidencePayload(evidence) {
|
|
1186
|
+
const entries = [];
|
|
1187
|
+
for (const candidate of evidence.slice(0, PREFERENCE_EVIDENCE_SKILL_LIMIT)) {
|
|
1188
|
+
for (const preference of candidate.preferences.slice(
|
|
1189
|
+
0,
|
|
1190
|
+
PREFERENCE_EVIDENCE_SETTING_LIMIT,
|
|
1191
|
+
)) {
|
|
1192
|
+
const next = {
|
|
1193
|
+
query_id: candidate.query_id,
|
|
1194
|
+
result_id: candidate.result_id,
|
|
1195
|
+
skill_slug: candidate.skill_slug,
|
|
1196
|
+
skill_version_id: candidate.skill_version_id,
|
|
1197
|
+
preference_fingerprint: preference.preference_fingerprint,
|
|
1198
|
+
setting: preference.setting,
|
|
1199
|
+
};
|
|
1200
|
+
const serialized = JSON.stringify([...entries, next]);
|
|
1201
|
+
if (serialized.length > PREFERENCE_EVIDENCE_CONTEXT_CHARS) {
|
|
1202
|
+
return entries.length > 0 ? JSON.stringify(entries) : null;
|
|
1203
|
+
}
|
|
1204
|
+
entries.push(next);
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
return entries.length > 0 ? JSON.stringify(entries) : null;
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
function sanitizePreferenceFeedbackSettings(value) {
|
|
1211
|
+
if (!Array.isArray(value)) return [];
|
|
1212
|
+
return value
|
|
1213
|
+
.flatMap((entry) => {
|
|
1214
|
+
const fingerprint =
|
|
1215
|
+
typeof entry?.preference_fingerprint === "string" &&
|
|
1216
|
+
/^sha256:[a-f0-9]{64}$/.test(entry.preference_fingerprint)
|
|
1217
|
+
? entry.preference_fingerprint
|
|
1218
|
+
: null;
|
|
1219
|
+
const item = entry?.setting;
|
|
1220
|
+
if (
|
|
1221
|
+
!fingerprint ||
|
|
1222
|
+
!item ||
|
|
1223
|
+
typeof item !== "object" ||
|
|
1224
|
+
Array.isArray(item)
|
|
1225
|
+
) {
|
|
1226
|
+
return [];
|
|
1227
|
+
}
|
|
1228
|
+
const key = safePreferenceIdentifier(item.key, 96);
|
|
1229
|
+
const preferenceValue = safePreferenceIdentifier(item.value, 96);
|
|
1230
|
+
if (!key || !preferenceValue) return [];
|
|
1231
|
+
const setting = { key, value: preferenceValue };
|
|
1232
|
+
if (
|
|
1233
|
+
typeof item.label === "string" &&
|
|
1234
|
+
item.label.trim() &&
|
|
1235
|
+
typeof item.behavior === "string" &&
|
|
1236
|
+
item.behavior.trim() &&
|
|
1237
|
+
["presentation", "workflow", "strategy_selection"].includes(
|
|
1238
|
+
item.effect,
|
|
1239
|
+
) &&
|
|
1240
|
+
["prefer", "avoid"].includes(item.strength) &&
|
|
1241
|
+
Number.isInteger(item.definition_version) &&
|
|
1242
|
+
item.definition_version >= 1 &&
|
|
1243
|
+
item.definition_version <= 1_000_000
|
|
1244
|
+
) {
|
|
1245
|
+
Object.assign(setting, {
|
|
1246
|
+
label: safeText(item.label, 96),
|
|
1247
|
+
behavior: safeText(item.behavior, 320),
|
|
1248
|
+
effect: item.effect,
|
|
1249
|
+
strength: item.strength,
|
|
1250
|
+
definition_version: item.definition_version,
|
|
1251
|
+
});
|
|
1252
|
+
}
|
|
1253
|
+
return [{ preference_fingerprint: fingerprint, setting }];
|
|
1254
|
+
})
|
|
1255
|
+
.slice(0, PREFERENCE_EVIDENCE_SETTING_LIMIT);
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
function safeCorrelationIdentifier(value, maxLength) {
|
|
1259
|
+
const text = String(value ?? "").trim();
|
|
1260
|
+
return text.length > 0 &&
|
|
1261
|
+
text.length <= maxLength &&
|
|
1262
|
+
/^[A-Za-z0-9_-]+$/.test(text)
|
|
1263
|
+
? text
|
|
1264
|
+
: null;
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
function safePreferenceIdentifier(value, maxLength) {
|
|
1268
|
+
const text = String(value ?? "")
|
|
1269
|
+
.trim()
|
|
1270
|
+
.toLowerCase();
|
|
1271
|
+
return text.length <= maxLength &&
|
|
1272
|
+
/^[a-z][a-z0-9]*(?:[._-][a-z0-9]+)*$/.test(text)
|
|
1273
|
+
? text
|
|
1274
|
+
: null;
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1075
1277
|
function sanitizeWhyMatched(value) {
|
|
1076
1278
|
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
1077
1279
|
return {
|
|
@@ -1278,7 +1480,7 @@ export async function runPromptHook(prompt, options = {}) {
|
|
|
1278
1480
|
env,
|
|
1279
1481
|
)
|
|
1280
1482
|
: (formatContext(response, decision.reason, limitFromEnv(env)) ??
|
|
1281
|
-
|
|
1483
|
+
emptyQueryContext(decision.reason)),
|
|
1282
1484
|
preferenceCapture,
|
|
1283
1485
|
),
|
|
1284
1486
|
eligible: true,
|
|
@@ -1388,6 +1590,8 @@ export function contributionReason(
|
|
|
1388
1590
|
: []
|
|
1389
1591
|
).slice(0, DIRECT_SELECTION_MARKER_LIMIT);
|
|
1390
1592
|
if (directSelections.length > 0) {
|
|
1593
|
+
const preferenceEvidenceLine =
|
|
1594
|
+
directPreferenceCompatibilityInstruction(directSelections);
|
|
1391
1595
|
const selectionLines = directSelections.map((selection) => {
|
|
1392
1596
|
const correlation =
|
|
1393
1597
|
selection.query_id && selection.result_id
|
|
@@ -1403,6 +1607,7 @@ export function contributionReason(
|
|
|
1403
1607
|
signalLine,
|
|
1404
1608
|
"Do not submit query-fit feedback for these selections; they were chosen directly rather than ranked for a query.",
|
|
1405
1609
|
"Call submit_feedback once for each listed skill with useful true or false and a concise post-use lesson.",
|
|
1610
|
+
preferenceEvidenceLine,
|
|
1406
1611
|
"If submit_feedback returns next_step.submit_remembrance_payload, submit it when the redacted lesson is reusable.",
|
|
1407
1612
|
routineContributionVisibilityInstruction(),
|
|
1408
1613
|
]
|
|
@@ -1420,6 +1625,31 @@ export function contributionReason(
|
|
|
1420
1625
|
.join("\n");
|
|
1421
1626
|
}
|
|
1422
1627
|
|
|
1628
|
+
function directPreferenceCompatibilityInstruction(selections) {
|
|
1629
|
+
const evidence = selections.flatMap((selection) =>
|
|
1630
|
+
selection?.query_id &&
|
|
1631
|
+
selection?.result_id &&
|
|
1632
|
+
selection?.version_id &&
|
|
1633
|
+
Array.isArray(selection.preference_feedback_settings) &&
|
|
1634
|
+
selection.preference_feedback_settings.length > 0
|
|
1635
|
+
? [
|
|
1636
|
+
{
|
|
1637
|
+
query_id: safeText(selection.query_id, 160),
|
|
1638
|
+
result_id: safeText(selection.result_id, 160),
|
|
1639
|
+
skill_slug: safeText(selection.slug, 120),
|
|
1640
|
+
skill_version_id: safeText(selection.version_id, 160),
|
|
1641
|
+
preferences: sanitizeStoredPreferenceFeedbackSettings(
|
|
1642
|
+
selection.preference_feedback_settings,
|
|
1643
|
+
),
|
|
1644
|
+
},
|
|
1645
|
+
]
|
|
1646
|
+
: [],
|
|
1647
|
+
);
|
|
1648
|
+
return evidence.length > 0
|
|
1649
|
+
? preferenceCompatibilityEvidenceInstruction(evidence)
|
|
1650
|
+
: null;
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1423
1653
|
function routineContributionVisibilityInstruction() {
|
|
1424
1654
|
return `Use Remembrance MCP tools when available; otherwise use this session's REST fallback, or emit REMEMBRANCE_SUBMISSION_PAYLOAD only if the API is unavailable. Never remove or bypass an organization key to force a public candidate. Do not mention routine Remembrance calls, hooks, or receipt IDs in the final answer. Surface only failures, host blocks, or required action. On a host block, say exactly: "${HOST_POLICY_ALERT_TEXT}" and do not retry through another transport. Redact secrets and proprietary content.`;
|
|
1425
1655
|
}
|
|
@@ -1567,8 +1797,8 @@ function highMatchClosureInstruction(match) {
|
|
|
1567
1797
|
|
|
1568
1798
|
// --- Marker mechanism (Codex has no transcript path) -------------------------
|
|
1569
1799
|
//
|
|
1570
|
-
//
|
|
1571
|
-
// os.tmpdir()/remembrance-usage
|
|
1800
|
+
// Per-session counters and bounded state markers live under a private,
|
|
1801
|
+
// per-user os.tmpdir()/remembrance-usage-<owner>/<hash> directory:
|
|
1572
1802
|
// <hash>.use — incremented every time the query adapter completes a query.
|
|
1573
1803
|
// <hash>.eligible — records that a reusable task should be closed out even if
|
|
1574
1804
|
// no query result was consumed.
|
|
@@ -1580,10 +1810,20 @@ function highMatchClosureInstruction(match) {
|
|
|
1580
1810
|
|
|
1581
1811
|
const USAGE_DIR = "remembrance-usage";
|
|
1582
1812
|
|
|
1813
|
+
function localUsageOwnerId() {
|
|
1814
|
+
if (typeof process.getuid === "function") {
|
|
1815
|
+
return `uid-${process.getuid()}`;
|
|
1816
|
+
}
|
|
1817
|
+
return `home-${createHash("sha256")
|
|
1818
|
+
.update(homedir())
|
|
1819
|
+
.digest("hex")
|
|
1820
|
+
.slice(0, 16)}`;
|
|
1821
|
+
}
|
|
1822
|
+
|
|
1583
1823
|
function usageDir(env = process.env) {
|
|
1584
1824
|
return env?.REMEMBRANCE_USAGE_DIR
|
|
1585
1825
|
? String(env.REMEMBRANCE_USAGE_DIR)
|
|
1586
|
-
: join(tmpdir(), USAGE_DIR);
|
|
1826
|
+
: join(tmpdir(), `${USAGE_DIR}-${localUsageOwnerId()}`);
|
|
1587
1827
|
}
|
|
1588
1828
|
|
|
1589
1829
|
function sessionHash(sessionId) {
|
|
@@ -1631,17 +1871,30 @@ function readCountFile(path) {
|
|
|
1631
1871
|
}
|
|
1632
1872
|
}
|
|
1633
1873
|
|
|
1634
|
-
function
|
|
1874
|
+
function writePrivateUsageMarker(path, value) {
|
|
1875
|
+
const temporaryPath = `${path}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`;
|
|
1635
1876
|
try {
|
|
1636
|
-
|
|
1637
|
-
writeFileSync(
|
|
1877
|
+
ensureSecureHookDirectory(dirname(path));
|
|
1878
|
+
writeFileSync(temporaryPath, String(value), {
|
|
1879
|
+
mode: 0o600,
|
|
1880
|
+
flag: "wx",
|
|
1881
|
+
});
|
|
1882
|
+
renameSync(temporaryPath, path);
|
|
1638
1883
|
return true;
|
|
1639
1884
|
} catch {
|
|
1640
|
-
|
|
1885
|
+
try {
|
|
1886
|
+
rmSync(temporaryPath, { force: true });
|
|
1887
|
+
} catch {
|
|
1888
|
+
// Best-effort cleanup only; marker persistence must remain fail-open.
|
|
1889
|
+
}
|
|
1641
1890
|
return false;
|
|
1642
1891
|
}
|
|
1643
1892
|
}
|
|
1644
1893
|
|
|
1894
|
+
function writeCountFile(path, count) {
|
|
1895
|
+
return writePrivateUsageMarker(path, String(count));
|
|
1896
|
+
}
|
|
1897
|
+
|
|
1645
1898
|
// Increment (and return) the per-session registry-use counter. Prompt and tool
|
|
1646
1899
|
// adapters call this only after an authorized registry match or explicit skill
|
|
1647
1900
|
// invocation. A legitimate empty query remains eligible for completion recovery
|
|
@@ -1693,8 +1946,7 @@ export function recordDirectiveSurface(
|
|
|
1693
1946
|
rmSync(path, { force: true });
|
|
1694
1947
|
return true;
|
|
1695
1948
|
}
|
|
1696
|
-
|
|
1697
|
-
writeFileSync(
|
|
1949
|
+
return writePrivateUsageMarker(
|
|
1698
1950
|
path,
|
|
1699
1951
|
JSON.stringify({
|
|
1700
1952
|
directive_id: safeText(directive.directive_id, 96),
|
|
@@ -1706,7 +1958,6 @@ export function recordDirectiveSurface(
|
|
|
1706
1958
|
shown_at: safeText(directive.shown_at ?? new Date().toISOString(), 40),
|
|
1707
1959
|
}),
|
|
1708
1960
|
);
|
|
1709
|
-
return true;
|
|
1710
1961
|
} catch {
|
|
1711
1962
|
return false;
|
|
1712
1963
|
}
|
|
@@ -1916,6 +2167,8 @@ export function directSelectionFromResponse(response) {
|
|
|
1916
2167
|
) {
|
|
1917
2168
|
return null;
|
|
1918
2169
|
}
|
|
2170
|
+
const preferenceEvidence =
|
|
2171
|
+
preferenceCompatibilityEvidenceFromResponse(body)[0] ?? null;
|
|
1919
2172
|
return {
|
|
1920
2173
|
invocation_id:
|
|
1921
2174
|
typeof body.invocation_id === "string"
|
|
@@ -1938,6 +2191,10 @@ export function directSelectionFromResponse(response) {
|
|
|
1938
2191
|
typeof skill.source === "string" ? safeText(skill.source, 40) : null,
|
|
1939
2192
|
feedback_available: body?.feedback?.available === true,
|
|
1940
2193
|
task_outcome_available: body?.task_outcome?.available === true,
|
|
2194
|
+
preference_feedback_settings:
|
|
2195
|
+
preferenceEvidence?.skill_version_id === skill.version_id
|
|
2196
|
+
? preferenceEvidence.preferences
|
|
2197
|
+
: [],
|
|
1941
2198
|
used_at: new Date().toISOString(),
|
|
1942
2199
|
};
|
|
1943
2200
|
}
|
|
@@ -2014,6 +2271,9 @@ function normalizeDirectSelectionSurface(selection, sessionId, env) {
|
|
|
2014
2271
|
source: selection.source ? safeText(selection.source, 40) : null,
|
|
2015
2272
|
feedback_available: selection.feedback_available === true,
|
|
2016
2273
|
task_outcome_available: selection.task_outcome_available === true,
|
|
2274
|
+
preference_feedback_settings: sanitizeStoredPreferenceFeedbackSettings(
|
|
2275
|
+
selection.preference_feedback_settings,
|
|
2276
|
+
),
|
|
2017
2277
|
prompted_at: selection.prompted_at
|
|
2018
2278
|
? safeText(selection.prompted_at, 40)
|
|
2019
2279
|
: null,
|
|
@@ -2041,6 +2301,9 @@ function normalizeStoredDirectSelectionSurface(selection) {
|
|
|
2041
2301
|
source: selection.source ? safeText(selection.source, 40) : null,
|
|
2042
2302
|
feedback_available: selection.feedback_available === true,
|
|
2043
2303
|
task_outcome_available: selection.task_outcome_available === true,
|
|
2304
|
+
preference_feedback_settings: sanitizeStoredPreferenceFeedbackSettings(
|
|
2305
|
+
selection.preference_feedback_settings,
|
|
2306
|
+
),
|
|
2044
2307
|
prompted_at: selection.prompted_at
|
|
2045
2308
|
? safeText(selection.prompted_at, 40)
|
|
2046
2309
|
: null,
|
|
@@ -2051,15 +2314,54 @@ function normalizeStoredDirectSelectionSurface(selection) {
|
|
|
2051
2314
|
};
|
|
2052
2315
|
}
|
|
2053
2316
|
|
|
2317
|
+
function sanitizeStoredPreferenceFeedbackSettings(value) {
|
|
2318
|
+
if (!Array.isArray(value)) return [];
|
|
2319
|
+
return value
|
|
2320
|
+
.flatMap((entry) => {
|
|
2321
|
+
const fingerprint =
|
|
2322
|
+
typeof entry?.preference_fingerprint === "string" &&
|
|
2323
|
+
/^sha256:[a-f0-9]{64}$/.test(entry.preference_fingerprint)
|
|
2324
|
+
? entry.preference_fingerprint
|
|
2325
|
+
: null;
|
|
2326
|
+
const setting = entry?.setting;
|
|
2327
|
+
if (!fingerprint || !setting || typeof setting !== "object") return [];
|
|
2328
|
+
const key = safePreferenceIdentifier(setting?.key, 96);
|
|
2329
|
+
const preferenceValue = safePreferenceIdentifier(setting?.value, 96);
|
|
2330
|
+
if (!key || !preferenceValue) return [];
|
|
2331
|
+
const normalized = { key, value: preferenceValue };
|
|
2332
|
+
if (
|
|
2333
|
+
typeof setting?.label === "string" &&
|
|
2334
|
+
setting.label.trim() &&
|
|
2335
|
+
typeof setting?.behavior === "string" &&
|
|
2336
|
+
setting.behavior.trim() &&
|
|
2337
|
+
["presentation", "workflow", "strategy_selection"].includes(
|
|
2338
|
+
setting.effect,
|
|
2339
|
+
) &&
|
|
2340
|
+
["prefer", "avoid"].includes(setting.strength) &&
|
|
2341
|
+
Number.isInteger(setting.definition_version) &&
|
|
2342
|
+
setting.definition_version >= 1 &&
|
|
2343
|
+
setting.definition_version <= 1_000_000
|
|
2344
|
+
) {
|
|
2345
|
+
Object.assign(normalized, {
|
|
2346
|
+
label: safeText(setting.label, 96),
|
|
2347
|
+
behavior: safeText(setting.behavior, 320),
|
|
2348
|
+
effect: setting.effect,
|
|
2349
|
+
strength: setting.strength,
|
|
2350
|
+
definition_version: setting.definition_version,
|
|
2351
|
+
});
|
|
2352
|
+
}
|
|
2353
|
+
return [{ preference_fingerprint: fingerprint, setting: normalized }];
|
|
2354
|
+
})
|
|
2355
|
+
.slice(0, PREFERENCE_EVIDENCE_SETTING_LIMIT);
|
|
2356
|
+
}
|
|
2357
|
+
|
|
2054
2358
|
function writeDirectSelectionSurfaces(sessionId, records, env) {
|
|
2055
2359
|
try {
|
|
2056
2360
|
const path = directSelectionPath(sessionId, env);
|
|
2057
|
-
|
|
2058
|
-
writeFileSync(
|
|
2361
|
+
return writePrivateUsageMarker(
|
|
2059
2362
|
path,
|
|
2060
2363
|
JSON.stringify(records.slice(-DIRECT_SELECTION_MARKER_LIMIT)),
|
|
2061
2364
|
);
|
|
2062
|
-
return true;
|
|
2063
2365
|
} catch {
|
|
2064
2366
|
return false;
|
|
2065
2367
|
}
|
|
@@ -2109,8 +2411,7 @@ export function recordHighMatchSurface(sessionId, match, env = process.env) {
|
|
|
2109
2411
|
rmSync(path, { force: true });
|
|
2110
2412
|
return true;
|
|
2111
2413
|
}
|
|
2112
|
-
|
|
2113
|
-
writeFileSync(
|
|
2414
|
+
return writePrivateUsageMarker(
|
|
2114
2415
|
path,
|
|
2115
2416
|
JSON.stringify({
|
|
2116
2417
|
query_id: match.query_id ? safeText(match.query_id, 80) : null,
|
|
@@ -2132,7 +2433,6 @@ export function recordHighMatchSurface(sessionId, match, env = process.env) {
|
|
|
2132
2433
|
potential_savings: sanitizePotentialSavings(match.potential_savings),
|
|
2133
2434
|
}),
|
|
2134
2435
|
);
|
|
2135
|
-
return true;
|
|
2136
2436
|
} catch {
|
|
2137
2437
|
return false;
|
|
2138
2438
|
}
|
|
@@ -2488,12 +2788,10 @@ async function postTaskOutcome(payload, options = {}) {
|
|
|
2488
2788
|
function writeValueEpisodeSurfaces(sessionId, records, env) {
|
|
2489
2789
|
try {
|
|
2490
2790
|
const path = valueEpisodePath(sessionId, env);
|
|
2491
|
-
|
|
2492
|
-
writeFileSync(
|
|
2791
|
+
return writePrivateUsageMarker(
|
|
2493
2792
|
path,
|
|
2494
2793
|
JSON.stringify(records.slice(-VALUE_EPISODE_MARKER_LIMIT)),
|
|
2495
2794
|
);
|
|
2496
|
-
return true;
|
|
2497
2795
|
} catch {
|
|
2498
2796
|
return false;
|
|
2499
2797
|
}
|
|
@@ -3144,9 +3442,11 @@ function sanitizeEffectivePreferences(value) {
|
|
|
3144
3442
|
item &&
|
|
3145
3443
|
typeof item === "object" &&
|
|
3146
3444
|
!Array.isArray(item) &&
|
|
3445
|
+
typeof item.key === "string" &&
|
|
3147
3446
|
/^[a-z][a-z0-9]*(?:[._-][a-z0-9]+)*$/.test(item.key) &&
|
|
3148
3447
|
typeof item.value === "string" &&
|
|
3149
3448
|
/^[a-z][a-z0-9]*(?:[._-][a-z0-9]+)*$/.test(item.value) &&
|
|
3449
|
+
EFFECTIVE_PREFERENCE_SOURCES.has(item.source) &&
|
|
3150
3450
|
["presentation", "workflow", "strategy_selection"].includes(
|
|
3151
3451
|
item.effect ?? "presentation",
|
|
3152
3452
|
) &&
|
|
@@ -3366,6 +3666,7 @@ function hostPolicyOperationClass(toolName) {
|
|
|
3366
3666
|
normalized.endsWith("get_skill") ||
|
|
3367
3667
|
normalized.endsWith("get_resource") ||
|
|
3368
3668
|
normalized.endsWith("invoke_skill") ||
|
|
3669
|
+
normalized.endsWith("get_effective_preferences") ||
|
|
3369
3670
|
normalized.endsWith("run_connection_doctor") ||
|
|
3370
3671
|
normalized.endsWith("get_connection_status")
|
|
3371
3672
|
) {
|
|
@@ -3389,6 +3690,8 @@ function hostPolicyOperationClass(toolName) {
|
|
|
3389
3690
|
if (
|
|
3390
3691
|
normalized.endsWith("submit_query_feedback") ||
|
|
3391
3692
|
normalized.endsWith("submit_feedback") ||
|
|
3693
|
+
normalized.endsWith("submit_preference_compatibility_feedback") ||
|
|
3694
|
+
normalized.endsWith("record_preference") ||
|
|
3392
3695
|
normalized.endsWith("report_task_outcome")
|
|
3393
3696
|
) {
|
|
3394
3697
|
return "feedback";
|
|
@@ -3409,6 +3712,10 @@ function isRemembranceToolName(toolName) {
|
|
|
3409
3712
|
"get_resource",
|
|
3410
3713
|
"invoke_skill",
|
|
3411
3714
|
"get_value_proof",
|
|
3715
|
+
"get_effective_preferences",
|
|
3716
|
+
"record_preference",
|
|
3717
|
+
"submit_preference_compatibility_feedback",
|
|
3718
|
+
"link_current_installation",
|
|
3412
3719
|
"report_task_outcome",
|
|
3413
3720
|
"submit_query_feedback",
|
|
3414
3721
|
"submit_feedback",
|
|
@@ -5335,6 +5335,81 @@ var skillPreferenceTraitsSchema = external_exports.array(skillPreferenceTraitSch
|
|
|
5335
5335
|
seen.add(identity);
|
|
5336
5336
|
});
|
|
5337
5337
|
});
|
|
5338
|
+
var preferenceCompatibilityRelationshipSchema = external_exports.enum([
|
|
5339
|
+
"supports",
|
|
5340
|
+
"conflicts",
|
|
5341
|
+
"neutral",
|
|
5342
|
+
"uncertain"
|
|
5343
|
+
]);
|
|
5344
|
+
var preferenceCompatibilitySourceSchema = external_exports.enum([
|
|
5345
|
+
"classifier",
|
|
5346
|
+
"member_feedback",
|
|
5347
|
+
"organization_override",
|
|
5348
|
+
"legacy_trait"
|
|
5349
|
+
]);
|
|
5350
|
+
var preferenceCompatibilityAssessmentSchema = external_exports.object({
|
|
5351
|
+
preference_fingerprint: external_exports.string().regex(/^sha256:[a-f0-9]{64}$/),
|
|
5352
|
+
preference_key: preferenceKeySchema,
|
|
5353
|
+
preference_value: preferenceValueSchema,
|
|
5354
|
+
relationship: preferenceCompatibilityRelationshipSchema,
|
|
5355
|
+
confidence: external_exports.number().min(0).max(1),
|
|
5356
|
+
rationale: external_exports.string().trim().min(1).max(200).nullable().default(null),
|
|
5357
|
+
classification_version: external_exports.string().trim().min(1).max(96),
|
|
5358
|
+
source: preferenceCompatibilitySourceSchema,
|
|
5359
|
+
locked: external_exports.boolean().default(false)
|
|
5360
|
+
}).strict();
|
|
5361
|
+
var preferenceCompatibilityAssessmentsSchema = external_exports.array(preferenceCompatibilityAssessmentSchema).max(MAX_EFFECTIVE_PREFERENCES * 2);
|
|
5362
|
+
var preferenceCompatibilityClassifierResultSchema = external_exports.object({
|
|
5363
|
+
relationship: preferenceCompatibilityRelationshipSchema,
|
|
5364
|
+
confidence: external_exports.number().min(0).max(1),
|
|
5365
|
+
rationale: external_exports.string().trim().min(1).max(200),
|
|
5366
|
+
// `locked` means the skill declares this behavior as a required step. It
|
|
5367
|
+
// can block surgical application after an explicit invocation, but never
|
|
5368
|
+
// lets a preference bypass a skill or organization requirement.
|
|
5369
|
+
locked: external_exports.boolean()
|
|
5370
|
+
}).strict();
|
|
5371
|
+
var preferenceCompatibilityFeedbackReasonCodeSchema = external_exports.enum([
|
|
5372
|
+
"workflow_alignment",
|
|
5373
|
+
"strategy_alignment",
|
|
5374
|
+
"presentation_alignment",
|
|
5375
|
+
"task_mismatch",
|
|
5376
|
+
"skill_requirement",
|
|
5377
|
+
"other_observed"
|
|
5378
|
+
]);
|
|
5379
|
+
var preferenceCompatibilityFeedbackRequestSchema = external_exports.object({
|
|
5380
|
+
query_id: external_exports.string().trim().min(1).max(160),
|
|
5381
|
+
result_id: external_exports.string().trim().min(1).max(160),
|
|
5382
|
+
preference_fingerprint: external_exports.string().regex(/^sha256:[a-f0-9]{64}$/),
|
|
5383
|
+
skill_slug: external_exports.string().trim().toLowerCase().min(1).max(160).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/),
|
|
5384
|
+
skill_version_id: external_exports.string().trim().min(1).max(160),
|
|
5385
|
+
relationship: external_exports.enum(["supports", "conflicts"]),
|
|
5386
|
+
evidence_source: external_exports.enum(["explicit_user", "agent_observed"]),
|
|
5387
|
+
reason_code: preferenceCompatibilityFeedbackReasonCodeSchema.nullable().default(null)
|
|
5388
|
+
}).strict();
|
|
5389
|
+
var REMEMBRANCE_PREFERENCE_COMPATIBILITY_FEEDBACK_TOOL_DESCRIPTION = "After actually opening or invoking a skill, record whether that exact version supports or conflicts with one server-issued active working-preference fingerprint. This is private to the authenticated organization, requires the same verified installation principal that used the correlated query/result, and never changes the skill or another organization's ranking. Use evidence_source explicit_user only for a user's explicit assessment; otherwise use agent_observed. Send only correlation IDs, the returned fingerprint, exact slug/version id, relationship, source, and an optional allowlisted reason_code; never send prompts, source code, paths, free-text feedback, or a caller-generated idempotency hash.";
|
|
5390
|
+
var preferenceCompatibilityAdminActionSchema = external_exports.discriminatedUnion(
|
|
5391
|
+
"action",
|
|
5392
|
+
[
|
|
5393
|
+
external_exports.object({
|
|
5394
|
+
action: external_exports.literal("reclassify"),
|
|
5395
|
+
preference_fingerprint: external_exports.string().regex(/^sha256:[a-f0-9]{64}$/),
|
|
5396
|
+
skill_version_id: external_exports.string().trim().min(1).max(160)
|
|
5397
|
+
}).strict(),
|
|
5398
|
+
external_exports.object({
|
|
5399
|
+
action: external_exports.literal("override"),
|
|
5400
|
+
preference_fingerprint: external_exports.string().regex(/^sha256:[a-f0-9]{64}$/),
|
|
5401
|
+
skill_version_id: external_exports.string().trim().min(1).max(160),
|
|
5402
|
+
relationship: external_exports.enum(["supports", "conflicts"]),
|
|
5403
|
+
rationale: external_exports.string().trim().min(1).max(200),
|
|
5404
|
+
locked: external_exports.boolean().default(false)
|
|
5405
|
+
}).strict(),
|
|
5406
|
+
external_exports.object({
|
|
5407
|
+
action: external_exports.literal("reset"),
|
|
5408
|
+
preference_fingerprint: external_exports.string().regex(/^sha256:[a-f0-9]{64}$/),
|
|
5409
|
+
skill_version_id: external_exports.string().trim().min(1).max(160)
|
|
5410
|
+
}).strict()
|
|
5411
|
+
]
|
|
5412
|
+
);
|
|
5338
5413
|
var INFERRED_MAX_AGE_MS = 180 * 864e5;
|
|
5339
5414
|
function builtInPreferenceDefinition(key) {
|
|
5340
5415
|
return BUILT_IN_PREFERENCE_DEFINITIONS[key] ?? null;
|
|
@@ -5474,6 +5549,7 @@ var REMEMBRANCE_MCP_READ_TOOLS = [
|
|
|
5474
5549
|
var REMEMBRANCE_MCP_FEEDBACK_TOOLS = [
|
|
5475
5550
|
"submit_query_feedback",
|
|
5476
5551
|
"submit_feedback",
|
|
5552
|
+
"submit_preference_compatibility_feedback",
|
|
5477
5553
|
"report_task_outcome"
|
|
5478
5554
|
];
|
|
5479
5555
|
var REMEMBRANCE_MCP_PRIVATE_CONTRIBUTION_TOOLS = [
|
|
@@ -6658,7 +6734,7 @@ var seedSkills = [
|
|
|
6658
6734
|
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.",
|
|
6659
6735
|
status: "active",
|
|
6660
6736
|
visibility: "public",
|
|
6661
|
-
version: "0.1.
|
|
6737
|
+
version: "0.1.19",
|
|
6662
6738
|
domains: ["agent-skills", "mcp", "resource-discovery"],
|
|
6663
6739
|
tags: [
|
|
6664
6740
|
"remembrance",
|
|
@@ -6709,7 +6785,7 @@ var seedSkills = [
|
|
|
6709
6785
|
"gemini",
|
|
6710
6786
|
"rest"
|
|
6711
6787
|
],
|
|
6712
|
-
version: "0.1.
|
|
6788
|
+
version: "0.1.19",
|
|
6713
6789
|
status: "active",
|
|
6714
6790
|
visibility: "public",
|
|
6715
6791
|
providers: ["codex", "claude", "cursor", "openclaw", "generic"],
|
|
@@ -9765,6 +9841,12 @@ var toolDefinitions = [
|
|
|
9765
9841
|
"/api/v1/agent/preferences",
|
|
9766
9842
|
recordPreferenceRequestSchema
|
|
9767
9843
|
),
|
|
9844
|
+
tool(
|
|
9845
|
+
"submit_preference_compatibility_feedback",
|
|
9846
|
+
REMEMBRANCE_PREFERENCE_COMPATIBILITY_FEEDBACK_TOOL_DESCRIPTION,
|
|
9847
|
+
"/api/v1/agent/preferences/compatibility-feedback",
|
|
9848
|
+
preferenceCompatibilityFeedbackRequestSchema
|
|
9849
|
+
),
|
|
9768
9850
|
tool(
|
|
9769
9851
|
"link_current_installation",
|
|
9770
9852
|
"Link this verified installation to the signed-in organization member using a single-use dashboard token. The token expires after ten minutes and is consumed exactly once. A valid organization principal session is required; this never changes the organization API key or creates another billed agent.",
|
|
@@ -10921,7 +11003,7 @@ async function checkClientUpdate(input) {
|
|
|
10921
11003
|
// src/server.ts
|
|
10922
11004
|
var MAX_REMOTE_RESPONSE_BYTES = 4 * 1024 * 1024;
|
|
10923
11005
|
var DOCTOR_PROBE_TIMEOUT_MS = 7500;
|
|
10924
|
-
var SERVER_VERSION = true ? "0.1.
|
|
11006
|
+
var SERVER_VERSION = true ? "0.1.62" : "0.0.0-dev";
|
|
10925
11007
|
var tools = toolDefinitions;
|
|
10926
11008
|
var doctorCliRequested = process.argv[2] === "doctor";
|
|
10927
11009
|
var inputBuffer = Buffer.alloc(0);
|
|
@@ -10931,6 +11013,7 @@ var connectedClientInfo = null;
|
|
|
10931
11013
|
var cachedValueProofKeys = null;
|
|
10932
11014
|
var PRINCIPAL_SESSION_REQUIRED_TOOLS = /* @__PURE__ */ new Set([
|
|
10933
11015
|
"record_preference",
|
|
11016
|
+
"submit_preference_compatibility_feedback",
|
|
10934
11017
|
"link_current_installation"
|
|
10935
11018
|
]);
|
|
10936
11019
|
var PRINCIPAL_SESSION_AWAITED_TOOLS = /* @__PURE__ */ new Set([
|
|
@@ -11730,6 +11813,9 @@ async function callRemembrance(definition, rawArguments, options = {}) {
|
|
|
11730
11813
|
if (refreshed && refreshed !== sentPrincipalSession) {
|
|
11731
11814
|
headers["x-remembrance-principal-session"] = refreshed;
|
|
11732
11815
|
({ response, body } = await execute());
|
|
11816
|
+
} else if (!PRINCIPAL_SESSION_REQUIRED_TOOLS.has(definition.name)) {
|
|
11817
|
+
delete headers["x-remembrance-principal-session"];
|
|
11818
|
+
({ response, body } = await execute());
|
|
11733
11819
|
}
|
|
11734
11820
|
}
|
|
11735
11821
|
} catch (error) {
|
|
@@ -11775,7 +11861,7 @@ async function localProjectKey() {
|
|
|
11775
11861
|
function principalSessionAuthenticationFailed(response, body) {
|
|
11776
11862
|
if (response.status !== 401 && response.status !== 403) return false;
|
|
11777
11863
|
if (!isRecord4(body) || typeof body.error !== "string") return false;
|
|
11778
|
-
return /principal session|economics session|installation session/i.test(
|
|
11864
|
+
return /principal session|economics session|installation session|agent principal|installation principal/i.test(
|
|
11779
11865
|
body.error
|
|
11780
11866
|
);
|
|
11781
11867
|
}
|
|
@@ -107,12 +107,11 @@ Resolution order is:
|
|
|
107
107
|
11. recommended organization guidance;
|
|
108
108
|
12. declared skill default.
|
|
109
109
|
|
|
110
|
-
Organization admins choose Required or Recommended under
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
> policy scope.
|
|
110
|
+
Organization admins choose Required or Recommended under **Dashboard > Agents >
|
|
111
|
+
Preferences**. Required applies to every organization agent. Recommended is a
|
|
112
|
+
default that a clear task or personal preference may replace. The API field
|
|
113
|
+
`project_key` is an opaque local project-context hash, not an administrator
|
|
114
|
+
policy scope.
|
|
116
115
|
|
|
117
116
|
Use `record_preference` or `POST /api/v1/agent/preferences` only for an
|
|
118
117
|
built-in or complete bounded extensible setting. Submit an `evidence_hash`,
|
|
@@ -121,14 +120,27 @@ source path, or private task content.
|
|
|
121
120
|
|
|
122
121
|
Relevance and preference influence remain separate. The normal ranker first
|
|
123
122
|
establishes applicability and the stable `high`, `possible`, or `exploratory`
|
|
124
|
-
tier.
|
|
125
|
-
|
|
123
|
+
tier. Organization-private compatibility records for the exact skill and
|
|
124
|
+
preference versions can then reorder candidates only inside that same tier.
|
|
125
|
+
The response reports qualitative matched/conflicted relationships plus
|
|
126
126
|
`relevance_rank` and `personalized_rank`; it never exposes the internal bounded
|
|
127
127
|
adjustment. A single selected skill still receives a surgical preference
|
|
128
128
|
sidecar for discretionary behavior. A locked skill requirement blocks an
|
|
129
129
|
incompatible personal preference. A locked conflict with Required organization
|
|
130
130
|
guidance makes the skill ineligible. No preference weakens a hard constraint.
|
|
131
131
|
|
|
132
|
+
Compatibility is classified asynchronously. Query and invocation do not add a
|
|
133
|
+
generative preference call or a second embedding request. Missing, stale, or
|
|
134
|
+
unavailable coverage is neutral. Preference and policy changes use leased
|
|
135
|
+
catalog sweeps, while an exact skill-version change queues only that skill for
|
|
136
|
+
affected organizations. An ineligible organization is visibly blocked and
|
|
137
|
+
resumes automatically instead of retrying classification work continuously.
|
|
138
|
+
After actual use, submit compatibility
|
|
139
|
+
feedback only with the exact `query_id`, `result_id`, skill/version, evidence
|
|
140
|
+
source, and server-issued preference fingerprint from that result's feedback
|
|
141
|
+
offer. The server derives the evidence identity and rejects expired,
|
|
142
|
+
unfetched, cross-principal, stale-version, or unoffered claims.
|
|
143
|
+
|
|
132
144
|
- An explicit durable user correction applies immediately.
|
|
133
145
|
- Inferred member or installation values require at least three consistent
|
|
134
146
|
observations across two tasks and two days with confidence at least `0.85`.
|
|
@@ -335,6 +335,7 @@ enabled_tools = [
|
|
|
335
335
|
"get_value_proof",
|
|
336
336
|
"submit_query_feedback",
|
|
337
337
|
"submit_feedback",
|
|
338
|
+
"submit_preference_compatibility_feedback",
|
|
338
339
|
"report_task_outcome",
|
|
339
340
|
"submit_remembrance",
|
|
340
341
|
"propose_private_skill",
|
|
@@ -439,6 +440,7 @@ get_resource
|
|
|
439
440
|
get_value_proof
|
|
440
441
|
submit_query_feedback
|
|
441
442
|
submit_feedback
|
|
443
|
+
submit_preference_compatibility_feedback
|
|
442
444
|
report_task_outcome
|
|
443
445
|
submit_remembrance
|
|
444
446
|
propose_private_skill
|
|
@@ -508,6 +510,7 @@ Merge this into managed settings:
|
|
|
508
510
|
"mcp__remembrance__get_value_proof",
|
|
509
511
|
"mcp__remembrance__submit_query_feedback",
|
|
510
512
|
"mcp__remembrance__submit_feedback",
|
|
513
|
+
"mcp__remembrance__submit_preference_compatibility_feedback",
|
|
511
514
|
"mcp__remembrance__report_task_outcome",
|
|
512
515
|
"mcp__remembrance__submit_remembrance",
|
|
513
516
|
"mcp__remembrance__propose_private_skill",
|
|
@@ -566,6 +569,7 @@ narrow server/tool set:
|
|
|
566
569
|
"get_value_proof",
|
|
567
570
|
"submit_query_feedback",
|
|
568
571
|
"submit_feedback",
|
|
572
|
+
"submit_preference_compatibility_feedback",
|
|
569
573
|
"report_task_outcome",
|
|
570
574
|
"submit_remembrance",
|
|
571
575
|
"propose_private_skill",
|
|
@@ -622,6 +626,7 @@ tool profile hides MCP tools, and `tools.deny: ["bundle-mcp"]` disables them:
|
|
|
622
626
|
"get_value_proof",
|
|
623
627
|
"submit_query_feedback",
|
|
624
628
|
"submit_feedback",
|
|
629
|
+
"submit_preference_compatibility_feedback",
|
|
625
630
|
"report_task_outcome",
|
|
626
631
|
"submit_remembrance",
|
|
627
632
|
"propose_private_skill",
|
package/src/index.mjs
CHANGED
|
@@ -80,6 +80,9 @@ const CONTRIBUTION_TOOLS = [
|
|
|
80
80
|
"submit_resource",
|
|
81
81
|
"submit_resource_review",
|
|
82
82
|
];
|
|
83
|
+
const PREFERENCE_EVIDENCE_TOOLS = [
|
|
84
|
+
"submit_preference_compatibility_feedback",
|
|
85
|
+
];
|
|
83
86
|
|
|
84
87
|
export function pluginVersion(readFile = readFileSync) {
|
|
85
88
|
try {
|
|
@@ -598,6 +601,13 @@ export const Remembrance = async (context = {}) => {
|
|
|
598
601
|
);
|
|
599
602
|
return;
|
|
600
603
|
}
|
|
604
|
+
if (
|
|
605
|
+
PREFERENCE_EVIDENCE_TOOLS.some((candidate) =>
|
|
606
|
+
normalizedTool.endsWith(candidate),
|
|
607
|
+
)
|
|
608
|
+
) {
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
601
611
|
if (
|
|
602
612
|
CONTRIBUTION_TOOLS.some((candidate) =>
|
|
603
613
|
normalizedTool.endsWith(candidate),
|