@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.
@@ -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 economicsSessionChallengeSchema = external_exports.object({
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 economicsSessionExchangeSchema = external_exports.object({
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 economicsSessionRequestSchema = external_exports.discriminatedUnion("action", [
5810
- economicsSessionChallengeSchema,
5811
- economicsSessionExchangeSchema
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.object({
5820
- principal_id: boundedString(MAX_SHORT_TEXT_LENGTH),
5821
- action: external_exports.enum(["deactivate", "reactivate"])
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.17",
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.17",
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
- const apiConfiguration2 = resolveApiConfiguration(env, fileSystem);
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
- return unusableDestinationBinding();
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 { apiKey: "", source: "none" };
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
- if (!isRecord3(parsed)) {
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 unusableSharedConfig();
10360
+ return { present: true, parsed: null, config: {} };
9918
10361
  }
9919
10362
  }
9920
- function resolveApiKey(env = process.env, fileSystem = nodeFileSystem2) {
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 normalized = normalizeApiUrl(environmentUrl, env);
9927
- if (normalized.baseUrl !== null) {
9928
- return { baseUrl: normalized.baseUrl, source: "environment" };
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: normalized.issue
10373
+ issue: normalized2.issue
9934
10374
  };
9935
10375
  }
9936
- const path = remembranceConfigPath(env);
9937
- if (!fileSystem.exists(path)) {
10376
+ if (!shared.present) {
9938
10377
  return { baseUrl: DEFAULT_API_URL, source: "default" };
9939
10378
  }
9940
- try {
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.55" : "0.0.0-dev";
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 cachedEconomicsSession = null;
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
- const clientUpdate = await currentClientUpdate();
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, { uploadBaseUrl: apiBase });
11167
+ return queuePrivateSkillHandoff(payload, {
11168
+ uploadBaseUrl: access.configuration.baseUrl
11169
+ });
10717
11170
  }
10718
11171
  if (definition.name === "run_connection_doctor") {
10719
- return runLocalConnectionDoctor(payload);
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
- apiConfiguration.source,
10729
- resolveApiCredential().source,
10730
- apiConfiguration.issue
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: apiConfiguration.source,
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 = resolveApiKey();
10842
- await fetch(`${apiBase}/api/v1/agent/client-health-reports`, {
10843
- method: "POST",
10844
- headers: {
10845
- "Content-Type": "application/json",
10846
- ...apiKey ? { "X-Remembrance-API-Key": apiKey } : {},
10847
- "User-Agent": `@remembrance-ai/mcp-server/${SERVER_VERSION}`
10848
- },
10849
- body: JSON.stringify(report),
10850
- signal: controller.signal
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
- if (automaticIdentityBootstrap) return automaticIdentityBootstrap;
11100
- const existing = await readIdentity();
11101
- if (automaticIdentityBootstrap) return automaticIdentityBootstrap;
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
- if (!automaticIdentityBootstrap) {
11104
- automaticIdentityBootstrap = (async () => {
11105
- await bootstrapAgentIdentity({});
11106
- const created = await readIdentity();
11107
- if (!created) {
11108
- throw new McpPublicError(
11109
- "Remembrance could not initialize the local signing identity. Run bootstrap_agent_identity and retry."
11110
- );
11111
- }
11112
- return created;
11113
- })().finally(() => {
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
- return automaticIdentityBootstrap;
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
- ...parsed.client_context ?? {},
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 = resolveApiCredential();
11652
+ const credential = access.credential;
11133
11653
  const configurationError = remoteConfigurationError(
11134
- apiConfiguration.source,
11654
+ access.configuration.source,
11135
11655
  credential.source,
11136
- apiConfiguration.issue
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
- const economicsSession = await ensureEconomicsSessionToken().catch(
11147
- () => null
11148
- );
11149
- if (economicsSession) {
11150
- headers["x-remembrance-economics-session"] = economicsSession;
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
- response = await fetch(`${apiBase}${endpoint}`, {
11163
- method: definition.method ?? "POST",
11164
- headers,
11165
- body: definition.method === "GET" ? void 0 : JSON.stringify(payload),
11166
- signal: controller.signal
11167
- });
11168
- try {
11169
- body = await readBoundedResponseJson(response);
11170
- } catch {
11171
- if (controller.signal.aborted) {
11172
- return {
11173
- ok: false,
11174
- status: 0,
11175
- error: "Remembrance API request timed out."
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
- error: response.ok ? "Remembrance API returned an unreadable or oversized response." : "Remembrance API request failed."
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(body, fetchValueProofKeySet) : body;
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
- async function runLocalConnectionDoctor(options, execution = "mcp_tool") {
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
- apiConfiguration.source,
11205
- resolveApiCredential().source,
11206
- apiConfiguration.issue
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: apiConfiguration.source,
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
- `${apiBase}/.well-known/remembrance-value-proof-keys.json`,
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 ensureEconomicsSessionToken() {
11469
- const identity = await readIdentity();
11470
- if (!identity) return null;
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
- if (cachedEconomicsSession && cachedEconomicsSession.identityKey === identityKey && cachedEconomicsSession.expiresAt > Date.now() + 6e4) {
11473
- return cachedEconomicsSession.token;
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 challengeResponse = await directEconomicsSessionRequest({
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 directEconomicsSessionRequest({
11488
- action: "exchange",
11489
- provider: identity.provider,
11490
- key_id: identity.key_id,
11491
- challenge_id: challenge.challenge_id,
11492
- signature
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
- cachedEconomicsSession = {
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 cachedEconomicsSession.token;
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 directEconomicsSessionRequest(payload) {
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(`${apiBase}/api/v1/agent/economics/session`, {
11508
- method: "POST",
11509
- headers: { "content-type": "application/json" },
11510
- body: JSON.stringify(payload),
11511
- signal: controller.signal
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