pi-crew 0.9.59 → 0.9.60
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/CHANGELOG.md +41 -0
- package/dist/index.mjs +1252 -739
- package/docs/commands-reference.md +5 -0
- package/docs/resource-formats.md +7 -1
- package/package.json +1 -1
- package/skills/real-test-pi-crew/REPORT-TEMPLATE.md +52 -0
- package/skills/real-test-pi-crew/SKILL.md +6 -4
- package/src/config/types.ts +29 -0
- package/src/extension/registration/lifecycle-handlers.ts +33 -0
- package/src/extension/team-tool/chain-dispatch.ts +20 -2
- package/src/extension/team-tool/chain-executor.ts +6 -1
- package/src/extension/team-tool/doctor.ts +43 -0
- package/src/extension/team-tool/run.ts +19 -3
- package/src/extension/team-tool.ts +2 -1
- package/src/runtime/background-runner.ts +26 -0
- package/src/runtime/child-pi/child-pi-spawn.ts +1 -0
- package/src/runtime/child-pi/child-pi.ts +2 -0
- package/src/runtime/live-session/live-session-runtime.ts +83 -16
- package/src/runtime/model/model-fallback.ts +412 -36
- package/src/runtime/model/model-scope.ts +2 -0
- package/src/runtime/model/pi-args.ts +7 -3
- package/src/runtime/model/provider-quota.ts +228 -0
- package/src/runtime/model/session-model.ts +135 -0
- package/src/runtime/task-runner/child-executor.ts +82 -4
- package/src/runtime/task-runner/live-executor.ts +12 -0
- package/src/runtime/task-runner.ts +5 -0
- package/src/runtime/team-runner.ts +18 -4
- package/src/schema/config-schema.ts +12 -0
- package/src/state/types.ts +27 -0
- package/src/teams/discover-teams.ts +16 -2
- package/src/teams/team-config.ts +4 -0
|
@@ -13,7 +13,16 @@ import type { WorkflowStep } from "../../workflows/workflow-config.ts";
|
|
|
13
13
|
import { createIrcTool } from "../custom-tools/irc-tool.ts";
|
|
14
14
|
import { createSubmitResultTool } from "../custom-tools/submit-result-tool.ts";
|
|
15
15
|
import { buildMcpProxyFromSession } from "../mcp-proxy.ts";
|
|
16
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
availableModelInfosFromRegistry,
|
|
18
|
+
buildConfiguredModelRouting,
|
|
19
|
+
type ModelFallbackPolicy,
|
|
20
|
+
modelRefToString,
|
|
21
|
+
providerOfModelRef,
|
|
22
|
+
resolveDefaultSubagentModel,
|
|
23
|
+
resolveModelFallbackPolicy,
|
|
24
|
+
warnOutOfScopeSoft,
|
|
25
|
+
} from "../model/model-fallback.ts";
|
|
17
26
|
import { readEnabledModelsPatterns } from "../model/model-scope.ts";
|
|
18
27
|
import { isLiveSessionRuntimeAvailable } from "../model/runtime-resolver.ts";
|
|
19
28
|
import { awaitRuntimeWarmup } from "../model/runtime-warmup.ts";
|
|
@@ -94,6 +103,8 @@ export interface LiveSessionSpawnInput {
|
|
|
94
103
|
modelRegistry?: unknown;
|
|
95
104
|
modelOverride?: string;
|
|
96
105
|
teamRoleModel?: string;
|
|
106
|
+
teamRoleFallbackModels?: string[];
|
|
107
|
+
teamRoleThinking?: string;
|
|
97
108
|
isCurrent?: () => boolean;
|
|
98
109
|
/** Workspace where this run was initiated — used for session-scoped live-agent visibility. */
|
|
99
110
|
workspaceId: string;
|
|
@@ -111,6 +122,15 @@ export interface LiveSessionRunResult {
|
|
|
111
122
|
error?: string;
|
|
112
123
|
/** Phase 1: Extracted yield result from submit_result tool call. */
|
|
113
124
|
yieldResult?: YieldResult;
|
|
125
|
+
/** Model routing diagnostics for the task state. */
|
|
126
|
+
modelRouting?: {
|
|
127
|
+
requested?: string;
|
|
128
|
+
resolved: string;
|
|
129
|
+
fallbackChain: string[];
|
|
130
|
+
reason?: string;
|
|
131
|
+
droppedRequested?: string;
|
|
132
|
+
autoFallbackCount?: number;
|
|
133
|
+
};
|
|
114
134
|
}
|
|
115
135
|
|
|
116
136
|
export interface LiveSessionUnavailableResult {
|
|
@@ -263,6 +283,24 @@ async function resolveScopeModelsPatterns(cwd: string, agentDir?: string): Promi
|
|
|
263
283
|
return readEnabledModelsPatterns(cwd, agentDir);
|
|
264
284
|
}
|
|
265
285
|
|
|
286
|
+
/** Resolve model fallback policy from crew config + env (best-effort). */
|
|
287
|
+
function resolveLiveModelFallbackPolicy(cwd: string): ModelFallbackPolicy | undefined {
|
|
288
|
+
try {
|
|
289
|
+
return resolveModelFallbackPolicy(loadConfig(cwd).config.runtime?.modelFallback);
|
|
290
|
+
} catch {
|
|
291
|
+
return undefined;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/** Resolve the default subagent model from crew config + env. */
|
|
296
|
+
function resolveLiveDefaultSubagentModel(cwd: string): string | undefined {
|
|
297
|
+
try {
|
|
298
|
+
return resolveDefaultSubagentModel(loadConfig(cwd).config.runtime?.modelFallback);
|
|
299
|
+
} catch {
|
|
300
|
+
return undefined;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
266
304
|
function modelFromRegistry(modelRegistry: unknown, modelId: string | undefined): unknown {
|
|
267
305
|
if (!modelId?.includes("/")) return undefined;
|
|
268
306
|
const registry = asRecord(modelRegistry);
|
|
@@ -292,7 +330,11 @@ function modelFromRegistry(modelRegistry: unknown, modelId: string | undefined):
|
|
|
292
330
|
* raw `parentModel` unchanged so the caller surfaces E008.
|
|
293
331
|
*/
|
|
294
332
|
export function resolveParentModelFromRegistry(modelRegistry: unknown, rawParentModel: unknown): string | undefined {
|
|
295
|
-
|
|
333
|
+
// `ctx.model` is a pi `Model` OBJECT, not a string. The old string-only
|
|
334
|
+
// guard silently discarded it, so every live-session subagent that inherited
|
|
335
|
+
// the parent model landed on `getAvailable()[0]` instead — i.e. the parent
|
|
336
|
+
// model was never actually honoured on this path.
|
|
337
|
+
const raw = modelRefToString(rawParentModel);
|
|
296
338
|
if (raw) {
|
|
297
339
|
const candidate = raw.includes("/")
|
|
298
340
|
? raw
|
|
@@ -305,19 +347,13 @@ export function resolveParentModelFromRegistry(modelRegistry: unknown, rawParent
|
|
|
305
347
|
})();
|
|
306
348
|
if (candidate && modelFromRegistry(modelRegistry, candidate)) return candidate;
|
|
307
349
|
}
|
|
308
|
-
const
|
|
309
|
-
if (
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
return `${first.provider}/${first.id}`;
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
} catch {
|
|
319
|
-
// ignore — fall through to raw
|
|
320
|
-
}
|
|
350
|
+
const available = availableModelInfosFromRegistry(modelRegistry);
|
|
351
|
+
if (available && available.length > 0) {
|
|
352
|
+
// Staying on the parent's provider keeps the same auth + cost profile;
|
|
353
|
+
// only cross providers when the parent's provider has nothing available.
|
|
354
|
+
const parentProvider = providerOfModelRef(raw);
|
|
355
|
+
const sameProvider = parentProvider ? available.find((entry) => entry.provider === parentProvider) : undefined;
|
|
356
|
+
return (sameProvider ?? available[0]).fullId;
|
|
321
357
|
}
|
|
322
358
|
return raw;
|
|
323
359
|
}
|
|
@@ -657,15 +693,38 @@ export async function runLiveSessionTask(input: LiveSessionSpawnInput): Promise<
|
|
|
657
693
|
overrideModel: input.modelOverride,
|
|
658
694
|
stepModel: input.step.model,
|
|
659
695
|
teamRoleModel: input.teamRoleModel,
|
|
696
|
+
teamRoleFallbackModels: input.teamRoleFallbackModels,
|
|
660
697
|
agentModel: input.agent.model,
|
|
698
|
+
defaultSubagentModel: resolveLiveDefaultSubagentModel(input.manifest.cwd),
|
|
661
699
|
fallbackModels: input.agent.fallbackModels,
|
|
662
700
|
parentModel: effectiveParentModel,
|
|
663
701
|
modelRegistry: input.modelRegistry,
|
|
664
702
|
cwd: input.manifest.cwd,
|
|
703
|
+
policy: resolveLiveModelFallbackPolicy(input.manifest.cwd),
|
|
665
704
|
scopeModelsPatterns: await resolveScopeModelsPatterns(input.manifest.cwd),
|
|
666
705
|
});
|
|
667
706
|
const resolvedModel =
|
|
668
707
|
modelFromRegistry(input.modelRegistry, modelRouting.candidates[0] ?? modelRouting.requested) ?? input.parentModel;
|
|
708
|
+
// Surface a warning when the caller's requested model was silently replaced.
|
|
709
|
+
if (modelRouting.droppedRequested) {
|
|
710
|
+
appendEventFireAndForget(input.manifest.eventsPath, {
|
|
711
|
+
type: "task.model_dropped",
|
|
712
|
+
runId: input.manifest.runId,
|
|
713
|
+
taskId: input.task.id,
|
|
714
|
+
message: `Requested model "${modelRouting.droppedRequested}" is not available; using "${modelRouting.candidates[0] ?? "default"}" instead.`,
|
|
715
|
+
data: {
|
|
716
|
+
requested: modelRouting.droppedRequested,
|
|
717
|
+
resolved: modelRouting.candidates[0],
|
|
718
|
+
fallbackChain: modelRouting.candidates,
|
|
719
|
+
},
|
|
720
|
+
});
|
|
721
|
+
}
|
|
722
|
+
// Sec-M1: surface a non-silent warning when a soft-sourced model is out-of-scope.
|
|
723
|
+
// Only non-caller sources (frontmatter / resolved) get the soft warning —
|
|
724
|
+
// caller sources already throw inside buildConfiguredModelRouting.
|
|
725
|
+
warnOutOfScopeSoft(modelRouting.scopeVerdict, "live-session.model-out-of-scope");
|
|
726
|
+
// H1.a: teamRole.thinking takes precedence over agent.thinking.
|
|
727
|
+
const effectiveThinking = input.teamRoleThinking ?? input.agent.thinking;
|
|
669
728
|
// Phase 4: MCP proxy — will be determined after session creation
|
|
670
729
|
// (we check parent's MCP tools and share connections when available)
|
|
671
730
|
const mcpProxy = buildMcpProxyFromSession([], { shareMcp: true });
|
|
@@ -695,7 +754,7 @@ export async function runLiveSessionTask(input: LiveSessionSpawnInput): Promise<
|
|
|
695
754
|
: {}),
|
|
696
755
|
...(input.modelRegistry ? { modelRegistry: input.modelRegistry } : {}),
|
|
697
756
|
...(resolvedModel ? { model: resolvedModel } : {}),
|
|
698
|
-
...(
|
|
757
|
+
...(effectiveThinking ? { thinkingLevel: effectiveThinking } : {}),
|
|
699
758
|
...(mcpProxy.enableMcp ? {} : { enableMCP: false }),
|
|
700
759
|
customTools,
|
|
701
760
|
});
|
|
@@ -1087,6 +1146,14 @@ export async function runLiveSessionTask(input: LiveSessionSpawnInput): Promise<
|
|
|
1087
1146
|
jsonEvents,
|
|
1088
1147
|
usage,
|
|
1089
1148
|
yieldResult,
|
|
1149
|
+
modelRouting: {
|
|
1150
|
+
requested: modelRouting.requested,
|
|
1151
|
+
resolved: modelRefToString(resolvedModel) ?? modelRouting.candidates[0] ?? "default",
|
|
1152
|
+
fallbackChain: modelRouting.candidates,
|
|
1153
|
+
reason: modelRouting.reason,
|
|
1154
|
+
droppedRequested: modelRouting.droppedRequested,
|
|
1155
|
+
autoFallbackCount: modelRouting.autoFallbackCount,
|
|
1156
|
+
},
|
|
1090
1157
|
};
|
|
1091
1158
|
} catch (error) {
|
|
1092
1159
|
const message = error instanceof Error ? error.message : String(error);
|