pi-crew 0.9.59 → 0.9.61
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 +48 -0
- package/dist/index.mjs +1264 -743
- 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,
|
|
@@ -59396,7 +59883,8 @@ var init_chain_executor = __esm({
|
|
|
59396
59883
|
# Current Chain Step
|
|
59397
59884
|
${packet.goal}` : packet.goal;
|
|
59398
59885
|
const stepTeam = context.__chainStepTeam ?? this.overrides.team ?? "default";
|
|
59399
|
-
const
|
|
59886
|
+
const rawWorkflow = context.__chainStepWorkflow ?? this.overrides.workflow;
|
|
59887
|
+
const stepWorkflow = rawWorkflow === "chain" ? void 0 : rawWorkflow;
|
|
59400
59888
|
const stepModel = context.__chainStepModel ?? this.overrides.model;
|
|
59401
59889
|
const runParams = {
|
|
59402
59890
|
action: "run",
|
|
@@ -59441,6 +59929,13 @@ async function handleChainRun(params, ctx, handleRun3) {
|
|
|
59441
59929
|
if (!chainString || chainString.trim().length === 0) {
|
|
59442
59930
|
return result("Chain expression is empty.", { action: "run", status: "error" }, true);
|
|
59443
59931
|
}
|
|
59932
|
+
if (params.workflow === "chain") {
|
|
59933
|
+
return result(
|
|
59934
|
+
"Workflow 'chain' cannot be combined with a chain run: the chain runner owns step execution and has no per-step workflow. Omit `workflow` (steps use the team's defaultWorkflow) or use @team references in the chain expression.",
|
|
59935
|
+
{ action: "run", status: "error" },
|
|
59936
|
+
true
|
|
59937
|
+
);
|
|
59938
|
+
}
|
|
59444
59939
|
const spec = parseChainString(chainString);
|
|
59445
59940
|
if (spec.steps.length === 0) {
|
|
59446
59941
|
return result(
|
|
@@ -59454,8 +59949,8 @@ async function handleChainRun(params, ctx, handleRun3) {
|
|
|
59454
59949
|
ctx,
|
|
59455
59950
|
overrides: {
|
|
59456
59951
|
team: params.team,
|
|
59457
|
-
workflow
|
|
59458
|
-
model: params.model
|
|
59952
|
+
// workflow intentionally omitted — chain runner owns step execution (bug-44)
|
|
59953
|
+
...params.model ? { model: params.model } : {}
|
|
59459
59954
|
}
|
|
59460
59955
|
});
|
|
59461
59956
|
const runner = new ChainRunner(executor, new HandoffManager());
|
|
@@ -59720,14 +60215,14 @@ var init_dwf_state_store = __esm({
|
|
|
59720
60215
|
}
|
|
59721
60216
|
}
|
|
59722
60217
|
/** Atomically persist a checkpoint state. Stamps `updatedAt` (callers need not set it). */
|
|
59723
|
-
save(
|
|
60218
|
+
save(state2) {
|
|
59724
60219
|
const path89 = this.path;
|
|
59725
|
-
const next = { ...
|
|
60220
|
+
const next = { ...state2, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
59726
60221
|
try {
|
|
59727
60222
|
mkdirSync39(dirname37(path89), { recursive: true });
|
|
59728
60223
|
atomicWriteJson(path89, next);
|
|
59729
60224
|
} catch (error) {
|
|
59730
|
-
logInternalError("dwf-state-store.save", error, `runId=${
|
|
60225
|
+
logInternalError("dwf-state-store.save", error, `runId=${state2.runId}`);
|
|
59731
60226
|
throw error;
|
|
59732
60227
|
}
|
|
59733
60228
|
}
|
|
@@ -60626,9 +61121,9 @@ async function runDynamicWorkflow(input) {
|
|
|
60626
61121
|
resumedState,
|
|
60627
61122
|
// round-18 P2-3: checkpoint after each ctx.agent() call so a crash between calls
|
|
60628
61123
|
// leaves durable state. onCheckpoint captures the closure values at call time.
|
|
60629
|
-
onCheckpoint: (
|
|
61124
|
+
onCheckpoint: (state2) => {
|
|
60630
61125
|
try {
|
|
60631
|
-
dwfStore.save(
|
|
61126
|
+
dwfStore.save(state2);
|
|
60632
61127
|
} catch (error) {
|
|
60633
61128
|
logInternalError("dynamic-workflow-runner.checkpoint-save", error, `runId=${manifest.runId}`);
|
|
60634
61129
|
}
|
|
@@ -61012,7 +61507,7 @@ Commit or stash changes before using worktree mode, or use workspaceMode: 'singl
|
|
|
61012
61507
|
workspaceMode: params.workspaceMode
|
|
61013
61508
|
} : teams.find((item) => item.name === teamName);
|
|
61014
61509
|
if (!team) return result(`Team '${teamName}' not found.`, { action: "run", status: "error" }, true);
|
|
61015
|
-
const workflowName = directAgent ? "direct-agent" : params.workflow ?? team.defaultWorkflow ?? "default";
|
|
61510
|
+
const workflowName = directAgent ? "direct-agent" : params.workflow === "chain" && !params.chain ? team.defaultWorkflow ?? "default" : params.workflow ?? team.defaultWorkflow ?? "default";
|
|
61016
61511
|
const baseWorkflow = directAgent ? {
|
|
61017
61512
|
name: "direct-agent",
|
|
61018
61513
|
description: `Direct task for ${directAgent.name}`,
|
|
@@ -61226,6 +61721,10 @@ ${dwfResult.manifest.summary ?? ""}`,
|
|
|
61226
61721
|
...updatedManifest,
|
|
61227
61722
|
runtimeResolution,
|
|
61228
61723
|
runConfig: executedConfig,
|
|
61724
|
+
// Background/async runs re-enter through background-runner in a detached
|
|
61725
|
+
// process with no ExtensionContext. Snapshot the model routing inputs so
|
|
61726
|
+
// they survive the hand-off instead of being rediscovered from models.json.
|
|
61727
|
+
modelContext: captureRunModelContext(ctx, params.model),
|
|
61229
61728
|
// Persist budget config on the manifest so it's observable post-run
|
|
61230
61729
|
// (events.jsonl, status reads, audits). The team-runner reads these
|
|
61231
61730
|
// from the input, but persisting them means consumers can verify
|
|
@@ -61436,7 +61935,7 @@ ${dwfResult.manifest.summary ?? ""}`,
|
|
|
61436
61935
|
runtime,
|
|
61437
61936
|
runtimeConfig: executedConfig.runtime,
|
|
61438
61937
|
parentContext: buildParentContext(ctx),
|
|
61439
|
-
parentModel: ctx.model,
|
|
61938
|
+
parentModel: resolveParentModel(ctx.model),
|
|
61440
61939
|
modelRegistry: ctx.modelRegistry,
|
|
61441
61940
|
modelOverride: params.model,
|
|
61442
61941
|
skillOverride,
|
|
@@ -61502,7 +62001,7 @@ ${dwfResult.manifest.summary ?? ""}`,
|
|
|
61502
62001
|
runtime,
|
|
61503
62002
|
runtimeConfig: executedConfig.runtime,
|
|
61504
62003
|
parentContext: buildParentContext(ctx),
|
|
61505
|
-
parentModel: ctx.model,
|
|
62004
|
+
parentModel: resolveParentModel(ctx.model),
|
|
61506
62005
|
modelRegistry: ctx.modelRegistry,
|
|
61507
62006
|
modelOverride: params.model,
|
|
61508
62007
|
skillOverride,
|
|
@@ -61551,6 +62050,7 @@ var init_run2 = __esm({
|
|
|
61551
62050
|
init_param_error();
|
|
61552
62051
|
init_async_runner();
|
|
61553
62052
|
init_runtime_resolver();
|
|
62053
|
+
init_session_model();
|
|
61554
62054
|
init_event_log();
|
|
61555
62055
|
init_i18n();
|
|
61556
62056
|
init_async_marker();
|
|
@@ -61959,7 +62459,7 @@ async function handleResume2(params, ctx) {
|
|
|
61959
62459
|
runtime: decision2.runtime,
|
|
61960
62460
|
runtimeConfig: decision2.executedConfig.runtime,
|
|
61961
62461
|
parentContext: buildParentContext(ctx),
|
|
61962
|
-
parentModel: ctx.model,
|
|
62462
|
+
parentModel: resolveParentModel(ctx.model),
|
|
61963
62463
|
modelRegistry: ctx.modelRegistry,
|
|
61964
62464
|
modelOverride: params.model,
|
|
61965
62465
|
skillOverride: decision2.resumeSkillOverride,
|
|
@@ -62206,6 +62706,7 @@ var init_team_tool2 = __esm({
|
|
|
62206
62706
|
init_run_index();
|
|
62207
62707
|
init_direct_run();
|
|
62208
62708
|
init_runtime_resolver();
|
|
62709
|
+
init_session_model();
|
|
62209
62710
|
init_pi_json_output();
|
|
62210
62711
|
init_config_patch();
|
|
62211
62712
|
init_context();
|
|
@@ -63237,16 +63738,16 @@ function readRunTranscript(manifest, taskId, options = {}) {
|
|
|
63237
63738
|
readRunTranscriptCache.set(cacheKey2, { parsedAt: now, result: result4 });
|
|
63238
63739
|
return result4;
|
|
63239
63740
|
}
|
|
63240
|
-
function renderViewerBase(
|
|
63741
|
+
function renderViewerBase(state2, width, lines, title, subtitle) {
|
|
63241
63742
|
const inner = Math.max(20, width - 4);
|
|
63242
63743
|
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) =>
|
|
63744
|
+
const { visualLines, skippedCount } = truncateToVisualLines(bodyText, state2.lastHeight, inner);
|
|
63745
|
+
const maxScroll = Math.max(0, visualLines.length - state2.lastHeight);
|
|
63746
|
+
if (state2.autoScroll) state2.scroll = maxScroll;
|
|
63747
|
+
state2.scroll = Math.min(state2.scroll, maxScroll);
|
|
63748
|
+
const visible = visualLines.slice(state2.scroll, state2.scroll + state2.lastHeight);
|
|
63749
|
+
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"}`;
|
|
63750
|
+
const fg = (color, text) => state2.theme.fg(color, text);
|
|
63250
63751
|
const row = (text) => `${fg("border", "\u2502")} ${pad(truncate(text, inner), inner)} ${fg("border", "\u2502")}`;
|
|
63251
63752
|
const linesOut = [
|
|
63252
63753
|
fg("border", `\u256D${"\u2500".repeat(inner + 2)}\u256E`),
|
|
@@ -63815,11 +64316,11 @@ function extractDwfPhaseState(events) {
|
|
|
63815
64316
|
});
|
|
63816
64317
|
return { phases, currentPhase };
|
|
63817
64318
|
}
|
|
63818
|
-
function renderDwfPhaseLines(
|
|
64319
|
+
function renderDwfPhaseLines(state2, options) {
|
|
63819
64320
|
const ascii = options?.ascii === true;
|
|
63820
64321
|
const lines = [];
|
|
63821
|
-
if (
|
|
63822
|
-
for (const entry of
|
|
64322
|
+
if (state2.phases.length > 1) lines.push(DWF_PHASE_HEADER);
|
|
64323
|
+
for (const entry of state2.phases) {
|
|
63823
64324
|
lines.push(` ${markerFor(entry.status, ascii)} Phase: ${entry.name}`);
|
|
63824
64325
|
}
|
|
63825
64326
|
return lines;
|
|
@@ -65747,24 +66248,24 @@ var init_mascot = __esm({
|
|
|
65747
66248
|
}
|
|
65748
66249
|
}
|
|
65749
66250
|
tickTypewriter() {
|
|
65750
|
-
const
|
|
65751
|
-
if (
|
|
66251
|
+
const state2 = this.effectState;
|
|
66252
|
+
if (state2.pos === void 0) return true;
|
|
65752
66253
|
for (let i = 0; i < 18; i++) {
|
|
65753
|
-
const row = Math.floor(
|
|
65754
|
-
const x =
|
|
66254
|
+
const row = Math.floor(state2.pos / ARMIN_WIDTH);
|
|
66255
|
+
const x = state2.pos % ARMIN_WIDTH;
|
|
65755
66256
|
if (row >= ARMIN_DISPLAY_HEIGHT) return true;
|
|
65756
66257
|
this.currentArminGrid[row][x] = this.finalArminGrid[row][x];
|
|
65757
|
-
|
|
66258
|
+
state2.pos++;
|
|
65758
66259
|
}
|
|
65759
66260
|
return false;
|
|
65760
66261
|
}
|
|
65761
66262
|
tickScanline() {
|
|
65762
|
-
const
|
|
65763
|
-
if (
|
|
66263
|
+
const state2 = this.effectState;
|
|
66264
|
+
if (state2.row === void 0) return true;
|
|
65764
66265
|
for (let step = 0; step < 3; step++) {
|
|
65765
|
-
if (
|
|
65766
|
-
for (let x = 0; x < ARMIN_WIDTH; x++) this.currentArminGrid[
|
|
65767
|
-
|
|
66266
|
+
if (state2.row >= ARMIN_DISPLAY_HEIGHT) return true;
|
|
66267
|
+
for (let x = 0; x < ARMIN_WIDTH; x++) this.currentArminGrid[state2.row][x] = this.finalArminGrid[state2.row][x];
|
|
66268
|
+
state2.row++;
|
|
65768
66269
|
}
|
|
65769
66270
|
return false;
|
|
65770
66271
|
}
|
|
@@ -65800,33 +66301,33 @@ var init_mascot = __esm({
|
|
|
65800
66301
|
return allSettled;
|
|
65801
66302
|
}
|
|
65802
66303
|
tickFade() {
|
|
65803
|
-
const
|
|
65804
|
-
if (!
|
|
66304
|
+
const state2 = this.effectState;
|
|
66305
|
+
if (!state2.positions || state2.idx === void 0) return true;
|
|
65805
66306
|
for (let i = 0; i < 54; i++) {
|
|
65806
|
-
if (
|
|
65807
|
-
const [row, x] =
|
|
66307
|
+
if (state2.idx >= state2.positions.length) return true;
|
|
66308
|
+
const [row, x] = state2.positions[state2.idx];
|
|
65808
66309
|
this.currentArminGrid[row][x] = this.finalArminGrid[row][x];
|
|
65809
|
-
|
|
66310
|
+
state2.idx++;
|
|
65810
66311
|
}
|
|
65811
66312
|
return false;
|
|
65812
66313
|
}
|
|
65813
66314
|
tickCrt() {
|
|
65814
|
-
const
|
|
65815
|
-
if (
|
|
66315
|
+
const state2 = this.effectState;
|
|
66316
|
+
if (state2.expansion === void 0) return true;
|
|
65816
66317
|
const midRow = Math.floor(ARMIN_DISPLAY_HEIGHT / 2);
|
|
65817
66318
|
this.currentArminGrid = emptyArminGrid();
|
|
65818
|
-
const top = midRow -
|
|
65819
|
-
const bottom = midRow +
|
|
66319
|
+
const top = midRow - state2.expansion;
|
|
66320
|
+
const bottom = midRow + state2.expansion;
|
|
65820
66321
|
for (let row = Math.max(0, top); row <= Math.min(ARMIN_DISPLAY_HEIGHT - 1, bottom); row++) {
|
|
65821
66322
|
for (let x = 0; x < ARMIN_WIDTH; x++) this.currentArminGrid[row][x] = this.finalArminGrid[row][x];
|
|
65822
66323
|
}
|
|
65823
|
-
|
|
65824
|
-
return
|
|
66324
|
+
state2.expansion += 3;
|
|
66325
|
+
return state2.expansion > ARMIN_DISPLAY_HEIGHT;
|
|
65825
66326
|
}
|
|
65826
66327
|
tickGlitch() {
|
|
65827
|
-
const
|
|
65828
|
-
if (
|
|
65829
|
-
if (
|
|
66328
|
+
const state2 = this.effectState;
|
|
66329
|
+
if (state2.phase === void 0 || state2.glitchFrames === void 0) return true;
|
|
66330
|
+
if (state2.phase < state2.glitchFrames) {
|
|
65830
66331
|
const offset = Math.floor(Math.random() * 7) - 3;
|
|
65831
66332
|
const swapRow = Math.floor(Math.random() * ARMIN_DISPLAY_HEIGHT);
|
|
65832
66333
|
const doShift = Math.random() < 0.3;
|
|
@@ -65841,7 +66342,7 @@ var init_mascot = __esm({
|
|
|
65841
66342
|
}
|
|
65842
66343
|
}
|
|
65843
66344
|
}
|
|
65844
|
-
|
|
66345
|
+
state2.phase += 3;
|
|
65845
66346
|
return false;
|
|
65846
66347
|
}
|
|
65847
66348
|
for (let row = 0; row < ARMIN_DISPLAY_HEIGHT; row++) {
|
|
@@ -65852,13 +66353,13 @@ var init_mascot = __esm({
|
|
|
65852
66353
|
return true;
|
|
65853
66354
|
}
|
|
65854
66355
|
tickDissolve() {
|
|
65855
|
-
const
|
|
65856
|
-
if (!
|
|
66356
|
+
const state2 = this.effectState;
|
|
66357
|
+
if (!state2.positions || state2.idx === void 0) return true;
|
|
65857
66358
|
for (let i = 0; i < 66; i++) {
|
|
65858
|
-
if (
|
|
65859
|
-
const [row, x] =
|
|
66359
|
+
if (state2.idx >= state2.positions.length) return true;
|
|
66360
|
+
const [row, x] = state2.positions[state2.idx];
|
|
65860
66361
|
this.currentArminGrid[row][x] = this.finalArminGrid[row][x];
|
|
65861
|
-
|
|
66362
|
+
state2.idx++;
|
|
65862
66363
|
}
|
|
65863
66364
|
return false;
|
|
65864
66365
|
}
|
|
@@ -68647,16 +69148,16 @@ async function importLiveRunSidebar() {
|
|
|
68647
69148
|
}
|
|
68648
69149
|
return _cachedLiveRunSidebar;
|
|
68649
69150
|
}
|
|
68650
|
-
async function installLiveSidebar(ctx, runId,
|
|
69151
|
+
async function installLiveSidebar(ctx, runId, state2, deps) {
|
|
68651
69152
|
const uiConfig = loadConfig(ctx.cwd).config.ui;
|
|
68652
69153
|
const autoOpen = uiConfig?.autoOpenDashboard === true;
|
|
68653
69154
|
const foregroundAutoOpen = uiConfig?.autoOpenDashboardForForegroundRuns ?? DEFAULT_UI.autoOpenDashboardForForegroundRuns;
|
|
68654
69155
|
if (!ctx.hasUI || !autoOpen || !foregroundAutoOpen || (uiConfig?.dashboardPlacement ?? DEFAULT_UI.dashboardPlacement) !== "right") {
|
|
68655
69156
|
return false;
|
|
68656
69157
|
}
|
|
68657
|
-
if (
|
|
68658
|
-
|
|
68659
|
-
|
|
69158
|
+
if (state2.liveSidebarRunId === runId) return true;
|
|
69159
|
+
state2.liveSidebarRunId = runId;
|
|
69160
|
+
state2.dashboardOpened = true;
|
|
68660
69161
|
const widgetPlacement = uiConfig?.widgetPlacement ?? DEFAULT_UI.widgetPlacement;
|
|
68661
69162
|
setExtensionWidget(ctx, "pi-crew", void 0, { placement: widgetPlacement });
|
|
68662
69163
|
setExtensionWidget(ctx, "pi-crew-active", void 0, { placement: widgetPlacement });
|
|
@@ -68692,7 +69193,7 @@ async function installLiveSidebar(ctx, runId, state, deps) {
|
|
|
68692
69193
|
}
|
|
68693
69194
|
}
|
|
68694
69195
|
).finally(() => {
|
|
68695
|
-
if (
|
|
69196
|
+
if (state2.liveSidebarRunId === runId) state2.liveSidebarRunId = void 0;
|
|
68696
69197
|
const c = deps.getCurrentCtx();
|
|
68697
69198
|
if (!c) return;
|
|
68698
69199
|
updateCrewWidget(
|
|
@@ -68709,12 +69210,12 @@ async function installLiveSidebar(ctx, runId, state, deps) {
|
|
|
68709
69210
|
return false;
|
|
68710
69211
|
}
|
|
68711
69212
|
}
|
|
68712
|
-
function clearDashboardPowerbar(
|
|
69213
|
+
function clearDashboardPowerbar(state2, deps) {
|
|
68713
69214
|
const c = deps.getCurrentCtx();
|
|
68714
69215
|
if (c) stopCrewWidget(c, deps.widgetState, loadConfig(c.cwd).config.ui);
|
|
68715
69216
|
clearPiCrewPowerbar(deps.pi.events);
|
|
68716
69217
|
resetPowerbarDedupState();
|
|
68717
|
-
|
|
69218
|
+
state2.dashboardOpened = false;
|
|
68718
69219
|
}
|
|
68719
69220
|
function registerPowerbarSegments(deps, uiConfig) {
|
|
68720
69221
|
registerPiCrewPowerbarSegments(deps.pi.events, uiConfig);
|
|
@@ -68920,31 +69421,31 @@ function markDeadAsyncRunIfNeeded(run, now = Date.now(), quietMs = 3e4) {
|
|
|
68920
69421
|
return failed;
|
|
68921
69422
|
});
|
|
68922
69423
|
}
|
|
68923
|
-
function startAsyncRunNotifier(ctx,
|
|
68924
|
-
if (
|
|
68925
|
-
const generation = options.generation ?? (
|
|
68926
|
-
|
|
69424
|
+
function startAsyncRunNotifier(ctx, state2, intervalMs = 5e3, options = {}) {
|
|
69425
|
+
if (state2.interval) clearInterval(state2.interval);
|
|
69426
|
+
const generation = options.generation ?? (state2.generation ?? 0) + 1;
|
|
69427
|
+
state2.generation = generation;
|
|
68927
69428
|
const startedAtMs = Date.now();
|
|
68928
|
-
const staleBeforeMs =
|
|
69429
|
+
const staleBeforeMs = state2.lastStoppedAtMs ?? startedAtMs;
|
|
68929
69430
|
const sid = extractSessionId(ctx);
|
|
68930
69431
|
const ownsRun = (run) => !sid || !run.ownerSessionId || run.ownerSessionId === sid;
|
|
68931
69432
|
for (const run of listRuns(ctx.cwd).filter(ownsRun)) {
|
|
68932
69433
|
const updatedAtMs = timeMs(run.updatedAt) ?? 0;
|
|
68933
|
-
if (isFinished2(run.status) && updatedAtMs < staleBeforeMs)
|
|
69434
|
+
if (isFinished2(run.status) && updatedAtMs < staleBeforeMs) state2.seenFinishedRunIds.add(run.runId);
|
|
68934
69435
|
}
|
|
68935
69436
|
let cachedRuns;
|
|
68936
|
-
|
|
69437
|
+
state2.interval = setInterval(() => {
|
|
68937
69438
|
try {
|
|
68938
69439
|
if (options.isCurrent && !options.isCurrent(generation)) return;
|
|
68939
69440
|
const nowMs3 = Date.now();
|
|
68940
|
-
if (cachedRuns === void 0 || nowMs3 - (
|
|
69441
|
+
if (cachedRuns === void 0 || nowMs3 - (state2.lastListRunsMs ?? 0) > LIST_RUNS_DEBOUNCE_MS) {
|
|
68941
69442
|
cachedRuns = listRuns(ctx.cwd).filter(ownsRun).slice(0, 20);
|
|
68942
|
-
|
|
69443
|
+
state2.lastListRunsMs = nowMs3;
|
|
68943
69444
|
}
|
|
68944
69445
|
for (const run of cachedRuns) {
|
|
68945
69446
|
const current = markDeadAsyncRunIfNeeded(run) ?? run;
|
|
68946
|
-
if (!isFinished2(current.status) ||
|
|
68947
|
-
|
|
69447
|
+
if (!isFinished2(current.status) || state2.seenFinishedRunIds.has(current.runId)) continue;
|
|
69448
|
+
state2.seenFinishedRunIds.add(current.runId);
|
|
68948
69449
|
if (current.workflow === "goal-turn" && current.team.startsWith("goal-")) continue;
|
|
68949
69450
|
const level = current.status === "completed" ? "info" : current.status === "cancelled" ? "warning" : "error";
|
|
68950
69451
|
ctx.ui.notify(`pi-crew run ${current.status}: ${current.runId} (${current.team}/${current.workflow ?? "none"})`, level);
|
|
@@ -68957,13 +69458,13 @@ function startAsyncRunNotifier(ctx, state, intervalMs = 5e3, options = {}) {
|
|
|
68957
69458
|
logInternalError("async-notifier", error, `interval=${intervalMs}`);
|
|
68958
69459
|
}
|
|
68959
69460
|
}, intervalMs);
|
|
68960
|
-
if (typeof
|
|
69461
|
+
if (typeof state2.interval.unref === "function") state2.interval.unref();
|
|
68961
69462
|
}
|
|
68962
|
-
function stopAsyncRunNotifier(
|
|
68963
|
-
if (
|
|
68964
|
-
|
|
68965
|
-
|
|
68966
|
-
|
|
69463
|
+
function stopAsyncRunNotifier(state2) {
|
|
69464
|
+
if (state2.interval) clearInterval(state2.interval);
|
|
69465
|
+
state2.interval = void 0;
|
|
69466
|
+
state2.generation = (state2.generation ?? 0) + 1;
|
|
69467
|
+
state2.lastStoppedAtMs = Date.now();
|
|
68967
69468
|
}
|
|
68968
69469
|
var LIST_RUNS_DEBOUNCE_MS;
|
|
68969
69470
|
var init_async_notifier = __esm({
|
|
@@ -69419,7 +69920,7 @@ var init_overflow_recovery = __esm({
|
|
|
69419
69920
|
default:
|
|
69420
69921
|
break;
|
|
69421
69922
|
}
|
|
69422
|
-
const
|
|
69923
|
+
const state2 = {
|
|
69423
69924
|
taskId,
|
|
69424
69925
|
runId,
|
|
69425
69926
|
phase,
|
|
@@ -69428,14 +69929,14 @@ var init_overflow_recovery = __esm({
|
|
|
69428
69929
|
compactionCount,
|
|
69429
69930
|
retryCount
|
|
69430
69931
|
};
|
|
69431
|
-
this.states.set(key,
|
|
69932
|
+
this.states.set(key, state2);
|
|
69432
69933
|
this.resetTimeout(key);
|
|
69433
69934
|
if (this.states.size > MAX_TRACKED_STATES) {
|
|
69434
69935
|
this.evictOldestTerminalState();
|
|
69435
69936
|
}
|
|
69436
69937
|
if (previousPhase !== phase && this.callbacks.onPhaseChange) {
|
|
69437
69938
|
try {
|
|
69438
|
-
this.callbacks.onPhaseChange(
|
|
69939
|
+
this.callbacks.onPhaseChange(state2, previousPhase);
|
|
69439
69940
|
} catch (error) {
|
|
69440
69941
|
logInternalError("overflow-recovery.onPhaseChange", error, `taskId=${taskId}`);
|
|
69441
69942
|
}
|
|
@@ -69444,13 +69945,13 @@ var init_overflow_recovery = __esm({
|
|
|
69444
69945
|
}
|
|
69445
69946
|
getState(taskId, runId) {
|
|
69446
69947
|
if (runId) return this.states.get(this.keyFor(taskId, runId));
|
|
69447
|
-
return [...this.states.values()].find((
|
|
69948
|
+
return [...this.states.values()].find((state2) => state2.taskId === taskId);
|
|
69448
69949
|
}
|
|
69449
69950
|
getPhase(taskId, runId) {
|
|
69450
69951
|
return this.getState(taskId, runId)?.phase ?? "none";
|
|
69451
69952
|
}
|
|
69452
69953
|
removeTask(taskId, runId) {
|
|
69453
|
-
const keys = runId ? [this.keyFor(taskId, runId)] : [...this.states.entries()].filter(([,
|
|
69954
|
+
const keys = runId ? [this.keyFor(taskId, runId)] : [...this.states.entries()].filter(([, state2]) => state2.taskId === taskId).map(([key]) => key);
|
|
69454
69955
|
for (const key of keys) this.removeKey(key);
|
|
69455
69956
|
}
|
|
69456
69957
|
/**
|
|
@@ -69462,10 +69963,10 @@ var init_overflow_recovery = __esm({
|
|
|
69462
69963
|
evictOldestTerminalState() {
|
|
69463
69964
|
let oldestKey;
|
|
69464
69965
|
let oldestTimestamp = Infinity;
|
|
69465
|
-
for (const [key,
|
|
69466
|
-
const isTerminal =
|
|
69467
|
-
if (isTerminal &&
|
|
69468
|
-
oldestTimestamp =
|
|
69966
|
+
for (const [key, state2] of this.states) {
|
|
69967
|
+
const isTerminal = state2.phase === "recovered" || state2.phase === "failed" || state2.phase === "none";
|
|
69968
|
+
if (isTerminal && state2.lastEventAt < oldestTimestamp) {
|
|
69969
|
+
oldestTimestamp = state2.lastEventAt;
|
|
69469
69970
|
oldestKey = key;
|
|
69470
69971
|
}
|
|
69471
69972
|
}
|
|
@@ -69494,27 +69995,27 @@ var init_overflow_recovery = __esm({
|
|
|
69494
69995
|
const timeoutMs = current?.phase === "recovered" || current?.phase === "failed" || current?.phase === "none" ? TERMINAL_STATE_TTL_MS : PHASE_TIMEOUT_MS;
|
|
69495
69996
|
const timer = setTimeout(() => {
|
|
69496
69997
|
this.timers.delete(key);
|
|
69497
|
-
const
|
|
69498
|
-
if (!
|
|
69499
|
-
if (
|
|
69998
|
+
const state2 = this.states.get(key);
|
|
69999
|
+
if (!state2) return;
|
|
70000
|
+
if (state2.phase === "recovered" || state2.phase === "failed" || state2.phase === "none") {
|
|
69500
70001
|
this.states.delete(key);
|
|
69501
70002
|
return;
|
|
69502
70003
|
}
|
|
69503
|
-
const previousPhase =
|
|
69504
|
-
|
|
69505
|
-
|
|
70004
|
+
const previousPhase = state2.phase;
|
|
70005
|
+
state2.phase = "failed";
|
|
70006
|
+
state2.lastEventAt = Date.now();
|
|
69506
70007
|
if (this.callbacks.onTimeout) {
|
|
69507
70008
|
try {
|
|
69508
|
-
this.callbacks.onTimeout(
|
|
70009
|
+
this.callbacks.onTimeout(state2);
|
|
69509
70010
|
} catch (error) {
|
|
69510
|
-
logInternalError("overflow-recovery.onTimeout", error, `taskId=${
|
|
70011
|
+
logInternalError("overflow-recovery.onTimeout", error, `taskId=${state2.taskId}`);
|
|
69511
70012
|
}
|
|
69512
70013
|
}
|
|
69513
70014
|
if (this.callbacks.onPhaseChange) {
|
|
69514
70015
|
try {
|
|
69515
|
-
this.callbacks.onPhaseChange(
|
|
70016
|
+
this.callbacks.onPhaseChange(state2, previousPhase);
|
|
69516
70017
|
} catch (error) {
|
|
69517
|
-
logInternalError("overflow-recovery.onPhaseChange-timeout", error, `taskId=${
|
|
70018
|
+
logInternalError("overflow-recovery.onPhaseChange-timeout", error, `taskId=${state2.taskId}`);
|
|
69518
70019
|
}
|
|
69519
70020
|
}
|
|
69520
70021
|
}, timeoutMs);
|
|
@@ -69535,10 +70036,10 @@ __export(lifecycle_exports, {
|
|
|
69535
70036
|
startLifecycleWatchers: () => startLifecycleWatchers,
|
|
69536
70037
|
stopLifecycleWatchers: () => stopLifecycleWatchers
|
|
69537
70038
|
});
|
|
69538
|
-
function startLifecycleWatchers(ctx,
|
|
69539
|
-
if (
|
|
70039
|
+
function startLifecycleWatchers(ctx, state2, deps) {
|
|
70040
|
+
if (state2.notifierStarted) return false;
|
|
69540
70041
|
const loadedConfig = loadConfig(ctx.cwd);
|
|
69541
|
-
|
|
70042
|
+
state2.notifierStarted = true;
|
|
69542
70043
|
try {
|
|
69543
70044
|
void Promise.resolve().then(() => (init_async_notifier(), async_notifier_exports)).then(({ startAsyncRunNotifier: startAsyncRunNotifier2 }) => {
|
|
69544
70045
|
if (deps.isCleanedUp()) return;
|
|
@@ -69549,14 +70050,14 @@ function startLifecycleWatchers(ctx, state, deps) {
|
|
|
69549
70050
|
});
|
|
69550
70051
|
return true;
|
|
69551
70052
|
} catch (error) {
|
|
69552
|
-
|
|
70053
|
+
state2.notifierStarted = false;
|
|
69553
70054
|
logInternalError("register.startLifecycleWatchers", error);
|
|
69554
70055
|
return false;
|
|
69555
70056
|
}
|
|
69556
70057
|
}
|
|
69557
|
-
function stopLifecycleWatchers(
|
|
69558
|
-
if (!
|
|
69559
|
-
|
|
70058
|
+
function stopLifecycleWatchers(state2, deps) {
|
|
70059
|
+
if (!state2.notifierStarted) return;
|
|
70060
|
+
state2.notifierStarted = false;
|
|
69560
70061
|
try {
|
|
69561
70062
|
void Promise.resolve().then(() => (init_async_notifier(), async_notifier_exports)).then(({ stopAsyncRunNotifier: stopAsyncRunNotifier2 }) => {
|
|
69562
70063
|
stopAsyncRunNotifier2(deps.notifierState);
|
|
@@ -69565,11 +70066,11 @@ function stopLifecycleWatchers(state, deps) {
|
|
|
69565
70066
|
logInternalError("register.stopLifecycleWatchers", error);
|
|
69566
70067
|
}
|
|
69567
70068
|
}
|
|
69568
|
-
async function configureNotifications(ctx,
|
|
69569
|
-
|
|
69570
|
-
|
|
69571
|
-
|
|
69572
|
-
|
|
70069
|
+
async function configureNotifications(ctx, state2, deps) {
|
|
70070
|
+
state2.notificationRouter?.dispose();
|
|
70071
|
+
state2.notificationSink?.dispose();
|
|
70072
|
+
state2.notificationRouter = void 0;
|
|
70073
|
+
state2.notificationSink = void 0;
|
|
69573
70074
|
const config = loadConfig(ctx.cwd).config;
|
|
69574
70075
|
if (config.notifications?.enabled === false) return;
|
|
69575
70076
|
const { NotificationRouter: NotificationRouter2 } = await Promise.resolve().then(() => (init_notification_router(), notification_router_exports));
|
|
@@ -69577,18 +70078,18 @@ async function configureNotifications(ctx, state, deps) {
|
|
|
69577
70078
|
const { sendFollowUp: sendFollowUp2 } = await Promise.resolve().then(() => (init_subagent_helpers(), subagent_helpers_exports));
|
|
69578
70079
|
const { updateCrewWidget: updateCrewWidget2 } = await Promise.resolve().then(() => (init_widget(), widget_exports));
|
|
69579
70080
|
if (config.telemetry?.enabled !== false) {
|
|
69580
|
-
|
|
70081
|
+
state2.notificationSink = createJsonlSink2(
|
|
69581
70082
|
projectCrewRoot(ctx.cwd),
|
|
69582
70083
|
config.notifications?.sinkRetentionDays ?? DEFAULT_NOTIFICATIONS.sinkRetentionDays
|
|
69583
70084
|
);
|
|
69584
70085
|
}
|
|
69585
|
-
|
|
70086
|
+
state2.notificationRouter = new NotificationRouter2(
|
|
69586
70087
|
{
|
|
69587
70088
|
dedupWindowMs: config.notifications?.dedupWindowMs ?? DEFAULT_NOTIFICATIONS.dedupWindowMs,
|
|
69588
70089
|
batchWindowMs: config.notifications?.batchWindowMs ?? DEFAULT_NOTIFICATIONS.batchWindowMs,
|
|
69589
70090
|
quietHours: config.notifications?.quietHours,
|
|
69590
70091
|
severityFilter: config.notifications?.severityFilter ?? [...DEFAULT_NOTIFICATIONS.severityFilter],
|
|
69591
|
-
sink: (notification) =>
|
|
70092
|
+
sink: (notification) => state2.notificationSink?.write(notification)
|
|
69592
70093
|
},
|
|
69593
70094
|
(notification) => {
|
|
69594
70095
|
deps.widgetState.notificationCount = (deps.widgetState.notificationCount ?? 0) + 1;
|
|
@@ -69619,20 +70120,20 @@ async function configureNotifications(ctx, state, deps) {
|
|
|
69619
70120
|
}
|
|
69620
70121
|
);
|
|
69621
70122
|
}
|
|
69622
|
-
function disposeNotifications(
|
|
69623
|
-
|
|
69624
|
-
|
|
69625
|
-
|
|
69626
|
-
|
|
69627
|
-
}
|
|
69628
|
-
async function configureDeliveryCoordinator(
|
|
69629
|
-
|
|
69630
|
-
|
|
69631
|
-
|
|
69632
|
-
|
|
70123
|
+
function disposeNotifications(state2) {
|
|
70124
|
+
state2.notificationRouter?.dispose();
|
|
70125
|
+
state2.notificationRouter = void 0;
|
|
70126
|
+
state2.notificationSink?.dispose();
|
|
70127
|
+
state2.notificationSink = void 0;
|
|
70128
|
+
}
|
|
70129
|
+
async function configureDeliveryCoordinator(state2, deps) {
|
|
70130
|
+
state2.deliveryCoordinator?.dispose();
|
|
70131
|
+
state2.deliveryCoordinator = void 0;
|
|
70132
|
+
state2.overflowTracker?.dispose();
|
|
70133
|
+
state2.overflowTracker = void 0;
|
|
69633
70134
|
const { DeliveryCoordinator: DeliveryCoordinator2 } = await Promise.resolve().then(() => (init_delivery_coordinator(), delivery_coordinator_exports));
|
|
69634
70135
|
const { OverflowRecoveryTracker: OverflowRecoveryTracker2 } = await Promise.resolve().then(() => (init_overflow_recovery(), overflow_recovery_exports));
|
|
69635
|
-
|
|
70136
|
+
state2.deliveryCoordinator = new DeliveryCoordinator2({
|
|
69636
70137
|
emit: (event, data) => {
|
|
69637
70138
|
deps.pi.events?.emit?.(event, data);
|
|
69638
70139
|
},
|
|
@@ -69643,7 +70144,7 @@ async function configureDeliveryCoordinator(state, deps) {
|
|
|
69643
70144
|
deps.sendAgentWakeUp(deps.pi, message);
|
|
69644
70145
|
}
|
|
69645
70146
|
});
|
|
69646
|
-
|
|
70147
|
+
state2.overflowTracker = new OverflowRecoveryTracker2({
|
|
69647
70148
|
onPhaseChange: (phaseState, previousPhase) => {
|
|
69648
70149
|
if (deps.observabilityState.metricRegistry) {
|
|
69649
70150
|
deps.observabilityState.metricRegistry.counter("crew.task.overflow_recovery_total", "Overflow recovery phase transitions").inc({
|
|
@@ -69670,11 +70171,11 @@ async function configureDeliveryCoordinator(state, deps) {
|
|
|
69670
70171
|
}
|
|
69671
70172
|
});
|
|
69672
70173
|
}
|
|
69673
|
-
function disposeDeliveryCoordinator(
|
|
69674
|
-
|
|
69675
|
-
|
|
69676
|
-
|
|
69677
|
-
|
|
70174
|
+
function disposeDeliveryCoordinator(state2) {
|
|
70175
|
+
state2.deliveryCoordinator?.dispose();
|
|
70176
|
+
state2.deliveryCoordinator = void 0;
|
|
70177
|
+
state2.overflowTracker?.dispose();
|
|
70178
|
+
state2.overflowTracker = void 0;
|
|
69678
70179
|
}
|
|
69679
70180
|
var init_lifecycle = __esm({
|
|
69680
70181
|
"src/extension/registration/lifecycle.ts"() {
|
|
@@ -70651,21 +71152,21 @@ async function importOTLPExporter() {
|
|
|
70651
71152
|
}
|
|
70652
71153
|
return _cachedOTLPExporter;
|
|
70653
71154
|
}
|
|
70654
|
-
async function configureObservability(ctx,
|
|
70655
|
-
await disposeObservability(
|
|
71155
|
+
async function configureObservability(ctx, state2, deps) {
|
|
71156
|
+
await disposeObservability(state2, deps.isCleanedUp());
|
|
70656
71157
|
const config = loadConfig(ctx.cwd).config;
|
|
70657
71158
|
if (config.observability?.enabled === false) return;
|
|
70658
71159
|
const { createMetricRegistry: createMetricRegistry2 } = await Promise.resolve().then(() => (init_metric_registry(), metric_registry_exports));
|
|
70659
71160
|
const { wireEventToMetrics: wireEventToMetrics2 } = await Promise.resolve().then(() => (init_event_to_metric(), event_to_metric_exports));
|
|
70660
71161
|
const { createMetricFileSink: createMetricFileSink2 } = await Promise.resolve().then(() => (init_metric_sink(), metric_sink_exports));
|
|
70661
|
-
|
|
71162
|
+
state2.metricRegistry = createMetricRegistry2();
|
|
70662
71163
|
if (deps.pi.events) {
|
|
70663
|
-
|
|
71164
|
+
state2.eventMetricSub = wireEventToMetrics2(deps.pi.events, state2.metricRegistry);
|
|
70664
71165
|
}
|
|
70665
71166
|
if (config.telemetry?.enabled !== false) {
|
|
70666
|
-
|
|
71167
|
+
state2.metricSink = createMetricFileSink2({
|
|
70667
71168
|
crewRoot: projectCrewRoot(ctx.cwd),
|
|
70668
|
-
registry:
|
|
71169
|
+
registry: state2.metricRegistry,
|
|
70669
71170
|
retentionDays: config.observability?.metricRetentionDays ?? 7
|
|
70670
71171
|
});
|
|
70671
71172
|
}
|
|
@@ -70673,10 +71174,10 @@ async function configureObservability(ctx, state, deps) {
|
|
|
70673
71174
|
const otlpEndpoint = config.otlp.endpoint;
|
|
70674
71175
|
const otlpHeaders = config.otlp.headers;
|
|
70675
71176
|
const otlpInterval = config.otlp.intervalMs;
|
|
70676
|
-
const owningRegistry =
|
|
71177
|
+
const owningRegistry = state2.metricRegistry;
|
|
70677
71178
|
void importOTLPExporter().then((Ctor) => {
|
|
70678
|
-
if (deps.isCleanedUp() ||
|
|
70679
|
-
|
|
71179
|
+
if (deps.isCleanedUp() || state2.metricRegistry !== owningRegistry || !owningRegistry) return;
|
|
71180
|
+
state2.otlpExporter = new Ctor(
|
|
70680
71181
|
{
|
|
70681
71182
|
endpoint: otlpEndpoint,
|
|
70682
71183
|
headers: otlpHeaders,
|
|
@@ -70684,15 +71185,15 @@ async function configureObservability(ctx, state, deps) {
|
|
|
70684
71185
|
},
|
|
70685
71186
|
owningRegistry
|
|
70686
71187
|
);
|
|
70687
|
-
|
|
71188
|
+
state2.otlpExporter?.start();
|
|
70688
71189
|
}).catch((error) => logInternalError("register.otlp-lazy-import", error));
|
|
70689
71190
|
}
|
|
70690
71191
|
const { HeartbeatWatcher: HeartbeatWatcher2 } = await Promise.resolve().then(() => (init_heartbeat_watcher(), heartbeat_watcher_exports));
|
|
70691
|
-
|
|
71192
|
+
state2.heartbeatWatcher = new HeartbeatWatcher2({
|
|
70692
71193
|
cwd: ctx.cwd,
|
|
70693
71194
|
pollIntervalMs: config.observability?.pollIntervalMs ?? 5e3,
|
|
70694
71195
|
manifestCache: deps.getManifestCache(ctx.cwd),
|
|
70695
|
-
registry:
|
|
71196
|
+
registry: state2.metricRegistry,
|
|
70696
71197
|
router: {
|
|
70697
71198
|
enqueue: (notification) => {
|
|
70698
71199
|
deps.notifyOperator(notification);
|
|
@@ -70708,7 +71209,7 @@ async function configureObservability(ctx, state, deps) {
|
|
|
70708
71209
|
attempts: 0,
|
|
70709
71210
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
70710
71211
|
});
|
|
70711
|
-
|
|
71212
|
+
state2.metricRegistry?.counter("crew.task.deadletter_total", "Deadletter triggers by reason").inc({ reason: "heartbeat-dead" });
|
|
70712
71213
|
deps.pi.events?.emit?.("crew.task.deadletter", {
|
|
70713
71214
|
runId: manifest.runId,
|
|
70714
71215
|
taskId,
|
|
@@ -70716,7 +71217,7 @@ async function configureObservability(ctx, state, deps) {
|
|
|
70716
71217
|
});
|
|
70717
71218
|
}
|
|
70718
71219
|
});
|
|
70719
|
-
|
|
71220
|
+
state2.heartbeatWatcher.start();
|
|
70720
71221
|
try {
|
|
70721
71222
|
deps.pi.on?.("before_agent_start", () => {
|
|
70722
71223
|
if (deps.isCleanedUp()) return;
|
|
@@ -70730,7 +71231,7 @@ async function configureObservability(ctx, state, deps) {
|
|
|
70730
71231
|
}
|
|
70731
71232
|
const autoRepairIntervalMs = config.reliability?.autoRepairIntervalMs ?? 3e5;
|
|
70732
71233
|
if (autoRepairIntervalMs > 0) {
|
|
70733
|
-
|
|
71234
|
+
state2.autoRepairTimer = setInterval(() => {
|
|
70734
71235
|
if (deps.isCleanedUp()) return;
|
|
70735
71236
|
try {
|
|
70736
71237
|
const staleResults = deps.reconcileStaleRuns(ctx.cwd, deps.getManifestCache(ctx.cwd), extractSessionId(ctx));
|
|
@@ -70753,8 +71254,8 @@ async function configureObservability(ctx, state, deps) {
|
|
|
70753
71254
|
logInternalError("register.autoRepair", error);
|
|
70754
71255
|
}
|
|
70755
71256
|
}, autoRepairIntervalMs);
|
|
70756
|
-
|
|
70757
|
-
|
|
71257
|
+
state2.autoRepairTimer.unref();
|
|
71258
|
+
state2.tempReconcileTimer = setInterval(() => {
|
|
70758
71259
|
if (deps.isCleanedUp()) return;
|
|
70759
71260
|
try {
|
|
70760
71261
|
deps.reconcileOrphanedTempWorkspaces(Date.now(), {
|
|
@@ -70784,7 +71285,7 @@ async function configureObservability(ctx, state, deps) {
|
|
|
70784
71285
|
logInternalError("register.tempAutoRepair", error);
|
|
70785
71286
|
}
|
|
70786
71287
|
}, autoRepairIntervalMs * 5);
|
|
70787
|
-
|
|
71288
|
+
state2.tempReconcileTimer.unref();
|
|
70788
71289
|
}
|
|
70789
71290
|
if (config.reliability?.autoRecover === true) {
|
|
70790
71291
|
const cwdSnapshot = ctx.cwd;
|
|
@@ -70805,25 +71306,25 @@ async function configureObservability(ctx, state, deps) {
|
|
|
70805
71306
|
}).catch((error) => logInternalError("register.crash-recovery-lazy-import", error));
|
|
70806
71307
|
}
|
|
70807
71308
|
}
|
|
70808
|
-
async function disposeObservability(
|
|
70809
|
-
|
|
70810
|
-
|
|
70811
|
-
if (
|
|
70812
|
-
clearInterval(
|
|
70813
|
-
|
|
71309
|
+
async function disposeObservability(state2, _isCleanedUp) {
|
|
71310
|
+
state2.heartbeatWatcher?.dispose();
|
|
71311
|
+
state2.heartbeatWatcher = void 0;
|
|
71312
|
+
if (state2.autoRepairTimer) {
|
|
71313
|
+
clearInterval(state2.autoRepairTimer);
|
|
71314
|
+
state2.autoRepairTimer = void 0;
|
|
70814
71315
|
}
|
|
70815
|
-
if (
|
|
70816
|
-
clearInterval(
|
|
70817
|
-
|
|
71316
|
+
if (state2.tempReconcileTimer) {
|
|
71317
|
+
clearInterval(state2.tempReconcileTimer);
|
|
71318
|
+
state2.tempReconcileTimer = void 0;
|
|
70818
71319
|
}
|
|
70819
|
-
|
|
70820
|
-
|
|
70821
|
-
|
|
70822
|
-
|
|
70823
|
-
await
|
|
70824
|
-
|
|
70825
|
-
|
|
70826
|
-
|
|
71320
|
+
state2.metricSink?.dispose();
|
|
71321
|
+
state2.metricSink = void 0;
|
|
71322
|
+
state2.eventMetricSub?.dispose();
|
|
71323
|
+
state2.eventMetricSub = void 0;
|
|
71324
|
+
await state2.otlpExporter?.dispose();
|
|
71325
|
+
state2.otlpExporter = void 0;
|
|
71326
|
+
state2.metricRegistry?.dispose();
|
|
71327
|
+
state2.metricRegistry = void 0;
|
|
70827
71328
|
}
|
|
70828
71329
|
var _cachedOTLPExporter;
|
|
70829
71330
|
var init_observability = __esm({
|
|
@@ -76163,7 +76664,10 @@ function safeStringify(value) {
|
|
|
76163
76664
|
// src/extension/registration/lifecycle-handlers.ts
|
|
76164
76665
|
init_child_pi();
|
|
76165
76666
|
init_live_agent_manager();
|
|
76667
|
+
init_model_fallback();
|
|
76166
76668
|
init_pi_args();
|
|
76669
|
+
init_provider_quota();
|
|
76670
|
+
init_session_model();
|
|
76167
76671
|
init_orphan_worker_registry();
|
|
76168
76672
|
init_crash_recovery();
|
|
76169
76673
|
init_scheduler();
|
|
@@ -76449,6 +76953,20 @@ function installSessionLifecycleHandlers(pi, ctx) {
|
|
|
76449
76953
|
installSessionShutdownHandler(pi, ctx);
|
|
76450
76954
|
installSessionStartHandler(pi, ctx);
|
|
76451
76955
|
installSessionBeforeSwitchHandler(pi, ctx);
|
|
76956
|
+
installModelTrackingHandlers(pi);
|
|
76957
|
+
}
|
|
76958
|
+
function installModelTrackingHandlers(pi) {
|
|
76959
|
+
pi.on("model_select", (event) => {
|
|
76960
|
+
noteSessionModel(event.model);
|
|
76961
|
+
});
|
|
76962
|
+
pi.on("thinking_level_select", (event) => {
|
|
76963
|
+
noteSessionThinking(event.level);
|
|
76964
|
+
});
|
|
76965
|
+
pi.on("after_provider_response", (event) => {
|
|
76966
|
+
const model = currentSessionModel();
|
|
76967
|
+
const provider = model ? providerOfModelRef(model) : void 0;
|
|
76968
|
+
if (provider) noteProviderResponse(provider, event.status, event.headers);
|
|
76969
|
+
});
|
|
76452
76970
|
}
|
|
76453
76971
|
function installSessionShutdownHandler(pi, ctx) {
|
|
76454
76972
|
pi.on("session_shutdown", (event) => {
|
|
@@ -76478,6 +76996,7 @@ function installSessionBeforeSwitchHandler(pi, ctx) {
|
|
|
76478
76996
|
ctx.lifecycleState.deliveryCoordinator?.deactivate();
|
|
76479
76997
|
resetPowerbarDedupState();
|
|
76480
76998
|
stopAsyncRunNotifier(ctx.notifierState);
|
|
76999
|
+
clearProviderQuotaCache();
|
|
76481
77000
|
ctx.stopSessionBoundSubagents();
|
|
76482
77001
|
});
|
|
76483
77002
|
}
|
|
@@ -76498,6 +77017,8 @@ function installSessionStartHandler(pi, ctx) {
|
|
|
76498
77017
|
ctx.sessionGeneration++;
|
|
76499
77018
|
const ownerGeneration = ctx.sessionGeneration;
|
|
76500
77019
|
ctx.currentCtx = extensionCtx;
|
|
77020
|
+
noteSessionModel(extensionCtx.model, "session_start");
|
|
77021
|
+
noteSessionThinking(extensionCtx.thinkingLevel);
|
|
76501
77022
|
if (!ctx.crewAutocompleteRegistered) {
|
|
76502
77023
|
ctx.crewAutocompleteRegistered = true;
|
|
76503
77024
|
registerCrewAutocomplete(extensionCtx);
|