@sangyaahub/pi-marg 1.0.1 → 1.0.4
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/.omp-plugin/marketplace.json +2 -2
- package/CHANGELOG.md +19 -0
- package/MODEL-SETUP.md +53 -9
- package/README.md +20 -6
- package/adapters/omp/extensions/model-router.ts +212 -15
- package/adapters/pi/extensions/model-router.ts +112 -2
- package/adapters/pi/prompts/auto-models.md +3 -3
- package/agents/auto-intake.md +1 -1
- package/assets/pi-marg-banner.png +0 -0
- package/commands/auto-models.md +3 -3
- package/core/model-routing.ts +617 -17
- package/package.json +1 -1
- package/scripts/install.sh +1 -1
- package/scripts/validate.sh +44 -1
- package/skills/auto-mode-router/SKILL.md +1 -1
- package/skills/auto-mode-router/references/intake-and-state.md +17 -3
- package/skills/auto-mode-router/references/models-and-tools.md +15 -2
- package/skills/auto-mode-router/references/session-ledger.md +5 -0
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
import { Type } from "typebox";
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
|
+
FALLBACK_LEVELS,
|
|
4
5
|
MODEL_STATE_TYPE,
|
|
5
6
|
emptyModelRouteState,
|
|
7
|
+
executeAutomaticFailover,
|
|
6
8
|
executeModelRoute,
|
|
9
|
+
failedModelSelector,
|
|
10
|
+
findTerminalUsageLimitFailure,
|
|
11
|
+
formatFailoverRecoveryMessage,
|
|
12
|
+
hasConfiguredFallback,
|
|
13
|
+
modelFailureKey,
|
|
14
|
+
modelToolUsageLimitFailure,
|
|
7
15
|
restoreModelRouteState,
|
|
8
16
|
type ModelLike,
|
|
9
17
|
type ModelRouteParams,
|
|
@@ -26,24 +34,126 @@ function availableModels(ctx: any): ModelLike[] {
|
|
|
26
34
|
|
|
27
35
|
export default function modelRouter(pi: any) {
|
|
28
36
|
let state = emptyModelRouteState();
|
|
29
|
-
|
|
37
|
+
let recoveryToken: symbol | undefined;
|
|
38
|
+
let sessionGeneration = 0;
|
|
39
|
+
let pendingModelFailure: { selector: string; key: string } | undefined;
|
|
40
|
+
let pendingToolFailure: { selector: string; key: string } | undefined;
|
|
41
|
+
const restore = (_event: unknown, ctx: any) => {
|
|
42
|
+
sessionGeneration += 1;
|
|
43
|
+
recoveryToken = undefined;
|
|
44
|
+
pendingModelFailure = undefined;
|
|
45
|
+
pendingToolFailure = undefined;
|
|
30
46
|
state = restoreModelRouteState(ctx.sessionManager.getBranch());
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
pi.on("session_start", restore);
|
|
50
|
+
pi.on("session_tree", restore);
|
|
51
|
+
|
|
52
|
+
const recoverFromUsageLimit = async (failure: { selector: string; key: string }, ctx: any) => {
|
|
53
|
+
if (recoveryToken) return;
|
|
54
|
+
if (!hasConfiguredFallback(state)) return;
|
|
55
|
+
const generation = sessionGeneration;
|
|
56
|
+
const token = Symbol("pi-marg-recovery");
|
|
57
|
+
recoveryToken = token;
|
|
58
|
+
try {
|
|
59
|
+
const execution = await executeAutomaticFailover(state, failure.selector, {
|
|
60
|
+
runtimeName: "Pi",
|
|
61
|
+
models: availableModels(ctx),
|
|
62
|
+
hasExternalDevin: hasExternalDevin(pi),
|
|
63
|
+
activate: (model) => pi.setModel(model),
|
|
64
|
+
persist: (nextState) => {
|
|
65
|
+
if (sessionGeneration !== generation) throw new Error("session changed during automatic recovery");
|
|
66
|
+
pi.appendEntry(MODEL_STATE_TYPE, nextState);
|
|
67
|
+
},
|
|
68
|
+
}, failure.key);
|
|
69
|
+
if (sessionGeneration !== generation) {
|
|
70
|
+
ctx.ui?.notify?.(
|
|
71
|
+
"PiMarg stopped an in-flight recovery because the session changed. Verify the active model before continuing.",
|
|
72
|
+
"warning",
|
|
73
|
+
);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (execution.ignoredDuplicate) return;
|
|
77
|
+
state = execution.state;
|
|
78
|
+
if (!execution.switched) {
|
|
79
|
+
if (!execution.stateChanged) return;
|
|
80
|
+
ctx.ui?.notify?.(
|
|
81
|
+
"PiMarg could not resume automatically because no configured backup model remains. Re-run the model chooser after restoring provider access.",
|
|
82
|
+
"error",
|
|
83
|
+
);
|
|
84
|
+
if (execution.persistenceError) {
|
|
85
|
+
ctx.ui?.notify?.(
|
|
86
|
+
`PiMarg updated recovery state only in memory because the session ledger could not be saved: ${execution.persistenceError}`,
|
|
87
|
+
"error",
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const title = execution.switched.level === "high" ? "High" : "Low";
|
|
94
|
+
ctx.ui?.notify?.(
|
|
95
|
+
execution.reusedCurrent
|
|
96
|
+
? `PiMarg kept the ${title} backup ${execution.switched.selection.selector} active after ${failure.selector} reached its limit and is resuming safely.`
|
|
97
|
+
: `PiMarg switched ${failure.selector} to the ${title} backup ${execution.switched.selection.selector} and is resuming automatically.`,
|
|
98
|
+
"warning",
|
|
99
|
+
);
|
|
100
|
+
if (execution.persistenceError) {
|
|
101
|
+
ctx.ui?.notify?.(
|
|
102
|
+
`The model switched, but PiMarg could not save the recovery state to the session ledger: ${execution.persistenceError}`,
|
|
103
|
+
"error",
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
pi.sendUserMessage(
|
|
107
|
+
formatFailoverRecoveryMessage(
|
|
108
|
+
state,
|
|
109
|
+
failure.selector,
|
|
110
|
+
execution.switched.level,
|
|
111
|
+
execution.switched.selection.selector,
|
|
112
|
+
execution.reusedCurrent,
|
|
113
|
+
),
|
|
114
|
+
{ deliverAs: "followUp" },
|
|
115
|
+
);
|
|
116
|
+
} finally {
|
|
117
|
+
if (recoveryToken === token) recoveryToken = undefined;
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
pi.on("agent_end", (event: any, ctx: any) => {
|
|
122
|
+
const failed = findTerminalUsageLimitFailure(event?.messages ?? []);
|
|
123
|
+
pendingModelFailure = failed
|
|
124
|
+
? { selector: failedModelSelector(failed, ctx.model), key: modelFailureKey(failed) }
|
|
125
|
+
: undefined;
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
pi.on("tool_execution_end", (event: any) => {
|
|
129
|
+
pendingToolFailure = modelToolUsageLimitFailure(event) ?? pendingToolFailure;
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
pi.on("agent_settled", async (_event: unknown, ctx: any) => {
|
|
133
|
+
const failure = pendingModelFailure ?? pendingToolFailure;
|
|
134
|
+
pendingModelFailure = undefined;
|
|
135
|
+
pendingToolFailure = undefined;
|
|
136
|
+
if (!failure) return;
|
|
137
|
+
await recoverFromUsageLimit(failure, ctx);
|
|
31
138
|
});
|
|
32
139
|
|
|
33
140
|
pi.registerTool({
|
|
34
141
|
name: "auto_model_route",
|
|
35
142
|
label: "PiMarg Model Router",
|
|
36
|
-
description: "Discover, select, and activate live A/B/C/D
|
|
143
|
+
description: "Discover, select, and activate live A/B/C/D models plus distinct high/low automatic usage-limit backups.",
|
|
37
144
|
parameters: Type.Object({
|
|
38
145
|
action: Type.Union([
|
|
39
146
|
Type.Literal("catalog"),
|
|
40
147
|
Type.Literal("select"),
|
|
148
|
+
Type.Literal("select_fallback"),
|
|
41
149
|
Type.Literal("activate"),
|
|
42
150
|
Type.Literal("status"),
|
|
43
151
|
]),
|
|
44
152
|
stage: Type.Optional(Type.Union([Type.Literal("A"), Type.Literal("B"), Type.Literal("C"), Type.Literal("D")])),
|
|
153
|
+
fallback: Type.Optional(Type.Union(FALLBACK_LEVELS.map((level) => Type.Literal(level)))),
|
|
45
154
|
target: Type.Optional(Type.String()),
|
|
46
155
|
workType: Type.Optional(Type.Number()),
|
|
156
|
+
provider: Type.Optional(Type.String()),
|
|
47
157
|
}),
|
|
48
158
|
async execute(_toolCallId: string, params: ModelRouteParams, _signal: unknown, _onUpdate: unknown, ctx: any) {
|
|
49
159
|
const models = availableModels(ctx);
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: List or change
|
|
2
|
+
description: List or change A/B/C/D models and the high/low automatic recovery pair from Pi's live authenticated catalog.
|
|
3
3
|
argument-hint: [status | choose]
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
Load `auto-mode-router` and read its model-routing reference.
|
|
7
7
|
|
|
8
|
-
- With no argument or `status`, call `auto_model_route` with `action: status`, then show the saved A/B/C/D selectors and
|
|
9
|
-
- With `choose`, call `auto_model_route` with `action: catalog` and the active work type. Present
|
|
8
|
+
- With no argument or `status`, call `auto_model_route` with `action: status`, then show the saved A/B/C/D selectors, separation checks, high/low backups, active fallback, and last failover.
|
|
9
|
+
- With `choose`, call `auto_model_route` with `action: catalog` and the active work type. Present required stages and exact available selectors. Save each stage with `action: select`, then save two distinct runtime models with `action: select_fallback`, first `fallback: high`, then `fallback: low`.
|
|
10
10
|
- Do not type or persist a model ID that the live catalog did not return.
|
|
11
11
|
- Treat any model returned by Pi's registry as a normal in-session route. Treat Devin or another external coding-agent session as a capability-gated external route; creating one requires `post-web` approval.
|
|
12
12
|
- C/D are the authoritative independent review selectors. Any detected advisor or watcher is supplemental and must not weaken the A≠C or B≠D checks.
|
package/agents/auto-intake.md
CHANGED
|
@@ -7,4 +7,4 @@ blocking: true
|
|
|
7
7
|
autoloadSkills: [auto-mode-router]
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
-
Complete only the intake and availability checks from `auto-mode-router`. Ask in the required order: work context, work type, skill mode, live A/B/C/D selections, then add-ons. Call `auto_runtime_status` before recommending runtime capabilities. Return the proposed `[AUTO-MODE]` state. Do not plan, implement, or change project files.
|
|
10
|
+
Complete only the intake and availability checks from `auto-mode-router`. Ask in the required order: work context, work type, skill mode, live A/B/C/D selections, high and low runtime backups, then add-ons. Call `auto_runtime_status` before recommending runtime capabilities. Return the proposed `[AUTO-MODE]` state. Do not plan, implement, or change project files.
|
|
Binary file
|
package/commands/auto-models.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: List or change
|
|
2
|
+
description: List or change A/B/C/D models and the high/low automatic recovery pair from OMP's live authenticated catalog.
|
|
3
3
|
argument-hint: [status | choose]
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
Load `auto-mode-router` and read its model-routing reference.
|
|
7
7
|
|
|
8
|
-
- With no argument or `status`, call `auto_model_route` with `action: status`, then show the saved A/B/C/D selectors and
|
|
9
|
-
- With `choose`, call `auto_model_route` with `action: catalog` and the active work type. Present
|
|
8
|
+
- With no argument or `status`, call `auto_model_route` with `action: status`, then show the saved A/B/C/D selectors, separation checks, high/low backups, active fallback, and last failover.
|
|
9
|
+
- With `choose`, call `auto_model_route` with `action: catalog` and the active work type. Present required stages and exact available selectors. Save each stage with `action: select`, then save two distinct runtime models with `action: select_fallback`, first `fallback: high`, then `fallback: low`.
|
|
10
10
|
- Do not type or persist a model ID that the live catalog did not return.
|
|
11
11
|
- Distinguish native `devin/swe-*` OMP models from the optional external `devin/mcp-session` route. Only creating the external session requires `post-web` approval.
|
|
12
12
|
- C/D are the authoritative independent review selectors. A configured OMP advisor is supplemental and must not weaken the A≠C or B≠D checks.
|