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
package/dist/index.mjs
CHANGED
|
@@ -5867,16 +5867,16 @@ var init_assert = __esm({
|
|
|
5867
5867
|
init_errors2();
|
|
5868
5868
|
init_error();
|
|
5869
5869
|
init_check();
|
|
5870
|
-
__classPrivateFieldSet = function(receiver,
|
|
5870
|
+
__classPrivateFieldSet = function(receiver, state2, value, kind, f) {
|
|
5871
5871
|
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
5872
5872
|
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
5873
|
-
if (typeof
|
|
5874
|
-
return kind === "a" ? f.call(receiver, value) : f ? f.value = value :
|
|
5873
|
+
if (typeof state2 === "function" ? receiver !== state2 || !f : !state2.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
5874
|
+
return kind === "a" ? f.call(receiver, value) : f ? f.value = value : state2.set(receiver, value), value;
|
|
5875
5875
|
};
|
|
5876
|
-
__classPrivateFieldGet = function(receiver,
|
|
5876
|
+
__classPrivateFieldGet = function(receiver, state2, kind, f) {
|
|
5877
5877
|
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
5878
|
-
if (typeof
|
|
5879
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value :
|
|
5878
|
+
if (typeof state2 === "function" ? receiver !== state2 || !f : !state2.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
5879
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state2.get(receiver);
|
|
5880
5880
|
};
|
|
5881
5881
|
AssertError = class extends TypeBoxError {
|
|
5882
5882
|
constructor(iterator) {
|
|
@@ -8234,7 +8234,7 @@ var init_validation_types = __esm({
|
|
|
8234
8234
|
});
|
|
8235
8235
|
|
|
8236
8236
|
// src/schema/config-schema.ts
|
|
8237
|
-
var PiTeamsAutonomyProfileSchema, PiTeamsAutonomousConfigSchema, PiTeamsLimitsConfigSchema, PiTeamsRuntimeConfigSchema, PiTeamsControlConfigSchema, PiTeamsWorktreeConfigSchema, GoalWrapWorkflowConfigSchema, PiTeamsGoalWrapConfigSchema, AgentOverrideSchema, PiTeamsAgentsConfigSchema, PiTeamsToolsConfigSchema, PiTeamsTelemetryConfigSchema, PiTeamsPolicyConfigSchema, PiTeamsNotificationsConfigSchema, PiTeamsObservabilityConfigSchema, PiTeamsReliabilityConfigSchema, PiTeamsOtlpConfigSchema, PiTeamsUiConfigSchema, CrewBrokerConfigSchema, PiTeamsConfigSchema;
|
|
8237
|
+
var PiTeamsAutonomyProfileSchema, PiTeamsAutonomousConfigSchema, PiTeamsLimitsConfigSchema, PiTeamsModelFallbackConfigSchema, PiTeamsRuntimeConfigSchema, PiTeamsControlConfigSchema, PiTeamsWorktreeConfigSchema, GoalWrapWorkflowConfigSchema, PiTeamsGoalWrapConfigSchema, AgentOverrideSchema, PiTeamsAgentsConfigSchema, PiTeamsToolsConfigSchema, PiTeamsTelemetryConfigSchema, PiTeamsPolicyConfigSchema, PiTeamsNotificationsConfigSchema, PiTeamsObservabilityConfigSchema, PiTeamsReliabilityConfigSchema, PiTeamsOtlpConfigSchema, PiTeamsUiConfigSchema, CrewBrokerConfigSchema, PiTeamsConfigSchema;
|
|
8238
8238
|
var init_config_schema = __esm({
|
|
8239
8239
|
"src/schema/config-schema.ts"() {
|
|
8240
8240
|
"use strict";
|
|
@@ -8272,6 +8272,16 @@ var init_config_schema = __esm({
|
|
|
8272
8272
|
},
|
|
8273
8273
|
{ additionalProperties: false }
|
|
8274
8274
|
);
|
|
8275
|
+
PiTeamsModelFallbackConfigSchema = Type.Object(
|
|
8276
|
+
{
|
|
8277
|
+
maxAutoFallbacks: Type.Optional(Type.Integer({ minimum: 0 })),
|
|
8278
|
+
order: Type.Optional(Type.Union([Type.Literal("parentFirst"), Type.Literal("asIs")])),
|
|
8279
|
+
requireCredentials: Type.Optional(Type.Boolean()),
|
|
8280
|
+
quotaAwareOrdering: Type.Optional(Type.Boolean()),
|
|
8281
|
+
defaultSubagentModel: Type.Optional(Type.String({ minLength: 1 }))
|
|
8282
|
+
},
|
|
8283
|
+
{ additionalProperties: false }
|
|
8284
|
+
);
|
|
8275
8285
|
PiTeamsRuntimeConfigSchema = Type.Object(
|
|
8276
8286
|
{
|
|
8277
8287
|
mode: Type.Optional(
|
|
@@ -8311,7 +8321,8 @@ var init_config_schema = __esm({
|
|
|
8311
8321
|
},
|
|
8312
8322
|
{ additionalProperties: false }
|
|
8313
8323
|
)
|
|
8314
|
-
)
|
|
8324
|
+
),
|
|
8325
|
+
modelFallback: Type.Optional(PiTeamsModelFallbackConfigSchema)
|
|
8315
8326
|
},
|
|
8316
8327
|
{ additionalProperties: false }
|
|
8317
8328
|
);
|
|
@@ -12323,10 +12334,17 @@ function parseRoleLine(line4) {
|
|
|
12323
12334
|
metadata.skills = raw.replace(/\s*,\s*/g, ",").trim();
|
|
12324
12335
|
return "";
|
|
12325
12336
|
});
|
|
12326
|
-
descriptionSource = descriptionSource.replace(/\
|
|
12327
|
-
metadata
|
|
12337
|
+
descriptionSource = descriptionSource.replace(/\bfallbackModels\s*=\s*([\w/.:-]+(?:\s*,\s*[\w/.:-]+)*)/g, (_match, raw) => {
|
|
12338
|
+
metadata.fallbackModels = raw.replace(/\s*,\s*/g, ",").trim();
|
|
12328
12339
|
return "";
|
|
12329
12340
|
});
|
|
12341
|
+
descriptionSource = descriptionSource.replace(
|
|
12342
|
+
/\b(agent|model|thinking|maxConcurrency)\s*=\s*(\S+)/g,
|
|
12343
|
+
(_match, key, raw) => {
|
|
12344
|
+
metadata[key] = raw.trim();
|
|
12345
|
+
return "";
|
|
12346
|
+
}
|
|
12347
|
+
);
|
|
12330
12348
|
const description = descriptionSource.replace(/\s+/g, " ").trim() || void 0;
|
|
12331
12349
|
const maxConcurrency = metadata.maxConcurrency ? (() => {
|
|
12332
12350
|
const p = Number.parseInt(metadata.maxConcurrency, 10);
|
|
@@ -12337,6 +12355,8 @@ function parseRoleLine(line4) {
|
|
|
12337
12355
|
agent: metadata.agent ?? name,
|
|
12338
12356
|
description,
|
|
12339
12357
|
model: metadata.model,
|
|
12358
|
+
fallbackModels: metadata.fallbackModels ? metadata.fallbackModels.split(",").map((s) => s.trim()).filter(Boolean) : void 0,
|
|
12359
|
+
thinking: metadata.thinking?.trim() || void 0,
|
|
12340
12360
|
skills: parseRoleSkills(metadata.skills),
|
|
12341
12361
|
maxConcurrency: maxConcurrency && maxConcurrency > 0 ? maxConcurrency : void 0
|
|
12342
12362
|
};
|
|
@@ -13024,9 +13044,9 @@ var init_cancellation_token = __esm({
|
|
|
13024
13044
|
this.throwIfCancelled();
|
|
13025
13045
|
this.#lastHeartbeatAt = this.#now().toISOString();
|
|
13026
13046
|
this.#lastHeartbeatStage = stage;
|
|
13027
|
-
const
|
|
13028
|
-
this.#onHeartbeat?.(
|
|
13029
|
-
return
|
|
13047
|
+
const state2 = this.state();
|
|
13048
|
+
this.#onHeartbeat?.(state2);
|
|
13049
|
+
return state2;
|
|
13030
13050
|
}
|
|
13031
13051
|
throwIfCancelled() {
|
|
13032
13052
|
if (this.aborted) throw new CrewCancellationError(this.#reason ?? cancellationReasonFromUnknown(this.#controller.signal.reason));
|
|
@@ -13954,12 +13974,13 @@ function buildPiWorkerArgs(input) {
|
|
|
13954
13974
|
const args = ["--mode", "json", "-p"];
|
|
13955
13975
|
if (input.sessionEnabled === false) args.push("--no-session");
|
|
13956
13976
|
const resolvedModel = input.model ?? input.agent.model;
|
|
13977
|
+
const effectiveThinking = input.thinkingOverride ?? input.agent.thinking;
|
|
13957
13978
|
if (resolvedModel) {
|
|
13958
|
-
const modelWithThinking = applyThinkingSuffix(resolvedModel,
|
|
13979
|
+
const modelWithThinking = applyThinkingSuffix(resolvedModel, effectiveThinking);
|
|
13959
13980
|
if (modelWithThinking) args.push("--model", modelWithThinking);
|
|
13960
13981
|
}
|
|
13961
|
-
if (!resolvedModel &&
|
|
13962
|
-
args.push("--thinking",
|
|
13982
|
+
if (!resolvedModel && effectiveThinking && effectiveThinking !== "off" && isValidThinkingLevel(effectiveThinking)) {
|
|
13983
|
+
args.push("--thinking", effectiveThinking);
|
|
13963
13984
|
}
|
|
13964
13985
|
const policy = resolveToolPolicy(input.agent, input.role);
|
|
13965
13986
|
const explicitTools = policy.tools;
|
|
@@ -14925,7 +14946,8 @@ function prepareSpawnContext(input, effectiveTask) {
|
|
|
14925
14946
|
sessionEnabled: true,
|
|
14926
14947
|
maxDepth: input.maxDepth,
|
|
14927
14948
|
skillPaths: input.skillPaths,
|
|
14928
|
-
role: input.role
|
|
14949
|
+
role: input.role,
|
|
14950
|
+
thinkingOverride: input.thinkingOverride
|
|
14929
14951
|
});
|
|
14930
14952
|
if (input.steeringFile) built.env.PI_CREW_STEERING_FILE = input.steeringFile;
|
|
14931
14953
|
if (input.brokerSpawn?.socketPath && input.brokerSpawn.token) {
|
|
@@ -17843,45 +17865,45 @@ function readJsonlTail(filePath, tailBytes) {
|
|
|
17843
17865
|
}
|
|
17844
17866
|
}
|
|
17845
17867
|
}
|
|
17846
|
-
function readLinesSince(filePath,
|
|
17868
|
+
function readLinesSince(filePath, state2) {
|
|
17847
17869
|
let fd;
|
|
17848
17870
|
try {
|
|
17849
17871
|
fd = fs24.openSync(filePath, "r");
|
|
17850
17872
|
} catch {
|
|
17851
17873
|
return {
|
|
17852
17874
|
lines: [],
|
|
17853
|
-
state: { byteOffset:
|
|
17875
|
+
state: { byteOffset: state2.byteOffset, lineCount: state2.lineCount },
|
|
17854
17876
|
eof: true
|
|
17855
17877
|
};
|
|
17856
17878
|
}
|
|
17857
17879
|
try {
|
|
17858
17880
|
const stat2 = fs24.fstatSync(fd);
|
|
17859
17881
|
const fileSize = stat2.size;
|
|
17860
|
-
if (fileSize <=
|
|
17882
|
+
if (fileSize <= state2.byteOffset) {
|
|
17861
17883
|
return {
|
|
17862
17884
|
lines: [],
|
|
17863
|
-
state: { byteOffset: fileSize, lineCount:
|
|
17885
|
+
state: { byteOffset: fileSize, lineCount: state2.lineCount },
|
|
17864
17886
|
eof: true
|
|
17865
17887
|
};
|
|
17866
17888
|
}
|
|
17867
|
-
const bytesToRead = fileSize -
|
|
17889
|
+
const bytesToRead = fileSize - state2.byteOffset;
|
|
17868
17890
|
const buf = Buffer.alloc(bytesToRead);
|
|
17869
17891
|
let totalRead = 0;
|
|
17870
17892
|
while (totalRead < bytesToRead) {
|
|
17871
17893
|
const chunkSize = Math.min(CHUNK_SIZE, bytesToRead - totalRead);
|
|
17872
|
-
const bytesRead = fs24.readSync(fd, buf, totalRead, chunkSize,
|
|
17894
|
+
const bytesRead = fs24.readSync(fd, buf, totalRead, chunkSize, state2.byteOffset + totalRead);
|
|
17873
17895
|
if (bytesRead === 0) break;
|
|
17874
17896
|
totalRead += bytesRead;
|
|
17875
17897
|
}
|
|
17876
17898
|
const content = buf.toString("utf-8", 0, totalRead);
|
|
17877
17899
|
const lines = [];
|
|
17878
|
-
let lineCount =
|
|
17879
|
-
let committedOffset =
|
|
17900
|
+
let lineCount = state2.lineCount;
|
|
17901
|
+
let committedOffset = state2.byteOffset;
|
|
17880
17902
|
let searchFrom = 0;
|
|
17881
17903
|
let newlineIdx;
|
|
17882
17904
|
while ((newlineIdx = content.indexOf("\n", searchFrom)) !== -1) {
|
|
17883
17905
|
const lineText = content.slice(searchFrom, newlineIdx);
|
|
17884
|
-
committedOffset =
|
|
17906
|
+
committedOffset = state2.byteOffset + newlineIdx + 1;
|
|
17885
17907
|
searchFrom = newlineIdx + 1;
|
|
17886
17908
|
if (lineText.length > 0) {
|
|
17887
17909
|
lines.push(lineText);
|
|
@@ -17903,8 +17925,8 @@ function readLinesSince(filePath, state) {
|
|
|
17903
17925
|
}
|
|
17904
17926
|
}
|
|
17905
17927
|
}
|
|
17906
|
-
function readJsonlSince(filePath,
|
|
17907
|
-
const result4 = readLinesSince(filePath,
|
|
17928
|
+
function readJsonlSince(filePath, state2) {
|
|
17929
|
+
const result4 = readLinesSince(filePath, state2);
|
|
17908
17930
|
const items = [];
|
|
17909
17931
|
for (const line4 of result4.lines) {
|
|
17910
17932
|
try {
|
|
@@ -20969,12 +20991,12 @@ function readDeliveryState(manifest) {
|
|
|
20969
20991
|
if (obj.messages && typeof obj.messages === "object" && !Array.isArray(obj.messages)) {
|
|
20970
20992
|
for (const [id, status] of Object.entries(obj.messages)) if (isStatus(status)) messages[id] = status;
|
|
20971
20993
|
}
|
|
20972
|
-
const
|
|
20994
|
+
const state2 = {
|
|
20973
20995
|
messages,
|
|
20974
20996
|
updatedAt: typeof obj.updatedAt === "string" ? obj.updatedAt : (/* @__PURE__ */ new Date()).toISOString()
|
|
20975
20997
|
};
|
|
20976
|
-
setDeliveryCacheEntry(filePath, { mtimeMs: stat2.mtimeMs, state });
|
|
20977
|
-
return
|
|
20998
|
+
setDeliveryCacheEntry(filePath, { mtimeMs: stat2.mtimeMs, state: state2 });
|
|
20999
|
+
return state2;
|
|
20978
21000
|
} catch (error) {
|
|
20979
21001
|
const quarantinePath = `${filePath}.corrupt-${Date.now()}`;
|
|
20980
21002
|
try {
|
|
@@ -20989,25 +21011,25 @@ function readDeliveryState(manifest) {
|
|
|
20989
21011
|
return { messages: {}, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
20990
21012
|
}
|
|
20991
21013
|
}
|
|
20992
|
-
function writeDeliveryState(manifest,
|
|
21014
|
+
function writeDeliveryState(manifest, state2, options) {
|
|
20993
21015
|
ensureRunMailbox(manifest);
|
|
20994
21016
|
const MAX_DELIVERY_MESSAGES = 1e4;
|
|
20995
|
-
if (Object.keys(
|
|
20996
|
-
const sorted = Object.entries(
|
|
21017
|
+
if (Object.keys(state2.messages).length > MAX_DELIVERY_MESSAGES) {
|
|
21018
|
+
const sorted = Object.entries(state2.messages).sort(([, a], [, b]) => {
|
|
20997
21019
|
const order = { queued: 0, delivered: 1, acknowledged: 2 };
|
|
20998
21020
|
return (order[a] ?? 3) - (order[b] ?? 3);
|
|
20999
21021
|
});
|
|
21000
21022
|
const trimmed = sorted.slice(0, MAX_DELIVERY_MESSAGES);
|
|
21001
|
-
|
|
21023
|
+
state2.messages = Object.fromEntries(trimmed);
|
|
21002
21024
|
}
|
|
21003
21025
|
const filePath = deliveryFile(manifest, true);
|
|
21004
|
-
atomicWriteFile(filePath, `${JSON.stringify(redactSecrets(
|
|
21026
|
+
atomicWriteFile(filePath, `${JSON.stringify(redactSecrets(state2), null, 2)}
|
|
21005
21027
|
`, {
|
|
21006
21028
|
durability: options?.durability ?? "best-effort"
|
|
21007
21029
|
});
|
|
21008
21030
|
try {
|
|
21009
21031
|
const postStat = fs29.statSync(filePath);
|
|
21010
|
-
setDeliveryCacheEntry(filePath, { mtimeMs: postStat.mtimeMs, state });
|
|
21032
|
+
setDeliveryCacheEntry(filePath, { mtimeMs: postStat.mtimeMs, state: state2 });
|
|
21011
21033
|
} catch {
|
|
21012
21034
|
deliveryCache.delete(filePath);
|
|
21013
21035
|
}
|
|
@@ -21622,6 +21644,713 @@ var init_runtime_resolver = __esm({
|
|
|
21622
21644
|
}
|
|
21623
21645
|
});
|
|
21624
21646
|
|
|
21647
|
+
// src/runtime/model/model-resolver.ts
|
|
21648
|
+
function fuzzyResolveModelId(input, models) {
|
|
21649
|
+
const query = input.toLowerCase();
|
|
21650
|
+
let bestMatch;
|
|
21651
|
+
let bestScore = 0;
|
|
21652
|
+
for (const m of models) {
|
|
21653
|
+
const id = m.id.toLowerCase();
|
|
21654
|
+
const name = (m.name ?? "").toLowerCase();
|
|
21655
|
+
const full = `${m.provider}/${m.id}`.toLowerCase();
|
|
21656
|
+
let score = 0;
|
|
21657
|
+
if (id === query || full === query) {
|
|
21658
|
+
score = 100;
|
|
21659
|
+
} else if (id.includes(query) || full.includes(query)) {
|
|
21660
|
+
score = 60 + query.length / id.length * 30;
|
|
21661
|
+
} else if (name.includes(query)) {
|
|
21662
|
+
score = 40 + query.length / (name.length || 1) * 20;
|
|
21663
|
+
} else if (query.split(/[\s\-/]+/).every((part) => id.includes(part) || name.includes(part) || m.provider.toLowerCase().includes(part))) {
|
|
21664
|
+
score = 20;
|
|
21665
|
+
}
|
|
21666
|
+
if (score > bestScore) {
|
|
21667
|
+
bestScore = score;
|
|
21668
|
+
bestMatch = m;
|
|
21669
|
+
}
|
|
21670
|
+
}
|
|
21671
|
+
return bestMatch && bestScore >= 20 ? `${bestMatch.provider}/${bestMatch.id}` : void 0;
|
|
21672
|
+
}
|
|
21673
|
+
var init_model_resolver = __esm({
|
|
21674
|
+
"src/runtime/model/model-resolver.ts"() {
|
|
21675
|
+
"use strict";
|
|
21676
|
+
}
|
|
21677
|
+
});
|
|
21678
|
+
|
|
21679
|
+
// src/runtime/model/model-scope.ts
|
|
21680
|
+
function patternToRegExp(pattern) {
|
|
21681
|
+
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
|
|
21682
|
+
return new RegExp(`${escaped.replace(/\*/g, ".*")}`, "i");
|
|
21683
|
+
}
|
|
21684
|
+
function matchesModelPattern(modelId, pattern) {
|
|
21685
|
+
if (!modelId || !pattern) return false;
|
|
21686
|
+
const id = modelId.trim();
|
|
21687
|
+
const pat = pattern.trim();
|
|
21688
|
+
if (!id || !pat) return false;
|
|
21689
|
+
if (id.toLowerCase() === pat.toLowerCase()) return true;
|
|
21690
|
+
if (pat.includes("*")) {
|
|
21691
|
+
try {
|
|
21692
|
+
return patternToRegExp(pat).test(id);
|
|
21693
|
+
} catch {
|
|
21694
|
+
return false;
|
|
21695
|
+
}
|
|
21696
|
+
}
|
|
21697
|
+
return id.toLowerCase().includes(pat.toLowerCase());
|
|
21698
|
+
}
|
|
21699
|
+
function checkModelScope(modelId, patterns, source) {
|
|
21700
|
+
if (!modelId) {
|
|
21701
|
+
return {
|
|
21702
|
+
inScope: true,
|
|
21703
|
+
source,
|
|
21704
|
+
model: "",
|
|
21705
|
+
reason: "no model specified"
|
|
21706
|
+
};
|
|
21707
|
+
}
|
|
21708
|
+
if (!patterns || patterns.length === 0) {
|
|
21709
|
+
return { inScope: true, source, model: modelId };
|
|
21710
|
+
}
|
|
21711
|
+
for (const pattern of patterns) {
|
|
21712
|
+
if (matchesModelPattern(modelId, pattern)) {
|
|
21713
|
+
return {
|
|
21714
|
+
inScope: true,
|
|
21715
|
+
source,
|
|
21716
|
+
model: modelId,
|
|
21717
|
+
matchedPattern: pattern
|
|
21718
|
+
};
|
|
21719
|
+
}
|
|
21720
|
+
}
|
|
21721
|
+
return {
|
|
21722
|
+
inScope: false,
|
|
21723
|
+
source,
|
|
21724
|
+
model: modelId,
|
|
21725
|
+
reason: `model "${modelId}" is not in enabledModels allowlist (${patterns.join(", ")})`
|
|
21726
|
+
};
|
|
21727
|
+
}
|
|
21728
|
+
async function readEnabledModelsPatterns(cwd, agentDir) {
|
|
21729
|
+
try {
|
|
21730
|
+
const mod = await import("@earendil-works/pi-coding-agent").catch(() => null);
|
|
21731
|
+
if (!mod) return [];
|
|
21732
|
+
const SettingsManagerCtor = mod.SettingsManager;
|
|
21733
|
+
if (!SettingsManagerCtor?.create) return [];
|
|
21734
|
+
const sm = SettingsManagerCtor.create(cwd, agentDir);
|
|
21735
|
+
const patterns = sm.getEnabledModels?.();
|
|
21736
|
+
return Array.isArray(patterns) ? patterns : [];
|
|
21737
|
+
} catch {
|
|
21738
|
+
return [];
|
|
21739
|
+
}
|
|
21740
|
+
}
|
|
21741
|
+
var init_model_scope = __esm({
|
|
21742
|
+
"src/runtime/model/model-scope.ts"() {
|
|
21743
|
+
"use strict";
|
|
21744
|
+
}
|
|
21745
|
+
});
|
|
21746
|
+
|
|
21747
|
+
// src/runtime/model/provider-quota.ts
|
|
21748
|
+
function headerNumber(headers, ...names) {
|
|
21749
|
+
for (const name of names) {
|
|
21750
|
+
const raw = headers[name] ?? headers[name.toLowerCase()];
|
|
21751
|
+
if (raw === void 0) continue;
|
|
21752
|
+
const parsed = Number.parseInt(raw, 10);
|
|
21753
|
+
if (Number.isFinite(parsed) && parsed >= 0) return parsed;
|
|
21754
|
+
}
|
|
21755
|
+
return void 0;
|
|
21756
|
+
}
|
|
21757
|
+
function headerRaw(headers, ...names) {
|
|
21758
|
+
for (const name of names) {
|
|
21759
|
+
const raw = headers[name] ?? headers[name.toLowerCase()];
|
|
21760
|
+
if (raw !== void 0) return raw;
|
|
21761
|
+
}
|
|
21762
|
+
return void 0;
|
|
21763
|
+
}
|
|
21764
|
+
function parseResetValue(value, nowMs3) {
|
|
21765
|
+
const trimmed = value.trim();
|
|
21766
|
+
if (/^\d+$/.test(trimmed)) {
|
|
21767
|
+
const secs = Number.parseInt(trimmed, 10);
|
|
21768
|
+
if (Number.isFinite(secs) && secs >= 0) return nowMs3 + secs * 1e3;
|
|
21769
|
+
}
|
|
21770
|
+
if (GO_DURATION_RE.test(trimmed)) {
|
|
21771
|
+
let durationMs = 0;
|
|
21772
|
+
for (const seg of trimmed.matchAll(GO_DURATION_SEG_RE)) {
|
|
21773
|
+
const n = Number.parseInt(seg[1], 10);
|
|
21774
|
+
const unit = seg[2];
|
|
21775
|
+
durationMs += unit === "s" ? n * 1e3 : unit === "m" ? n * 6e4 : n * 36e5;
|
|
21776
|
+
}
|
|
21777
|
+
return nowMs3 + durationMs;
|
|
21778
|
+
}
|
|
21779
|
+
const parsed = Date.parse(trimmed);
|
|
21780
|
+
if (Number.isFinite(parsed) && parsed > 0) return parsed;
|
|
21781
|
+
return void 0;
|
|
21782
|
+
}
|
|
21783
|
+
function headerResetMs(headers, nowMs3) {
|
|
21784
|
+
const resetRaw = headerRaw(headers, "x-ratelimit-reset-requests", "x-ratelimit-reset");
|
|
21785
|
+
if (resetRaw !== void 0) {
|
|
21786
|
+
const parsed = parseResetValue(resetRaw, nowMs3);
|
|
21787
|
+
if (parsed !== void 0) return parsed;
|
|
21788
|
+
}
|
|
21789
|
+
const retryRaw = headerRaw(headers, "retry-after");
|
|
21790
|
+
if (retryRaw !== void 0) {
|
|
21791
|
+
return parseResetValue(retryRaw, nowMs3);
|
|
21792
|
+
}
|
|
21793
|
+
return void 0;
|
|
21794
|
+
}
|
|
21795
|
+
function noteProviderResponse(provider, status, headers, nowMs3 = Date.now()) {
|
|
21796
|
+
const key = provider.toLowerCase();
|
|
21797
|
+
const entry = {
|
|
21798
|
+
provider: key,
|
|
21799
|
+
remainingRequests: headerNumber(headers, "x-ratelimit-remaining-requests", "x-ratelimit-remaining"),
|
|
21800
|
+
remainingTokens: headerNumber(headers, "x-ratelimit-remaining-tokens"),
|
|
21801
|
+
resetAtMs: headerResetMs(headers, nowMs3),
|
|
21802
|
+
lastStatus: status,
|
|
21803
|
+
updatedAtMs: nowMs3
|
|
21804
|
+
};
|
|
21805
|
+
quotaCache.set(key, entry);
|
|
21806
|
+
const evictionCutoff = nowMs3 - 2 * QUOTA_TTL_MS;
|
|
21807
|
+
for (const [cacheKey2, cached2] of quotaCache) {
|
|
21808
|
+
if (cached2.updatedAtMs < evictionCutoff) quotaCache.delete(cacheKey2);
|
|
21809
|
+
}
|
|
21810
|
+
}
|
|
21811
|
+
function isProviderDeprioritized(provider, nowMs3 = Date.now()) {
|
|
21812
|
+
const entry = quotaCache.get(provider.toLowerCase());
|
|
21813
|
+
if (!entry) return false;
|
|
21814
|
+
if (nowMs3 - entry.updatedAtMs > QUOTA_TTL_MS) return false;
|
|
21815
|
+
if (entry.lastStatus === 429) return true;
|
|
21816
|
+
if (entry.remainingRequests === 0 || entry.remainingTokens === 0) return true;
|
|
21817
|
+
if (entry.resetAtMs !== void 0 && entry.resetAtMs > nowMs3) {
|
|
21818
|
+
if (entry.remainingRequests !== void 0 && entry.remainingRequests < 5) return true;
|
|
21819
|
+
if (entry.remainingTokens !== void 0 && entry.remainingTokens < 1e3) return true;
|
|
21820
|
+
}
|
|
21821
|
+
return false;
|
|
21822
|
+
}
|
|
21823
|
+
function deprioritizedProviders(providers, nowMs3 = Date.now()) {
|
|
21824
|
+
return providers.filter((p) => isProviderDeprioritized(p, nowMs3));
|
|
21825
|
+
}
|
|
21826
|
+
function providerRankFromQuota(providers, nowMs3 = Date.now()) {
|
|
21827
|
+
const rank = {};
|
|
21828
|
+
for (const provider of providers) {
|
|
21829
|
+
const key = provider.toLowerCase();
|
|
21830
|
+
const entry = quotaCache.get(key);
|
|
21831
|
+
if (!entry || nowMs3 - entry.updatedAtMs > QUOTA_TTL_MS) {
|
|
21832
|
+
rank[key] = Number.MAX_SAFE_INTEGER;
|
|
21833
|
+
continue;
|
|
21834
|
+
}
|
|
21835
|
+
if (isProviderDeprioritized(key, nowMs3)) {
|
|
21836
|
+
rank[key] = Number.MAX_SAFE_INTEGER - 1;
|
|
21837
|
+
continue;
|
|
21838
|
+
}
|
|
21839
|
+
const remaining = entry.remainingRequests ?? entry.remainingTokens;
|
|
21840
|
+
rank[key] = remaining !== void 0 ? Math.max(0, 1e3 - remaining) : Number.MAX_SAFE_INTEGER;
|
|
21841
|
+
}
|
|
21842
|
+
return rank;
|
|
21843
|
+
}
|
|
21844
|
+
function clearProviderQuotaCache() {
|
|
21845
|
+
quotaCache.clear();
|
|
21846
|
+
}
|
|
21847
|
+
var QUOTA_TTL_MS, quotaCache, GO_DURATION_RE, GO_DURATION_SEG_RE;
|
|
21848
|
+
var init_provider_quota = __esm({
|
|
21849
|
+
"src/runtime/model/provider-quota.ts"() {
|
|
21850
|
+
"use strict";
|
|
21851
|
+
QUOTA_TTL_MS = 5 * 60 * 1e3;
|
|
21852
|
+
quotaCache = /* @__PURE__ */ new Map();
|
|
21853
|
+
GO_DURATION_RE = /^(\d+[smh])+$/;
|
|
21854
|
+
GO_DURATION_SEG_RE = /(\d+)([smh])/g;
|
|
21855
|
+
}
|
|
21856
|
+
});
|
|
21857
|
+
|
|
21858
|
+
// src/runtime/model/model-fallback.ts
|
|
21859
|
+
import * as fs31 from "node:fs";
|
|
21860
|
+
import * as os9 from "node:os";
|
|
21861
|
+
import * as path27 from "node:path";
|
|
21862
|
+
function modelInfoFromUnknown(value) {
|
|
21863
|
+
if (typeof value === "string") {
|
|
21864
|
+
const raw = value.trim();
|
|
21865
|
+
if (!raw) return void 0;
|
|
21866
|
+
const slashIdx = raw.indexOf("/");
|
|
21867
|
+
if (slashIdx <= 0) return { provider: "", id: raw, fullId: raw };
|
|
21868
|
+
return {
|
|
21869
|
+
provider: raw.slice(0, slashIdx),
|
|
21870
|
+
id: raw.slice(slashIdx + 1),
|
|
21871
|
+
fullId: raw
|
|
21872
|
+
};
|
|
21873
|
+
}
|
|
21874
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
21875
|
+
const record = value;
|
|
21876
|
+
if (typeof record.provider !== "string" || typeof record.id !== "string") return void 0;
|
|
21877
|
+
return {
|
|
21878
|
+
provider: record.provider,
|
|
21879
|
+
id: record.id,
|
|
21880
|
+
fullId: `${record.provider}/${record.id}`
|
|
21881
|
+
};
|
|
21882
|
+
}
|
|
21883
|
+
function availableModelInfosFromRegistry(registry2) {
|
|
21884
|
+
if (!registry2 || typeof registry2 !== "object" || Array.isArray(registry2)) return void 0;
|
|
21885
|
+
const candidate = registry2;
|
|
21886
|
+
const raw = typeof candidate.getAvailable === "function" ? candidate.getAvailable() : typeof candidate.getAll === "function" ? candidate.getAll() : void 0;
|
|
21887
|
+
if (!Array.isArray(raw)) return void 0;
|
|
21888
|
+
return raw.map(modelInfoFromUnknown).filter((entry) => entry !== void 0);
|
|
21889
|
+
}
|
|
21890
|
+
function modelStringFromUnknown(model) {
|
|
21891
|
+
return modelInfoFromUnknown(model)?.fullId;
|
|
21892
|
+
}
|
|
21893
|
+
function modelRefToString(model) {
|
|
21894
|
+
return modelInfoFromUnknown(model)?.fullId;
|
|
21895
|
+
}
|
|
21896
|
+
function providerOfModelRef(model) {
|
|
21897
|
+
if (!model) return void 0;
|
|
21898
|
+
const slashIdx = model.indexOf("/");
|
|
21899
|
+
return slashIdx > 0 ? model.slice(0, slashIdx) : void 0;
|
|
21900
|
+
}
|
|
21901
|
+
function uniqueModelInfos(models) {
|
|
21902
|
+
const seen = /* @__PURE__ */ new Set();
|
|
21903
|
+
return models.filter((model) => {
|
|
21904
|
+
if (seen.has(model.fullId)) return false;
|
|
21905
|
+
seen.add(model.fullId);
|
|
21906
|
+
return true;
|
|
21907
|
+
});
|
|
21908
|
+
}
|
|
21909
|
+
function readJsonObject(filePath) {
|
|
21910
|
+
try {
|
|
21911
|
+
const parsed = JSON.parse(fs31.readFileSync(filePath, "utf-8"));
|
|
21912
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : void 0;
|
|
21913
|
+
} catch {
|
|
21914
|
+
return void 0;
|
|
21915
|
+
}
|
|
21916
|
+
}
|
|
21917
|
+
function piAgentDir() {
|
|
21918
|
+
const envDir = process.env.PI_CODING_AGENT_DIR?.trim();
|
|
21919
|
+
if (envDir) {
|
|
21920
|
+
if (envDir === "~") return os9.homedir();
|
|
21921
|
+
if (envDir.startsWith("~/")) return path27.join(os9.homedir(), envDir.slice(2));
|
|
21922
|
+
return envDir;
|
|
21923
|
+
}
|
|
21924
|
+
return path27.join(os9.homedir(), ".pi", "agent");
|
|
21925
|
+
}
|
|
21926
|
+
function settingsModelInfo(settings) {
|
|
21927
|
+
if (typeof settings?.defaultProvider !== "string" || typeof settings.defaultModel !== "string") return void 0;
|
|
21928
|
+
return {
|
|
21929
|
+
provider: settings.defaultProvider,
|
|
21930
|
+
id: settings.defaultModel,
|
|
21931
|
+
fullId: `${settings.defaultProvider}/${settings.defaultModel}`
|
|
21932
|
+
};
|
|
21933
|
+
}
|
|
21934
|
+
function modelsJsonInfos(modelsJson) {
|
|
21935
|
+
if (!modelsJson?.providers || typeof modelsJson.providers !== "object" || Array.isArray(modelsJson.providers)) return [];
|
|
21936
|
+
const infos = [];
|
|
21937
|
+
for (const [provider, rawConfig] of Object.entries(modelsJson.providers)) {
|
|
21938
|
+
if (!rawConfig || typeof rawConfig !== "object" || Array.isArray(rawConfig)) continue;
|
|
21939
|
+
const config = rawConfig;
|
|
21940
|
+
if (Array.isArray(config.models)) {
|
|
21941
|
+
for (const rawModel of config.models) {
|
|
21942
|
+
if (!rawModel || typeof rawModel !== "object" || Array.isArray(rawModel)) continue;
|
|
21943
|
+
const id = rawModel.id;
|
|
21944
|
+
if (typeof id === "string") infos.push({ provider, id, fullId: `${provider}/${id}` });
|
|
21945
|
+
}
|
|
21946
|
+
}
|
|
21947
|
+
if (config.modelOverrides && typeof config.modelOverrides === "object" && !Array.isArray(config.modelOverrides)) {
|
|
21948
|
+
for (const id of Object.keys(config.modelOverrides)) infos.push({ provider, id, fullId: `${provider}/${id}` });
|
|
21949
|
+
}
|
|
21950
|
+
}
|
|
21951
|
+
return infos;
|
|
21952
|
+
}
|
|
21953
|
+
function providersWithCredentials(modelsJson, env = process.env) {
|
|
21954
|
+
const providers = /* @__PURE__ */ new Set();
|
|
21955
|
+
const auth = readJsonObject(path27.join(piAgentDir(), "auth.json"));
|
|
21956
|
+
for (const key of Object.keys(auth ?? {})) providers.add(key);
|
|
21957
|
+
if (modelsJson?.providers && typeof modelsJson.providers === "object" && !Array.isArray(modelsJson.providers)) {
|
|
21958
|
+
for (const [provider, rawConfig] of Object.entries(modelsJson.providers)) {
|
|
21959
|
+
if (!rawConfig || typeof rawConfig !== "object" || Array.isArray(rawConfig)) continue;
|
|
21960
|
+
const config = rawConfig;
|
|
21961
|
+
if (typeof config.apiKey === "string" && config.apiKey.trim()) providers.add(provider);
|
|
21962
|
+
if (typeof config.baseUrl === "string" && config.baseUrl.trim()) providers.add(provider);
|
|
21963
|
+
}
|
|
21964
|
+
}
|
|
21965
|
+
for (const key of Object.keys(env)) {
|
|
21966
|
+
const match = /^([A-Z0-9]+(?:_[A-Z0-9]+)*)_API_KEY$/.exec(key);
|
|
21967
|
+
if (match && env[key]?.trim()) providers.add(match[1].toLowerCase().replace(/_/g, "-"));
|
|
21968
|
+
}
|
|
21969
|
+
return providers;
|
|
21970
|
+
}
|
|
21971
|
+
function fileSignature(filePath) {
|
|
21972
|
+
try {
|
|
21973
|
+
const stat2 = fs31.statSync(filePath);
|
|
21974
|
+
return `${stat2.mtimeMs}:${stat2.size}`;
|
|
21975
|
+
} catch {
|
|
21976
|
+
return "-";
|
|
21977
|
+
}
|
|
21978
|
+
}
|
|
21979
|
+
function configuredModelInfosFromPiConfig(cwd, options) {
|
|
21980
|
+
const agentDir = piAgentDir();
|
|
21981
|
+
const globalSettingsPath = path27.join(agentDir, "settings.json");
|
|
21982
|
+
const modelsJsonPath = path27.join(agentDir, "models.json");
|
|
21983
|
+
const authPath = path27.join(agentDir, "auth.json");
|
|
21984
|
+
const projectSettingsPath = cwd ? path27.join(cwd, ".pi", "settings.json") : void 0;
|
|
21985
|
+
const cacheKey2 = `${agentDir}\0${cwd ?? ""}`;
|
|
21986
|
+
const signature = [globalSettingsPath, modelsJsonPath, authPath, ...projectSettingsPath ? [projectSettingsPath] : []].map(fileSignature).join("|");
|
|
21987
|
+
const cached2 = configuredModelCache.get(cacheKey2);
|
|
21988
|
+
if (cached2?.signature === signature) return options?.requireCredentials ? cached2.credentialed : cached2.all;
|
|
21989
|
+
const globalSettings = readJsonObject(globalSettingsPath);
|
|
21990
|
+
const projectSettings = projectSettingsPath ? readJsonObject(projectSettingsPath) : void 0;
|
|
21991
|
+
const effectiveSettings = {
|
|
21992
|
+
...globalSettings ?? {},
|
|
21993
|
+
...projectSettings ?? {}
|
|
21994
|
+
};
|
|
21995
|
+
const defaultModel = settingsModelInfo(effectiveSettings);
|
|
21996
|
+
const modelsJson = readJsonObject(modelsJsonPath);
|
|
21997
|
+
const all = uniqueModelInfos([...defaultModel ? [defaultModel] : [], ...modelsJsonInfos(modelsJson)]);
|
|
21998
|
+
const credentialedProviders = providersWithCredentials(modelsJson);
|
|
21999
|
+
const credentialed = all.filter(
|
|
22000
|
+
(info2) => info2.fullId === defaultModel?.fullId || credentialedProviders.has(info2.provider.toLowerCase())
|
|
22001
|
+
);
|
|
22002
|
+
configuredModelCache.set(cacheKey2, { signature, all, credentialed });
|
|
22003
|
+
if (configuredModelCache.size > 32) {
|
|
22004
|
+
const oldestKey = configuredModelCache.keys().next().value;
|
|
22005
|
+
if (oldestKey !== void 0) configuredModelCache.delete(oldestKey);
|
|
22006
|
+
}
|
|
22007
|
+
return options?.requireCredentials ? credentialed : all;
|
|
22008
|
+
}
|
|
22009
|
+
function splitThinkingSuffix(model) {
|
|
22010
|
+
const colonIdx = model.lastIndexOf(":");
|
|
22011
|
+
if (colonIdx === -1) return { baseModel: model, thinkingSuffix: "" };
|
|
22012
|
+
return {
|
|
22013
|
+
baseModel: model.substring(0, colonIdx),
|
|
22014
|
+
thinkingSuffix: model.substring(colonIdx)
|
|
22015
|
+
};
|
|
22016
|
+
}
|
|
22017
|
+
function resolveModelCandidate(model, availableModels, preferredProvider) {
|
|
22018
|
+
if (!model) return void 0;
|
|
22019
|
+
if (model.includes("/")) return model;
|
|
22020
|
+
if (!availableModels || availableModels.length === 0) return model;
|
|
22021
|
+
const { baseModel, thinkingSuffix } = splitThinkingSuffix(model);
|
|
22022
|
+
const matches = availableModels.filter((entry) => entry.id === baseModel);
|
|
22023
|
+
if (preferredProvider) {
|
|
22024
|
+
const preferredMatch = matches.find((entry) => entry.provider === preferredProvider);
|
|
22025
|
+
if (preferredMatch) return `${preferredMatch.fullId}${thinkingSuffix}`;
|
|
22026
|
+
}
|
|
22027
|
+
if (matches.length !== 1) {
|
|
22028
|
+
const fuzzy = fuzzyResolveModelId(baseModel, availableModels);
|
|
22029
|
+
if (fuzzy) return `${fuzzy}${thinkingSuffix}`;
|
|
22030
|
+
return model;
|
|
22031
|
+
}
|
|
22032
|
+
return `${matches[0].fullId}${thinkingSuffix}`;
|
|
22033
|
+
}
|
|
22034
|
+
function isRetryableModelFailure(error) {
|
|
22035
|
+
if (!error) return false;
|
|
22036
|
+
if (NON_RETRYABLE_MODEL_FAILURE_PATTERNS.some((pattern) => pattern.test(error))) return false;
|
|
22037
|
+
return RETRYABLE_MODEL_FAILURE_PATTERNS.some((pattern) => pattern.test(error));
|
|
22038
|
+
}
|
|
22039
|
+
function formatModelAttemptNote(attempt, nextModel) {
|
|
22040
|
+
const failure = attempt.error?.trim() || `exit ${attempt.exitCode ?? 1}`;
|
|
22041
|
+
return nextModel ? `[fallback] ${attempt.model} failed: ${failure}. Retrying with ${nextModel}.` : `[fallback] ${attempt.model} failed: ${failure}.`;
|
|
22042
|
+
}
|
|
22043
|
+
function buildModelCandidates(primaryModel, fallbackModels, availableModels, preferredProvider) {
|
|
22044
|
+
const seen = /* @__PURE__ */ new Set();
|
|
22045
|
+
const candidates = [];
|
|
22046
|
+
for (const raw of [primaryModel, ...fallbackModels ?? []]) {
|
|
22047
|
+
if (!raw) continue;
|
|
22048
|
+
const normalized = resolveModelCandidate(raw.trim(), availableModels, preferredProvider);
|
|
22049
|
+
if (!normalized || seen.has(normalized)) continue;
|
|
22050
|
+
seen.add(normalized);
|
|
22051
|
+
candidates.push(normalized);
|
|
22052
|
+
}
|
|
22053
|
+
return candidates;
|
|
22054
|
+
}
|
|
22055
|
+
function isAvailableModel(model, availableModels) {
|
|
22056
|
+
if (!availableModels || availableModels.length === 0) return true;
|
|
22057
|
+
const { baseModel } = splitThinkingSuffix(model);
|
|
22058
|
+
if (baseModel.includes("/")) return availableModels.some((entry) => entry.fullId === baseModel);
|
|
22059
|
+
if (availableModels.some((entry) => entry.id === baseModel)) return true;
|
|
22060
|
+
const fuzzy = fuzzyResolveModelId(baseModel, availableModels);
|
|
22061
|
+
return fuzzy !== void 0;
|
|
22062
|
+
}
|
|
22063
|
+
function orderAutoFallbacks(candidates, policy, anchorProvider) {
|
|
22064
|
+
if (!policy || policy.order === "asIs") return candidates;
|
|
22065
|
+
const deprioritized = new Set((policy.deprioritizedProviders ?? []).map((p) => p.toLowerCase()));
|
|
22066
|
+
const rank = policy.providerRank ?? {};
|
|
22067
|
+
const rankFor = (provider) => {
|
|
22068
|
+
if (!provider) return Number.MAX_SAFE_INTEGER;
|
|
22069
|
+
const value = rank[provider] ?? rank[provider.toLowerCase()];
|
|
22070
|
+
return typeof value === "number" ? value : Number.MAX_SAFE_INTEGER;
|
|
22071
|
+
};
|
|
22072
|
+
return candidates.map((model, index) => {
|
|
22073
|
+
const provider = providerOfModelRef(model);
|
|
22074
|
+
return {
|
|
22075
|
+
model,
|
|
22076
|
+
index,
|
|
22077
|
+
exhausted: provider && deprioritized.has(provider.toLowerCase()) ? 1 : 0,
|
|
22078
|
+
anchored: anchorProvider && provider === anchorProvider ? 0 : 1,
|
|
22079
|
+
rank: rankFor(provider)
|
|
22080
|
+
};
|
|
22081
|
+
}).sort((a, b) => a.exhausted - b.exhausted || a.anchored - b.anchored || a.rank - b.rank || a.index - b.index).map((entry) => entry.model);
|
|
22082
|
+
}
|
|
22083
|
+
function resolveModelFallbackPolicy(config, env = process.env) {
|
|
22084
|
+
const envMaxAuto = env.PI_CREW_MAX_AUTO_FALLBACKS;
|
|
22085
|
+
let maxAutoFallbacks;
|
|
22086
|
+
if (envMaxAuto) {
|
|
22087
|
+
const parsed = Number.parseInt(envMaxAuto, 10);
|
|
22088
|
+
if (Number.isFinite(parsed) && parsed >= 0) {
|
|
22089
|
+
maxAutoFallbacks = parsed;
|
|
22090
|
+
} else if (Number.isFinite(parsed)) {
|
|
22091
|
+
logInternalError(
|
|
22092
|
+
"model-fallback.max-auto-fallbacks-invalid",
|
|
22093
|
+
void 0,
|
|
22094
|
+
`PI_CREW_MAX_AUTO_FALLBACKS="${envMaxAuto}" is negative, clamped to 0`,
|
|
22095
|
+
"warn"
|
|
22096
|
+
);
|
|
22097
|
+
maxAutoFallbacks = 0;
|
|
22098
|
+
} else {
|
|
22099
|
+
logInternalError(
|
|
22100
|
+
"model-fallback.max-auto-fallbacks-invalid",
|
|
22101
|
+
void 0,
|
|
22102
|
+
`PI_CREW_MAX_AUTO_FALLBACKS="${envMaxAuto}" is invalid, falling back to config`,
|
|
22103
|
+
"warn"
|
|
22104
|
+
);
|
|
22105
|
+
maxAutoFallbacks = config?.maxAutoFallbacks;
|
|
22106
|
+
}
|
|
22107
|
+
} else {
|
|
22108
|
+
maxAutoFallbacks = config?.maxAutoFallbacks;
|
|
22109
|
+
}
|
|
22110
|
+
const order = env.PI_CREW_MODEL_FALLBACK_ORDER ?? config?.order;
|
|
22111
|
+
const requireCredentials = env.PI_CREW_MODEL_REQUIRE_CREDENTIALS === "1" ? true : env.PI_CREW_MODEL_REQUIRE_CREDENTIALS === "0" ? false : config?.requireCredentials;
|
|
22112
|
+
const quotaAware = config?.quotaAwareOrdering !== false;
|
|
22113
|
+
if (maxAutoFallbacks === void 0 && !order && requireCredentials === void 0 && config?.quotaAwareOrdering === void 0)
|
|
22114
|
+
return void 0;
|
|
22115
|
+
return {
|
|
22116
|
+
...maxAutoFallbacks !== void 0 && Number.isFinite(maxAutoFallbacks) ? { maxAutoFallbacks } : {},
|
|
22117
|
+
...order ? { order } : {},
|
|
22118
|
+
...requireCredentials !== void 0 ? { requireCredentials } : {},
|
|
22119
|
+
quotaAwareOrdering: quotaAware
|
|
22120
|
+
};
|
|
22121
|
+
}
|
|
22122
|
+
function resolveDefaultSubagentModel(config, env = process.env) {
|
|
22123
|
+
return env.PI_CREW_MODEL?.trim() || config?.defaultSubagentModel?.trim() || void 0;
|
|
22124
|
+
}
|
|
22125
|
+
function buildConfiguredModelRouting(input) {
|
|
22126
|
+
const registryModels = availableModelInfosFromRegistry(input.modelRegistry);
|
|
22127
|
+
const configModels = configuredModelInfosFromPiConfig(input.cwd, {
|
|
22128
|
+
requireCredentials: input.policy?.requireCredentials
|
|
22129
|
+
});
|
|
22130
|
+
const availableModels = registryModels && registryModels.length > 0 ? registryModels : configModels.length > 0 ? configModels : registryModels;
|
|
22131
|
+
const parentModel = modelStringFromUnknown(input.parentModel);
|
|
22132
|
+
const preferredProvider = providerOfModelRef(parentModel) ?? availableModels?.[0]?.provider;
|
|
22133
|
+
const defaultSubagent = input.defaultSubagentModel?.trim() || void 0;
|
|
22134
|
+
const effectiveAgentModel = input.agentModel?.trim() ? input.agentModel : defaultSubagent ?? parentModel;
|
|
22135
|
+
const requested = [input.overrideModel, input.stepModel, input.teamRoleModel, effectiveAgentModel].find(
|
|
22136
|
+
(model) => Boolean(model?.trim())
|
|
22137
|
+
);
|
|
22138
|
+
if (availableModels && availableModels.length === 0)
|
|
22139
|
+
return {
|
|
22140
|
+
requested,
|
|
22141
|
+
candidates: [],
|
|
22142
|
+
reason: "no configured Pi models available"
|
|
22143
|
+
};
|
|
22144
|
+
const declaredRaw = [
|
|
22145
|
+
input.overrideModel,
|
|
22146
|
+
input.stepModel,
|
|
22147
|
+
input.teamRoleModel,
|
|
22148
|
+
effectiveAgentModel,
|
|
22149
|
+
// When defaultSubagentModel replaced parentModel as the effective agent
|
|
22150
|
+
// model, keep parentModel as an explicit fallback before the auto tail.
|
|
22151
|
+
...defaultSubagent && !input.agentModel?.trim() ? [parentModel] : [],
|
|
22152
|
+
...input.teamRoleFallbackModels ?? [],
|
|
22153
|
+
...input.fallbackModels ?? []
|
|
22154
|
+
];
|
|
22155
|
+
const parentModelRaw = effectiveAgentModel?.trim() || void 0;
|
|
22156
|
+
const parentModelFallback = defaultSubagent && !input.agentModel?.trim() ? parentModel : void 0;
|
|
22157
|
+
const declaredModels = declaredRaw.filter((model) => Boolean(model?.trim())).filter((model, idx) => {
|
|
22158
|
+
if (parentModelRaw && idx === 0 && model.trim() === parentModelRaw) return true;
|
|
22159
|
+
if (parentModelFallback && model.trim() === parentModelFallback) return true;
|
|
22160
|
+
return isAvailableModel(model.trim(), availableModels);
|
|
22161
|
+
});
|
|
22162
|
+
const declaredCandidates = buildModelCandidates(declaredModels[0], declaredModels.slice(1), availableModels, preferredProvider);
|
|
22163
|
+
const autoRaw = availableModels ? availableModels.map((model) => model.fullId) : parentModel ? [parentModel] : [];
|
|
22164
|
+
const declaredSet = new Set(declaredCandidates);
|
|
22165
|
+
const autoResolved = buildModelCandidates(void 0, autoRaw, availableModels, preferredProvider).filter(
|
|
22166
|
+
(candidate) => !declaredSet.has(candidate)
|
|
22167
|
+
);
|
|
22168
|
+
const anchorProvider = providerOfModelRef(declaredCandidates[0]) ?? providerOfModelRef(parentModel);
|
|
22169
|
+
let effectivePolicy = input.policy;
|
|
22170
|
+
if (effectivePolicy?.quotaAwareOrdering !== false && autoResolved.length > 0) {
|
|
22171
|
+
const tailProviders = [...new Set(autoResolved.map((m) => providerOfModelRef(m)).filter((p) => Boolean(p)))];
|
|
22172
|
+
const deprioritized = deprioritizedProviders(tailProviders);
|
|
22173
|
+
const rank = providerRankFromQuota(tailProviders);
|
|
22174
|
+
if (deprioritized.length > 0 || Object.keys(rank).length > 0) {
|
|
22175
|
+
effectivePolicy = {
|
|
22176
|
+
...effectivePolicy,
|
|
22177
|
+
deprioritizedProviders: [...effectivePolicy?.deprioritizedProviders ?? [], ...deprioritized],
|
|
22178
|
+
providerRank: { ...rank, ...effectivePolicy?.providerRank ?? {} }
|
|
22179
|
+
};
|
|
22180
|
+
}
|
|
22181
|
+
}
|
|
22182
|
+
const autoOrdered = orderAutoFallbacks(autoResolved, effectivePolicy, anchorProvider);
|
|
22183
|
+
const autoCandidates = effectivePolicy?.maxAutoFallbacks === void 0 ? autoOrdered : autoOrdered.slice(0, Math.max(0, effectivePolicy.maxAutoFallbacks));
|
|
22184
|
+
const candidates = [...declaredCandidates, ...autoCandidates];
|
|
22185
|
+
const resolvedRequested = requested ? resolveModelCandidate(requested, availableModels, preferredProvider) : void 0;
|
|
22186
|
+
const droppedRequested = requested && candidates[0] && resolvedRequested !== candidates[0] ? requested : void 0;
|
|
22187
|
+
const reason = droppedRequested ? "requested model unavailable; selected configured Pi fallback" : candidates.length > 1 ? "configured Pi fallback chain" : void 0;
|
|
22188
|
+
let scopeVerdict;
|
|
22189
|
+
if (input.scopeModelsPatterns && input.scopeModelsPatterns.length > 0) {
|
|
22190
|
+
const resolved = candidates[0] ?? requested;
|
|
22191
|
+
const source = input.isFrontmatterOverride ? "frontmatter" : input.overrideModel ? "caller" : input.stepModel ? "caller" : input.teamRoleModel ? "caller" : input.agentModel?.trim() ? "frontmatter" : "resolved";
|
|
22192
|
+
scopeVerdict = checkModelScope(resolved, input.scopeModelsPatterns, source);
|
|
22193
|
+
if (!scopeVerdict.inScope && source === "caller" && !input.isFrontmatterOverride) {
|
|
22194
|
+
throw errors.modelOutOfScope(resolved ?? "", input.scopeModelsPatterns);
|
|
22195
|
+
}
|
|
22196
|
+
}
|
|
22197
|
+
return {
|
|
22198
|
+
requested,
|
|
22199
|
+
candidates,
|
|
22200
|
+
reason,
|
|
22201
|
+
droppedRequested,
|
|
22202
|
+
autoFallbackCount: autoCandidates.length,
|
|
22203
|
+
scopeVerdict
|
|
22204
|
+
};
|
|
22205
|
+
}
|
|
22206
|
+
function warnOutOfScopeSoft(verdict, scope, prefix = "Model") {
|
|
22207
|
+
if (!verdict || verdict.inScope || verdict.source === "caller") return;
|
|
22208
|
+
logInternalError(
|
|
22209
|
+
scope,
|
|
22210
|
+
void 0,
|
|
22211
|
+
`${prefix} "${verdict.model}" from source "${verdict.source}" is outside enabledModels scope: ${verdict.reason ?? "unknown"}. Running anyway (soft warn).`,
|
|
22212
|
+
"warn"
|
|
22213
|
+
);
|
|
22214
|
+
}
|
|
22215
|
+
var configuredModelCache, RETRYABLE_MODEL_FAILURE_PATTERNS, NON_RETRYABLE_MODEL_FAILURE_PATTERNS;
|
|
22216
|
+
var init_model_fallback = __esm({
|
|
22217
|
+
"src/runtime/model/model-fallback.ts"() {
|
|
22218
|
+
"use strict";
|
|
22219
|
+
init_errors3();
|
|
22220
|
+
init_internal_error();
|
|
22221
|
+
init_model_resolver();
|
|
22222
|
+
init_model_scope();
|
|
22223
|
+
init_provider_quota();
|
|
22224
|
+
configuredModelCache = /* @__PURE__ */ new Map();
|
|
22225
|
+
RETRYABLE_MODEL_FAILURE_PATTERNS = [
|
|
22226
|
+
/rate.?limit/i,
|
|
22227
|
+
/too many requests/i,
|
|
22228
|
+
/\b429\b/,
|
|
22229
|
+
/rate_limit_error/i,
|
|
22230
|
+
/quota/i,
|
|
22231
|
+
/provider.*unavailable/i,
|
|
22232
|
+
/model.*unavailable/i,
|
|
22233
|
+
/model.*disabled/i,
|
|
22234
|
+
/model.*not found/i,
|
|
22235
|
+
/unknown model/i,
|
|
22236
|
+
/overloaded/i,
|
|
22237
|
+
/service unavailable/i,
|
|
22238
|
+
/temporar(?:ily)? unavailable/i,
|
|
22239
|
+
/connection refused/i,
|
|
22240
|
+
/fetch failed/i,
|
|
22241
|
+
/network error/i,
|
|
22242
|
+
/socket hang up/i,
|
|
22243
|
+
/upstream/i,
|
|
22244
|
+
/timed? out/i,
|
|
22245
|
+
/timeout/i,
|
|
22246
|
+
/\b502\b/,
|
|
22247
|
+
/\b503\b/,
|
|
22248
|
+
/\b504\b/,
|
|
22249
|
+
//
|
|
22250
|
+
// Provider-side 5xx / generic api_error. The pi-core retry layer already
|
|
22251
|
+
// retries these (agent-session.ts matches `500|server error|internal error`),
|
|
22252
|
+
// but the pi-crew MODEL FALLBACK layer must ALSO treat them as retryable so
|
|
22253
|
+
// that when the provider is hard-down across all 3 provider retries, we fail
|
|
22254
|
+
// over to the next configured model instead of giving up. Reported case
|
|
22255
|
+
// (2026-06-17): `500 {"type":"error","error":{"type":"api_error",
|
|
22256
|
+
// "message":"unknown error, 999 (1000)"}}` — a transient provider outage that
|
|
22257
|
+
// should trigger the fallback chain, not abort.
|
|
22258
|
+
//
|
|
22259
|
+
// `api_error` is the OpenAI-compatible generic error type (vs rate_limit_error
|
|
22260
|
+
// / overloaded_error / etc.) and almost always means a transient server fault.
|
|
22261
|
+
//
|
|
22262
|
+
// `unknown error` is the body of the generic message; `internal`/`server`
|
|
22263
|
+
// catch the common phrasings. `\b500\b`/`\b501\b` catch the HTTP status in
|
|
22264
|
+
// the rendered error string.
|
|
22265
|
+
/\b500\b/,
|
|
22266
|
+
/\b501\b/,
|
|
22267
|
+
/api_error/i,
|
|
22268
|
+
/unknown error/i,
|
|
22269
|
+
/internal(?:_server)?[ _]error/i,
|
|
22270
|
+
/server error/i,
|
|
22271
|
+
/bad gateway/i,
|
|
22272
|
+
//
|
|
22273
|
+
// Broader retryable patterns (added 2026-06-25, FIX 2):
|
|
22274
|
+
// - `/provider[_ ]?error/i`: OpenAI-compatible "Provider error" generic fault.
|
|
22275
|
+
// - `/context[_ ]?length[_ ]?exceeded/i`: "context_length_exceeded" from
|
|
22276
|
+
// OpenAI/Anthropic — when the configured model is the bottleneck, a
|
|
22277
|
+
// different model in the fallback chain may have a larger window.
|
|
22278
|
+
// - `/safety/i`: Anthropic safety blocks — typically retryable on a
|
|
22279
|
+
// different model in the fallback chain.
|
|
22280
|
+
// - `/is[_ ]?overloaded/i`: alias to the existing `/overloaded/i` pattern
|
|
22281
|
+
// to catch phrasings like "upstream is overloaded".
|
|
22282
|
+
// - `/\b408\b/`: HTTP 408 Request Timeout — transient, provider-side.
|
|
22283
|
+
//
|
|
22284
|
+
// Intentionally NOT added: `/bad_request/` — can mean bad input (e.g.
|
|
22285
|
+
// invalid schema), which is non-retryable.
|
|
22286
|
+
/provider[_ ]?error/i,
|
|
22287
|
+
/context[_ ]?length[_ ]?exceeded/i,
|
|
22288
|
+
/safety/i,
|
|
22289
|
+
/is[_ ]?overloaded/i,
|
|
22290
|
+
/\b408\b/
|
|
22291
|
+
];
|
|
22292
|
+
NON_RETRYABLE_MODEL_FAILURE_PATTERNS = [
|
|
22293
|
+
/auth(?:entication)?/i,
|
|
22294
|
+
/unauthori[sz]ed/i,
|
|
22295
|
+
/forbidden/i,
|
|
22296
|
+
/api key/i,
|
|
22297
|
+
/token expired/i,
|
|
22298
|
+
/invalid key/i,
|
|
22299
|
+
/billing/i,
|
|
22300
|
+
/credit/i
|
|
22301
|
+
];
|
|
22302
|
+
}
|
|
22303
|
+
});
|
|
22304
|
+
|
|
22305
|
+
// src/runtime/model/session-model.ts
|
|
22306
|
+
function noteSessionModel(model, source = "model_select") {
|
|
22307
|
+
const normalized = modelRefToString(model);
|
|
22308
|
+
if (!normalized) return;
|
|
22309
|
+
if (source === "session_start" && state.source === "model_select") return;
|
|
22310
|
+
state.model = normalized;
|
|
22311
|
+
state.source = source;
|
|
22312
|
+
state.updatedAt = Date.now();
|
|
22313
|
+
}
|
|
22314
|
+
function noteSessionThinking(level) {
|
|
22315
|
+
if (typeof level !== "string") return;
|
|
22316
|
+
const value = level.trim();
|
|
22317
|
+
state.thinking = value && value !== "off" ? value : void 0;
|
|
22318
|
+
}
|
|
22319
|
+
function currentSessionModel() {
|
|
22320
|
+
return state.model;
|
|
22321
|
+
}
|
|
22322
|
+
function currentSessionThinking() {
|
|
22323
|
+
return state.thinking;
|
|
22324
|
+
}
|
|
22325
|
+
function resolveParentModel(ctxModel) {
|
|
22326
|
+
return state.model ?? modelRefToString(ctxModel);
|
|
22327
|
+
}
|
|
22328
|
+
function captureRunModelContext(ctx, override) {
|
|
22329
|
+
const parentModel = resolveParentModel(ctx.model);
|
|
22330
|
+
const registryModels = availableModelInfosFromRegistry(ctx.modelRegistry);
|
|
22331
|
+
const availableModels = registryModels && registryModels.length > 0 ? registryModels.map((model) => model.fullId) : void 0;
|
|
22332
|
+
const parentThinking = currentSessionThinking() ?? (typeof ctx.thinkingLevel === "string" ? ctx.thinkingLevel : void 0);
|
|
22333
|
+
const trimmedOverride = override?.trim() || void 0;
|
|
22334
|
+
if (!parentModel && !availableModels && !parentThinking && !trimmedOverride) return void 0;
|
|
22335
|
+
return {
|
|
22336
|
+
...trimmedOverride ? { override: trimmedOverride } : {},
|
|
22337
|
+
...parentModel ? { parentModel } : {},
|
|
22338
|
+
...parentThinking && parentThinking !== "off" ? { parentThinking } : {},
|
|
22339
|
+
...availableModels ? { availableModels } : {}
|
|
22340
|
+
};
|
|
22341
|
+
}
|
|
22342
|
+
function sessionModelSnapshot() {
|
|
22343
|
+
return { ...state };
|
|
22344
|
+
}
|
|
22345
|
+
var state;
|
|
22346
|
+
var init_session_model = __esm({
|
|
22347
|
+
"src/runtime/model/session-model.ts"() {
|
|
22348
|
+
"use strict";
|
|
22349
|
+
init_model_fallback();
|
|
22350
|
+
state = { source: "none" };
|
|
22351
|
+
}
|
|
22352
|
+
});
|
|
22353
|
+
|
|
21625
22354
|
// src/extension/team-tool/config-patch.ts
|
|
21626
22355
|
function sanitizeObject2(obj) {
|
|
21627
22356
|
if (obj === null || obj === void 0 || typeof obj !== "object") return obj;
|
|
@@ -21925,14 +22654,14 @@ var init_i18n = __esm({
|
|
|
21925
22654
|
});
|
|
21926
22655
|
|
|
21927
22656
|
// src/runtime/async-marker.ts
|
|
21928
|
-
import * as
|
|
21929
|
-
import * as
|
|
22657
|
+
import * as fs32 from "node:fs";
|
|
22658
|
+
import * as path28 from "node:path";
|
|
21930
22659
|
function asyncStartMarkerPath(manifest) {
|
|
21931
|
-
return
|
|
22660
|
+
return path28.join(manifest.stateRoot, "async.pid");
|
|
21932
22661
|
}
|
|
21933
22662
|
function hasAsyncStartMarker(manifest) {
|
|
21934
22663
|
try {
|
|
21935
|
-
const raw = JSON.parse(
|
|
22664
|
+
const raw = JSON.parse(fs32.readFileSync(asyncStartMarkerPath(manifest), "utf-8"));
|
|
21936
22665
|
return typeof raw.pid === "number" && Number.isInteger(raw.pid) && raw.pid > 0 && typeof raw.startedAt === "string" && raw.startedAt.length > 0;
|
|
21937
22666
|
} catch {
|
|
21938
22667
|
return false;
|
|
@@ -22045,8 +22774,8 @@ var init_process_status = __esm({
|
|
|
22045
22774
|
});
|
|
22046
22775
|
|
|
22047
22776
|
// src/runtime/run-tracker.ts
|
|
22048
|
-
import * as
|
|
22049
|
-
import * as
|
|
22777
|
+
import * as fs33 from "node:fs";
|
|
22778
|
+
import * as path29 from "node:path";
|
|
22050
22779
|
function registerRunPromise(runId) {
|
|
22051
22780
|
let resolve25;
|
|
22052
22781
|
let reject;
|
|
@@ -22094,8 +22823,8 @@ async function waitForRun(runId, cwd, options = {}) {
|
|
|
22094
22823
|
let attempt = 0;
|
|
22095
22824
|
while (Date.now() < deadline) {
|
|
22096
22825
|
if (attempt === 0) {
|
|
22097
|
-
const runDir =
|
|
22098
|
-
if (!
|
|
22826
|
+
const runDir = path29.join(projectCrewRoot(cwd), "state", "runs", runId);
|
|
22827
|
+
if (!fs33.existsSync(runDir)) {
|
|
22099
22828
|
throw new Error(`Run ${runId} not found. No run directory at ${runDir}`);
|
|
22100
22829
|
}
|
|
22101
22830
|
}
|
|
@@ -22245,13 +22974,13 @@ var init_crew_hooks = __esm({
|
|
|
22245
22974
|
});
|
|
22246
22975
|
|
|
22247
22976
|
// src/runtime/skill-effectiveness.ts
|
|
22248
|
-
import { existsSync as existsSync23, mkdirSync as mkdirSync14, readFileSync as
|
|
22249
|
-
import { dirname as dirname16, join as
|
|
22977
|
+
import { existsSync as existsSync23, mkdirSync as mkdirSync14, readFileSync as readFileSync23, writeFileSync as writeFileSync4 } from "node:fs";
|
|
22978
|
+
import { dirname as dirname16, join as join29 } from "node:path";
|
|
22250
22979
|
function getSkillMetricsPath(cwd, runId) {
|
|
22251
|
-
return
|
|
22980
|
+
return join29(projectCrewRoot(cwd), `state/runs/${runId}/skill-metrics.jsonl`);
|
|
22252
22981
|
}
|
|
22253
22982
|
function getSkillActivationsPath(cwd, runId) {
|
|
22254
|
-
return
|
|
22983
|
+
return join29(projectCrewRoot(cwd), `state/runs/${runId}/skill-activations.jsonl`);
|
|
22255
22984
|
}
|
|
22256
22985
|
function ensureSkillMetricsDir(cwd, runId) {
|
|
22257
22986
|
const dir = dirname16(getSkillMetricsPath(cwd, runId));
|
|
@@ -22308,7 +23037,7 @@ function getSkillActivations(cwd, runId) {
|
|
|
22308
23037
|
if (!existsSync23(path89)) {
|
|
22309
23038
|
return [];
|
|
22310
23039
|
}
|
|
22311
|
-
const content =
|
|
23040
|
+
const content = readFileSync23(path89, "utf-8");
|
|
22312
23041
|
if (!content.trim()) {
|
|
22313
23042
|
return [];
|
|
22314
23043
|
}
|
|
@@ -22486,10 +23215,10 @@ __export(skill_instructions_exports, {
|
|
|
22486
23215
|
resetSkillCacheStats: () => resetSkillCacheStats,
|
|
22487
23216
|
resolveTaskSkillNames: () => resolveTaskSkillNames
|
|
22488
23217
|
});
|
|
22489
|
-
import * as
|
|
22490
|
-
import * as
|
|
23218
|
+
import * as fs34 from "node:fs";
|
|
23219
|
+
import * as path30 from "node:path";
|
|
22491
23220
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
22492
|
-
import * as
|
|
23221
|
+
import * as os10 from "node:os";
|
|
22493
23222
|
function isValidSkillName(name) {
|
|
22494
23223
|
return name.length > 0 && name.length <= MAX_SKILL_NAME_CHARS && isSafePathId(name);
|
|
22495
23224
|
}
|
|
@@ -22539,18 +23268,18 @@ function candidateSkillDirs(cwd) {
|
|
|
22539
23268
|
// F6 (v0.7.9): same five roots as discover-skills, in the same precedence
|
|
22540
23269
|
// order. The first hit wins, so a project `.pi/skills/foo/SKILL.md`
|
|
22541
23270
|
// overrides both the bundled `foo` and any legacy `<cwd>/skills/foo`.
|
|
22542
|
-
{ root:
|
|
23271
|
+
{ root: path30.resolve(cwd, ".pi", "skills"), source: "project-pi" },
|
|
22543
23272
|
{
|
|
22544
|
-
root:
|
|
23273
|
+
root: path30.resolve(cwd, ".agents", "skills"),
|
|
22545
23274
|
source: "project-agents"
|
|
22546
23275
|
},
|
|
22547
|
-
{ root:
|
|
22548
|
-
{ root:
|
|
23276
|
+
{ root: path30.resolve(cwd, "skills"), source: "project" },
|
|
23277
|
+
{ root: path30.join(getAgentDir(), "skills"), source: "user-pi" },
|
|
22549
23278
|
{
|
|
22550
|
-
root:
|
|
23279
|
+
root: path30.join(os10.homedir(), ".agents", "skills"),
|
|
22551
23280
|
source: "user-agents"
|
|
22552
23281
|
},
|
|
22553
|
-
{ root:
|
|
23282
|
+
{ root: path30.join(os10.homedir(), ".pi", "skills"), source: "user-pi" }
|
|
22554
23283
|
];
|
|
22555
23284
|
}
|
|
22556
23285
|
function rememberSkill(key, value) {
|
|
@@ -22588,7 +23317,7 @@ function _setSkillCacheMaxEntriesForTesting(max) {
|
|
|
22588
23317
|
}
|
|
22589
23318
|
function cachedSkillFresh(value) {
|
|
22590
23319
|
try {
|
|
22591
|
-
const stat2 =
|
|
23320
|
+
const stat2 = fs34.statSync(value.path);
|
|
22592
23321
|
return stat2.mtimeMs === value.mtimeMs && stat2.size === value.size;
|
|
22593
23322
|
} catch {
|
|
22594
23323
|
return false;
|
|
@@ -22596,7 +23325,7 @@ function cachedSkillFresh(value) {
|
|
|
22596
23325
|
}
|
|
22597
23326
|
function readSkillMarkdown(cwd, name) {
|
|
22598
23327
|
if (!isValidSkillName(name)) return void 0;
|
|
22599
|
-
const cacheKey2 = `${
|
|
23328
|
+
const cacheKey2 = `${path30.resolve(cwd)}:${name}`;
|
|
22600
23329
|
const cached2 = skillReadCache.get(cacheKey2);
|
|
22601
23330
|
if (cached2 && cachedSkillFresh(cached2)) {
|
|
22602
23331
|
skillCacheStats.hits++;
|
|
@@ -22607,13 +23336,13 @@ function readSkillMarkdown(cwd, name) {
|
|
|
22607
23336
|
skillCacheStats.currentSize = skillReadCache.size;
|
|
22608
23337
|
for (const entry of candidateSkillDirs(cwd)) {
|
|
22609
23338
|
try {
|
|
22610
|
-
const relative9 =
|
|
23339
|
+
const relative9 = path30.join(name, "SKILL.md");
|
|
22611
23340
|
const contained = resolveRealContainedPath(entry.root, relative9);
|
|
22612
|
-
if (!
|
|
22613
|
-
if (
|
|
23341
|
+
if (!fs34.existsSync(contained)) continue;
|
|
23342
|
+
if (fs34.lstatSync(contained).isSymbolicLink()) continue;
|
|
22614
23343
|
const filePath = resolveRealContainedPath(entry.root, relative9);
|
|
22615
|
-
const stat2 =
|
|
22616
|
-
const rawContent =
|
|
23344
|
+
const stat2 = fs34.statSync(filePath);
|
|
23345
|
+
const rawContent = fs34.readFileSync(filePath, "utf-8");
|
|
22617
23346
|
return rememberSkill(cacheKey2, {
|
|
22618
23347
|
path: filePath,
|
|
22619
23348
|
source: entry.source,
|
|
@@ -22682,7 +23411,7 @@ Skill '${safeName}' was selected but no SKILL.md file was found. Continue with t
|
|
|
22682
23411
|
if (!pushSection(missing)) omittedCount += 1;
|
|
22683
23412
|
continue;
|
|
22684
23413
|
}
|
|
22685
|
-
skillPaths.push(
|
|
23414
|
+
skillPaths.push(path30.dirname(loaded.path));
|
|
22686
23415
|
const description = frontmatterDescription(loaded.content);
|
|
22687
23416
|
const source = loaded.source === "project" ? `project:skills/${safeName}` : `package:skills/${safeName}`;
|
|
22688
23417
|
const weighted = weightedSkills?.find((w) => w.skillId === name);
|
|
@@ -22697,7 +23426,7 @@ Skill '${safeName}' was selected but no SKILL.md file was found. Continue with t
|
|
|
22697
23426
|
// spec "small instruction + large local reference" pattern, e.g.
|
|
22698
23427
|
// effective-html's `references/html-effectiveness/`) leave the agent
|
|
22699
23428
|
// guessing the skill dir. No behavior change for corpus-less skills.
|
|
22700
|
-
`Path: ${
|
|
23429
|
+
`Path: ${path30.dirname(loaded.path)}`
|
|
22701
23430
|
].filter(Boolean).join("\n");
|
|
22702
23431
|
const rawContent = loaded.compacted;
|
|
22703
23432
|
const wrappedContent = `<!-- skill: ${safeName} -->
|
|
@@ -22741,7 +23470,7 @@ var init_skill_instructions = __esm({
|
|
|
22741
23470
|
init_safe_paths();
|
|
22742
23471
|
init_skill_effectiveness();
|
|
22743
23472
|
init_peer_dep();
|
|
22744
|
-
PACKAGE_SKILLS_DIR =
|
|
23473
|
+
PACKAGE_SKILLS_DIR = path30.resolve(path30.dirname(fileURLToPath4(import.meta.url)), "..", "..", "skills");
|
|
22745
23474
|
MAX_SKILL_CHARS = 1500;
|
|
22746
23475
|
MAX_TOTAL_CHARS = 6e3;
|
|
22747
23476
|
MAX_SKILL_NAME_CHARS = 80;
|
|
@@ -23641,15 +24370,15 @@ var init_anchor = __esm({
|
|
|
23641
24370
|
});
|
|
23642
24371
|
|
|
23643
24372
|
// src/runtime/agent-observability.ts
|
|
23644
|
-
import * as
|
|
24373
|
+
import * as fs35 from "node:fs";
|
|
23645
24374
|
function readTextTail(filePath, maxBytes = 64e3) {
|
|
23646
|
-
if (!
|
|
23647
|
-
const stat2 =
|
|
24375
|
+
if (!fs35.existsSync(filePath)) return { path: filePath, text: "", bytes: 0, truncated: false };
|
|
24376
|
+
const stat2 = fs35.statSync(filePath);
|
|
23648
24377
|
const bytesToRead = Math.min(stat2.size, Math.max(0, maxBytes));
|
|
23649
|
-
const fd =
|
|
24378
|
+
const fd = fs35.openSync(filePath, "r");
|
|
23650
24379
|
try {
|
|
23651
24380
|
const buffer = Buffer.alloc(bytesToRead);
|
|
23652
|
-
|
|
24381
|
+
fs35.readSync(fd, buffer, 0, bytesToRead, stat2.size - bytesToRead);
|
|
23653
24382
|
return {
|
|
23654
24383
|
path: filePath,
|
|
23655
24384
|
text: buffer.toString("utf-8"),
|
|
@@ -23657,7 +24386,7 @@ function readTextTail(filePath, maxBytes = 64e3) {
|
|
|
23657
24386
|
truncated: stat2.size > bytesToRead
|
|
23658
24387
|
};
|
|
23659
24388
|
} finally {
|
|
23660
|
-
|
|
24389
|
+
fs35.closeSync(fd);
|
|
23661
24390
|
}
|
|
23662
24391
|
}
|
|
23663
24392
|
function compactDuration(ms) {
|
|
@@ -23696,8 +24425,8 @@ function outputWarning(manifest, agent) {
|
|
|
23696
24425
|
if (agent.status !== "completed") return "";
|
|
23697
24426
|
try {
|
|
23698
24427
|
const outputPath = agentOutputPath(manifest, agent.taskId);
|
|
23699
|
-
if (!
|
|
23700
|
-
return
|
|
24428
|
+
if (!fs35.existsSync(outputPath)) return " no-output";
|
|
24429
|
+
return fs35.statSync(outputPath).size === 0 ? " no-output" : "";
|
|
23701
24430
|
} catch {
|
|
23702
24431
|
return " no-output";
|
|
23703
24432
|
}
|
|
@@ -23749,9 +24478,9 @@ var init_agent_observability = __esm({
|
|
|
23749
24478
|
});
|
|
23750
24479
|
|
|
23751
24480
|
// src/skills/validate.ts
|
|
23752
|
-
import * as
|
|
24481
|
+
import * as fs36 from "node:fs";
|
|
23753
24482
|
import { createRequire as createRequire3 } from "node:module";
|
|
23754
|
-
import * as
|
|
24483
|
+
import * as path31 from "node:path";
|
|
23755
24484
|
function getYaml() {
|
|
23756
24485
|
if (!yamlModule) {
|
|
23757
24486
|
const require4 = createRequire3(import.meta.url);
|
|
@@ -23787,11 +24516,11 @@ function warn(path89, field, reason) {
|
|
|
23787
24516
|
}
|
|
23788
24517
|
function validateSkillFrontmatter(skillDir) {
|
|
23789
24518
|
const errors2 = [];
|
|
23790
|
-
const skillMdPath =
|
|
23791
|
-
const derivedName =
|
|
24519
|
+
const skillMdPath = path31.join(skillDir, "SKILL.md");
|
|
24520
|
+
const derivedName = path31.basename(skillDir);
|
|
23792
24521
|
let content;
|
|
23793
24522
|
try {
|
|
23794
|
-
content =
|
|
24523
|
+
content = fs36.readFileSync(skillMdPath, "utf-8");
|
|
23795
24524
|
} catch (e) {
|
|
23796
24525
|
errors2.push(hard(skillDir, "SKILL.md", `Cannot read SKILL.md: ${e.message}`));
|
|
23797
24526
|
return { ok: false, errors: errors2 };
|
|
@@ -23922,25 +24651,25 @@ var init_validate = __esm({
|
|
|
23922
24651
|
});
|
|
23923
24652
|
|
|
23924
24653
|
// src/skills/discover-skills.ts
|
|
23925
|
-
import * as
|
|
23926
|
-
import * as
|
|
23927
|
-
import * as
|
|
24654
|
+
import * as fs37 from "node:fs";
|
|
24655
|
+
import * as os11 from "node:os";
|
|
24656
|
+
import * as path32 from "node:path";
|
|
23928
24657
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
23929
24658
|
function listSkillDirs(cwd) {
|
|
23930
24659
|
return [
|
|
23931
24660
|
{ root: PACKAGE_SKILLS_DIR2, source: "package" },
|
|
23932
|
-
{ root:
|
|
24661
|
+
{ root: path32.resolve(cwd, ".pi", "skills"), source: "project-pi" },
|
|
23933
24662
|
{
|
|
23934
|
-
root:
|
|
24663
|
+
root: path32.resolve(cwd, ".agents", "skills"),
|
|
23935
24664
|
source: "project-agents"
|
|
23936
24665
|
},
|
|
23937
|
-
{ root:
|
|
23938
|
-
{ root:
|
|
24666
|
+
{ root: path32.resolve(cwd, "skills"), source: "project" },
|
|
24667
|
+
{ root: path32.join(getAgentDir(), "skills"), source: "user-pi" },
|
|
23939
24668
|
{
|
|
23940
|
-
root:
|
|
24669
|
+
root: path32.join(os11.homedir(), ".agents", "skills"),
|
|
23941
24670
|
source: "user-agents"
|
|
23942
24671
|
},
|
|
23943
|
-
{ root:
|
|
24672
|
+
{ root: path32.join(os11.homedir(), ".pi", "skills"), source: "user-pi" }
|
|
23944
24673
|
];
|
|
23945
24674
|
}
|
|
23946
24675
|
function readDescription(content) {
|
|
@@ -23965,29 +24694,29 @@ function discoverSkills(cwd) {
|
|
|
23965
24694
|
const results = [];
|
|
23966
24695
|
const diagnostics = [];
|
|
23967
24696
|
for (const dir of listSkillDirs(cwd)) {
|
|
23968
|
-
if (!
|
|
24697
|
+
if (!fs37.existsSync(dir.root)) continue;
|
|
23969
24698
|
try {
|
|
23970
|
-
for (const entry of
|
|
24699
|
+
for (const entry of fs37.readdirSync(dir.root, {
|
|
23971
24700
|
withFileTypes: true
|
|
23972
24701
|
})) {
|
|
23973
24702
|
if (!entry.isDirectory()) continue;
|
|
23974
24703
|
if (!isSafePathId(entry.name)) continue;
|
|
23975
|
-
const skillDirPath =
|
|
24704
|
+
const skillDirPath = path32.join(dir.root, entry.name);
|
|
23976
24705
|
try {
|
|
23977
|
-
if (
|
|
24706
|
+
if (fs37.lstatSync(skillDirPath).isSymbolicLink()) continue;
|
|
23978
24707
|
} catch {
|
|
23979
24708
|
continue;
|
|
23980
24709
|
}
|
|
23981
|
-
const skillMdRelative =
|
|
24710
|
+
const skillMdRelative = path32.join(entry.name, "SKILL.md");
|
|
23982
24711
|
let skillMdPath;
|
|
23983
24712
|
try {
|
|
23984
24713
|
skillMdPath = resolveContainedPath(dir.root, skillMdRelative);
|
|
23985
24714
|
} catch {
|
|
23986
24715
|
continue;
|
|
23987
24716
|
}
|
|
23988
|
-
if (!
|
|
24717
|
+
if (!fs37.existsSync(skillMdPath)) continue;
|
|
23989
24718
|
try {
|
|
23990
|
-
if (
|
|
24719
|
+
if (fs37.lstatSync(skillMdPath).isSymbolicLink()) continue;
|
|
23991
24720
|
} catch {
|
|
23992
24721
|
continue;
|
|
23993
24722
|
}
|
|
@@ -23999,12 +24728,12 @@ function discoverSkills(cwd) {
|
|
|
23999
24728
|
skillMdPath = readPath;
|
|
24000
24729
|
} catch {
|
|
24001
24730
|
}
|
|
24002
|
-
const content =
|
|
24731
|
+
const content = fs37.readFileSync(readPath, "utf-8");
|
|
24003
24732
|
const { description: desc, parseError } = readDescription(content);
|
|
24004
24733
|
description = desc;
|
|
24005
24734
|
if (parseError) {
|
|
24006
24735
|
diagnostics.push({
|
|
24007
|
-
path:
|
|
24736
|
+
path: path32.dirname(skillMdPath),
|
|
24008
24737
|
field: "frontmatter",
|
|
24009
24738
|
reason: parseError,
|
|
24010
24739
|
severity: "error"
|
|
@@ -24026,7 +24755,7 @@ function discoverSkills(cwd) {
|
|
|
24026
24755
|
}
|
|
24027
24756
|
const filtered = [];
|
|
24028
24757
|
for (const skill of results) {
|
|
24029
|
-
const validation = validateSkillFrontmatter(
|
|
24758
|
+
const validation = validateSkillFrontmatter(path32.dirname(skill.path));
|
|
24030
24759
|
if (validation.ok) {
|
|
24031
24760
|
filtered.push(skill);
|
|
24032
24761
|
} else {
|
|
@@ -24045,7 +24774,7 @@ var init_discover_skills = __esm({
|
|
|
24045
24774
|
init_internal_error();
|
|
24046
24775
|
init_safe_paths();
|
|
24047
24776
|
init_validate();
|
|
24048
|
-
PACKAGE_SKILLS_DIR2 =
|
|
24777
|
+
PACKAGE_SKILLS_DIR2 = path32.resolve(path32.dirname(fileURLToPath5(import.meta.url)), "..", "..", "skills");
|
|
24049
24778
|
CACHE_TTL_MS = 3e4;
|
|
24050
24779
|
cache2 = null;
|
|
24051
24780
|
lastDiagnostics = [];
|
|
@@ -24152,15 +24881,15 @@ var init_capability_inventory = __esm({
|
|
|
24152
24881
|
});
|
|
24153
24882
|
|
|
24154
24883
|
// src/runtime/foreground-control.ts
|
|
24155
|
-
import * as
|
|
24156
|
-
import * as
|
|
24884
|
+
import * as fs38 from "node:fs";
|
|
24885
|
+
import * as path33 from "node:path";
|
|
24157
24886
|
function foregroundControlPath(manifest) {
|
|
24158
|
-
return
|
|
24887
|
+
return path33.join(manifest.stateRoot, "foreground-control.json");
|
|
24159
24888
|
}
|
|
24160
24889
|
function readLastRequest(controlPath) {
|
|
24161
|
-
if (!
|
|
24890
|
+
if (!fs38.existsSync(controlPath)) return void 0;
|
|
24162
24891
|
try {
|
|
24163
|
-
const parsed = JSON.parse(
|
|
24892
|
+
const parsed = JSON.parse(fs38.readFileSync(controlPath, "utf-8"));
|
|
24164
24893
|
return parsed.requests?.at(-1);
|
|
24165
24894
|
} catch {
|
|
24166
24895
|
return void 0;
|
|
@@ -24184,7 +24913,7 @@ function readForegroundControlStatus(manifest, tasks) {
|
|
|
24184
24913
|
function writeForegroundInterruptRequest(manifest, reason = "User requested foreground interrupt.") {
|
|
24185
24914
|
const controlPath = foregroundControlPath(manifest);
|
|
24186
24915
|
const lockDir = `${controlPath}.lock`;
|
|
24187
|
-
const pidFile =
|
|
24916
|
+
const pidFile = path33.join(lockDir, "pid");
|
|
24188
24917
|
let requests = [];
|
|
24189
24918
|
const acquireLock = () => {
|
|
24190
24919
|
const timeout = 5e3;
|
|
@@ -24192,7 +24921,7 @@ function writeForegroundInterruptRequest(manifest, reason = "User requested fore
|
|
|
24192
24921
|
const start = Date.now();
|
|
24193
24922
|
while (true) {
|
|
24194
24923
|
try {
|
|
24195
|
-
|
|
24924
|
+
fs38.mkdirSync(lockDir, { recursive: true });
|
|
24196
24925
|
try {
|
|
24197
24926
|
atomicWriteFile(pidFile, String(process.pid));
|
|
24198
24927
|
} catch {
|
|
@@ -24201,7 +24930,7 @@ function writeForegroundInterruptRequest(manifest, reason = "User requested fore
|
|
|
24201
24930
|
} catch {
|
|
24202
24931
|
if (Date.now() - start > timeout) {
|
|
24203
24932
|
try {
|
|
24204
|
-
const raw =
|
|
24933
|
+
const raw = fs38.readFileSync(pidFile, "utf-8").trim();
|
|
24205
24934
|
const ownerPid = Number.parseInt(raw, 10);
|
|
24206
24935
|
if (!Number.isNaN(ownerPid) && ownerPid !== process.pid) {
|
|
24207
24936
|
let alive = false;
|
|
@@ -24211,7 +24940,7 @@ function writeForegroundInterruptRequest(manifest, reason = "User requested fore
|
|
|
24211
24940
|
} catch {
|
|
24212
24941
|
}
|
|
24213
24942
|
if (!alive) {
|
|
24214
|
-
|
|
24943
|
+
fs38.rmSync(lockDir, {
|
|
24215
24944
|
recursive: true,
|
|
24216
24945
|
force: true
|
|
24217
24946
|
});
|
|
@@ -24228,7 +24957,7 @@ function writeForegroundInterruptRequest(manifest, reason = "User requested fore
|
|
|
24228
24957
|
throw err2;
|
|
24229
24958
|
}
|
|
24230
24959
|
try {
|
|
24231
|
-
const raw =
|
|
24960
|
+
const raw = fs38.readFileSync(pidFile, "utf-8").trim();
|
|
24232
24961
|
const ownerPid = Number.parseInt(raw, 10);
|
|
24233
24962
|
if (!Number.isNaN(ownerPid) && ownerPid !== process.pid) {
|
|
24234
24963
|
let alive = false;
|
|
@@ -24238,9 +24967,9 @@ function writeForegroundInterruptRequest(manifest, reason = "User requested fore
|
|
|
24238
24967
|
} catch {
|
|
24239
24968
|
}
|
|
24240
24969
|
if (!alive) {
|
|
24241
|
-
const stat2 =
|
|
24970
|
+
const stat2 = fs38.statSync(lockDir);
|
|
24242
24971
|
if (Date.now() - stat2.mtimeMs > staleMs) {
|
|
24243
|
-
|
|
24972
|
+
fs38.rmSync(lockDir, {
|
|
24244
24973
|
recursive: true,
|
|
24245
24974
|
force: true
|
|
24246
24975
|
});
|
|
@@ -24256,15 +24985,15 @@ function writeForegroundInterruptRequest(manifest, reason = "User requested fore
|
|
|
24256
24985
|
};
|
|
24257
24986
|
const releaseLock2 = () => {
|
|
24258
24987
|
try {
|
|
24259
|
-
|
|
24988
|
+
fs38.rmSync(lockDir, { recursive: true, force: true });
|
|
24260
24989
|
} catch {
|
|
24261
24990
|
}
|
|
24262
24991
|
};
|
|
24263
24992
|
acquireLock();
|
|
24264
24993
|
try {
|
|
24265
|
-
if (
|
|
24994
|
+
if (fs38.existsSync(controlPath)) {
|
|
24266
24995
|
try {
|
|
24267
|
-
const parsed = JSON.parse(
|
|
24996
|
+
const parsed = JSON.parse(fs38.readFileSync(controlPath, "utf-8"));
|
|
24268
24997
|
requests = Array.isArray(parsed.requests) ? parsed.requests : [];
|
|
24269
24998
|
} catch {
|
|
24270
24999
|
requests = [];
|
|
@@ -24277,7 +25006,7 @@ function writeForegroundInterruptRequest(manifest, reason = "User requested fore
|
|
|
24277
25006
|
reason,
|
|
24278
25007
|
acknowledged: false
|
|
24279
25008
|
};
|
|
24280
|
-
|
|
25009
|
+
fs38.mkdirSync(path33.dirname(controlPath), { recursive: true });
|
|
24281
25010
|
atomicWriteFile(controlPath, `${JSON.stringify({ requests: [...requests, request] }, null, 2)}
|
|
24282
25011
|
`);
|
|
24283
25012
|
appendEvent(manifest.eventsPath, {
|
|
@@ -24320,7 +25049,7 @@ var init_worker_heartbeat = __esm({
|
|
|
24320
25049
|
});
|
|
24321
25050
|
|
|
24322
25051
|
// src/runtime/live-session/live-agent-control.ts
|
|
24323
|
-
import * as
|
|
25052
|
+
import * as fs39 from "node:fs";
|
|
24324
25053
|
function liveAgentControlFile(manifest, taskId) {
|
|
24325
25054
|
return agentStateFile(manifest, taskId, "live-control.jsonl");
|
|
24326
25055
|
}
|
|
@@ -24338,7 +25067,7 @@ function appendLiveAgentControlRequest(manifest, input) {
|
|
|
24338
25067
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
24339
25068
|
};
|
|
24340
25069
|
const filePath = liveAgentControlFile(manifest, input.taskId);
|
|
24341
|
-
|
|
25070
|
+
fs39.appendFileSync(filePath, `${JSON.stringify(request)}
|
|
24342
25071
|
`, "utf-8");
|
|
24343
25072
|
return request;
|
|
24344
25073
|
}
|
|
@@ -24349,8 +25078,8 @@ function readLiveAgentControlRequests(manifest, taskId, cursor = { offset: 0 })
|
|
|
24349
25078
|
} catch {
|
|
24350
25079
|
return { requests: [], cursor };
|
|
24351
25080
|
}
|
|
24352
|
-
if (!
|
|
24353
|
-
const text =
|
|
25081
|
+
if (!fs39.existsSync(filePath)) return { requests: [], cursor };
|
|
25082
|
+
const text = fs39.readFileSync(filePath, "utf-8");
|
|
24354
25083
|
const lines = text.split(/\r?\n/).filter(Boolean);
|
|
24355
25084
|
const requests = lines.slice(cursor.offset).flatMap((line4) => {
|
|
24356
25085
|
try {
|
|
@@ -25145,379 +25874,6 @@ var init_mcp_proxy = __esm({
|
|
|
25145
25874
|
}
|
|
25146
25875
|
});
|
|
25147
25876
|
|
|
25148
|
-
// src/runtime/model/model-resolver.ts
|
|
25149
|
-
function fuzzyResolveModelId(input, models) {
|
|
25150
|
-
const query = input.toLowerCase();
|
|
25151
|
-
let bestMatch;
|
|
25152
|
-
let bestScore = 0;
|
|
25153
|
-
for (const m of models) {
|
|
25154
|
-
const id = m.id.toLowerCase();
|
|
25155
|
-
const name = (m.name ?? "").toLowerCase();
|
|
25156
|
-
const full = `${m.provider}/${m.id}`.toLowerCase();
|
|
25157
|
-
let score = 0;
|
|
25158
|
-
if (id === query || full === query) {
|
|
25159
|
-
score = 100;
|
|
25160
|
-
} else if (id.includes(query) || full.includes(query)) {
|
|
25161
|
-
score = 60 + query.length / id.length * 30;
|
|
25162
|
-
} else if (name.includes(query)) {
|
|
25163
|
-
score = 40 + query.length / (name.length || 1) * 20;
|
|
25164
|
-
} else if (query.split(/[\s\-/]+/).every((part) => id.includes(part) || name.includes(part) || m.provider.toLowerCase().includes(part))) {
|
|
25165
|
-
score = 20;
|
|
25166
|
-
}
|
|
25167
|
-
if (score > bestScore) {
|
|
25168
|
-
bestScore = score;
|
|
25169
|
-
bestMatch = m;
|
|
25170
|
-
}
|
|
25171
|
-
}
|
|
25172
|
-
return bestMatch && bestScore >= 20 ? `${bestMatch.provider}/${bestMatch.id}` : void 0;
|
|
25173
|
-
}
|
|
25174
|
-
var init_model_resolver = __esm({
|
|
25175
|
-
"src/runtime/model/model-resolver.ts"() {
|
|
25176
|
-
"use strict";
|
|
25177
|
-
}
|
|
25178
|
-
});
|
|
25179
|
-
|
|
25180
|
-
// src/runtime/model/model-scope.ts
|
|
25181
|
-
function patternToRegExp(pattern) {
|
|
25182
|
-
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
|
|
25183
|
-
return new RegExp(`${escaped.replace(/\*/g, ".*")}`, "i");
|
|
25184
|
-
}
|
|
25185
|
-
function matchesModelPattern(modelId, pattern) {
|
|
25186
|
-
if (!modelId || !pattern) return false;
|
|
25187
|
-
const id = modelId.trim();
|
|
25188
|
-
const pat = pattern.trim();
|
|
25189
|
-
if (!id || !pat) return false;
|
|
25190
|
-
if (id.toLowerCase() === pat.toLowerCase()) return true;
|
|
25191
|
-
if (pat.includes("*")) {
|
|
25192
|
-
try {
|
|
25193
|
-
return patternToRegExp(pat).test(id);
|
|
25194
|
-
} catch {
|
|
25195
|
-
return false;
|
|
25196
|
-
}
|
|
25197
|
-
}
|
|
25198
|
-
return id.toLowerCase().includes(pat.toLowerCase());
|
|
25199
|
-
}
|
|
25200
|
-
function checkModelScope(modelId, patterns, source) {
|
|
25201
|
-
if (!modelId) {
|
|
25202
|
-
return {
|
|
25203
|
-
inScope: true,
|
|
25204
|
-
source,
|
|
25205
|
-
model: "",
|
|
25206
|
-
reason: "no model specified"
|
|
25207
|
-
};
|
|
25208
|
-
}
|
|
25209
|
-
if (!patterns || patterns.length === 0) {
|
|
25210
|
-
return { inScope: true, source, model: modelId };
|
|
25211
|
-
}
|
|
25212
|
-
for (const pattern of patterns) {
|
|
25213
|
-
if (matchesModelPattern(modelId, pattern)) {
|
|
25214
|
-
return {
|
|
25215
|
-
inScope: true,
|
|
25216
|
-
source,
|
|
25217
|
-
model: modelId,
|
|
25218
|
-
matchedPattern: pattern
|
|
25219
|
-
};
|
|
25220
|
-
}
|
|
25221
|
-
}
|
|
25222
|
-
return {
|
|
25223
|
-
inScope: false,
|
|
25224
|
-
source,
|
|
25225
|
-
model: modelId,
|
|
25226
|
-
reason: `model "${modelId}" is not in enabledModels allowlist (${patterns.join(", ")})`
|
|
25227
|
-
};
|
|
25228
|
-
}
|
|
25229
|
-
async function readEnabledModelsPatterns(cwd, agentDir) {
|
|
25230
|
-
try {
|
|
25231
|
-
const mod = await import("@earendil-works/pi-coding-agent").catch(() => null);
|
|
25232
|
-
if (!mod) return [];
|
|
25233
|
-
const SettingsManagerCtor = mod.SettingsManager;
|
|
25234
|
-
if (!SettingsManagerCtor?.create) return [];
|
|
25235
|
-
const sm = SettingsManagerCtor.create(cwd, agentDir);
|
|
25236
|
-
const patterns = sm.getEnabledModels?.();
|
|
25237
|
-
return Array.isArray(patterns) ? patterns : [];
|
|
25238
|
-
} catch {
|
|
25239
|
-
return [];
|
|
25240
|
-
}
|
|
25241
|
-
}
|
|
25242
|
-
var init_model_scope = __esm({
|
|
25243
|
-
"src/runtime/model/model-scope.ts"() {
|
|
25244
|
-
"use strict";
|
|
25245
|
-
}
|
|
25246
|
-
});
|
|
25247
|
-
|
|
25248
|
-
// src/runtime/model/model-fallback.ts
|
|
25249
|
-
import * as fs39 from "node:fs";
|
|
25250
|
-
import * as os11 from "node:os";
|
|
25251
|
-
import * as path33 from "node:path";
|
|
25252
|
-
function modelInfoFromUnknown(value) {
|
|
25253
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
25254
|
-
const record = value;
|
|
25255
|
-
if (typeof record.provider !== "string" || typeof record.id !== "string") return void 0;
|
|
25256
|
-
return {
|
|
25257
|
-
provider: record.provider,
|
|
25258
|
-
id: record.id,
|
|
25259
|
-
fullId: `${record.provider}/${record.id}`
|
|
25260
|
-
};
|
|
25261
|
-
}
|
|
25262
|
-
function availableModelInfosFromRegistry(registry2) {
|
|
25263
|
-
if (!registry2 || typeof registry2 !== "object" || Array.isArray(registry2)) return void 0;
|
|
25264
|
-
const candidate = registry2;
|
|
25265
|
-
const raw = typeof candidate.getAvailable === "function" ? candidate.getAvailable() : typeof candidate.getAll === "function" ? candidate.getAll() : void 0;
|
|
25266
|
-
if (!Array.isArray(raw)) return void 0;
|
|
25267
|
-
return raw.map(modelInfoFromUnknown).filter((entry) => entry !== void 0);
|
|
25268
|
-
}
|
|
25269
|
-
function modelStringFromUnknown(model) {
|
|
25270
|
-
return modelInfoFromUnknown(model)?.fullId;
|
|
25271
|
-
}
|
|
25272
|
-
function uniqueModelInfos(models) {
|
|
25273
|
-
const seen = /* @__PURE__ */ new Set();
|
|
25274
|
-
return models.filter((model) => {
|
|
25275
|
-
if (seen.has(model.fullId)) return false;
|
|
25276
|
-
seen.add(model.fullId);
|
|
25277
|
-
return true;
|
|
25278
|
-
});
|
|
25279
|
-
}
|
|
25280
|
-
function readJsonObject(filePath) {
|
|
25281
|
-
try {
|
|
25282
|
-
const parsed = JSON.parse(fs39.readFileSync(filePath, "utf-8"));
|
|
25283
|
-
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : void 0;
|
|
25284
|
-
} catch {
|
|
25285
|
-
return void 0;
|
|
25286
|
-
}
|
|
25287
|
-
}
|
|
25288
|
-
function piAgentDir() {
|
|
25289
|
-
const envDir = process.env.PI_CODING_AGENT_DIR?.trim();
|
|
25290
|
-
if (envDir) {
|
|
25291
|
-
if (envDir === "~") return os11.homedir();
|
|
25292
|
-
if (envDir.startsWith("~/")) return path33.join(os11.homedir(), envDir.slice(2));
|
|
25293
|
-
return envDir;
|
|
25294
|
-
}
|
|
25295
|
-
return path33.join(os11.homedir(), ".pi", "agent");
|
|
25296
|
-
}
|
|
25297
|
-
function settingsModelInfo(settings) {
|
|
25298
|
-
if (typeof settings?.defaultProvider !== "string" || typeof settings.defaultModel !== "string") return void 0;
|
|
25299
|
-
return {
|
|
25300
|
-
provider: settings.defaultProvider,
|
|
25301
|
-
id: settings.defaultModel,
|
|
25302
|
-
fullId: `${settings.defaultProvider}/${settings.defaultModel}`
|
|
25303
|
-
};
|
|
25304
|
-
}
|
|
25305
|
-
function modelsJsonInfos(modelsJson) {
|
|
25306
|
-
if (!modelsJson?.providers || typeof modelsJson.providers !== "object" || Array.isArray(modelsJson.providers)) return [];
|
|
25307
|
-
const infos = [];
|
|
25308
|
-
for (const [provider, rawConfig] of Object.entries(modelsJson.providers)) {
|
|
25309
|
-
if (!rawConfig || typeof rawConfig !== "object" || Array.isArray(rawConfig)) continue;
|
|
25310
|
-
const config = rawConfig;
|
|
25311
|
-
if (Array.isArray(config.models)) {
|
|
25312
|
-
for (const rawModel of config.models) {
|
|
25313
|
-
if (!rawModel || typeof rawModel !== "object" || Array.isArray(rawModel)) continue;
|
|
25314
|
-
const id = rawModel.id;
|
|
25315
|
-
if (typeof id === "string") infos.push({ provider, id, fullId: `${provider}/${id}` });
|
|
25316
|
-
}
|
|
25317
|
-
}
|
|
25318
|
-
if (config.modelOverrides && typeof config.modelOverrides === "object" && !Array.isArray(config.modelOverrides)) {
|
|
25319
|
-
for (const id of Object.keys(config.modelOverrides)) infos.push({ provider, id, fullId: `${provider}/${id}` });
|
|
25320
|
-
}
|
|
25321
|
-
}
|
|
25322
|
-
return infos;
|
|
25323
|
-
}
|
|
25324
|
-
function configuredModelInfosFromPiConfig(cwd) {
|
|
25325
|
-
const agentDir = piAgentDir();
|
|
25326
|
-
const globalSettings = readJsonObject(path33.join(agentDir, "settings.json"));
|
|
25327
|
-
const projectSettings = cwd ? readJsonObject(path33.join(cwd, ".pi", "settings.json")) : void 0;
|
|
25328
|
-
const effectiveSettings = {
|
|
25329
|
-
...globalSettings ?? {},
|
|
25330
|
-
...projectSettings ?? {}
|
|
25331
|
-
};
|
|
25332
|
-
const defaultModel = settingsModelInfo(effectiveSettings);
|
|
25333
|
-
return uniqueModelInfos([
|
|
25334
|
-
...defaultModel ? [defaultModel] : [],
|
|
25335
|
-
...modelsJsonInfos(readJsonObject(path33.join(agentDir, "models.json")))
|
|
25336
|
-
]);
|
|
25337
|
-
}
|
|
25338
|
-
function splitThinkingSuffix(model) {
|
|
25339
|
-
const colonIdx = model.lastIndexOf(":");
|
|
25340
|
-
if (colonIdx === -1) return { baseModel: model, thinkingSuffix: "" };
|
|
25341
|
-
return {
|
|
25342
|
-
baseModel: model.substring(0, colonIdx),
|
|
25343
|
-
thinkingSuffix: model.substring(colonIdx)
|
|
25344
|
-
};
|
|
25345
|
-
}
|
|
25346
|
-
function resolveModelCandidate(model, availableModels, preferredProvider) {
|
|
25347
|
-
if (!model) return void 0;
|
|
25348
|
-
if (model.includes("/")) return model;
|
|
25349
|
-
if (!availableModels || availableModels.length === 0) return model;
|
|
25350
|
-
const { baseModel, thinkingSuffix } = splitThinkingSuffix(model);
|
|
25351
|
-
const matches = availableModels.filter((entry) => entry.id === baseModel);
|
|
25352
|
-
if (preferredProvider) {
|
|
25353
|
-
const preferredMatch = matches.find((entry) => entry.provider === preferredProvider);
|
|
25354
|
-
if (preferredMatch) return `${preferredMatch.fullId}${thinkingSuffix}`;
|
|
25355
|
-
}
|
|
25356
|
-
if (matches.length !== 1) {
|
|
25357
|
-
const fuzzy = fuzzyResolveModelId(baseModel, availableModels);
|
|
25358
|
-
if (fuzzy) return `${fuzzy}${thinkingSuffix}`;
|
|
25359
|
-
return model;
|
|
25360
|
-
}
|
|
25361
|
-
return `${matches[0].fullId}${thinkingSuffix}`;
|
|
25362
|
-
}
|
|
25363
|
-
function isRetryableModelFailure(error) {
|
|
25364
|
-
if (!error) return false;
|
|
25365
|
-
if (NON_RETRYABLE_MODEL_FAILURE_PATTERNS.some((pattern) => pattern.test(error))) return false;
|
|
25366
|
-
return RETRYABLE_MODEL_FAILURE_PATTERNS.some((pattern) => pattern.test(error));
|
|
25367
|
-
}
|
|
25368
|
-
function formatModelAttemptNote(attempt, nextModel) {
|
|
25369
|
-
const failure = attempt.error?.trim() || `exit ${attempt.exitCode ?? 1}`;
|
|
25370
|
-
return nextModel ? `[fallback] ${attempt.model} failed: ${failure}. Retrying with ${nextModel}.` : `[fallback] ${attempt.model} failed: ${failure}.`;
|
|
25371
|
-
}
|
|
25372
|
-
function buildModelCandidates(primaryModel, fallbackModels, availableModels, preferredProvider) {
|
|
25373
|
-
const seen = /* @__PURE__ */ new Set();
|
|
25374
|
-
const candidates = [];
|
|
25375
|
-
for (const raw of [primaryModel, ...fallbackModels ?? []]) {
|
|
25376
|
-
if (!raw) continue;
|
|
25377
|
-
const normalized = resolveModelCandidate(raw.trim(), availableModels, preferredProvider);
|
|
25378
|
-
if (!normalized || seen.has(normalized)) continue;
|
|
25379
|
-
seen.add(normalized);
|
|
25380
|
-
candidates.push(normalized);
|
|
25381
|
-
}
|
|
25382
|
-
return candidates;
|
|
25383
|
-
}
|
|
25384
|
-
function isAvailableModel(model, availableModels) {
|
|
25385
|
-
if (!availableModels || availableModels.length === 0) return true;
|
|
25386
|
-
const { baseModel } = splitThinkingSuffix(model);
|
|
25387
|
-
if (baseModel.includes("/")) return availableModels.some((entry) => entry.fullId === baseModel);
|
|
25388
|
-
if (availableModels.some((entry) => entry.id === baseModel)) return true;
|
|
25389
|
-
const fuzzy = fuzzyResolveModelId(baseModel, availableModels);
|
|
25390
|
-
return fuzzy !== void 0;
|
|
25391
|
-
}
|
|
25392
|
-
function buildConfiguredModelRouting(input) {
|
|
25393
|
-
const registryModels = availableModelInfosFromRegistry(input.modelRegistry);
|
|
25394
|
-
const configModels = configuredModelInfosFromPiConfig(input.cwd);
|
|
25395
|
-
const availableModels = registryModels && registryModels.length > 0 ? registryModels : configModels.length > 0 ? configModels : registryModels;
|
|
25396
|
-
const parentModel = modelStringFromUnknown(input.parentModel);
|
|
25397
|
-
const preferredProvider = parentModel?.split("/")[0] ?? availableModels?.[0]?.provider;
|
|
25398
|
-
const effectiveAgentModel = input.agentModel?.trim() ? input.agentModel : parentModel;
|
|
25399
|
-
const requested = [input.overrideModel, input.stepModel, input.teamRoleModel, effectiveAgentModel].find(
|
|
25400
|
-
(model) => Boolean(model?.trim())
|
|
25401
|
-
);
|
|
25402
|
-
if (availableModels && availableModels.length === 0)
|
|
25403
|
-
return {
|
|
25404
|
-
requested,
|
|
25405
|
-
candidates: [],
|
|
25406
|
-
reason: "no configured Pi models available"
|
|
25407
|
-
};
|
|
25408
|
-
const rawModels = availableModels ? [
|
|
25409
|
-
input.overrideModel,
|
|
25410
|
-
input.stepModel,
|
|
25411
|
-
input.teamRoleModel,
|
|
25412
|
-
effectiveAgentModel,
|
|
25413
|
-
...input.fallbackModels ?? [],
|
|
25414
|
-
...availableModels.map((model) => model.fullId)
|
|
25415
|
-
] : [input.overrideModel, input.stepModel, input.teamRoleModel, effectiveAgentModel, ...input.fallbackModels ?? [], parentModel];
|
|
25416
|
-
const parentModelRaw = effectiveAgentModel?.trim() || void 0;
|
|
25417
|
-
const configuredModels = rawModels.filter((model) => Boolean(model?.trim())).filter((model, idx) => {
|
|
25418
|
-
if (parentModelRaw && idx === 0 && model.trim() === parentModelRaw) return true;
|
|
25419
|
-
return isAvailableModel(model.trim(), availableModels);
|
|
25420
|
-
});
|
|
25421
|
-
const candidates = buildModelCandidates(configuredModels[0], configuredModels.slice(1), availableModels, preferredProvider);
|
|
25422
|
-
const reason = requested && candidates[0] && resolveModelCandidate(requested, availableModels, preferredProvider) !== candidates[0] ? "requested model unavailable; selected configured Pi fallback" : candidates.length > 1 ? "configured Pi fallback chain" : void 0;
|
|
25423
|
-
let scopeVerdict;
|
|
25424
|
-
if (input.scopeModelsPatterns && input.scopeModelsPatterns.length > 0) {
|
|
25425
|
-
const resolved = candidates[0] ?? requested;
|
|
25426
|
-
const source = input.overrideModel ? "caller" : input.agentModel ? "frontmatter" : "resolved";
|
|
25427
|
-
scopeVerdict = checkModelScope(resolved, input.scopeModelsPatterns, source);
|
|
25428
|
-
if (!scopeVerdict.inScope && source === "caller" && !input.isFrontmatterOverride) {
|
|
25429
|
-
throw errors.modelOutOfScope(resolved ?? "", input.scopeModelsPatterns);
|
|
25430
|
-
}
|
|
25431
|
-
}
|
|
25432
|
-
return { requested, candidates, reason, scopeVerdict };
|
|
25433
|
-
}
|
|
25434
|
-
var RETRYABLE_MODEL_FAILURE_PATTERNS, NON_RETRYABLE_MODEL_FAILURE_PATTERNS;
|
|
25435
|
-
var init_model_fallback = __esm({
|
|
25436
|
-
"src/runtime/model/model-fallback.ts"() {
|
|
25437
|
-
"use strict";
|
|
25438
|
-
init_errors3();
|
|
25439
|
-
init_model_resolver();
|
|
25440
|
-
init_model_scope();
|
|
25441
|
-
RETRYABLE_MODEL_FAILURE_PATTERNS = [
|
|
25442
|
-
/rate.?limit/i,
|
|
25443
|
-
/too many requests/i,
|
|
25444
|
-
/\b429\b/,
|
|
25445
|
-
/rate_limit_error/i,
|
|
25446
|
-
/quota/i,
|
|
25447
|
-
/provider.*unavailable/i,
|
|
25448
|
-
/model.*unavailable/i,
|
|
25449
|
-
/model.*disabled/i,
|
|
25450
|
-
/model.*not found/i,
|
|
25451
|
-
/unknown model/i,
|
|
25452
|
-
/overloaded/i,
|
|
25453
|
-
/service unavailable/i,
|
|
25454
|
-
/temporar(?:ily)? unavailable/i,
|
|
25455
|
-
/connection refused/i,
|
|
25456
|
-
/fetch failed/i,
|
|
25457
|
-
/network error/i,
|
|
25458
|
-
/socket hang up/i,
|
|
25459
|
-
/upstream/i,
|
|
25460
|
-
/timed? out/i,
|
|
25461
|
-
/timeout/i,
|
|
25462
|
-
/\b502\b/,
|
|
25463
|
-
/\b503\b/,
|
|
25464
|
-
/\b504\b/,
|
|
25465
|
-
//
|
|
25466
|
-
// Provider-side 5xx / generic api_error. The pi-core retry layer already
|
|
25467
|
-
// retries these (agent-session.ts matches `500|server error|internal error`),
|
|
25468
|
-
// but the pi-crew MODEL FALLBACK layer must ALSO treat them as retryable so
|
|
25469
|
-
// that when the provider is hard-down across all 3 provider retries, we fail
|
|
25470
|
-
// over to the next configured model instead of giving up. Reported case
|
|
25471
|
-
// (2026-06-17): `500 {"type":"error","error":{"type":"api_error",
|
|
25472
|
-
// "message":"unknown error, 999 (1000)"}}` — a transient provider outage that
|
|
25473
|
-
// should trigger the fallback chain, not abort.
|
|
25474
|
-
//
|
|
25475
|
-
// `api_error` is the OpenAI-compatible generic error type (vs rate_limit_error
|
|
25476
|
-
// / overloaded_error / etc.) and almost always means a transient server fault.
|
|
25477
|
-
//
|
|
25478
|
-
// `unknown error` is the body of the generic message; `internal`/`server`
|
|
25479
|
-
// catch the common phrasings. `\b500\b`/`\b501\b` catch the HTTP status in
|
|
25480
|
-
// the rendered error string.
|
|
25481
|
-
/\b500\b/,
|
|
25482
|
-
/\b501\b/,
|
|
25483
|
-
/api_error/i,
|
|
25484
|
-
/unknown error/i,
|
|
25485
|
-
/internal(?:_server)?[ _]error/i,
|
|
25486
|
-
/server error/i,
|
|
25487
|
-
/bad gateway/i,
|
|
25488
|
-
//
|
|
25489
|
-
// Broader retryable patterns (added 2026-06-25, FIX 2):
|
|
25490
|
-
// - `/provider[_ ]?error/i`: OpenAI-compatible "Provider error" generic fault.
|
|
25491
|
-
// - `/context[_ ]?length[_ ]?exceeded/i`: "context_length_exceeded" from
|
|
25492
|
-
// OpenAI/Anthropic — when the configured model is the bottleneck, a
|
|
25493
|
-
// different model in the fallback chain may have a larger window.
|
|
25494
|
-
// - `/safety/i`: Anthropic safety blocks — typically retryable on a
|
|
25495
|
-
// different model in the fallback chain.
|
|
25496
|
-
// - `/is[_ ]?overloaded/i`: alias to the existing `/overloaded/i` pattern
|
|
25497
|
-
// to catch phrasings like "upstream is overloaded".
|
|
25498
|
-
// - `/\b408\b/`: HTTP 408 Request Timeout — transient, provider-side.
|
|
25499
|
-
//
|
|
25500
|
-
// Intentionally NOT added: `/bad_request/` — can mean bad input (e.g.
|
|
25501
|
-
// invalid schema), which is non-retryable.
|
|
25502
|
-
/provider[_ ]?error/i,
|
|
25503
|
-
/context[_ ]?length[_ ]?exceeded/i,
|
|
25504
|
-
/safety/i,
|
|
25505
|
-
/is[_ ]?overloaded/i,
|
|
25506
|
-
/\b408\b/
|
|
25507
|
-
];
|
|
25508
|
-
NON_RETRYABLE_MODEL_FAILURE_PATTERNS = [
|
|
25509
|
-
/auth(?:entication)?/i,
|
|
25510
|
-
/unauthori[sz]ed/i,
|
|
25511
|
-
/forbidden/i,
|
|
25512
|
-
/api key/i,
|
|
25513
|
-
/token expired/i,
|
|
25514
|
-
/invalid key/i,
|
|
25515
|
-
/billing/i,
|
|
25516
|
-
/credit/i
|
|
25517
|
-
];
|
|
25518
|
-
}
|
|
25519
|
-
});
|
|
25520
|
-
|
|
25521
25877
|
// src/runtime/output/sidechain-output.ts
|
|
25522
25878
|
import * as fs40 from "node:fs";
|
|
25523
25879
|
import * as path34 from "node:path";
|
|
@@ -32567,6 +32923,20 @@ async function resolveScopeModelsPatterns(cwd, agentDir) {
|
|
|
32567
32923
|
if (!scopeModels) return [];
|
|
32568
32924
|
return readEnabledModelsPatterns(cwd, agentDir);
|
|
32569
32925
|
}
|
|
32926
|
+
function resolveLiveModelFallbackPolicy(cwd) {
|
|
32927
|
+
try {
|
|
32928
|
+
return resolveModelFallbackPolicy(loadConfig(cwd).config.runtime?.modelFallback);
|
|
32929
|
+
} catch {
|
|
32930
|
+
return void 0;
|
|
32931
|
+
}
|
|
32932
|
+
}
|
|
32933
|
+
function resolveLiveDefaultSubagentModel(cwd) {
|
|
32934
|
+
try {
|
|
32935
|
+
return resolveDefaultSubagentModel(loadConfig(cwd).config.runtime?.modelFallback);
|
|
32936
|
+
} catch {
|
|
32937
|
+
return void 0;
|
|
32938
|
+
}
|
|
32939
|
+
}
|
|
32570
32940
|
function modelFromRegistry(modelRegistry, modelId) {
|
|
32571
32941
|
if (!modelId?.includes("/")) return void 0;
|
|
32572
32942
|
const registry2 = asRecord6(modelRegistry);
|
|
@@ -32581,7 +32951,7 @@ function modelFromRegistry(modelRegistry, modelId) {
|
|
|
32581
32951
|
}
|
|
32582
32952
|
}
|
|
32583
32953
|
function resolveParentModelFromRegistry(modelRegistry, rawParentModel) {
|
|
32584
|
-
const raw =
|
|
32954
|
+
const raw = modelRefToString(rawParentModel);
|
|
32585
32955
|
if (raw) {
|
|
32586
32956
|
const candidate = raw.includes("/") ? raw : (() => {
|
|
32587
32957
|
const m = modelFromRegistry(modelRegistry, raw);
|
|
@@ -32592,18 +32962,11 @@ function resolveParentModelFromRegistry(modelRegistry, rawParentModel) {
|
|
|
32592
32962
|
})();
|
|
32593
32963
|
if (candidate && modelFromRegistry(modelRegistry, candidate)) return candidate;
|
|
32594
32964
|
}
|
|
32595
|
-
const
|
|
32596
|
-
if (
|
|
32597
|
-
|
|
32598
|
-
|
|
32599
|
-
|
|
32600
|
-
const first = available[0];
|
|
32601
|
-
if (first && typeof first.provider === "string" && typeof first.id === "string") {
|
|
32602
|
-
return `${first.provider}/${first.id}`;
|
|
32603
|
-
}
|
|
32604
|
-
}
|
|
32605
|
-
} catch {
|
|
32606
|
-
}
|
|
32965
|
+
const available = availableModelInfosFromRegistry(modelRegistry);
|
|
32966
|
+
if (available && available.length > 0) {
|
|
32967
|
+
const parentProvider = providerOfModelRef(raw);
|
|
32968
|
+
const sameProvider = parentProvider ? available.find((entry) => entry.provider === parentProvider) : void 0;
|
|
32969
|
+
return (sameProvider ?? available[0]).fullId;
|
|
32607
32970
|
}
|
|
32608
32971
|
return raw;
|
|
32609
32972
|
}
|
|
@@ -32863,14 +33226,32 @@ async function runLiveSessionTask(input) {
|
|
|
32863
33226
|
overrideModel: input.modelOverride,
|
|
32864
33227
|
stepModel: input.step.model,
|
|
32865
33228
|
teamRoleModel: input.teamRoleModel,
|
|
33229
|
+
teamRoleFallbackModels: input.teamRoleFallbackModels,
|
|
32866
33230
|
agentModel: input.agent.model,
|
|
33231
|
+
defaultSubagentModel: resolveLiveDefaultSubagentModel(input.manifest.cwd),
|
|
32867
33232
|
fallbackModels: input.agent.fallbackModels,
|
|
32868
33233
|
parentModel: effectiveParentModel,
|
|
32869
33234
|
modelRegistry: input.modelRegistry,
|
|
32870
33235
|
cwd: input.manifest.cwd,
|
|
33236
|
+
policy: resolveLiveModelFallbackPolicy(input.manifest.cwd),
|
|
32871
33237
|
scopeModelsPatterns: await resolveScopeModelsPatterns(input.manifest.cwd)
|
|
32872
33238
|
});
|
|
32873
33239
|
const resolvedModel = modelFromRegistry(input.modelRegistry, modelRouting.candidates[0] ?? modelRouting.requested) ?? input.parentModel;
|
|
33240
|
+
if (modelRouting.droppedRequested) {
|
|
33241
|
+
appendEventFireAndForget(input.manifest.eventsPath, {
|
|
33242
|
+
type: "task.model_dropped",
|
|
33243
|
+
runId: input.manifest.runId,
|
|
33244
|
+
taskId: input.task.id,
|
|
33245
|
+
message: `Requested model "${modelRouting.droppedRequested}" is not available; using "${modelRouting.candidates[0] ?? "default"}" instead.`,
|
|
33246
|
+
data: {
|
|
33247
|
+
requested: modelRouting.droppedRequested,
|
|
33248
|
+
resolved: modelRouting.candidates[0],
|
|
33249
|
+
fallbackChain: modelRouting.candidates
|
|
33250
|
+
}
|
|
33251
|
+
});
|
|
33252
|
+
}
|
|
33253
|
+
warnOutOfScopeSoft(modelRouting.scopeVerdict, "live-session.model-out-of-scope");
|
|
33254
|
+
const effectiveThinking = input.teamRoleThinking ?? input.agent.thinking;
|
|
32874
33255
|
const mcpProxy = buildMcpProxyFromSession([], { shareMcp: true });
|
|
32875
33256
|
const submitResultTool = createSubmitResultTool((result4) => {
|
|
32876
33257
|
customToolYieldResult = result4;
|
|
@@ -32891,7 +33272,7 @@ async function runLiveSessionTask(input) {
|
|
|
32891
33272
|
} : {},
|
|
32892
33273
|
...input.modelRegistry ? { modelRegistry: input.modelRegistry } : {},
|
|
32893
33274
|
...resolvedModel ? { model: resolvedModel } : {},
|
|
32894
|
-
...
|
|
33275
|
+
...effectiveThinking ? { thinkingLevel: effectiveThinking } : {},
|
|
32895
33276
|
...mcpProxy.enableMcp ? {} : { enableMCP: false },
|
|
32896
33277
|
customTools
|
|
32897
33278
|
});
|
|
@@ -33229,7 +33610,15 @@ ${input.prompt}` : input.prompt;
|
|
|
33229
33610
|
stderr: created.modelFallbackMessage ?? "",
|
|
33230
33611
|
jsonEvents,
|
|
33231
33612
|
usage,
|
|
33232
|
-
yieldResult
|
|
33613
|
+
yieldResult,
|
|
33614
|
+
modelRouting: {
|
|
33615
|
+
requested: modelRouting.requested,
|
|
33616
|
+
resolved: modelRefToString(resolvedModel) ?? modelRouting.candidates[0] ?? "default",
|
|
33617
|
+
fallbackChain: modelRouting.candidates,
|
|
33618
|
+
reason: modelRouting.reason,
|
|
33619
|
+
droppedRequested: modelRouting.droppedRequested,
|
|
33620
|
+
autoFallbackCount: modelRouting.autoFallbackCount
|
|
33621
|
+
}
|
|
33233
33622
|
};
|
|
33234
33623
|
} catch (error) {
|
|
33235
33624
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -36966,6 +37355,43 @@ function buildTeamDoctorReport(input) {
|
|
|
36966
37355
|
}
|
|
36967
37356
|
];
|
|
36968
37357
|
}),
|
|
37358
|
+
section("Model Routing", () => {
|
|
37359
|
+
const snapshot = sessionModelSnapshot();
|
|
37360
|
+
const liveModel = currentSessionModel();
|
|
37361
|
+
const policy = resolveModelFallbackPolicy(loadConfig(input.cwd).config.runtime?.modelFallback);
|
|
37362
|
+
const sampleRouting = buildConfiguredModelRouting({
|
|
37363
|
+
parentModel: liveModel,
|
|
37364
|
+
cwd: input.cwd,
|
|
37365
|
+
policy
|
|
37366
|
+
});
|
|
37367
|
+
return [
|
|
37368
|
+
{
|
|
37369
|
+
label: "session model (live)",
|
|
37370
|
+
ok: true,
|
|
37371
|
+
detail: liveModel ?? "not tracked yet"
|
|
37372
|
+
},
|
|
37373
|
+
{
|
|
37374
|
+
label: "session model (source)",
|
|
37375
|
+
ok: true,
|
|
37376
|
+
detail: `${snapshot.source}${snapshot.updatedAt ? ` @ ${new Date(snapshot.updatedAt).toISOString()}` : ""}`
|
|
37377
|
+
},
|
|
37378
|
+
{
|
|
37379
|
+
label: "fallback policy",
|
|
37380
|
+
ok: true,
|
|
37381
|
+
detail: policy ? `maxAuto=${policy.maxAutoFallbacks ?? "\u221E"} order=${policy.order ?? "parentFirst"} creds=${policy.requireCredentials ?? false} quota=${policy.quotaAwareOrdering ?? true}` : "legacy (unbounded, unordered)"
|
|
37382
|
+
},
|
|
37383
|
+
{
|
|
37384
|
+
label: "sample chain (no explicit model)",
|
|
37385
|
+
ok: true,
|
|
37386
|
+
detail: sampleRouting.candidates.length > 0 ? sampleRouting.candidates.join(" \u2192 ") : "(empty)"
|
|
37387
|
+
},
|
|
37388
|
+
{
|
|
37389
|
+
label: "auto tail size",
|
|
37390
|
+
ok: true,
|
|
37391
|
+
detail: `${sampleRouting.autoFallbackCount ?? 0} models`
|
|
37392
|
+
}
|
|
37393
|
+
];
|
|
37394
|
+
}),
|
|
36969
37395
|
section("Discovery", () => {
|
|
36970
37396
|
const agentModelHints = discoveredAgentsAll.filter((agent) => agent.model || agent.fallbackModels?.length).length;
|
|
36971
37397
|
return [
|
|
@@ -37150,7 +37576,9 @@ var init_doctor = __esm({
|
|
|
37150
37576
|
init_config();
|
|
37151
37577
|
init_defaults();
|
|
37152
37578
|
init_drift_detector();
|
|
37579
|
+
init_model_fallback();
|
|
37153
37580
|
init_runtime_warmup();
|
|
37581
|
+
init_session_model();
|
|
37154
37582
|
init_pi_spawn();
|
|
37155
37583
|
init_zombie_scanner();
|
|
37156
37584
|
init_team_tool_schema();
|
|
@@ -41175,7 +41603,7 @@ var init_async_runner = __esm({
|
|
|
41175
41603
|
});
|
|
41176
41604
|
|
|
41177
41605
|
// src/runtime/goal-workflow/goal-state-store.ts
|
|
41178
|
-
import { closeSync as closeSync10, existsSync as existsSync44, mkdirSync as mkdirSync26, openSync as openSync10, readdirSync as readdirSync20, readFileSync as readFileSync42, statSync as
|
|
41606
|
+
import { closeSync as closeSync10, existsSync as existsSync44, mkdirSync as mkdirSync26, openSync as openSync10, readdirSync as readdirSync20, readFileSync as readFileSync42, statSync as statSync33, unlinkSync as unlinkSync7 } from "node:fs";
|
|
41179
41607
|
import { dirname as dirname32 } from "node:path";
|
|
41180
41608
|
function resolveGoalsRoot(cwd) {
|
|
41181
41609
|
const crewRoot = projectCrewRoot(cwd) ?? userCrewRoot();
|
|
@@ -41218,22 +41646,22 @@ var init_goal_state_store = __esm({
|
|
|
41218
41646
|
}
|
|
41219
41647
|
}
|
|
41220
41648
|
/** Atomically persist a goal state. Emits a goal.state_changed event if eventsPath given. */
|
|
41221
|
-
save(
|
|
41222
|
-
assertSafePathId("goalId",
|
|
41223
|
-
const path89 = goalFilePath(this.cwd,
|
|
41224
|
-
const next = { ...
|
|
41649
|
+
save(state2, eventsPath) {
|
|
41650
|
+
assertSafePathId("goalId", state2.goalId);
|
|
41651
|
+
const path89 = goalFilePath(this.cwd, state2.goalId);
|
|
41652
|
+
const next = { ...state2, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
41225
41653
|
try {
|
|
41226
41654
|
mkdirSync26(dirname32(path89), { recursive: true });
|
|
41227
41655
|
atomicWriteJson(path89, next);
|
|
41228
41656
|
if (eventsPath) {
|
|
41229
41657
|
appendEvent(eventsPath, {
|
|
41230
41658
|
type: "goal.state_changed",
|
|
41231
|
-
runId:
|
|
41232
|
-
data: { goalId:
|
|
41659
|
+
runId: state2.goalId,
|
|
41660
|
+
data: { goalId: state2.goalId, state: state2.state }
|
|
41233
41661
|
});
|
|
41234
41662
|
}
|
|
41235
41663
|
} catch (error) {
|
|
41236
|
-
logInternalError("goal-state-store.save", error, `goalId=${
|
|
41664
|
+
logInternalError("goal-state-store.save", error, `goalId=${state2.goalId}`);
|
|
41237
41665
|
throw error;
|
|
41238
41666
|
}
|
|
41239
41667
|
}
|
|
@@ -41251,8 +41679,8 @@ var init_goal_state_store = __esm({
|
|
|
41251
41679
|
return next;
|
|
41252
41680
|
}
|
|
41253
41681
|
/** Convenience: transition state with optional event emission. */
|
|
41254
|
-
setStatus(goalId,
|
|
41255
|
-
return this.patch(goalId, { state }, eventsPath);
|
|
41682
|
+
setStatus(goalId, state2, eventsPath) {
|
|
41683
|
+
return this.patch(goalId, { state: state2 }, eventsPath);
|
|
41256
41684
|
}
|
|
41257
41685
|
/**
|
|
41258
41686
|
* Compare-And-Set status for atomic stuck↔resume transitions (P1b, RFC v0.5 §P1b).
|
|
@@ -41306,7 +41734,7 @@ var init_goal_state_store = __esm({
|
|
|
41306
41734
|
const code = error.code;
|
|
41307
41735
|
if (code !== "EEXIST") return false;
|
|
41308
41736
|
try {
|
|
41309
|
-
const stat2 =
|
|
41737
|
+
const stat2 = statSync33(lockPath2);
|
|
41310
41738
|
if (Date.now() - stat2.mtimeMs > 5e3) {
|
|
41311
41739
|
unlinkSync7(lockPath2);
|
|
41312
41740
|
const fd = openSync10(lockPath2, "wx");
|
|
@@ -41399,7 +41827,7 @@ var init_verification_integrity = __esm({
|
|
|
41399
41827
|
|
|
41400
41828
|
// src/runtime/workspace-lock.ts
|
|
41401
41829
|
import { createHash as createHash7 } from "node:crypto";
|
|
41402
|
-
import { closeSync as closeSync11, existsSync as existsSync45, mkdirSync as mkdirSync27, openSync as openSync11, readdirSync as readdirSync21, readFileSync as readFileSync44, statSync as
|
|
41830
|
+
import { closeSync as closeSync11, existsSync as existsSync45, mkdirSync as mkdirSync27, openSync as openSync11, readdirSync as readdirSync21, readFileSync as readFileSync44, statSync as statSync35, unlinkSync as unlinkSync8, writeFileSync as writeFileSync8 } from "node:fs";
|
|
41403
41831
|
import * as path50 from "node:path";
|
|
41404
41832
|
function workspaceLockPath(cwd) {
|
|
41405
41833
|
const absCwd = path50.resolve(cwd);
|
|
@@ -43971,19 +44399,19 @@ function ensureShared() {
|
|
|
43971
44399
|
return shared;
|
|
43972
44400
|
}
|
|
43973
44401
|
function registerOverlayScheduler(render, onInvalidate) {
|
|
43974
|
-
const
|
|
43975
|
-
|
|
43976
|
-
if (onInvalidate)
|
|
44402
|
+
const state2 = ensureShared();
|
|
44403
|
+
state2.renderers.add(render);
|
|
44404
|
+
if (onInvalidate) state2.invalidators.add(onInvalidate);
|
|
43977
44405
|
return {
|
|
43978
44406
|
schedule: () => {
|
|
43979
|
-
|
|
44407
|
+
state2.scheduler.schedule();
|
|
43980
44408
|
},
|
|
43981
44409
|
dispose: () => {
|
|
43982
|
-
|
|
43983
|
-
if (onInvalidate)
|
|
43984
|
-
if (
|
|
43985
|
-
|
|
43986
|
-
if (shared ===
|
|
44410
|
+
state2.renderers.delete(render);
|
|
44411
|
+
if (onInvalidate) state2.invalidators.delete(onInvalidate);
|
|
44412
|
+
if (state2.renderers.size === 0) {
|
|
44413
|
+
state2.scheduler.dispose();
|
|
44414
|
+
if (shared === state2) shared = void 0;
|
|
43987
44415
|
}
|
|
43988
44416
|
}
|
|
43989
44417
|
};
|
|
@@ -45452,9 +45880,9 @@ function uninstallResizeListener() {
|
|
|
45452
45880
|
process.stdout.removeListener("resize", onResize);
|
|
45453
45881
|
}
|
|
45454
45882
|
}
|
|
45455
|
-
function updateCrewWidget(ctx,
|
|
45883
|
+
function updateCrewWidget(ctx, state2, config, manifestCache2, snapshotCache, preloadedManifests) {
|
|
45456
45884
|
if (!ctx.hasUI) return;
|
|
45457
|
-
|
|
45885
|
+
state2.frame += 1;
|
|
45458
45886
|
const maxLines = config?.widgetMaxLines ?? MAX_LINES_DEFAULT;
|
|
45459
45887
|
let workspaceId = ctx.sessionManager?.getSessionId?.();
|
|
45460
45888
|
if (!workspaceId && manifestCache2) {
|
|
@@ -45463,75 +45891,75 @@ function updateCrewWidget(ctx, state, config, manifestCache2, snapshotCache, pre
|
|
|
45463
45891
|
if (active?.ownerSessionId) workspaceId = active.ownerSessionId;
|
|
45464
45892
|
}
|
|
45465
45893
|
const runs = activeWidgetRuns(ctx.cwd, manifestCache2, snapshotCache, preloadedManifests, workspaceId);
|
|
45466
|
-
const lines = buildWidgetLines(ctx.cwd,
|
|
45894
|
+
const lines = buildWidgetLines(ctx.cwd, state2.frame, maxLines, runs, state2.notificationCount ?? 0, getRenderWidth());
|
|
45467
45895
|
const placement = config?.widgetPlacement ?? DEFAULT_UI.widgetPlacement;
|
|
45468
45896
|
ctx.ui.setStatus(STATUS_KEY, lines.length ? statusSummary(runs) : void 0);
|
|
45469
|
-
const shouldClearLegacy =
|
|
45897
|
+
const shouldClearLegacy = state2.legacyCleared !== true || state2.lastPlacement !== placement;
|
|
45470
45898
|
if (shouldClearLegacy) {
|
|
45471
45899
|
setExtensionWidget(ctx, LEGACY_WIDGET_KEY, void 0, { placement });
|
|
45472
|
-
|
|
45900
|
+
state2.legacyCleared = true;
|
|
45473
45901
|
}
|
|
45474
45902
|
if (!lines.length) {
|
|
45475
|
-
if (
|
|
45903
|
+
if (state2.lastVisibility !== "hidden" || state2.lastPlacement !== placement) {
|
|
45476
45904
|
setExtensionWidget(ctx, WIDGET_KEY, void 0, { placement });
|
|
45477
|
-
|
|
45478
|
-
|
|
45479
|
-
|
|
45480
|
-
|
|
45481
|
-
|
|
45482
|
-
|
|
45905
|
+
state2.lastVisibility = "hidden";
|
|
45906
|
+
state2.lastPlacement = placement;
|
|
45907
|
+
state2.lastKey = WIDGET_KEY;
|
|
45908
|
+
state2.lastMaxLines = maxLines;
|
|
45909
|
+
state2.lastCwd = ctx.cwd;
|
|
45910
|
+
state2.model = void 0;
|
|
45483
45911
|
}
|
|
45484
45912
|
requestRender(ctx);
|
|
45485
45913
|
return;
|
|
45486
45914
|
}
|
|
45487
|
-
const needsWidgetInstall =
|
|
45488
|
-
if (!
|
|
45489
|
-
|
|
45915
|
+
const needsWidgetInstall = state2.lastVisibility !== "visible" || state2.lastPlacement !== placement || state2.lastKey !== WIDGET_KEY || state2.lastMaxLines !== maxLines || state2.lastCwd !== ctx.cwd || !state2.model;
|
|
45916
|
+
if (!state2.model)
|
|
45917
|
+
state2.model = {
|
|
45490
45918
|
cwd: ctx.cwd,
|
|
45491
|
-
frame:
|
|
45919
|
+
frame: state2.frame,
|
|
45492
45920
|
maxLines,
|
|
45493
|
-
notificationCount:
|
|
45921
|
+
notificationCount: state2.notificationCount ?? 0,
|
|
45494
45922
|
manifestCache: manifestCache2,
|
|
45495
45923
|
snapshotCache,
|
|
45496
45924
|
preloadManifests: preloadedManifests,
|
|
45497
45925
|
workspaceId
|
|
45498
45926
|
};
|
|
45499
45927
|
else {
|
|
45500
|
-
|
|
45501
|
-
|
|
45502
|
-
|
|
45503
|
-
|
|
45504
|
-
|
|
45505
|
-
|
|
45506
|
-
|
|
45507
|
-
|
|
45928
|
+
state2.model.cwd = ctx.cwd;
|
|
45929
|
+
state2.model.frame = state2.frame;
|
|
45930
|
+
state2.model.maxLines = maxLines;
|
|
45931
|
+
state2.model.notificationCount = state2.notificationCount ?? 0;
|
|
45932
|
+
state2.model.manifestCache = manifestCache2;
|
|
45933
|
+
state2.model.snapshotCache = snapshotCache;
|
|
45934
|
+
state2.model.preloadManifests = preloadedManifests;
|
|
45935
|
+
state2.model.workspaceId = workspaceId;
|
|
45508
45936
|
}
|
|
45509
45937
|
if (needsWidgetInstall) {
|
|
45510
|
-
const model =
|
|
45938
|
+
const model = state2.model;
|
|
45511
45939
|
setExtensionWidget(ctx, WIDGET_KEY, ((_tui, theme) => new CrewWidgetComponent(model, theme, _tui)), {
|
|
45512
45940
|
placement,
|
|
45513
45941
|
persist: true
|
|
45514
45942
|
});
|
|
45515
|
-
|
|
45516
|
-
|
|
45517
|
-
|
|
45518
|
-
|
|
45519
|
-
|
|
45943
|
+
state2.lastVisibility = "visible";
|
|
45944
|
+
state2.lastPlacement = placement;
|
|
45945
|
+
state2.lastKey = WIDGET_KEY;
|
|
45946
|
+
state2.lastMaxLines = maxLines;
|
|
45947
|
+
state2.lastCwd = ctx.cwd;
|
|
45520
45948
|
}
|
|
45521
45949
|
requestRender(ctx);
|
|
45522
45950
|
}
|
|
45523
|
-
function stopCrewWidget(ctx,
|
|
45951
|
+
function stopCrewWidget(ctx, state2, config) {
|
|
45524
45952
|
uninstallResizeListener();
|
|
45525
45953
|
if (ctx?.hasUI) {
|
|
45526
45954
|
const placement = config?.widgetPlacement ?? DEFAULT_UI.widgetPlacement;
|
|
45527
45955
|
ctx.ui.setStatus(STATUS_KEY, void 0);
|
|
45528
45956
|
setExtensionWidget(ctx, LEGACY_WIDGET_KEY, void 0, { placement });
|
|
45529
45957
|
setExtensionWidget(ctx, WIDGET_KEY, void 0, { placement });
|
|
45530
|
-
|
|
45531
|
-
|
|
45532
|
-
|
|
45533
|
-
|
|
45534
|
-
|
|
45958
|
+
state2.lastVisibility = "hidden";
|
|
45959
|
+
state2.lastPlacement = placement;
|
|
45960
|
+
state2.lastKey = WIDGET_KEY;
|
|
45961
|
+
state2.model = void 0;
|
|
45962
|
+
state2.legacyCleared = true;
|
|
45535
45963
|
requestRender(ctx);
|
|
45536
45964
|
}
|
|
45537
45965
|
}
|
|
@@ -45751,7 +46179,7 @@ var init_tool_progress_formatter = __esm({
|
|
|
45751
46179
|
});
|
|
45752
46180
|
|
|
45753
46181
|
// src/extension/registration/team-tool.ts
|
|
45754
|
-
import { statSync as
|
|
46182
|
+
import { statSync as statSync38 } from "node:fs";
|
|
45755
46183
|
import { Text as Text3 } from "@earendil-works/pi-tui";
|
|
45756
46184
|
async function handleTeamTool(params, ctx) {
|
|
45757
46185
|
if (!_cachedHandleTeamTool) {
|
|
@@ -45774,7 +46202,7 @@ function resolveCwdOverride(baseCwd, override) {
|
|
|
45774
46202
|
if (!override) return { ok: true, cwd: baseCwd };
|
|
45775
46203
|
try {
|
|
45776
46204
|
const resolved = resolveRealContainedPath(baseCwd, override);
|
|
45777
|
-
const stat2 =
|
|
46205
|
+
const stat2 = statSync38(resolved);
|
|
45778
46206
|
if (!stat2.isDirectory())
|
|
45779
46207
|
return {
|
|
45780
46208
|
ok: false,
|
|
@@ -52684,6 +53112,20 @@ function resolveConfiguredMaxAttempts(cwd) {
|
|
|
52684
53112
|
function computeSpawnBudgetMax(attemptModelsCount, configuredMaxAttempts) {
|
|
52685
53113
|
return attemptModelsCount * (configuredMaxAttempts + 1);
|
|
52686
53114
|
}
|
|
53115
|
+
function resolveTaskModelFallbackPolicy(cwd) {
|
|
53116
|
+
try {
|
|
53117
|
+
return resolveModelFallbackPolicy(loadConfig(cwd).config.runtime?.modelFallback);
|
|
53118
|
+
} catch {
|
|
53119
|
+
return void 0;
|
|
53120
|
+
}
|
|
53121
|
+
}
|
|
53122
|
+
function resolveTaskDefaultSubagentModel(cwd) {
|
|
53123
|
+
try {
|
|
53124
|
+
return resolveDefaultSubagentModel(loadConfig(cwd).config.runtime?.modelFallback);
|
|
53125
|
+
} catch {
|
|
53126
|
+
return void 0;
|
|
53127
|
+
}
|
|
53128
|
+
}
|
|
52687
53129
|
function detectRetryableModelFailureFromOutput(parsed) {
|
|
52688
53130
|
const messages = parsed.errorMessages;
|
|
52689
53131
|
if (messages && messages.length > 0) {
|
|
@@ -52729,24 +53171,45 @@ async function runChildProcessTask(ctx) {
|
|
|
52729
53171
|
let transcriptPath;
|
|
52730
53172
|
let terminalEvidence = [];
|
|
52731
53173
|
let startupEvidence = ctx.startupEvidence;
|
|
53174
|
+
const modelFallbackPolicy = resolveTaskModelFallbackPolicy(task.cwd);
|
|
53175
|
+
const defaultSubagentModel = resolveTaskDefaultSubagentModel(task.cwd);
|
|
52732
53176
|
const modelRoutingPlan = buildConfiguredModelRouting({
|
|
52733
53177
|
overrideModel: input.modelOverride,
|
|
52734
53178
|
stepModel: input.step.model,
|
|
52735
53179
|
teamRoleModel: input.teamRoleModel,
|
|
53180
|
+
teamRoleFallbackModels: input.teamRoleFallbackModels,
|
|
52736
53181
|
agentModel: input.agent.model,
|
|
53182
|
+
defaultSubagentModel,
|
|
52737
53183
|
fallbackModels: input.agent.fallbackModels,
|
|
52738
53184
|
parentModel: input.parentModel,
|
|
52739
53185
|
modelRegistry: input.modelRegistry,
|
|
52740
53186
|
cwd: task.cwd,
|
|
53187
|
+
policy: modelFallbackPolicy,
|
|
52741
53188
|
scopeModelsPatterns: await resolveTaskScopeModelsPatterns(task.cwd)
|
|
52742
53189
|
});
|
|
52743
53190
|
const candidates = modelRoutingPlan.candidates;
|
|
52744
|
-
|
|
53191
|
+
if (modelRoutingPlan.droppedRequested) {
|
|
53192
|
+
void appendEventAsync(manifest.eventsPath, {
|
|
53193
|
+
type: "task.model_dropped",
|
|
53194
|
+
runId: manifest.runId,
|
|
53195
|
+
taskId: task.id,
|
|
53196
|
+
message: `Requested model "${modelRoutingPlan.droppedRequested}" is not available; using "${candidates[0] ?? "default"}" instead.`,
|
|
53197
|
+
data: {
|
|
53198
|
+
requested: modelRoutingPlan.droppedRequested,
|
|
53199
|
+
resolved: candidates[0],
|
|
53200
|
+
fallbackChain: candidates
|
|
53201
|
+
}
|
|
53202
|
+
}).catch(() => {
|
|
53203
|
+
});
|
|
53204
|
+
}
|
|
53205
|
+
warnOutOfScopeSoft(modelRoutingPlan.scopeVerdict, "child-executor.initial-out-of-scope");
|
|
53206
|
+
const attemptModels = candidates.length > 0 ? [...candidates] : [void 0];
|
|
52745
53207
|
if (input.spawnBudget && input.spawnBudget.max === 0) {
|
|
52746
53208
|
input.spawnBudget.max = computeSpawnBudgetMax(attemptModels.length, resolveConfiguredMaxAttempts(task.cwd));
|
|
52747
53209
|
}
|
|
52748
53210
|
const logs = [];
|
|
52749
53211
|
let finalStderr = "";
|
|
53212
|
+
let reResolveUsed = false;
|
|
52750
53213
|
modelAttempts = [];
|
|
52751
53214
|
let finalCheckpointWritten = false;
|
|
52752
53215
|
let lastAgentRecordPersistedAt = 0;
|
|
@@ -52867,6 +53330,7 @@ async function runChildProcessTask(ctx) {
|
|
|
52867
53330
|
excludeContextBash: input.runtimeConfig?.excludeContextBash,
|
|
52868
53331
|
sessionId: manifest.sessionId,
|
|
52869
53332
|
role: task.role,
|
|
53333
|
+
thinkingOverride: input.teamRoleThinking,
|
|
52870
53334
|
runId: manifest.runId,
|
|
52871
53335
|
agentId: task.id,
|
|
52872
53336
|
artifactsRoot: manifest.artifactsRoot,
|
|
@@ -53035,7 +53499,8 @@ async function runChildProcessTask(ctx) {
|
|
|
53035
53499
|
);
|
|
53036
53500
|
if (!error) break;
|
|
53037
53501
|
let nextModel = attemptModels[i + 1];
|
|
53038
|
-
if (!nextModel && isRetryableModelFailure(error)) {
|
|
53502
|
+
if (!nextModel && !reResolveUsed && isRetryableModelFailure(error)) {
|
|
53503
|
+
reResolveUsed = true;
|
|
53039
53504
|
const reResolved = buildConfiguredModelRouting({
|
|
53040
53505
|
overrideModel: void 0,
|
|
53041
53506
|
stepModel: void 0,
|
|
@@ -53045,10 +53510,17 @@ async function runChildProcessTask(ctx) {
|
|
|
53045
53510
|
parentModel: attempt.model,
|
|
53046
53511
|
modelRegistry: input.modelRegistry,
|
|
53047
53512
|
cwd: task.cwd,
|
|
53513
|
+
policy: modelFallbackPolicy,
|
|
53048
53514
|
scopeModelsPatterns: await resolveTaskScopeModelsPatterns(task.cwd)
|
|
53049
53515
|
});
|
|
53050
|
-
|
|
53051
|
-
|
|
53516
|
+
warnOutOfScopeSoft(reResolved.scopeVerdict, "child-executor.re-resolve-out-of-scope", "Re-resolved model");
|
|
53517
|
+
const tried = new Set(modelAttempts.map((a) => a.model));
|
|
53518
|
+
const alt = reResolved.candidates.find((candidate) => !tried.has(candidate));
|
|
53519
|
+
if (alt) {
|
|
53520
|
+
attemptModels.push(alt);
|
|
53521
|
+
nextModel = alt;
|
|
53522
|
+
if (input.spawnBudget) input.spawnBudget.max += 1;
|
|
53523
|
+
}
|
|
53052
53524
|
}
|
|
53053
53525
|
if (!nextModel || !isRetryableModelFailure(error)) break;
|
|
53054
53526
|
logs.push(formatModelAttemptNote(attempt, nextModel), "");
|
|
@@ -53117,7 +53589,9 @@ async function runChildProcessTask(ctx) {
|
|
|
53117
53589
|
resolved: resolvedModel,
|
|
53118
53590
|
fallbackChain: candidates,
|
|
53119
53591
|
reason: fallbackReason ?? modelRoutingPlan.reason,
|
|
53120
|
-
usedAttempt
|
|
53592
|
+
usedAttempt,
|
|
53593
|
+
droppedRequested: modelRoutingPlan.droppedRequested,
|
|
53594
|
+
autoFallbackCount: modelRoutingPlan.autoFallbackCount
|
|
53121
53595
|
}
|
|
53122
53596
|
};
|
|
53123
53597
|
tasks = updateTask(tasks, task);
|
|
@@ -55028,6 +55502,8 @@ async function runLiveTask(input) {
|
|
|
55028
55502
|
modelRegistry: input.modelRegistry,
|
|
55029
55503
|
modelOverride: input.modelOverride,
|
|
55030
55504
|
teamRoleModel: input.teamRoleModel,
|
|
55505
|
+
teamRoleFallbackModels: input.teamRoleFallbackModels,
|
|
55506
|
+
teamRoleThinking: input.teamRoleThinking,
|
|
55031
55507
|
isCurrent,
|
|
55032
55508
|
workspaceId: input.workspaceId,
|
|
55033
55509
|
// Phase 2: Pass output schema for yield validation
|
|
@@ -55071,6 +55547,14 @@ async function runLiveTask(input) {
|
|
|
55071
55547
|
usage: liveResult.usage,
|
|
55072
55548
|
agentProgress: applyUsageToProgress(task.agentProgress, liveResult.usage)
|
|
55073
55549
|
};
|
|
55550
|
+
if (liveResult.modelRouting)
|
|
55551
|
+
task = {
|
|
55552
|
+
...task,
|
|
55553
|
+
modelRouting: {
|
|
55554
|
+
...liveResult.modelRouting,
|
|
55555
|
+
usedAttempt: 0
|
|
55556
|
+
}
|
|
55557
|
+
};
|
|
55074
55558
|
persistLiveProgress({ type: "attempt_finished" }, true);
|
|
55075
55559
|
const resultArtifact = writeArtifact(manifest.artifactsRoot, {
|
|
55076
55560
|
kind: "result",
|
|
@@ -55197,6 +55681,8 @@ async function runTeamTask(input) {
|
|
|
55197
55681
|
modelRegistry: input.modelRegistry,
|
|
55198
55682
|
modelOverride: input.modelOverride,
|
|
55199
55683
|
teamRoleModel: input.teamRoleModel,
|
|
55684
|
+
teamRoleFallbackModels: input.teamRoleFallbackModels,
|
|
55685
|
+
teamRoleThinking: input.teamRoleThinking,
|
|
55200
55686
|
workspaceId: input.workspaceId
|
|
55201
55687
|
});
|
|
55202
55688
|
task = live.task;
|
|
@@ -56725,10 +57211,7 @@ async function cancelRunFromSignal(ctx) {
|
|
|
56725
57211
|
return { kind: "return", result: { manifest: ctx.manifest, tasks: ctx.tasks } };
|
|
56726
57212
|
}
|
|
56727
57213
|
async function terminaliseRunWithDrain(ctx, opts) {
|
|
56728
|
-
const inflightTaskIds =
|
|
56729
|
-
for (const unit of ctx.pendingUnits.values()) {
|
|
56730
|
-
for (const id of unit.taskIds) inflightTaskIds.add(id);
|
|
56731
|
-
}
|
|
57214
|
+
const inflightTaskIds = ctx.dispatchedTaskIds;
|
|
56732
57215
|
const outcomes = await drainPendingUnits(ctx.pendingUnits, ctx.runController);
|
|
56733
57216
|
const validResults = [];
|
|
56734
57217
|
for (const outcome of outcomes) {
|
|
@@ -57049,6 +57532,8 @@ async function dispatchBatch(ctx, decision2) {
|
|
|
57049
57532
|
modelRegistry: input.modelRegistry,
|
|
57050
57533
|
modelOverride: input.modelOverride,
|
|
57051
57534
|
teamRoleModel: teamRole?.model,
|
|
57535
|
+
teamRoleThinking: teamRole?.thinking,
|
|
57536
|
+
teamRoleFallbackModels: teamRole?.fallbackModels,
|
|
57052
57537
|
teamRoleSkills: teamRole?.skills,
|
|
57053
57538
|
skillOverride: input.skillOverride,
|
|
57054
57539
|
limits: input.limits,
|
|
@@ -57234,6 +57719,7 @@ async function dispatchBatch(ctx, decision2) {
|
|
|
57234
57719
|
promise: rawPromise,
|
|
57235
57720
|
wrapped
|
|
57236
57721
|
});
|
|
57722
|
+
for (const id of unitTaskIds) ctx.dispatchedTaskIds.add(id);
|
|
57237
57723
|
}
|
|
57238
57724
|
}
|
|
57239
57725
|
async function mergeUnitResult(ctx) {
|
|
@@ -57594,6 +58080,7 @@ async function executeTeamRunCore(input, manifest, workflow) {
|
|
|
57594
58080
|
queueIndex,
|
|
57595
58081
|
wfMachine,
|
|
57596
58082
|
pendingUnits,
|
|
58083
|
+
dispatchedTaskIds: /* @__PURE__ */ new Set(),
|
|
57597
58084
|
runController,
|
|
57598
58085
|
runtimeKind,
|
|
57599
58086
|
adaptivePlanInjected,
|
|
@@ -59720,14 +60207,14 @@ var init_dwf_state_store = __esm({
|
|
|
59720
60207
|
}
|
|
59721
60208
|
}
|
|
59722
60209
|
/** Atomically persist a checkpoint state. Stamps `updatedAt` (callers need not set it). */
|
|
59723
|
-
save(
|
|
60210
|
+
save(state2) {
|
|
59724
60211
|
const path89 = this.path;
|
|
59725
|
-
const next = { ...
|
|
60212
|
+
const next = { ...state2, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
59726
60213
|
try {
|
|
59727
60214
|
mkdirSync39(dirname37(path89), { recursive: true });
|
|
59728
60215
|
atomicWriteJson(path89, next);
|
|
59729
60216
|
} catch (error) {
|
|
59730
|
-
logInternalError("dwf-state-store.save", error, `runId=${
|
|
60217
|
+
logInternalError("dwf-state-store.save", error, `runId=${state2.runId}`);
|
|
59731
60218
|
throw error;
|
|
59732
60219
|
}
|
|
59733
60220
|
}
|
|
@@ -60626,9 +61113,9 @@ async function runDynamicWorkflow(input) {
|
|
|
60626
61113
|
resumedState,
|
|
60627
61114
|
// round-18 P2-3: checkpoint after each ctx.agent() call so a crash between calls
|
|
60628
61115
|
// leaves durable state. onCheckpoint captures the closure values at call time.
|
|
60629
|
-
onCheckpoint: (
|
|
61116
|
+
onCheckpoint: (state2) => {
|
|
60630
61117
|
try {
|
|
60631
|
-
dwfStore.save(
|
|
61118
|
+
dwfStore.save(state2);
|
|
60632
61119
|
} catch (error) {
|
|
60633
61120
|
logInternalError("dynamic-workflow-runner.checkpoint-save", error, `runId=${manifest.runId}`);
|
|
60634
61121
|
}
|
|
@@ -61226,6 +61713,10 @@ ${dwfResult.manifest.summary ?? ""}`,
|
|
|
61226
61713
|
...updatedManifest,
|
|
61227
61714
|
runtimeResolution,
|
|
61228
61715
|
runConfig: executedConfig,
|
|
61716
|
+
// Background/async runs re-enter through background-runner in a detached
|
|
61717
|
+
// process with no ExtensionContext. Snapshot the model routing inputs so
|
|
61718
|
+
// they survive the hand-off instead of being rediscovered from models.json.
|
|
61719
|
+
modelContext: captureRunModelContext(ctx, params.model),
|
|
61229
61720
|
// Persist budget config on the manifest so it's observable post-run
|
|
61230
61721
|
// (events.jsonl, status reads, audits). The team-runner reads these
|
|
61231
61722
|
// from the input, but persisting them means consumers can verify
|
|
@@ -61436,7 +61927,7 @@ ${dwfResult.manifest.summary ?? ""}`,
|
|
|
61436
61927
|
runtime,
|
|
61437
61928
|
runtimeConfig: executedConfig.runtime,
|
|
61438
61929
|
parentContext: buildParentContext(ctx),
|
|
61439
|
-
parentModel: ctx.model,
|
|
61930
|
+
parentModel: resolveParentModel(ctx.model),
|
|
61440
61931
|
modelRegistry: ctx.modelRegistry,
|
|
61441
61932
|
modelOverride: params.model,
|
|
61442
61933
|
skillOverride,
|
|
@@ -61502,7 +61993,7 @@ ${dwfResult.manifest.summary ?? ""}`,
|
|
|
61502
61993
|
runtime,
|
|
61503
61994
|
runtimeConfig: executedConfig.runtime,
|
|
61504
61995
|
parentContext: buildParentContext(ctx),
|
|
61505
|
-
parentModel: ctx.model,
|
|
61996
|
+
parentModel: resolveParentModel(ctx.model),
|
|
61506
61997
|
modelRegistry: ctx.modelRegistry,
|
|
61507
61998
|
modelOverride: params.model,
|
|
61508
61999
|
skillOverride,
|
|
@@ -61551,6 +62042,7 @@ var init_run2 = __esm({
|
|
|
61551
62042
|
init_param_error();
|
|
61552
62043
|
init_async_runner();
|
|
61553
62044
|
init_runtime_resolver();
|
|
62045
|
+
init_session_model();
|
|
61554
62046
|
init_event_log();
|
|
61555
62047
|
init_i18n();
|
|
61556
62048
|
init_async_marker();
|
|
@@ -61959,7 +62451,7 @@ async function handleResume2(params, ctx) {
|
|
|
61959
62451
|
runtime: decision2.runtime,
|
|
61960
62452
|
runtimeConfig: decision2.executedConfig.runtime,
|
|
61961
62453
|
parentContext: buildParentContext(ctx),
|
|
61962
|
-
parentModel: ctx.model,
|
|
62454
|
+
parentModel: resolveParentModel(ctx.model),
|
|
61963
62455
|
modelRegistry: ctx.modelRegistry,
|
|
61964
62456
|
modelOverride: params.model,
|
|
61965
62457
|
skillOverride: decision2.resumeSkillOverride,
|
|
@@ -62206,6 +62698,7 @@ var init_team_tool2 = __esm({
|
|
|
62206
62698
|
init_run_index();
|
|
62207
62699
|
init_direct_run();
|
|
62208
62700
|
init_runtime_resolver();
|
|
62701
|
+
init_session_model();
|
|
62209
62702
|
init_pi_json_output();
|
|
62210
62703
|
init_config_patch();
|
|
62211
62704
|
init_context();
|
|
@@ -63237,16 +63730,16 @@ function readRunTranscript(manifest, taskId, options = {}) {
|
|
|
63237
63730
|
readRunTranscriptCache.set(cacheKey2, { parsedAt: now, result: result4 });
|
|
63238
63731
|
return result4;
|
|
63239
63732
|
}
|
|
63240
|
-
function renderViewerBase(
|
|
63733
|
+
function renderViewerBase(state2, width, lines, title, subtitle) {
|
|
63241
63734
|
const inner = Math.max(20, width - 4);
|
|
63242
63735
|
const bodyText = lines.join("\n");
|
|
63243
|
-
const { visualLines, skippedCount } = truncateToVisualLines(bodyText,
|
|
63244
|
-
const maxScroll = Math.max(0, visualLines.length -
|
|
63245
|
-
if (
|
|
63246
|
-
|
|
63247
|
-
const visible = visualLines.slice(
|
|
63248
|
-
const statusLine = `${visualLines.length} lines \xB7 ${visualLines.length ? Math.round((
|
|
63249
|
-
const fg = (color, text) =>
|
|
63736
|
+
const { visualLines, skippedCount } = truncateToVisualLines(bodyText, state2.lastHeight, inner);
|
|
63737
|
+
const maxScroll = Math.max(0, visualLines.length - state2.lastHeight);
|
|
63738
|
+
if (state2.autoScroll) state2.scroll = maxScroll;
|
|
63739
|
+
state2.scroll = Math.min(state2.scroll, maxScroll);
|
|
63740
|
+
const visible = visualLines.slice(state2.scroll, state2.scroll + state2.lastHeight);
|
|
63741
|
+
const statusLine = `${visualLines.length} lines \xB7 ${visualLines.length ? Math.round((state2.scroll + visible.length) / visualLines.length * 100) : 100}% \xB7 auto-scroll ${state2.autoScroll ? "on" : "off"}`;
|
|
63742
|
+
const fg = (color, text) => state2.theme.fg(color, text);
|
|
63250
63743
|
const row = (text) => `${fg("border", "\u2502")} ${pad(truncate(text, inner), inner)} ${fg("border", "\u2502")}`;
|
|
63251
63744
|
const linesOut = [
|
|
63252
63745
|
fg("border", `\u256D${"\u2500".repeat(inner + 2)}\u256E`),
|
|
@@ -63815,11 +64308,11 @@ function extractDwfPhaseState(events) {
|
|
|
63815
64308
|
});
|
|
63816
64309
|
return { phases, currentPhase };
|
|
63817
64310
|
}
|
|
63818
|
-
function renderDwfPhaseLines(
|
|
64311
|
+
function renderDwfPhaseLines(state2, options) {
|
|
63819
64312
|
const ascii = options?.ascii === true;
|
|
63820
64313
|
const lines = [];
|
|
63821
|
-
if (
|
|
63822
|
-
for (const entry of
|
|
64314
|
+
if (state2.phases.length > 1) lines.push(DWF_PHASE_HEADER);
|
|
64315
|
+
for (const entry of state2.phases) {
|
|
63823
64316
|
lines.push(` ${markerFor(entry.status, ascii)} Phase: ${entry.name}`);
|
|
63824
64317
|
}
|
|
63825
64318
|
return lines;
|
|
@@ -65747,24 +66240,24 @@ var init_mascot = __esm({
|
|
|
65747
66240
|
}
|
|
65748
66241
|
}
|
|
65749
66242
|
tickTypewriter() {
|
|
65750
|
-
const
|
|
65751
|
-
if (
|
|
66243
|
+
const state2 = this.effectState;
|
|
66244
|
+
if (state2.pos === void 0) return true;
|
|
65752
66245
|
for (let i = 0; i < 18; i++) {
|
|
65753
|
-
const row = Math.floor(
|
|
65754
|
-
const x =
|
|
66246
|
+
const row = Math.floor(state2.pos / ARMIN_WIDTH);
|
|
66247
|
+
const x = state2.pos % ARMIN_WIDTH;
|
|
65755
66248
|
if (row >= ARMIN_DISPLAY_HEIGHT) return true;
|
|
65756
66249
|
this.currentArminGrid[row][x] = this.finalArminGrid[row][x];
|
|
65757
|
-
|
|
66250
|
+
state2.pos++;
|
|
65758
66251
|
}
|
|
65759
66252
|
return false;
|
|
65760
66253
|
}
|
|
65761
66254
|
tickScanline() {
|
|
65762
|
-
const
|
|
65763
|
-
if (
|
|
66255
|
+
const state2 = this.effectState;
|
|
66256
|
+
if (state2.row === void 0) return true;
|
|
65764
66257
|
for (let step = 0; step < 3; step++) {
|
|
65765
|
-
if (
|
|
65766
|
-
for (let x = 0; x < ARMIN_WIDTH; x++) this.currentArminGrid[
|
|
65767
|
-
|
|
66258
|
+
if (state2.row >= ARMIN_DISPLAY_HEIGHT) return true;
|
|
66259
|
+
for (let x = 0; x < ARMIN_WIDTH; x++) this.currentArminGrid[state2.row][x] = this.finalArminGrid[state2.row][x];
|
|
66260
|
+
state2.row++;
|
|
65768
66261
|
}
|
|
65769
66262
|
return false;
|
|
65770
66263
|
}
|
|
@@ -65800,33 +66293,33 @@ var init_mascot = __esm({
|
|
|
65800
66293
|
return allSettled;
|
|
65801
66294
|
}
|
|
65802
66295
|
tickFade() {
|
|
65803
|
-
const
|
|
65804
|
-
if (!
|
|
66296
|
+
const state2 = this.effectState;
|
|
66297
|
+
if (!state2.positions || state2.idx === void 0) return true;
|
|
65805
66298
|
for (let i = 0; i < 54; i++) {
|
|
65806
|
-
if (
|
|
65807
|
-
const [row, x] =
|
|
66299
|
+
if (state2.idx >= state2.positions.length) return true;
|
|
66300
|
+
const [row, x] = state2.positions[state2.idx];
|
|
65808
66301
|
this.currentArminGrid[row][x] = this.finalArminGrid[row][x];
|
|
65809
|
-
|
|
66302
|
+
state2.idx++;
|
|
65810
66303
|
}
|
|
65811
66304
|
return false;
|
|
65812
66305
|
}
|
|
65813
66306
|
tickCrt() {
|
|
65814
|
-
const
|
|
65815
|
-
if (
|
|
66307
|
+
const state2 = this.effectState;
|
|
66308
|
+
if (state2.expansion === void 0) return true;
|
|
65816
66309
|
const midRow = Math.floor(ARMIN_DISPLAY_HEIGHT / 2);
|
|
65817
66310
|
this.currentArminGrid = emptyArminGrid();
|
|
65818
|
-
const top = midRow -
|
|
65819
|
-
const bottom = midRow +
|
|
66311
|
+
const top = midRow - state2.expansion;
|
|
66312
|
+
const bottom = midRow + state2.expansion;
|
|
65820
66313
|
for (let row = Math.max(0, top); row <= Math.min(ARMIN_DISPLAY_HEIGHT - 1, bottom); row++) {
|
|
65821
66314
|
for (let x = 0; x < ARMIN_WIDTH; x++) this.currentArminGrid[row][x] = this.finalArminGrid[row][x];
|
|
65822
66315
|
}
|
|
65823
|
-
|
|
65824
|
-
return
|
|
66316
|
+
state2.expansion += 3;
|
|
66317
|
+
return state2.expansion > ARMIN_DISPLAY_HEIGHT;
|
|
65825
66318
|
}
|
|
65826
66319
|
tickGlitch() {
|
|
65827
|
-
const
|
|
65828
|
-
if (
|
|
65829
|
-
if (
|
|
66320
|
+
const state2 = this.effectState;
|
|
66321
|
+
if (state2.phase === void 0 || state2.glitchFrames === void 0) return true;
|
|
66322
|
+
if (state2.phase < state2.glitchFrames) {
|
|
65830
66323
|
const offset = Math.floor(Math.random() * 7) - 3;
|
|
65831
66324
|
const swapRow = Math.floor(Math.random() * ARMIN_DISPLAY_HEIGHT);
|
|
65832
66325
|
const doShift = Math.random() < 0.3;
|
|
@@ -65841,7 +66334,7 @@ var init_mascot = __esm({
|
|
|
65841
66334
|
}
|
|
65842
66335
|
}
|
|
65843
66336
|
}
|
|
65844
|
-
|
|
66337
|
+
state2.phase += 3;
|
|
65845
66338
|
return false;
|
|
65846
66339
|
}
|
|
65847
66340
|
for (let row = 0; row < ARMIN_DISPLAY_HEIGHT; row++) {
|
|
@@ -65852,13 +66345,13 @@ var init_mascot = __esm({
|
|
|
65852
66345
|
return true;
|
|
65853
66346
|
}
|
|
65854
66347
|
tickDissolve() {
|
|
65855
|
-
const
|
|
65856
|
-
if (!
|
|
66348
|
+
const state2 = this.effectState;
|
|
66349
|
+
if (!state2.positions || state2.idx === void 0) return true;
|
|
65857
66350
|
for (let i = 0; i < 66; i++) {
|
|
65858
|
-
if (
|
|
65859
|
-
const [row, x] =
|
|
66351
|
+
if (state2.idx >= state2.positions.length) return true;
|
|
66352
|
+
const [row, x] = state2.positions[state2.idx];
|
|
65860
66353
|
this.currentArminGrid[row][x] = this.finalArminGrid[row][x];
|
|
65861
|
-
|
|
66354
|
+
state2.idx++;
|
|
65862
66355
|
}
|
|
65863
66356
|
return false;
|
|
65864
66357
|
}
|
|
@@ -68647,16 +69140,16 @@ async function importLiveRunSidebar() {
|
|
|
68647
69140
|
}
|
|
68648
69141
|
return _cachedLiveRunSidebar;
|
|
68649
69142
|
}
|
|
68650
|
-
async function installLiveSidebar(ctx, runId,
|
|
69143
|
+
async function installLiveSidebar(ctx, runId, state2, deps) {
|
|
68651
69144
|
const uiConfig = loadConfig(ctx.cwd).config.ui;
|
|
68652
69145
|
const autoOpen = uiConfig?.autoOpenDashboard === true;
|
|
68653
69146
|
const foregroundAutoOpen = uiConfig?.autoOpenDashboardForForegroundRuns ?? DEFAULT_UI.autoOpenDashboardForForegroundRuns;
|
|
68654
69147
|
if (!ctx.hasUI || !autoOpen || !foregroundAutoOpen || (uiConfig?.dashboardPlacement ?? DEFAULT_UI.dashboardPlacement) !== "right") {
|
|
68655
69148
|
return false;
|
|
68656
69149
|
}
|
|
68657
|
-
if (
|
|
68658
|
-
|
|
68659
|
-
|
|
69150
|
+
if (state2.liveSidebarRunId === runId) return true;
|
|
69151
|
+
state2.liveSidebarRunId = runId;
|
|
69152
|
+
state2.dashboardOpened = true;
|
|
68660
69153
|
const widgetPlacement = uiConfig?.widgetPlacement ?? DEFAULT_UI.widgetPlacement;
|
|
68661
69154
|
setExtensionWidget(ctx, "pi-crew", void 0, { placement: widgetPlacement });
|
|
68662
69155
|
setExtensionWidget(ctx, "pi-crew-active", void 0, { placement: widgetPlacement });
|
|
@@ -68692,7 +69185,7 @@ async function installLiveSidebar(ctx, runId, state, deps) {
|
|
|
68692
69185
|
}
|
|
68693
69186
|
}
|
|
68694
69187
|
).finally(() => {
|
|
68695
|
-
if (
|
|
69188
|
+
if (state2.liveSidebarRunId === runId) state2.liveSidebarRunId = void 0;
|
|
68696
69189
|
const c = deps.getCurrentCtx();
|
|
68697
69190
|
if (!c) return;
|
|
68698
69191
|
updateCrewWidget(
|
|
@@ -68709,12 +69202,12 @@ async function installLiveSidebar(ctx, runId, state, deps) {
|
|
|
68709
69202
|
return false;
|
|
68710
69203
|
}
|
|
68711
69204
|
}
|
|
68712
|
-
function clearDashboardPowerbar(
|
|
69205
|
+
function clearDashboardPowerbar(state2, deps) {
|
|
68713
69206
|
const c = deps.getCurrentCtx();
|
|
68714
69207
|
if (c) stopCrewWidget(c, deps.widgetState, loadConfig(c.cwd).config.ui);
|
|
68715
69208
|
clearPiCrewPowerbar(deps.pi.events);
|
|
68716
69209
|
resetPowerbarDedupState();
|
|
68717
|
-
|
|
69210
|
+
state2.dashboardOpened = false;
|
|
68718
69211
|
}
|
|
68719
69212
|
function registerPowerbarSegments(deps, uiConfig) {
|
|
68720
69213
|
registerPiCrewPowerbarSegments(deps.pi.events, uiConfig);
|
|
@@ -68920,31 +69413,31 @@ function markDeadAsyncRunIfNeeded(run, now = Date.now(), quietMs = 3e4) {
|
|
|
68920
69413
|
return failed;
|
|
68921
69414
|
});
|
|
68922
69415
|
}
|
|
68923
|
-
function startAsyncRunNotifier(ctx,
|
|
68924
|
-
if (
|
|
68925
|
-
const generation = options.generation ?? (
|
|
68926
|
-
|
|
69416
|
+
function startAsyncRunNotifier(ctx, state2, intervalMs = 5e3, options = {}) {
|
|
69417
|
+
if (state2.interval) clearInterval(state2.interval);
|
|
69418
|
+
const generation = options.generation ?? (state2.generation ?? 0) + 1;
|
|
69419
|
+
state2.generation = generation;
|
|
68927
69420
|
const startedAtMs = Date.now();
|
|
68928
|
-
const staleBeforeMs =
|
|
69421
|
+
const staleBeforeMs = state2.lastStoppedAtMs ?? startedAtMs;
|
|
68929
69422
|
const sid = extractSessionId(ctx);
|
|
68930
69423
|
const ownsRun = (run) => !sid || !run.ownerSessionId || run.ownerSessionId === sid;
|
|
68931
69424
|
for (const run of listRuns(ctx.cwd).filter(ownsRun)) {
|
|
68932
69425
|
const updatedAtMs = timeMs(run.updatedAt) ?? 0;
|
|
68933
|
-
if (isFinished2(run.status) && updatedAtMs < staleBeforeMs)
|
|
69426
|
+
if (isFinished2(run.status) && updatedAtMs < staleBeforeMs) state2.seenFinishedRunIds.add(run.runId);
|
|
68934
69427
|
}
|
|
68935
69428
|
let cachedRuns;
|
|
68936
|
-
|
|
69429
|
+
state2.interval = setInterval(() => {
|
|
68937
69430
|
try {
|
|
68938
69431
|
if (options.isCurrent && !options.isCurrent(generation)) return;
|
|
68939
69432
|
const nowMs3 = Date.now();
|
|
68940
|
-
if (cachedRuns === void 0 || nowMs3 - (
|
|
69433
|
+
if (cachedRuns === void 0 || nowMs3 - (state2.lastListRunsMs ?? 0) > LIST_RUNS_DEBOUNCE_MS) {
|
|
68941
69434
|
cachedRuns = listRuns(ctx.cwd).filter(ownsRun).slice(0, 20);
|
|
68942
|
-
|
|
69435
|
+
state2.lastListRunsMs = nowMs3;
|
|
68943
69436
|
}
|
|
68944
69437
|
for (const run of cachedRuns) {
|
|
68945
69438
|
const current = markDeadAsyncRunIfNeeded(run) ?? run;
|
|
68946
|
-
if (!isFinished2(current.status) ||
|
|
68947
|
-
|
|
69439
|
+
if (!isFinished2(current.status) || state2.seenFinishedRunIds.has(current.runId)) continue;
|
|
69440
|
+
state2.seenFinishedRunIds.add(current.runId);
|
|
68948
69441
|
if (current.workflow === "goal-turn" && current.team.startsWith("goal-")) continue;
|
|
68949
69442
|
const level = current.status === "completed" ? "info" : current.status === "cancelled" ? "warning" : "error";
|
|
68950
69443
|
ctx.ui.notify(`pi-crew run ${current.status}: ${current.runId} (${current.team}/${current.workflow ?? "none"})`, level);
|
|
@@ -68957,13 +69450,13 @@ function startAsyncRunNotifier(ctx, state, intervalMs = 5e3, options = {}) {
|
|
|
68957
69450
|
logInternalError("async-notifier", error, `interval=${intervalMs}`);
|
|
68958
69451
|
}
|
|
68959
69452
|
}, intervalMs);
|
|
68960
|
-
if (typeof
|
|
69453
|
+
if (typeof state2.interval.unref === "function") state2.interval.unref();
|
|
68961
69454
|
}
|
|
68962
|
-
function stopAsyncRunNotifier(
|
|
68963
|
-
if (
|
|
68964
|
-
|
|
68965
|
-
|
|
68966
|
-
|
|
69455
|
+
function stopAsyncRunNotifier(state2) {
|
|
69456
|
+
if (state2.interval) clearInterval(state2.interval);
|
|
69457
|
+
state2.interval = void 0;
|
|
69458
|
+
state2.generation = (state2.generation ?? 0) + 1;
|
|
69459
|
+
state2.lastStoppedAtMs = Date.now();
|
|
68967
69460
|
}
|
|
68968
69461
|
var LIST_RUNS_DEBOUNCE_MS;
|
|
68969
69462
|
var init_async_notifier = __esm({
|
|
@@ -69419,7 +69912,7 @@ var init_overflow_recovery = __esm({
|
|
|
69419
69912
|
default:
|
|
69420
69913
|
break;
|
|
69421
69914
|
}
|
|
69422
|
-
const
|
|
69915
|
+
const state2 = {
|
|
69423
69916
|
taskId,
|
|
69424
69917
|
runId,
|
|
69425
69918
|
phase,
|
|
@@ -69428,14 +69921,14 @@ var init_overflow_recovery = __esm({
|
|
|
69428
69921
|
compactionCount,
|
|
69429
69922
|
retryCount
|
|
69430
69923
|
};
|
|
69431
|
-
this.states.set(key,
|
|
69924
|
+
this.states.set(key, state2);
|
|
69432
69925
|
this.resetTimeout(key);
|
|
69433
69926
|
if (this.states.size > MAX_TRACKED_STATES) {
|
|
69434
69927
|
this.evictOldestTerminalState();
|
|
69435
69928
|
}
|
|
69436
69929
|
if (previousPhase !== phase && this.callbacks.onPhaseChange) {
|
|
69437
69930
|
try {
|
|
69438
|
-
this.callbacks.onPhaseChange(
|
|
69931
|
+
this.callbacks.onPhaseChange(state2, previousPhase);
|
|
69439
69932
|
} catch (error) {
|
|
69440
69933
|
logInternalError("overflow-recovery.onPhaseChange", error, `taskId=${taskId}`);
|
|
69441
69934
|
}
|
|
@@ -69444,13 +69937,13 @@ var init_overflow_recovery = __esm({
|
|
|
69444
69937
|
}
|
|
69445
69938
|
getState(taskId, runId) {
|
|
69446
69939
|
if (runId) return this.states.get(this.keyFor(taskId, runId));
|
|
69447
|
-
return [...this.states.values()].find((
|
|
69940
|
+
return [...this.states.values()].find((state2) => state2.taskId === taskId);
|
|
69448
69941
|
}
|
|
69449
69942
|
getPhase(taskId, runId) {
|
|
69450
69943
|
return this.getState(taskId, runId)?.phase ?? "none";
|
|
69451
69944
|
}
|
|
69452
69945
|
removeTask(taskId, runId) {
|
|
69453
|
-
const keys = runId ? [this.keyFor(taskId, runId)] : [...this.states.entries()].filter(([,
|
|
69946
|
+
const keys = runId ? [this.keyFor(taskId, runId)] : [...this.states.entries()].filter(([, state2]) => state2.taskId === taskId).map(([key]) => key);
|
|
69454
69947
|
for (const key of keys) this.removeKey(key);
|
|
69455
69948
|
}
|
|
69456
69949
|
/**
|
|
@@ -69462,10 +69955,10 @@ var init_overflow_recovery = __esm({
|
|
|
69462
69955
|
evictOldestTerminalState() {
|
|
69463
69956
|
let oldestKey;
|
|
69464
69957
|
let oldestTimestamp = Infinity;
|
|
69465
|
-
for (const [key,
|
|
69466
|
-
const isTerminal =
|
|
69467
|
-
if (isTerminal &&
|
|
69468
|
-
oldestTimestamp =
|
|
69958
|
+
for (const [key, state2] of this.states) {
|
|
69959
|
+
const isTerminal = state2.phase === "recovered" || state2.phase === "failed" || state2.phase === "none";
|
|
69960
|
+
if (isTerminal && state2.lastEventAt < oldestTimestamp) {
|
|
69961
|
+
oldestTimestamp = state2.lastEventAt;
|
|
69469
69962
|
oldestKey = key;
|
|
69470
69963
|
}
|
|
69471
69964
|
}
|
|
@@ -69494,27 +69987,27 @@ var init_overflow_recovery = __esm({
|
|
|
69494
69987
|
const timeoutMs = current?.phase === "recovered" || current?.phase === "failed" || current?.phase === "none" ? TERMINAL_STATE_TTL_MS : PHASE_TIMEOUT_MS;
|
|
69495
69988
|
const timer = setTimeout(() => {
|
|
69496
69989
|
this.timers.delete(key);
|
|
69497
|
-
const
|
|
69498
|
-
if (!
|
|
69499
|
-
if (
|
|
69990
|
+
const state2 = this.states.get(key);
|
|
69991
|
+
if (!state2) return;
|
|
69992
|
+
if (state2.phase === "recovered" || state2.phase === "failed" || state2.phase === "none") {
|
|
69500
69993
|
this.states.delete(key);
|
|
69501
69994
|
return;
|
|
69502
69995
|
}
|
|
69503
|
-
const previousPhase =
|
|
69504
|
-
|
|
69505
|
-
|
|
69996
|
+
const previousPhase = state2.phase;
|
|
69997
|
+
state2.phase = "failed";
|
|
69998
|
+
state2.lastEventAt = Date.now();
|
|
69506
69999
|
if (this.callbacks.onTimeout) {
|
|
69507
70000
|
try {
|
|
69508
|
-
this.callbacks.onTimeout(
|
|
70001
|
+
this.callbacks.onTimeout(state2);
|
|
69509
70002
|
} catch (error) {
|
|
69510
|
-
logInternalError("overflow-recovery.onTimeout", error, `taskId=${
|
|
70003
|
+
logInternalError("overflow-recovery.onTimeout", error, `taskId=${state2.taskId}`);
|
|
69511
70004
|
}
|
|
69512
70005
|
}
|
|
69513
70006
|
if (this.callbacks.onPhaseChange) {
|
|
69514
70007
|
try {
|
|
69515
|
-
this.callbacks.onPhaseChange(
|
|
70008
|
+
this.callbacks.onPhaseChange(state2, previousPhase);
|
|
69516
70009
|
} catch (error) {
|
|
69517
|
-
logInternalError("overflow-recovery.onPhaseChange-timeout", error, `taskId=${
|
|
70010
|
+
logInternalError("overflow-recovery.onPhaseChange-timeout", error, `taskId=${state2.taskId}`);
|
|
69518
70011
|
}
|
|
69519
70012
|
}
|
|
69520
70013
|
}, timeoutMs);
|
|
@@ -69535,10 +70028,10 @@ __export(lifecycle_exports, {
|
|
|
69535
70028
|
startLifecycleWatchers: () => startLifecycleWatchers,
|
|
69536
70029
|
stopLifecycleWatchers: () => stopLifecycleWatchers
|
|
69537
70030
|
});
|
|
69538
|
-
function startLifecycleWatchers(ctx,
|
|
69539
|
-
if (
|
|
70031
|
+
function startLifecycleWatchers(ctx, state2, deps) {
|
|
70032
|
+
if (state2.notifierStarted) return false;
|
|
69540
70033
|
const loadedConfig = loadConfig(ctx.cwd);
|
|
69541
|
-
|
|
70034
|
+
state2.notifierStarted = true;
|
|
69542
70035
|
try {
|
|
69543
70036
|
void Promise.resolve().then(() => (init_async_notifier(), async_notifier_exports)).then(({ startAsyncRunNotifier: startAsyncRunNotifier2 }) => {
|
|
69544
70037
|
if (deps.isCleanedUp()) return;
|
|
@@ -69549,14 +70042,14 @@ function startLifecycleWatchers(ctx, state, deps) {
|
|
|
69549
70042
|
});
|
|
69550
70043
|
return true;
|
|
69551
70044
|
} catch (error) {
|
|
69552
|
-
|
|
70045
|
+
state2.notifierStarted = false;
|
|
69553
70046
|
logInternalError("register.startLifecycleWatchers", error);
|
|
69554
70047
|
return false;
|
|
69555
70048
|
}
|
|
69556
70049
|
}
|
|
69557
|
-
function stopLifecycleWatchers(
|
|
69558
|
-
if (!
|
|
69559
|
-
|
|
70050
|
+
function stopLifecycleWatchers(state2, deps) {
|
|
70051
|
+
if (!state2.notifierStarted) return;
|
|
70052
|
+
state2.notifierStarted = false;
|
|
69560
70053
|
try {
|
|
69561
70054
|
void Promise.resolve().then(() => (init_async_notifier(), async_notifier_exports)).then(({ stopAsyncRunNotifier: stopAsyncRunNotifier2 }) => {
|
|
69562
70055
|
stopAsyncRunNotifier2(deps.notifierState);
|
|
@@ -69565,11 +70058,11 @@ function stopLifecycleWatchers(state, deps) {
|
|
|
69565
70058
|
logInternalError("register.stopLifecycleWatchers", error);
|
|
69566
70059
|
}
|
|
69567
70060
|
}
|
|
69568
|
-
async function configureNotifications(ctx,
|
|
69569
|
-
|
|
69570
|
-
|
|
69571
|
-
|
|
69572
|
-
|
|
70061
|
+
async function configureNotifications(ctx, state2, deps) {
|
|
70062
|
+
state2.notificationRouter?.dispose();
|
|
70063
|
+
state2.notificationSink?.dispose();
|
|
70064
|
+
state2.notificationRouter = void 0;
|
|
70065
|
+
state2.notificationSink = void 0;
|
|
69573
70066
|
const config = loadConfig(ctx.cwd).config;
|
|
69574
70067
|
if (config.notifications?.enabled === false) return;
|
|
69575
70068
|
const { NotificationRouter: NotificationRouter2 } = await Promise.resolve().then(() => (init_notification_router(), notification_router_exports));
|
|
@@ -69577,18 +70070,18 @@ async function configureNotifications(ctx, state, deps) {
|
|
|
69577
70070
|
const { sendFollowUp: sendFollowUp2 } = await Promise.resolve().then(() => (init_subagent_helpers(), subagent_helpers_exports));
|
|
69578
70071
|
const { updateCrewWidget: updateCrewWidget2 } = await Promise.resolve().then(() => (init_widget(), widget_exports));
|
|
69579
70072
|
if (config.telemetry?.enabled !== false) {
|
|
69580
|
-
|
|
70073
|
+
state2.notificationSink = createJsonlSink2(
|
|
69581
70074
|
projectCrewRoot(ctx.cwd),
|
|
69582
70075
|
config.notifications?.sinkRetentionDays ?? DEFAULT_NOTIFICATIONS.sinkRetentionDays
|
|
69583
70076
|
);
|
|
69584
70077
|
}
|
|
69585
|
-
|
|
70078
|
+
state2.notificationRouter = new NotificationRouter2(
|
|
69586
70079
|
{
|
|
69587
70080
|
dedupWindowMs: config.notifications?.dedupWindowMs ?? DEFAULT_NOTIFICATIONS.dedupWindowMs,
|
|
69588
70081
|
batchWindowMs: config.notifications?.batchWindowMs ?? DEFAULT_NOTIFICATIONS.batchWindowMs,
|
|
69589
70082
|
quietHours: config.notifications?.quietHours,
|
|
69590
70083
|
severityFilter: config.notifications?.severityFilter ?? [...DEFAULT_NOTIFICATIONS.severityFilter],
|
|
69591
|
-
sink: (notification) =>
|
|
70084
|
+
sink: (notification) => state2.notificationSink?.write(notification)
|
|
69592
70085
|
},
|
|
69593
70086
|
(notification) => {
|
|
69594
70087
|
deps.widgetState.notificationCount = (deps.widgetState.notificationCount ?? 0) + 1;
|
|
@@ -69619,20 +70112,20 @@ async function configureNotifications(ctx, state, deps) {
|
|
|
69619
70112
|
}
|
|
69620
70113
|
);
|
|
69621
70114
|
}
|
|
69622
|
-
function disposeNotifications(
|
|
69623
|
-
|
|
69624
|
-
|
|
69625
|
-
|
|
69626
|
-
|
|
69627
|
-
}
|
|
69628
|
-
async function configureDeliveryCoordinator(
|
|
69629
|
-
|
|
69630
|
-
|
|
69631
|
-
|
|
69632
|
-
|
|
70115
|
+
function disposeNotifications(state2) {
|
|
70116
|
+
state2.notificationRouter?.dispose();
|
|
70117
|
+
state2.notificationRouter = void 0;
|
|
70118
|
+
state2.notificationSink?.dispose();
|
|
70119
|
+
state2.notificationSink = void 0;
|
|
70120
|
+
}
|
|
70121
|
+
async function configureDeliveryCoordinator(state2, deps) {
|
|
70122
|
+
state2.deliveryCoordinator?.dispose();
|
|
70123
|
+
state2.deliveryCoordinator = void 0;
|
|
70124
|
+
state2.overflowTracker?.dispose();
|
|
70125
|
+
state2.overflowTracker = void 0;
|
|
69633
70126
|
const { DeliveryCoordinator: DeliveryCoordinator2 } = await Promise.resolve().then(() => (init_delivery_coordinator(), delivery_coordinator_exports));
|
|
69634
70127
|
const { OverflowRecoveryTracker: OverflowRecoveryTracker2 } = await Promise.resolve().then(() => (init_overflow_recovery(), overflow_recovery_exports));
|
|
69635
|
-
|
|
70128
|
+
state2.deliveryCoordinator = new DeliveryCoordinator2({
|
|
69636
70129
|
emit: (event, data) => {
|
|
69637
70130
|
deps.pi.events?.emit?.(event, data);
|
|
69638
70131
|
},
|
|
@@ -69643,7 +70136,7 @@ async function configureDeliveryCoordinator(state, deps) {
|
|
|
69643
70136
|
deps.sendAgentWakeUp(deps.pi, message);
|
|
69644
70137
|
}
|
|
69645
70138
|
});
|
|
69646
|
-
|
|
70139
|
+
state2.overflowTracker = new OverflowRecoveryTracker2({
|
|
69647
70140
|
onPhaseChange: (phaseState, previousPhase) => {
|
|
69648
70141
|
if (deps.observabilityState.metricRegistry) {
|
|
69649
70142
|
deps.observabilityState.metricRegistry.counter("crew.task.overflow_recovery_total", "Overflow recovery phase transitions").inc({
|
|
@@ -69670,11 +70163,11 @@ async function configureDeliveryCoordinator(state, deps) {
|
|
|
69670
70163
|
}
|
|
69671
70164
|
});
|
|
69672
70165
|
}
|
|
69673
|
-
function disposeDeliveryCoordinator(
|
|
69674
|
-
|
|
69675
|
-
|
|
69676
|
-
|
|
69677
|
-
|
|
70166
|
+
function disposeDeliveryCoordinator(state2) {
|
|
70167
|
+
state2.deliveryCoordinator?.dispose();
|
|
70168
|
+
state2.deliveryCoordinator = void 0;
|
|
70169
|
+
state2.overflowTracker?.dispose();
|
|
70170
|
+
state2.overflowTracker = void 0;
|
|
69678
70171
|
}
|
|
69679
70172
|
var init_lifecycle = __esm({
|
|
69680
70173
|
"src/extension/registration/lifecycle.ts"() {
|
|
@@ -70651,21 +71144,21 @@ async function importOTLPExporter() {
|
|
|
70651
71144
|
}
|
|
70652
71145
|
return _cachedOTLPExporter;
|
|
70653
71146
|
}
|
|
70654
|
-
async function configureObservability(ctx,
|
|
70655
|
-
await disposeObservability(
|
|
71147
|
+
async function configureObservability(ctx, state2, deps) {
|
|
71148
|
+
await disposeObservability(state2, deps.isCleanedUp());
|
|
70656
71149
|
const config = loadConfig(ctx.cwd).config;
|
|
70657
71150
|
if (config.observability?.enabled === false) return;
|
|
70658
71151
|
const { createMetricRegistry: createMetricRegistry2 } = await Promise.resolve().then(() => (init_metric_registry(), metric_registry_exports));
|
|
70659
71152
|
const { wireEventToMetrics: wireEventToMetrics2 } = await Promise.resolve().then(() => (init_event_to_metric(), event_to_metric_exports));
|
|
70660
71153
|
const { createMetricFileSink: createMetricFileSink2 } = await Promise.resolve().then(() => (init_metric_sink(), metric_sink_exports));
|
|
70661
|
-
|
|
71154
|
+
state2.metricRegistry = createMetricRegistry2();
|
|
70662
71155
|
if (deps.pi.events) {
|
|
70663
|
-
|
|
71156
|
+
state2.eventMetricSub = wireEventToMetrics2(deps.pi.events, state2.metricRegistry);
|
|
70664
71157
|
}
|
|
70665
71158
|
if (config.telemetry?.enabled !== false) {
|
|
70666
|
-
|
|
71159
|
+
state2.metricSink = createMetricFileSink2({
|
|
70667
71160
|
crewRoot: projectCrewRoot(ctx.cwd),
|
|
70668
|
-
registry:
|
|
71161
|
+
registry: state2.metricRegistry,
|
|
70669
71162
|
retentionDays: config.observability?.metricRetentionDays ?? 7
|
|
70670
71163
|
});
|
|
70671
71164
|
}
|
|
@@ -70673,10 +71166,10 @@ async function configureObservability(ctx, state, deps) {
|
|
|
70673
71166
|
const otlpEndpoint = config.otlp.endpoint;
|
|
70674
71167
|
const otlpHeaders = config.otlp.headers;
|
|
70675
71168
|
const otlpInterval = config.otlp.intervalMs;
|
|
70676
|
-
const owningRegistry =
|
|
71169
|
+
const owningRegistry = state2.metricRegistry;
|
|
70677
71170
|
void importOTLPExporter().then((Ctor) => {
|
|
70678
|
-
if (deps.isCleanedUp() ||
|
|
70679
|
-
|
|
71171
|
+
if (deps.isCleanedUp() || state2.metricRegistry !== owningRegistry || !owningRegistry) return;
|
|
71172
|
+
state2.otlpExporter = new Ctor(
|
|
70680
71173
|
{
|
|
70681
71174
|
endpoint: otlpEndpoint,
|
|
70682
71175
|
headers: otlpHeaders,
|
|
@@ -70684,15 +71177,15 @@ async function configureObservability(ctx, state, deps) {
|
|
|
70684
71177
|
},
|
|
70685
71178
|
owningRegistry
|
|
70686
71179
|
);
|
|
70687
|
-
|
|
71180
|
+
state2.otlpExporter?.start();
|
|
70688
71181
|
}).catch((error) => logInternalError("register.otlp-lazy-import", error));
|
|
70689
71182
|
}
|
|
70690
71183
|
const { HeartbeatWatcher: HeartbeatWatcher2 } = await Promise.resolve().then(() => (init_heartbeat_watcher(), heartbeat_watcher_exports));
|
|
70691
|
-
|
|
71184
|
+
state2.heartbeatWatcher = new HeartbeatWatcher2({
|
|
70692
71185
|
cwd: ctx.cwd,
|
|
70693
71186
|
pollIntervalMs: config.observability?.pollIntervalMs ?? 5e3,
|
|
70694
71187
|
manifestCache: deps.getManifestCache(ctx.cwd),
|
|
70695
|
-
registry:
|
|
71188
|
+
registry: state2.metricRegistry,
|
|
70696
71189
|
router: {
|
|
70697
71190
|
enqueue: (notification) => {
|
|
70698
71191
|
deps.notifyOperator(notification);
|
|
@@ -70708,7 +71201,7 @@ async function configureObservability(ctx, state, deps) {
|
|
|
70708
71201
|
attempts: 0,
|
|
70709
71202
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
70710
71203
|
});
|
|
70711
|
-
|
|
71204
|
+
state2.metricRegistry?.counter("crew.task.deadletter_total", "Deadletter triggers by reason").inc({ reason: "heartbeat-dead" });
|
|
70712
71205
|
deps.pi.events?.emit?.("crew.task.deadletter", {
|
|
70713
71206
|
runId: manifest.runId,
|
|
70714
71207
|
taskId,
|
|
@@ -70716,7 +71209,7 @@ async function configureObservability(ctx, state, deps) {
|
|
|
70716
71209
|
});
|
|
70717
71210
|
}
|
|
70718
71211
|
});
|
|
70719
|
-
|
|
71212
|
+
state2.heartbeatWatcher.start();
|
|
70720
71213
|
try {
|
|
70721
71214
|
deps.pi.on?.("before_agent_start", () => {
|
|
70722
71215
|
if (deps.isCleanedUp()) return;
|
|
@@ -70730,7 +71223,7 @@ async function configureObservability(ctx, state, deps) {
|
|
|
70730
71223
|
}
|
|
70731
71224
|
const autoRepairIntervalMs = config.reliability?.autoRepairIntervalMs ?? 3e5;
|
|
70732
71225
|
if (autoRepairIntervalMs > 0) {
|
|
70733
|
-
|
|
71226
|
+
state2.autoRepairTimer = setInterval(() => {
|
|
70734
71227
|
if (deps.isCleanedUp()) return;
|
|
70735
71228
|
try {
|
|
70736
71229
|
const staleResults = deps.reconcileStaleRuns(ctx.cwd, deps.getManifestCache(ctx.cwd), extractSessionId(ctx));
|
|
@@ -70753,8 +71246,8 @@ async function configureObservability(ctx, state, deps) {
|
|
|
70753
71246
|
logInternalError("register.autoRepair", error);
|
|
70754
71247
|
}
|
|
70755
71248
|
}, autoRepairIntervalMs);
|
|
70756
|
-
|
|
70757
|
-
|
|
71249
|
+
state2.autoRepairTimer.unref();
|
|
71250
|
+
state2.tempReconcileTimer = setInterval(() => {
|
|
70758
71251
|
if (deps.isCleanedUp()) return;
|
|
70759
71252
|
try {
|
|
70760
71253
|
deps.reconcileOrphanedTempWorkspaces(Date.now(), {
|
|
@@ -70784,7 +71277,7 @@ async function configureObservability(ctx, state, deps) {
|
|
|
70784
71277
|
logInternalError("register.tempAutoRepair", error);
|
|
70785
71278
|
}
|
|
70786
71279
|
}, autoRepairIntervalMs * 5);
|
|
70787
|
-
|
|
71280
|
+
state2.tempReconcileTimer.unref();
|
|
70788
71281
|
}
|
|
70789
71282
|
if (config.reliability?.autoRecover === true) {
|
|
70790
71283
|
const cwdSnapshot = ctx.cwd;
|
|
@@ -70805,25 +71298,25 @@ async function configureObservability(ctx, state, deps) {
|
|
|
70805
71298
|
}).catch((error) => logInternalError("register.crash-recovery-lazy-import", error));
|
|
70806
71299
|
}
|
|
70807
71300
|
}
|
|
70808
|
-
async function disposeObservability(
|
|
70809
|
-
|
|
70810
|
-
|
|
70811
|
-
if (
|
|
70812
|
-
clearInterval(
|
|
70813
|
-
|
|
71301
|
+
async function disposeObservability(state2, _isCleanedUp) {
|
|
71302
|
+
state2.heartbeatWatcher?.dispose();
|
|
71303
|
+
state2.heartbeatWatcher = void 0;
|
|
71304
|
+
if (state2.autoRepairTimer) {
|
|
71305
|
+
clearInterval(state2.autoRepairTimer);
|
|
71306
|
+
state2.autoRepairTimer = void 0;
|
|
70814
71307
|
}
|
|
70815
|
-
if (
|
|
70816
|
-
clearInterval(
|
|
70817
|
-
|
|
71308
|
+
if (state2.tempReconcileTimer) {
|
|
71309
|
+
clearInterval(state2.tempReconcileTimer);
|
|
71310
|
+
state2.tempReconcileTimer = void 0;
|
|
70818
71311
|
}
|
|
70819
|
-
|
|
70820
|
-
|
|
70821
|
-
|
|
70822
|
-
|
|
70823
|
-
await
|
|
70824
|
-
|
|
70825
|
-
|
|
70826
|
-
|
|
71312
|
+
state2.metricSink?.dispose();
|
|
71313
|
+
state2.metricSink = void 0;
|
|
71314
|
+
state2.eventMetricSub?.dispose();
|
|
71315
|
+
state2.eventMetricSub = void 0;
|
|
71316
|
+
await state2.otlpExporter?.dispose();
|
|
71317
|
+
state2.otlpExporter = void 0;
|
|
71318
|
+
state2.metricRegistry?.dispose();
|
|
71319
|
+
state2.metricRegistry = void 0;
|
|
70827
71320
|
}
|
|
70828
71321
|
var _cachedOTLPExporter;
|
|
70829
71322
|
var init_observability = __esm({
|
|
@@ -76163,7 +76656,10 @@ function safeStringify(value) {
|
|
|
76163
76656
|
// src/extension/registration/lifecycle-handlers.ts
|
|
76164
76657
|
init_child_pi();
|
|
76165
76658
|
init_live_agent_manager();
|
|
76659
|
+
init_model_fallback();
|
|
76166
76660
|
init_pi_args();
|
|
76661
|
+
init_provider_quota();
|
|
76662
|
+
init_session_model();
|
|
76167
76663
|
init_orphan_worker_registry();
|
|
76168
76664
|
init_crash_recovery();
|
|
76169
76665
|
init_scheduler();
|
|
@@ -76449,6 +76945,20 @@ function installSessionLifecycleHandlers(pi, ctx) {
|
|
|
76449
76945
|
installSessionShutdownHandler(pi, ctx);
|
|
76450
76946
|
installSessionStartHandler(pi, ctx);
|
|
76451
76947
|
installSessionBeforeSwitchHandler(pi, ctx);
|
|
76948
|
+
installModelTrackingHandlers(pi);
|
|
76949
|
+
}
|
|
76950
|
+
function installModelTrackingHandlers(pi) {
|
|
76951
|
+
pi.on("model_select", (event) => {
|
|
76952
|
+
noteSessionModel(event.model);
|
|
76953
|
+
});
|
|
76954
|
+
pi.on("thinking_level_select", (event) => {
|
|
76955
|
+
noteSessionThinking(event.level);
|
|
76956
|
+
});
|
|
76957
|
+
pi.on("after_provider_response", (event) => {
|
|
76958
|
+
const model = currentSessionModel();
|
|
76959
|
+
const provider = model ? providerOfModelRef(model) : void 0;
|
|
76960
|
+
if (provider) noteProviderResponse(provider, event.status, event.headers);
|
|
76961
|
+
});
|
|
76452
76962
|
}
|
|
76453
76963
|
function installSessionShutdownHandler(pi, ctx) {
|
|
76454
76964
|
pi.on("session_shutdown", (event) => {
|
|
@@ -76478,6 +76988,7 @@ function installSessionBeforeSwitchHandler(pi, ctx) {
|
|
|
76478
76988
|
ctx.lifecycleState.deliveryCoordinator?.deactivate();
|
|
76479
76989
|
resetPowerbarDedupState();
|
|
76480
76990
|
stopAsyncRunNotifier(ctx.notifierState);
|
|
76991
|
+
clearProviderQuotaCache();
|
|
76481
76992
|
ctx.stopSessionBoundSubagents();
|
|
76482
76993
|
});
|
|
76483
76994
|
}
|
|
@@ -76498,6 +77009,8 @@ function installSessionStartHandler(pi, ctx) {
|
|
|
76498
77009
|
ctx.sessionGeneration++;
|
|
76499
77010
|
const ownerGeneration = ctx.sessionGeneration;
|
|
76500
77011
|
ctx.currentCtx = extensionCtx;
|
|
77012
|
+
noteSessionModel(extensionCtx.model, "session_start");
|
|
77013
|
+
noteSessionThinking(extensionCtx.thinkingLevel);
|
|
76501
77014
|
if (!ctx.crewAutocompleteRegistered) {
|
|
76502
77015
|
ctx.crewAutocompleteRegistered = true;
|
|
76503
77016
|
registerCrewAutocomplete(extensionCtx);
|