@d3ara1n/pi-model-roles 0.2.1 → 0.3.0

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 CHANGED
@@ -100,6 +100,11 @@ if (resolved.model) {
100
100
 
101
101
  // Reverse lookup
102
102
  roles.findRoleByModel("anthropic/claude-opus-4"); // "heavy"
103
+
104
+ // "Which role is the currently-active model?" — recognizes the default role
105
+ // even when all roles are model=null (the common case), so callers (e.g.
106
+ // pi-scout's router) have a real baseline instead of "unknown".
107
+ roles.getCurrentRole("anthropic/claude-sonnet-4");
103
108
  ```
104
109
 
105
110
  ## License
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@d3ara1n/pi-model-roles",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
5
  "description": "Model role configuration library for pi extensions — defines named model roles and resolves them to Model instances",
6
6
  "main": "src/index.ts",
package/src/api.ts CHANGED
@@ -118,6 +118,21 @@ export function initModelRolesAPI(modelRegistry: any, currentModel: any, cwd?: s
118
118
  }
119
119
  return undefined;
120
120
  },
121
+
122
+ getCurrentRole(modelId: string): string | undefined {
123
+ const roles = getConfig().roles;
124
+ // 1. Exact match wins: a role explicitly bound to this model.
125
+ const exact = Object.entries(roles).find(([, c]) => c.model === modelId)?.[0];
126
+ if (exact) return exact;
127
+ // 2. Default role — when model=null it transparently uses the current
128
+ // model, so it is the meaningful base in the all-null config.
129
+ const defaultName = getConfig().defaultRole ?? "default";
130
+ const defaultConfig = roles[defaultName];
131
+ if (defaultConfig && !defaultConfig.model) return defaultName;
132
+ // 3. First model=null role (reached only when default is bound elsewhere).
133
+ const nullRole = Object.entries(roles).find(([, c]) => !c.model)?.[0];
134
+ return nullRole;
135
+ },
121
136
  };
122
137
 
123
138
  // Store on globalThis — survives module identity mismatches
package/src/types.ts CHANGED
@@ -69,9 +69,30 @@ export interface ModelRolesAPI {
69
69
  /** Get all non-hidden roles (for displaying to users). */
70
70
  getVisibleRoles(): Record<string, RoleConfig>;
71
71
  /**
72
- * Given a model identifier (e.g. "anthropic/claude-sonnet-4"),
73
- * find the first role name that uses that model.
74
- * Skips roles with model=null.
72
+ * Given a model identifier (e.g. "anthropic/claude-sonnet-4"), find the first
73
+ * role name that is explicitly bound to that model. Skips roles with
74
+ * model=null (they have no declared model to match).
75
+ *
76
+ * This is a pure declared-binding lookup — it does NOT infer "the current
77
+ * role". For that, use {@link getCurrentRole}.
78
+ *
79
+ * Returns undefined if no role declares this model.
75
80
  */
76
81
  findRoleByModel(modelId: string): string | undefined;
82
+
83
+ /**
84
+ * Identify which role the currently-active model belongs to. Resolution order:
85
+ * 1. Exact match — a role explicitly bound to `modelId`
86
+ * ({@link findRoleByModel}).
87
+ * 2. The configured default role — if it is model=null, the current model
88
+ * is transparently "the default role's model", so default is the
89
+ * meaningful base. This covers the common config where ALL roles are
90
+ * model=null (use current model): without this step, whichever null
91
+ * role iterates first would win — often a hidden utility role.
92
+ * 3. The first model=null role found (it too transparently uses the current
93
+ * model); only reached when the default role is explicitly bound to a
94
+ * different model.
95
+ * Returns undefined only if no role matches by any rule.
96
+ */
97
+ getCurrentRole(modelId: string): string | undefined;
77
98
  }