@jwilger/pi-development-system 0.13.0 → 0.14.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/package.json
CHANGED
|
@@ -34,8 +34,11 @@ agent_spawn({path: "/impl-add-flag", type: "implementer", task: "...",
|
|
|
34
34
|
|
|
35
35
|
- Jev judges difficulty and risk; the project's `[routing]` table (slots, never
|
|
36
36
|
model ids) decides. Jev offline means `routine/low`.
|
|
37
|
-
- If the result says the slot is unresolvable,
|
|
38
|
-
|
|
37
|
+
- If the result says the slot is unresolvable, spawning without a pin fails the same
|
|
38
|
+
way (the agent's own `models:` list is the same candidate set). Run `/devsys-models`
|
|
39
|
+
to fix the matrix, or pin a model you know works.
|
|
40
|
+
- A pin is an explicit choice: it bypasses `/scoped-models` and the agent's `models:`
|
|
41
|
+
list, so only pin what the route (or you, with a stated reason) selected.
|
|
39
42
|
- You may override the route. Say why in your reply; overriding downwards for
|
|
40
43
|
risky work is a judgement call worth a line in the decision log.
|
|
41
44
|
|
package/src/core/routing.ts
CHANGED
|
@@ -55,7 +55,7 @@ export function recommendRoute(input: {
|
|
|
55
55
|
if (!resolved.ok) {
|
|
56
56
|
return {
|
|
57
57
|
...route,
|
|
58
|
-
note: `${prefix}No model with credentials resolves slot "${route.slot}" (unresolvable);
|
|
58
|
+
note: `${prefix}No model with credentials resolves slot "${route.slot}" (unresolvable); an agent spawned without a pin will fail the same way, so run /devsys-models to fix the matrix or pin a model you know works.`,
|
|
59
59
|
};
|
|
60
60
|
}
|
|
61
61
|
return {
|
|
@@ -57,3 +57,21 @@ export function applySpawnOverrides(
|
|
|
57
57
|
...(overrides.thinkingLevel === undefined ? {} : { thinkingLevel: overrides.thinkingLevel }),
|
|
58
58
|
};
|
|
59
59
|
}
|
|
60
|
+
|
|
61
|
+
export type ModelSource = "current" | "preferences" | "restored" | "inherited";
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Where a subagent's starting model comes from. A per-spawn pin disqualifies the type's
|
|
65
|
+
* preference list (the pin itself is applied afterwards for fresh spawns), so a resumed pinned
|
|
66
|
+
* thread keeps the model it was already running instead of falling back to the preferences.
|
|
67
|
+
*/
|
|
68
|
+
export function modelSource(input: {
|
|
69
|
+
mode: "use-current" | "pick-first-scoped" | "pick-first";
|
|
70
|
+
pinned: boolean;
|
|
71
|
+
hasPreferences: boolean;
|
|
72
|
+
hasRestored: boolean;
|
|
73
|
+
}): ModelSource {
|
|
74
|
+
if (input.mode === "use-current") return "current";
|
|
75
|
+
if (!input.pinned && input.hasPreferences) return "preferences";
|
|
76
|
+
return input.hasRestored ? "restored" : "inherited";
|
|
77
|
+
}
|
|
@@ -56,7 +56,7 @@ export function medianLabel<T extends string>(
|
|
|
56
56
|
let cumulative = 0;
|
|
57
57
|
for (const level of levels) {
|
|
58
58
|
cumulative += (answer.probabilities[level] ?? 0) / total;
|
|
59
|
-
if (cumulative
|
|
59
|
+
if (cumulative > 0.5) return level;
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
return levels.find((level) => level === answer.choice);
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
1. Every `*.ts` file starts with `// @ts-nocheck`: upstream compiles under looser options than this repo's strict `tsconfig.json` (`exactOptionalPropertyTypes`, `erasableSyntaxOnly`). `biome.json` excludes `src/subagents`. Both are deliberate: the code is verbatim, and reformatting or retyping it would make upstream diffs unreadable.
|
|
13
13
|
2. Relative `.js` import specifiers were rewritten to `.ts` (upstream relies on a TypeScript-aware loader; this repo's tests run under Node's native type stripping).
|
|
14
14
|
3. `prefs/config.ts`: the bundled-agents directory is `../../../agents/` (was `../../agents/`) because the sources moved one level deeper.
|
|
15
|
-
4. Per-spawn pins (I5.2): `types.ts` `AgentType.spawnOverrides` and `ThreadService.spawn` args gain `model`/`thinkingLevel`; `orch/tools.ts` `agent_spawn` schema exposes both; `orch/manager.ts` `spawn` validates them with `src/core/spawn-overrides.ts` and stores them on the cloned type (so saved threads keep them); `orch/runtime.ts` `resolveInitialSettings` applies
|
|
15
|
+
4. Per-spawn pins (I5.2): `types.ts` `AgentType.spawnOverrides` and `ThreadService.spawn` args gain `model`/`thinkingLevel`; `orch/tools.ts` `agent_spawn` schema exposes both; `orch/manager.ts` `spawn` validates them with `src/core/spawn-overrides.ts` and stores them on the cloned type (so saved threads keep them); `orch/runtime.ts` `resolveInitialSettings` skips the type's preference list when a model is pinned (via `modelSource` in `src/core/spawn-overrides.ts`, so resumed threads keep their model), applies the pin last for fresh spawns, and `createDriver` drops model preferences/scope policy when a model is pinned; `ThreadView.pinned` (`types.ts`) is filled in `orch/manager.ts` `view()` so `agent_status` reports the pins. A pin therefore bypasses `/scoped-models`.
|
|
16
16
|
5. Family preferences (I5.3): `prefs/models.ts` `selectPreferredModel` resolves each `models:` entry with `resolveCandidate` from `src/core/models.ts`, so `provider/gpt-*-sol` picks the newest available match; exact ids behave as before.
|
|
17
17
|
|
|
18
18
|
## MIT notice
|
|
@@ -26,7 +26,7 @@ import { selectTools } from "../prefs/config.ts";
|
|
|
26
26
|
import { TranscriptChannel } from "./transcript.ts";
|
|
27
27
|
import type { InheritedToolSource } from "./inherited-tools.ts";
|
|
28
28
|
import type { ModelSelectionMode, ToolFilteringMode } from "../prefs/settings.ts";
|
|
29
|
-
import { applySpawnOverrides } from "../../core/spawn-overrides.ts";
|
|
29
|
+
import { applySpawnOverrides, modelSource } from "../../core/spawn-overrides.ts";
|
|
30
30
|
import {
|
|
31
31
|
modelIdentity,
|
|
32
32
|
getModelPreferences,
|
|
@@ -202,13 +202,19 @@ export function createDriverFactory(
|
|
|
202
202
|
const mode = getModelSelection();
|
|
203
203
|
// devsys: an explicit per-spawn model pin bypasses preference/scope policy (also on resume).
|
|
204
204
|
const pinned = type.spawnOverrides?.model !== undefined;
|
|
205
|
-
const preferences =
|
|
205
|
+
const preferences = pinned ? undefined : getModelPreferences(type);
|
|
206
|
+
const source = modelSource({
|
|
207
|
+
mode,
|
|
208
|
+
pinned,
|
|
209
|
+
hasPreferences: preferences !== undefined,
|
|
210
|
+
hasRestored: restored?.model !== undefined,
|
|
211
|
+
});
|
|
206
212
|
let provider = parent?.provider ?? ctx.model?.provider;
|
|
207
213
|
let id = parent?.id ?? ctx.model?.id;
|
|
208
|
-
if (
|
|
214
|
+
if (source === "current") {
|
|
209
215
|
provider = ctx.model?.provider;
|
|
210
216
|
id = ctx.model?.id;
|
|
211
|
-
} else if (
|
|
217
|
+
} else if (source === "preferences") {
|
|
212
218
|
const filtering = mode === "pick-first-scoped";
|
|
213
219
|
const candidates = filtering
|
|
214
220
|
? availableScopedModels(normalizeScopedModels(ctx.scopedModels))
|
|
@@ -216,7 +222,7 @@ export function createDriverFactory(
|
|
|
216
222
|
({ provider, id } = parseModelIdentity(
|
|
217
223
|
selectTypeModelPreference(type, candidates, filtering)!,
|
|
218
224
|
));
|
|
219
|
-
} else if (restored?.model) {
|
|
225
|
+
} else if (source === "restored" && restored?.model) {
|
|
220
226
|
provider = restored.model.provider;
|
|
221
227
|
id = restored.model.modelId;
|
|
222
228
|
}
|