@remembrance-ai/opencode-plugin 0.1.55 → 0.1.60
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 +23 -2
- package/opencode.json +4 -9
- package/package.json +1 -1
- package/scripts/hook-core.mjs +1110 -115
- package/servers/remembrance-mcp.mjs +898 -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 +221 -0
- package/skills/remembrancer/references/remembrance-payloads.md +13 -8
- package/skills/remembrancer/references/remembrance-setup.md +15 -0
- package/src/index.mjs +19 -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,224 @@ 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 INFERRED_MAX_AGE_MS = 180 * 864e5;
|
|
5339
|
+
function builtInPreferenceDefinition(key) {
|
|
5340
|
+
return BUILT_IN_PREFERENCE_DEFINITIONS[key] ?? null;
|
|
5341
|
+
}
|
|
5342
|
+
|
|
5123
5343
|
// ../core/src/agent-guidance.ts
|
|
5124
5344
|
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
5345
|
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 +5466,7 @@ var REMEMBRANCE_MCP_READ_TOOLS = [
|
|
|
5246
5466
|
"query_skills",
|
|
5247
5467
|
"list_skills",
|
|
5248
5468
|
"invoke_skill",
|
|
5469
|
+
"get_effective_preferences",
|
|
5249
5470
|
"get_skill",
|
|
5250
5471
|
"get_resource",
|
|
5251
5472
|
"get_value_proof"
|
|
@@ -5258,7 +5479,9 @@ var REMEMBRANCE_MCP_FEEDBACK_TOOLS = [
|
|
|
5258
5479
|
var REMEMBRANCE_MCP_PRIVATE_CONTRIBUTION_TOOLS = [
|
|
5259
5480
|
"submit_remembrance",
|
|
5260
5481
|
"propose_private_skill",
|
|
5261
|
-
"submit_suggestion"
|
|
5482
|
+
"submit_suggestion",
|
|
5483
|
+
"record_preference",
|
|
5484
|
+
"link_current_installation"
|
|
5262
5485
|
];
|
|
5263
5486
|
var REMEMBRANCE_MCP_OPTIONAL_SHARED_CONTRIBUTION_TOOLS = [
|
|
5264
5487
|
"propose_skill_idea",
|
|
@@ -5336,6 +5559,49 @@ function remembranceSkillResourceHandle(entry) {
|
|
|
5336
5559
|
);
|
|
5337
5560
|
}
|
|
5338
5561
|
|
|
5562
|
+
// ../core/src/skill-topology.ts
|
|
5563
|
+
var skillTopologyActionSchema = external_exports.enum([
|
|
5564
|
+
"amend",
|
|
5565
|
+
"specialize",
|
|
5566
|
+
"strategy_fork",
|
|
5567
|
+
"independent_skill",
|
|
5568
|
+
"preference",
|
|
5569
|
+
"evidence_only",
|
|
5570
|
+
"hold"
|
|
5571
|
+
]);
|
|
5572
|
+
var SKILL_TOPOLOGY_ACTIONS = skillTopologyActionSchema.options;
|
|
5573
|
+
var skillScopePredicateSchema = external_exports.object({
|
|
5574
|
+
dimension: external_exports.enum([
|
|
5575
|
+
"runtime",
|
|
5576
|
+
"runtime_version",
|
|
5577
|
+
"platform",
|
|
5578
|
+
"language",
|
|
5579
|
+
"framework",
|
|
5580
|
+
"dependency",
|
|
5581
|
+
"scale",
|
|
5582
|
+
"task_stage"
|
|
5583
|
+
]),
|
|
5584
|
+
operator: external_exports.enum(["equals", "includes", "at_least", "at_most"]),
|
|
5585
|
+
value: external_exports.string().trim().min(1).max(256)
|
|
5586
|
+
}).strict();
|
|
5587
|
+
var skillTopologyAssessmentSchema = external_exports.object({
|
|
5588
|
+
action: skillTopologyActionSchema,
|
|
5589
|
+
confidence: external_exports.number().min(0).max(1),
|
|
5590
|
+
subjectivity: external_exports.number().min(0).max(1),
|
|
5591
|
+
generalizability: external_exports.number().min(0).max(1),
|
|
5592
|
+
specificity_delta: external_exports.number().min(-1).max(1),
|
|
5593
|
+
approach_divergence: external_exports.number().min(0).max(1),
|
|
5594
|
+
stable_conditions: external_exports.array(skillScopePredicateSchema).max(12).default([]),
|
|
5595
|
+
use_when: external_exports.array(external_exports.string().trim().min(1).max(512)).max(20).default([]),
|
|
5596
|
+
avoid_when: external_exports.array(external_exports.string().trim().min(1).max(512)).max(20).default([]),
|
|
5597
|
+
preference_setting: preferenceSettingSchema.nullable().default(null),
|
|
5598
|
+
rationale: external_exports.string().trim().min(1).max(2e3)
|
|
5599
|
+
}).strict();
|
|
5600
|
+
var skillTopologyRoutingHintSchema = external_exports.object({
|
|
5601
|
+
suggested_action: skillTopologyActionSchema,
|
|
5602
|
+
conditions: external_exports.array(skillScopePredicateSchema).max(12).default([])
|
|
5603
|
+
}).strict();
|
|
5604
|
+
|
|
5339
5605
|
// ../core/src/schemas.ts
|
|
5340
5606
|
var DEFAULT_MUTATION_BODY_LIMIT_BYTES = 256 * 1024;
|
|
5341
5607
|
var MAX_SHORT_TEXT_LENGTH = 512;
|
|
@@ -5554,7 +5820,11 @@ var agentSchema = external_exports.object({
|
|
|
5554
5820
|
var queryTaskSchema = external_exports.object({
|
|
5555
5821
|
domain: boundedString(MAX_SHORT_TEXT_LENGTH),
|
|
5556
5822
|
summary: boundedString(MAX_LONG_TEXT_LENGTH),
|
|
5557
|
-
constraints: external_exports.array(boundedString(MAX_SHORT_TEXT_LENGTH)).max(40).default([])
|
|
5823
|
+
constraints: external_exports.array(boundedString(MAX_SHORT_TEXT_LENGTH)).max(40).default([]),
|
|
5824
|
+
// Explicit task-local preferences are normalized data, not raw prompt text.
|
|
5825
|
+
// They are resolved for this request only and are never persisted as a
|
|
5826
|
+
// durable member preference unless the caller separately records them.
|
|
5827
|
+
preferences: taskPreferenceSettingsSchema.optional()
|
|
5558
5828
|
});
|
|
5559
5829
|
var queryRuntimeSchema = external_exports.enum([
|
|
5560
5830
|
"codex",
|
|
@@ -5566,12 +5836,23 @@ var queryRuntimeSchema = external_exports.enum([
|
|
|
5566
5836
|
"other",
|
|
5567
5837
|
"unknown"
|
|
5568
5838
|
]);
|
|
5839
|
+
var runtimeHostSurfaceSchema = external_exports.enum([
|
|
5840
|
+
"desktop",
|
|
5841
|
+
"cli",
|
|
5842
|
+
"extension",
|
|
5843
|
+
"gateway",
|
|
5844
|
+
"unknown"
|
|
5845
|
+
]);
|
|
5846
|
+
var runtimeProfileVersionSchema = external_exports.string().trim().min(1).max(64).regex(/^[A-Za-z0-9][A-Za-z0-9._+-]*$/);
|
|
5569
5847
|
var queryDirectiveIdSchema = external_exports.string().regex(/^dir_[A-Za-z0-9_-]{16,80}$/);
|
|
5570
5848
|
var queryClientContextSchema = external_exports.object({
|
|
5571
5849
|
surface: external_exports.enum(["plugin_hook", "mcp", "rest", "unknown"]).default("unknown"),
|
|
5572
5850
|
runtime: queryRuntimeSchema.optional(),
|
|
5573
5851
|
trigger_reason: boundedString(MAX_SHORT_TEXT_LENGTH).optional(),
|
|
5574
|
-
directive_id: queryDirectiveIdSchema.optional()
|
|
5852
|
+
directive_id: queryDirectiveIdSchema.optional(),
|
|
5853
|
+
// A local, installation-keyed digest. It lets one engineer retain
|
|
5854
|
+
// project-scoped preferences without disclosing a path or repository.
|
|
5855
|
+
project_key: external_exports.string().regex(/^prj_[A-Za-z0-9_-]{12,120}$/).optional()
|
|
5575
5856
|
}).strict();
|
|
5576
5857
|
var reasoningEffortSchema = external_exports.enum([
|
|
5577
5858
|
"none",
|
|
@@ -5794,31 +6075,145 @@ var agentTaskOutcomeRequestSchema = external_exports.object({
|
|
|
5794
6075
|
});
|
|
5795
6076
|
}
|
|
5796
6077
|
});
|
|
5797
|
-
var
|
|
6078
|
+
var runtimeProfileRegistrationSchema = external_exports.object({
|
|
6079
|
+
runtime: queryRuntimeSchema,
|
|
6080
|
+
surface: external_exports.enum(["plugin_hook", "mcp", "rest", "unknown"]),
|
|
6081
|
+
host_surface: runtimeHostSurfaceSchema.default("unknown"),
|
|
6082
|
+
client_name: external_exports.string().trim().min(1).max(64).regex(/^[A-Za-z0-9][A-Za-z0-9 ._+-]*$/),
|
|
6083
|
+
client_version: runtimeProfileVersionSchema.nullable().optional(),
|
|
6084
|
+
runtime_version: runtimeProfileVersionSchema.nullable().optional(),
|
|
6085
|
+
profile_key: external_exports.string().regex(/^rpf_[A-Za-z0-9_-]{16,120}$/)
|
|
6086
|
+
}).strict();
|
|
6087
|
+
var principalSessionChallengeSchema = external_exports.object({
|
|
5798
6088
|
action: external_exports.literal("challenge"),
|
|
5799
6089
|
provider: economicsSessionProviderSchema,
|
|
5800
|
-
key_id: boundedString(MAX_SHORT_TEXT_LENGTH)
|
|
6090
|
+
key_id: boundedString(MAX_SHORT_TEXT_LENGTH),
|
|
6091
|
+
runtime_profile: runtimeProfileRegistrationSchema.nullable().optional(),
|
|
6092
|
+
member_link_token: external_exports.string().regex(/^mlink_[A-Za-z0-9_-]{24,160}$/).nullable().optional()
|
|
5801
6093
|
}).strict();
|
|
5802
|
-
var
|
|
6094
|
+
var principalSessionExchangeSchema = external_exports.object({
|
|
5803
6095
|
action: external_exports.literal("exchange"),
|
|
5804
6096
|
provider: economicsSessionProviderSchema,
|
|
5805
6097
|
key_id: boundedString(MAX_SHORT_TEXT_LENGTH),
|
|
5806
6098
|
challenge_id: boundedString(MAX_SHORT_TEXT_LENGTH),
|
|
5807
6099
|
signature: boundedString(MAX_LONG_TEXT_LENGTH)
|
|
5808
6100
|
}).strict();
|
|
5809
|
-
var
|
|
5810
|
-
|
|
5811
|
-
|
|
6101
|
+
var principalSessionRequestSchema = external_exports.discriminatedUnion("action", [
|
|
6102
|
+
principalSessionChallengeSchema,
|
|
6103
|
+
principalSessionExchangeSchema
|
|
5812
6104
|
]);
|
|
6105
|
+
var recordPreferenceRequestSchema = external_exports.object({
|
|
6106
|
+
setting: preferenceSettingSchema,
|
|
6107
|
+
// Organization guidance is dashboard-owned policy. Agent transports may
|
|
6108
|
+
// record only identity-bound or task-context preference observations.
|
|
6109
|
+
scope: external_exports.enum([
|
|
6110
|
+
"auto",
|
|
6111
|
+
"member",
|
|
6112
|
+
"member_runtime",
|
|
6113
|
+
"installation",
|
|
6114
|
+
"project",
|
|
6115
|
+
"skill",
|
|
6116
|
+
"domain"
|
|
6117
|
+
]),
|
|
6118
|
+
source_category: external_exports.enum(["explicit_user", "agent_observed"]),
|
|
6119
|
+
evidence_hash: external_exports.string().regex(/^[a-f0-9]{32,128}$/i),
|
|
6120
|
+
task_hash: external_exports.string().regex(/^[a-f0-9]{32,128}$/i),
|
|
6121
|
+
project_key: external_exports.string().regex(/^prj_[A-Za-z0-9_-]{12,120}$/).nullable().optional(),
|
|
6122
|
+
skill_slug: boundedString(MAX_SHORT_TEXT_LENGTH).nullable().optional(),
|
|
6123
|
+
domain: boundedString(MAX_SHORT_TEXT_LENGTH).nullable().optional(),
|
|
6124
|
+
confidence: external_exports.number().min(0).max(1)
|
|
6125
|
+
}).strict().superRefine((value, ctx) => {
|
|
6126
|
+
const required = value.scope === "project" ? "project_key" : value.scope === "skill" ? "skill_slug" : value.scope === "domain" ? "domain" : null;
|
|
6127
|
+
if (required && !value[required]) {
|
|
6128
|
+
ctx.addIssue({
|
|
6129
|
+
code: external_exports.ZodIssueCode.custom,
|
|
6130
|
+
path: [required],
|
|
6131
|
+
message: `${required} is required for ${value.scope} preferences`
|
|
6132
|
+
});
|
|
6133
|
+
}
|
|
6134
|
+
if (value.source_category === "agent_observed" && value.confidence >= 1) {
|
|
6135
|
+
ctx.addIssue({
|
|
6136
|
+
code: external_exports.ZodIssueCode.custom,
|
|
6137
|
+
path: ["confidence"],
|
|
6138
|
+
message: "Inferred observations cannot claim absolute confidence"
|
|
6139
|
+
});
|
|
6140
|
+
}
|
|
6141
|
+
});
|
|
6142
|
+
var effectivePreferencesRequestSchema = external_exports.object({
|
|
6143
|
+
// Omit or send [] to resolve every active bounded preference. Supplying
|
|
6144
|
+
// keys is an optional projection for clients that need only a subset.
|
|
6145
|
+
keys: external_exports.array(preferenceKeySchema).max(MAX_EFFECTIVE_PREFERENCES).default([]),
|
|
6146
|
+
task_preferences: taskPreferenceSettingsSchema.optional(),
|
|
6147
|
+
project_key: external_exports.string().regex(/^prj_[A-Za-z0-9_-]{12,120}$/).nullable().optional(),
|
|
6148
|
+
skill_slug: boundedString(MAX_SHORT_TEXT_LENGTH).nullable().optional(),
|
|
6149
|
+
domains: external_exports.array(boundedString(MAX_SHORT_TEXT_LENGTH)).max(20).default([])
|
|
6150
|
+
}).strict();
|
|
5813
6151
|
var agentPrincipalRegistrationRequestSchema = external_exports.object({
|
|
5814
6152
|
display_name: boundedString(MAX_SHORT_TEXT_LENGTH),
|
|
5815
6153
|
provider: agentProviderSchema,
|
|
5816
6154
|
runtime_version: boundedString(MAX_SHORT_TEXT_LENGTH).nullable().optional(),
|
|
5817
6155
|
parent_principal_id: boundedString(MAX_SHORT_TEXT_LENGTH).nullable().optional()
|
|
5818
6156
|
}).strict();
|
|
5819
|
-
var agentPrincipalUpdateRequestSchema = external_exports.
|
|
5820
|
-
|
|
5821
|
-
|
|
6157
|
+
var agentPrincipalUpdateRequestSchema = external_exports.discriminatedUnion(
|
|
6158
|
+
"action",
|
|
6159
|
+
[
|
|
6160
|
+
external_exports.object({
|
|
6161
|
+
principal_id: boundedString(MAX_SHORT_TEXT_LENGTH),
|
|
6162
|
+
action: external_exports.enum(["deactivate", "reactivate"])
|
|
6163
|
+
}).strict(),
|
|
6164
|
+
external_exports.object({
|
|
6165
|
+
principal_id: boundedString(MAX_SHORT_TEXT_LENGTH),
|
|
6166
|
+
action: external_exports.literal("rename"),
|
|
6167
|
+
display_name: boundedString(MAX_SHORT_TEXT_LENGTH)
|
|
6168
|
+
}).strict()
|
|
6169
|
+
]
|
|
6170
|
+
);
|
|
6171
|
+
var agentInstallationManagementRequestSchema = external_exports.discriminatedUnion(
|
|
6172
|
+
"action",
|
|
6173
|
+
[
|
|
6174
|
+
external_exports.object({
|
|
6175
|
+
action: external_exports.literal("rename"),
|
|
6176
|
+
installation_id: boundedString(MAX_SHORT_TEXT_LENGTH),
|
|
6177
|
+
display_name: boundedString(MAX_SHORT_TEXT_LENGTH)
|
|
6178
|
+
}).strict(),
|
|
6179
|
+
external_exports.object({
|
|
6180
|
+
action: external_exports.literal("rename_runtime_profile"),
|
|
6181
|
+
runtime_profile_id: boundedString(MAX_SHORT_TEXT_LENGTH),
|
|
6182
|
+
display_name: boundedString(MAX_SHORT_TEXT_LENGTH)
|
|
6183
|
+
}).strict(),
|
|
6184
|
+
external_exports.object({
|
|
6185
|
+
action: external_exports.literal("set_status"),
|
|
6186
|
+
installation_id: boundedString(MAX_SHORT_TEXT_LENGTH),
|
|
6187
|
+
status: external_exports.enum(["active", "inactive"])
|
|
6188
|
+
}).strict(),
|
|
6189
|
+
external_exports.object({
|
|
6190
|
+
action: external_exports.literal("assign_member"),
|
|
6191
|
+
installation_id: boundedString(MAX_SHORT_TEXT_LENGTH),
|
|
6192
|
+
member_principal_id: boundedString(MAX_SHORT_TEXT_LENGTH)
|
|
6193
|
+
}).strict(),
|
|
6194
|
+
external_exports.object({
|
|
6195
|
+
action: external_exports.literal("unlink_member"),
|
|
6196
|
+
installation_id: boundedString(MAX_SHORT_TEXT_LENGTH)
|
|
6197
|
+
}).strict()
|
|
6198
|
+
]
|
|
6199
|
+
);
|
|
6200
|
+
var dashboardPreferenceMutationSchema = external_exports.discriminatedUnion(
|
|
6201
|
+
"action",
|
|
6202
|
+
[
|
|
6203
|
+
external_exports.object({
|
|
6204
|
+
action: external_exports.literal("set"),
|
|
6205
|
+
setting: preferenceSettingSchema,
|
|
6206
|
+
scope: external_exports.enum(["member", "organization"]),
|
|
6207
|
+
enforcement: external_exports.enum(["recommended", "required"]).default("recommended")
|
|
6208
|
+
}).strict(),
|
|
6209
|
+
external_exports.object({
|
|
6210
|
+
action: external_exports.enum(["reset", "undo"]),
|
|
6211
|
+
revision_id: boundedString(MAX_SHORT_TEXT_LENGTH)
|
|
6212
|
+
}).strict()
|
|
6213
|
+
]
|
|
6214
|
+
);
|
|
6215
|
+
var agentMemberLinkRequestSchema = external_exports.object({
|
|
6216
|
+
member_link_token: external_exports.string().regex(/^mlink_[A-Za-z0-9_-]{24,160}$/)
|
|
5822
6217
|
}).strict();
|
|
5823
6218
|
var agentPrincipalKeyBindingRequestSchema = external_exports.object({
|
|
5824
6219
|
principal_id: boundedString(MAX_SHORT_TEXT_LENGTH),
|
|
@@ -5901,6 +6296,7 @@ var remembrancePayloadSchema = external_exports.object({
|
|
|
5901
6296
|
}).strict().optional(),
|
|
5902
6297
|
enterprise_encryption: enterpriseEncryptedPayloadEnvelopeSchema.optional(),
|
|
5903
6298
|
suggested_update: suggestedUpdateSchema.default({ kind: "none" }),
|
|
6299
|
+
routing_hint: skillTopologyRoutingHintSchema.optional(),
|
|
5904
6300
|
evidence: evidenceSchema.default({ artifact_hashes: [] }),
|
|
5905
6301
|
// Client-only directive: "sign this with my local TOFU key" (the MCP
|
|
5906
6302
|
// submit_remembrance tool exposes it, and feedback-next-step tells REST
|
|
@@ -5935,6 +6331,8 @@ var skillMetadataSchema = external_exports.object({
|
|
|
5935
6331
|
permissions: external_exports.record(external_exports.boolean()).default({}),
|
|
5936
6332
|
contraindications: external_exports.array(external_exports.string().max(MAX_LONG_TEXT_LENGTH)).max(80).default([]),
|
|
5937
6333
|
applicability: skillApplicabilitySchema.optional(),
|
|
6334
|
+
preference_defaults: skillPreferenceDefaultsSchema.optional(),
|
|
6335
|
+
preference_traits: skillPreferenceTraitsSchema.optional(),
|
|
5938
6336
|
feedback_url: external_exports.string().url(),
|
|
5939
6337
|
install_command: external_exports.string().min(1),
|
|
5940
6338
|
stats: external_exports.object({
|
|
@@ -5967,6 +6365,7 @@ var skillIdeaRequestSchema = external_exports.object({
|
|
|
5967
6365
|
domain_slug: boundedString(MAX_SHORT_TEXT_LENGTH).nullable().optional(),
|
|
5968
6366
|
proposed_skill_md: external_exports.string().max(MAX_LONG_TEXT_LENGTH).nullable().optional(),
|
|
5969
6367
|
proposed_metadata: boundedJsonRecord().default({}),
|
|
6368
|
+
routing_hint: skillTopologyRoutingHintSchema.optional(),
|
|
5970
6369
|
enterprise_encryption: enterpriseEncryptedPayloadEnvelopeSchema.optional(),
|
|
5971
6370
|
idempotency_key: boundedString(MAX_SHORT_TEXT_LENGTH).optional()
|
|
5972
6371
|
});
|
|
@@ -5979,6 +6378,7 @@ var suggestionRequestSchema = external_exports.object({
|
|
|
5979
6378
|
summary: boundedString(MAX_LONG_TEXT_LENGTH),
|
|
5980
6379
|
diff_text: external_exports.string().max(MAX_LONG_TEXT_LENGTH).nullable().optional(),
|
|
5981
6380
|
payload: boundedJsonRecord().default({}),
|
|
6381
|
+
routing_hint: skillTopologyRoutingHintSchema.optional(),
|
|
5982
6382
|
enterprise_encryption: enterpriseEncryptedPayloadEnvelopeSchema.optional(),
|
|
5983
6383
|
idempotency_key: boundedString(MAX_SHORT_TEXT_LENGTH).optional()
|
|
5984
6384
|
});
|
|
@@ -6059,6 +6459,7 @@ var attestationKeyRegistrationRequestSchema = external_exports.object({
|
|
|
6059
6459
|
agent: agentSchema.optional(),
|
|
6060
6460
|
proof: external_exports.object({
|
|
6061
6461
|
algorithm: external_exports.enum(["ed25519"]).default("ed25519"),
|
|
6462
|
+
owner_binding: external_exports.string().regex(/^areg_[A-Za-z0-9_-]{24,120}$/).optional(),
|
|
6062
6463
|
signed_at: external_exports.string().datetime(),
|
|
6063
6464
|
signature: boundedString(MAX_LONG_TEXT_LENGTH)
|
|
6064
6465
|
}),
|
|
@@ -6257,7 +6658,7 @@ var seedSkills = [
|
|
|
6257
6658
|
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
6659
|
status: "active",
|
|
6259
6660
|
visibility: "public",
|
|
6260
|
-
version: "0.1.
|
|
6661
|
+
version: "0.1.18",
|
|
6261
6662
|
domains: ["agent-skills", "mcp", "resource-discovery"],
|
|
6262
6663
|
tags: [
|
|
6263
6664
|
"remembrance",
|
|
@@ -6308,7 +6709,7 @@ var seedSkills = [
|
|
|
6308
6709
|
"gemini",
|
|
6309
6710
|
"rest"
|
|
6310
6711
|
],
|
|
6311
|
-
version: "0.1.
|
|
6712
|
+
version: "0.1.18",
|
|
6312
6713
|
status: "active",
|
|
6313
6714
|
visibility: "public",
|
|
6314
6715
|
providers: ["codex", "claude", "cursor", "openclaw", "generic"],
|
|
@@ -7974,7 +8375,14 @@ var verifierOutputSchema = external_exports.object({
|
|
|
7974
8375
|
}).nullable().default(null),
|
|
7975
8376
|
// Advisory retrieval enrichment (see proposedSkillMetadataSchema). Optional
|
|
7976
8377
|
// and defaults to null so existing verifier outputs remain valid.
|
|
7977
|
-
proposed_metadata: proposedSkillMetadataSchema.nullable().default(null)
|
|
8378
|
+
proposed_metadata: proposedSkillMetadataSchema.nullable().default(null),
|
|
8379
|
+
// Advisory evidence-routing assessment. Deterministic routing and the
|
|
8380
|
+
// existing safety/review gates decide whether it can create a candidate.
|
|
8381
|
+
// Topology is advisory and newer than the base verifier contract. A legacy
|
|
8382
|
+
// or malformed topology fragment must not discard an otherwise valid safety
|
|
8383
|
+
// verdict; it degrades to null, which the topology router retains as
|
|
8384
|
+
// evidence-only rather than allowing an unclassified mutation.
|
|
8385
|
+
topology_assessment: skillTopologyAssessmentSchema.nullable().default(null).catch(null)
|
|
7978
8386
|
});
|
|
7979
8387
|
|
|
7980
8388
|
// src/server.ts
|
|
@@ -7982,6 +8390,8 @@ import { existsSync as existsSync3 } from "node:fs";
|
|
|
7982
8390
|
import { mkdir as mkdir2, writeFile as writeFile2 } from "node:fs/promises";
|
|
7983
8391
|
import { dirname as dirname2 } from "node:path";
|
|
7984
8392
|
import {
|
|
8393
|
+
createHash as createHash3,
|
|
8394
|
+
createHmac,
|
|
7985
8395
|
createPrivateKey as createPrivateKey2,
|
|
7986
8396
|
generateKeyPairSync,
|
|
7987
8397
|
sign as signPayload
|
|
@@ -9343,6 +9753,24 @@ var toolDefinitions = [
|
|
|
9343
9753
|
"/api/v1/agent/skill-invocations",
|
|
9344
9754
|
agentSkillInvocationRequestSchema
|
|
9345
9755
|
),
|
|
9756
|
+
tool(
|
|
9757
|
+
"get_effective_preferences",
|
|
9758
|
+
"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.",
|
|
9759
|
+
"/api/v1/agent/preferences/effective",
|
|
9760
|
+
effectivePreferencesRequestSchema
|
|
9761
|
+
),
|
|
9762
|
+
tool(
|
|
9763
|
+
"record_preference",
|
|
9764
|
+
"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.",
|
|
9765
|
+
"/api/v1/agent/preferences",
|
|
9766
|
+
recordPreferenceRequestSchema
|
|
9767
|
+
),
|
|
9768
|
+
tool(
|
|
9769
|
+
"link_current_installation",
|
|
9770
|
+
"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.",
|
|
9771
|
+
"/api/v1/agent/member-links",
|
|
9772
|
+
agentMemberLinkRequestSchema
|
|
9773
|
+
),
|
|
9346
9774
|
tool(
|
|
9347
9775
|
"get_skill",
|
|
9348
9776
|
"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 +9893,7 @@ function localTool(name, description, local, schema) {
|
|
|
9465
9893
|
};
|
|
9466
9894
|
}
|
|
9467
9895
|
function annotationsForTool(name) {
|
|
9468
|
-
const readOnly = name === "list_skills" || name === "get_value_proof" || name === "run_connection_doctor";
|
|
9896
|
+
const readOnly = name === "list_skills" || name === "get_effective_preferences" || name === "get_value_proof" || name === "run_connection_doctor";
|
|
9469
9897
|
const openWorld = name === "submit_feedback" || name === "submit_remembrance" || name === "propose_skill_idea" || name === "submit_suggestion" || name === "submit_resource" || name === "submit_resource_review";
|
|
9470
9898
|
return {
|
|
9471
9899
|
readOnlyHint: readOnly,
|
|
@@ -9866,107 +10294,104 @@ function remembranceConfigPath(env = process.env, homeDirectory = homedir3()) {
|
|
|
9866
10294
|
return join3(configRoot, "remembrance", "config.json");
|
|
9867
10295
|
}
|
|
9868
10296
|
function resolveApiCredential(env = process.env, fileSystem = nodeFileSystem2) {
|
|
9869
|
-
|
|
10297
|
+
return resolveApiAccess(env, fileSystem).credential;
|
|
10298
|
+
}
|
|
10299
|
+
function resolveApiAccess(env = process.env, fileSystem = nodeFileSystem2) {
|
|
10300
|
+
const shared = readSharedConfigSnapshot(env, fileSystem);
|
|
10301
|
+
const configuration = resolveApiConfigurationFromSnapshot(env, shared);
|
|
9870
10302
|
const environmentKey = env.REMEMBRANCE_API_KEY?.trim();
|
|
10303
|
+
let credential;
|
|
9871
10304
|
if (environmentKey) {
|
|
9872
10305
|
const explicitBinding = env.REMEMBRANCE_API_KEY_ORIGIN?.trim();
|
|
9873
10306
|
const binding = explicitBinding ? normalizeApiUrl(explicitBinding, env) : { baseUrl: DEFAULT_API_URL, issue: null };
|
|
9874
10307
|
if (!binding.baseUrl || binding.issue) {
|
|
9875
|
-
|
|
10308
|
+
credential = unusableDestinationBinding();
|
|
10309
|
+
} else {
|
|
10310
|
+
credential = credentialForDestination(
|
|
10311
|
+
{
|
|
10312
|
+
apiKey: environmentKey,
|
|
10313
|
+
source: "environment",
|
|
10314
|
+
boundBaseUrl: binding.baseUrl
|
|
10315
|
+
},
|
|
10316
|
+
configuration
|
|
10317
|
+
);
|
|
10318
|
+
}
|
|
10319
|
+
} else if (!shared.present) {
|
|
10320
|
+
credential = { apiKey: "", source: "none" };
|
|
10321
|
+
} else if (!shared.parsed || Object.prototype.hasOwnProperty.call(shared.parsed, "apiKey") && (typeof shared.parsed.apiKey !== "string" || !shared.parsed.apiKey.trim())) {
|
|
10322
|
+
credential = unusableSharedConfig();
|
|
10323
|
+
} else if (!shared.config.apiKey) {
|
|
10324
|
+
credential = { apiKey: "", source: "none" };
|
|
10325
|
+
} else {
|
|
10326
|
+
const binding = shared.config.apiUrl ? normalizeApiUrl(shared.config.apiUrl, env) : { baseUrl: DEFAULT_API_URL, issue: null };
|
|
10327
|
+
if (!binding.baseUrl || binding.issue) {
|
|
10328
|
+
credential = unusableSharedConfig();
|
|
10329
|
+
} else {
|
|
10330
|
+
credential = credentialForDestination(
|
|
10331
|
+
{
|
|
10332
|
+
apiKey: shared.config.apiKey,
|
|
10333
|
+
source: "shared_config",
|
|
10334
|
+
boundBaseUrl: binding.baseUrl
|
|
10335
|
+
},
|
|
10336
|
+
configuration
|
|
10337
|
+
);
|
|
9876
10338
|
}
|
|
9877
|
-
return credentialForDestination(
|
|
9878
|
-
{
|
|
9879
|
-
apiKey: environmentKey,
|
|
9880
|
-
source: "environment",
|
|
9881
|
-
boundBaseUrl: binding.baseUrl
|
|
9882
|
-
},
|
|
9883
|
-
apiConfiguration2
|
|
9884
|
-
);
|
|
9885
10339
|
}
|
|
10340
|
+
return {
|
|
10341
|
+
configuration,
|
|
10342
|
+
credential,
|
|
10343
|
+
memberLinkToken: env.REMEMBRANCE_MEMBER_LINK_TOKEN?.trim() ?? shared.config.memberLinkToken ?? ""
|
|
10344
|
+
};
|
|
10345
|
+
}
|
|
10346
|
+
function isUnusableCredentialSource(source) {
|
|
10347
|
+
return source.startsWith("unusable_");
|
|
10348
|
+
}
|
|
10349
|
+
function readSharedConfigSnapshot(env, fileSystem) {
|
|
9886
10350
|
const path = remembranceConfigPath(env);
|
|
9887
10351
|
if (!fileSystem.exists(path)) {
|
|
9888
|
-
return {
|
|
10352
|
+
return { present: false, parsed: null, config: {} };
|
|
9889
10353
|
}
|
|
9890
10354
|
try {
|
|
9891
10355
|
const parsed = JSON.parse(
|
|
9892
10356
|
fileSystem.read(path, MAX_LOCAL_CONFIG_BYTES)
|
|
9893
10357
|
);
|
|
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
|
-
);
|
|
10358
|
+
return isRecord3(parsed) ? { present: true, parsed, config: normalizeConfig(parsed) } : { present: true, parsed: null, config: {} };
|
|
9916
10359
|
} catch {
|
|
9917
|
-
return
|
|
10360
|
+
return { present: true, parsed: null, config: {} };
|
|
9918
10361
|
}
|
|
9919
10362
|
}
|
|
9920
|
-
function
|
|
9921
|
-
return resolveApiCredential(env, fileSystem).apiKey;
|
|
9922
|
-
}
|
|
9923
|
-
function resolveApiConfiguration(env = process.env, fileSystem = nodeFileSystem2) {
|
|
10363
|
+
function resolveApiConfigurationFromSnapshot(env, shared) {
|
|
9924
10364
|
const environmentUrl = env.REMEMBRANCE_API_URL?.trim();
|
|
9925
10365
|
if (environmentUrl) {
|
|
9926
|
-
const
|
|
9927
|
-
if (
|
|
9928
|
-
return { baseUrl:
|
|
10366
|
+
const normalized2 = normalizeApiUrl(environmentUrl, env);
|
|
10367
|
+
if (normalized2.baseUrl !== null) {
|
|
10368
|
+
return { baseUrl: normalized2.baseUrl, source: "environment" };
|
|
9929
10369
|
}
|
|
9930
10370
|
return {
|
|
9931
10371
|
baseUrl: DEFAULT_API_URL,
|
|
9932
10372
|
source: "unusable_environment",
|
|
9933
|
-
issue:
|
|
10373
|
+
issue: normalized2.issue
|
|
9934
10374
|
};
|
|
9935
10375
|
}
|
|
9936
|
-
|
|
9937
|
-
if (!fileSystem.exists(path)) {
|
|
10376
|
+
if (!shared.present) {
|
|
9938
10377
|
return { baseUrl: DEFAULT_API_URL, source: "default" };
|
|
9939
10378
|
}
|
|
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 {
|
|
10379
|
+
if (!shared.parsed) {
|
|
9964
10380
|
return {
|
|
9965
10381
|
baseUrl: DEFAULT_API_URL,
|
|
9966
10382
|
source: "unusable_shared_config",
|
|
9967
10383
|
issue: "invalid_url"
|
|
9968
10384
|
};
|
|
9969
10385
|
}
|
|
10386
|
+
if (!Object.prototype.hasOwnProperty.call(shared.parsed, "apiUrl")) {
|
|
10387
|
+
return { baseUrl: DEFAULT_API_URL, source: "default" };
|
|
10388
|
+
}
|
|
10389
|
+
const normalized = normalizeApiUrl(shared.parsed.apiUrl, env);
|
|
10390
|
+
return normalized.baseUrl !== null ? { baseUrl: normalized.baseUrl, source: "shared_config" } : {
|
|
10391
|
+
baseUrl: DEFAULT_API_URL,
|
|
10392
|
+
source: "unusable_shared_config",
|
|
10393
|
+
issue: normalized.issue
|
|
10394
|
+
};
|
|
9970
10395
|
}
|
|
9971
10396
|
function sharedConfigStatus(env = process.env, fileSystem = nodeFileSystem2) {
|
|
9972
10397
|
const path = remembranceConfigPath(env);
|
|
@@ -10348,9 +10773,11 @@ function normalizeConfig(value) {
|
|
|
10348
10773
|
if (!isRecord3(value)) return {};
|
|
10349
10774
|
const apiKey = typeof value.apiKey === "string" ? value.apiKey.trim() : "";
|
|
10350
10775
|
const apiUrl = typeof value.apiUrl === "string" ? value.apiUrl.trim() : "";
|
|
10776
|
+
const memberLinkToken = typeof value.memberLinkToken === "string" ? value.memberLinkToken.trim() : "";
|
|
10351
10777
|
return {
|
|
10352
10778
|
...apiKey ? { apiKey } : {},
|
|
10353
|
-
...apiUrl ? { apiUrl } : {}
|
|
10779
|
+
...apiUrl ? { apiUrl } : {},
|
|
10780
|
+
...memberLinkToken && /^mlink_[A-Za-z0-9_-]{24,160}$/.test(memberLinkToken) ? { memberLinkToken } : {}
|
|
10354
10781
|
};
|
|
10355
10782
|
}
|
|
10356
10783
|
function normalizeApiUrl(value, env) {
|
|
@@ -10492,17 +10919,37 @@ async function checkClientUpdate(input) {
|
|
|
10492
10919
|
}
|
|
10493
10920
|
|
|
10494
10921
|
// src/server.ts
|
|
10495
|
-
var apiConfiguration = resolveApiConfiguration();
|
|
10496
|
-
var apiBase = apiConfiguration.baseUrl;
|
|
10497
10922
|
var MAX_REMOTE_RESPONSE_BYTES = 4 * 1024 * 1024;
|
|
10498
10923
|
var DOCTOR_PROBE_TIMEOUT_MS = 7500;
|
|
10499
|
-
var SERVER_VERSION = true ? "0.1.
|
|
10924
|
+
var SERVER_VERSION = true ? "0.1.60" : "0.0.0-dev";
|
|
10500
10925
|
var tools = toolDefinitions;
|
|
10501
10926
|
var doctorCliRequested = process.argv[2] === "doctor";
|
|
10502
10927
|
var inputBuffer = Buffer.alloc(0);
|
|
10503
10928
|
var clientFraming = "ndjson";
|
|
10504
|
-
var
|
|
10929
|
+
var cachedPrincipalSession = null;
|
|
10930
|
+
var connectedClientInfo = null;
|
|
10505
10931
|
var cachedValueProofKeys = null;
|
|
10932
|
+
var PRINCIPAL_SESSION_REQUIRED_TOOLS = /* @__PURE__ */ new Set([
|
|
10933
|
+
"record_preference",
|
|
10934
|
+
"link_current_installation"
|
|
10935
|
+
]);
|
|
10936
|
+
var PRINCIPAL_SESSION_AWAITED_TOOLS = /* @__PURE__ */ new Set([
|
|
10937
|
+
...PRINCIPAL_SESSION_REQUIRED_TOOLS,
|
|
10938
|
+
"report_task_outcome"
|
|
10939
|
+
]);
|
|
10940
|
+
function apiAccessKey(access) {
|
|
10941
|
+
return createHash3("sha256").update(
|
|
10942
|
+
[
|
|
10943
|
+
access.configuration.baseUrl,
|
|
10944
|
+
access.credential.source,
|
|
10945
|
+
access.credential.apiKey
|
|
10946
|
+
].join("\0"),
|
|
10947
|
+
"utf8"
|
|
10948
|
+
).digest("hex");
|
|
10949
|
+
}
|
|
10950
|
+
function resetPrincipalSessionCacheForTests() {
|
|
10951
|
+
cachedPrincipalSession = null;
|
|
10952
|
+
}
|
|
10506
10953
|
if (!doctorCliRequested) {
|
|
10507
10954
|
process.stdin.on("data", (chunk) => {
|
|
10508
10955
|
inputBuffer = Buffer.concat([inputBuffer, chunk]);
|
|
@@ -10592,7 +11039,10 @@ async function handleRequest(request) {
|
|
|
10592
11039
|
}
|
|
10593
11040
|
async function dispatchJsonRpcRequest(request) {
|
|
10594
11041
|
if (request.method === "initialize") {
|
|
10595
|
-
|
|
11042
|
+
connectedClientInfo = normalizedMcpClientInfo(request.params?.clientInfo);
|
|
11043
|
+
const access = resolveApiAccess();
|
|
11044
|
+
void ensurePrincipalSessionToken(access).catch(() => null);
|
|
11045
|
+
const clientUpdate = await currentClientUpdate(access);
|
|
10596
11046
|
return {
|
|
10597
11047
|
id: request.id,
|
|
10598
11048
|
result: {
|
|
@@ -10709,47 +11159,54 @@ function messageLooksLikeZodError(message) {
|
|
|
10709
11159
|
}
|
|
10710
11160
|
async function callTool(definition, rawArguments) {
|
|
10711
11161
|
const payload = definition.schema.parse(rawArguments ?? {});
|
|
11162
|
+
const access = resolveApiAccess();
|
|
10712
11163
|
if (definition.local === "bootstrap_agent_identity") {
|
|
10713
|
-
return bootstrapAgentIdentity(payload);
|
|
11164
|
+
return bootstrapAgentIdentity(payload, access);
|
|
10714
11165
|
}
|
|
10715
11166
|
if (definition.local === "queue_private_skill_import") {
|
|
10716
|
-
return queuePrivateSkillHandoff(payload, {
|
|
11167
|
+
return queuePrivateSkillHandoff(payload, {
|
|
11168
|
+
uploadBaseUrl: access.configuration.baseUrl
|
|
11169
|
+
});
|
|
10717
11170
|
}
|
|
10718
11171
|
if (definition.name === "run_connection_doctor") {
|
|
10719
|
-
return runLocalConnectionDoctor(
|
|
11172
|
+
return runLocalConnectionDoctor(
|
|
11173
|
+
payload,
|
|
11174
|
+
"mcp_tool",
|
|
11175
|
+
access
|
|
11176
|
+
);
|
|
10720
11177
|
}
|
|
10721
11178
|
if (definition.name === "submit_feedback") {
|
|
10722
|
-
return submitFeedback(payload);
|
|
11179
|
+
return submitFeedback(payload, access);
|
|
10723
11180
|
}
|
|
10724
11181
|
if (definition.name === "submit_remembrance") {
|
|
10725
|
-
return submitRemembrance(payload);
|
|
11182
|
+
return submitRemembrance(payload, access);
|
|
10726
11183
|
}
|
|
10727
11184
|
const localConfigurationError = remoteConfigurationError(
|
|
10728
|
-
|
|
10729
|
-
|
|
10730
|
-
|
|
11185
|
+
access.configuration.source,
|
|
11186
|
+
access.credential.source,
|
|
11187
|
+
access.configuration.issue
|
|
10731
11188
|
);
|
|
10732
|
-
const result = definition.name === "get_connection_status" && localConfigurationError ? { ok: false, status: 0, error: localConfigurationError } : await callRemembrance(definition, payload);
|
|
11189
|
+
const result = definition.name === "get_connection_status" && localConfigurationError ? { ok: false, status: 0, error: localConfigurationError } : await callRemembrance(definition, payload, { access });
|
|
10733
11190
|
if (definition.name === "get_connection_status") {
|
|
10734
11191
|
const status = localConnectionStatus(result, {
|
|
10735
|
-
apiBase,
|
|
10736
|
-
apiUrlSource:
|
|
11192
|
+
apiBase: access.configuration.baseUrl,
|
|
11193
|
+
apiUrlSource: access.configuration.source,
|
|
10737
11194
|
pluginVersion: SERVER_VERSION
|
|
10738
11195
|
});
|
|
10739
11196
|
if (!localConfigurationError) {
|
|
10740
|
-
void reportDegradedClientHealth(status).catch(() => void 0);
|
|
11197
|
+
void reportDegradedClientHealth(status, access).catch(() => void 0);
|
|
10741
11198
|
}
|
|
10742
11199
|
return status;
|
|
10743
11200
|
}
|
|
10744
11201
|
return result;
|
|
10745
11202
|
}
|
|
10746
11203
|
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
11204
|
if (apiUrlSource === "unusable_environment") {
|
|
10751
11205
|
return apiConfigurationErrorMessage(apiUrlIssue, "REMEMBRANCE_API_URL");
|
|
10752
11206
|
}
|
|
11207
|
+
if (credentialSource === "unusable_destination_binding") {
|
|
11208
|
+
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.";
|
|
11209
|
+
}
|
|
10753
11210
|
if (apiUrlSource === "unusable_shared_config" || credentialSource === "unusable_shared_config") {
|
|
10754
11211
|
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
11212
|
}
|
|
@@ -10829,7 +11286,7 @@ function isSafePluginVersion(value) {
|
|
|
10829
11286
|
function isSafeHostVersion(value) {
|
|
10830
11287
|
return value === null || typeof value === "string" && value.length <= 64 && /^[0-9A-Za-z._+ -]*$/.test(value.trim());
|
|
10831
11288
|
}
|
|
10832
|
-
async function reportDegradedClientHealth(status) {
|
|
11289
|
+
async function reportDegradedClientHealth(status, access = resolveApiAccess()) {
|
|
10833
11290
|
if (/^(0|false|no)$/i.test(process.env.REMEMBRANCE_HEALTH_REPORTING ?? "")) {
|
|
10834
11291
|
return;
|
|
10835
11292
|
}
|
|
@@ -10838,25 +11295,30 @@ async function reportDegradedClientHealth(status) {
|
|
|
10838
11295
|
const controller = new AbortController();
|
|
10839
11296
|
const timeout = setTimeout(() => controller.abort(), 1500);
|
|
10840
11297
|
try {
|
|
10841
|
-
const apiKey =
|
|
10842
|
-
await fetch(
|
|
10843
|
-
|
|
10844
|
-
|
|
10845
|
-
|
|
10846
|
-
|
|
10847
|
-
|
|
10848
|
-
|
|
10849
|
-
|
|
10850
|
-
|
|
10851
|
-
|
|
11298
|
+
const apiKey = access.credential.apiKey;
|
|
11299
|
+
await fetch(
|
|
11300
|
+
`${access.configuration.baseUrl}/api/v1/agent/client-health-reports`,
|
|
11301
|
+
{
|
|
11302
|
+
method: "POST",
|
|
11303
|
+
headers: {
|
|
11304
|
+
"Content-Type": "application/json",
|
|
11305
|
+
...apiKey ? { "X-Remembrance-API-Key": apiKey } : {},
|
|
11306
|
+
"User-Agent": `@remembrance-ai/mcp-server/${SERVER_VERSION}`
|
|
11307
|
+
},
|
|
11308
|
+
body: JSON.stringify(report),
|
|
11309
|
+
signal: controller.signal
|
|
11310
|
+
}
|
|
11311
|
+
);
|
|
10852
11312
|
} finally {
|
|
10853
11313
|
clearTimeout(timeout);
|
|
10854
11314
|
}
|
|
10855
11315
|
}
|
|
10856
|
-
async function submitRemembrance(payload) {
|
|
11316
|
+
async function submitRemembrance(payload, access) {
|
|
10857
11317
|
const { verified_attestation: verifiedAttestation, ...request } = payload;
|
|
10858
11318
|
if (!verifiedAttestation) {
|
|
10859
|
-
return callRemembrance(mustFindTool("submit_remembrance"), request
|
|
11319
|
+
return callRemembrance(mustFindTool("submit_remembrance"), request, {
|
|
11320
|
+
access
|
|
11321
|
+
});
|
|
10860
11322
|
}
|
|
10861
11323
|
const skillSlug = request.skill?.slug;
|
|
10862
11324
|
if (!skillSlug) {
|
|
@@ -10872,20 +11334,27 @@ async function submitRemembrance(payload) {
|
|
|
10872
11334
|
};
|
|
10873
11335
|
const attestation = await signedRemembranceAttestation(
|
|
10874
11336
|
remembrancePayload,
|
|
10875
|
-
skillSlug
|
|
11337
|
+
skillSlug,
|
|
11338
|
+
access
|
|
11339
|
+
);
|
|
11340
|
+
return callRemembrance(
|
|
11341
|
+
mustFindTool("submit_remembrance"),
|
|
11342
|
+
{
|
|
11343
|
+
...remembrancePayload,
|
|
11344
|
+
evidence: {
|
|
11345
|
+
...evidence,
|
|
11346
|
+
attestation
|
|
11347
|
+
}
|
|
11348
|
+
},
|
|
11349
|
+
{ access }
|
|
10876
11350
|
);
|
|
10877
|
-
return callRemembrance(mustFindTool("submit_remembrance"), {
|
|
10878
|
-
...remembrancePayload,
|
|
10879
|
-
evidence: {
|
|
10880
|
-
...evidence,
|
|
10881
|
-
attestation
|
|
10882
|
-
}
|
|
10883
|
-
});
|
|
10884
11351
|
}
|
|
10885
|
-
async function submitFeedback(payload) {
|
|
11352
|
+
async function submitFeedback(payload, access) {
|
|
10886
11353
|
const { verified_attestation: verifiedAttestation, ...request } = payload;
|
|
10887
11354
|
if (!verifiedAttestation) {
|
|
10888
|
-
return callRemembrance(mustFindTool("submit_feedback"), request
|
|
11355
|
+
return callRemembrance(mustFindTool("submit_feedback"), request, {
|
|
11356
|
+
access
|
|
11357
|
+
});
|
|
10889
11358
|
}
|
|
10890
11359
|
const identity = await ensureSigningIdentity();
|
|
10891
11360
|
const agent = request.agent ?? {
|
|
@@ -10920,18 +11389,23 @@ async function submitFeedback(payload) {
|
|
|
10920
11389
|
};
|
|
10921
11390
|
const attestation = await signedRemembranceAttestation(
|
|
10922
11391
|
remembrancePayload,
|
|
10923
|
-
request.skill_slug
|
|
11392
|
+
request.skill_slug,
|
|
11393
|
+
access
|
|
11394
|
+
);
|
|
11395
|
+
return callRemembrance(
|
|
11396
|
+
mustFindTool("submit_feedback"),
|
|
11397
|
+
{
|
|
11398
|
+
...request,
|
|
11399
|
+
agent,
|
|
11400
|
+
evidence: {
|
|
11401
|
+
...evidence,
|
|
11402
|
+
attestation
|
|
11403
|
+
}
|
|
11404
|
+
},
|
|
11405
|
+
{ access }
|
|
10924
11406
|
);
|
|
10925
|
-
return callRemembrance(mustFindTool("submit_feedback"), {
|
|
10926
|
-
...request,
|
|
10927
|
-
agent,
|
|
10928
|
-
evidence: {
|
|
10929
|
-
...evidence,
|
|
10930
|
-
attestation
|
|
10931
|
-
}
|
|
10932
|
-
});
|
|
10933
11407
|
}
|
|
10934
|
-
async function signedRemembranceAttestation(remembrancePayload, skillSlug) {
|
|
11408
|
+
async function signedRemembranceAttestation(remembrancePayload, skillSlug, access) {
|
|
10935
11409
|
const identity = await ensureSigningIdentity();
|
|
10936
11410
|
const evidenceHash = attestationEvidenceHashForRemembrance(remembrancePayload);
|
|
10937
11411
|
const challenge = await callRemembrance(
|
|
@@ -10943,7 +11417,8 @@ async function signedRemembranceAttestation(remembrancePayload, skillSlug) {
|
|
|
10943
11417
|
subject: identity.subject,
|
|
10944
11418
|
skill_slug: skillSlug,
|
|
10945
11419
|
evidence_hash: evidenceHash
|
|
10946
|
-
}
|
|
11420
|
+
},
|
|
11421
|
+
{ access }
|
|
10947
11422
|
);
|
|
10948
11423
|
if (challenge.ok === false) {
|
|
10949
11424
|
throw new Error("Unable to create Remembrance attestation challenge.");
|
|
@@ -10970,7 +11445,7 @@ async function signedRemembranceAttestation(remembrancePayload, skillSlug) {
|
|
|
10970
11445
|
signature
|
|
10971
11446
|
};
|
|
10972
11447
|
}
|
|
10973
|
-
async function bootstrapAgentIdentity(args) {
|
|
11448
|
+
async function bootstrapAgentIdentity(args, access = resolveApiAccess()) {
|
|
10974
11449
|
const keyPath = identityPath(args.key_path);
|
|
10975
11450
|
let reusedExistingIdentity = !args.force_rotate && existsSync3(keyPath);
|
|
10976
11451
|
let identity = reusedExistingIdentity ? await readIdentity(keyPath) : null;
|
|
@@ -10993,10 +11468,14 @@ async function bootstrapAgentIdentity(args) {
|
|
|
10993
11468
|
mismatchedFields.push("key_id");
|
|
10994
11469
|
}
|
|
10995
11470
|
}
|
|
11471
|
+
const ownerBinding = await agentRegistrationOwnerBinding(access).catch(
|
|
11472
|
+
() => null
|
|
11473
|
+
);
|
|
10996
11474
|
const signedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
10997
11475
|
const proofPayload = buildAgentKeyRegistrationSigningPayload({
|
|
10998
11476
|
provider: identity.provider,
|
|
10999
11477
|
keyId: identity.key_id,
|
|
11478
|
+
ownerBinding,
|
|
11000
11479
|
publicKey: identity.public_key,
|
|
11001
11480
|
subject: identity.subject,
|
|
11002
11481
|
signedAt
|
|
@@ -11015,13 +11494,15 @@ async function bootstrapAgentIdentity(args) {
|
|
|
11015
11494
|
subject: identity.subject,
|
|
11016
11495
|
proof: {
|
|
11017
11496
|
algorithm: "ed25519",
|
|
11497
|
+
...ownerBinding ? { owner_binding: ownerBinding } : {},
|
|
11018
11498
|
signed_at: signedAt,
|
|
11019
11499
|
signature: proofSignature
|
|
11020
11500
|
},
|
|
11021
11501
|
metadata: {
|
|
11022
11502
|
registered_by: "@remembrance-ai/mcp-server"
|
|
11023
11503
|
}
|
|
11024
|
-
}
|
|
11504
|
+
},
|
|
11505
|
+
{ access }
|
|
11025
11506
|
);
|
|
11026
11507
|
if (registration.ok !== true) {
|
|
11027
11508
|
throw new McpPublicError(
|
|
@@ -11039,6 +11520,33 @@ async function bootstrapAgentIdentity(args) {
|
|
|
11039
11520
|
registration
|
|
11040
11521
|
};
|
|
11041
11522
|
}
|
|
11523
|
+
async function agentRegistrationOwnerBinding(access) {
|
|
11524
|
+
const credential = access.credential;
|
|
11525
|
+
const configurationError = remoteConfigurationError(
|
|
11526
|
+
access.configuration.source,
|
|
11527
|
+
credential.source,
|
|
11528
|
+
access.configuration.issue
|
|
11529
|
+
);
|
|
11530
|
+
if (configurationError) throw new McpPublicError(configurationError);
|
|
11531
|
+
const headers = {
|
|
11532
|
+
accept: "application/json",
|
|
11533
|
+
"user-agent": `@remembrance-ai/mcp-server/${SERVER_VERSION}`
|
|
11534
|
+
};
|
|
11535
|
+
if (credential.apiKey) {
|
|
11536
|
+
headers["x-remembrance-api-key"] = credential.apiKey;
|
|
11537
|
+
}
|
|
11538
|
+
const response = await fetch(
|
|
11539
|
+
`${access.configuration.baseUrl}/api/v1/agent/keys/register`,
|
|
11540
|
+
{
|
|
11541
|
+
method: "GET",
|
|
11542
|
+
headers
|
|
11543
|
+
}
|
|
11544
|
+
);
|
|
11545
|
+
if (!response.ok) return null;
|
|
11546
|
+
const payload = await response.json();
|
|
11547
|
+
const candidate = String(payload.owner_binding ?? "").trim();
|
|
11548
|
+
return /^areg_[A-Za-z0-9_-]{24,120}$/.test(candidate) ? candidate : null;
|
|
11549
|
+
}
|
|
11042
11550
|
async function createAndPersistIdentity(args, keyPath) {
|
|
11043
11551
|
const { privateKey, publicKey } = generateKeyPairSync("ed25519");
|
|
11044
11552
|
const publicKeyPem = String(
|
|
@@ -11096,44 +11604,56 @@ async function readIdentity(path = identityPath()) {
|
|
|
11096
11604
|
}
|
|
11097
11605
|
var automaticIdentityBootstrap = null;
|
|
11098
11606
|
async function ensureSigningIdentity() {
|
|
11099
|
-
|
|
11100
|
-
|
|
11101
|
-
|
|
11607
|
+
const keyPath = identityPath();
|
|
11608
|
+
if (automaticIdentityBootstrap?.keyPath === keyPath) {
|
|
11609
|
+
return automaticIdentityBootstrap.promise;
|
|
11610
|
+
}
|
|
11611
|
+
const existing = await readIdentity(keyPath);
|
|
11612
|
+
if (automaticIdentityBootstrap?.keyPath === keyPath) {
|
|
11613
|
+
return automaticIdentityBootstrap.promise;
|
|
11614
|
+
}
|
|
11102
11615
|
if (existing) return existing;
|
|
11103
|
-
|
|
11104
|
-
|
|
11105
|
-
|
|
11106
|
-
|
|
11107
|
-
|
|
11108
|
-
|
|
11109
|
-
|
|
11110
|
-
|
|
11111
|
-
|
|
11112
|
-
|
|
11113
|
-
|
|
11616
|
+
const promise = (async () => {
|
|
11617
|
+
await bootstrapAgentIdentity({ key_path: keyPath });
|
|
11618
|
+
const created = await readIdentity(keyPath);
|
|
11619
|
+
if (!created) {
|
|
11620
|
+
throw new McpPublicError(
|
|
11621
|
+
"Remembrance could not initialize the local signing identity. Run bootstrap_agent_identity and retry."
|
|
11622
|
+
);
|
|
11623
|
+
}
|
|
11624
|
+
return created;
|
|
11625
|
+
})();
|
|
11626
|
+
automaticIdentityBootstrap = { keyPath, promise };
|
|
11627
|
+
const clearBootstrap = () => {
|
|
11628
|
+
if (automaticIdentityBootstrap?.promise === promise) {
|
|
11114
11629
|
automaticIdentityBootstrap = null;
|
|
11115
|
-
}
|
|
11116
|
-
}
|
|
11117
|
-
|
|
11630
|
+
}
|
|
11631
|
+
};
|
|
11632
|
+
void promise.then(clearBootstrap, clearBootstrap);
|
|
11633
|
+
return promise;
|
|
11118
11634
|
}
|
|
11119
11635
|
async function callRemembrance(definition, rawArguments, options = {}) {
|
|
11636
|
+
const access = options.access ?? resolveApiAccess();
|
|
11120
11637
|
const parsed = definition.schema.parse(rawArguments ?? {});
|
|
11638
|
+
const projectKey = definition.name === "query_skills" || definition.name === "invoke_skill" ? await localProjectKey().catch(() => null) : null;
|
|
11639
|
+
const callerClientContext = parsed.client_context ?? {};
|
|
11121
11640
|
const payload = definition.name === "query_skills" || definition.name === "invoke_skill" ? {
|
|
11122
11641
|
...parsed,
|
|
11123
11642
|
client_context: {
|
|
11124
|
-
...
|
|
11125
|
-
surface: "mcp"
|
|
11643
|
+
...callerClientContext,
|
|
11644
|
+
surface: "mcp",
|
|
11645
|
+
...callerClientContext.project_key || !projectKey ? {} : { project_key: projectKey }
|
|
11126
11646
|
}
|
|
11127
11647
|
} : parsed;
|
|
11128
11648
|
const headers = {
|
|
11129
11649
|
"content-type": "application/json",
|
|
11130
11650
|
"user-agent": `@remembrance-ai/mcp-server/${SERVER_VERSION}`
|
|
11131
11651
|
};
|
|
11132
|
-
const credential =
|
|
11652
|
+
const credential = access.credential;
|
|
11133
11653
|
const configurationError = remoteConfigurationError(
|
|
11134
|
-
|
|
11654
|
+
access.configuration.source,
|
|
11135
11655
|
credential.source,
|
|
11136
|
-
|
|
11656
|
+
access.configuration.issue
|
|
11137
11657
|
);
|
|
11138
11658
|
if (configurationError && definition.name !== "get_connection_status") {
|
|
11139
11659
|
throw new McpPublicError(configurationError);
|
|
@@ -11143,11 +11663,21 @@ async function callRemembrance(definition, rawArguments, options = {}) {
|
|
|
11143
11663
|
headers["x-remembrance-api-key"] = apiKey;
|
|
11144
11664
|
}
|
|
11145
11665
|
if (!options.skipEconomicsSession && definition.name !== "register_agent_key" && definition.name !== "request_attestation_challenge" && definition.name !== "get_connection_status") {
|
|
11146
|
-
|
|
11147
|
-
|
|
11148
|
-
|
|
11149
|
-
|
|
11150
|
-
|
|
11666
|
+
let principalSession = currentPrincipalSessionToken(access);
|
|
11667
|
+
if (!principalSession && PRINCIPAL_SESSION_AWAITED_TOOLS.has(definition.name)) {
|
|
11668
|
+
principalSession = await ensurePrincipalSessionToken(access).catch(
|
|
11669
|
+
() => null
|
|
11670
|
+
);
|
|
11671
|
+
}
|
|
11672
|
+
if (!principalSession && PRINCIPAL_SESSION_REQUIRED_TOOLS.has(definition.name)) {
|
|
11673
|
+
throw new McpPublicError(
|
|
11674
|
+
"This operation requires a verified local installation session. Check the organization key and local TOFU identity, then retry."
|
|
11675
|
+
);
|
|
11676
|
+
}
|
|
11677
|
+
if (principalSession) {
|
|
11678
|
+
headers["x-remembrance-principal-session"] = principalSession;
|
|
11679
|
+
} else if (definition.name === "query_skills" || definition.name === "invoke_skill") {
|
|
11680
|
+
void ensurePrincipalSessionToken(access).catch(() => null);
|
|
11151
11681
|
}
|
|
11152
11682
|
}
|
|
11153
11683
|
const endpoint = endpointFor(definition, payload);
|
|
@@ -11159,25 +11689,48 @@ async function callRemembrance(definition, rawArguments, options = {}) {
|
|
|
11159
11689
|
let response;
|
|
11160
11690
|
let body;
|
|
11161
11691
|
try {
|
|
11162
|
-
|
|
11163
|
-
|
|
11164
|
-
|
|
11165
|
-
|
|
11166
|
-
|
|
11167
|
-
|
|
11168
|
-
|
|
11169
|
-
|
|
11170
|
-
|
|
11171
|
-
|
|
11172
|
-
|
|
11173
|
-
|
|
11174
|
-
|
|
11175
|
-
|
|
11692
|
+
const execute = async () => {
|
|
11693
|
+
const nextResponse = await fetch(
|
|
11694
|
+
`${access.configuration.baseUrl}${endpoint}`,
|
|
11695
|
+
{
|
|
11696
|
+
method: definition.method ?? "POST",
|
|
11697
|
+
headers,
|
|
11698
|
+
body: definition.method === "GET" ? void 0 : JSON.stringify(payload),
|
|
11699
|
+
signal: controller.signal
|
|
11700
|
+
}
|
|
11701
|
+
);
|
|
11702
|
+
let nextBody;
|
|
11703
|
+
try {
|
|
11704
|
+
nextBody = await readBoundedResponseJson(nextResponse);
|
|
11705
|
+
} catch {
|
|
11706
|
+
if (controller.signal.aborted) {
|
|
11707
|
+
throw new DOMException("Request timed out", "AbortError");
|
|
11708
|
+
}
|
|
11709
|
+
nextBody = {
|
|
11710
|
+
error: nextResponse.ok ? "Remembrance API returned an unreadable or oversized response." : "Remembrance API request failed."
|
|
11176
11711
|
};
|
|
11177
11712
|
}
|
|
11178
|
-
body
|
|
11179
|
-
|
|
11180
|
-
|
|
11713
|
+
return { response: nextResponse, body: nextBody };
|
|
11714
|
+
};
|
|
11715
|
+
({ response, body } = await execute());
|
|
11716
|
+
const sentPrincipalSession = headers["x-remembrance-principal-session"] ?? null;
|
|
11717
|
+
const refreshSignaled = response.headers.get("x-remembrance-principal-session-status") === "refresh_required";
|
|
11718
|
+
const authenticationFailed = principalSessionAuthenticationFailed(
|
|
11719
|
+
response,
|
|
11720
|
+
body
|
|
11721
|
+
);
|
|
11722
|
+
if (sentPrincipalSession && refreshSignaled && !authenticationFailed) {
|
|
11723
|
+
cachedPrincipalSession = null;
|
|
11724
|
+
void ensurePrincipalSessionToken(access).catch(() => null);
|
|
11725
|
+
} else if (sentPrincipalSession && authenticationFailed) {
|
|
11726
|
+
cachedPrincipalSession = null;
|
|
11727
|
+
const refreshed = await ensurePrincipalSessionToken(access).catch(
|
|
11728
|
+
() => null
|
|
11729
|
+
);
|
|
11730
|
+
if (refreshed && refreshed !== sentPrincipalSession) {
|
|
11731
|
+
headers["x-remembrance-principal-session"] = refreshed;
|
|
11732
|
+
({ response, body } = await execute());
|
|
11733
|
+
}
|
|
11181
11734
|
}
|
|
11182
11735
|
} catch (error) {
|
|
11183
11736
|
return {
|
|
@@ -11188,7 +11741,10 @@ async function callRemembrance(definition, rawArguments, options = {}) {
|
|
|
11188
11741
|
} finally {
|
|
11189
11742
|
clearTimeout(timeout);
|
|
11190
11743
|
}
|
|
11191
|
-
const verifiedBody = definition.name === "get_value_proof" && response.ok ? await verifySignedValueProofWithKeyRefresh(
|
|
11744
|
+
const verifiedBody = definition.name === "get_value_proof" && response.ok ? await verifySignedValueProofWithKeyRefresh(
|
|
11745
|
+
body,
|
|
11746
|
+
(forceRefresh) => fetchValueProofKeySet(forceRefresh, access)
|
|
11747
|
+
) : body;
|
|
11192
11748
|
return {
|
|
11193
11749
|
ok: response.ok,
|
|
11194
11750
|
status: response.status,
|
|
@@ -11198,27 +11754,52 @@ async function callRemembrance(definition, rawArguments, options = {}) {
|
|
|
11198
11754
|
body: verifiedBody
|
|
11199
11755
|
};
|
|
11200
11756
|
}
|
|
11201
|
-
|
|
11757
|
+
var cachedLocalProjectKey;
|
|
11758
|
+
async function localProjectKey() {
|
|
11759
|
+
const configured = process.env.REMEMBRANCE_PROJECT_KEY?.trim() ?? "";
|
|
11760
|
+
if (/^prj_[A-Za-z0-9_-]{12,120}$/.test(configured)) return configured;
|
|
11761
|
+
const projectPath = (process.env.REMEMBRANCE_PROJECT_PATH ?? process.env.PWD ?? process.cwd()).trim();
|
|
11762
|
+
if (!projectPath) return null;
|
|
11763
|
+
const identity = await ensureSigningIdentity();
|
|
11764
|
+
if (cachedLocalProjectKey?.identityKeyId === identity.key_id && cachedLocalProjectKey.projectPath === projectPath) {
|
|
11765
|
+
return cachedLocalProjectKey.value;
|
|
11766
|
+
}
|
|
11767
|
+
const value = `prj_${createHmac("sha256", identity.private_key).update(`remembrance-project-v1:${projectPath}`, "utf8").digest("base64url").slice(0, 32)}`;
|
|
11768
|
+
cachedLocalProjectKey = {
|
|
11769
|
+
identityKeyId: identity.key_id,
|
|
11770
|
+
projectPath,
|
|
11771
|
+
value
|
|
11772
|
+
};
|
|
11773
|
+
return value;
|
|
11774
|
+
}
|
|
11775
|
+
function principalSessionAuthenticationFailed(response, body) {
|
|
11776
|
+
if (response.status !== 401 && response.status !== 403) return false;
|
|
11777
|
+
if (!isRecord4(body) || typeof body.error !== "string") return false;
|
|
11778
|
+
return /principal session|economics session|installation session/i.test(
|
|
11779
|
+
body.error
|
|
11780
|
+
);
|
|
11781
|
+
}
|
|
11782
|
+
async function runLocalConnectionDoctor(options, execution = "mcp_tool", access = resolveApiAccess()) {
|
|
11202
11783
|
const connectionDefinition = mustFindTool("get_connection_status");
|
|
11203
11784
|
const localConfigurationError = remoteConfigurationError(
|
|
11204
|
-
|
|
11205
|
-
|
|
11206
|
-
|
|
11785
|
+
access.configuration.source,
|
|
11786
|
+
access.credential.source,
|
|
11787
|
+
access.configuration.issue
|
|
11207
11788
|
);
|
|
11208
11789
|
const connectionPromise = localConfigurationError ? Promise.resolve({
|
|
11209
11790
|
ok: false,
|
|
11210
11791
|
status: 0,
|
|
11211
11792
|
error: localConfigurationError
|
|
11212
|
-
}) : callDoctorReadProbe(connectionDefinition, {});
|
|
11213
|
-
const catalogPromise = options.active_read_probe && !localConfigurationError ? callDoctorReadProbe(mustFindTool("list_skills"), { limit: 1 }) : Promise.resolve(null);
|
|
11793
|
+
}) : callDoctorReadProbe(connectionDefinition, {}, access);
|
|
11794
|
+
const catalogPromise = options.active_read_probe && !localConfigurationError ? callDoctorReadProbe(mustFindTool("list_skills"), { limit: 1 }, access) : Promise.resolve(null);
|
|
11214
11795
|
const [connectionResult, catalogResult, clientUpdate] = await Promise.all([
|
|
11215
11796
|
connectionPromise,
|
|
11216
11797
|
catalogPromise,
|
|
11217
|
-
currentClientUpdate()
|
|
11798
|
+
currentClientUpdate(access)
|
|
11218
11799
|
]);
|
|
11219
11800
|
const connectionStatus = localConnectionStatus(connectionResult, {
|
|
11220
|
-
apiBase,
|
|
11221
|
-
apiUrlSource:
|
|
11801
|
+
apiBase: access.configuration.baseUrl,
|
|
11802
|
+
apiUrlSource: access.configuration.source,
|
|
11222
11803
|
pluginVersion: SERVER_VERSION
|
|
11223
11804
|
});
|
|
11224
11805
|
const activeReadProbe = {
|
|
@@ -11244,23 +11825,25 @@ async function runLocalConnectionDoctor(options, execution = "mcp_tool") {
|
|
|
11244
11825
|
client_update: clientUpdate
|
|
11245
11826
|
});
|
|
11246
11827
|
}
|
|
11247
|
-
async function currentClientUpdate() {
|
|
11828
|
+
async function currentClientUpdate(access = resolveApiAccess()) {
|
|
11248
11829
|
const configuredSurface = process.env.REMEMBRANCE_PLUGIN_HOST?.trim().toLowerCase();
|
|
11249
11830
|
const surface = configuredSurface && isPluginHostSurface(configuredSurface) ? configuredSurface : "mcp";
|
|
11250
11831
|
return checkClientUpdate({
|
|
11251
|
-
apiBase,
|
|
11832
|
+
apiBase: access.configuration.baseUrl,
|
|
11252
11833
|
currentVersion: SERVER_VERSION,
|
|
11253
11834
|
surface
|
|
11254
11835
|
});
|
|
11255
11836
|
}
|
|
11256
|
-
async function callDoctorReadProbe(definition, payload) {
|
|
11837
|
+
async function callDoctorReadProbe(definition, payload, access) {
|
|
11257
11838
|
const first = await callRemembrance(definition, payload, {
|
|
11258
11839
|
skipEconomicsSession: true,
|
|
11259
|
-
timeoutMs: DOCTOR_PROBE_TIMEOUT_MS
|
|
11840
|
+
timeoutMs: DOCTOR_PROBE_TIMEOUT_MS,
|
|
11841
|
+
access
|
|
11260
11842
|
});
|
|
11261
11843
|
return isTransientDoctorProbeFailure(first) ? callRemembrance(definition, payload, {
|
|
11262
11844
|
skipEconomicsSession: true,
|
|
11263
|
-
timeoutMs: DOCTOR_PROBE_TIMEOUT_MS
|
|
11845
|
+
timeoutMs: DOCTOR_PROBE_TIMEOUT_MS,
|
|
11846
|
+
access
|
|
11264
11847
|
}) : first;
|
|
11265
11848
|
}
|
|
11266
11849
|
function isTransientDoctorProbeFailure(result) {
|
|
@@ -11324,16 +11907,16 @@ async function runConnectionDoctorCli(args = process.argv.slice(3)) {
|
|
|
11324
11907
|
);
|
|
11325
11908
|
return report.status === "blocked" ? 1 : 0;
|
|
11326
11909
|
}
|
|
11327
|
-
async function fetchValueProofKeySet(forceRefresh) {
|
|
11910
|
+
async function fetchValueProofKeySet(forceRefresh, access = resolveApiAccess()) {
|
|
11328
11911
|
const now = Date.now();
|
|
11329
|
-
if (!forceRefresh && cachedValueProofKeys && cachedValueProofKeys.expiresAt > now) {
|
|
11912
|
+
if (!forceRefresh && cachedValueProofKeys && cachedValueProofKeys.apiBase === access.configuration.baseUrl && cachedValueProofKeys.expiresAt > now) {
|
|
11330
11913
|
return cachedValueProofKeys.value;
|
|
11331
11914
|
}
|
|
11332
11915
|
const controller = new AbortController();
|
|
11333
11916
|
const timeout = setTimeout(() => controller.abort(), apiTimeoutMs());
|
|
11334
11917
|
try {
|
|
11335
11918
|
const response = await fetch(
|
|
11336
|
-
`${
|
|
11919
|
+
`${access.configuration.baseUrl}/.well-known/remembrance-value-proof-keys.json`,
|
|
11337
11920
|
{
|
|
11338
11921
|
headers: { accept: "application/json" },
|
|
11339
11922
|
signal: controller.signal
|
|
@@ -11347,7 +11930,8 @@ async function fetchValueProofKeySet(forceRefresh) {
|
|
|
11347
11930
|
const value = await readBoundedResponseJson(response);
|
|
11348
11931
|
cachedValueProofKeys = {
|
|
11349
11932
|
value,
|
|
11350
|
-
expiresAt: now + valueProofKeyCacheTtlMs(response.headers)
|
|
11933
|
+
expiresAt: now + valueProofKeyCacheTtlMs(response.headers),
|
|
11934
|
+
apiBase: access.configuration.baseUrl
|
|
11351
11935
|
};
|
|
11352
11936
|
return value;
|
|
11353
11937
|
} finally {
|
|
@@ -11465,18 +12049,38 @@ async function fetchSkillCatalog(input) {
|
|
|
11465
12049
|
}
|
|
11466
12050
|
return skillCatalogResponseSchema.parse(response.body);
|
|
11467
12051
|
}
|
|
11468
|
-
async function
|
|
11469
|
-
|
|
11470
|
-
|
|
12052
|
+
async function ensurePrincipalSessionToken(access = resolveApiAccess()) {
|
|
12053
|
+
if (isUnusableCredentialSource(access.configuration.source) || isUnusableCredentialSource(access.credential.source)) {
|
|
12054
|
+
return null;
|
|
12055
|
+
}
|
|
12056
|
+
const identity = await ensureSigningIdentity();
|
|
11471
12057
|
const identityKey = `${identity.provider}:${identity.key_id}`;
|
|
11472
|
-
|
|
11473
|
-
|
|
12058
|
+
const accessKey = apiAccessKey(access);
|
|
12059
|
+
if (cachedPrincipalSession && cachedPrincipalSession.identityKey === identityKey && cachedPrincipalSession.accessKey === accessKey && cachedPrincipalSession.expiresAt > Date.now() + 6e4) {
|
|
12060
|
+
return cachedPrincipalSession.token;
|
|
11474
12061
|
}
|
|
11475
|
-
const
|
|
12062
|
+
const memberLinkToken = access.memberLinkToken;
|
|
12063
|
+
const challengePayload = {
|
|
11476
12064
|
action: "challenge",
|
|
11477
12065
|
provider: identity.provider,
|
|
11478
|
-
key_id: identity.key_id
|
|
11479
|
-
|
|
12066
|
+
key_id: identity.key_id,
|
|
12067
|
+
runtime_profile: localRuntimeProfile(identity),
|
|
12068
|
+
...memberLinkToken ? { member_link_token: memberLinkToken } : {}
|
|
12069
|
+
};
|
|
12070
|
+
let challengeResponse = await directPrincipalSessionRequest(
|
|
12071
|
+
challengePayload,
|
|
12072
|
+
access
|
|
12073
|
+
);
|
|
12074
|
+
if (!challengeResponse) {
|
|
12075
|
+
await bootstrapAgentIdentity({}, access).catch(() => null);
|
|
12076
|
+
challengeResponse = await directPrincipalSessionRequest(
|
|
12077
|
+
{
|
|
12078
|
+
...challengePayload,
|
|
12079
|
+
member_link_token: void 0
|
|
12080
|
+
},
|
|
12081
|
+
access
|
|
12082
|
+
);
|
|
12083
|
+
}
|
|
11480
12084
|
const challenge = challengeResponse;
|
|
11481
12085
|
if (!challenge.challenge_id || !challenge.signing_payload) return null;
|
|
11482
12086
|
const signature = signPayload(
|
|
@@ -11484,38 +12088,100 @@ async function ensureEconomicsSessionToken() {
|
|
|
11484
12088
|
Buffer.from(challenge.signing_payload),
|
|
11485
12089
|
createPrivateKey2(identity.private_key)
|
|
11486
12090
|
).toString("base64url");
|
|
11487
|
-
const exchangeResponse = await
|
|
11488
|
-
|
|
11489
|
-
|
|
11490
|
-
|
|
11491
|
-
|
|
11492
|
-
|
|
11493
|
-
|
|
12091
|
+
const exchangeResponse = await directPrincipalSessionRequest(
|
|
12092
|
+
{
|
|
12093
|
+
action: "exchange",
|
|
12094
|
+
provider: identity.provider,
|
|
12095
|
+
key_id: identity.key_id,
|
|
12096
|
+
challenge_id: challenge.challenge_id,
|
|
12097
|
+
signature
|
|
12098
|
+
},
|
|
12099
|
+
access
|
|
12100
|
+
);
|
|
11494
12101
|
const exchange = exchangeResponse;
|
|
11495
12102
|
if (!exchange.session_token || !exchange.expires_at) return null;
|
|
11496
|
-
|
|
12103
|
+
cachedPrincipalSession = {
|
|
11497
12104
|
token: exchange.session_token,
|
|
11498
12105
|
expiresAt: new Date(exchange.expires_at).getTime(),
|
|
11499
|
-
identityKey
|
|
12106
|
+
identityKey,
|
|
12107
|
+
accessKey
|
|
11500
12108
|
};
|
|
11501
|
-
return
|
|
12109
|
+
return cachedPrincipalSession.token;
|
|
12110
|
+
}
|
|
12111
|
+
function currentPrincipalSessionToken(access) {
|
|
12112
|
+
if (!cachedPrincipalSession) return null;
|
|
12113
|
+
if (cachedPrincipalSession.accessKey !== apiAccessKey(access) || cachedPrincipalSession.expiresAt <= Date.now() + 6e4) {
|
|
12114
|
+
cachedPrincipalSession = null;
|
|
12115
|
+
return null;
|
|
12116
|
+
}
|
|
12117
|
+
return cachedPrincipalSession.token;
|
|
11502
12118
|
}
|
|
11503
|
-
async function
|
|
12119
|
+
async function directPrincipalSessionRequest(payload, access) {
|
|
11504
12120
|
const controller = new AbortController();
|
|
11505
12121
|
const timeout = setTimeout(() => controller.abort(), apiTimeoutMs());
|
|
11506
12122
|
try {
|
|
11507
|
-
const response = await fetch(
|
|
11508
|
-
|
|
11509
|
-
|
|
11510
|
-
|
|
11511
|
-
|
|
11512
|
-
|
|
12123
|
+
const response = await fetch(
|
|
12124
|
+
`${access.configuration.baseUrl}/api/v1/agent/principal-sessions`,
|
|
12125
|
+
{
|
|
12126
|
+
method: "POST",
|
|
12127
|
+
headers: { "content-type": "application/json" },
|
|
12128
|
+
body: JSON.stringify(payload),
|
|
12129
|
+
signal: controller.signal
|
|
12130
|
+
}
|
|
12131
|
+
);
|
|
11513
12132
|
if (!response.ok) return null;
|
|
11514
12133
|
return response.json();
|
|
11515
12134
|
} finally {
|
|
11516
12135
|
clearTimeout(timeout);
|
|
11517
12136
|
}
|
|
11518
12137
|
}
|
|
12138
|
+
function normalizedMcpClientInfo(value) {
|
|
12139
|
+
if (!isRecord4(value)) return null;
|
|
12140
|
+
const name = boundedLocalProfileField(value.name);
|
|
12141
|
+
if (!name) return null;
|
|
12142
|
+
return {
|
|
12143
|
+
name,
|
|
12144
|
+
version: boundedLocalProfileField(value.version)
|
|
12145
|
+
};
|
|
12146
|
+
}
|
|
12147
|
+
function localRuntimeProfile(identity) {
|
|
12148
|
+
const configuredSurface = String(process.env.REMEMBRANCE_PLUGIN_HOST ?? "").trim().toLowerCase();
|
|
12149
|
+
const runtime = isPluginHostSurface(configuredSurface) ? configuredSurface : "other";
|
|
12150
|
+
const clientName = connectedClientInfo?.name ?? agentHostBySurface(runtime)?.host_name ?? "MCP";
|
|
12151
|
+
const hostSurface = localRuntimeHostSurface(runtime, clientName);
|
|
12152
|
+
const profileDigest = createHash3("sha256").update(
|
|
12153
|
+
[identity.key_id, runtime, hostSurface, clientName.toLowerCase()].join(
|
|
12154
|
+
":"
|
|
12155
|
+
),
|
|
12156
|
+
"utf8"
|
|
12157
|
+
).digest("base64url");
|
|
12158
|
+
return {
|
|
12159
|
+
runtime,
|
|
12160
|
+
surface: "mcp",
|
|
12161
|
+
host_surface: hostSurface,
|
|
12162
|
+
client_name: clientName,
|
|
12163
|
+
client_version: SERVER_VERSION,
|
|
12164
|
+
runtime_version: connectedClientInfo?.version ?? null,
|
|
12165
|
+
profile_key: `rpf_${profileDigest}`
|
|
12166
|
+
};
|
|
12167
|
+
}
|
|
12168
|
+
function localRuntimeHostSurface(runtime, clientName) {
|
|
12169
|
+
const configured = String(process.env.REMEMBRANCE_HOST_SURFACE ?? "").trim().toLowerCase();
|
|
12170
|
+
if (configured === "desktop" || configured === "cli" || configured === "extension" || configured === "gateway" || configured === "unknown") {
|
|
12171
|
+
return configured;
|
|
12172
|
+
}
|
|
12173
|
+
if (/desktop|chatgpt|codex app/i.test(clientName)) return "desktop";
|
|
12174
|
+
if (/cursor|visual studio|vscode/i.test(clientName)) return "extension";
|
|
12175
|
+
if (runtime === "openclaw") return "gateway";
|
|
12176
|
+
if (runtime === "cursor" || runtime === "vs_code") return "extension";
|
|
12177
|
+
if (runtime === "claude_code" || runtime === "opencode") return "cli";
|
|
12178
|
+
return "unknown";
|
|
12179
|
+
}
|
|
12180
|
+
function boundedLocalProfileField(value) {
|
|
12181
|
+
if (typeof value !== "string") return null;
|
|
12182
|
+
const normalized = value.trim().replace(/[\u0000-\u001f\u007f]/g, "");
|
|
12183
|
+
return normalized ? normalized.slice(0, 512) : null;
|
|
12184
|
+
}
|
|
11519
12185
|
function mustFindTool(name) {
|
|
11520
12186
|
const tool2 = tools.find((item) => item.name === name);
|
|
11521
12187
|
if (!tool2) {
|
|
@@ -11577,6 +12243,7 @@ export {
|
|
|
11577
12243
|
readJsonRpcMessages,
|
|
11578
12244
|
remoteConfigurationError,
|
|
11579
12245
|
reportDegradedClientHealth,
|
|
12246
|
+
resetPrincipalSessionCacheForTests,
|
|
11580
12247
|
resetValueProofKeyCacheForTests,
|
|
11581
12248
|
runConnectionDoctorCli,
|
|
11582
12249
|
runLocalConnectionDoctor
|