auto-model-router 0.6.2 → 0.6.3

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.
@@ -26,16 +26,21 @@ jobs:
26
26
  - name: Install dependencies
27
27
  run: npm install
28
28
 
29
- - name: Check if version already published
29
+ # Publish only when the tagged version is NEWER than npm's latest. A tag that
30
+ # reaches GitHub late (git push --tags after the fact) must never publish an
31
+ # old version as a fresh release.
32
+ - name: Check if version is newer than the published one
30
33
  id: version
31
34
  run: |
32
35
  LOCAL=$(node -p "require('./package.json').version")
33
36
  PUBLISHED=$(npm view auto-model-router version 2>/dev/null || echo "0.0.0")
34
37
  echo "local=$LOCAL published=$PUBLISHED"
35
- if [ "$LOCAL" != "$PUBLISHED" ]; then
38
+ NEWEST=$(printf '%s\n' "$PUBLISHED" "$LOCAL" | sort -V | tail -1)
39
+ if [ "$LOCAL" != "$PUBLISHED" ] && [ "$NEWEST" = "$LOCAL" ]; then
36
40
  echo "changed=true" >> "$GITHUB_OUTPUT"
37
41
  else
38
42
  echo "changed=false" >> "$GITHUB_OUTPUT"
43
+ echo "::warning::$LOCAL is not newer than the published $PUBLISHED; nothing published"
39
44
  fi
40
45
 
41
46
  - name: Publish to npm
@@ -45,6 +50,7 @@ jobs:
45
50
  NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
46
51
 
47
52
  - name: Create GitHub Release
53
+ if: steps.version.outputs.changed == 'true'
48
54
  uses: softprops/action-gh-release@v2
49
55
  with:
50
56
  generate_release_notes: true
@@ -7,14 +7,14 @@
7
7
  },
8
8
  "metadata": {
9
9
  "description": "auto-model-router: a local cost/complexity-aware model router for Oh My Pi, backed by OpenRouter",
10
- "version": "0.6.2",
10
+ "version": "0.6.3",
11
11
  "pluginRoot": "."
12
12
  },
13
13
  "plugins": [
14
14
  {
15
15
  "name": "auto-model-router",
16
16
  "description": "Local cost/complexity-aware model router for Oh My Pi, backed by OpenRouter. Runs in-process, routes per turn by price and task complexity, with budget caps, mid-stream escalation, and cache-aware hysteresis.",
17
- "version": "0.6.2",
17
+ "version": "0.6.3",
18
18
  "author": {
19
19
  "name": "drewappling",
20
20
  "email": "drewappling@gmail.com"
package/README.md CHANGED
@@ -1267,6 +1267,11 @@ adds the Codex provider and the Aider settings, and prints (or with `--profile`
1267
1267
  the environment lines for Claude Code. `--harness omp,hermes` restricts it; `--dry-run`
1268
1268
  shows the changes. Delete `remote.json` to go back to a local router. (`join` is an alias.)
1269
1269
 
1270
+ In remote mode omp sends `X-Agentdox-Scope` derived from the workspace folder, so one
1271
+ remote router serves every repo on the machine with that repo's shared context. The remote
1272
+ decides what to do with it: a team edition that pins a scope on the member's group
1273
+ overrides it, and one that pins none follows the workspace.
1274
+
1270
1275
  ## Multiple coding harnesses, one router
1271
1276
 
1272
1277
  **One router process for everything.** omp's embed extension binds a private
@@ -63,7 +63,7 @@ export const REMOTE_MODELS: readonly { id: string; name: string }[] = [
63
63
  * the session and subagent tags, and the virtual models. Costs are USD per
64
64
  * million tokens, like the embedded config.
65
65
  */
66
- export function remoteProviderRegistration(remote: RemoteRouter, sessionId: string, subagent: boolean, blend: { inputPerMtok: number; outputPerMtok: number }): {
66
+ export function remoteProviderRegistration(remote: RemoteRouter, sessionId: string, subagent: boolean, blend: { inputPerMtok: number; outputPerMtok: number }, agentdoxScope = ""): {
67
67
  baseUrl: string;
68
68
  api: string;
69
69
  apiKey: string;
@@ -73,6 +73,10 @@ export function remoteProviderRegistration(remote: RemoteRouter, sessionId: stri
73
73
  const headers: Record<string, string> = {};
74
74
  if (sessionId !== "") headers["X-Omp-Session"] = sessionId;
75
75
  if (subagent) headers["X-Omp-Subagent"] = "1";
76
+ // Which project's shared context this workspace draws on. The bridge lives on the remote
77
+ // router, so this is sent whatever the local config says; the remote decides what to do with
78
+ // it (a team that pins a scope for the group overrides it, and one that pins none follows it).
79
+ if (agentdoxScope !== "") headers["X-Agentdox-Scope"] = agentdoxScope;
76
80
  const round = (v: number): number => Math.round(v * 1e4) / 1e4;
77
81
  return {
78
82
  baseUrl: `${remote.url}/v1`,
@@ -32,17 +32,7 @@ import type { RouterConfig } from "../src/config/types.ts";
32
32
 
33
33
  import type { ExtensionAPI } from "@oh-my-pi/pi-coding-agent";
34
34
 
35
- import {
36
- buildProviderConfig,
37
- EMBED_DUMMY_API_KEY,
38
- EMBED_PROVIDER_ID,
39
- embedPortPath,
40
- readEmbedPort,
41
- modelsYmlPort,
42
- probeEmbed,
43
- resolveEmbedPort,
44
- writeEmbedPort,
45
- } from "./embed-logic.ts";
35
+ import { EMBED_DUMMY_API_KEY, EMBED_PROVIDER_ID, buildProviderConfig, deriveAgentdoxScope, embedPortPath, modelsYmlPort, probeEmbed, readEmbedPort, resolveEmbedPort, writeEmbedPort } from "./embed-logic.ts";
46
36
 
47
37
  /** omp's models.yml as text, or "" when it does not exist / cannot be read. */
48
38
  function readModelsYml(): string {
@@ -173,7 +163,7 @@ export default function (pi: ExtensionAPI): void {
173
163
  // locally; the other extensions find it through remote.json.
174
164
  const remote = readRemoteRouter(routerHome());
175
165
  if (remote !== null) {
176
- pi.registerProvider(EMBED_PROVIDER_ID, remoteProviderRegistration(remote, sessionId, !ctx.hasUI, cfg.ledger.fallbackBlend));
166
+ pi.registerProvider(EMBED_PROVIDER_ID, remoteProviderRegistration(remote, sessionId, !ctx.hasUI, cfg.ledger.fallbackBlend, deriveAgentdoxScope(process.cwd())));
177
167
  pi.setLabel(`auto-model-router remote (${remote.url.replace(/^https?:\/\//, "")})`);
178
168
  writeEmbedLog(`remote mode url=${remote.url} user=${remote.userId} session=${sessionId}`);
179
169
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "auto-model-router",
3
- "version": "0.6.2",
3
+ "version": "0.6.3",
4
4
  "private": false,
5
5
  "description": "Local cost/complexity-aware model router for Oh My Pi, backed by OpenRouter",
6
6
  "type": "module",
@@ -22,6 +22,10 @@ describe("remote-logic", () => {
22
22
  expect(reg).toMatchObject({ baseUrl: "https://team.example/v1", api: "openai-completions", apiKey: "amrt_k", headers: { "X-Omp-Session": "sess-1", "X-Omp-Subagent": "1" } });
23
23
  expect(reg.models.map((m) => m.id)).toEqual(["auto", "auto-cheap", "auto-max"]);
24
24
  expect(reg.models[0]!.cost).toEqual({ input: 1, output: 4, cacheRead: 0.1, cacheWrite: 1.25 });
25
+ // The workspace's project travels with the turn, so one remote router serves every repo on
26
+ // the machine with the right context; the remote may still override it.
27
+ expect(reg.headers["X-Agentdox-Scope"]).toBeUndefined();
28
+ expect(remoteProviderRegistration(t, "", false, { inputPerMtok: 1, outputPerMtok: 4 }, "omp-router").headers).toEqual({ "X-Agentdox-Scope": "omp-router" });
25
29
  const dir = mkdtempSync(join(tmpdir(), "amr-remote-"));
26
30
  expect(readRemoteRouter(dir)).toBeNull();
27
31
  writeFileSync(join(dir, "remote.json"), JSON.stringify({ url: "https://t", key: "k" }));