@ilya-lesikov/pi-pi 0.9.0 → 0.11.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/3p/pi-ask-user/index.ts +71 -124
- package/3p/pi-ask-user/single-select-layout.ts +3 -14
- package/3p/pi-subagents/package.json +12 -7
- package/3p/pi-subagents/src/agent-manager.ts +243 -79
- package/3p/pi-subagents/src/agent-runner.ts +501 -365
- package/3p/pi-subagents/src/agent-types.ts +46 -57
- package/3p/pi-subagents/src/cross-extension-rpc.ts +52 -46
- package/3p/pi-subagents/src/custom-agents.ts +30 -6
- package/3p/pi-subagents/src/default-agents.ts +25 -43
- package/3p/pi-subagents/src/enabled-models.ts +180 -0
- package/3p/pi-subagents/src/index.ts +599 -839
- package/3p/pi-subagents/src/model-resolver.ts +26 -7
- package/3p/pi-subagents/src/output-file.ts +21 -2
- package/3p/pi-subagents/src/prompts.ts +17 -3
- package/3p/pi-subagents/src/schedule-store.ts +153 -0
- package/3p/pi-subagents/src/schedule.ts +365 -0
- package/3p/pi-subagents/src/settings.ts +261 -0
- package/3p/pi-subagents/src/skill-loader.ts +77 -54
- package/3p/pi-subagents/src/status-note.ts +25 -0
- package/3p/pi-subagents/src/types.ts +97 -6
- package/3p/pi-subagents/src/ui/agent-widget.ts +94 -21
- package/3p/pi-subagents/src/ui/conversation-viewer.ts +147 -35
- package/3p/pi-subagents/src/ui/schedule-menu.ts +104 -0
- package/3p/pi-subagents/src/ui/viewer-keys.ts +39 -0
- package/3p/pi-subagents/src/usage.ts +60 -0
- package/3p/pi-subagents/src/worktree.ts +49 -20
- package/extensions/orchestrator/agents/advisor.ts +13 -8
- package/extensions/orchestrator/agents/brainstorm-reviewer.ts +8 -3
- package/extensions/orchestrator/agents/code-reviewer.ts +8 -3
- package/extensions/orchestrator/agents/constraints.test.ts +26 -0
- package/extensions/orchestrator/agents/constraints.ts +12 -2
- package/extensions/orchestrator/agents/deep-debugger.ts +13 -8
- package/extensions/orchestrator/agents/explore.ts +12 -6
- package/extensions/orchestrator/agents/librarian.ts +13 -5
- package/extensions/orchestrator/agents/plan-reviewer.ts +8 -3
- package/extensions/orchestrator/agents/planner.ts +8 -3
- package/extensions/orchestrator/agents/pools.test.ts +56 -0
- package/extensions/orchestrator/agents/prompts.test.ts +52 -10
- package/extensions/orchestrator/agents/registry.test.ts +245 -0
- package/extensions/orchestrator/agents/registry.ts +145 -10
- package/extensions/orchestrator/agents/reviewer.ts +14 -8
- package/extensions/orchestrator/agents/task.ts +12 -6
- package/extensions/orchestrator/agents/tool-routing.ts +213 -85
- package/extensions/orchestrator/agents/wait-for-completion.test.ts +171 -0
- package/extensions/orchestrator/ast-search.test.ts +124 -0
- package/extensions/orchestrator/billing-spoof.test.ts +67 -0
- package/extensions/orchestrator/billing-spoof.ts +95 -0
- package/extensions/orchestrator/cbm.more.test.ts +358 -0
- package/extensions/orchestrator/command-handlers.ts +6 -0
- package/extensions/orchestrator/commands.test.ts +15 -2
- package/extensions/orchestrator/commands.ts +1 -1
- package/extensions/orchestrator/config.test.ts +89 -1
- package/extensions/orchestrator/config.ts +102 -19
- package/extensions/orchestrator/context.test.ts +46 -0
- package/extensions/orchestrator/context.ts +18 -5
- package/extensions/orchestrator/custom-footer.test.ts +24 -10
- package/extensions/orchestrator/custom-footer.ts +4 -2
- package/extensions/orchestrator/doctor.more.test.ts +315 -0
- package/extensions/orchestrator/doctor.ts +6 -2
- package/extensions/orchestrator/event-handlers.more.test.ts +561 -0
- package/extensions/orchestrator/event-handlers.test.ts +96 -9
- package/extensions/orchestrator/event-handlers.ts +344 -151
- package/extensions/orchestrator/exa.more.test.ts +118 -0
- package/extensions/orchestrator/flant-infra.more.test.ts +381 -0
- package/extensions/orchestrator/flant-infra.test.ts +127 -0
- package/extensions/orchestrator/flant-infra.ts +126 -41
- package/extensions/orchestrator/index.test.ts +76 -0
- package/extensions/orchestrator/index.ts +2 -0
- package/extensions/orchestrator/integration.test.ts +183 -65
- package/extensions/orchestrator/model-registry.test.ts +2 -1
- package/extensions/orchestrator/model-registry.ts +12 -2
- package/extensions/orchestrator/orchestrator.test.ts +67 -0
- package/extensions/orchestrator/orchestrator.ts +119 -27
- package/extensions/orchestrator/phases/brainstorm.test.ts +30 -1
- package/extensions/orchestrator/phases/brainstorm.ts +43 -6
- package/extensions/orchestrator/phases/implementation.ts +2 -0
- package/extensions/orchestrator/phases/machine.test.ts +17 -1
- package/extensions/orchestrator/phases/machine.ts +4 -1
- package/extensions/orchestrator/phases/planning.test.ts +47 -1
- package/extensions/orchestrator/phases/planning.ts +18 -3
- package/extensions/orchestrator/phases/review-task.ts +5 -0
- package/extensions/orchestrator/phases/review.test.ts +10 -0
- package/extensions/orchestrator/phases/review.ts +22 -7
- package/extensions/orchestrator/phases/spawn-blocking.test.ts +1 -0
- package/extensions/orchestrator/phases/verdict.ts +6 -5
- package/extensions/orchestrator/plannotator-open-failure.test.ts +67 -0
- package/extensions/orchestrator/plannotator.test.ts +38 -1
- package/extensions/orchestrator/plannotator.ts +50 -3
- package/extensions/orchestrator/pp-menu.leaves.test.ts +590 -0
- package/extensions/orchestrator/pp-menu.more.test.ts +524 -0
- package/extensions/orchestrator/pp-menu.test.ts +114 -7
- package/extensions/orchestrator/pp-menu.ts +580 -91
- package/extensions/orchestrator/pp-state-tools.test.ts +80 -0
- package/extensions/orchestrator/pp-state-tools.ts +65 -0
- package/extensions/orchestrator/rate-limit-fallback.more.test.ts +241 -0
- package/extensions/orchestrator/rate-limit-fallback.test.ts +20 -1
- package/extensions/orchestrator/rate-limit-fallback.ts +12 -0
- package/extensions/orchestrator/review-files.test.ts +26 -0
- package/extensions/orchestrator/review-files.ts +3 -0
- package/extensions/orchestrator/state.test.ts +73 -1
- package/extensions/orchestrator/state.ts +95 -23
- package/extensions/orchestrator/suppress-pierre-theme-spam.test.ts +56 -0
- package/extensions/orchestrator/suppress-pierre-theme-spam.ts +45 -0
- package/extensions/orchestrator/validate-artifacts.ts +1 -1
- package/package.json +9 -2
- package/scripts/lib/smoke-resolve.mjs +99 -0
- package/scripts/test-3p.sh +52 -0
- package/scripts/test-package.sh +62 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reads `enabledModels` from pi's settings (global `<agentDir>/settings.json`
|
|
3
|
+
* + project-local `<cwd>/.pi/settings.json`, project wins) and resolves
|
|
4
|
+
* entries to concrete `provider/modelId` keys for scope validation.
|
|
5
|
+
*
|
|
6
|
+
* **Project overrides global**, mirroring pi's own `SettingsManager`
|
|
7
|
+
* deep-merge behavior and matching the precedence we use for our own
|
|
8
|
+
* `subagents.json` settings (see `src/settings.ts:loadSettings`). If
|
|
9
|
+
* project file has `enabledModels` set, it wholly replaces global's
|
|
10
|
+
* (array fields are replaced, not concatenated).
|
|
11
|
+
*
|
|
12
|
+
* **Limited subset of upstream's resolveModelScope.** We support exact
|
|
13
|
+
* `provider/modelId` matching only. Upstream (pi-coding-agent's
|
|
14
|
+
* `core/model-resolver.ts`) additionally supports glob patterns
|
|
15
|
+
* (`*sonnet*`, `anthropic/*`), bare model IDs without provider, and
|
|
16
|
+
* thinking-level suffixes (`provider/*:high`). Those forms are silently
|
|
17
|
+
* ignored here.
|
|
18
|
+
*
|
|
19
|
+
* In practice, pi's `/scoped-models` picker writes exact `provider/modelId`
|
|
20
|
+
* entries, so the limitation is invisible for users who configure scope
|
|
21
|
+
* through pi's UI. Hand-edited settings using globs or bare IDs will
|
|
22
|
+
* produce an empty allowed set (scope check becomes a no-op).
|
|
23
|
+
*
|
|
24
|
+
* Example:
|
|
25
|
+
* enabledModels = ["anthropic/claude-sonnet-4-6", "anthropic/claude-opus-4-6"]
|
|
26
|
+
* → resolves to { "anthropic/claude-sonnet-4-6", "anthropic/claude-opus-4-6" }
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
30
|
+
import { join } from "node:path";
|
|
31
|
+
import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
32
|
+
import type { ModelEntry } from "./model-resolver.js";
|
|
33
|
+
|
|
34
|
+
/** Minimal registry shape — only the methods resolveEnabledModels actually calls. */
|
|
35
|
+
export interface ModelRegistryRef {
|
|
36
|
+
getAll(): unknown[];
|
|
37
|
+
getAvailable?(): unknown[];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Paths to pi's settings.json files: [project, global] (project takes precedence). */
|
|
41
|
+
function settingsPaths(cwd: string): [project: string, global: string] {
|
|
42
|
+
return [
|
|
43
|
+
join(cwd, ".pi", "settings.json"),
|
|
44
|
+
join(getAgentDir(), "settings.json"),
|
|
45
|
+
];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Read `enabledModels` from a single settings.json file. Undefined when missing or absent. */
|
|
49
|
+
function readField(path: string): string[] | undefined {
|
|
50
|
+
if (!existsSync(path)) return undefined;
|
|
51
|
+
try {
|
|
52
|
+
const raw = JSON.parse(readFileSync(path, "utf-8"));
|
|
53
|
+
if (Array.isArray(raw?.enabledModels)) return raw.enabledModels as string[];
|
|
54
|
+
} catch {
|
|
55
|
+
/* corrupt file — silent */
|
|
56
|
+
}
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Read enabledModels from pi's settings — project-local overrides global.
|
|
62
|
+
* Mirrors pi's SettingsManager deep-merge for the `enabledModels` field
|
|
63
|
+
* (and matches our own loadSettings precedence in src/settings.ts).
|
|
64
|
+
* Returns undefined when neither file has the field.
|
|
65
|
+
*/
|
|
66
|
+
export function readEnabledModels(cwd: string): string[] | undefined {
|
|
67
|
+
const [project, global] = settingsPaths(cwd);
|
|
68
|
+
return readField(project) ?? readField(global);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Resolve enabledModels patterns → Set<"provider/modelId"> (lowercase keys).
|
|
73
|
+
*
|
|
74
|
+
* Only exact `provider/modelId` patterns are matched (case-insensitive).
|
|
75
|
+
* Patterns without a slash, with glob characters, or with a `:thinking`
|
|
76
|
+
* suffix are silently dropped. See module-level docstring for rationale.
|
|
77
|
+
*
|
|
78
|
+
* Cache: keyed on JSON.stringify(patterns) + mtime/size of *both*
|
|
79
|
+
* project and global settings.json files. Re-resolves when either file
|
|
80
|
+
* changes or the patterns argument differs.
|
|
81
|
+
*
|
|
82
|
+
* Returns undefined when no patterns are provided or no patterns match
|
|
83
|
+
* (scope check becomes a no-op at the call site).
|
|
84
|
+
*/
|
|
85
|
+
|
|
86
|
+
// Module-level cache — invalidated when either settings.json changes or patterns differ.
|
|
87
|
+
let cachedAllowed: Set<string> | undefined;
|
|
88
|
+
let cachedHash = "";
|
|
89
|
+
let cachedPatternsKey = "";
|
|
90
|
+
|
|
91
|
+
/** mtime+size hash of one file, or "missing" if absent. */
|
|
92
|
+
function hashOf(path: string): string {
|
|
93
|
+
try {
|
|
94
|
+
const s = statSync(path);
|
|
95
|
+
return `${s.mtimeMs}-${s.size}`;
|
|
96
|
+
} catch {
|
|
97
|
+
return "missing";
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function resolveEnabledModels(
|
|
102
|
+
patterns: string[] | undefined,
|
|
103
|
+
registry: ModelRegistryRef,
|
|
104
|
+
cwd: string = process.cwd(),
|
|
105
|
+
): Set<string> | undefined {
|
|
106
|
+
// Fast path: check cache (stat both project and global settings.json files)
|
|
107
|
+
const patternsKey = JSON.stringify(patterns);
|
|
108
|
+
const [project, global] = settingsPaths(cwd);
|
|
109
|
+
const fileHash = `${hashOf(project)};${hashOf(global)}`;
|
|
110
|
+
|
|
111
|
+
if (fileHash === cachedHash && patternsKey === cachedPatternsKey) {
|
|
112
|
+
return cachedAllowed;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Cache miss — resolve
|
|
116
|
+
if (!patterns || patterns.length === 0) {
|
|
117
|
+
cachedHash = fileHash;
|
|
118
|
+
cachedPatternsKey = patternsKey;
|
|
119
|
+
cachedAllowed = undefined;
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const available = (registry.getAvailable?.() ?? registry.getAll()) as ModelEntry[];
|
|
124
|
+
const allowed = new Set<string>();
|
|
125
|
+
|
|
126
|
+
for (const pattern of patterns) {
|
|
127
|
+
const trimmed = pattern.trim();
|
|
128
|
+
if (!trimmed) continue; // skip empty/whitespace
|
|
129
|
+
resolveExact(trimmed, available, allowed);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const result = allowed.size > 0 ? allowed : undefined;
|
|
133
|
+
cachedHash = fileHash;
|
|
134
|
+
cachedPatternsKey = patternsKey;
|
|
135
|
+
cachedAllowed = result;
|
|
136
|
+
return result;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* True when `model` is in the allowed set. Centralizes the key format
|
|
143
|
+
* (`provider/id` lowercase) so callers don't have to reproduce it —
|
|
144
|
+
* both set-building (resolveExact) and lookup go through `modelKey`.
|
|
145
|
+
*/
|
|
146
|
+
export function isModelInScope(
|
|
147
|
+
model: { provider: string; id: string },
|
|
148
|
+
allowed: Set<string>,
|
|
149
|
+
): boolean {
|
|
150
|
+
return allowed.has(modelKey(model));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** Canonical lowercase `provider/id` key for the allowed set. */
|
|
154
|
+
function modelKey(model: { provider: string; id: string }): string {
|
|
155
|
+
return `${model.provider}/${model.id}`.toLowerCase();
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Resolve exact model pattern. Example: "google/gemma-4-31b-it".
|
|
160
|
+
*/
|
|
161
|
+
function resolveExact(
|
|
162
|
+
pattern: string,
|
|
163
|
+
available: ModelEntry[],
|
|
164
|
+
allowed: Set<string>,
|
|
165
|
+
): void {
|
|
166
|
+
// "provider/modelId" — exact (colon is part of id, not split)
|
|
167
|
+
const slashIdx = pattern.indexOf("/");
|
|
168
|
+
if (slashIdx === -1) return; // bare modelId not supported
|
|
169
|
+
|
|
170
|
+
const provider = pattern.slice(0, slashIdx).toLowerCase();
|
|
171
|
+
const modelId = pattern.slice(slashIdx + 1).toLowerCase();
|
|
172
|
+
const exact = available.find(
|
|
173
|
+
m => m.provider.toLowerCase() === provider && m.id.toLowerCase() === modelId,
|
|
174
|
+
);
|
|
175
|
+
if (exact) {
|
|
176
|
+
allowed.add(modelKey(exact));
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
|