@remembrance-ai/opencode-plugin 0.1.55 → 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 +37 -2
- package/opencode.json +4 -3
- package/package.json +1 -1
- package/scripts/hook-core.mjs +1477 -175
- package/servers/remembrance-mcp.mjs +984 -231
- package/skills/remembrancer/SKILL.md +64 -39
- package/skills/remembrancer/references/attestation-rest.md +43 -5
- package/skills/remembrancer/references/identity-preferences-topology.md +233 -0
- package/skills/remembrancer/references/remembrance-payloads.md +13 -8
- package/skills/remembrancer/references/remembrance-setup.md +20 -0
- package/src/index.mjs +29 -9
|
@@ -38,11 +38,13 @@ function defaultAgentKeyIdForPublicKey(publicKey) {
|
|
|
38
38
|
}
|
|
39
39
|
function buildAgentKeyRegistrationSigningPayload(args) {
|
|
40
40
|
const signedAt = args.signedAt instanceof Date ? args.signedAt.toISOString() : args.signedAt;
|
|
41
|
+
const ownerBinding = args.ownerBinding?.trim() || null;
|
|
41
42
|
return canonicalJson({
|
|
42
|
-
version: "v1",
|
|
43
|
+
version: ownerBinding ? "v2" : "v1",
|
|
43
44
|
purpose: "remembrance-agent-key-registration",
|
|
44
45
|
provider: args.provider,
|
|
45
46
|
key_id: args.keyId ?? defaultAgentKeyIdForPublicKey(args.publicKey),
|
|
47
|
+
...ownerBinding ? { owner_binding: ownerBinding } : {},
|
|
46
48
|
public_key_hash: hashValue(args.publicKey),
|
|
47
49
|
subject: args.subject ?? null,
|
|
48
50
|
signed_at: signedAt
|
|
@@ -5120,6 +5122,299 @@ function stringValue(value) {
|
|
|
5120
5122
|
return typeof value === "string" && value ? value : null;
|
|
5121
5123
|
}
|
|
5122
5124
|
|
|
5125
|
+
// ../core/src/agent-preferences.ts
|
|
5126
|
+
var MAX_EFFECTIVE_PREFERENCES = 32;
|
|
5127
|
+
var MAX_SKILL_PREFERENCE_TRAITS = 40;
|
|
5128
|
+
var preferenceIdentifierSchema = external_exports.string().trim().toLowerCase().min(1).max(96).regex(/^[a-z][a-z0-9]*(?:[._-][a-z0-9]+)*$/);
|
|
5129
|
+
var preferenceKeySchema = preferenceIdentifierSchema;
|
|
5130
|
+
var preferenceValueSchema = preferenceIdentifierSchema;
|
|
5131
|
+
var preferenceEffectSchema = external_exports.enum([
|
|
5132
|
+
"presentation",
|
|
5133
|
+
"workflow",
|
|
5134
|
+
"strategy_selection"
|
|
5135
|
+
]);
|
|
5136
|
+
var preferenceStrengthSchema = external_exports.enum(["prefer", "avoid"]);
|
|
5137
|
+
var BUILT_IN_PREFERENCE_DEFINITIONS = {
|
|
5138
|
+
comment_density: {
|
|
5139
|
+
label: "Comment density",
|
|
5140
|
+
effect: "presentation",
|
|
5141
|
+
values: {
|
|
5142
|
+
sparse: "Use few comments and reserve them for decisions that code cannot explain.",
|
|
5143
|
+
balanced: "Use comments selectively for intent and non-obvious behavior.",
|
|
5144
|
+
detailed: "Use detailed comments where they improve maintainability."
|
|
5145
|
+
}
|
|
5146
|
+
},
|
|
5147
|
+
comment_focus: {
|
|
5148
|
+
label: "Comment focus",
|
|
5149
|
+
effect: "presentation",
|
|
5150
|
+
values: {
|
|
5151
|
+
intent_only: "Comment intent and tradeoffs rather than restating code.",
|
|
5152
|
+
tricky_logic: "Prioritize comments around tricky or non-obvious logic.",
|
|
5153
|
+
api_contracts: "Prioritize comments that clarify API and data contracts.",
|
|
5154
|
+
comprehensive: "Comment intent, tricky logic, and important contracts comprehensively."
|
|
5155
|
+
}
|
|
5156
|
+
},
|
|
5157
|
+
explanation_depth: {
|
|
5158
|
+
label: "Explanation depth",
|
|
5159
|
+
effect: "presentation",
|
|
5160
|
+
values: {
|
|
5161
|
+
concise: "Keep explanations concise and focused on decisions and outcomes.",
|
|
5162
|
+
balanced: "Balance concise conclusions with enough rationale to verify the work.",
|
|
5163
|
+
detailed: "Provide detailed rationale, tradeoffs, and verification context."
|
|
5164
|
+
}
|
|
5165
|
+
},
|
|
5166
|
+
output_organization: {
|
|
5167
|
+
label: "Output organization",
|
|
5168
|
+
effect: "presentation",
|
|
5169
|
+
values: {
|
|
5170
|
+
compact: "Use a compact response with minimal structural overhead.",
|
|
5171
|
+
structured: "Use clear sections when they improve scanability.",
|
|
5172
|
+
step_by_step: "Present the work as an ordered sequence of steps."
|
|
5173
|
+
}
|
|
5174
|
+
}
|
|
5175
|
+
};
|
|
5176
|
+
var PREFERENCE_KEYS = Object.keys(
|
|
5177
|
+
BUILT_IN_PREFERENCE_DEFINITIONS
|
|
5178
|
+
);
|
|
5179
|
+
var PREFERENCE_VALUE_OPTIONS = Object.fromEntries(
|
|
5180
|
+
Object.entries(BUILT_IN_PREFERENCE_DEFINITIONS).map(([key, definition]) => [
|
|
5181
|
+
key,
|
|
5182
|
+
Object.keys(definition.values)
|
|
5183
|
+
])
|
|
5184
|
+
);
|
|
5185
|
+
var preferenceSettingBaseSchema = external_exports.object({
|
|
5186
|
+
key: preferenceKeySchema,
|
|
5187
|
+
value: preferenceValueSchema,
|
|
5188
|
+
// Built-in definitions can omit these fields. Extensible definitions carry
|
|
5189
|
+
// their complete bounded meaning with the observation so no hot-path lookup
|
|
5190
|
+
// or code release is needed to understand a new preference type.
|
|
5191
|
+
label: external_exports.string().trim().min(1).max(96).optional(),
|
|
5192
|
+
behavior: external_exports.string().trim().min(1).max(320).optional(),
|
|
5193
|
+
effect: preferenceEffectSchema.optional(),
|
|
5194
|
+
strength: preferenceStrengthSchema.optional(),
|
|
5195
|
+
definition_version: external_exports.number().int().min(1).max(1e6).optional()
|
|
5196
|
+
}).strict();
|
|
5197
|
+
var preferenceSettingSchema = preferenceSettingBaseSchema.superRefine(
|
|
5198
|
+
(setting, ctx) => {
|
|
5199
|
+
const builtIn = builtInPreferenceDefinition(setting.key);
|
|
5200
|
+
if (builtIn) {
|
|
5201
|
+
const builtInBehavior = builtIn.values[setting.value];
|
|
5202
|
+
if (!builtInBehavior) {
|
|
5203
|
+
ctx.addIssue({
|
|
5204
|
+
code: external_exports.ZodIssueCode.custom,
|
|
5205
|
+
path: ["value"],
|
|
5206
|
+
message: `Unsupported ${setting.key} value`
|
|
5207
|
+
});
|
|
5208
|
+
}
|
|
5209
|
+
if (setting.label && setting.label !== builtIn.label) {
|
|
5210
|
+
ctx.addIssue({
|
|
5211
|
+
code: external_exports.ZodIssueCode.custom,
|
|
5212
|
+
path: ["label"],
|
|
5213
|
+
message: `${setting.key} uses its canonical label`
|
|
5214
|
+
});
|
|
5215
|
+
}
|
|
5216
|
+
if (setting.behavior && setting.behavior !== builtInBehavior) {
|
|
5217
|
+
ctx.addIssue({
|
|
5218
|
+
code: external_exports.ZodIssueCode.custom,
|
|
5219
|
+
path: ["behavior"],
|
|
5220
|
+
message: `${setting.key} uses the canonical behavior for ${setting.value}`
|
|
5221
|
+
});
|
|
5222
|
+
}
|
|
5223
|
+
if (setting.effect && setting.effect !== builtIn.effect) {
|
|
5224
|
+
ctx.addIssue({
|
|
5225
|
+
code: external_exports.ZodIssueCode.custom,
|
|
5226
|
+
path: ["effect"],
|
|
5227
|
+
message: `${setting.key} is a ${builtIn.effect} preference`
|
|
5228
|
+
});
|
|
5229
|
+
}
|
|
5230
|
+
if (setting.strength && setting.strength !== "prefer") {
|
|
5231
|
+
ctx.addIssue({
|
|
5232
|
+
code: external_exports.ZodIssueCode.custom,
|
|
5233
|
+
path: ["strength"],
|
|
5234
|
+
message: `${setting.key} uses prefer; define a custom key for inverse behavior`
|
|
5235
|
+
});
|
|
5236
|
+
}
|
|
5237
|
+
if (setting.definition_version !== void 0 && setting.definition_version !== 1) {
|
|
5238
|
+
ctx.addIssue({
|
|
5239
|
+
code: external_exports.ZodIssueCode.custom,
|
|
5240
|
+
path: ["definition_version"],
|
|
5241
|
+
message: `${setting.key} uses definition version 1`
|
|
5242
|
+
});
|
|
5243
|
+
}
|
|
5244
|
+
return;
|
|
5245
|
+
}
|
|
5246
|
+
for (const field of ["label", "behavior", "effect"]) {
|
|
5247
|
+
if (!setting[field]) {
|
|
5248
|
+
ctx.addIssue({
|
|
5249
|
+
code: external_exports.ZodIssueCode.custom,
|
|
5250
|
+
path: [field],
|
|
5251
|
+
message: `${field} is required for an extensible preference`
|
|
5252
|
+
});
|
|
5253
|
+
}
|
|
5254
|
+
}
|
|
5255
|
+
}
|
|
5256
|
+
);
|
|
5257
|
+
var preferenceScopeSchema = external_exports.enum([
|
|
5258
|
+
"task",
|
|
5259
|
+
"organization",
|
|
5260
|
+
"member",
|
|
5261
|
+
"member_runtime",
|
|
5262
|
+
"installation",
|
|
5263
|
+
"project",
|
|
5264
|
+
"skill",
|
|
5265
|
+
"domain"
|
|
5266
|
+
]);
|
|
5267
|
+
var preferenceSourceSchema = external_exports.enum([
|
|
5268
|
+
"mandatory_org",
|
|
5269
|
+
"explicit_task",
|
|
5270
|
+
"explicit_project",
|
|
5271
|
+
"explicit_member_runtime",
|
|
5272
|
+
"explicit_member",
|
|
5273
|
+
"learned_member_runtime",
|
|
5274
|
+
"learned_member",
|
|
5275
|
+
"explicit_installation",
|
|
5276
|
+
"learned_installation",
|
|
5277
|
+
"recommended_org",
|
|
5278
|
+
"skill_default"
|
|
5279
|
+
]);
|
|
5280
|
+
var preferenceObservationSchema = external_exports.object({
|
|
5281
|
+
setting: preferenceSettingSchema,
|
|
5282
|
+
scope: preferenceScopeSchema,
|
|
5283
|
+
source: external_exports.enum([
|
|
5284
|
+
"explicit_user",
|
|
5285
|
+
"agent_observed",
|
|
5286
|
+
"admin_override",
|
|
5287
|
+
"admin_required"
|
|
5288
|
+
]),
|
|
5289
|
+
task_hash: external_exports.string().min(1).max(128),
|
|
5290
|
+
project_key: external_exports.string().min(1).max(128).nullable().default(null),
|
|
5291
|
+
skill_slug: external_exports.string().min(1).max(512).nullable().default(null),
|
|
5292
|
+
domain: external_exports.string().min(1).max(512).nullable().default(null),
|
|
5293
|
+
confidence: external_exports.number().min(0).max(1),
|
|
5294
|
+
observed_at: external_exports.string().datetime()
|
|
5295
|
+
}).strict();
|
|
5296
|
+
function boundedUniquePreferenceSettingsSchema(duplicateMessage) {
|
|
5297
|
+
return external_exports.array(preferenceSettingSchema).max(MAX_EFFECTIVE_PREFERENCES).superRefine((settings, ctx) => {
|
|
5298
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5299
|
+
settings.forEach((setting, index) => {
|
|
5300
|
+
if (seen.has(setting.key)) {
|
|
5301
|
+
ctx.addIssue({
|
|
5302
|
+
code: external_exports.ZodIssueCode.custom,
|
|
5303
|
+
path: [index, "key"],
|
|
5304
|
+
message: duplicateMessage
|
|
5305
|
+
});
|
|
5306
|
+
}
|
|
5307
|
+
seen.add(setting.key);
|
|
5308
|
+
});
|
|
5309
|
+
});
|
|
5310
|
+
}
|
|
5311
|
+
var taskPreferenceSettingsSchema = boundedUniquePreferenceSettingsSchema(
|
|
5312
|
+
"Task preferences must use each key at most once"
|
|
5313
|
+
);
|
|
5314
|
+
var skillPreferenceDefaultsSchema = boundedUniquePreferenceSettingsSchema(
|
|
5315
|
+
"Skill preference defaults must use each key at most once"
|
|
5316
|
+
);
|
|
5317
|
+
var skillPreferenceTraitSchema = external_exports.object({
|
|
5318
|
+
key: preferenceKeySchema,
|
|
5319
|
+
value: preferenceValueSchema,
|
|
5320
|
+
relationship: external_exports.enum(["supports", "conflicts"]).default("supports"),
|
|
5321
|
+
locked: external_exports.boolean().default(false),
|
|
5322
|
+
reason: external_exports.string().trim().min(1).max(200).nullable().default(null)
|
|
5323
|
+
}).strict();
|
|
5324
|
+
var skillPreferenceTraitsSchema = external_exports.array(skillPreferenceTraitSchema).max(MAX_SKILL_PREFERENCE_TRAITS).superRefine((traits, ctx) => {
|
|
5325
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5326
|
+
traits.forEach((trait, index) => {
|
|
5327
|
+
const identity = `${trait.key}:${trait.value}:${trait.relationship}`;
|
|
5328
|
+
if (seen.has(identity)) {
|
|
5329
|
+
ctx.addIssue({
|
|
5330
|
+
code: external_exports.ZodIssueCode.custom,
|
|
5331
|
+
path: [index],
|
|
5332
|
+
message: "Skill preference traits must be unique"
|
|
5333
|
+
});
|
|
5334
|
+
}
|
|
5335
|
+
seen.add(identity);
|
|
5336
|
+
});
|
|
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
|
+
);
|
|
5413
|
+
var INFERRED_MAX_AGE_MS = 180 * 864e5;
|
|
5414
|
+
function builtInPreferenceDefinition(key) {
|
|
5415
|
+
return BUILT_IN_PREFERENCE_DEFINITIONS[key] ?? null;
|
|
5416
|
+
}
|
|
5417
|
+
|
|
5123
5418
|
// ../core/src/agent-guidance.ts
|
|
5124
5419
|
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.";
|
|
5125
5420
|
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.";
|
|
@@ -5246,6 +5541,7 @@ var REMEMBRANCE_MCP_READ_TOOLS = [
|
|
|
5246
5541
|
"query_skills",
|
|
5247
5542
|
"list_skills",
|
|
5248
5543
|
"invoke_skill",
|
|
5544
|
+
"get_effective_preferences",
|
|
5249
5545
|
"get_skill",
|
|
5250
5546
|
"get_resource",
|
|
5251
5547
|
"get_value_proof"
|
|
@@ -5253,12 +5549,15 @@ var REMEMBRANCE_MCP_READ_TOOLS = [
|
|
|
5253
5549
|
var REMEMBRANCE_MCP_FEEDBACK_TOOLS = [
|
|
5254
5550
|
"submit_query_feedback",
|
|
5255
5551
|
"submit_feedback",
|
|
5552
|
+
"submit_preference_compatibility_feedback",
|
|
5256
5553
|
"report_task_outcome"
|
|
5257
5554
|
];
|
|
5258
5555
|
var REMEMBRANCE_MCP_PRIVATE_CONTRIBUTION_TOOLS = [
|
|
5259
5556
|
"submit_remembrance",
|
|
5260
5557
|
"propose_private_skill",
|
|
5261
|
-
"submit_suggestion"
|
|
5558
|
+
"submit_suggestion",
|
|
5559
|
+
"record_preference",
|
|
5560
|
+
"link_current_installation"
|
|
5262
5561
|
];
|
|
5263
5562
|
var REMEMBRANCE_MCP_OPTIONAL_SHARED_CONTRIBUTION_TOOLS = [
|
|
5264
5563
|
"propose_skill_idea",
|
|
@@ -5336,6 +5635,49 @@ function remembranceSkillResourceHandle(entry) {
|
|
|
5336
5635
|
);
|
|
5337
5636
|
}
|
|
5338
5637
|
|
|
5638
|
+
// ../core/src/skill-topology.ts
|
|
5639
|
+
var skillTopologyActionSchema = external_exports.enum([
|
|
5640
|
+
"amend",
|
|
5641
|
+
"specialize",
|
|
5642
|
+
"strategy_fork",
|
|
5643
|
+
"independent_skill",
|
|
5644
|
+
"preference",
|
|
5645
|
+
"evidence_only",
|
|
5646
|
+
"hold"
|
|
5647
|
+
]);
|
|
5648
|
+
var SKILL_TOPOLOGY_ACTIONS = skillTopologyActionSchema.options;
|
|
5649
|
+
var skillScopePredicateSchema = external_exports.object({
|
|
5650
|
+
dimension: external_exports.enum([
|
|
5651
|
+
"runtime",
|
|
5652
|
+
"runtime_version",
|
|
5653
|
+
"platform",
|
|
5654
|
+
"language",
|
|
5655
|
+
"framework",
|
|
5656
|
+
"dependency",
|
|
5657
|
+
"scale",
|
|
5658
|
+
"task_stage"
|
|
5659
|
+
]),
|
|
5660
|
+
operator: external_exports.enum(["equals", "includes", "at_least", "at_most"]),
|
|
5661
|
+
value: external_exports.string().trim().min(1).max(256)
|
|
5662
|
+
}).strict();
|
|
5663
|
+
var skillTopologyAssessmentSchema = external_exports.object({
|
|
5664
|
+
action: skillTopologyActionSchema,
|
|
5665
|
+
confidence: external_exports.number().min(0).max(1),
|
|
5666
|
+
subjectivity: external_exports.number().min(0).max(1),
|
|
5667
|
+
generalizability: external_exports.number().min(0).max(1),
|
|
5668
|
+
specificity_delta: external_exports.number().min(-1).max(1),
|
|
5669
|
+
approach_divergence: external_exports.number().min(0).max(1),
|
|
5670
|
+
stable_conditions: external_exports.array(skillScopePredicateSchema).max(12).default([]),
|
|
5671
|
+
use_when: external_exports.array(external_exports.string().trim().min(1).max(512)).max(20).default([]),
|
|
5672
|
+
avoid_when: external_exports.array(external_exports.string().trim().min(1).max(512)).max(20).default([]),
|
|
5673
|
+
preference_setting: preferenceSettingSchema.nullable().default(null),
|
|
5674
|
+
rationale: external_exports.string().trim().min(1).max(2e3)
|
|
5675
|
+
}).strict();
|
|
5676
|
+
var skillTopologyRoutingHintSchema = external_exports.object({
|
|
5677
|
+
suggested_action: skillTopologyActionSchema,
|
|
5678
|
+
conditions: external_exports.array(skillScopePredicateSchema).max(12).default([])
|
|
5679
|
+
}).strict();
|
|
5680
|
+
|
|
5339
5681
|
// ../core/src/schemas.ts
|
|
5340
5682
|
var DEFAULT_MUTATION_BODY_LIMIT_BYTES = 256 * 1024;
|
|
5341
5683
|
var MAX_SHORT_TEXT_LENGTH = 512;
|
|
@@ -5554,7 +5896,11 @@ var agentSchema = external_exports.object({
|
|
|
5554
5896
|
var queryTaskSchema = external_exports.object({
|
|
5555
5897
|
domain: boundedString(MAX_SHORT_TEXT_LENGTH),
|
|
5556
5898
|
summary: boundedString(MAX_LONG_TEXT_LENGTH),
|
|
5557
|
-
constraints: external_exports.array(boundedString(MAX_SHORT_TEXT_LENGTH)).max(40).default([])
|
|
5899
|
+
constraints: external_exports.array(boundedString(MAX_SHORT_TEXT_LENGTH)).max(40).default([]),
|
|
5900
|
+
// Explicit task-local preferences are normalized data, not raw prompt text.
|
|
5901
|
+
// They are resolved for this request only and are never persisted as a
|
|
5902
|
+
// durable member preference unless the caller separately records them.
|
|
5903
|
+
preferences: taskPreferenceSettingsSchema.optional()
|
|
5558
5904
|
});
|
|
5559
5905
|
var queryRuntimeSchema = external_exports.enum([
|
|
5560
5906
|
"codex",
|
|
@@ -5566,12 +5912,23 @@ var queryRuntimeSchema = external_exports.enum([
|
|
|
5566
5912
|
"other",
|
|
5567
5913
|
"unknown"
|
|
5568
5914
|
]);
|
|
5915
|
+
var runtimeHostSurfaceSchema = external_exports.enum([
|
|
5916
|
+
"desktop",
|
|
5917
|
+
"cli",
|
|
5918
|
+
"extension",
|
|
5919
|
+
"gateway",
|
|
5920
|
+
"unknown"
|
|
5921
|
+
]);
|
|
5922
|
+
var runtimeProfileVersionSchema = external_exports.string().trim().min(1).max(64).regex(/^[A-Za-z0-9][A-Za-z0-9._+-]*$/);
|
|
5569
5923
|
var queryDirectiveIdSchema = external_exports.string().regex(/^dir_[A-Za-z0-9_-]{16,80}$/);
|
|
5570
5924
|
var queryClientContextSchema = external_exports.object({
|
|
5571
5925
|
surface: external_exports.enum(["plugin_hook", "mcp", "rest", "unknown"]).default("unknown"),
|
|
5572
5926
|
runtime: queryRuntimeSchema.optional(),
|
|
5573
5927
|
trigger_reason: boundedString(MAX_SHORT_TEXT_LENGTH).optional(),
|
|
5574
|
-
directive_id: queryDirectiveIdSchema.optional()
|
|
5928
|
+
directive_id: queryDirectiveIdSchema.optional(),
|
|
5929
|
+
// A local, installation-keyed digest. It lets one engineer retain
|
|
5930
|
+
// project-scoped preferences without disclosing a path or repository.
|
|
5931
|
+
project_key: external_exports.string().regex(/^prj_[A-Za-z0-9_-]{12,120}$/).optional()
|
|
5575
5932
|
}).strict();
|
|
5576
5933
|
var reasoningEffortSchema = external_exports.enum([
|
|
5577
5934
|
"none",
|
|
@@ -5794,31 +6151,145 @@ var agentTaskOutcomeRequestSchema = external_exports.object({
|
|
|
5794
6151
|
});
|
|
5795
6152
|
}
|
|
5796
6153
|
});
|
|
5797
|
-
var
|
|
6154
|
+
var runtimeProfileRegistrationSchema = external_exports.object({
|
|
6155
|
+
runtime: queryRuntimeSchema,
|
|
6156
|
+
surface: external_exports.enum(["plugin_hook", "mcp", "rest", "unknown"]),
|
|
6157
|
+
host_surface: runtimeHostSurfaceSchema.default("unknown"),
|
|
6158
|
+
client_name: external_exports.string().trim().min(1).max(64).regex(/^[A-Za-z0-9][A-Za-z0-9 ._+-]*$/),
|
|
6159
|
+
client_version: runtimeProfileVersionSchema.nullable().optional(),
|
|
6160
|
+
runtime_version: runtimeProfileVersionSchema.nullable().optional(),
|
|
6161
|
+
profile_key: external_exports.string().regex(/^rpf_[A-Za-z0-9_-]{16,120}$/)
|
|
6162
|
+
}).strict();
|
|
6163
|
+
var principalSessionChallengeSchema = external_exports.object({
|
|
5798
6164
|
action: external_exports.literal("challenge"),
|
|
5799
6165
|
provider: economicsSessionProviderSchema,
|
|
5800
|
-
key_id: boundedString(MAX_SHORT_TEXT_LENGTH)
|
|
6166
|
+
key_id: boundedString(MAX_SHORT_TEXT_LENGTH),
|
|
6167
|
+
runtime_profile: runtimeProfileRegistrationSchema.nullable().optional(),
|
|
6168
|
+
member_link_token: external_exports.string().regex(/^mlink_[A-Za-z0-9_-]{24,160}$/).nullable().optional()
|
|
5801
6169
|
}).strict();
|
|
5802
|
-
var
|
|
6170
|
+
var principalSessionExchangeSchema = external_exports.object({
|
|
5803
6171
|
action: external_exports.literal("exchange"),
|
|
5804
6172
|
provider: economicsSessionProviderSchema,
|
|
5805
6173
|
key_id: boundedString(MAX_SHORT_TEXT_LENGTH),
|
|
5806
6174
|
challenge_id: boundedString(MAX_SHORT_TEXT_LENGTH),
|
|
5807
6175
|
signature: boundedString(MAX_LONG_TEXT_LENGTH)
|
|
5808
6176
|
}).strict();
|
|
5809
|
-
var
|
|
5810
|
-
|
|
5811
|
-
|
|
6177
|
+
var principalSessionRequestSchema = external_exports.discriminatedUnion("action", [
|
|
6178
|
+
principalSessionChallengeSchema,
|
|
6179
|
+
principalSessionExchangeSchema
|
|
5812
6180
|
]);
|
|
6181
|
+
var recordPreferenceRequestSchema = external_exports.object({
|
|
6182
|
+
setting: preferenceSettingSchema,
|
|
6183
|
+
// Organization guidance is dashboard-owned policy. Agent transports may
|
|
6184
|
+
// record only identity-bound or task-context preference observations.
|
|
6185
|
+
scope: external_exports.enum([
|
|
6186
|
+
"auto",
|
|
6187
|
+
"member",
|
|
6188
|
+
"member_runtime",
|
|
6189
|
+
"installation",
|
|
6190
|
+
"project",
|
|
6191
|
+
"skill",
|
|
6192
|
+
"domain"
|
|
6193
|
+
]),
|
|
6194
|
+
source_category: external_exports.enum(["explicit_user", "agent_observed"]),
|
|
6195
|
+
evidence_hash: external_exports.string().regex(/^[a-f0-9]{32,128}$/i),
|
|
6196
|
+
task_hash: external_exports.string().regex(/^[a-f0-9]{32,128}$/i),
|
|
6197
|
+
project_key: external_exports.string().regex(/^prj_[A-Za-z0-9_-]{12,120}$/).nullable().optional(),
|
|
6198
|
+
skill_slug: boundedString(MAX_SHORT_TEXT_LENGTH).nullable().optional(),
|
|
6199
|
+
domain: boundedString(MAX_SHORT_TEXT_LENGTH).nullable().optional(),
|
|
6200
|
+
confidence: external_exports.number().min(0).max(1)
|
|
6201
|
+
}).strict().superRefine((value, ctx) => {
|
|
6202
|
+
const required = value.scope === "project" ? "project_key" : value.scope === "skill" ? "skill_slug" : value.scope === "domain" ? "domain" : null;
|
|
6203
|
+
if (required && !value[required]) {
|
|
6204
|
+
ctx.addIssue({
|
|
6205
|
+
code: external_exports.ZodIssueCode.custom,
|
|
6206
|
+
path: [required],
|
|
6207
|
+
message: `${required} is required for ${value.scope} preferences`
|
|
6208
|
+
});
|
|
6209
|
+
}
|
|
6210
|
+
if (value.source_category === "agent_observed" && value.confidence >= 1) {
|
|
6211
|
+
ctx.addIssue({
|
|
6212
|
+
code: external_exports.ZodIssueCode.custom,
|
|
6213
|
+
path: ["confidence"],
|
|
6214
|
+
message: "Inferred observations cannot claim absolute confidence"
|
|
6215
|
+
});
|
|
6216
|
+
}
|
|
6217
|
+
});
|
|
6218
|
+
var effectivePreferencesRequestSchema = external_exports.object({
|
|
6219
|
+
// Omit or send [] to resolve every active bounded preference. Supplying
|
|
6220
|
+
// keys is an optional projection for clients that need only a subset.
|
|
6221
|
+
keys: external_exports.array(preferenceKeySchema).max(MAX_EFFECTIVE_PREFERENCES).default([]),
|
|
6222
|
+
task_preferences: taskPreferenceSettingsSchema.optional(),
|
|
6223
|
+
project_key: external_exports.string().regex(/^prj_[A-Za-z0-9_-]{12,120}$/).nullable().optional(),
|
|
6224
|
+
skill_slug: boundedString(MAX_SHORT_TEXT_LENGTH).nullable().optional(),
|
|
6225
|
+
domains: external_exports.array(boundedString(MAX_SHORT_TEXT_LENGTH)).max(20).default([])
|
|
6226
|
+
}).strict();
|
|
5813
6227
|
var agentPrincipalRegistrationRequestSchema = external_exports.object({
|
|
5814
6228
|
display_name: boundedString(MAX_SHORT_TEXT_LENGTH),
|
|
5815
6229
|
provider: agentProviderSchema,
|
|
5816
6230
|
runtime_version: boundedString(MAX_SHORT_TEXT_LENGTH).nullable().optional(),
|
|
5817
6231
|
parent_principal_id: boundedString(MAX_SHORT_TEXT_LENGTH).nullable().optional()
|
|
5818
6232
|
}).strict();
|
|
5819
|
-
var agentPrincipalUpdateRequestSchema = external_exports.
|
|
5820
|
-
|
|
5821
|
-
|
|
6233
|
+
var agentPrincipalUpdateRequestSchema = external_exports.discriminatedUnion(
|
|
6234
|
+
"action",
|
|
6235
|
+
[
|
|
6236
|
+
external_exports.object({
|
|
6237
|
+
principal_id: boundedString(MAX_SHORT_TEXT_LENGTH),
|
|
6238
|
+
action: external_exports.enum(["deactivate", "reactivate"])
|
|
6239
|
+
}).strict(),
|
|
6240
|
+
external_exports.object({
|
|
6241
|
+
principal_id: boundedString(MAX_SHORT_TEXT_LENGTH),
|
|
6242
|
+
action: external_exports.literal("rename"),
|
|
6243
|
+
display_name: boundedString(MAX_SHORT_TEXT_LENGTH)
|
|
6244
|
+
}).strict()
|
|
6245
|
+
]
|
|
6246
|
+
);
|
|
6247
|
+
var agentInstallationManagementRequestSchema = external_exports.discriminatedUnion(
|
|
6248
|
+
"action",
|
|
6249
|
+
[
|
|
6250
|
+
external_exports.object({
|
|
6251
|
+
action: external_exports.literal("rename"),
|
|
6252
|
+
installation_id: boundedString(MAX_SHORT_TEXT_LENGTH),
|
|
6253
|
+
display_name: boundedString(MAX_SHORT_TEXT_LENGTH)
|
|
6254
|
+
}).strict(),
|
|
6255
|
+
external_exports.object({
|
|
6256
|
+
action: external_exports.literal("rename_runtime_profile"),
|
|
6257
|
+
runtime_profile_id: boundedString(MAX_SHORT_TEXT_LENGTH),
|
|
6258
|
+
display_name: boundedString(MAX_SHORT_TEXT_LENGTH)
|
|
6259
|
+
}).strict(),
|
|
6260
|
+
external_exports.object({
|
|
6261
|
+
action: external_exports.literal("set_status"),
|
|
6262
|
+
installation_id: boundedString(MAX_SHORT_TEXT_LENGTH),
|
|
6263
|
+
status: external_exports.enum(["active", "inactive"])
|
|
6264
|
+
}).strict(),
|
|
6265
|
+
external_exports.object({
|
|
6266
|
+
action: external_exports.literal("assign_member"),
|
|
6267
|
+
installation_id: boundedString(MAX_SHORT_TEXT_LENGTH),
|
|
6268
|
+
member_principal_id: boundedString(MAX_SHORT_TEXT_LENGTH)
|
|
6269
|
+
}).strict(),
|
|
6270
|
+
external_exports.object({
|
|
6271
|
+
action: external_exports.literal("unlink_member"),
|
|
6272
|
+
installation_id: boundedString(MAX_SHORT_TEXT_LENGTH)
|
|
6273
|
+
}).strict()
|
|
6274
|
+
]
|
|
6275
|
+
);
|
|
6276
|
+
var dashboardPreferenceMutationSchema = external_exports.discriminatedUnion(
|
|
6277
|
+
"action",
|
|
6278
|
+
[
|
|
6279
|
+
external_exports.object({
|
|
6280
|
+
action: external_exports.literal("set"),
|
|
6281
|
+
setting: preferenceSettingSchema,
|
|
6282
|
+
scope: external_exports.enum(["member", "organization"]),
|
|
6283
|
+
enforcement: external_exports.enum(["recommended", "required"]).default("recommended")
|
|
6284
|
+
}).strict(),
|
|
6285
|
+
external_exports.object({
|
|
6286
|
+
action: external_exports.enum(["reset", "undo"]),
|
|
6287
|
+
revision_id: boundedString(MAX_SHORT_TEXT_LENGTH)
|
|
6288
|
+
}).strict()
|
|
6289
|
+
]
|
|
6290
|
+
);
|
|
6291
|
+
var agentMemberLinkRequestSchema = external_exports.object({
|
|
6292
|
+
member_link_token: external_exports.string().regex(/^mlink_[A-Za-z0-9_-]{24,160}$/)
|
|
5822
6293
|
}).strict();
|
|
5823
6294
|
var agentPrincipalKeyBindingRequestSchema = external_exports.object({
|
|
5824
6295
|
principal_id: boundedString(MAX_SHORT_TEXT_LENGTH),
|
|
@@ -5901,6 +6372,7 @@ var remembrancePayloadSchema = external_exports.object({
|
|
|
5901
6372
|
}).strict().optional(),
|
|
5902
6373
|
enterprise_encryption: enterpriseEncryptedPayloadEnvelopeSchema.optional(),
|
|
5903
6374
|
suggested_update: suggestedUpdateSchema.default({ kind: "none" }),
|
|
6375
|
+
routing_hint: skillTopologyRoutingHintSchema.optional(),
|
|
5904
6376
|
evidence: evidenceSchema.default({ artifact_hashes: [] }),
|
|
5905
6377
|
// Client-only directive: "sign this with my local TOFU key" (the MCP
|
|
5906
6378
|
// submit_remembrance tool exposes it, and feedback-next-step tells REST
|
|
@@ -5935,6 +6407,8 @@ var skillMetadataSchema = external_exports.object({
|
|
|
5935
6407
|
permissions: external_exports.record(external_exports.boolean()).default({}),
|
|
5936
6408
|
contraindications: external_exports.array(external_exports.string().max(MAX_LONG_TEXT_LENGTH)).max(80).default([]),
|
|
5937
6409
|
applicability: skillApplicabilitySchema.optional(),
|
|
6410
|
+
preference_defaults: skillPreferenceDefaultsSchema.optional(),
|
|
6411
|
+
preference_traits: skillPreferenceTraitsSchema.optional(),
|
|
5938
6412
|
feedback_url: external_exports.string().url(),
|
|
5939
6413
|
install_command: external_exports.string().min(1),
|
|
5940
6414
|
stats: external_exports.object({
|
|
@@ -5967,6 +6441,7 @@ var skillIdeaRequestSchema = external_exports.object({
|
|
|
5967
6441
|
domain_slug: boundedString(MAX_SHORT_TEXT_LENGTH).nullable().optional(),
|
|
5968
6442
|
proposed_skill_md: external_exports.string().max(MAX_LONG_TEXT_LENGTH).nullable().optional(),
|
|
5969
6443
|
proposed_metadata: boundedJsonRecord().default({}),
|
|
6444
|
+
routing_hint: skillTopologyRoutingHintSchema.optional(),
|
|
5970
6445
|
enterprise_encryption: enterpriseEncryptedPayloadEnvelopeSchema.optional(),
|
|
5971
6446
|
idempotency_key: boundedString(MAX_SHORT_TEXT_LENGTH).optional()
|
|
5972
6447
|
});
|
|
@@ -5979,6 +6454,7 @@ var suggestionRequestSchema = external_exports.object({
|
|
|
5979
6454
|
summary: boundedString(MAX_LONG_TEXT_LENGTH),
|
|
5980
6455
|
diff_text: external_exports.string().max(MAX_LONG_TEXT_LENGTH).nullable().optional(),
|
|
5981
6456
|
payload: boundedJsonRecord().default({}),
|
|
6457
|
+
routing_hint: skillTopologyRoutingHintSchema.optional(),
|
|
5982
6458
|
enterprise_encryption: enterpriseEncryptedPayloadEnvelopeSchema.optional(),
|
|
5983
6459
|
idempotency_key: boundedString(MAX_SHORT_TEXT_LENGTH).optional()
|
|
5984
6460
|
});
|
|
@@ -6059,6 +6535,7 @@ var attestationKeyRegistrationRequestSchema = external_exports.object({
|
|
|
6059
6535
|
agent: agentSchema.optional(),
|
|
6060
6536
|
proof: external_exports.object({
|
|
6061
6537
|
algorithm: external_exports.enum(["ed25519"]).default("ed25519"),
|
|
6538
|
+
owner_binding: external_exports.string().regex(/^areg_[A-Za-z0-9_-]{24,120}$/).optional(),
|
|
6062
6539
|
signed_at: external_exports.string().datetime(),
|
|
6063
6540
|
signature: boundedString(MAX_LONG_TEXT_LENGTH)
|
|
6064
6541
|
}),
|
|
@@ -6257,7 +6734,7 @@ var seedSkills = [
|
|
|
6257
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.",
|
|
6258
6735
|
status: "active",
|
|
6259
6736
|
visibility: "public",
|
|
6260
|
-
version: "0.1.
|
|
6737
|
+
version: "0.1.19",
|
|
6261
6738
|
domains: ["agent-skills", "mcp", "resource-discovery"],
|
|
6262
6739
|
tags: [
|
|
6263
6740
|
"remembrance",
|
|
@@ -6308,7 +6785,7 @@ var seedSkills = [
|
|
|
6308
6785
|
"gemini",
|
|
6309
6786
|
"rest"
|
|
6310
6787
|
],
|
|
6311
|
-
version: "0.1.
|
|
6788
|
+
version: "0.1.19",
|
|
6312
6789
|
status: "active",
|
|
6313
6790
|
visibility: "public",
|
|
6314
6791
|
providers: ["codex", "claude", "cursor", "openclaw", "generic"],
|
|
@@ -7974,7 +8451,14 @@ var verifierOutputSchema = external_exports.object({
|
|
|
7974
8451
|
}).nullable().default(null),
|
|
7975
8452
|
// Advisory retrieval enrichment (see proposedSkillMetadataSchema). Optional
|
|
7976
8453
|
// and defaults to null so existing verifier outputs remain valid.
|
|
7977
|
-
proposed_metadata: proposedSkillMetadataSchema.nullable().default(null)
|
|
8454
|
+
proposed_metadata: proposedSkillMetadataSchema.nullable().default(null),
|
|
8455
|
+
// Advisory evidence-routing assessment. Deterministic routing and the
|
|
8456
|
+
// existing safety/review gates decide whether it can create a candidate.
|
|
8457
|
+
// Topology is advisory and newer than the base verifier contract. A legacy
|
|
8458
|
+
// or malformed topology fragment must not discard an otherwise valid safety
|
|
8459
|
+
// verdict; it degrades to null, which the topology router retains as
|
|
8460
|
+
// evidence-only rather than allowing an unclassified mutation.
|
|
8461
|
+
topology_assessment: skillTopologyAssessmentSchema.nullable().default(null).catch(null)
|
|
7978
8462
|
});
|
|
7979
8463
|
|
|
7980
8464
|
// src/server.ts
|
|
@@ -7982,6 +8466,8 @@ import { existsSync as existsSync3 } from "node:fs";
|
|
|
7982
8466
|
import { mkdir as mkdir2, writeFile as writeFile2 } from "node:fs/promises";
|
|
7983
8467
|
import { dirname as dirname2 } from "node:path";
|
|
7984
8468
|
import {
|
|
8469
|
+
createHash as createHash3,
|
|
8470
|
+
createHmac,
|
|
7985
8471
|
createPrivateKey as createPrivateKey2,
|
|
7986
8472
|
generateKeyPairSync,
|
|
7987
8473
|
sign as signPayload
|
|
@@ -9343,6 +9829,30 @@ var toolDefinitions = [
|
|
|
9343
9829
|
"/api/v1/agent/skill-invocations",
|
|
9344
9830
|
agentSkillInvocationRequestSchema
|
|
9345
9831
|
),
|
|
9832
|
+
tool(
|
|
9833
|
+
"get_effective_preferences",
|
|
9834
|
+
"Return bounded, task-relevant working preferences for this verified installation and optional linked member/runtime profile. project_key is an opaque local project-context hash; paths and repository names are never transmitted. A clear current-task preference overrides every non-mandatory stored preference; Required organization guidance remains authoritative. Preferences may steer among already-relevant skills and surgically alter discretionary presentation, workflow, or strategy choices. They never change relevance tiers or weaken applicability, safety, authorization, privacy, required skill steps, validation, or review. Clients without a valid principal session remain organization-level and receive no personal preferences.",
|
|
9835
|
+
"/api/v1/agent/preferences/effective",
|
|
9836
|
+
effectivePreferencesRequestSchema
|
|
9837
|
+
),
|
|
9838
|
+
tool(
|
|
9839
|
+
"record_preference",
|
|
9840
|
+
"Record a bounded, privacy-safe preference observation for the current verified installation. Built-in settings need only key and value; a custom setting also supplies its plain-language label, behavior, presentation/workflow/strategy_selection effect, prefer/avoid direction, and definition version. Send normalized settings and hashes only, never prompt text. A valid principal session is required. Explicit user preferences apply immediately; inferred observations activate only after Remembrance's consistency and confidence thresholds are met.",
|
|
9841
|
+
"/api/v1/agent/preferences",
|
|
9842
|
+
recordPreferenceRequestSchema
|
|
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
|
+
),
|
|
9850
|
+
tool(
|
|
9851
|
+
"link_current_installation",
|
|
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.",
|
|
9853
|
+
"/api/v1/agent/member-links",
|
|
9854
|
+
agentMemberLinkRequestSchema
|
|
9855
|
+
),
|
|
9346
9856
|
tool(
|
|
9347
9857
|
"get_skill",
|
|
9348
9858
|
"Fetch the exact active reviewed skill body and version after query_skills returns a known slug. The response includes skill_md, version_id, and source. Pass query_id and result_id from that response so Remembrance can measure whether surfaced guidance was opened. Do not guess private or inactive slugs.",
|
|
@@ -9465,7 +9975,7 @@ function localTool(name, description, local, schema) {
|
|
|
9465
9975
|
};
|
|
9466
9976
|
}
|
|
9467
9977
|
function annotationsForTool(name) {
|
|
9468
|
-
const readOnly = name === "list_skills" || name === "get_value_proof" || name === "run_connection_doctor";
|
|
9978
|
+
const readOnly = name === "list_skills" || name === "get_effective_preferences" || name === "get_value_proof" || name === "run_connection_doctor";
|
|
9469
9979
|
const openWorld = name === "submit_feedback" || name === "submit_remembrance" || name === "propose_skill_idea" || name === "submit_suggestion" || name === "submit_resource" || name === "submit_resource_review";
|
|
9470
9980
|
return {
|
|
9471
9981
|
readOnlyHint: readOnly,
|
|
@@ -9866,107 +10376,104 @@ function remembranceConfigPath(env = process.env, homeDirectory = homedir3()) {
|
|
|
9866
10376
|
return join3(configRoot, "remembrance", "config.json");
|
|
9867
10377
|
}
|
|
9868
10378
|
function resolveApiCredential(env = process.env, fileSystem = nodeFileSystem2) {
|
|
9869
|
-
|
|
10379
|
+
return resolveApiAccess(env, fileSystem).credential;
|
|
10380
|
+
}
|
|
10381
|
+
function resolveApiAccess(env = process.env, fileSystem = nodeFileSystem2) {
|
|
10382
|
+
const shared = readSharedConfigSnapshot(env, fileSystem);
|
|
10383
|
+
const configuration = resolveApiConfigurationFromSnapshot(env, shared);
|
|
9870
10384
|
const environmentKey = env.REMEMBRANCE_API_KEY?.trim();
|
|
10385
|
+
let credential;
|
|
9871
10386
|
if (environmentKey) {
|
|
9872
10387
|
const explicitBinding = env.REMEMBRANCE_API_KEY_ORIGIN?.trim();
|
|
9873
10388
|
const binding = explicitBinding ? normalizeApiUrl(explicitBinding, env) : { baseUrl: DEFAULT_API_URL, issue: null };
|
|
9874
10389
|
if (!binding.baseUrl || binding.issue) {
|
|
9875
|
-
|
|
10390
|
+
credential = unusableDestinationBinding();
|
|
10391
|
+
} else {
|
|
10392
|
+
credential = credentialForDestination(
|
|
10393
|
+
{
|
|
10394
|
+
apiKey: environmentKey,
|
|
10395
|
+
source: "environment",
|
|
10396
|
+
boundBaseUrl: binding.baseUrl
|
|
10397
|
+
},
|
|
10398
|
+
configuration
|
|
10399
|
+
);
|
|
10400
|
+
}
|
|
10401
|
+
} else if (!shared.present) {
|
|
10402
|
+
credential = { apiKey: "", source: "none" };
|
|
10403
|
+
} else if (!shared.parsed || Object.prototype.hasOwnProperty.call(shared.parsed, "apiKey") && (typeof shared.parsed.apiKey !== "string" || !shared.parsed.apiKey.trim())) {
|
|
10404
|
+
credential = unusableSharedConfig();
|
|
10405
|
+
} else if (!shared.config.apiKey) {
|
|
10406
|
+
credential = { apiKey: "", source: "none" };
|
|
10407
|
+
} else {
|
|
10408
|
+
const binding = shared.config.apiUrl ? normalizeApiUrl(shared.config.apiUrl, env) : { baseUrl: DEFAULT_API_URL, issue: null };
|
|
10409
|
+
if (!binding.baseUrl || binding.issue) {
|
|
10410
|
+
credential = unusableSharedConfig();
|
|
10411
|
+
} else {
|
|
10412
|
+
credential = credentialForDestination(
|
|
10413
|
+
{
|
|
10414
|
+
apiKey: shared.config.apiKey,
|
|
10415
|
+
source: "shared_config",
|
|
10416
|
+
boundBaseUrl: binding.baseUrl
|
|
10417
|
+
},
|
|
10418
|
+
configuration
|
|
10419
|
+
);
|
|
9876
10420
|
}
|
|
9877
|
-
return credentialForDestination(
|
|
9878
|
-
{
|
|
9879
|
-
apiKey: environmentKey,
|
|
9880
|
-
source: "environment",
|
|
9881
|
-
boundBaseUrl: binding.baseUrl
|
|
9882
|
-
},
|
|
9883
|
-
apiConfiguration2
|
|
9884
|
-
);
|
|
9885
10421
|
}
|
|
10422
|
+
return {
|
|
10423
|
+
configuration,
|
|
10424
|
+
credential,
|
|
10425
|
+
memberLinkToken: env.REMEMBRANCE_MEMBER_LINK_TOKEN?.trim() ?? shared.config.memberLinkToken ?? ""
|
|
10426
|
+
};
|
|
10427
|
+
}
|
|
10428
|
+
function isUnusableCredentialSource(source) {
|
|
10429
|
+
return source.startsWith("unusable_");
|
|
10430
|
+
}
|
|
10431
|
+
function readSharedConfigSnapshot(env, fileSystem) {
|
|
9886
10432
|
const path = remembranceConfigPath(env);
|
|
9887
10433
|
if (!fileSystem.exists(path)) {
|
|
9888
|
-
return {
|
|
10434
|
+
return { present: false, parsed: null, config: {} };
|
|
9889
10435
|
}
|
|
9890
10436
|
try {
|
|
9891
10437
|
const parsed = JSON.parse(
|
|
9892
10438
|
fileSystem.read(path, MAX_LOCAL_CONFIG_BYTES)
|
|
9893
10439
|
);
|
|
9894
|
-
|
|
9895
|
-
return unusableSharedConfig();
|
|
9896
|
-
}
|
|
9897
|
-
if (Object.prototype.hasOwnProperty.call(parsed, "apiKey") && (typeof parsed.apiKey !== "string" || !parsed.apiKey.trim())) {
|
|
9898
|
-
return unusableSharedConfig();
|
|
9899
|
-
}
|
|
9900
|
-
const config = normalizeConfig(parsed);
|
|
9901
|
-
if (!config.apiKey) {
|
|
9902
|
-
return { apiKey: "", source: "none" };
|
|
9903
|
-
}
|
|
9904
|
-
const binding = config.apiUrl ? normalizeApiUrl(config.apiUrl, env) : { baseUrl: DEFAULT_API_URL, issue: null };
|
|
9905
|
-
if (!binding.baseUrl || binding.issue) {
|
|
9906
|
-
return unusableSharedConfig();
|
|
9907
|
-
}
|
|
9908
|
-
return credentialForDestination(
|
|
9909
|
-
{
|
|
9910
|
-
apiKey: config.apiKey,
|
|
9911
|
-
source: "shared_config",
|
|
9912
|
-
boundBaseUrl: binding.baseUrl
|
|
9913
|
-
},
|
|
9914
|
-
apiConfiguration2
|
|
9915
|
-
);
|
|
10440
|
+
return isRecord3(parsed) ? { present: true, parsed, config: normalizeConfig(parsed) } : { present: true, parsed: null, config: {} };
|
|
9916
10441
|
} catch {
|
|
9917
|
-
return
|
|
10442
|
+
return { present: true, parsed: null, config: {} };
|
|
9918
10443
|
}
|
|
9919
10444
|
}
|
|
9920
|
-
function
|
|
9921
|
-
return resolveApiCredential(env, fileSystem).apiKey;
|
|
9922
|
-
}
|
|
9923
|
-
function resolveApiConfiguration(env = process.env, fileSystem = nodeFileSystem2) {
|
|
10445
|
+
function resolveApiConfigurationFromSnapshot(env, shared) {
|
|
9924
10446
|
const environmentUrl = env.REMEMBRANCE_API_URL?.trim();
|
|
9925
10447
|
if (environmentUrl) {
|
|
9926
|
-
const
|
|
9927
|
-
if (
|
|
9928
|
-
return { baseUrl:
|
|
10448
|
+
const normalized2 = normalizeApiUrl(environmentUrl, env);
|
|
10449
|
+
if (normalized2.baseUrl !== null) {
|
|
10450
|
+
return { baseUrl: normalized2.baseUrl, source: "environment" };
|
|
9929
10451
|
}
|
|
9930
10452
|
return {
|
|
9931
10453
|
baseUrl: DEFAULT_API_URL,
|
|
9932
10454
|
source: "unusable_environment",
|
|
9933
|
-
issue:
|
|
10455
|
+
issue: normalized2.issue
|
|
9934
10456
|
};
|
|
9935
10457
|
}
|
|
9936
|
-
|
|
9937
|
-
if (!fileSystem.exists(path)) {
|
|
10458
|
+
if (!shared.present) {
|
|
9938
10459
|
return { baseUrl: DEFAULT_API_URL, source: "default" };
|
|
9939
10460
|
}
|
|
9940
|
-
|
|
9941
|
-
const parsed = JSON.parse(
|
|
9942
|
-
fileSystem.read(path, MAX_LOCAL_CONFIG_BYTES)
|
|
9943
|
-
);
|
|
9944
|
-
if (!isRecord3(parsed)) {
|
|
9945
|
-
return {
|
|
9946
|
-
baseUrl: DEFAULT_API_URL,
|
|
9947
|
-
source: "unusable_shared_config",
|
|
9948
|
-
issue: "invalid_url"
|
|
9949
|
-
};
|
|
9950
|
-
}
|
|
9951
|
-
if (!Object.prototype.hasOwnProperty.call(parsed, "apiUrl")) {
|
|
9952
|
-
return { baseUrl: DEFAULT_API_URL, source: "default" };
|
|
9953
|
-
}
|
|
9954
|
-
const normalized = normalizeApiUrl(parsed.apiUrl, env);
|
|
9955
|
-
if (normalized.baseUrl !== null) {
|
|
9956
|
-
return { baseUrl: normalized.baseUrl, source: "shared_config" };
|
|
9957
|
-
}
|
|
9958
|
-
return {
|
|
9959
|
-
baseUrl: DEFAULT_API_URL,
|
|
9960
|
-
source: "unusable_shared_config",
|
|
9961
|
-
issue: normalized.issue
|
|
9962
|
-
};
|
|
9963
|
-
} catch {
|
|
10461
|
+
if (!shared.parsed) {
|
|
9964
10462
|
return {
|
|
9965
10463
|
baseUrl: DEFAULT_API_URL,
|
|
9966
10464
|
source: "unusable_shared_config",
|
|
9967
10465
|
issue: "invalid_url"
|
|
9968
10466
|
};
|
|
9969
10467
|
}
|
|
10468
|
+
if (!Object.prototype.hasOwnProperty.call(shared.parsed, "apiUrl")) {
|
|
10469
|
+
return { baseUrl: DEFAULT_API_URL, source: "default" };
|
|
10470
|
+
}
|
|
10471
|
+
const normalized = normalizeApiUrl(shared.parsed.apiUrl, env);
|
|
10472
|
+
return normalized.baseUrl !== null ? { baseUrl: normalized.baseUrl, source: "shared_config" } : {
|
|
10473
|
+
baseUrl: DEFAULT_API_URL,
|
|
10474
|
+
source: "unusable_shared_config",
|
|
10475
|
+
issue: normalized.issue
|
|
10476
|
+
};
|
|
9970
10477
|
}
|
|
9971
10478
|
function sharedConfigStatus(env = process.env, fileSystem = nodeFileSystem2) {
|
|
9972
10479
|
const path = remembranceConfigPath(env);
|
|
@@ -10348,9 +10855,11 @@ function normalizeConfig(value) {
|
|
|
10348
10855
|
if (!isRecord3(value)) return {};
|
|
10349
10856
|
const apiKey = typeof value.apiKey === "string" ? value.apiKey.trim() : "";
|
|
10350
10857
|
const apiUrl = typeof value.apiUrl === "string" ? value.apiUrl.trim() : "";
|
|
10858
|
+
const memberLinkToken = typeof value.memberLinkToken === "string" ? value.memberLinkToken.trim() : "";
|
|
10351
10859
|
return {
|
|
10352
10860
|
...apiKey ? { apiKey } : {},
|
|
10353
|
-
...apiUrl ? { apiUrl } : {}
|
|
10861
|
+
...apiUrl ? { apiUrl } : {},
|
|
10862
|
+
...memberLinkToken && /^mlink_[A-Za-z0-9_-]{24,160}$/.test(memberLinkToken) ? { memberLinkToken } : {}
|
|
10354
10863
|
};
|
|
10355
10864
|
}
|
|
10356
10865
|
function normalizeApiUrl(value, env) {
|
|
@@ -10492,17 +11001,38 @@ async function checkClientUpdate(input) {
|
|
|
10492
11001
|
}
|
|
10493
11002
|
|
|
10494
11003
|
// src/server.ts
|
|
10495
|
-
var apiConfiguration = resolveApiConfiguration();
|
|
10496
|
-
var apiBase = apiConfiguration.baseUrl;
|
|
10497
11004
|
var MAX_REMOTE_RESPONSE_BYTES = 4 * 1024 * 1024;
|
|
10498
11005
|
var DOCTOR_PROBE_TIMEOUT_MS = 7500;
|
|
10499
|
-
var SERVER_VERSION = true ? "0.1.
|
|
11006
|
+
var SERVER_VERSION = true ? "0.1.62" : "0.0.0-dev";
|
|
10500
11007
|
var tools = toolDefinitions;
|
|
10501
11008
|
var doctorCliRequested = process.argv[2] === "doctor";
|
|
10502
11009
|
var inputBuffer = Buffer.alloc(0);
|
|
10503
11010
|
var clientFraming = "ndjson";
|
|
10504
|
-
var
|
|
11011
|
+
var cachedPrincipalSession = null;
|
|
11012
|
+
var connectedClientInfo = null;
|
|
10505
11013
|
var cachedValueProofKeys = null;
|
|
11014
|
+
var PRINCIPAL_SESSION_REQUIRED_TOOLS = /* @__PURE__ */ new Set([
|
|
11015
|
+
"record_preference",
|
|
11016
|
+
"submit_preference_compatibility_feedback",
|
|
11017
|
+
"link_current_installation"
|
|
11018
|
+
]);
|
|
11019
|
+
var PRINCIPAL_SESSION_AWAITED_TOOLS = /* @__PURE__ */ new Set([
|
|
11020
|
+
...PRINCIPAL_SESSION_REQUIRED_TOOLS,
|
|
11021
|
+
"report_task_outcome"
|
|
11022
|
+
]);
|
|
11023
|
+
function apiAccessKey(access) {
|
|
11024
|
+
return createHash3("sha256").update(
|
|
11025
|
+
[
|
|
11026
|
+
access.configuration.baseUrl,
|
|
11027
|
+
access.credential.source,
|
|
11028
|
+
access.credential.apiKey
|
|
11029
|
+
].join("\0"),
|
|
11030
|
+
"utf8"
|
|
11031
|
+
).digest("hex");
|
|
11032
|
+
}
|
|
11033
|
+
function resetPrincipalSessionCacheForTests() {
|
|
11034
|
+
cachedPrincipalSession = null;
|
|
11035
|
+
}
|
|
10506
11036
|
if (!doctorCliRequested) {
|
|
10507
11037
|
process.stdin.on("data", (chunk) => {
|
|
10508
11038
|
inputBuffer = Buffer.concat([inputBuffer, chunk]);
|
|
@@ -10592,7 +11122,10 @@ async function handleRequest(request) {
|
|
|
10592
11122
|
}
|
|
10593
11123
|
async function dispatchJsonRpcRequest(request) {
|
|
10594
11124
|
if (request.method === "initialize") {
|
|
10595
|
-
|
|
11125
|
+
connectedClientInfo = normalizedMcpClientInfo(request.params?.clientInfo);
|
|
11126
|
+
const access = resolveApiAccess();
|
|
11127
|
+
void ensurePrincipalSessionToken(access).catch(() => null);
|
|
11128
|
+
const clientUpdate = await currentClientUpdate(access);
|
|
10596
11129
|
return {
|
|
10597
11130
|
id: request.id,
|
|
10598
11131
|
result: {
|
|
@@ -10709,47 +11242,54 @@ function messageLooksLikeZodError(message) {
|
|
|
10709
11242
|
}
|
|
10710
11243
|
async function callTool(definition, rawArguments) {
|
|
10711
11244
|
const payload = definition.schema.parse(rawArguments ?? {});
|
|
11245
|
+
const access = resolveApiAccess();
|
|
10712
11246
|
if (definition.local === "bootstrap_agent_identity") {
|
|
10713
|
-
return bootstrapAgentIdentity(payload);
|
|
11247
|
+
return bootstrapAgentIdentity(payload, access);
|
|
10714
11248
|
}
|
|
10715
11249
|
if (definition.local === "queue_private_skill_import") {
|
|
10716
|
-
return queuePrivateSkillHandoff(payload, {
|
|
11250
|
+
return queuePrivateSkillHandoff(payload, {
|
|
11251
|
+
uploadBaseUrl: access.configuration.baseUrl
|
|
11252
|
+
});
|
|
10717
11253
|
}
|
|
10718
11254
|
if (definition.name === "run_connection_doctor") {
|
|
10719
|
-
return runLocalConnectionDoctor(
|
|
11255
|
+
return runLocalConnectionDoctor(
|
|
11256
|
+
payload,
|
|
11257
|
+
"mcp_tool",
|
|
11258
|
+
access
|
|
11259
|
+
);
|
|
10720
11260
|
}
|
|
10721
11261
|
if (definition.name === "submit_feedback") {
|
|
10722
|
-
return submitFeedback(payload);
|
|
11262
|
+
return submitFeedback(payload, access);
|
|
10723
11263
|
}
|
|
10724
11264
|
if (definition.name === "submit_remembrance") {
|
|
10725
|
-
return submitRemembrance(payload);
|
|
11265
|
+
return submitRemembrance(payload, access);
|
|
10726
11266
|
}
|
|
10727
11267
|
const localConfigurationError = remoteConfigurationError(
|
|
10728
|
-
|
|
10729
|
-
|
|
10730
|
-
|
|
11268
|
+
access.configuration.source,
|
|
11269
|
+
access.credential.source,
|
|
11270
|
+
access.configuration.issue
|
|
10731
11271
|
);
|
|
10732
|
-
const result = definition.name === "get_connection_status" && localConfigurationError ? { ok: false, status: 0, error: localConfigurationError } : await callRemembrance(definition, payload);
|
|
11272
|
+
const result = definition.name === "get_connection_status" && localConfigurationError ? { ok: false, status: 0, error: localConfigurationError } : await callRemembrance(definition, payload, { access });
|
|
10733
11273
|
if (definition.name === "get_connection_status") {
|
|
10734
11274
|
const status = localConnectionStatus(result, {
|
|
10735
|
-
apiBase,
|
|
10736
|
-
apiUrlSource:
|
|
11275
|
+
apiBase: access.configuration.baseUrl,
|
|
11276
|
+
apiUrlSource: access.configuration.source,
|
|
10737
11277
|
pluginVersion: SERVER_VERSION
|
|
10738
11278
|
});
|
|
10739
11279
|
if (!localConfigurationError) {
|
|
10740
|
-
void reportDegradedClientHealth(status).catch(() => void 0);
|
|
11280
|
+
void reportDegradedClientHealth(status, access).catch(() => void 0);
|
|
10741
11281
|
}
|
|
10742
11282
|
return status;
|
|
10743
11283
|
}
|
|
10744
11284
|
return result;
|
|
10745
11285
|
}
|
|
10746
11286
|
function remoteConfigurationError(apiUrlSource, credentialSource, apiUrlIssue) {
|
|
10747
|
-
if (credentialSource === "unusable_destination_binding") {
|
|
10748
|
-
return "The Remembrance API key is not bound to the configured registry destination. For a custom registry, store apiKey and apiUrl together in the shared config, or set REMEMBRANCE_API_KEY_ORIGIN to the exact REMEMBRANCE_API_URL value. Remote tools remain paused so the key cannot be forwarded elsewhere.";
|
|
10749
|
-
}
|
|
10750
11287
|
if (apiUrlSource === "unusable_environment") {
|
|
10751
11288
|
return apiConfigurationErrorMessage(apiUrlIssue, "REMEMBRANCE_API_URL");
|
|
10752
11289
|
}
|
|
11290
|
+
if (credentialSource === "unusable_destination_binding") {
|
|
11291
|
+
return "The Remembrance API key is not bound to the configured registry destination. For a custom registry, store apiKey and apiUrl together in the shared config, or set REMEMBRANCE_API_KEY_ORIGIN to the exact REMEMBRANCE_API_URL value. Remote tools remain paused so the key cannot be forwarded elsewhere.";
|
|
11292
|
+
}
|
|
10753
11293
|
if (apiUrlSource === "unusable_shared_config" || credentialSource === "unusable_shared_config") {
|
|
10754
11294
|
return "The shared Remembrance config exists but is unreadable or invalid. Fix or intentionally remove ~/.config/remembrance/config.json before using remote Remembrance tools.";
|
|
10755
11295
|
}
|
|
@@ -10829,7 +11369,7 @@ function isSafePluginVersion(value) {
|
|
|
10829
11369
|
function isSafeHostVersion(value) {
|
|
10830
11370
|
return value === null || typeof value === "string" && value.length <= 64 && /^[0-9A-Za-z._+ -]*$/.test(value.trim());
|
|
10831
11371
|
}
|
|
10832
|
-
async function reportDegradedClientHealth(status) {
|
|
11372
|
+
async function reportDegradedClientHealth(status, access = resolveApiAccess()) {
|
|
10833
11373
|
if (/^(0|false|no)$/i.test(process.env.REMEMBRANCE_HEALTH_REPORTING ?? "")) {
|
|
10834
11374
|
return;
|
|
10835
11375
|
}
|
|
@@ -10838,25 +11378,30 @@ async function reportDegradedClientHealth(status) {
|
|
|
10838
11378
|
const controller = new AbortController();
|
|
10839
11379
|
const timeout = setTimeout(() => controller.abort(), 1500);
|
|
10840
11380
|
try {
|
|
10841
|
-
const apiKey =
|
|
10842
|
-
await fetch(
|
|
10843
|
-
|
|
10844
|
-
|
|
10845
|
-
|
|
10846
|
-
|
|
10847
|
-
|
|
10848
|
-
|
|
10849
|
-
|
|
10850
|
-
|
|
10851
|
-
|
|
11381
|
+
const apiKey = access.credential.apiKey;
|
|
11382
|
+
await fetch(
|
|
11383
|
+
`${access.configuration.baseUrl}/api/v1/agent/client-health-reports`,
|
|
11384
|
+
{
|
|
11385
|
+
method: "POST",
|
|
11386
|
+
headers: {
|
|
11387
|
+
"Content-Type": "application/json",
|
|
11388
|
+
...apiKey ? { "X-Remembrance-API-Key": apiKey } : {},
|
|
11389
|
+
"User-Agent": `@remembrance-ai/mcp-server/${SERVER_VERSION}`
|
|
11390
|
+
},
|
|
11391
|
+
body: JSON.stringify(report),
|
|
11392
|
+
signal: controller.signal
|
|
11393
|
+
}
|
|
11394
|
+
);
|
|
10852
11395
|
} finally {
|
|
10853
11396
|
clearTimeout(timeout);
|
|
10854
11397
|
}
|
|
10855
11398
|
}
|
|
10856
|
-
async function submitRemembrance(payload) {
|
|
11399
|
+
async function submitRemembrance(payload, access) {
|
|
10857
11400
|
const { verified_attestation: verifiedAttestation, ...request } = payload;
|
|
10858
11401
|
if (!verifiedAttestation) {
|
|
10859
|
-
return callRemembrance(mustFindTool("submit_remembrance"), request
|
|
11402
|
+
return callRemembrance(mustFindTool("submit_remembrance"), request, {
|
|
11403
|
+
access
|
|
11404
|
+
});
|
|
10860
11405
|
}
|
|
10861
11406
|
const skillSlug = request.skill?.slug;
|
|
10862
11407
|
if (!skillSlug) {
|
|
@@ -10872,20 +11417,27 @@ async function submitRemembrance(payload) {
|
|
|
10872
11417
|
};
|
|
10873
11418
|
const attestation = await signedRemembranceAttestation(
|
|
10874
11419
|
remembrancePayload,
|
|
10875
|
-
skillSlug
|
|
11420
|
+
skillSlug,
|
|
11421
|
+
access
|
|
11422
|
+
);
|
|
11423
|
+
return callRemembrance(
|
|
11424
|
+
mustFindTool("submit_remembrance"),
|
|
11425
|
+
{
|
|
11426
|
+
...remembrancePayload,
|
|
11427
|
+
evidence: {
|
|
11428
|
+
...evidence,
|
|
11429
|
+
attestation
|
|
11430
|
+
}
|
|
11431
|
+
},
|
|
11432
|
+
{ access }
|
|
10876
11433
|
);
|
|
10877
|
-
return callRemembrance(mustFindTool("submit_remembrance"), {
|
|
10878
|
-
...remembrancePayload,
|
|
10879
|
-
evidence: {
|
|
10880
|
-
...evidence,
|
|
10881
|
-
attestation
|
|
10882
|
-
}
|
|
10883
|
-
});
|
|
10884
11434
|
}
|
|
10885
|
-
async function submitFeedback(payload) {
|
|
11435
|
+
async function submitFeedback(payload, access) {
|
|
10886
11436
|
const { verified_attestation: verifiedAttestation, ...request } = payload;
|
|
10887
11437
|
if (!verifiedAttestation) {
|
|
10888
|
-
return callRemembrance(mustFindTool("submit_feedback"), request
|
|
11438
|
+
return callRemembrance(mustFindTool("submit_feedback"), request, {
|
|
11439
|
+
access
|
|
11440
|
+
});
|
|
10889
11441
|
}
|
|
10890
11442
|
const identity = await ensureSigningIdentity();
|
|
10891
11443
|
const agent = request.agent ?? {
|
|
@@ -10920,18 +11472,23 @@ async function submitFeedback(payload) {
|
|
|
10920
11472
|
};
|
|
10921
11473
|
const attestation = await signedRemembranceAttestation(
|
|
10922
11474
|
remembrancePayload,
|
|
10923
|
-
request.skill_slug
|
|
11475
|
+
request.skill_slug,
|
|
11476
|
+
access
|
|
11477
|
+
);
|
|
11478
|
+
return callRemembrance(
|
|
11479
|
+
mustFindTool("submit_feedback"),
|
|
11480
|
+
{
|
|
11481
|
+
...request,
|
|
11482
|
+
agent,
|
|
11483
|
+
evidence: {
|
|
11484
|
+
...evidence,
|
|
11485
|
+
attestation
|
|
11486
|
+
}
|
|
11487
|
+
},
|
|
11488
|
+
{ access }
|
|
10924
11489
|
);
|
|
10925
|
-
return callRemembrance(mustFindTool("submit_feedback"), {
|
|
10926
|
-
...request,
|
|
10927
|
-
agent,
|
|
10928
|
-
evidence: {
|
|
10929
|
-
...evidence,
|
|
10930
|
-
attestation
|
|
10931
|
-
}
|
|
10932
|
-
});
|
|
10933
11490
|
}
|
|
10934
|
-
async function signedRemembranceAttestation(remembrancePayload, skillSlug) {
|
|
11491
|
+
async function signedRemembranceAttestation(remembrancePayload, skillSlug, access) {
|
|
10935
11492
|
const identity = await ensureSigningIdentity();
|
|
10936
11493
|
const evidenceHash = attestationEvidenceHashForRemembrance(remembrancePayload);
|
|
10937
11494
|
const challenge = await callRemembrance(
|
|
@@ -10943,7 +11500,8 @@ async function signedRemembranceAttestation(remembrancePayload, skillSlug) {
|
|
|
10943
11500
|
subject: identity.subject,
|
|
10944
11501
|
skill_slug: skillSlug,
|
|
10945
11502
|
evidence_hash: evidenceHash
|
|
10946
|
-
}
|
|
11503
|
+
},
|
|
11504
|
+
{ access }
|
|
10947
11505
|
);
|
|
10948
11506
|
if (challenge.ok === false) {
|
|
10949
11507
|
throw new Error("Unable to create Remembrance attestation challenge.");
|
|
@@ -10970,7 +11528,7 @@ async function signedRemembranceAttestation(remembrancePayload, skillSlug) {
|
|
|
10970
11528
|
signature
|
|
10971
11529
|
};
|
|
10972
11530
|
}
|
|
10973
|
-
async function bootstrapAgentIdentity(args) {
|
|
11531
|
+
async function bootstrapAgentIdentity(args, access = resolveApiAccess()) {
|
|
10974
11532
|
const keyPath = identityPath(args.key_path);
|
|
10975
11533
|
let reusedExistingIdentity = !args.force_rotate && existsSync3(keyPath);
|
|
10976
11534
|
let identity = reusedExistingIdentity ? await readIdentity(keyPath) : null;
|
|
@@ -10993,10 +11551,14 @@ async function bootstrapAgentIdentity(args) {
|
|
|
10993
11551
|
mismatchedFields.push("key_id");
|
|
10994
11552
|
}
|
|
10995
11553
|
}
|
|
11554
|
+
const ownerBinding = await agentRegistrationOwnerBinding(access).catch(
|
|
11555
|
+
() => null
|
|
11556
|
+
);
|
|
10996
11557
|
const signedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
10997
11558
|
const proofPayload = buildAgentKeyRegistrationSigningPayload({
|
|
10998
11559
|
provider: identity.provider,
|
|
10999
11560
|
keyId: identity.key_id,
|
|
11561
|
+
ownerBinding,
|
|
11000
11562
|
publicKey: identity.public_key,
|
|
11001
11563
|
subject: identity.subject,
|
|
11002
11564
|
signedAt
|
|
@@ -11015,13 +11577,15 @@ async function bootstrapAgentIdentity(args) {
|
|
|
11015
11577
|
subject: identity.subject,
|
|
11016
11578
|
proof: {
|
|
11017
11579
|
algorithm: "ed25519",
|
|
11580
|
+
...ownerBinding ? { owner_binding: ownerBinding } : {},
|
|
11018
11581
|
signed_at: signedAt,
|
|
11019
11582
|
signature: proofSignature
|
|
11020
11583
|
},
|
|
11021
11584
|
metadata: {
|
|
11022
11585
|
registered_by: "@remembrance-ai/mcp-server"
|
|
11023
11586
|
}
|
|
11024
|
-
}
|
|
11587
|
+
},
|
|
11588
|
+
{ access }
|
|
11025
11589
|
);
|
|
11026
11590
|
if (registration.ok !== true) {
|
|
11027
11591
|
throw new McpPublicError(
|
|
@@ -11039,6 +11603,33 @@ async function bootstrapAgentIdentity(args) {
|
|
|
11039
11603
|
registration
|
|
11040
11604
|
};
|
|
11041
11605
|
}
|
|
11606
|
+
async function agentRegistrationOwnerBinding(access) {
|
|
11607
|
+
const credential = access.credential;
|
|
11608
|
+
const configurationError = remoteConfigurationError(
|
|
11609
|
+
access.configuration.source,
|
|
11610
|
+
credential.source,
|
|
11611
|
+
access.configuration.issue
|
|
11612
|
+
);
|
|
11613
|
+
if (configurationError) throw new McpPublicError(configurationError);
|
|
11614
|
+
const headers = {
|
|
11615
|
+
accept: "application/json",
|
|
11616
|
+
"user-agent": `@remembrance-ai/mcp-server/${SERVER_VERSION}`
|
|
11617
|
+
};
|
|
11618
|
+
if (credential.apiKey) {
|
|
11619
|
+
headers["x-remembrance-api-key"] = credential.apiKey;
|
|
11620
|
+
}
|
|
11621
|
+
const response = await fetch(
|
|
11622
|
+
`${access.configuration.baseUrl}/api/v1/agent/keys/register`,
|
|
11623
|
+
{
|
|
11624
|
+
method: "GET",
|
|
11625
|
+
headers
|
|
11626
|
+
}
|
|
11627
|
+
);
|
|
11628
|
+
if (!response.ok) return null;
|
|
11629
|
+
const payload = await response.json();
|
|
11630
|
+
const candidate = String(payload.owner_binding ?? "").trim();
|
|
11631
|
+
return /^areg_[A-Za-z0-9_-]{24,120}$/.test(candidate) ? candidate : null;
|
|
11632
|
+
}
|
|
11042
11633
|
async function createAndPersistIdentity(args, keyPath) {
|
|
11043
11634
|
const { privateKey, publicKey } = generateKeyPairSync("ed25519");
|
|
11044
11635
|
const publicKeyPem = String(
|
|
@@ -11096,44 +11687,56 @@ async function readIdentity(path = identityPath()) {
|
|
|
11096
11687
|
}
|
|
11097
11688
|
var automaticIdentityBootstrap = null;
|
|
11098
11689
|
async function ensureSigningIdentity() {
|
|
11099
|
-
|
|
11100
|
-
|
|
11101
|
-
|
|
11690
|
+
const keyPath = identityPath();
|
|
11691
|
+
if (automaticIdentityBootstrap?.keyPath === keyPath) {
|
|
11692
|
+
return automaticIdentityBootstrap.promise;
|
|
11693
|
+
}
|
|
11694
|
+
const existing = await readIdentity(keyPath);
|
|
11695
|
+
if (automaticIdentityBootstrap?.keyPath === keyPath) {
|
|
11696
|
+
return automaticIdentityBootstrap.promise;
|
|
11697
|
+
}
|
|
11102
11698
|
if (existing) return existing;
|
|
11103
|
-
|
|
11104
|
-
|
|
11105
|
-
|
|
11106
|
-
|
|
11107
|
-
|
|
11108
|
-
|
|
11109
|
-
|
|
11110
|
-
|
|
11111
|
-
|
|
11112
|
-
|
|
11113
|
-
|
|
11699
|
+
const promise = (async () => {
|
|
11700
|
+
await bootstrapAgentIdentity({ key_path: keyPath });
|
|
11701
|
+
const created = await readIdentity(keyPath);
|
|
11702
|
+
if (!created) {
|
|
11703
|
+
throw new McpPublicError(
|
|
11704
|
+
"Remembrance could not initialize the local signing identity. Run bootstrap_agent_identity and retry."
|
|
11705
|
+
);
|
|
11706
|
+
}
|
|
11707
|
+
return created;
|
|
11708
|
+
})();
|
|
11709
|
+
automaticIdentityBootstrap = { keyPath, promise };
|
|
11710
|
+
const clearBootstrap = () => {
|
|
11711
|
+
if (automaticIdentityBootstrap?.promise === promise) {
|
|
11114
11712
|
automaticIdentityBootstrap = null;
|
|
11115
|
-
}
|
|
11116
|
-
}
|
|
11117
|
-
|
|
11713
|
+
}
|
|
11714
|
+
};
|
|
11715
|
+
void promise.then(clearBootstrap, clearBootstrap);
|
|
11716
|
+
return promise;
|
|
11118
11717
|
}
|
|
11119
11718
|
async function callRemembrance(definition, rawArguments, options = {}) {
|
|
11719
|
+
const access = options.access ?? resolveApiAccess();
|
|
11120
11720
|
const parsed = definition.schema.parse(rawArguments ?? {});
|
|
11721
|
+
const projectKey = definition.name === "query_skills" || definition.name === "invoke_skill" ? await localProjectKey().catch(() => null) : null;
|
|
11722
|
+
const callerClientContext = parsed.client_context ?? {};
|
|
11121
11723
|
const payload = definition.name === "query_skills" || definition.name === "invoke_skill" ? {
|
|
11122
11724
|
...parsed,
|
|
11123
11725
|
client_context: {
|
|
11124
|
-
...
|
|
11125
|
-
surface: "mcp"
|
|
11726
|
+
...callerClientContext,
|
|
11727
|
+
surface: "mcp",
|
|
11728
|
+
...callerClientContext.project_key || !projectKey ? {} : { project_key: projectKey }
|
|
11126
11729
|
}
|
|
11127
11730
|
} : parsed;
|
|
11128
11731
|
const headers = {
|
|
11129
11732
|
"content-type": "application/json",
|
|
11130
11733
|
"user-agent": `@remembrance-ai/mcp-server/${SERVER_VERSION}`
|
|
11131
11734
|
};
|
|
11132
|
-
const credential =
|
|
11735
|
+
const credential = access.credential;
|
|
11133
11736
|
const configurationError = remoteConfigurationError(
|
|
11134
|
-
|
|
11737
|
+
access.configuration.source,
|
|
11135
11738
|
credential.source,
|
|
11136
|
-
|
|
11739
|
+
access.configuration.issue
|
|
11137
11740
|
);
|
|
11138
11741
|
if (configurationError && definition.name !== "get_connection_status") {
|
|
11139
11742
|
throw new McpPublicError(configurationError);
|
|
@@ -11143,11 +11746,21 @@ async function callRemembrance(definition, rawArguments, options = {}) {
|
|
|
11143
11746
|
headers["x-remembrance-api-key"] = apiKey;
|
|
11144
11747
|
}
|
|
11145
11748
|
if (!options.skipEconomicsSession && definition.name !== "register_agent_key" && definition.name !== "request_attestation_challenge" && definition.name !== "get_connection_status") {
|
|
11146
|
-
|
|
11147
|
-
|
|
11148
|
-
|
|
11149
|
-
|
|
11150
|
-
|
|
11749
|
+
let principalSession = currentPrincipalSessionToken(access);
|
|
11750
|
+
if (!principalSession && PRINCIPAL_SESSION_AWAITED_TOOLS.has(definition.name)) {
|
|
11751
|
+
principalSession = await ensurePrincipalSessionToken(access).catch(
|
|
11752
|
+
() => null
|
|
11753
|
+
);
|
|
11754
|
+
}
|
|
11755
|
+
if (!principalSession && PRINCIPAL_SESSION_REQUIRED_TOOLS.has(definition.name)) {
|
|
11756
|
+
throw new McpPublicError(
|
|
11757
|
+
"This operation requires a verified local installation session. Check the organization key and local TOFU identity, then retry."
|
|
11758
|
+
);
|
|
11759
|
+
}
|
|
11760
|
+
if (principalSession) {
|
|
11761
|
+
headers["x-remembrance-principal-session"] = principalSession;
|
|
11762
|
+
} else if (definition.name === "query_skills" || definition.name === "invoke_skill") {
|
|
11763
|
+
void ensurePrincipalSessionToken(access).catch(() => null);
|
|
11151
11764
|
}
|
|
11152
11765
|
}
|
|
11153
11766
|
const endpoint = endpointFor(definition, payload);
|
|
@@ -11159,25 +11772,51 @@ async function callRemembrance(definition, rawArguments, options = {}) {
|
|
|
11159
11772
|
let response;
|
|
11160
11773
|
let body;
|
|
11161
11774
|
try {
|
|
11162
|
-
|
|
11163
|
-
|
|
11164
|
-
|
|
11165
|
-
|
|
11166
|
-
|
|
11167
|
-
|
|
11168
|
-
|
|
11169
|
-
|
|
11170
|
-
|
|
11171
|
-
|
|
11172
|
-
|
|
11173
|
-
|
|
11174
|
-
|
|
11175
|
-
|
|
11775
|
+
const execute = async () => {
|
|
11776
|
+
const nextResponse = await fetch(
|
|
11777
|
+
`${access.configuration.baseUrl}${endpoint}`,
|
|
11778
|
+
{
|
|
11779
|
+
method: definition.method ?? "POST",
|
|
11780
|
+
headers,
|
|
11781
|
+
body: definition.method === "GET" ? void 0 : JSON.stringify(payload),
|
|
11782
|
+
signal: controller.signal
|
|
11783
|
+
}
|
|
11784
|
+
);
|
|
11785
|
+
let nextBody;
|
|
11786
|
+
try {
|
|
11787
|
+
nextBody = await readBoundedResponseJson(nextResponse);
|
|
11788
|
+
} catch {
|
|
11789
|
+
if (controller.signal.aborted) {
|
|
11790
|
+
throw new DOMException("Request timed out", "AbortError");
|
|
11791
|
+
}
|
|
11792
|
+
nextBody = {
|
|
11793
|
+
error: nextResponse.ok ? "Remembrance API returned an unreadable or oversized response." : "Remembrance API request failed."
|
|
11176
11794
|
};
|
|
11177
11795
|
}
|
|
11178
|
-
body
|
|
11179
|
-
|
|
11180
|
-
|
|
11796
|
+
return { response: nextResponse, body: nextBody };
|
|
11797
|
+
};
|
|
11798
|
+
({ response, body } = await execute());
|
|
11799
|
+
const sentPrincipalSession = headers["x-remembrance-principal-session"] ?? null;
|
|
11800
|
+
const refreshSignaled = response.headers.get("x-remembrance-principal-session-status") === "refresh_required";
|
|
11801
|
+
const authenticationFailed = principalSessionAuthenticationFailed(
|
|
11802
|
+
response,
|
|
11803
|
+
body
|
|
11804
|
+
);
|
|
11805
|
+
if (sentPrincipalSession && refreshSignaled && !authenticationFailed) {
|
|
11806
|
+
cachedPrincipalSession = null;
|
|
11807
|
+
void ensurePrincipalSessionToken(access).catch(() => null);
|
|
11808
|
+
} else if (sentPrincipalSession && authenticationFailed) {
|
|
11809
|
+
cachedPrincipalSession = null;
|
|
11810
|
+
const refreshed = await ensurePrincipalSessionToken(access).catch(
|
|
11811
|
+
() => null
|
|
11812
|
+
);
|
|
11813
|
+
if (refreshed && refreshed !== sentPrincipalSession) {
|
|
11814
|
+
headers["x-remembrance-principal-session"] = refreshed;
|
|
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());
|
|
11819
|
+
}
|
|
11181
11820
|
}
|
|
11182
11821
|
} catch (error) {
|
|
11183
11822
|
return {
|
|
@@ -11188,7 +11827,10 @@ async function callRemembrance(definition, rawArguments, options = {}) {
|
|
|
11188
11827
|
} finally {
|
|
11189
11828
|
clearTimeout(timeout);
|
|
11190
11829
|
}
|
|
11191
|
-
const verifiedBody = definition.name === "get_value_proof" && response.ok ? await verifySignedValueProofWithKeyRefresh(
|
|
11830
|
+
const verifiedBody = definition.name === "get_value_proof" && response.ok ? await verifySignedValueProofWithKeyRefresh(
|
|
11831
|
+
body,
|
|
11832
|
+
(forceRefresh) => fetchValueProofKeySet(forceRefresh, access)
|
|
11833
|
+
) : body;
|
|
11192
11834
|
return {
|
|
11193
11835
|
ok: response.ok,
|
|
11194
11836
|
status: response.status,
|
|
@@ -11198,27 +11840,52 @@ async function callRemembrance(definition, rawArguments, options = {}) {
|
|
|
11198
11840
|
body: verifiedBody
|
|
11199
11841
|
};
|
|
11200
11842
|
}
|
|
11201
|
-
|
|
11843
|
+
var cachedLocalProjectKey;
|
|
11844
|
+
async function localProjectKey() {
|
|
11845
|
+
const configured = process.env.REMEMBRANCE_PROJECT_KEY?.trim() ?? "";
|
|
11846
|
+
if (/^prj_[A-Za-z0-9_-]{12,120}$/.test(configured)) return configured;
|
|
11847
|
+
const projectPath = (process.env.REMEMBRANCE_PROJECT_PATH ?? process.env.PWD ?? process.cwd()).trim();
|
|
11848
|
+
if (!projectPath) return null;
|
|
11849
|
+
const identity = await ensureSigningIdentity();
|
|
11850
|
+
if (cachedLocalProjectKey?.identityKeyId === identity.key_id && cachedLocalProjectKey.projectPath === projectPath) {
|
|
11851
|
+
return cachedLocalProjectKey.value;
|
|
11852
|
+
}
|
|
11853
|
+
const value = `prj_${createHmac("sha256", identity.private_key).update(`remembrance-project-v1:${projectPath}`, "utf8").digest("base64url").slice(0, 32)}`;
|
|
11854
|
+
cachedLocalProjectKey = {
|
|
11855
|
+
identityKeyId: identity.key_id,
|
|
11856
|
+
projectPath,
|
|
11857
|
+
value
|
|
11858
|
+
};
|
|
11859
|
+
return value;
|
|
11860
|
+
}
|
|
11861
|
+
function principalSessionAuthenticationFailed(response, body) {
|
|
11862
|
+
if (response.status !== 401 && response.status !== 403) return false;
|
|
11863
|
+
if (!isRecord4(body) || typeof body.error !== "string") return false;
|
|
11864
|
+
return /principal session|economics session|installation session|agent principal|installation principal/i.test(
|
|
11865
|
+
body.error
|
|
11866
|
+
);
|
|
11867
|
+
}
|
|
11868
|
+
async function runLocalConnectionDoctor(options, execution = "mcp_tool", access = resolveApiAccess()) {
|
|
11202
11869
|
const connectionDefinition = mustFindTool("get_connection_status");
|
|
11203
11870
|
const localConfigurationError = remoteConfigurationError(
|
|
11204
|
-
|
|
11205
|
-
|
|
11206
|
-
|
|
11871
|
+
access.configuration.source,
|
|
11872
|
+
access.credential.source,
|
|
11873
|
+
access.configuration.issue
|
|
11207
11874
|
);
|
|
11208
11875
|
const connectionPromise = localConfigurationError ? Promise.resolve({
|
|
11209
11876
|
ok: false,
|
|
11210
11877
|
status: 0,
|
|
11211
11878
|
error: localConfigurationError
|
|
11212
|
-
}) : callDoctorReadProbe(connectionDefinition, {});
|
|
11213
|
-
const catalogPromise = options.active_read_probe && !localConfigurationError ? callDoctorReadProbe(mustFindTool("list_skills"), { limit: 1 }) : Promise.resolve(null);
|
|
11879
|
+
}) : callDoctorReadProbe(connectionDefinition, {}, access);
|
|
11880
|
+
const catalogPromise = options.active_read_probe && !localConfigurationError ? callDoctorReadProbe(mustFindTool("list_skills"), { limit: 1 }, access) : Promise.resolve(null);
|
|
11214
11881
|
const [connectionResult, catalogResult, clientUpdate] = await Promise.all([
|
|
11215
11882
|
connectionPromise,
|
|
11216
11883
|
catalogPromise,
|
|
11217
|
-
currentClientUpdate()
|
|
11884
|
+
currentClientUpdate(access)
|
|
11218
11885
|
]);
|
|
11219
11886
|
const connectionStatus = localConnectionStatus(connectionResult, {
|
|
11220
|
-
apiBase,
|
|
11221
|
-
apiUrlSource:
|
|
11887
|
+
apiBase: access.configuration.baseUrl,
|
|
11888
|
+
apiUrlSource: access.configuration.source,
|
|
11222
11889
|
pluginVersion: SERVER_VERSION
|
|
11223
11890
|
});
|
|
11224
11891
|
const activeReadProbe = {
|
|
@@ -11244,23 +11911,25 @@ async function runLocalConnectionDoctor(options, execution = "mcp_tool") {
|
|
|
11244
11911
|
client_update: clientUpdate
|
|
11245
11912
|
});
|
|
11246
11913
|
}
|
|
11247
|
-
async function currentClientUpdate() {
|
|
11914
|
+
async function currentClientUpdate(access = resolveApiAccess()) {
|
|
11248
11915
|
const configuredSurface = process.env.REMEMBRANCE_PLUGIN_HOST?.trim().toLowerCase();
|
|
11249
11916
|
const surface = configuredSurface && isPluginHostSurface(configuredSurface) ? configuredSurface : "mcp";
|
|
11250
11917
|
return checkClientUpdate({
|
|
11251
|
-
apiBase,
|
|
11918
|
+
apiBase: access.configuration.baseUrl,
|
|
11252
11919
|
currentVersion: SERVER_VERSION,
|
|
11253
11920
|
surface
|
|
11254
11921
|
});
|
|
11255
11922
|
}
|
|
11256
|
-
async function callDoctorReadProbe(definition, payload) {
|
|
11923
|
+
async function callDoctorReadProbe(definition, payload, access) {
|
|
11257
11924
|
const first = await callRemembrance(definition, payload, {
|
|
11258
11925
|
skipEconomicsSession: true,
|
|
11259
|
-
timeoutMs: DOCTOR_PROBE_TIMEOUT_MS
|
|
11926
|
+
timeoutMs: DOCTOR_PROBE_TIMEOUT_MS,
|
|
11927
|
+
access
|
|
11260
11928
|
});
|
|
11261
11929
|
return isTransientDoctorProbeFailure(first) ? callRemembrance(definition, payload, {
|
|
11262
11930
|
skipEconomicsSession: true,
|
|
11263
|
-
timeoutMs: DOCTOR_PROBE_TIMEOUT_MS
|
|
11931
|
+
timeoutMs: DOCTOR_PROBE_TIMEOUT_MS,
|
|
11932
|
+
access
|
|
11264
11933
|
}) : first;
|
|
11265
11934
|
}
|
|
11266
11935
|
function isTransientDoctorProbeFailure(result) {
|
|
@@ -11324,16 +11993,16 @@ async function runConnectionDoctorCli(args = process.argv.slice(3)) {
|
|
|
11324
11993
|
);
|
|
11325
11994
|
return report.status === "blocked" ? 1 : 0;
|
|
11326
11995
|
}
|
|
11327
|
-
async function fetchValueProofKeySet(forceRefresh) {
|
|
11996
|
+
async function fetchValueProofKeySet(forceRefresh, access = resolveApiAccess()) {
|
|
11328
11997
|
const now = Date.now();
|
|
11329
|
-
if (!forceRefresh && cachedValueProofKeys && cachedValueProofKeys.expiresAt > now) {
|
|
11998
|
+
if (!forceRefresh && cachedValueProofKeys && cachedValueProofKeys.apiBase === access.configuration.baseUrl && cachedValueProofKeys.expiresAt > now) {
|
|
11330
11999
|
return cachedValueProofKeys.value;
|
|
11331
12000
|
}
|
|
11332
12001
|
const controller = new AbortController();
|
|
11333
12002
|
const timeout = setTimeout(() => controller.abort(), apiTimeoutMs());
|
|
11334
12003
|
try {
|
|
11335
12004
|
const response = await fetch(
|
|
11336
|
-
`${
|
|
12005
|
+
`${access.configuration.baseUrl}/.well-known/remembrance-value-proof-keys.json`,
|
|
11337
12006
|
{
|
|
11338
12007
|
headers: { accept: "application/json" },
|
|
11339
12008
|
signal: controller.signal
|
|
@@ -11347,7 +12016,8 @@ async function fetchValueProofKeySet(forceRefresh) {
|
|
|
11347
12016
|
const value = await readBoundedResponseJson(response);
|
|
11348
12017
|
cachedValueProofKeys = {
|
|
11349
12018
|
value,
|
|
11350
|
-
expiresAt: now + valueProofKeyCacheTtlMs(response.headers)
|
|
12019
|
+
expiresAt: now + valueProofKeyCacheTtlMs(response.headers),
|
|
12020
|
+
apiBase: access.configuration.baseUrl
|
|
11351
12021
|
};
|
|
11352
12022
|
return value;
|
|
11353
12023
|
} finally {
|
|
@@ -11465,18 +12135,38 @@ async function fetchSkillCatalog(input) {
|
|
|
11465
12135
|
}
|
|
11466
12136
|
return skillCatalogResponseSchema.parse(response.body);
|
|
11467
12137
|
}
|
|
11468
|
-
async function
|
|
11469
|
-
|
|
11470
|
-
|
|
12138
|
+
async function ensurePrincipalSessionToken(access = resolveApiAccess()) {
|
|
12139
|
+
if (isUnusableCredentialSource(access.configuration.source) || isUnusableCredentialSource(access.credential.source)) {
|
|
12140
|
+
return null;
|
|
12141
|
+
}
|
|
12142
|
+
const identity = await ensureSigningIdentity();
|
|
11471
12143
|
const identityKey = `${identity.provider}:${identity.key_id}`;
|
|
11472
|
-
|
|
11473
|
-
|
|
12144
|
+
const accessKey = apiAccessKey(access);
|
|
12145
|
+
if (cachedPrincipalSession && cachedPrincipalSession.identityKey === identityKey && cachedPrincipalSession.accessKey === accessKey && cachedPrincipalSession.expiresAt > Date.now() + 6e4) {
|
|
12146
|
+
return cachedPrincipalSession.token;
|
|
11474
12147
|
}
|
|
11475
|
-
const
|
|
12148
|
+
const memberLinkToken = access.memberLinkToken;
|
|
12149
|
+
const challengePayload = {
|
|
11476
12150
|
action: "challenge",
|
|
11477
12151
|
provider: identity.provider,
|
|
11478
|
-
key_id: identity.key_id
|
|
11479
|
-
|
|
12152
|
+
key_id: identity.key_id,
|
|
12153
|
+
runtime_profile: localRuntimeProfile(identity),
|
|
12154
|
+
...memberLinkToken ? { member_link_token: memberLinkToken } : {}
|
|
12155
|
+
};
|
|
12156
|
+
let challengeResponse = await directPrincipalSessionRequest(
|
|
12157
|
+
challengePayload,
|
|
12158
|
+
access
|
|
12159
|
+
);
|
|
12160
|
+
if (!challengeResponse) {
|
|
12161
|
+
await bootstrapAgentIdentity({}, access).catch(() => null);
|
|
12162
|
+
challengeResponse = await directPrincipalSessionRequest(
|
|
12163
|
+
{
|
|
12164
|
+
...challengePayload,
|
|
12165
|
+
member_link_token: void 0
|
|
12166
|
+
},
|
|
12167
|
+
access
|
|
12168
|
+
);
|
|
12169
|
+
}
|
|
11480
12170
|
const challenge = challengeResponse;
|
|
11481
12171
|
if (!challenge.challenge_id || !challenge.signing_payload) return null;
|
|
11482
12172
|
const signature = signPayload(
|
|
@@ -11484,38 +12174,100 @@ async function ensureEconomicsSessionToken() {
|
|
|
11484
12174
|
Buffer.from(challenge.signing_payload),
|
|
11485
12175
|
createPrivateKey2(identity.private_key)
|
|
11486
12176
|
).toString("base64url");
|
|
11487
|
-
const exchangeResponse = await
|
|
11488
|
-
|
|
11489
|
-
|
|
11490
|
-
|
|
11491
|
-
|
|
11492
|
-
|
|
11493
|
-
|
|
12177
|
+
const exchangeResponse = await directPrincipalSessionRequest(
|
|
12178
|
+
{
|
|
12179
|
+
action: "exchange",
|
|
12180
|
+
provider: identity.provider,
|
|
12181
|
+
key_id: identity.key_id,
|
|
12182
|
+
challenge_id: challenge.challenge_id,
|
|
12183
|
+
signature
|
|
12184
|
+
},
|
|
12185
|
+
access
|
|
12186
|
+
);
|
|
11494
12187
|
const exchange = exchangeResponse;
|
|
11495
12188
|
if (!exchange.session_token || !exchange.expires_at) return null;
|
|
11496
|
-
|
|
12189
|
+
cachedPrincipalSession = {
|
|
11497
12190
|
token: exchange.session_token,
|
|
11498
12191
|
expiresAt: new Date(exchange.expires_at).getTime(),
|
|
11499
|
-
identityKey
|
|
12192
|
+
identityKey,
|
|
12193
|
+
accessKey
|
|
11500
12194
|
};
|
|
11501
|
-
return
|
|
12195
|
+
return cachedPrincipalSession.token;
|
|
11502
12196
|
}
|
|
11503
|
-
|
|
12197
|
+
function currentPrincipalSessionToken(access) {
|
|
12198
|
+
if (!cachedPrincipalSession) return null;
|
|
12199
|
+
if (cachedPrincipalSession.accessKey !== apiAccessKey(access) || cachedPrincipalSession.expiresAt <= Date.now() + 6e4) {
|
|
12200
|
+
cachedPrincipalSession = null;
|
|
12201
|
+
return null;
|
|
12202
|
+
}
|
|
12203
|
+
return cachedPrincipalSession.token;
|
|
12204
|
+
}
|
|
12205
|
+
async function directPrincipalSessionRequest(payload, access) {
|
|
11504
12206
|
const controller = new AbortController();
|
|
11505
12207
|
const timeout = setTimeout(() => controller.abort(), apiTimeoutMs());
|
|
11506
12208
|
try {
|
|
11507
|
-
const response = await fetch(
|
|
11508
|
-
|
|
11509
|
-
|
|
11510
|
-
|
|
11511
|
-
|
|
11512
|
-
|
|
12209
|
+
const response = await fetch(
|
|
12210
|
+
`${access.configuration.baseUrl}/api/v1/agent/principal-sessions`,
|
|
12211
|
+
{
|
|
12212
|
+
method: "POST",
|
|
12213
|
+
headers: { "content-type": "application/json" },
|
|
12214
|
+
body: JSON.stringify(payload),
|
|
12215
|
+
signal: controller.signal
|
|
12216
|
+
}
|
|
12217
|
+
);
|
|
11513
12218
|
if (!response.ok) return null;
|
|
11514
12219
|
return response.json();
|
|
11515
12220
|
} finally {
|
|
11516
12221
|
clearTimeout(timeout);
|
|
11517
12222
|
}
|
|
11518
12223
|
}
|
|
12224
|
+
function normalizedMcpClientInfo(value) {
|
|
12225
|
+
if (!isRecord4(value)) return null;
|
|
12226
|
+
const name = boundedLocalProfileField(value.name);
|
|
12227
|
+
if (!name) return null;
|
|
12228
|
+
return {
|
|
12229
|
+
name,
|
|
12230
|
+
version: boundedLocalProfileField(value.version)
|
|
12231
|
+
};
|
|
12232
|
+
}
|
|
12233
|
+
function localRuntimeProfile(identity) {
|
|
12234
|
+
const configuredSurface = String(process.env.REMEMBRANCE_PLUGIN_HOST ?? "").trim().toLowerCase();
|
|
12235
|
+
const runtime = isPluginHostSurface(configuredSurface) ? configuredSurface : "other";
|
|
12236
|
+
const clientName = connectedClientInfo?.name ?? agentHostBySurface(runtime)?.host_name ?? "MCP";
|
|
12237
|
+
const hostSurface = localRuntimeHostSurface(runtime, clientName);
|
|
12238
|
+
const profileDigest = createHash3("sha256").update(
|
|
12239
|
+
[identity.key_id, runtime, hostSurface, clientName.toLowerCase()].join(
|
|
12240
|
+
":"
|
|
12241
|
+
),
|
|
12242
|
+
"utf8"
|
|
12243
|
+
).digest("base64url");
|
|
12244
|
+
return {
|
|
12245
|
+
runtime,
|
|
12246
|
+
surface: "mcp",
|
|
12247
|
+
host_surface: hostSurface,
|
|
12248
|
+
client_name: clientName,
|
|
12249
|
+
client_version: SERVER_VERSION,
|
|
12250
|
+
runtime_version: connectedClientInfo?.version ?? null,
|
|
12251
|
+
profile_key: `rpf_${profileDigest}`
|
|
12252
|
+
};
|
|
12253
|
+
}
|
|
12254
|
+
function localRuntimeHostSurface(runtime, clientName) {
|
|
12255
|
+
const configured = String(process.env.REMEMBRANCE_HOST_SURFACE ?? "").trim().toLowerCase();
|
|
12256
|
+
if (configured === "desktop" || configured === "cli" || configured === "extension" || configured === "gateway" || configured === "unknown") {
|
|
12257
|
+
return configured;
|
|
12258
|
+
}
|
|
12259
|
+
if (/desktop|chatgpt|codex app/i.test(clientName)) return "desktop";
|
|
12260
|
+
if (/cursor|visual studio|vscode/i.test(clientName)) return "extension";
|
|
12261
|
+
if (runtime === "openclaw") return "gateway";
|
|
12262
|
+
if (runtime === "cursor" || runtime === "vs_code") return "extension";
|
|
12263
|
+
if (runtime === "claude_code" || runtime === "opencode") return "cli";
|
|
12264
|
+
return "unknown";
|
|
12265
|
+
}
|
|
12266
|
+
function boundedLocalProfileField(value) {
|
|
12267
|
+
if (typeof value !== "string") return null;
|
|
12268
|
+
const normalized = value.trim().replace(/[\u0000-\u001f\u007f]/g, "");
|
|
12269
|
+
return normalized ? normalized.slice(0, 512) : null;
|
|
12270
|
+
}
|
|
11519
12271
|
function mustFindTool(name) {
|
|
11520
12272
|
const tool2 = tools.find((item) => item.name === name);
|
|
11521
12273
|
if (!tool2) {
|
|
@@ -11577,6 +12329,7 @@ export {
|
|
|
11577
12329
|
readJsonRpcMessages,
|
|
11578
12330
|
remoteConfigurationError,
|
|
11579
12331
|
reportDegradedClientHealth,
|
|
12332
|
+
resetPrincipalSessionCacheForTests,
|
|
11580
12333
|
resetValueProofKeyCacheForTests,
|
|
11581
12334
|
runConnectionDoctorCli,
|
|
11582
12335
|
runLocalConnectionDoctor
|