dsh-mnemon-strategy-default-three-tier 0.5.11 → 0.5.12

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.
Files changed (3) hide show
  1. package/README.md +13 -0
  2. package/lib/index.js +27 -3
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -69,6 +69,19 @@ the same role, the original ambiguity check applies rather than silently
69
69
  choosing a Source. Projection limits are not token limits or delta injection;
70
70
  capture guidance is not a background worker or a guarantee of model behavior.
71
71
 
72
+ ## Sources with another role
73
+
74
+ An installed Source whose role is not `working-context`, `narrative` or
75
+ `durable-evidence` is not a failure and is not admitted either: the strategy
76
+ keeps composing the three tiers and returns one `unsupported-source-role`
77
+ entry per such Source in `ViewSpec.diagnostics`. The Host validates those
78
+ entries and merges them into `view.diagnostics`, so the turn's
79
+ `MNEMON VIEW AVAILABILITY` line and the View inspection name the Source and its
80
+ role. A diagnostic is an explanation, never authority: the Source still gets no
81
+ projection, Route or ActionOffer. Select the general Strategy to compose it;
82
+ an explicit `selection` still rejects unsupported roles. An unavailable Source keeps
83
+ the Host's own diagnostic instead.
84
+
72
85
  Its own directory is a standalone project: install its declared dependencies,
73
86
  then run `pnpm verify`. When developing against an unreleased Mnemon, install a
74
87
  packed `dsh-mnemon` artifact as the peer instead of linking its source tree.
package/lib/index.js CHANGED
@@ -510,11 +510,30 @@ const VIEW_ROLES = [
510
510
  "narrative",
511
511
  "durable-evidence"
512
512
  ];
513
+ /** The Host accepts at most 32 ViewSpec diagnostics; the summary keeps us inside it. */
514
+ const MAX_DIAGNOSTICS = 32;
513
515
  function soleSource(sources, role) {
514
516
  const matches = sources.filter((source) => source.role === role && source.availability !== "unavailable").sort((left, right) => left.sourceInstanceKey.localeCompare(right.sourceInstanceKey));
515
517
  if (matches.length > 1) throw new Error(`default-three-tier View Strategy found ambiguous ${role} Sources; select an explicit Strategy`);
516
518
  return matches[0];
517
519
  }
520
+ /**
521
+ * Why an installed Source stays out of this View (#504). A custom role is not a
522
+ * failure, so it is explained instead of silently dropped; an unavailable Source
523
+ * already carries the Host's own diagnostic. Sorted by instance key so repeated
524
+ * composition stays deterministic.
525
+ */
526
+ function unsupportedRoleDiagnostics(sources) {
527
+ const unsupported = sources.filter((source) => source.availability !== "unavailable" && !VIEW_ROLES.includes(source.role)).sort((left, right) => left.sourceInstanceKey.localeCompare(right.sourceInstanceKey));
528
+ const reported = unsupported.length <= MAX_DIAGNOSTICS ? unsupported : unsupported.slice(0, 31);
529
+ return [...reported.map((source) => ({
530
+ code: "unsupported-source-role",
531
+ message: `Installed Source ${source.sourceInstanceKey} declares the role "${source.role}", which the default-three-tier Strategy does not admit, so it contributes no projection, Routes or Actions to this View. Use the general Strategy to compose it.`
532
+ })), ...reported.length === unsupported.length ? [] : [{
533
+ code: "unsupported-source-role",
534
+ message: `${unsupported.length - reported.length} more installed Sources declare a role the default-three-tier Strategy does not admit.`
535
+ }]];
536
+ }
518
537
  /** Pure View composition; no dependency on any Source implementation. */
519
538
  const DEFAULT_THREE_TIER_VIEW_STRATEGY = defineMemoryStrategy({
520
539
  createTurn: createThreeTierTurn,
@@ -536,7 +555,12 @@ const DEFAULT_THREE_TIER_VIEW_STRATEGY = defineMemoryStrategy({
536
555
  workflows: ["idle-review", "runtime-capacity"]
537
556
  },
538
557
  compose(request, sources, contributions = []) {
539
- if (contributions.length === 0) return composeThreeTier(request, sources);
558
+ const diagnostics = unsupportedRoleDiagnostics(sources);
559
+ const explained = (spec) => diagnostics.length === 0 ? spec : {
560
+ ...spec,
561
+ diagnostics
562
+ };
563
+ if (contributions.length === 0) return explained(composeThreeTier(request, sources));
540
564
  const policies = threeTierContributions(contributions);
541
565
  const boundedRequest = policies.projection === void 0 ? request : {
542
566
  ...request,
@@ -545,7 +569,7 @@ const DEFAULT_THREE_TIER_VIEW_STRATEGY = defineMemoryStrategy({
545
569
  maxProjectionCharacters: Math.min(request.budget.maxProjectionCharacters, policies.projection.maxProjectionCharacters)
546
570
  }
547
571
  };
548
- const spec = policies.selection === void 0 ? composeThreeTier(boundedRequest, sources) : composeSelected(boundedRequest, sources, policies.selection);
572
+ const spec = explained(policies.selection === void 0 ? composeThreeTier(boundedRequest, sources) : composeSelected(boundedRequest, sources, policies.selection));
549
573
  if (policies.capture !== void 0) {
550
574
  const targets = spec.sources.filter((selected) => (policies.capture.sourceKeys === void 0 || policies.capture.sourceKeys.includes(selected.sourceInstanceKey)) && sources.some((source) => source.sourceInstanceKey === selected.sourceInstanceKey && source.actions.some((action) => selected.actionIds?.includes(action.id) && policies.capture.actionIds.includes(action.id) && action.authority === void 0 && ["write", "maintain"].includes(action.capability))));
551
575
  if (targets.length > 0) {
@@ -627,7 +651,7 @@ function composeSelected(request, sources, selection) {
627
651
  const selected = selection.sourceKeys.map((key) => {
628
652
  const source = sources.find((source) => source.sourceInstanceKey === key);
629
653
  if (source === void 0) throw new Error(`scoped three-tier Source is not installed: ${key}`);
630
- if (!VIEW_ROLES.includes(source.role)) throw new Error(`unsupported scoped three-tier Source role: ${source.role}`);
654
+ if (!VIEW_ROLES.includes(source.role)) throw new Error(`unsupported scoped three-tier Source role: ${source.role} (${source.sourceInstanceKey}); default-three-tier admits ${VIEW_ROLES.join(", ")} only, so remove it from the selection or select the general Strategy`);
631
655
  return source;
632
656
  }).filter((source) => source.availability !== "unavailable");
633
657
  const projected = selected.filter((source) => source.capabilities.includes("project"));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-mnemon-strategy-default-three-tier",
3
- "version": "0.5.11",
3
+ "version": "0.5.12",
4
4
  "description": "Explicit three-tier View Strategy for Composable View Memory.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -36,7 +36,7 @@
36
36
  },
37
37
  "devDependencies": {
38
38
  "@types/node": "^22.0.0",
39
- "dsh-mnemon": "^0.5.35",
39
+ "dsh-mnemon": "^0.5.40",
40
40
  "tsdown": "^0.22.0",
41
41
  "typescript": "^5.9.0",
42
42
  "vitest": "^3.2.0"