@nredd/pi-subagents 0.0.0-stage → 0.22.0-nredd.10
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/LICENSE +21 -0
- package/README.md +1444 -2
- package/dist/chunks/chunk-MSGGFB5Q.ts +6755 -0
- package/dist/chunks/chunk-MSGGFB5Q.ts.map +7 -0
- package/dist/chunks/conversation-viewer-A7AMRDVY.ts +15 -0
- package/dist/chunks/conversation-viewer-A7AMRDVY.ts.map +7 -0
- package/dist/index.ts +9874 -0
- package/dist/index.ts.map +7 -0
- package/package.json +82 -4
- package/src/abortable.ts +43 -0
- package/src/agent-color.ts +161 -0
- package/src/agent-file-toggle.ts +298 -0
- package/src/agent-manager.ts +2431 -0
- package/src/agent-runner.ts +1703 -0
- package/src/agent-source-badge.ts +75 -0
- package/src/agent-types.ts +346 -0
- package/src/child-context.ts +33 -0
- package/src/clock.ts +25 -0
- package/src/completion-outbox.ts +144 -0
- package/src/context.ts +58 -0
- package/src/cross-extension-rpc.ts +200 -0
- package/src/custom-agents.ts +441 -0
- package/src/default-agents.ts +126 -0
- package/src/detached-results.ts +150 -0
- package/src/enabled-models.ts +181 -0
- package/src/enterprise-local.ts +67 -0
- package/src/env.ts +33 -0
- package/src/error-message.ts +11 -0
- package/src/git-path.ts +66 -0
- package/src/group-join.ts +142 -0
- package/src/home-paths.ts +27 -0
- package/src/index.ts +5047 -0
- package/src/invocation-config.ts +158 -0
- package/src/memory.ts +179 -0
- package/src/mention-clone.ts +200 -0
- package/src/mention.ts +141 -0
- package/src/model-call.ts +83 -0
- package/src/model-resolver.ts +333 -0
- package/src/model-scope.ts +71 -0
- package/src/model-selection.ts +170 -0
- package/src/model-tiers.ts +176 -0
- package/src/nested-tools.ts +512 -0
- package/src/notification-result.ts +118 -0
- package/src/output-file.ts +186 -0
- package/src/package-resources.ts +639 -0
- package/src/plan-mode-children.ts +176 -0
- package/src/process-budget.ts +346 -0
- package/src/prompts.ts +161 -0
- package/src/quota-admission.ts +121 -0
- package/src/resource-loader.ts +65 -0
- package/src/schedule-store.ts +153 -0
- package/src/schedule.ts +514 -0
- package/src/settings.ts +851 -0
- package/src/skill-loader.ts +288 -0
- package/src/stale-ctx.ts +16 -0
- package/src/startup-error.ts +29 -0
- package/src/status-note.ts +113 -0
- package/src/structured-output.ts +130 -0
- package/src/types.ts +487 -0
- package/src/ui/agent-mention.ts +219 -0
- package/src/ui/agent-row.ts +245 -0
- package/src/ui/agent-widget.ts +722 -0
- package/src/ui/conversation-viewer.ts +837 -0
- package/src/ui/fallback-flag.ts +114 -0
- package/src/ui/fleet-list.ts +1020 -0
- package/src/ui/fleet-state.ts +154 -0
- package/src/ui/notification-view.ts +139 -0
- package/src/ui/schedule-menu.ts +105 -0
- package/src/ui/select-item.ts +45 -0
- package/src/ui/viewer-keys.ts +39 -0
- package/src/ui/workflow-card.ts +459 -0
- package/src/ui/workflow-dialog.ts +1138 -0
- package/src/ui/workflow-menu.ts +193 -0
- package/src/usage.ts +167 -0
- package/src/workflow/collisions.ts +123 -0
- package/src/workflow/entry.ts +47 -0
- package/src/workflow/host.ts +398 -0
- package/src/workflow/journal.ts +176 -0
- package/src/workflow/json-schema.ts +129 -0
- package/src/workflow/meta.ts +329 -0
- package/src/workflow/progress.ts +554 -0
- package/src/workflow/runtime.ts +1249 -0
- package/src/workflow/saved.ts +259 -0
- package/src/workflow/task.ts +317 -0
- package/src/workflow/tool-description.ts +200 -0
- package/src/workflow/worker-source.ts +903 -0
- package/src/worktree.ts +520 -0
- package/src/xml.ts +13 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/** Exact, provider-neutral model tiers. Router profiles retain ownership of failover. */
|
|
2
|
+
import type { Api, Model } from "@earendil-works/pi-ai";
|
|
3
|
+
import type { ModelRegistry } from "./model-resolver.js";
|
|
4
|
+
import type { QuotaBlock } from "./quota-admission.js";
|
|
5
|
+
|
|
6
|
+
/** One registered router profile OR an ordered exact physical chain, never both. */
|
|
7
|
+
export type TierBinding =
|
|
8
|
+
| { kind: "routed"; tier: string; profile: string }
|
|
9
|
+
| { kind: "static"; tier: string; members: string[] };
|
|
10
|
+
|
|
11
|
+
/** Why an earlier static member could not be selected. */
|
|
12
|
+
interface TierSkip {
|
|
13
|
+
ref: string;
|
|
14
|
+
why: "no-credentials" | "quota";
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Resolution is explicit even when no model can run; callers never guess a neighbouring tier. */
|
|
18
|
+
export type TierOutcome =
|
|
19
|
+
| { kind: "routed"; tier: string; model: Model<Api>; profile: string; admitted?: string; answered?: string }
|
|
20
|
+
| { kind: "static"; tier: string; model: Model<Api>; member: string; skipped: TierSkip[]; quotaSignal: boolean; answered?: string }
|
|
21
|
+
| { kind: "unmapped"; tier: string }
|
|
22
|
+
| { kind: "unavailable"; tier: string; tried: string[] }
|
|
23
|
+
| { kind: "exhausted"; tier: string; block: QuotaBlock; skipped: TierSkip[] };
|
|
24
|
+
|
|
25
|
+
/** Persist identity and decisions, never provider URLs, headers or credential-bearing model overlays. */
|
|
26
|
+
export type TierDisclosure = TierOutcome extends infer Outcome
|
|
27
|
+
? Outcome extends { model: Model<Api> } ? Omit<Outcome, "model"> & { model: Pick<Model<Api>, "provider" | "id" | "name" | "api"> } : Outcome
|
|
28
|
+
: never;
|
|
29
|
+
|
|
30
|
+
/** Project a runtime outcome onto its credential-free persisted shape. */
|
|
31
|
+
export function tierDisclosure(outcome: TierDisclosure): TierDisclosure {
|
|
32
|
+
if (outcome.kind !== "routed" && outcome.kind !== "static") return outcome;
|
|
33
|
+
const { provider, id, name, api } = outcome.model;
|
|
34
|
+
return { ...outcome, model: { provider, id, name, api } };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Trusted policy for configuration failures; quota exhaustion is handled separately. */
|
|
38
|
+
export interface ModelResolution {
|
|
39
|
+
onUnmapped?: "error" | "inherit";
|
|
40
|
+
onUnavailable?: "error" | "inherit";
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Admission metadata includes the physical label and distinguishes fail-open from measured admission. */
|
|
44
|
+
export interface TierAdmission {
|
|
45
|
+
block?: QuotaBlock;
|
|
46
|
+
resolvedModel?: string;
|
|
47
|
+
quotaSignal: boolean;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const TIER_NAME = /^[a-z0-9][a-z0-9._-]*$/;
|
|
51
|
+
const MODEL_REF = /^[^\s/]+\/\S+$/;
|
|
52
|
+
let bindings: Record<string, string[]> = {};
|
|
53
|
+
let policy: ModelResolution = {};
|
|
54
|
+
|
|
55
|
+
/** Reject the entire binding, never silently shorten a malformed chain. */
|
|
56
|
+
export function parseTierBinding(tier: string, members: unknown): TierBinding | undefined {
|
|
57
|
+
if (tier.trim() !== tier || !TIER_NAME.test(tier) || !Array.isArray(members) || members.length === 0
|
|
58
|
+
|| members.some(ref => typeof ref !== "string" || ref.trim() !== ref || !MODEL_REF.test(ref))) return undefined;
|
|
59
|
+
const refs = members as string[];
|
|
60
|
+
if (refs.some(ref => ref.startsWith("router/"))) {
|
|
61
|
+
return refs.length === 1 ? { kind: "routed", tier, profile: refs[0]! } : undefined;
|
|
62
|
+
}
|
|
63
|
+
return { kind: "static", tier, members: [...refs] };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Settings have already been validated at the trusted global boundary. */
|
|
67
|
+
export function setModelTiers(tiers: Record<string, string[]> | undefined, resolution: ModelResolution | undefined): void {
|
|
68
|
+
bindings = Object.fromEntries(Object.entries(tiers ?? {}).map(([key, refs]) => [key, [...refs]]));
|
|
69
|
+
policy = { ...resolution };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Configured tier keys, sorted, for prompt guidance. Never the members: those are profile config. */
|
|
73
|
+
export function configuredTierNames(): string[] {
|
|
74
|
+
return Object.keys(bindings).sort();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Return a snapshot; missing policies mean inherit with disclosure. */
|
|
78
|
+
export function getModelResolution(): ModelResolution {
|
|
79
|
+
return { ...policy };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** A missing or malformed name is unmapped, including bare @ and mixed case. */
|
|
83
|
+
export function tierBinding(tier: string): TierBinding | undefined {
|
|
84
|
+
return parseTierBinding(tier, bindings[tier]);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Exact lookup independent of enabledModels, version ranking and provider retry. */
|
|
88
|
+
export function exactTierModel(ref: string, registry: ModelRegistry): Model<Api> | undefined {
|
|
89
|
+
if (ref.trim() !== ref || !MODEL_REF.test(ref)) return undefined;
|
|
90
|
+
const slash = ref.indexOf("/");
|
|
91
|
+
const found = registry.find(ref.slice(0, slash), ref.slice(slash + 1)) as Model<Api> | undefined;
|
|
92
|
+
if (!found) return undefined;
|
|
93
|
+
if (ref.startsWith("router/")) return found.api === "pi-virtual" ? found : undefined;
|
|
94
|
+
const available = registry.getAvailable?.() ?? registry.getAll();
|
|
95
|
+
return available.some(model => model.provider === found.provider && model.id === found.id) ? found : undefined;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Resolve once per dispatch; a refused routed tier never tries another profile. */
|
|
99
|
+
export async function resolveTier(tier: string, deps: {
|
|
100
|
+
registry: ModelRegistry;
|
|
101
|
+
admit: (ref: string) => Promise<TierAdmission>;
|
|
102
|
+
binding?: TierBinding;
|
|
103
|
+
}): Promise<TierOutcome> {
|
|
104
|
+
const binding = deps.binding ?? tierBinding(tier);
|
|
105
|
+
if (!binding) return { kind: "unmapped", tier };
|
|
106
|
+
if (binding.kind === "routed") {
|
|
107
|
+
const model = exactTierModel(binding.profile, deps.registry);
|
|
108
|
+
if (!model) return { kind: "unavailable", tier, tried: [binding.profile] };
|
|
109
|
+
const admission = await deps.admit(binding.profile);
|
|
110
|
+
if (admission.block) return { kind: "exhausted", tier, block: admission.block, skipped: [] };
|
|
111
|
+
return { kind: "routed", tier, model, profile: binding.profile, admitted: admission.resolvedModel };
|
|
112
|
+
}
|
|
113
|
+
const skipped: TierSkip[] = [];
|
|
114
|
+
const blocks: QuotaBlock[] = [];
|
|
115
|
+
for (const member of binding.members) {
|
|
116
|
+
const model = exactTierModel(member, deps.registry);
|
|
117
|
+
if (!model) {
|
|
118
|
+
skipped.push({ ref: member, why: "no-credentials" });
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
const admission = await deps.admit(member);
|
|
122
|
+
if (admission.block) {
|
|
123
|
+
skipped.push({ ref: member, why: "quota" });
|
|
124
|
+
blocks.push(admission.block);
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
return { kind: "static", tier, model, member, skipped, quotaSignal: admission.quotaSignal };
|
|
128
|
+
}
|
|
129
|
+
if (blocks.length) {
|
|
130
|
+
// Wait only if every refusal permits it. Unknown resets must not invent a wake time.
|
|
131
|
+
const resets = blocks.flatMap(block => block.resetAt === undefined ? [] : [block.resetAt]);
|
|
132
|
+
return { kind: "exhausted", tier, skipped, block: {
|
|
133
|
+
model: `@${tier}`, wait: blocks.every(block => block.wait !== false),
|
|
134
|
+
resetAt: resets.length ? Math.min(...resets) : undefined,
|
|
135
|
+
} };
|
|
136
|
+
}
|
|
137
|
+
return { kind: "unavailable", tier, tried: [...binding.members] };
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** Validate stored provenance without re-resolving a historical request against today's registry. */
|
|
141
|
+
export function isTierDisclosure(raw: unknown): raw is TierDisclosure {
|
|
142
|
+
if (!raw || typeof raw !== "object") return false;
|
|
143
|
+
const value = raw as Record<string, unknown>;
|
|
144
|
+
if (typeof value.tier !== "string") return false;
|
|
145
|
+
if (value.kind === "unmapped") return true;
|
|
146
|
+
if (value.kind === "unavailable") return Array.isArray(value.tried) && value.tried.every(ref => typeof ref === "string");
|
|
147
|
+
const skipsValid = Array.isArray(value.skipped) && value.skipped.every(skip => skip && typeof skip === "object"
|
|
148
|
+
&& typeof skip.ref === "string" && (skip.why === "quota" || skip.why === "no-credentials"));
|
|
149
|
+
if (value.kind === "exhausted") {
|
|
150
|
+
const block = value.block as QuotaBlock | undefined;
|
|
151
|
+
return skipsValid && !!block && typeof block.model === "string"
|
|
152
|
+
&& (block.wait === undefined || typeof block.wait === "boolean")
|
|
153
|
+
&& (block.resetAt === undefined || (typeof block.resetAt === "number" && Number.isFinite(block.resetAt)));
|
|
154
|
+
}
|
|
155
|
+
const model = value.model as Partial<Model<Api>> | undefined;
|
|
156
|
+
if (!model || typeof model.provider !== "string" || typeof model.id !== "string"
|
|
157
|
+
|| typeof model.name !== "string" || typeof model.api !== "string") return false;
|
|
158
|
+
if (value.answered !== undefined && typeof value.answered !== "string") return false;
|
|
159
|
+
if (value.kind === "routed") return typeof value.profile === "string" && value.profile.startsWith("router/")
|
|
160
|
+
&& (value.admitted === undefined || typeof value.admitted === "string");
|
|
161
|
+
return value.kind === "static" && typeof value.member === "string" && skipsValid && typeof value.quotaSignal === "boolean";
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** Durable one-line disclosure, also useful before the first assistant response. */
|
|
165
|
+
export function tierNote(outcome: TierDisclosure | undefined, answering?: string): string {
|
|
166
|
+
if (!outcome) return "";
|
|
167
|
+
const tier = `@${outcome.tier}`;
|
|
168
|
+
const answered = answering ?? ((outcome.kind === "routed" || outcome.kind === "static") ? outcome.answered : undefined);
|
|
169
|
+
switch (outcome.kind) {
|
|
170
|
+
case "routed": return `Model: ${tier} -> ${answered ?? outcome.admitted ?? outcome.profile} via ${outcome.profile}${answered && outcome.admitted && answered !== outcome.admitted ? ` (switched from ${outcome.admitted} within tier)` : ""}`;
|
|
171
|
+
case "static": return `Model: ${tier} -> ${answered ?? outcome.member} (static${outcome.quotaSignal ? "" : "; no quota signal"})${outcome.skipped.length ? `; skipped ${outcome.skipped.map(skip => `${skip.ref}: ${skip.why}`).join(", ")}` : ""}`;
|
|
172
|
+
case "unmapped": return `Note: model tier ${tier} is unmapped; inheriting the parent model.`;
|
|
173
|
+
case "unavailable": return `Note: model tier ${tier} is unavailable; inheriting the parent model.`;
|
|
174
|
+
case "exhausted": return `Model: ${tier} is exhausted.`;
|
|
175
|
+
}
|
|
176
|
+
}
|