pi-crew 0.9.58 → 0.9.60
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +58 -0
- package/dist/index.mjs +1390 -796
- package/docs/commands-reference.md +5 -0
- package/docs/resource-formats.md +7 -1
- package/package.json +1 -1
- package/skills/distill-software/SKILL.md +7 -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/async-notifier.ts +10 -2
- package/src/extension/registration/context-builder.ts +5 -2
- package/src/extension/registration/crash-recovery-cache.ts +2 -2
- package/src/extension/registration/lazy-configurers.ts +1 -1
- package/src/extension/registration/lifecycle-handlers.ts +58 -10
- package/src/extension/registration/observability.ts +8 -4
- package/src/extension/registration/registration-types.ts +2 -2
- package/src/extension/registration/runtime-cleanup.ts +5 -2
- package/src/extension/registration/subagent-manager-setup.ts +9 -4
- package/src/extension/registration/subagent-tools.ts +15 -3
- package/src/extension/session-summary.ts +5 -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/delivery-coordinator.ts +24 -3
- 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/recovery/crash-recovery.ts +34 -4
- package/src/runtime/subagent-manager.ts +7 -1
- 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/src/ui/powerbar-publisher.ts +31 -6
- package/src/ui/run-dashboard.ts +7 -1
- package/src/ui/widget/index.ts +9 -1
- package/src/ui/widget/widget-model.ts +1 -1
- package/src/ui/widget/widget-types.ts +3 -0
- package/src/utils/session-utils.ts +42 -19
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
|
};
|
|
@@ -12871,14 +12891,23 @@ function toPiSessionId(runId) {
|
|
|
12871
12891
|
}
|
|
12872
12892
|
function extractSessionId(ctx) {
|
|
12873
12893
|
if (typeof ctx !== "object" || ctx === null) return void 0;
|
|
12874
|
-
let raw;
|
|
12875
12894
|
try {
|
|
12876
|
-
|
|
12895
|
+
const sm = ctx.sessionManager;
|
|
12896
|
+
if (sm && typeof sm === "object") {
|
|
12897
|
+
const cached2 = sessionIdCache.get(sm);
|
|
12898
|
+
if (cached2) return cached2;
|
|
12899
|
+
const id = sm.getSessionId?.();
|
|
12900
|
+
if (typeof id === "string" && id.length > 0) {
|
|
12901
|
+
sessionIdCache.set(sm, id);
|
|
12902
|
+
return id;
|
|
12903
|
+
}
|
|
12904
|
+
}
|
|
12905
|
+
const direct = Object.getOwnPropertyDescriptor(ctx, "sessionId")?.value;
|
|
12906
|
+
if (typeof direct === "string" && direct.length > 0) return direct;
|
|
12877
12907
|
} catch {
|
|
12878
12908
|
return void 0;
|
|
12879
12909
|
}
|
|
12880
|
-
|
|
12881
|
-
return raw;
|
|
12910
|
+
return void 0;
|
|
12882
12911
|
}
|
|
12883
12912
|
function extractBrokerSessionId(ctx) {
|
|
12884
12913
|
if (typeof ctx !== "object" || ctx === null) return void 0;
|
|
@@ -12893,9 +12922,11 @@ function extractBrokerSessionId(ctx) {
|
|
|
12893
12922
|
return void 0;
|
|
12894
12923
|
}
|
|
12895
12924
|
}
|
|
12925
|
+
var sessionIdCache;
|
|
12896
12926
|
var init_session_utils = __esm({
|
|
12897
12927
|
"src/utils/session-utils.ts"() {
|
|
12898
12928
|
"use strict";
|
|
12929
|
+
sessionIdCache = /* @__PURE__ */ new WeakMap();
|
|
12899
12930
|
}
|
|
12900
12931
|
});
|
|
12901
12932
|
|
|
@@ -13013,9 +13044,9 @@ var init_cancellation_token = __esm({
|
|
|
13013
13044
|
this.throwIfCancelled();
|
|
13014
13045
|
this.#lastHeartbeatAt = this.#now().toISOString();
|
|
13015
13046
|
this.#lastHeartbeatStage = stage;
|
|
13016
|
-
const
|
|
13017
|
-
this.#onHeartbeat?.(
|
|
13018
|
-
return
|
|
13047
|
+
const state2 = this.state();
|
|
13048
|
+
this.#onHeartbeat?.(state2);
|
|
13049
|
+
return state2;
|
|
13019
13050
|
}
|
|
13020
13051
|
throwIfCancelled() {
|
|
13021
13052
|
if (this.aborted) throw new CrewCancellationError(this.#reason ?? cancellationReasonFromUnknown(this.#controller.signal.reason));
|
|
@@ -13943,12 +13974,13 @@ function buildPiWorkerArgs(input) {
|
|
|
13943
13974
|
const args = ["--mode", "json", "-p"];
|
|
13944
13975
|
if (input.sessionEnabled === false) args.push("--no-session");
|
|
13945
13976
|
const resolvedModel = input.model ?? input.agent.model;
|
|
13977
|
+
const effectiveThinking = input.thinkingOverride ?? input.agent.thinking;
|
|
13946
13978
|
if (resolvedModel) {
|
|
13947
|
-
const modelWithThinking = applyThinkingSuffix(resolvedModel,
|
|
13979
|
+
const modelWithThinking = applyThinkingSuffix(resolvedModel, effectiveThinking);
|
|
13948
13980
|
if (modelWithThinking) args.push("--model", modelWithThinking);
|
|
13949
13981
|
}
|
|
13950
|
-
if (!resolvedModel &&
|
|
13951
|
-
args.push("--thinking",
|
|
13982
|
+
if (!resolvedModel && effectiveThinking && effectiveThinking !== "off" && isValidThinkingLevel(effectiveThinking)) {
|
|
13983
|
+
args.push("--thinking", effectiveThinking);
|
|
13952
13984
|
}
|
|
13953
13985
|
const policy = resolveToolPolicy(input.agent, input.role);
|
|
13954
13986
|
const explicitTools = policy.tools;
|
|
@@ -14914,7 +14946,8 @@ function prepareSpawnContext(input, effectiveTask) {
|
|
|
14914
14946
|
sessionEnabled: true,
|
|
14915
14947
|
maxDepth: input.maxDepth,
|
|
14916
14948
|
skillPaths: input.skillPaths,
|
|
14917
|
-
role: input.role
|
|
14949
|
+
role: input.role,
|
|
14950
|
+
thinkingOverride: input.thinkingOverride
|
|
14918
14951
|
});
|
|
14919
14952
|
if (input.steeringFile) built.env.PI_CREW_STEERING_FILE = input.steeringFile;
|
|
14920
14953
|
if (input.brokerSpawn?.socketPath && input.brokerSpawn.token) {
|
|
@@ -17832,45 +17865,45 @@ function readJsonlTail(filePath, tailBytes) {
|
|
|
17832
17865
|
}
|
|
17833
17866
|
}
|
|
17834
17867
|
}
|
|
17835
|
-
function readLinesSince(filePath,
|
|
17868
|
+
function readLinesSince(filePath, state2) {
|
|
17836
17869
|
let fd;
|
|
17837
17870
|
try {
|
|
17838
17871
|
fd = fs24.openSync(filePath, "r");
|
|
17839
17872
|
} catch {
|
|
17840
17873
|
return {
|
|
17841
17874
|
lines: [],
|
|
17842
|
-
state: { byteOffset:
|
|
17875
|
+
state: { byteOffset: state2.byteOffset, lineCount: state2.lineCount },
|
|
17843
17876
|
eof: true
|
|
17844
17877
|
};
|
|
17845
17878
|
}
|
|
17846
17879
|
try {
|
|
17847
17880
|
const stat2 = fs24.fstatSync(fd);
|
|
17848
17881
|
const fileSize = stat2.size;
|
|
17849
|
-
if (fileSize <=
|
|
17882
|
+
if (fileSize <= state2.byteOffset) {
|
|
17850
17883
|
return {
|
|
17851
17884
|
lines: [],
|
|
17852
|
-
state: { byteOffset: fileSize, lineCount:
|
|
17885
|
+
state: { byteOffset: fileSize, lineCount: state2.lineCount },
|
|
17853
17886
|
eof: true
|
|
17854
17887
|
};
|
|
17855
17888
|
}
|
|
17856
|
-
const bytesToRead = fileSize -
|
|
17889
|
+
const bytesToRead = fileSize - state2.byteOffset;
|
|
17857
17890
|
const buf = Buffer.alloc(bytesToRead);
|
|
17858
17891
|
let totalRead = 0;
|
|
17859
17892
|
while (totalRead < bytesToRead) {
|
|
17860
17893
|
const chunkSize = Math.min(CHUNK_SIZE, bytesToRead - totalRead);
|
|
17861
|
-
const bytesRead = fs24.readSync(fd, buf, totalRead, chunkSize,
|
|
17894
|
+
const bytesRead = fs24.readSync(fd, buf, totalRead, chunkSize, state2.byteOffset + totalRead);
|
|
17862
17895
|
if (bytesRead === 0) break;
|
|
17863
17896
|
totalRead += bytesRead;
|
|
17864
17897
|
}
|
|
17865
17898
|
const content = buf.toString("utf-8", 0, totalRead);
|
|
17866
17899
|
const lines = [];
|
|
17867
|
-
let lineCount =
|
|
17868
|
-
let committedOffset =
|
|
17900
|
+
let lineCount = state2.lineCount;
|
|
17901
|
+
let committedOffset = state2.byteOffset;
|
|
17869
17902
|
let searchFrom = 0;
|
|
17870
17903
|
let newlineIdx;
|
|
17871
17904
|
while ((newlineIdx = content.indexOf("\n", searchFrom)) !== -1) {
|
|
17872
17905
|
const lineText = content.slice(searchFrom, newlineIdx);
|
|
17873
|
-
committedOffset =
|
|
17906
|
+
committedOffset = state2.byteOffset + newlineIdx + 1;
|
|
17874
17907
|
searchFrom = newlineIdx + 1;
|
|
17875
17908
|
if (lineText.length > 0) {
|
|
17876
17909
|
lines.push(lineText);
|
|
@@ -17892,8 +17925,8 @@ function readLinesSince(filePath, state) {
|
|
|
17892
17925
|
}
|
|
17893
17926
|
}
|
|
17894
17927
|
}
|
|
17895
|
-
function readJsonlSince(filePath,
|
|
17896
|
-
const result4 = readLinesSince(filePath,
|
|
17928
|
+
function readJsonlSince(filePath, state2) {
|
|
17929
|
+
const result4 = readLinesSince(filePath, state2);
|
|
17897
17930
|
const items = [];
|
|
17898
17931
|
for (const line4 of result4.lines) {
|
|
17899
17932
|
try {
|
|
@@ -20958,12 +20991,12 @@ function readDeliveryState(manifest) {
|
|
|
20958
20991
|
if (obj.messages && typeof obj.messages === "object" && !Array.isArray(obj.messages)) {
|
|
20959
20992
|
for (const [id, status] of Object.entries(obj.messages)) if (isStatus(status)) messages[id] = status;
|
|
20960
20993
|
}
|
|
20961
|
-
const
|
|
20994
|
+
const state2 = {
|
|
20962
20995
|
messages,
|
|
20963
20996
|
updatedAt: typeof obj.updatedAt === "string" ? obj.updatedAt : (/* @__PURE__ */ new Date()).toISOString()
|
|
20964
20997
|
};
|
|
20965
|
-
setDeliveryCacheEntry(filePath, { mtimeMs: stat2.mtimeMs, state });
|
|
20966
|
-
return
|
|
20998
|
+
setDeliveryCacheEntry(filePath, { mtimeMs: stat2.mtimeMs, state: state2 });
|
|
20999
|
+
return state2;
|
|
20967
21000
|
} catch (error) {
|
|
20968
21001
|
const quarantinePath = `${filePath}.corrupt-${Date.now()}`;
|
|
20969
21002
|
try {
|
|
@@ -20978,25 +21011,25 @@ function readDeliveryState(manifest) {
|
|
|
20978
21011
|
return { messages: {}, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
20979
21012
|
}
|
|
20980
21013
|
}
|
|
20981
|
-
function writeDeliveryState(manifest,
|
|
21014
|
+
function writeDeliveryState(manifest, state2, options) {
|
|
20982
21015
|
ensureRunMailbox(manifest);
|
|
20983
21016
|
const MAX_DELIVERY_MESSAGES = 1e4;
|
|
20984
|
-
if (Object.keys(
|
|
20985
|
-
const sorted = Object.entries(
|
|
21017
|
+
if (Object.keys(state2.messages).length > MAX_DELIVERY_MESSAGES) {
|
|
21018
|
+
const sorted = Object.entries(state2.messages).sort(([, a], [, b]) => {
|
|
20986
21019
|
const order = { queued: 0, delivered: 1, acknowledged: 2 };
|
|
20987
21020
|
return (order[a] ?? 3) - (order[b] ?? 3);
|
|
20988
21021
|
});
|
|
20989
21022
|
const trimmed = sorted.slice(0, MAX_DELIVERY_MESSAGES);
|
|
20990
|
-
|
|
21023
|
+
state2.messages = Object.fromEntries(trimmed);
|
|
20991
21024
|
}
|
|
20992
21025
|
const filePath = deliveryFile(manifest, true);
|
|
20993
|
-
atomicWriteFile(filePath, `${JSON.stringify(redactSecrets(
|
|
21026
|
+
atomicWriteFile(filePath, `${JSON.stringify(redactSecrets(state2), null, 2)}
|
|
20994
21027
|
`, {
|
|
20995
21028
|
durability: options?.durability ?? "best-effort"
|
|
20996
21029
|
});
|
|
20997
21030
|
try {
|
|
20998
21031
|
const postStat = fs29.statSync(filePath);
|
|
20999
|
-
setDeliveryCacheEntry(filePath, { mtimeMs: postStat.mtimeMs, state });
|
|
21032
|
+
setDeliveryCacheEntry(filePath, { mtimeMs: postStat.mtimeMs, state: state2 });
|
|
21000
21033
|
} catch {
|
|
21001
21034
|
deliveryCache.delete(filePath);
|
|
21002
21035
|
}
|
|
@@ -21611,6 +21644,713 @@ var init_runtime_resolver = __esm({
|
|
|
21611
21644
|
}
|
|
21612
21645
|
});
|
|
21613
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
|
+
|
|
21614
22354
|
// src/extension/team-tool/config-patch.ts
|
|
21615
22355
|
function sanitizeObject2(obj) {
|
|
21616
22356
|
if (obj === null || obj === void 0 || typeof obj !== "object") return obj;
|
|
@@ -21914,14 +22654,14 @@ var init_i18n = __esm({
|
|
|
21914
22654
|
});
|
|
21915
22655
|
|
|
21916
22656
|
// src/runtime/async-marker.ts
|
|
21917
|
-
import * as
|
|
21918
|
-
import * as
|
|
22657
|
+
import * as fs32 from "node:fs";
|
|
22658
|
+
import * as path28 from "node:path";
|
|
21919
22659
|
function asyncStartMarkerPath(manifest) {
|
|
21920
|
-
return
|
|
22660
|
+
return path28.join(manifest.stateRoot, "async.pid");
|
|
21921
22661
|
}
|
|
21922
22662
|
function hasAsyncStartMarker(manifest) {
|
|
21923
22663
|
try {
|
|
21924
|
-
const raw = JSON.parse(
|
|
22664
|
+
const raw = JSON.parse(fs32.readFileSync(asyncStartMarkerPath(manifest), "utf-8"));
|
|
21925
22665
|
return typeof raw.pid === "number" && Number.isInteger(raw.pid) && raw.pid > 0 && typeof raw.startedAt === "string" && raw.startedAt.length > 0;
|
|
21926
22666
|
} catch {
|
|
21927
22667
|
return false;
|
|
@@ -22034,8 +22774,8 @@ var init_process_status = __esm({
|
|
|
22034
22774
|
});
|
|
22035
22775
|
|
|
22036
22776
|
// src/runtime/run-tracker.ts
|
|
22037
|
-
import * as
|
|
22038
|
-
import * as
|
|
22777
|
+
import * as fs33 from "node:fs";
|
|
22778
|
+
import * as path29 from "node:path";
|
|
22039
22779
|
function registerRunPromise(runId) {
|
|
22040
22780
|
let resolve25;
|
|
22041
22781
|
let reject;
|
|
@@ -22083,8 +22823,8 @@ async function waitForRun(runId, cwd, options = {}) {
|
|
|
22083
22823
|
let attempt = 0;
|
|
22084
22824
|
while (Date.now() < deadline) {
|
|
22085
22825
|
if (attempt === 0) {
|
|
22086
|
-
const runDir =
|
|
22087
|
-
if (!
|
|
22826
|
+
const runDir = path29.join(projectCrewRoot(cwd), "state", "runs", runId);
|
|
22827
|
+
if (!fs33.existsSync(runDir)) {
|
|
22088
22828
|
throw new Error(`Run ${runId} not found. No run directory at ${runDir}`);
|
|
22089
22829
|
}
|
|
22090
22830
|
}
|
|
@@ -22234,13 +22974,13 @@ var init_crew_hooks = __esm({
|
|
|
22234
22974
|
});
|
|
22235
22975
|
|
|
22236
22976
|
// src/runtime/skill-effectiveness.ts
|
|
22237
|
-
import { existsSync as existsSync23, mkdirSync as mkdirSync14, readFileSync as
|
|
22238
|
-
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";
|
|
22239
22979
|
function getSkillMetricsPath(cwd, runId) {
|
|
22240
|
-
return
|
|
22980
|
+
return join29(projectCrewRoot(cwd), `state/runs/${runId}/skill-metrics.jsonl`);
|
|
22241
22981
|
}
|
|
22242
22982
|
function getSkillActivationsPath(cwd, runId) {
|
|
22243
|
-
return
|
|
22983
|
+
return join29(projectCrewRoot(cwd), `state/runs/${runId}/skill-activations.jsonl`);
|
|
22244
22984
|
}
|
|
22245
22985
|
function ensureSkillMetricsDir(cwd, runId) {
|
|
22246
22986
|
const dir = dirname16(getSkillMetricsPath(cwd, runId));
|
|
@@ -22297,7 +23037,7 @@ function getSkillActivations(cwd, runId) {
|
|
|
22297
23037
|
if (!existsSync23(path89)) {
|
|
22298
23038
|
return [];
|
|
22299
23039
|
}
|
|
22300
|
-
const content =
|
|
23040
|
+
const content = readFileSync23(path89, "utf-8");
|
|
22301
23041
|
if (!content.trim()) {
|
|
22302
23042
|
return [];
|
|
22303
23043
|
}
|
|
@@ -22475,10 +23215,10 @@ __export(skill_instructions_exports, {
|
|
|
22475
23215
|
resetSkillCacheStats: () => resetSkillCacheStats,
|
|
22476
23216
|
resolveTaskSkillNames: () => resolveTaskSkillNames
|
|
22477
23217
|
});
|
|
22478
|
-
import * as
|
|
22479
|
-
import * as
|
|
23218
|
+
import * as fs34 from "node:fs";
|
|
23219
|
+
import * as path30 from "node:path";
|
|
22480
23220
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
22481
|
-
import * as
|
|
23221
|
+
import * as os10 from "node:os";
|
|
22482
23222
|
function isValidSkillName(name) {
|
|
22483
23223
|
return name.length > 0 && name.length <= MAX_SKILL_NAME_CHARS && isSafePathId(name);
|
|
22484
23224
|
}
|
|
@@ -22528,18 +23268,18 @@ function candidateSkillDirs(cwd) {
|
|
|
22528
23268
|
// F6 (v0.7.9): same five roots as discover-skills, in the same precedence
|
|
22529
23269
|
// order. The first hit wins, so a project `.pi/skills/foo/SKILL.md`
|
|
22530
23270
|
// overrides both the bundled `foo` and any legacy `<cwd>/skills/foo`.
|
|
22531
|
-
{ root:
|
|
23271
|
+
{ root: path30.resolve(cwd, ".pi", "skills"), source: "project-pi" },
|
|
22532
23272
|
{
|
|
22533
|
-
root:
|
|
23273
|
+
root: path30.resolve(cwd, ".agents", "skills"),
|
|
22534
23274
|
source: "project-agents"
|
|
22535
23275
|
},
|
|
22536
|
-
{ root:
|
|
22537
|
-
{ root:
|
|
23276
|
+
{ root: path30.resolve(cwd, "skills"), source: "project" },
|
|
23277
|
+
{ root: path30.join(getAgentDir(), "skills"), source: "user-pi" },
|
|
22538
23278
|
{
|
|
22539
|
-
root:
|
|
23279
|
+
root: path30.join(os10.homedir(), ".agents", "skills"),
|
|
22540
23280
|
source: "user-agents"
|
|
22541
23281
|
},
|
|
22542
|
-
{ root:
|
|
23282
|
+
{ root: path30.join(os10.homedir(), ".pi", "skills"), source: "user-pi" }
|
|
22543
23283
|
];
|
|
22544
23284
|
}
|
|
22545
23285
|
function rememberSkill(key, value) {
|
|
@@ -22577,7 +23317,7 @@ function _setSkillCacheMaxEntriesForTesting(max) {
|
|
|
22577
23317
|
}
|
|
22578
23318
|
function cachedSkillFresh(value) {
|
|
22579
23319
|
try {
|
|
22580
|
-
const stat2 =
|
|
23320
|
+
const stat2 = fs34.statSync(value.path);
|
|
22581
23321
|
return stat2.mtimeMs === value.mtimeMs && stat2.size === value.size;
|
|
22582
23322
|
} catch {
|
|
22583
23323
|
return false;
|
|
@@ -22585,7 +23325,7 @@ function cachedSkillFresh(value) {
|
|
|
22585
23325
|
}
|
|
22586
23326
|
function readSkillMarkdown(cwd, name) {
|
|
22587
23327
|
if (!isValidSkillName(name)) return void 0;
|
|
22588
|
-
const cacheKey2 = `${
|
|
23328
|
+
const cacheKey2 = `${path30.resolve(cwd)}:${name}`;
|
|
22589
23329
|
const cached2 = skillReadCache.get(cacheKey2);
|
|
22590
23330
|
if (cached2 && cachedSkillFresh(cached2)) {
|
|
22591
23331
|
skillCacheStats.hits++;
|
|
@@ -22596,13 +23336,13 @@ function readSkillMarkdown(cwd, name) {
|
|
|
22596
23336
|
skillCacheStats.currentSize = skillReadCache.size;
|
|
22597
23337
|
for (const entry of candidateSkillDirs(cwd)) {
|
|
22598
23338
|
try {
|
|
22599
|
-
const relative9 =
|
|
23339
|
+
const relative9 = path30.join(name, "SKILL.md");
|
|
22600
23340
|
const contained = resolveRealContainedPath(entry.root, relative9);
|
|
22601
|
-
if (!
|
|
22602
|
-
if (
|
|
23341
|
+
if (!fs34.existsSync(contained)) continue;
|
|
23342
|
+
if (fs34.lstatSync(contained).isSymbolicLink()) continue;
|
|
22603
23343
|
const filePath = resolveRealContainedPath(entry.root, relative9);
|
|
22604
|
-
const stat2 =
|
|
22605
|
-
const rawContent =
|
|
23344
|
+
const stat2 = fs34.statSync(filePath);
|
|
23345
|
+
const rawContent = fs34.readFileSync(filePath, "utf-8");
|
|
22606
23346
|
return rememberSkill(cacheKey2, {
|
|
22607
23347
|
path: filePath,
|
|
22608
23348
|
source: entry.source,
|
|
@@ -22671,7 +23411,7 @@ Skill '${safeName}' was selected but no SKILL.md file was found. Continue with t
|
|
|
22671
23411
|
if (!pushSection(missing)) omittedCount += 1;
|
|
22672
23412
|
continue;
|
|
22673
23413
|
}
|
|
22674
|
-
skillPaths.push(
|
|
23414
|
+
skillPaths.push(path30.dirname(loaded.path));
|
|
22675
23415
|
const description = frontmatterDescription(loaded.content);
|
|
22676
23416
|
const source = loaded.source === "project" ? `project:skills/${safeName}` : `package:skills/${safeName}`;
|
|
22677
23417
|
const weighted = weightedSkills?.find((w) => w.skillId === name);
|
|
@@ -22686,7 +23426,7 @@ Skill '${safeName}' was selected but no SKILL.md file was found. Continue with t
|
|
|
22686
23426
|
// spec "small instruction + large local reference" pattern, e.g.
|
|
22687
23427
|
// effective-html's `references/html-effectiveness/`) leave the agent
|
|
22688
23428
|
// guessing the skill dir. No behavior change for corpus-less skills.
|
|
22689
|
-
`Path: ${
|
|
23429
|
+
`Path: ${path30.dirname(loaded.path)}`
|
|
22690
23430
|
].filter(Boolean).join("\n");
|
|
22691
23431
|
const rawContent = loaded.compacted;
|
|
22692
23432
|
const wrappedContent = `<!-- skill: ${safeName} -->
|
|
@@ -22730,7 +23470,7 @@ var init_skill_instructions = __esm({
|
|
|
22730
23470
|
init_safe_paths();
|
|
22731
23471
|
init_skill_effectiveness();
|
|
22732
23472
|
init_peer_dep();
|
|
22733
|
-
PACKAGE_SKILLS_DIR =
|
|
23473
|
+
PACKAGE_SKILLS_DIR = path30.resolve(path30.dirname(fileURLToPath4(import.meta.url)), "..", "..", "skills");
|
|
22734
23474
|
MAX_SKILL_CHARS = 1500;
|
|
22735
23475
|
MAX_TOTAL_CHARS = 6e3;
|
|
22736
23476
|
MAX_SKILL_NAME_CHARS = 80;
|
|
@@ -23630,15 +24370,15 @@ var init_anchor = __esm({
|
|
|
23630
24370
|
});
|
|
23631
24371
|
|
|
23632
24372
|
// src/runtime/agent-observability.ts
|
|
23633
|
-
import * as
|
|
24373
|
+
import * as fs35 from "node:fs";
|
|
23634
24374
|
function readTextTail(filePath, maxBytes = 64e3) {
|
|
23635
|
-
if (!
|
|
23636
|
-
const stat2 =
|
|
24375
|
+
if (!fs35.existsSync(filePath)) return { path: filePath, text: "", bytes: 0, truncated: false };
|
|
24376
|
+
const stat2 = fs35.statSync(filePath);
|
|
23637
24377
|
const bytesToRead = Math.min(stat2.size, Math.max(0, maxBytes));
|
|
23638
|
-
const fd =
|
|
24378
|
+
const fd = fs35.openSync(filePath, "r");
|
|
23639
24379
|
try {
|
|
23640
24380
|
const buffer = Buffer.alloc(bytesToRead);
|
|
23641
|
-
|
|
24381
|
+
fs35.readSync(fd, buffer, 0, bytesToRead, stat2.size - bytesToRead);
|
|
23642
24382
|
return {
|
|
23643
24383
|
path: filePath,
|
|
23644
24384
|
text: buffer.toString("utf-8"),
|
|
@@ -23646,7 +24386,7 @@ function readTextTail(filePath, maxBytes = 64e3) {
|
|
|
23646
24386
|
truncated: stat2.size > bytesToRead
|
|
23647
24387
|
};
|
|
23648
24388
|
} finally {
|
|
23649
|
-
|
|
24389
|
+
fs35.closeSync(fd);
|
|
23650
24390
|
}
|
|
23651
24391
|
}
|
|
23652
24392
|
function compactDuration(ms) {
|
|
@@ -23685,8 +24425,8 @@ function outputWarning(manifest, agent) {
|
|
|
23685
24425
|
if (agent.status !== "completed") return "";
|
|
23686
24426
|
try {
|
|
23687
24427
|
const outputPath = agentOutputPath(manifest, agent.taskId);
|
|
23688
|
-
if (!
|
|
23689
|
-
return
|
|
24428
|
+
if (!fs35.existsSync(outputPath)) return " no-output";
|
|
24429
|
+
return fs35.statSync(outputPath).size === 0 ? " no-output" : "";
|
|
23690
24430
|
} catch {
|
|
23691
24431
|
return " no-output";
|
|
23692
24432
|
}
|
|
@@ -23738,9 +24478,9 @@ var init_agent_observability = __esm({
|
|
|
23738
24478
|
});
|
|
23739
24479
|
|
|
23740
24480
|
// src/skills/validate.ts
|
|
23741
|
-
import * as
|
|
24481
|
+
import * as fs36 from "node:fs";
|
|
23742
24482
|
import { createRequire as createRequire3 } from "node:module";
|
|
23743
|
-
import * as
|
|
24483
|
+
import * as path31 from "node:path";
|
|
23744
24484
|
function getYaml() {
|
|
23745
24485
|
if (!yamlModule) {
|
|
23746
24486
|
const require4 = createRequire3(import.meta.url);
|
|
@@ -23776,11 +24516,11 @@ function warn(path89, field, reason) {
|
|
|
23776
24516
|
}
|
|
23777
24517
|
function validateSkillFrontmatter(skillDir) {
|
|
23778
24518
|
const errors2 = [];
|
|
23779
|
-
const skillMdPath =
|
|
23780
|
-
const derivedName =
|
|
24519
|
+
const skillMdPath = path31.join(skillDir, "SKILL.md");
|
|
24520
|
+
const derivedName = path31.basename(skillDir);
|
|
23781
24521
|
let content;
|
|
23782
24522
|
try {
|
|
23783
|
-
content =
|
|
24523
|
+
content = fs36.readFileSync(skillMdPath, "utf-8");
|
|
23784
24524
|
} catch (e) {
|
|
23785
24525
|
errors2.push(hard(skillDir, "SKILL.md", `Cannot read SKILL.md: ${e.message}`));
|
|
23786
24526
|
return { ok: false, errors: errors2 };
|
|
@@ -23911,25 +24651,25 @@ var init_validate = __esm({
|
|
|
23911
24651
|
});
|
|
23912
24652
|
|
|
23913
24653
|
// src/skills/discover-skills.ts
|
|
23914
|
-
import * as
|
|
23915
|
-
import * as
|
|
23916
|
-
import * as
|
|
24654
|
+
import * as fs37 from "node:fs";
|
|
24655
|
+
import * as os11 from "node:os";
|
|
24656
|
+
import * as path32 from "node:path";
|
|
23917
24657
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
23918
24658
|
function listSkillDirs(cwd) {
|
|
23919
24659
|
return [
|
|
23920
24660
|
{ root: PACKAGE_SKILLS_DIR2, source: "package" },
|
|
23921
|
-
{ root:
|
|
24661
|
+
{ root: path32.resolve(cwd, ".pi", "skills"), source: "project-pi" },
|
|
23922
24662
|
{
|
|
23923
|
-
root:
|
|
24663
|
+
root: path32.resolve(cwd, ".agents", "skills"),
|
|
23924
24664
|
source: "project-agents"
|
|
23925
24665
|
},
|
|
23926
|
-
{ root:
|
|
23927
|
-
{ root:
|
|
24666
|
+
{ root: path32.resolve(cwd, "skills"), source: "project" },
|
|
24667
|
+
{ root: path32.join(getAgentDir(), "skills"), source: "user-pi" },
|
|
23928
24668
|
{
|
|
23929
|
-
root:
|
|
24669
|
+
root: path32.join(os11.homedir(), ".agents", "skills"),
|
|
23930
24670
|
source: "user-agents"
|
|
23931
24671
|
},
|
|
23932
|
-
{ root:
|
|
24672
|
+
{ root: path32.join(os11.homedir(), ".pi", "skills"), source: "user-pi" }
|
|
23933
24673
|
];
|
|
23934
24674
|
}
|
|
23935
24675
|
function readDescription(content) {
|
|
@@ -23954,29 +24694,29 @@ function discoverSkills(cwd) {
|
|
|
23954
24694
|
const results = [];
|
|
23955
24695
|
const diagnostics = [];
|
|
23956
24696
|
for (const dir of listSkillDirs(cwd)) {
|
|
23957
|
-
if (!
|
|
24697
|
+
if (!fs37.existsSync(dir.root)) continue;
|
|
23958
24698
|
try {
|
|
23959
|
-
for (const entry of
|
|
24699
|
+
for (const entry of fs37.readdirSync(dir.root, {
|
|
23960
24700
|
withFileTypes: true
|
|
23961
24701
|
})) {
|
|
23962
24702
|
if (!entry.isDirectory()) continue;
|
|
23963
24703
|
if (!isSafePathId(entry.name)) continue;
|
|
23964
|
-
const skillDirPath =
|
|
24704
|
+
const skillDirPath = path32.join(dir.root, entry.name);
|
|
23965
24705
|
try {
|
|
23966
|
-
if (
|
|
24706
|
+
if (fs37.lstatSync(skillDirPath).isSymbolicLink()) continue;
|
|
23967
24707
|
} catch {
|
|
23968
24708
|
continue;
|
|
23969
24709
|
}
|
|
23970
|
-
const skillMdRelative =
|
|
24710
|
+
const skillMdRelative = path32.join(entry.name, "SKILL.md");
|
|
23971
24711
|
let skillMdPath;
|
|
23972
24712
|
try {
|
|
23973
24713
|
skillMdPath = resolveContainedPath(dir.root, skillMdRelative);
|
|
23974
24714
|
} catch {
|
|
23975
24715
|
continue;
|
|
23976
24716
|
}
|
|
23977
|
-
if (!
|
|
24717
|
+
if (!fs37.existsSync(skillMdPath)) continue;
|
|
23978
24718
|
try {
|
|
23979
|
-
if (
|
|
24719
|
+
if (fs37.lstatSync(skillMdPath).isSymbolicLink()) continue;
|
|
23980
24720
|
} catch {
|
|
23981
24721
|
continue;
|
|
23982
24722
|
}
|
|
@@ -23988,12 +24728,12 @@ function discoverSkills(cwd) {
|
|
|
23988
24728
|
skillMdPath = readPath;
|
|
23989
24729
|
} catch {
|
|
23990
24730
|
}
|
|
23991
|
-
const content =
|
|
24731
|
+
const content = fs37.readFileSync(readPath, "utf-8");
|
|
23992
24732
|
const { description: desc, parseError } = readDescription(content);
|
|
23993
24733
|
description = desc;
|
|
23994
24734
|
if (parseError) {
|
|
23995
24735
|
diagnostics.push({
|
|
23996
|
-
path:
|
|
24736
|
+
path: path32.dirname(skillMdPath),
|
|
23997
24737
|
field: "frontmatter",
|
|
23998
24738
|
reason: parseError,
|
|
23999
24739
|
severity: "error"
|
|
@@ -24015,7 +24755,7 @@ function discoverSkills(cwd) {
|
|
|
24015
24755
|
}
|
|
24016
24756
|
const filtered = [];
|
|
24017
24757
|
for (const skill of results) {
|
|
24018
|
-
const validation = validateSkillFrontmatter(
|
|
24758
|
+
const validation = validateSkillFrontmatter(path32.dirname(skill.path));
|
|
24019
24759
|
if (validation.ok) {
|
|
24020
24760
|
filtered.push(skill);
|
|
24021
24761
|
} else {
|
|
@@ -24034,7 +24774,7 @@ var init_discover_skills = __esm({
|
|
|
24034
24774
|
init_internal_error();
|
|
24035
24775
|
init_safe_paths();
|
|
24036
24776
|
init_validate();
|
|
24037
|
-
PACKAGE_SKILLS_DIR2 =
|
|
24777
|
+
PACKAGE_SKILLS_DIR2 = path32.resolve(path32.dirname(fileURLToPath5(import.meta.url)), "..", "..", "skills");
|
|
24038
24778
|
CACHE_TTL_MS = 3e4;
|
|
24039
24779
|
cache2 = null;
|
|
24040
24780
|
lastDiagnostics = [];
|
|
@@ -24141,15 +24881,15 @@ var init_capability_inventory = __esm({
|
|
|
24141
24881
|
});
|
|
24142
24882
|
|
|
24143
24883
|
// src/runtime/foreground-control.ts
|
|
24144
|
-
import * as
|
|
24145
|
-
import * as
|
|
24884
|
+
import * as fs38 from "node:fs";
|
|
24885
|
+
import * as path33 from "node:path";
|
|
24146
24886
|
function foregroundControlPath(manifest) {
|
|
24147
|
-
return
|
|
24887
|
+
return path33.join(manifest.stateRoot, "foreground-control.json");
|
|
24148
24888
|
}
|
|
24149
24889
|
function readLastRequest(controlPath) {
|
|
24150
|
-
if (!
|
|
24890
|
+
if (!fs38.existsSync(controlPath)) return void 0;
|
|
24151
24891
|
try {
|
|
24152
|
-
const parsed = JSON.parse(
|
|
24892
|
+
const parsed = JSON.parse(fs38.readFileSync(controlPath, "utf-8"));
|
|
24153
24893
|
return parsed.requests?.at(-1);
|
|
24154
24894
|
} catch {
|
|
24155
24895
|
return void 0;
|
|
@@ -24173,7 +24913,7 @@ function readForegroundControlStatus(manifest, tasks) {
|
|
|
24173
24913
|
function writeForegroundInterruptRequest(manifest, reason = "User requested foreground interrupt.") {
|
|
24174
24914
|
const controlPath = foregroundControlPath(manifest);
|
|
24175
24915
|
const lockDir = `${controlPath}.lock`;
|
|
24176
|
-
const pidFile =
|
|
24916
|
+
const pidFile = path33.join(lockDir, "pid");
|
|
24177
24917
|
let requests = [];
|
|
24178
24918
|
const acquireLock = () => {
|
|
24179
24919
|
const timeout = 5e3;
|
|
@@ -24181,7 +24921,7 @@ function writeForegroundInterruptRequest(manifest, reason = "User requested fore
|
|
|
24181
24921
|
const start = Date.now();
|
|
24182
24922
|
while (true) {
|
|
24183
24923
|
try {
|
|
24184
|
-
|
|
24924
|
+
fs38.mkdirSync(lockDir, { recursive: true });
|
|
24185
24925
|
try {
|
|
24186
24926
|
atomicWriteFile(pidFile, String(process.pid));
|
|
24187
24927
|
} catch {
|
|
@@ -24190,7 +24930,7 @@ function writeForegroundInterruptRequest(manifest, reason = "User requested fore
|
|
|
24190
24930
|
} catch {
|
|
24191
24931
|
if (Date.now() - start > timeout) {
|
|
24192
24932
|
try {
|
|
24193
|
-
const raw =
|
|
24933
|
+
const raw = fs38.readFileSync(pidFile, "utf-8").trim();
|
|
24194
24934
|
const ownerPid = Number.parseInt(raw, 10);
|
|
24195
24935
|
if (!Number.isNaN(ownerPid) && ownerPid !== process.pid) {
|
|
24196
24936
|
let alive = false;
|
|
@@ -24200,7 +24940,7 @@ function writeForegroundInterruptRequest(manifest, reason = "User requested fore
|
|
|
24200
24940
|
} catch {
|
|
24201
24941
|
}
|
|
24202
24942
|
if (!alive) {
|
|
24203
|
-
|
|
24943
|
+
fs38.rmSync(lockDir, {
|
|
24204
24944
|
recursive: true,
|
|
24205
24945
|
force: true
|
|
24206
24946
|
});
|
|
@@ -24217,7 +24957,7 @@ function writeForegroundInterruptRequest(manifest, reason = "User requested fore
|
|
|
24217
24957
|
throw err2;
|
|
24218
24958
|
}
|
|
24219
24959
|
try {
|
|
24220
|
-
const raw =
|
|
24960
|
+
const raw = fs38.readFileSync(pidFile, "utf-8").trim();
|
|
24221
24961
|
const ownerPid = Number.parseInt(raw, 10);
|
|
24222
24962
|
if (!Number.isNaN(ownerPid) && ownerPid !== process.pid) {
|
|
24223
24963
|
let alive = false;
|
|
@@ -24227,9 +24967,9 @@ function writeForegroundInterruptRequest(manifest, reason = "User requested fore
|
|
|
24227
24967
|
} catch {
|
|
24228
24968
|
}
|
|
24229
24969
|
if (!alive) {
|
|
24230
|
-
const stat2 =
|
|
24970
|
+
const stat2 = fs38.statSync(lockDir);
|
|
24231
24971
|
if (Date.now() - stat2.mtimeMs > staleMs) {
|
|
24232
|
-
|
|
24972
|
+
fs38.rmSync(lockDir, {
|
|
24233
24973
|
recursive: true,
|
|
24234
24974
|
force: true
|
|
24235
24975
|
});
|
|
@@ -24245,15 +24985,15 @@ function writeForegroundInterruptRequest(manifest, reason = "User requested fore
|
|
|
24245
24985
|
};
|
|
24246
24986
|
const releaseLock2 = () => {
|
|
24247
24987
|
try {
|
|
24248
|
-
|
|
24988
|
+
fs38.rmSync(lockDir, { recursive: true, force: true });
|
|
24249
24989
|
} catch {
|
|
24250
24990
|
}
|
|
24251
24991
|
};
|
|
24252
24992
|
acquireLock();
|
|
24253
24993
|
try {
|
|
24254
|
-
if (
|
|
24994
|
+
if (fs38.existsSync(controlPath)) {
|
|
24255
24995
|
try {
|
|
24256
|
-
const parsed = JSON.parse(
|
|
24996
|
+
const parsed = JSON.parse(fs38.readFileSync(controlPath, "utf-8"));
|
|
24257
24997
|
requests = Array.isArray(parsed.requests) ? parsed.requests : [];
|
|
24258
24998
|
} catch {
|
|
24259
24999
|
requests = [];
|
|
@@ -24266,7 +25006,7 @@ function writeForegroundInterruptRequest(manifest, reason = "User requested fore
|
|
|
24266
25006
|
reason,
|
|
24267
25007
|
acknowledged: false
|
|
24268
25008
|
};
|
|
24269
|
-
|
|
25009
|
+
fs38.mkdirSync(path33.dirname(controlPath), { recursive: true });
|
|
24270
25010
|
atomicWriteFile(controlPath, `${JSON.stringify({ requests: [...requests, request] }, null, 2)}
|
|
24271
25011
|
`);
|
|
24272
25012
|
appendEvent(manifest.eventsPath, {
|
|
@@ -24309,7 +25049,7 @@ var init_worker_heartbeat = __esm({
|
|
|
24309
25049
|
});
|
|
24310
25050
|
|
|
24311
25051
|
// src/runtime/live-session/live-agent-control.ts
|
|
24312
|
-
import * as
|
|
25052
|
+
import * as fs39 from "node:fs";
|
|
24313
25053
|
function liveAgentControlFile(manifest, taskId) {
|
|
24314
25054
|
return agentStateFile(manifest, taskId, "live-control.jsonl");
|
|
24315
25055
|
}
|
|
@@ -24327,7 +25067,7 @@ function appendLiveAgentControlRequest(manifest, input) {
|
|
|
24327
25067
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
24328
25068
|
};
|
|
24329
25069
|
const filePath = liveAgentControlFile(manifest, input.taskId);
|
|
24330
|
-
|
|
25070
|
+
fs39.appendFileSync(filePath, `${JSON.stringify(request)}
|
|
24331
25071
|
`, "utf-8");
|
|
24332
25072
|
return request;
|
|
24333
25073
|
}
|
|
@@ -24338,8 +25078,8 @@ function readLiveAgentControlRequests(manifest, taskId, cursor = { offset: 0 })
|
|
|
24338
25078
|
} catch {
|
|
24339
25079
|
return { requests: [], cursor };
|
|
24340
25080
|
}
|
|
24341
|
-
if (!
|
|
24342
|
-
const text =
|
|
25081
|
+
if (!fs39.existsSync(filePath)) return { requests: [], cursor };
|
|
25082
|
+
const text = fs39.readFileSync(filePath, "utf-8");
|
|
24343
25083
|
const lines = text.split(/\r?\n/).filter(Boolean);
|
|
24344
25084
|
const requests = lines.slice(cursor.offset).flatMap((line4) => {
|
|
24345
25085
|
try {
|
|
@@ -25134,379 +25874,6 @@ var init_mcp_proxy = __esm({
|
|
|
25134
25874
|
}
|
|
25135
25875
|
});
|
|
25136
25876
|
|
|
25137
|
-
// src/runtime/model/model-resolver.ts
|
|
25138
|
-
function fuzzyResolveModelId(input, models) {
|
|
25139
|
-
const query = input.toLowerCase();
|
|
25140
|
-
let bestMatch;
|
|
25141
|
-
let bestScore = 0;
|
|
25142
|
-
for (const m of models) {
|
|
25143
|
-
const id = m.id.toLowerCase();
|
|
25144
|
-
const name = (m.name ?? "").toLowerCase();
|
|
25145
|
-
const full = `${m.provider}/${m.id}`.toLowerCase();
|
|
25146
|
-
let score = 0;
|
|
25147
|
-
if (id === query || full === query) {
|
|
25148
|
-
score = 100;
|
|
25149
|
-
} else if (id.includes(query) || full.includes(query)) {
|
|
25150
|
-
score = 60 + query.length / id.length * 30;
|
|
25151
|
-
} else if (name.includes(query)) {
|
|
25152
|
-
score = 40 + query.length / (name.length || 1) * 20;
|
|
25153
|
-
} else if (query.split(/[\s\-/]+/).every((part) => id.includes(part) || name.includes(part) || m.provider.toLowerCase().includes(part))) {
|
|
25154
|
-
score = 20;
|
|
25155
|
-
}
|
|
25156
|
-
if (score > bestScore) {
|
|
25157
|
-
bestScore = score;
|
|
25158
|
-
bestMatch = m;
|
|
25159
|
-
}
|
|
25160
|
-
}
|
|
25161
|
-
return bestMatch && bestScore >= 20 ? `${bestMatch.provider}/${bestMatch.id}` : void 0;
|
|
25162
|
-
}
|
|
25163
|
-
var init_model_resolver = __esm({
|
|
25164
|
-
"src/runtime/model/model-resolver.ts"() {
|
|
25165
|
-
"use strict";
|
|
25166
|
-
}
|
|
25167
|
-
});
|
|
25168
|
-
|
|
25169
|
-
// src/runtime/model/model-scope.ts
|
|
25170
|
-
function patternToRegExp(pattern) {
|
|
25171
|
-
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
|
|
25172
|
-
return new RegExp(`${escaped.replace(/\*/g, ".*")}`, "i");
|
|
25173
|
-
}
|
|
25174
|
-
function matchesModelPattern(modelId, pattern) {
|
|
25175
|
-
if (!modelId || !pattern) return false;
|
|
25176
|
-
const id = modelId.trim();
|
|
25177
|
-
const pat = pattern.trim();
|
|
25178
|
-
if (!id || !pat) return false;
|
|
25179
|
-
if (id.toLowerCase() === pat.toLowerCase()) return true;
|
|
25180
|
-
if (pat.includes("*")) {
|
|
25181
|
-
try {
|
|
25182
|
-
return patternToRegExp(pat).test(id);
|
|
25183
|
-
} catch {
|
|
25184
|
-
return false;
|
|
25185
|
-
}
|
|
25186
|
-
}
|
|
25187
|
-
return id.toLowerCase().includes(pat.toLowerCase());
|
|
25188
|
-
}
|
|
25189
|
-
function checkModelScope(modelId, patterns, source) {
|
|
25190
|
-
if (!modelId) {
|
|
25191
|
-
return {
|
|
25192
|
-
inScope: true,
|
|
25193
|
-
source,
|
|
25194
|
-
model: "",
|
|
25195
|
-
reason: "no model specified"
|
|
25196
|
-
};
|
|
25197
|
-
}
|
|
25198
|
-
if (!patterns || patterns.length === 0) {
|
|
25199
|
-
return { inScope: true, source, model: modelId };
|
|
25200
|
-
}
|
|
25201
|
-
for (const pattern of patterns) {
|
|
25202
|
-
if (matchesModelPattern(modelId, pattern)) {
|
|
25203
|
-
return {
|
|
25204
|
-
inScope: true,
|
|
25205
|
-
source,
|
|
25206
|
-
model: modelId,
|
|
25207
|
-
matchedPattern: pattern
|
|
25208
|
-
};
|
|
25209
|
-
}
|
|
25210
|
-
}
|
|
25211
|
-
return {
|
|
25212
|
-
inScope: false,
|
|
25213
|
-
source,
|
|
25214
|
-
model: modelId,
|
|
25215
|
-
reason: `model "${modelId}" is not in enabledModels allowlist (${patterns.join(", ")})`
|
|
25216
|
-
};
|
|
25217
|
-
}
|
|
25218
|
-
async function readEnabledModelsPatterns(cwd, agentDir) {
|
|
25219
|
-
try {
|
|
25220
|
-
const mod = await import("@earendil-works/pi-coding-agent").catch(() => null);
|
|
25221
|
-
if (!mod) return [];
|
|
25222
|
-
const SettingsManagerCtor = mod.SettingsManager;
|
|
25223
|
-
if (!SettingsManagerCtor?.create) return [];
|
|
25224
|
-
const sm = SettingsManagerCtor.create(cwd, agentDir);
|
|
25225
|
-
const patterns = sm.getEnabledModels?.();
|
|
25226
|
-
return Array.isArray(patterns) ? patterns : [];
|
|
25227
|
-
} catch {
|
|
25228
|
-
return [];
|
|
25229
|
-
}
|
|
25230
|
-
}
|
|
25231
|
-
var init_model_scope = __esm({
|
|
25232
|
-
"src/runtime/model/model-scope.ts"() {
|
|
25233
|
-
"use strict";
|
|
25234
|
-
}
|
|
25235
|
-
});
|
|
25236
|
-
|
|
25237
|
-
// src/runtime/model/model-fallback.ts
|
|
25238
|
-
import * as fs39 from "node:fs";
|
|
25239
|
-
import * as os11 from "node:os";
|
|
25240
|
-
import * as path33 from "node:path";
|
|
25241
|
-
function modelInfoFromUnknown(value) {
|
|
25242
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
25243
|
-
const record = value;
|
|
25244
|
-
if (typeof record.provider !== "string" || typeof record.id !== "string") return void 0;
|
|
25245
|
-
return {
|
|
25246
|
-
provider: record.provider,
|
|
25247
|
-
id: record.id,
|
|
25248
|
-
fullId: `${record.provider}/${record.id}`
|
|
25249
|
-
};
|
|
25250
|
-
}
|
|
25251
|
-
function availableModelInfosFromRegistry(registry2) {
|
|
25252
|
-
if (!registry2 || typeof registry2 !== "object" || Array.isArray(registry2)) return void 0;
|
|
25253
|
-
const candidate = registry2;
|
|
25254
|
-
const raw = typeof candidate.getAvailable === "function" ? candidate.getAvailable() : typeof candidate.getAll === "function" ? candidate.getAll() : void 0;
|
|
25255
|
-
if (!Array.isArray(raw)) return void 0;
|
|
25256
|
-
return raw.map(modelInfoFromUnknown).filter((entry) => entry !== void 0);
|
|
25257
|
-
}
|
|
25258
|
-
function modelStringFromUnknown(model) {
|
|
25259
|
-
return modelInfoFromUnknown(model)?.fullId;
|
|
25260
|
-
}
|
|
25261
|
-
function uniqueModelInfos(models) {
|
|
25262
|
-
const seen = /* @__PURE__ */ new Set();
|
|
25263
|
-
return models.filter((model) => {
|
|
25264
|
-
if (seen.has(model.fullId)) return false;
|
|
25265
|
-
seen.add(model.fullId);
|
|
25266
|
-
return true;
|
|
25267
|
-
});
|
|
25268
|
-
}
|
|
25269
|
-
function readJsonObject(filePath) {
|
|
25270
|
-
try {
|
|
25271
|
-
const parsed = JSON.parse(fs39.readFileSync(filePath, "utf-8"));
|
|
25272
|
-
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : void 0;
|
|
25273
|
-
} catch {
|
|
25274
|
-
return void 0;
|
|
25275
|
-
}
|
|
25276
|
-
}
|
|
25277
|
-
function piAgentDir() {
|
|
25278
|
-
const envDir = process.env.PI_CODING_AGENT_DIR?.trim();
|
|
25279
|
-
if (envDir) {
|
|
25280
|
-
if (envDir === "~") return os11.homedir();
|
|
25281
|
-
if (envDir.startsWith("~/")) return path33.join(os11.homedir(), envDir.slice(2));
|
|
25282
|
-
return envDir;
|
|
25283
|
-
}
|
|
25284
|
-
return path33.join(os11.homedir(), ".pi", "agent");
|
|
25285
|
-
}
|
|
25286
|
-
function settingsModelInfo(settings) {
|
|
25287
|
-
if (typeof settings?.defaultProvider !== "string" || typeof settings.defaultModel !== "string") return void 0;
|
|
25288
|
-
return {
|
|
25289
|
-
provider: settings.defaultProvider,
|
|
25290
|
-
id: settings.defaultModel,
|
|
25291
|
-
fullId: `${settings.defaultProvider}/${settings.defaultModel}`
|
|
25292
|
-
};
|
|
25293
|
-
}
|
|
25294
|
-
function modelsJsonInfos(modelsJson) {
|
|
25295
|
-
if (!modelsJson?.providers || typeof modelsJson.providers !== "object" || Array.isArray(modelsJson.providers)) return [];
|
|
25296
|
-
const infos = [];
|
|
25297
|
-
for (const [provider, rawConfig] of Object.entries(modelsJson.providers)) {
|
|
25298
|
-
if (!rawConfig || typeof rawConfig !== "object" || Array.isArray(rawConfig)) continue;
|
|
25299
|
-
const config = rawConfig;
|
|
25300
|
-
if (Array.isArray(config.models)) {
|
|
25301
|
-
for (const rawModel of config.models) {
|
|
25302
|
-
if (!rawModel || typeof rawModel !== "object" || Array.isArray(rawModel)) continue;
|
|
25303
|
-
const id = rawModel.id;
|
|
25304
|
-
if (typeof id === "string") infos.push({ provider, id, fullId: `${provider}/${id}` });
|
|
25305
|
-
}
|
|
25306
|
-
}
|
|
25307
|
-
if (config.modelOverrides && typeof config.modelOverrides === "object" && !Array.isArray(config.modelOverrides)) {
|
|
25308
|
-
for (const id of Object.keys(config.modelOverrides)) infos.push({ provider, id, fullId: `${provider}/${id}` });
|
|
25309
|
-
}
|
|
25310
|
-
}
|
|
25311
|
-
return infos;
|
|
25312
|
-
}
|
|
25313
|
-
function configuredModelInfosFromPiConfig(cwd) {
|
|
25314
|
-
const agentDir = piAgentDir();
|
|
25315
|
-
const globalSettings = readJsonObject(path33.join(agentDir, "settings.json"));
|
|
25316
|
-
const projectSettings = cwd ? readJsonObject(path33.join(cwd, ".pi", "settings.json")) : void 0;
|
|
25317
|
-
const effectiveSettings = {
|
|
25318
|
-
...globalSettings ?? {},
|
|
25319
|
-
...projectSettings ?? {}
|
|
25320
|
-
};
|
|
25321
|
-
const defaultModel = settingsModelInfo(effectiveSettings);
|
|
25322
|
-
return uniqueModelInfos([
|
|
25323
|
-
...defaultModel ? [defaultModel] : [],
|
|
25324
|
-
...modelsJsonInfos(readJsonObject(path33.join(agentDir, "models.json")))
|
|
25325
|
-
]);
|
|
25326
|
-
}
|
|
25327
|
-
function splitThinkingSuffix(model) {
|
|
25328
|
-
const colonIdx = model.lastIndexOf(":");
|
|
25329
|
-
if (colonIdx === -1) return { baseModel: model, thinkingSuffix: "" };
|
|
25330
|
-
return {
|
|
25331
|
-
baseModel: model.substring(0, colonIdx),
|
|
25332
|
-
thinkingSuffix: model.substring(colonIdx)
|
|
25333
|
-
};
|
|
25334
|
-
}
|
|
25335
|
-
function resolveModelCandidate(model, availableModels, preferredProvider) {
|
|
25336
|
-
if (!model) return void 0;
|
|
25337
|
-
if (model.includes("/")) return model;
|
|
25338
|
-
if (!availableModels || availableModels.length === 0) return model;
|
|
25339
|
-
const { baseModel, thinkingSuffix } = splitThinkingSuffix(model);
|
|
25340
|
-
const matches = availableModels.filter((entry) => entry.id === baseModel);
|
|
25341
|
-
if (preferredProvider) {
|
|
25342
|
-
const preferredMatch = matches.find((entry) => entry.provider === preferredProvider);
|
|
25343
|
-
if (preferredMatch) return `${preferredMatch.fullId}${thinkingSuffix}`;
|
|
25344
|
-
}
|
|
25345
|
-
if (matches.length !== 1) {
|
|
25346
|
-
const fuzzy = fuzzyResolveModelId(baseModel, availableModels);
|
|
25347
|
-
if (fuzzy) return `${fuzzy}${thinkingSuffix}`;
|
|
25348
|
-
return model;
|
|
25349
|
-
}
|
|
25350
|
-
return `${matches[0].fullId}${thinkingSuffix}`;
|
|
25351
|
-
}
|
|
25352
|
-
function isRetryableModelFailure(error) {
|
|
25353
|
-
if (!error) return false;
|
|
25354
|
-
if (NON_RETRYABLE_MODEL_FAILURE_PATTERNS.some((pattern) => pattern.test(error))) return false;
|
|
25355
|
-
return RETRYABLE_MODEL_FAILURE_PATTERNS.some((pattern) => pattern.test(error));
|
|
25356
|
-
}
|
|
25357
|
-
function formatModelAttemptNote(attempt, nextModel) {
|
|
25358
|
-
const failure = attempt.error?.trim() || `exit ${attempt.exitCode ?? 1}`;
|
|
25359
|
-
return nextModel ? `[fallback] ${attempt.model} failed: ${failure}. Retrying with ${nextModel}.` : `[fallback] ${attempt.model} failed: ${failure}.`;
|
|
25360
|
-
}
|
|
25361
|
-
function buildModelCandidates(primaryModel, fallbackModels, availableModels, preferredProvider) {
|
|
25362
|
-
const seen = /* @__PURE__ */ new Set();
|
|
25363
|
-
const candidates = [];
|
|
25364
|
-
for (const raw of [primaryModel, ...fallbackModels ?? []]) {
|
|
25365
|
-
if (!raw) continue;
|
|
25366
|
-
const normalized = resolveModelCandidate(raw.trim(), availableModels, preferredProvider);
|
|
25367
|
-
if (!normalized || seen.has(normalized)) continue;
|
|
25368
|
-
seen.add(normalized);
|
|
25369
|
-
candidates.push(normalized);
|
|
25370
|
-
}
|
|
25371
|
-
return candidates;
|
|
25372
|
-
}
|
|
25373
|
-
function isAvailableModel(model, availableModels) {
|
|
25374
|
-
if (!availableModels || availableModels.length === 0) return true;
|
|
25375
|
-
const { baseModel } = splitThinkingSuffix(model);
|
|
25376
|
-
if (baseModel.includes("/")) return availableModels.some((entry) => entry.fullId === baseModel);
|
|
25377
|
-
if (availableModels.some((entry) => entry.id === baseModel)) return true;
|
|
25378
|
-
const fuzzy = fuzzyResolveModelId(baseModel, availableModels);
|
|
25379
|
-
return fuzzy !== void 0;
|
|
25380
|
-
}
|
|
25381
|
-
function buildConfiguredModelRouting(input) {
|
|
25382
|
-
const registryModels = availableModelInfosFromRegistry(input.modelRegistry);
|
|
25383
|
-
const configModels = configuredModelInfosFromPiConfig(input.cwd);
|
|
25384
|
-
const availableModels = registryModels && registryModels.length > 0 ? registryModels : configModels.length > 0 ? configModels : registryModels;
|
|
25385
|
-
const parentModel = modelStringFromUnknown(input.parentModel);
|
|
25386
|
-
const preferredProvider = parentModel?.split("/")[0] ?? availableModels?.[0]?.provider;
|
|
25387
|
-
const effectiveAgentModel = input.agentModel?.trim() ? input.agentModel : parentModel;
|
|
25388
|
-
const requested = [input.overrideModel, input.stepModel, input.teamRoleModel, effectiveAgentModel].find(
|
|
25389
|
-
(model) => Boolean(model?.trim())
|
|
25390
|
-
);
|
|
25391
|
-
if (availableModels && availableModels.length === 0)
|
|
25392
|
-
return {
|
|
25393
|
-
requested,
|
|
25394
|
-
candidates: [],
|
|
25395
|
-
reason: "no configured Pi models available"
|
|
25396
|
-
};
|
|
25397
|
-
const rawModels = availableModels ? [
|
|
25398
|
-
input.overrideModel,
|
|
25399
|
-
input.stepModel,
|
|
25400
|
-
input.teamRoleModel,
|
|
25401
|
-
effectiveAgentModel,
|
|
25402
|
-
...input.fallbackModels ?? [],
|
|
25403
|
-
...availableModels.map((model) => model.fullId)
|
|
25404
|
-
] : [input.overrideModel, input.stepModel, input.teamRoleModel, effectiveAgentModel, ...input.fallbackModels ?? [], parentModel];
|
|
25405
|
-
const parentModelRaw = effectiveAgentModel?.trim() || void 0;
|
|
25406
|
-
const configuredModels = rawModels.filter((model) => Boolean(model?.trim())).filter((model, idx) => {
|
|
25407
|
-
if (parentModelRaw && idx === 0 && model.trim() === parentModelRaw) return true;
|
|
25408
|
-
return isAvailableModel(model.trim(), availableModels);
|
|
25409
|
-
});
|
|
25410
|
-
const candidates = buildModelCandidates(configuredModels[0], configuredModels.slice(1), availableModels, preferredProvider);
|
|
25411
|
-
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;
|
|
25412
|
-
let scopeVerdict;
|
|
25413
|
-
if (input.scopeModelsPatterns && input.scopeModelsPatterns.length > 0) {
|
|
25414
|
-
const resolved = candidates[0] ?? requested;
|
|
25415
|
-
const source = input.overrideModel ? "caller" : input.agentModel ? "frontmatter" : "resolved";
|
|
25416
|
-
scopeVerdict = checkModelScope(resolved, input.scopeModelsPatterns, source);
|
|
25417
|
-
if (!scopeVerdict.inScope && source === "caller" && !input.isFrontmatterOverride) {
|
|
25418
|
-
throw errors.modelOutOfScope(resolved ?? "", input.scopeModelsPatterns);
|
|
25419
|
-
}
|
|
25420
|
-
}
|
|
25421
|
-
return { requested, candidates, reason, scopeVerdict };
|
|
25422
|
-
}
|
|
25423
|
-
var RETRYABLE_MODEL_FAILURE_PATTERNS, NON_RETRYABLE_MODEL_FAILURE_PATTERNS;
|
|
25424
|
-
var init_model_fallback = __esm({
|
|
25425
|
-
"src/runtime/model/model-fallback.ts"() {
|
|
25426
|
-
"use strict";
|
|
25427
|
-
init_errors3();
|
|
25428
|
-
init_model_resolver();
|
|
25429
|
-
init_model_scope();
|
|
25430
|
-
RETRYABLE_MODEL_FAILURE_PATTERNS = [
|
|
25431
|
-
/rate.?limit/i,
|
|
25432
|
-
/too many requests/i,
|
|
25433
|
-
/\b429\b/,
|
|
25434
|
-
/rate_limit_error/i,
|
|
25435
|
-
/quota/i,
|
|
25436
|
-
/provider.*unavailable/i,
|
|
25437
|
-
/model.*unavailable/i,
|
|
25438
|
-
/model.*disabled/i,
|
|
25439
|
-
/model.*not found/i,
|
|
25440
|
-
/unknown model/i,
|
|
25441
|
-
/overloaded/i,
|
|
25442
|
-
/service unavailable/i,
|
|
25443
|
-
/temporar(?:ily)? unavailable/i,
|
|
25444
|
-
/connection refused/i,
|
|
25445
|
-
/fetch failed/i,
|
|
25446
|
-
/network error/i,
|
|
25447
|
-
/socket hang up/i,
|
|
25448
|
-
/upstream/i,
|
|
25449
|
-
/timed? out/i,
|
|
25450
|
-
/timeout/i,
|
|
25451
|
-
/\b502\b/,
|
|
25452
|
-
/\b503\b/,
|
|
25453
|
-
/\b504\b/,
|
|
25454
|
-
//
|
|
25455
|
-
// Provider-side 5xx / generic api_error. The pi-core retry layer already
|
|
25456
|
-
// retries these (agent-session.ts matches `500|server error|internal error`),
|
|
25457
|
-
// but the pi-crew MODEL FALLBACK layer must ALSO treat them as retryable so
|
|
25458
|
-
// that when the provider is hard-down across all 3 provider retries, we fail
|
|
25459
|
-
// over to the next configured model instead of giving up. Reported case
|
|
25460
|
-
// (2026-06-17): `500 {"type":"error","error":{"type":"api_error",
|
|
25461
|
-
// "message":"unknown error, 999 (1000)"}}` — a transient provider outage that
|
|
25462
|
-
// should trigger the fallback chain, not abort.
|
|
25463
|
-
//
|
|
25464
|
-
// `api_error` is the OpenAI-compatible generic error type (vs rate_limit_error
|
|
25465
|
-
// / overloaded_error / etc.) and almost always means a transient server fault.
|
|
25466
|
-
//
|
|
25467
|
-
// `unknown error` is the body of the generic message; `internal`/`server`
|
|
25468
|
-
// catch the common phrasings. `\b500\b`/`\b501\b` catch the HTTP status in
|
|
25469
|
-
// the rendered error string.
|
|
25470
|
-
/\b500\b/,
|
|
25471
|
-
/\b501\b/,
|
|
25472
|
-
/api_error/i,
|
|
25473
|
-
/unknown error/i,
|
|
25474
|
-
/internal(?:_server)?[ _]error/i,
|
|
25475
|
-
/server error/i,
|
|
25476
|
-
/bad gateway/i,
|
|
25477
|
-
//
|
|
25478
|
-
// Broader retryable patterns (added 2026-06-25, FIX 2):
|
|
25479
|
-
// - `/provider[_ ]?error/i`: OpenAI-compatible "Provider error" generic fault.
|
|
25480
|
-
// - `/context[_ ]?length[_ ]?exceeded/i`: "context_length_exceeded" from
|
|
25481
|
-
// OpenAI/Anthropic — when the configured model is the bottleneck, a
|
|
25482
|
-
// different model in the fallback chain may have a larger window.
|
|
25483
|
-
// - `/safety/i`: Anthropic safety blocks — typically retryable on a
|
|
25484
|
-
// different model in the fallback chain.
|
|
25485
|
-
// - `/is[_ ]?overloaded/i`: alias to the existing `/overloaded/i` pattern
|
|
25486
|
-
// to catch phrasings like "upstream is overloaded".
|
|
25487
|
-
// - `/\b408\b/`: HTTP 408 Request Timeout — transient, provider-side.
|
|
25488
|
-
//
|
|
25489
|
-
// Intentionally NOT added: `/bad_request/` — can mean bad input (e.g.
|
|
25490
|
-
// invalid schema), which is non-retryable.
|
|
25491
|
-
/provider[_ ]?error/i,
|
|
25492
|
-
/context[_ ]?length[_ ]?exceeded/i,
|
|
25493
|
-
/safety/i,
|
|
25494
|
-
/is[_ ]?overloaded/i,
|
|
25495
|
-
/\b408\b/
|
|
25496
|
-
];
|
|
25497
|
-
NON_RETRYABLE_MODEL_FAILURE_PATTERNS = [
|
|
25498
|
-
/auth(?:entication)?/i,
|
|
25499
|
-
/unauthori[sz]ed/i,
|
|
25500
|
-
/forbidden/i,
|
|
25501
|
-
/api key/i,
|
|
25502
|
-
/token expired/i,
|
|
25503
|
-
/invalid key/i,
|
|
25504
|
-
/billing/i,
|
|
25505
|
-
/credit/i
|
|
25506
|
-
];
|
|
25507
|
-
}
|
|
25508
|
-
});
|
|
25509
|
-
|
|
25510
25877
|
// src/runtime/output/sidechain-output.ts
|
|
25511
25878
|
import * as fs40 from "node:fs";
|
|
25512
25879
|
import * as path34 from "node:path";
|
|
@@ -32556,6 +32923,20 @@ async function resolveScopeModelsPatterns(cwd, agentDir) {
|
|
|
32556
32923
|
if (!scopeModels) return [];
|
|
32557
32924
|
return readEnabledModelsPatterns(cwd, agentDir);
|
|
32558
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
|
+
}
|
|
32559
32940
|
function modelFromRegistry(modelRegistry, modelId) {
|
|
32560
32941
|
if (!modelId?.includes("/")) return void 0;
|
|
32561
32942
|
const registry2 = asRecord6(modelRegistry);
|
|
@@ -32570,7 +32951,7 @@ function modelFromRegistry(modelRegistry, modelId) {
|
|
|
32570
32951
|
}
|
|
32571
32952
|
}
|
|
32572
32953
|
function resolveParentModelFromRegistry(modelRegistry, rawParentModel) {
|
|
32573
|
-
const raw =
|
|
32954
|
+
const raw = modelRefToString(rawParentModel);
|
|
32574
32955
|
if (raw) {
|
|
32575
32956
|
const candidate = raw.includes("/") ? raw : (() => {
|
|
32576
32957
|
const m = modelFromRegistry(modelRegistry, raw);
|
|
@@ -32581,18 +32962,11 @@ function resolveParentModelFromRegistry(modelRegistry, rawParentModel) {
|
|
|
32581
32962
|
})();
|
|
32582
32963
|
if (candidate && modelFromRegistry(modelRegistry, candidate)) return candidate;
|
|
32583
32964
|
}
|
|
32584
|
-
const
|
|
32585
|
-
if (
|
|
32586
|
-
|
|
32587
|
-
|
|
32588
|
-
|
|
32589
|
-
const first = available[0];
|
|
32590
|
-
if (first && typeof first.provider === "string" && typeof first.id === "string") {
|
|
32591
|
-
return `${first.provider}/${first.id}`;
|
|
32592
|
-
}
|
|
32593
|
-
}
|
|
32594
|
-
} catch {
|
|
32595
|
-
}
|
|
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;
|
|
32596
32970
|
}
|
|
32597
32971
|
return raw;
|
|
32598
32972
|
}
|
|
@@ -32852,14 +33226,32 @@ async function runLiveSessionTask(input) {
|
|
|
32852
33226
|
overrideModel: input.modelOverride,
|
|
32853
33227
|
stepModel: input.step.model,
|
|
32854
33228
|
teamRoleModel: input.teamRoleModel,
|
|
33229
|
+
teamRoleFallbackModels: input.teamRoleFallbackModels,
|
|
32855
33230
|
agentModel: input.agent.model,
|
|
33231
|
+
defaultSubagentModel: resolveLiveDefaultSubagentModel(input.manifest.cwd),
|
|
32856
33232
|
fallbackModels: input.agent.fallbackModels,
|
|
32857
33233
|
parentModel: effectiveParentModel,
|
|
32858
33234
|
modelRegistry: input.modelRegistry,
|
|
32859
33235
|
cwd: input.manifest.cwd,
|
|
33236
|
+
policy: resolveLiveModelFallbackPolicy(input.manifest.cwd),
|
|
32860
33237
|
scopeModelsPatterns: await resolveScopeModelsPatterns(input.manifest.cwd)
|
|
32861
33238
|
});
|
|
32862
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;
|
|
32863
33255
|
const mcpProxy = buildMcpProxyFromSession([], { shareMcp: true });
|
|
32864
33256
|
const submitResultTool = createSubmitResultTool((result4) => {
|
|
32865
33257
|
customToolYieldResult = result4;
|
|
@@ -32880,7 +33272,7 @@ async function runLiveSessionTask(input) {
|
|
|
32880
33272
|
} : {},
|
|
32881
33273
|
...input.modelRegistry ? { modelRegistry: input.modelRegistry } : {},
|
|
32882
33274
|
...resolvedModel ? { model: resolvedModel } : {},
|
|
32883
|
-
...
|
|
33275
|
+
...effectiveThinking ? { thinkingLevel: effectiveThinking } : {},
|
|
32884
33276
|
...mcpProxy.enableMcp ? {} : { enableMCP: false },
|
|
32885
33277
|
customTools
|
|
32886
33278
|
});
|
|
@@ -33218,7 +33610,15 @@ ${input.prompt}` : input.prompt;
|
|
|
33218
33610
|
stderr: created.modelFallbackMessage ?? "",
|
|
33219
33611
|
jsonEvents,
|
|
33220
33612
|
usage,
|
|
33221
|
-
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
|
+
}
|
|
33222
33622
|
};
|
|
33223
33623
|
} catch (error) {
|
|
33224
33624
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -36955,6 +37355,43 @@ function buildTeamDoctorReport(input) {
|
|
|
36955
37355
|
}
|
|
36956
37356
|
];
|
|
36957
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
|
+
}),
|
|
36958
37395
|
section("Discovery", () => {
|
|
36959
37396
|
const agentModelHints = discoveredAgentsAll.filter((agent) => agent.model || agent.fallbackModels?.length).length;
|
|
36960
37397
|
return [
|
|
@@ -37139,7 +37576,9 @@ var init_doctor = __esm({
|
|
|
37139
37576
|
init_config();
|
|
37140
37577
|
init_defaults();
|
|
37141
37578
|
init_drift_detector();
|
|
37579
|
+
init_model_fallback();
|
|
37142
37580
|
init_runtime_warmup();
|
|
37581
|
+
init_session_model();
|
|
37143
37582
|
init_pi_spawn();
|
|
37144
37583
|
init_zombie_scanner();
|
|
37145
37584
|
init_team_tool_schema();
|
|
@@ -41164,7 +41603,7 @@ var init_async_runner = __esm({
|
|
|
41164
41603
|
});
|
|
41165
41604
|
|
|
41166
41605
|
// src/runtime/goal-workflow/goal-state-store.ts
|
|
41167
|
-
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";
|
|
41168
41607
|
import { dirname as dirname32 } from "node:path";
|
|
41169
41608
|
function resolveGoalsRoot(cwd) {
|
|
41170
41609
|
const crewRoot = projectCrewRoot(cwd) ?? userCrewRoot();
|
|
@@ -41207,22 +41646,22 @@ var init_goal_state_store = __esm({
|
|
|
41207
41646
|
}
|
|
41208
41647
|
}
|
|
41209
41648
|
/** Atomically persist a goal state. Emits a goal.state_changed event if eventsPath given. */
|
|
41210
|
-
save(
|
|
41211
|
-
assertSafePathId("goalId",
|
|
41212
|
-
const path89 = goalFilePath(this.cwd,
|
|
41213
|
-
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() };
|
|
41214
41653
|
try {
|
|
41215
41654
|
mkdirSync26(dirname32(path89), { recursive: true });
|
|
41216
41655
|
atomicWriteJson(path89, next);
|
|
41217
41656
|
if (eventsPath) {
|
|
41218
41657
|
appendEvent(eventsPath, {
|
|
41219
41658
|
type: "goal.state_changed",
|
|
41220
|
-
runId:
|
|
41221
|
-
data: { goalId:
|
|
41659
|
+
runId: state2.goalId,
|
|
41660
|
+
data: { goalId: state2.goalId, state: state2.state }
|
|
41222
41661
|
});
|
|
41223
41662
|
}
|
|
41224
41663
|
} catch (error) {
|
|
41225
|
-
logInternalError("goal-state-store.save", error, `goalId=${
|
|
41664
|
+
logInternalError("goal-state-store.save", error, `goalId=${state2.goalId}`);
|
|
41226
41665
|
throw error;
|
|
41227
41666
|
}
|
|
41228
41667
|
}
|
|
@@ -41240,8 +41679,8 @@ var init_goal_state_store = __esm({
|
|
|
41240
41679
|
return next;
|
|
41241
41680
|
}
|
|
41242
41681
|
/** Convenience: transition state with optional event emission. */
|
|
41243
|
-
setStatus(goalId,
|
|
41244
|
-
return this.patch(goalId, { state }, eventsPath);
|
|
41682
|
+
setStatus(goalId, state2, eventsPath) {
|
|
41683
|
+
return this.patch(goalId, { state: state2 }, eventsPath);
|
|
41245
41684
|
}
|
|
41246
41685
|
/**
|
|
41247
41686
|
* Compare-And-Set status for atomic stuck↔resume transitions (P1b, RFC v0.5 §P1b).
|
|
@@ -41295,7 +41734,7 @@ var init_goal_state_store = __esm({
|
|
|
41295
41734
|
const code = error.code;
|
|
41296
41735
|
if (code !== "EEXIST") return false;
|
|
41297
41736
|
try {
|
|
41298
|
-
const stat2 =
|
|
41737
|
+
const stat2 = statSync33(lockPath2);
|
|
41299
41738
|
if (Date.now() - stat2.mtimeMs > 5e3) {
|
|
41300
41739
|
unlinkSync7(lockPath2);
|
|
41301
41740
|
const fd = openSync10(lockPath2, "wx");
|
|
@@ -41388,7 +41827,7 @@ var init_verification_integrity = __esm({
|
|
|
41388
41827
|
|
|
41389
41828
|
// src/runtime/workspace-lock.ts
|
|
41390
41829
|
import { createHash as createHash7 } from "node:crypto";
|
|
41391
|
-
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";
|
|
41392
41831
|
import * as path50 from "node:path";
|
|
41393
41832
|
function workspaceLockPath(cwd) {
|
|
41394
41833
|
const absCwd = path50.resolve(cwd);
|
|
@@ -42694,8 +43133,8 @@ function registerPiCrewPowerbarSegments(events, config) {
|
|
|
42694
43133
|
label: "pi-crew workflow steps"
|
|
42695
43134
|
});
|
|
42696
43135
|
}
|
|
42697
|
-
function updatePiCrewPowerbar(events, cwd, config, manifestCache2, snapshotCache, ctx, notificationCount = 0, preloadedManifests) {
|
|
42698
|
-
powerbarPublisher.update(events, cwd, config, manifestCache2, snapshotCache, ctx, notificationCount, preloadedManifests);
|
|
43136
|
+
function updatePiCrewPowerbar(events, cwd, config, manifestCache2, snapshotCache, ctx, notificationCount = 0, preloadedManifests, workspaceId) {
|
|
43137
|
+
powerbarPublisher.update(events, cwd, config, manifestCache2, snapshotCache, ctx, notificationCount, preloadedManifests, workspaceId);
|
|
42699
43138
|
}
|
|
42700
43139
|
function powerbarKey(payload) {
|
|
42701
43140
|
return `${payload.text ?? ""}|${payload.suffix ?? ""}|${payload.bar ?? ""}|${payload.color ?? ""}|${payload.icon ?? ""}|${payload.barSegments ?? ""}`;
|
|
@@ -42739,8 +43178,8 @@ function buildStepsPayload(active, allTasks) {
|
|
|
42739
43178
|
color
|
|
42740
43179
|
};
|
|
42741
43180
|
}
|
|
42742
|
-
function requestPowerbarUpdate(events, cwd, config, manifestCache2, snapshotCache, ctx, notificationCount = 0, preloadedManifests) {
|
|
42743
|
-
powerbarPublisher.request(events, cwd, config, manifestCache2, snapshotCache, ctx, notificationCount, preloadedManifests);
|
|
43181
|
+
function requestPowerbarUpdate(events, cwd, config, manifestCache2, snapshotCache, ctx, notificationCount = 0, preloadedManifests, workspaceId) {
|
|
43182
|
+
powerbarPublisher.request(events, cwd, config, manifestCache2, snapshotCache, ctx, notificationCount, preloadedManifests, workspaceId);
|
|
42744
43183
|
}
|
|
42745
43184
|
function disposePowerbarCoalescer() {
|
|
42746
43185
|
powerbarPublisher.dispose();
|
|
@@ -42762,6 +43201,7 @@ var init_powerbar_publisher = __esm({
|
|
|
42762
43201
|
init_usage();
|
|
42763
43202
|
init_file_coalescer();
|
|
42764
43203
|
init_internal_error();
|
|
43204
|
+
init_session_utils();
|
|
42765
43205
|
init_discover_workflows();
|
|
42766
43206
|
init_render_coalescer();
|
|
42767
43207
|
init_widget_formatters();
|
|
@@ -42777,13 +43217,25 @@ var init_powerbar_publisher = __esm({
|
|
|
42777
43217
|
const a = this.#latestArgs;
|
|
42778
43218
|
this.#latestArgs = null;
|
|
42779
43219
|
if (!a) return;
|
|
42780
|
-
this.update(
|
|
43220
|
+
this.update(
|
|
43221
|
+
a.events,
|
|
43222
|
+
a.cwd,
|
|
43223
|
+
a.config,
|
|
43224
|
+
a.manifestCache,
|
|
43225
|
+
a.snapshotCache,
|
|
43226
|
+
a.ctx,
|
|
43227
|
+
a.notificationCount,
|
|
43228
|
+
a.preloadedManifests,
|
|
43229
|
+
a.workspaceId
|
|
43230
|
+
);
|
|
42781
43231
|
}, 200);
|
|
42782
43232
|
}
|
|
42783
|
-
update(events, cwd, config, manifestCache2, snapshotCache, ctx, notificationCount = 0, preloadedManifests) {
|
|
43233
|
+
update(events, cwd, config, manifestCache2, snapshotCache, ctx, notificationCount = 0, preloadedManifests, workspaceId) {
|
|
42784
43234
|
if (config?.powerbar === false) return;
|
|
42785
43235
|
const useStatusFallback = !hasPowerbarConsumer(events);
|
|
42786
|
-
const
|
|
43236
|
+
const effectiveWorkspaceId = workspaceId ?? extractSessionId(ctx);
|
|
43237
|
+
const allRuns = preloadedManifests ?? (manifestCache2 ? manifestCache2.list(20) : listRecentRuns(cwd, 20));
|
|
43238
|
+
const runs = effectiveWorkspaceId ? allRuns.filter((run) => !run.ownerSessionId || run.ownerSessionId === effectiveWorkspaceId) : allRuns;
|
|
42787
43239
|
const active = runs.map((run) => {
|
|
42788
43240
|
let snapshot;
|
|
42789
43241
|
try {
|
|
@@ -42839,7 +43291,8 @@ var init_powerbar_publisher = __esm({
|
|
|
42839
43291
|
const tokenTotal = hasUsage ? (usage.input ?? 0) + (usage.output ?? 0) + (usage.cacheRead ?? 0) + (usage.cacheWrite ?? 0) : snapshotTokens;
|
|
42840
43292
|
const model = config?.showModel === false ? void 0 : agents.find((agent) => agent.model)?.model?.split("/").at(-1);
|
|
42841
43293
|
const tokenText = config?.showTokens === false || !tokenTotal ? void 0 : compactTokens(tokenTotal);
|
|
42842
|
-
const
|
|
43294
|
+
const liveAgents2 = effectiveWorkspaceId ? listLiveAgentsByWorkspace(effectiveWorkspaceId) : listLiveAgents();
|
|
43295
|
+
const liveRunning = liveAgents2.filter((a) => a.status === "running").length;
|
|
42843
43296
|
const runningCount = agents.filter((a) => a.status === "running").length;
|
|
42844
43297
|
const queuedCount = active.reduce(
|
|
42845
43298
|
(sum, item) => sum + item.tasks.reduce((s, t2) => s + (t2.status === "queued" || t2.status === "waiting" ? 1 : 0), 0),
|
|
@@ -42893,7 +43346,7 @@ var init_powerbar_publisher = __esm({
|
|
|
42893
43346
|
* into a single render pass within 200ms, preventing UI flicker from
|
|
42894
43347
|
* event bursts.
|
|
42895
43348
|
*/
|
|
42896
|
-
request(events, cwd, config, manifestCache2, snapshotCache, ctx, notificationCount = 0, preloadedManifests) {
|
|
43349
|
+
request(events, cwd, config, manifestCache2, snapshotCache, ctx, notificationCount = 0, preloadedManifests, workspaceId) {
|
|
42897
43350
|
if (config?.powerbar === false) return;
|
|
42898
43351
|
this.#latestArgs = {
|
|
42899
43352
|
events,
|
|
@@ -42903,7 +43356,8 @@ var init_powerbar_publisher = __esm({
|
|
|
42903
43356
|
snapshotCache,
|
|
42904
43357
|
ctx,
|
|
42905
43358
|
notificationCount,
|
|
42906
|
-
preloadedManifests
|
|
43359
|
+
preloadedManifests,
|
|
43360
|
+
workspaceId
|
|
42907
43361
|
};
|
|
42908
43362
|
this.#coalescer.request();
|
|
42909
43363
|
}
|
|
@@ -43945,19 +44399,19 @@ function ensureShared() {
|
|
|
43945
44399
|
return shared;
|
|
43946
44400
|
}
|
|
43947
44401
|
function registerOverlayScheduler(render, onInvalidate) {
|
|
43948
|
-
const
|
|
43949
|
-
|
|
43950
|
-
if (onInvalidate)
|
|
44402
|
+
const state2 = ensureShared();
|
|
44403
|
+
state2.renderers.add(render);
|
|
44404
|
+
if (onInvalidate) state2.invalidators.add(onInvalidate);
|
|
43951
44405
|
return {
|
|
43952
44406
|
schedule: () => {
|
|
43953
|
-
|
|
44407
|
+
state2.scheduler.schedule();
|
|
43954
44408
|
},
|
|
43955
44409
|
dispose: () => {
|
|
43956
|
-
|
|
43957
|
-
if (onInvalidate)
|
|
43958
|
-
if (
|
|
43959
|
-
|
|
43960
|
-
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;
|
|
43961
44415
|
}
|
|
43962
44416
|
}
|
|
43963
44417
|
};
|
|
@@ -44611,12 +45065,13 @@ function shouldRecoverTask(task, deadMs) {
|
|
|
44611
45065
|
if (!task.heartbeat) return true;
|
|
44612
45066
|
return task.heartbeat.alive === false || isWorkerHeartbeatStale(task.heartbeat, deadMs);
|
|
44613
45067
|
}
|
|
44614
|
-
function detectInterruptedRuns(cwd, manifestCache2, deadMs = 3e5) {
|
|
45068
|
+
function detectInterruptedRuns(cwd, manifestCache2, deadMs = 3e5, currentSessionId) {
|
|
44615
45069
|
const plans = [];
|
|
44616
45070
|
for (const manifest of manifestCache2.list(50)) {
|
|
44617
45071
|
if (manifest.status !== "running" && manifest.status !== "blocked") continue;
|
|
44618
45072
|
if (isPlanApprovalPending(manifest)) continue;
|
|
44619
45073
|
if (manifest.async?.pid !== void 0 && checkProcessLiveness(manifest.async.pid).alive) continue;
|
|
45074
|
+
if (currentSessionId && manifest.ownerSessionId && manifest.ownerSessionId === currentSessionId) continue;
|
|
44620
45075
|
const loaded = loadRunManifestById(cwd, manifest.runId);
|
|
44621
45076
|
if (!loaded) continue;
|
|
44622
45077
|
const resumableTasks = loaded.tasks.filter((task) => shouldRecoverTask(task, deadMs)).map((task) => task.id);
|
|
@@ -44788,7 +45243,7 @@ function hasRecentLifeEvidence(entry, manifestUpdatedAt, now, staleThresholdMs)
|
|
|
44788
45243
|
if (Number.isFinite(hbAge) && hbAge <= staleThresholdMs) return true;
|
|
44789
45244
|
return false;
|
|
44790
45245
|
}
|
|
44791
|
-
function purgeStaleActiveRunIndex(staleThresholdMs = 3e5, now = Date.now()) {
|
|
45246
|
+
function purgeStaleActiveRunIndex(staleThresholdMs = 3e5, now = Date.now(), currentSessionId) {
|
|
44792
45247
|
const purged = [];
|
|
44793
45248
|
const kept = [];
|
|
44794
45249
|
const entries = readActiveRunRegistry();
|
|
@@ -44830,6 +45285,10 @@ function purgeStaleActiveRunIndex(staleThresholdMs = 3e5, now = Date.now()) {
|
|
|
44830
45285
|
purged.push(entry.runId);
|
|
44831
45286
|
continue;
|
|
44832
45287
|
}
|
|
45288
|
+
if (currentSessionId && manifest?.ownerSessionId && manifest.ownerSessionId === currentSessionId) {
|
|
45289
|
+
kept.push(entry.runId);
|
|
45290
|
+
continue;
|
|
45291
|
+
}
|
|
44833
45292
|
if (manifest?.status === "running" && manifest.async?.pid !== void 0) {
|
|
44834
45293
|
const pidAlive = checkProcessLiveness(manifest.async.pid).alive;
|
|
44835
45294
|
if (!pidAlive && !hasRecentLifeEvidence(entry, manifest.updatedAt, now, staleThresholdMs)) {
|
|
@@ -44930,9 +45389,13 @@ function purgeStaleActiveRunIndex(staleThresholdMs = 3e5, now = Date.now()) {
|
|
|
44930
45389
|
}
|
|
44931
45390
|
return { purged, kept };
|
|
44932
45391
|
}
|
|
44933
|
-
function reconcileAllStaleRuns(cwd, manifestCache2, now = Date.now()) {
|
|
45392
|
+
function reconcileAllStaleRuns(cwd, manifestCache2, now = Date.now(), currentSessionId) {
|
|
44934
45393
|
const results = [];
|
|
44935
|
-
const runIds = manifestCache2.list(50).filter((m) =>
|
|
45394
|
+
const runIds = manifestCache2.list(50).filter((m) => {
|
|
45395
|
+
if (m.status !== "running" && m.status !== "blocked") return false;
|
|
45396
|
+
if (currentSessionId && m.ownerSessionId && m.ownerSessionId === currentSessionId) return false;
|
|
45397
|
+
return true;
|
|
45398
|
+
}).map((m) => m.runId);
|
|
44936
45399
|
for (const runId of runIds) {
|
|
44937
45400
|
const cached2 = manifestCache2.get(runId);
|
|
44938
45401
|
if (!cached2) continue;
|
|
@@ -45016,7 +45479,7 @@ function activeWidgetRuns(cwd, manifestCache2, snapshotCache, preloadedManifests
|
|
|
45016
45479
|
if (now - lastStaleReconcileAt > STALE_RECONCILE_INTERVAL_MS && manifestCache2) {
|
|
45017
45480
|
lastStaleReconcileAt = now;
|
|
45018
45481
|
try {
|
|
45019
|
-
reconcileAllStaleRuns(cwd, manifestCache2);
|
|
45482
|
+
reconcileAllStaleRuns(cwd, manifestCache2, Date.now(), workspaceId);
|
|
45020
45483
|
} catch {
|
|
45021
45484
|
}
|
|
45022
45485
|
}
|
|
@@ -45417,9 +45880,9 @@ function uninstallResizeListener() {
|
|
|
45417
45880
|
process.stdout.removeListener("resize", onResize);
|
|
45418
45881
|
}
|
|
45419
45882
|
}
|
|
45420
|
-
function updateCrewWidget(ctx,
|
|
45883
|
+
function updateCrewWidget(ctx, state2, config, manifestCache2, snapshotCache, preloadedManifests) {
|
|
45421
45884
|
if (!ctx.hasUI) return;
|
|
45422
|
-
|
|
45885
|
+
state2.frame += 1;
|
|
45423
45886
|
const maxLines = config?.widgetMaxLines ?? MAX_LINES_DEFAULT;
|
|
45424
45887
|
let workspaceId = ctx.sessionManager?.getSessionId?.();
|
|
45425
45888
|
if (!workspaceId && manifestCache2) {
|
|
@@ -45428,73 +45891,75 @@ function updateCrewWidget(ctx, state, config, manifestCache2, snapshotCache, pre
|
|
|
45428
45891
|
if (active?.ownerSessionId) workspaceId = active.ownerSessionId;
|
|
45429
45892
|
}
|
|
45430
45893
|
const runs = activeWidgetRuns(ctx.cwd, manifestCache2, snapshotCache, preloadedManifests, workspaceId);
|
|
45431
|
-
const lines = buildWidgetLines(ctx.cwd,
|
|
45894
|
+
const lines = buildWidgetLines(ctx.cwd, state2.frame, maxLines, runs, state2.notificationCount ?? 0, getRenderWidth());
|
|
45432
45895
|
const placement = config?.widgetPlacement ?? DEFAULT_UI.widgetPlacement;
|
|
45433
45896
|
ctx.ui.setStatus(STATUS_KEY, lines.length ? statusSummary(runs) : void 0);
|
|
45434
|
-
const shouldClearLegacy =
|
|
45897
|
+
const shouldClearLegacy = state2.legacyCleared !== true || state2.lastPlacement !== placement;
|
|
45435
45898
|
if (shouldClearLegacy) {
|
|
45436
45899
|
setExtensionWidget(ctx, LEGACY_WIDGET_KEY, void 0, { placement });
|
|
45437
|
-
|
|
45900
|
+
state2.legacyCleared = true;
|
|
45438
45901
|
}
|
|
45439
45902
|
if (!lines.length) {
|
|
45440
|
-
if (
|
|
45903
|
+
if (state2.lastVisibility !== "hidden" || state2.lastPlacement !== placement) {
|
|
45441
45904
|
setExtensionWidget(ctx, WIDGET_KEY, void 0, { placement });
|
|
45442
|
-
|
|
45443
|
-
|
|
45444
|
-
|
|
45445
|
-
|
|
45446
|
-
|
|
45447
|
-
|
|
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;
|
|
45448
45911
|
}
|
|
45449
45912
|
requestRender(ctx);
|
|
45450
45913
|
return;
|
|
45451
45914
|
}
|
|
45452
|
-
const needsWidgetInstall =
|
|
45453
|
-
if (!
|
|
45454
|
-
|
|
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 = {
|
|
45455
45918
|
cwd: ctx.cwd,
|
|
45456
|
-
frame:
|
|
45919
|
+
frame: state2.frame,
|
|
45457
45920
|
maxLines,
|
|
45458
|
-
notificationCount:
|
|
45921
|
+
notificationCount: state2.notificationCount ?? 0,
|
|
45459
45922
|
manifestCache: manifestCache2,
|
|
45460
45923
|
snapshotCache,
|
|
45461
|
-
preloadManifests: preloadedManifests
|
|
45924
|
+
preloadManifests: preloadedManifests,
|
|
45925
|
+
workspaceId
|
|
45462
45926
|
};
|
|
45463
45927
|
else {
|
|
45464
|
-
|
|
45465
|
-
|
|
45466
|
-
|
|
45467
|
-
|
|
45468
|
-
|
|
45469
|
-
|
|
45470
|
-
|
|
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;
|
|
45471
45936
|
}
|
|
45472
45937
|
if (needsWidgetInstall) {
|
|
45473
|
-
const model =
|
|
45938
|
+
const model = state2.model;
|
|
45474
45939
|
setExtensionWidget(ctx, WIDGET_KEY, ((_tui, theme) => new CrewWidgetComponent(model, theme, _tui)), {
|
|
45475
45940
|
placement,
|
|
45476
45941
|
persist: true
|
|
45477
45942
|
});
|
|
45478
|
-
|
|
45479
|
-
|
|
45480
|
-
|
|
45481
|
-
|
|
45482
|
-
|
|
45943
|
+
state2.lastVisibility = "visible";
|
|
45944
|
+
state2.lastPlacement = placement;
|
|
45945
|
+
state2.lastKey = WIDGET_KEY;
|
|
45946
|
+
state2.lastMaxLines = maxLines;
|
|
45947
|
+
state2.lastCwd = ctx.cwd;
|
|
45483
45948
|
}
|
|
45484
45949
|
requestRender(ctx);
|
|
45485
45950
|
}
|
|
45486
|
-
function stopCrewWidget(ctx,
|
|
45951
|
+
function stopCrewWidget(ctx, state2, config) {
|
|
45487
45952
|
uninstallResizeListener();
|
|
45488
45953
|
if (ctx?.hasUI) {
|
|
45489
45954
|
const placement = config?.widgetPlacement ?? DEFAULT_UI.widgetPlacement;
|
|
45490
45955
|
ctx.ui.setStatus(STATUS_KEY, void 0);
|
|
45491
45956
|
setExtensionWidget(ctx, LEGACY_WIDGET_KEY, void 0, { placement });
|
|
45492
45957
|
setExtensionWidget(ctx, WIDGET_KEY, void 0, { placement });
|
|
45493
|
-
|
|
45494
|
-
|
|
45495
|
-
|
|
45496
|
-
|
|
45497
|
-
|
|
45958
|
+
state2.lastVisibility = "hidden";
|
|
45959
|
+
state2.lastPlacement = placement;
|
|
45960
|
+
state2.lastKey = WIDGET_KEY;
|
|
45961
|
+
state2.model = void 0;
|
|
45962
|
+
state2.legacyCleared = true;
|
|
45498
45963
|
requestRender(ctx);
|
|
45499
45964
|
}
|
|
45500
45965
|
}
|
|
@@ -45603,7 +46068,13 @@ var init_widget = __esm({
|
|
|
45603
46068
|
if (activeResizeTarget === this) activeResizeTarget = void 0;
|
|
45604
46069
|
}
|
|
45605
46070
|
render(width) {
|
|
45606
|
-
const runs = activeWidgetRuns(
|
|
46071
|
+
const runs = activeWidgetRuns(
|
|
46072
|
+
this.model.cwd,
|
|
46073
|
+
this.model.manifestCache,
|
|
46074
|
+
this.model.snapshotCache,
|
|
46075
|
+
this.model.preloadManifests,
|
|
46076
|
+
this.model.workspaceId
|
|
46077
|
+
);
|
|
45607
46078
|
const now = Date.now();
|
|
45608
46079
|
let sigBase;
|
|
45609
46080
|
if (this.cachedBuildSignature !== "" && now - this.cachedBuildSignatureAt < SIGNATURE_CACHE_TTL_MS) {
|
|
@@ -45708,7 +46179,7 @@ var init_tool_progress_formatter = __esm({
|
|
|
45708
46179
|
});
|
|
45709
46180
|
|
|
45710
46181
|
// src/extension/registration/team-tool.ts
|
|
45711
|
-
import { statSync as
|
|
46182
|
+
import { statSync as statSync38 } from "node:fs";
|
|
45712
46183
|
import { Text as Text3 } from "@earendil-works/pi-tui";
|
|
45713
46184
|
async function handleTeamTool(params, ctx) {
|
|
45714
46185
|
if (!_cachedHandleTeamTool) {
|
|
@@ -45731,7 +46202,7 @@ function resolveCwdOverride(baseCwd, override) {
|
|
|
45731
46202
|
if (!override) return { ok: true, cwd: baseCwd };
|
|
45732
46203
|
try {
|
|
45733
46204
|
const resolved = resolveRealContainedPath(baseCwd, override);
|
|
45734
|
-
const stat2 =
|
|
46205
|
+
const stat2 = statSync38(resolved);
|
|
45735
46206
|
if (!stat2.isDirectory())
|
|
45736
46207
|
return {
|
|
45737
46208
|
ok: false,
|
|
@@ -52641,6 +53112,20 @@ function resolveConfiguredMaxAttempts(cwd) {
|
|
|
52641
53112
|
function computeSpawnBudgetMax(attemptModelsCount, configuredMaxAttempts) {
|
|
52642
53113
|
return attemptModelsCount * (configuredMaxAttempts + 1);
|
|
52643
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
|
+
}
|
|
52644
53129
|
function detectRetryableModelFailureFromOutput(parsed) {
|
|
52645
53130
|
const messages = parsed.errorMessages;
|
|
52646
53131
|
if (messages && messages.length > 0) {
|
|
@@ -52686,24 +53171,45 @@ async function runChildProcessTask(ctx) {
|
|
|
52686
53171
|
let transcriptPath;
|
|
52687
53172
|
let terminalEvidence = [];
|
|
52688
53173
|
let startupEvidence = ctx.startupEvidence;
|
|
53174
|
+
const modelFallbackPolicy = resolveTaskModelFallbackPolicy(task.cwd);
|
|
53175
|
+
const defaultSubagentModel = resolveTaskDefaultSubagentModel(task.cwd);
|
|
52689
53176
|
const modelRoutingPlan = buildConfiguredModelRouting({
|
|
52690
53177
|
overrideModel: input.modelOverride,
|
|
52691
53178
|
stepModel: input.step.model,
|
|
52692
53179
|
teamRoleModel: input.teamRoleModel,
|
|
53180
|
+
teamRoleFallbackModels: input.teamRoleFallbackModels,
|
|
52693
53181
|
agentModel: input.agent.model,
|
|
53182
|
+
defaultSubagentModel,
|
|
52694
53183
|
fallbackModels: input.agent.fallbackModels,
|
|
52695
53184
|
parentModel: input.parentModel,
|
|
52696
53185
|
modelRegistry: input.modelRegistry,
|
|
52697
53186
|
cwd: task.cwd,
|
|
53187
|
+
policy: modelFallbackPolicy,
|
|
52698
53188
|
scopeModelsPatterns: await resolveTaskScopeModelsPatterns(task.cwd)
|
|
52699
53189
|
});
|
|
52700
53190
|
const candidates = modelRoutingPlan.candidates;
|
|
52701
|
-
|
|
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];
|
|
52702
53207
|
if (input.spawnBudget && input.spawnBudget.max === 0) {
|
|
52703
53208
|
input.spawnBudget.max = computeSpawnBudgetMax(attemptModels.length, resolveConfiguredMaxAttempts(task.cwd));
|
|
52704
53209
|
}
|
|
52705
53210
|
const logs = [];
|
|
52706
53211
|
let finalStderr = "";
|
|
53212
|
+
let reResolveUsed = false;
|
|
52707
53213
|
modelAttempts = [];
|
|
52708
53214
|
let finalCheckpointWritten = false;
|
|
52709
53215
|
let lastAgentRecordPersistedAt = 0;
|
|
@@ -52824,6 +53330,7 @@ async function runChildProcessTask(ctx) {
|
|
|
52824
53330
|
excludeContextBash: input.runtimeConfig?.excludeContextBash,
|
|
52825
53331
|
sessionId: manifest.sessionId,
|
|
52826
53332
|
role: task.role,
|
|
53333
|
+
thinkingOverride: input.teamRoleThinking,
|
|
52827
53334
|
runId: manifest.runId,
|
|
52828
53335
|
agentId: task.id,
|
|
52829
53336
|
artifactsRoot: manifest.artifactsRoot,
|
|
@@ -52992,7 +53499,8 @@ async function runChildProcessTask(ctx) {
|
|
|
52992
53499
|
);
|
|
52993
53500
|
if (!error) break;
|
|
52994
53501
|
let nextModel = attemptModels[i + 1];
|
|
52995
|
-
if (!nextModel && isRetryableModelFailure(error)) {
|
|
53502
|
+
if (!nextModel && !reResolveUsed && isRetryableModelFailure(error)) {
|
|
53503
|
+
reResolveUsed = true;
|
|
52996
53504
|
const reResolved = buildConfiguredModelRouting({
|
|
52997
53505
|
overrideModel: void 0,
|
|
52998
53506
|
stepModel: void 0,
|
|
@@ -53002,10 +53510,17 @@ async function runChildProcessTask(ctx) {
|
|
|
53002
53510
|
parentModel: attempt.model,
|
|
53003
53511
|
modelRegistry: input.modelRegistry,
|
|
53004
53512
|
cwd: task.cwd,
|
|
53513
|
+
policy: modelFallbackPolicy,
|
|
53005
53514
|
scopeModelsPatterns: await resolveTaskScopeModelsPatterns(task.cwd)
|
|
53006
53515
|
});
|
|
53007
|
-
|
|
53008
|
-
|
|
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
|
+
}
|
|
53009
53524
|
}
|
|
53010
53525
|
if (!nextModel || !isRetryableModelFailure(error)) break;
|
|
53011
53526
|
logs.push(formatModelAttemptNote(attempt, nextModel), "");
|
|
@@ -53074,7 +53589,9 @@ async function runChildProcessTask(ctx) {
|
|
|
53074
53589
|
resolved: resolvedModel,
|
|
53075
53590
|
fallbackChain: candidates,
|
|
53076
53591
|
reason: fallbackReason ?? modelRoutingPlan.reason,
|
|
53077
|
-
usedAttempt
|
|
53592
|
+
usedAttempt,
|
|
53593
|
+
droppedRequested: modelRoutingPlan.droppedRequested,
|
|
53594
|
+
autoFallbackCount: modelRoutingPlan.autoFallbackCount
|
|
53078
53595
|
}
|
|
53079
53596
|
};
|
|
53080
53597
|
tasks = updateTask(tasks, task);
|
|
@@ -54985,6 +55502,8 @@ async function runLiveTask(input) {
|
|
|
54985
55502
|
modelRegistry: input.modelRegistry,
|
|
54986
55503
|
modelOverride: input.modelOverride,
|
|
54987
55504
|
teamRoleModel: input.teamRoleModel,
|
|
55505
|
+
teamRoleFallbackModels: input.teamRoleFallbackModels,
|
|
55506
|
+
teamRoleThinking: input.teamRoleThinking,
|
|
54988
55507
|
isCurrent,
|
|
54989
55508
|
workspaceId: input.workspaceId,
|
|
54990
55509
|
// Phase 2: Pass output schema for yield validation
|
|
@@ -55028,6 +55547,14 @@ async function runLiveTask(input) {
|
|
|
55028
55547
|
usage: liveResult.usage,
|
|
55029
55548
|
agentProgress: applyUsageToProgress(task.agentProgress, liveResult.usage)
|
|
55030
55549
|
};
|
|
55550
|
+
if (liveResult.modelRouting)
|
|
55551
|
+
task = {
|
|
55552
|
+
...task,
|
|
55553
|
+
modelRouting: {
|
|
55554
|
+
...liveResult.modelRouting,
|
|
55555
|
+
usedAttempt: 0
|
|
55556
|
+
}
|
|
55557
|
+
};
|
|
55031
55558
|
persistLiveProgress({ type: "attempt_finished" }, true);
|
|
55032
55559
|
const resultArtifact = writeArtifact(manifest.artifactsRoot, {
|
|
55033
55560
|
kind: "result",
|
|
@@ -55154,6 +55681,8 @@ async function runTeamTask(input) {
|
|
|
55154
55681
|
modelRegistry: input.modelRegistry,
|
|
55155
55682
|
modelOverride: input.modelOverride,
|
|
55156
55683
|
teamRoleModel: input.teamRoleModel,
|
|
55684
|
+
teamRoleFallbackModels: input.teamRoleFallbackModels,
|
|
55685
|
+
teamRoleThinking: input.teamRoleThinking,
|
|
55157
55686
|
workspaceId: input.workspaceId
|
|
55158
55687
|
});
|
|
55159
55688
|
task = live.task;
|
|
@@ -56682,10 +57211,7 @@ async function cancelRunFromSignal(ctx) {
|
|
|
56682
57211
|
return { kind: "return", result: { manifest: ctx.manifest, tasks: ctx.tasks } };
|
|
56683
57212
|
}
|
|
56684
57213
|
async function terminaliseRunWithDrain(ctx, opts) {
|
|
56685
|
-
const inflightTaskIds =
|
|
56686
|
-
for (const unit of ctx.pendingUnits.values()) {
|
|
56687
|
-
for (const id of unit.taskIds) inflightTaskIds.add(id);
|
|
56688
|
-
}
|
|
57214
|
+
const inflightTaskIds = ctx.dispatchedTaskIds;
|
|
56689
57215
|
const outcomes = await drainPendingUnits(ctx.pendingUnits, ctx.runController);
|
|
56690
57216
|
const validResults = [];
|
|
56691
57217
|
for (const outcome of outcomes) {
|
|
@@ -57006,6 +57532,8 @@ async function dispatchBatch(ctx, decision2) {
|
|
|
57006
57532
|
modelRegistry: input.modelRegistry,
|
|
57007
57533
|
modelOverride: input.modelOverride,
|
|
57008
57534
|
teamRoleModel: teamRole?.model,
|
|
57535
|
+
teamRoleThinking: teamRole?.thinking,
|
|
57536
|
+
teamRoleFallbackModels: teamRole?.fallbackModels,
|
|
57009
57537
|
teamRoleSkills: teamRole?.skills,
|
|
57010
57538
|
skillOverride: input.skillOverride,
|
|
57011
57539
|
limits: input.limits,
|
|
@@ -57191,6 +57719,7 @@ async function dispatchBatch(ctx, decision2) {
|
|
|
57191
57719
|
promise: rawPromise,
|
|
57192
57720
|
wrapped
|
|
57193
57721
|
});
|
|
57722
|
+
for (const id of unitTaskIds) ctx.dispatchedTaskIds.add(id);
|
|
57194
57723
|
}
|
|
57195
57724
|
}
|
|
57196
57725
|
async function mergeUnitResult(ctx) {
|
|
@@ -57551,6 +58080,7 @@ async function executeTeamRunCore(input, manifest, workflow) {
|
|
|
57551
58080
|
queueIndex,
|
|
57552
58081
|
wfMachine,
|
|
57553
58082
|
pendingUnits,
|
|
58083
|
+
dispatchedTaskIds: /* @__PURE__ */ new Set(),
|
|
57554
58084
|
runController,
|
|
57555
58085
|
runtimeKind,
|
|
57556
58086
|
adaptivePlanInjected,
|
|
@@ -59677,14 +60207,14 @@ var init_dwf_state_store = __esm({
|
|
|
59677
60207
|
}
|
|
59678
60208
|
}
|
|
59679
60209
|
/** Atomically persist a checkpoint state. Stamps `updatedAt` (callers need not set it). */
|
|
59680
|
-
save(
|
|
60210
|
+
save(state2) {
|
|
59681
60211
|
const path89 = this.path;
|
|
59682
|
-
const next = { ...
|
|
60212
|
+
const next = { ...state2, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
59683
60213
|
try {
|
|
59684
60214
|
mkdirSync39(dirname37(path89), { recursive: true });
|
|
59685
60215
|
atomicWriteJson(path89, next);
|
|
59686
60216
|
} catch (error) {
|
|
59687
|
-
logInternalError("dwf-state-store.save", error, `runId=${
|
|
60217
|
+
logInternalError("dwf-state-store.save", error, `runId=${state2.runId}`);
|
|
59688
60218
|
throw error;
|
|
59689
60219
|
}
|
|
59690
60220
|
}
|
|
@@ -60583,9 +61113,9 @@ async function runDynamicWorkflow(input) {
|
|
|
60583
61113
|
resumedState,
|
|
60584
61114
|
// round-18 P2-3: checkpoint after each ctx.agent() call so a crash between calls
|
|
60585
61115
|
// leaves durable state. onCheckpoint captures the closure values at call time.
|
|
60586
|
-
onCheckpoint: (
|
|
61116
|
+
onCheckpoint: (state2) => {
|
|
60587
61117
|
try {
|
|
60588
|
-
dwfStore.save(
|
|
61118
|
+
dwfStore.save(state2);
|
|
60589
61119
|
} catch (error) {
|
|
60590
61120
|
logInternalError("dynamic-workflow-runner.checkpoint-save", error, `runId=${manifest.runId}`);
|
|
60591
61121
|
}
|
|
@@ -61183,6 +61713,10 @@ ${dwfResult.manifest.summary ?? ""}`,
|
|
|
61183
61713
|
...updatedManifest,
|
|
61184
61714
|
runtimeResolution,
|
|
61185
61715
|
runConfig: executedConfig,
|
|
61716
|
+
// Background/async runs re-enter through background-runner in a detached
|
|
61717
|
+
// process with no ExtensionContext. Snapshot the model routing inputs so
|
|
61718
|
+
// they survive the hand-off instead of being rediscovered from models.json.
|
|
61719
|
+
modelContext: captureRunModelContext(ctx, params.model),
|
|
61186
61720
|
// Persist budget config on the manifest so it's observable post-run
|
|
61187
61721
|
// (events.jsonl, status reads, audits). The team-runner reads these
|
|
61188
61722
|
// from the input, but persisting them means consumers can verify
|
|
@@ -61393,7 +61927,7 @@ ${dwfResult.manifest.summary ?? ""}`,
|
|
|
61393
61927
|
runtime,
|
|
61394
61928
|
runtimeConfig: executedConfig.runtime,
|
|
61395
61929
|
parentContext: buildParentContext(ctx),
|
|
61396
|
-
parentModel: ctx.model,
|
|
61930
|
+
parentModel: resolveParentModel(ctx.model),
|
|
61397
61931
|
modelRegistry: ctx.modelRegistry,
|
|
61398
61932
|
modelOverride: params.model,
|
|
61399
61933
|
skillOverride,
|
|
@@ -61459,7 +61993,7 @@ ${dwfResult.manifest.summary ?? ""}`,
|
|
|
61459
61993
|
runtime,
|
|
61460
61994
|
runtimeConfig: executedConfig.runtime,
|
|
61461
61995
|
parentContext: buildParentContext(ctx),
|
|
61462
|
-
parentModel: ctx.model,
|
|
61996
|
+
parentModel: resolveParentModel(ctx.model),
|
|
61463
61997
|
modelRegistry: ctx.modelRegistry,
|
|
61464
61998
|
modelOverride: params.model,
|
|
61465
61999
|
skillOverride,
|
|
@@ -61508,6 +62042,7 @@ var init_run2 = __esm({
|
|
|
61508
62042
|
init_param_error();
|
|
61509
62043
|
init_async_runner();
|
|
61510
62044
|
init_runtime_resolver();
|
|
62045
|
+
init_session_model();
|
|
61511
62046
|
init_event_log();
|
|
61512
62047
|
init_i18n();
|
|
61513
62048
|
init_async_marker();
|
|
@@ -61916,7 +62451,7 @@ async function handleResume2(params, ctx) {
|
|
|
61916
62451
|
runtime: decision2.runtime,
|
|
61917
62452
|
runtimeConfig: decision2.executedConfig.runtime,
|
|
61918
62453
|
parentContext: buildParentContext(ctx),
|
|
61919
|
-
parentModel: ctx.model,
|
|
62454
|
+
parentModel: resolveParentModel(ctx.model),
|
|
61920
62455
|
modelRegistry: ctx.modelRegistry,
|
|
61921
62456
|
modelOverride: params.model,
|
|
61922
62457
|
skillOverride: decision2.resumeSkillOverride,
|
|
@@ -62163,6 +62698,7 @@ var init_team_tool2 = __esm({
|
|
|
62163
62698
|
init_run_index();
|
|
62164
62699
|
init_direct_run();
|
|
62165
62700
|
init_runtime_resolver();
|
|
62701
|
+
init_session_model();
|
|
62166
62702
|
init_pi_json_output();
|
|
62167
62703
|
init_config_patch();
|
|
62168
62704
|
init_context();
|
|
@@ -63194,16 +63730,16 @@ function readRunTranscript(manifest, taskId, options = {}) {
|
|
|
63194
63730
|
readRunTranscriptCache.set(cacheKey2, { parsedAt: now, result: result4 });
|
|
63195
63731
|
return result4;
|
|
63196
63732
|
}
|
|
63197
|
-
function renderViewerBase(
|
|
63733
|
+
function renderViewerBase(state2, width, lines, title, subtitle) {
|
|
63198
63734
|
const inner = Math.max(20, width - 4);
|
|
63199
63735
|
const bodyText = lines.join("\n");
|
|
63200
|
-
const { visualLines, skippedCount } = truncateToVisualLines(bodyText,
|
|
63201
|
-
const maxScroll = Math.max(0, visualLines.length -
|
|
63202
|
-
if (
|
|
63203
|
-
|
|
63204
|
-
const visible = visualLines.slice(
|
|
63205
|
-
const statusLine = `${visualLines.length} lines \xB7 ${visualLines.length ? Math.round((
|
|
63206
|
-
const fg = (color, text) =>
|
|
63736
|
+
const { visualLines, skippedCount } = truncateToVisualLines(bodyText, state2.lastHeight, inner);
|
|
63737
|
+
const maxScroll = Math.max(0, visualLines.length - state2.lastHeight);
|
|
63738
|
+
if (state2.autoScroll) state2.scroll = maxScroll;
|
|
63739
|
+
state2.scroll = Math.min(state2.scroll, maxScroll);
|
|
63740
|
+
const visible = visualLines.slice(state2.scroll, state2.scroll + state2.lastHeight);
|
|
63741
|
+
const statusLine = `${visualLines.length} lines \xB7 ${visualLines.length ? Math.round((state2.scroll + visible.length) / visualLines.length * 100) : 100}% \xB7 auto-scroll ${state2.autoScroll ? "on" : "off"}`;
|
|
63742
|
+
const fg = (color, text) => state2.theme.fg(color, text);
|
|
63207
63743
|
const row = (text) => `${fg("border", "\u2502")} ${pad(truncate(text, inner), inner)} ${fg("border", "\u2502")}`;
|
|
63208
63744
|
const linesOut = [
|
|
63209
63745
|
fg("border", `\u256D${"\u2500".repeat(inner + 2)}\u256E`),
|
|
@@ -63772,11 +64308,11 @@ function extractDwfPhaseState(events) {
|
|
|
63772
64308
|
});
|
|
63773
64309
|
return { phases, currentPhase };
|
|
63774
64310
|
}
|
|
63775
|
-
function renderDwfPhaseLines(
|
|
64311
|
+
function renderDwfPhaseLines(state2, options) {
|
|
63776
64312
|
const ascii = options?.ascii === true;
|
|
63777
64313
|
const lines = [];
|
|
63778
|
-
if (
|
|
63779
|
-
for (const entry of
|
|
64314
|
+
if (state2.phases.length > 1) lines.push(DWF_PHASE_HEADER);
|
|
64315
|
+
for (const entry of state2.phases) {
|
|
63780
64316
|
lines.push(` ${markerFor(entry.status, ascii)} Phase: ${entry.name}`);
|
|
63781
64317
|
}
|
|
63782
64318
|
return lines;
|
|
@@ -64582,7 +65118,8 @@ var init_run_dashboard = __esm({
|
|
|
64582
65118
|
if (!this.options.runProvider) return;
|
|
64583
65119
|
const selectedRunId = this.selectedRunId();
|
|
64584
65120
|
const next = this.options.runProvider();
|
|
64585
|
-
|
|
65121
|
+
const unfiltered = Array.isArray(next) ? next : this.runs;
|
|
65122
|
+
this.runs = this.options.workspaceId ? unfiltered.filter((run) => !run.ownerSessionId || run.ownerSessionId === this.options.workspaceId) : unfiltered;
|
|
64586
65123
|
if (selectedRunId) {
|
|
64587
65124
|
const nextIndex = groupedRuns(this.runs, this.options.snapshotCache).filter((row) => row.run).findIndex((row) => row.run?.runId === selectedRunId);
|
|
64588
65125
|
if (nextIndex >= 0) this.selected = nextIndex;
|
|
@@ -65703,24 +66240,24 @@ var init_mascot = __esm({
|
|
|
65703
66240
|
}
|
|
65704
66241
|
}
|
|
65705
66242
|
tickTypewriter() {
|
|
65706
|
-
const
|
|
65707
|
-
if (
|
|
66243
|
+
const state2 = this.effectState;
|
|
66244
|
+
if (state2.pos === void 0) return true;
|
|
65708
66245
|
for (let i = 0; i < 18; i++) {
|
|
65709
|
-
const row = Math.floor(
|
|
65710
|
-
const x =
|
|
66246
|
+
const row = Math.floor(state2.pos / ARMIN_WIDTH);
|
|
66247
|
+
const x = state2.pos % ARMIN_WIDTH;
|
|
65711
66248
|
if (row >= ARMIN_DISPLAY_HEIGHT) return true;
|
|
65712
66249
|
this.currentArminGrid[row][x] = this.finalArminGrid[row][x];
|
|
65713
|
-
|
|
66250
|
+
state2.pos++;
|
|
65714
66251
|
}
|
|
65715
66252
|
return false;
|
|
65716
66253
|
}
|
|
65717
66254
|
tickScanline() {
|
|
65718
|
-
const
|
|
65719
|
-
if (
|
|
66255
|
+
const state2 = this.effectState;
|
|
66256
|
+
if (state2.row === void 0) return true;
|
|
65720
66257
|
for (let step = 0; step < 3; step++) {
|
|
65721
|
-
if (
|
|
65722
|
-
for (let x = 0; x < ARMIN_WIDTH; x++) this.currentArminGrid[
|
|
65723
|
-
|
|
66258
|
+
if (state2.row >= ARMIN_DISPLAY_HEIGHT) return true;
|
|
66259
|
+
for (let x = 0; x < ARMIN_WIDTH; x++) this.currentArminGrid[state2.row][x] = this.finalArminGrid[state2.row][x];
|
|
66260
|
+
state2.row++;
|
|
65724
66261
|
}
|
|
65725
66262
|
return false;
|
|
65726
66263
|
}
|
|
@@ -65756,33 +66293,33 @@ var init_mascot = __esm({
|
|
|
65756
66293
|
return allSettled;
|
|
65757
66294
|
}
|
|
65758
66295
|
tickFade() {
|
|
65759
|
-
const
|
|
65760
|
-
if (!
|
|
66296
|
+
const state2 = this.effectState;
|
|
66297
|
+
if (!state2.positions || state2.idx === void 0) return true;
|
|
65761
66298
|
for (let i = 0; i < 54; i++) {
|
|
65762
|
-
if (
|
|
65763
|
-
const [row, x] =
|
|
66299
|
+
if (state2.idx >= state2.positions.length) return true;
|
|
66300
|
+
const [row, x] = state2.positions[state2.idx];
|
|
65764
66301
|
this.currentArminGrid[row][x] = this.finalArminGrid[row][x];
|
|
65765
|
-
|
|
66302
|
+
state2.idx++;
|
|
65766
66303
|
}
|
|
65767
66304
|
return false;
|
|
65768
66305
|
}
|
|
65769
66306
|
tickCrt() {
|
|
65770
|
-
const
|
|
65771
|
-
if (
|
|
66307
|
+
const state2 = this.effectState;
|
|
66308
|
+
if (state2.expansion === void 0) return true;
|
|
65772
66309
|
const midRow = Math.floor(ARMIN_DISPLAY_HEIGHT / 2);
|
|
65773
66310
|
this.currentArminGrid = emptyArminGrid();
|
|
65774
|
-
const top = midRow -
|
|
65775
|
-
const bottom = midRow +
|
|
66311
|
+
const top = midRow - state2.expansion;
|
|
66312
|
+
const bottom = midRow + state2.expansion;
|
|
65776
66313
|
for (let row = Math.max(0, top); row <= Math.min(ARMIN_DISPLAY_HEIGHT - 1, bottom); row++) {
|
|
65777
66314
|
for (let x = 0; x < ARMIN_WIDTH; x++) this.currentArminGrid[row][x] = this.finalArminGrid[row][x];
|
|
65778
66315
|
}
|
|
65779
|
-
|
|
65780
|
-
return
|
|
66316
|
+
state2.expansion += 3;
|
|
66317
|
+
return state2.expansion > ARMIN_DISPLAY_HEIGHT;
|
|
65781
66318
|
}
|
|
65782
66319
|
tickGlitch() {
|
|
65783
|
-
const
|
|
65784
|
-
if (
|
|
65785
|
-
if (
|
|
66320
|
+
const state2 = this.effectState;
|
|
66321
|
+
if (state2.phase === void 0 || state2.glitchFrames === void 0) return true;
|
|
66322
|
+
if (state2.phase < state2.glitchFrames) {
|
|
65786
66323
|
const offset = Math.floor(Math.random() * 7) - 3;
|
|
65787
66324
|
const swapRow = Math.floor(Math.random() * ARMIN_DISPLAY_HEIGHT);
|
|
65788
66325
|
const doShift = Math.random() < 0.3;
|
|
@@ -65797,7 +66334,7 @@ var init_mascot = __esm({
|
|
|
65797
66334
|
}
|
|
65798
66335
|
}
|
|
65799
66336
|
}
|
|
65800
|
-
|
|
66337
|
+
state2.phase += 3;
|
|
65801
66338
|
return false;
|
|
65802
66339
|
}
|
|
65803
66340
|
for (let row = 0; row < ARMIN_DISPLAY_HEIGHT; row++) {
|
|
@@ -65808,13 +66345,13 @@ var init_mascot = __esm({
|
|
|
65808
66345
|
return true;
|
|
65809
66346
|
}
|
|
65810
66347
|
tickDissolve() {
|
|
65811
|
-
const
|
|
65812
|
-
if (!
|
|
66348
|
+
const state2 = this.effectState;
|
|
66349
|
+
if (!state2.positions || state2.idx === void 0) return true;
|
|
65813
66350
|
for (let i = 0; i < 66; i++) {
|
|
65814
|
-
if (
|
|
65815
|
-
const [row, x] =
|
|
66351
|
+
if (state2.idx >= state2.positions.length) return true;
|
|
66352
|
+
const [row, x] = state2.positions[state2.idx];
|
|
65816
66353
|
this.currentArminGrid[row][x] = this.finalArminGrid[row][x];
|
|
65817
|
-
|
|
66354
|
+
state2.idx++;
|
|
65818
66355
|
}
|
|
65819
66356
|
return false;
|
|
65820
66357
|
}
|
|
@@ -67808,6 +68345,7 @@ var init_commands = __esm({
|
|
|
67808
68345
|
});
|
|
67809
68346
|
|
|
67810
68347
|
// src/runtime/subagent-manager.ts
|
|
68348
|
+
import { randomUUID as randomUUID8 } from "node:crypto";
|
|
67811
68349
|
import * as fs100 from "node:fs";
|
|
67812
68350
|
import * as path81 from "node:path";
|
|
67813
68351
|
function isValidSubagentId(id) {
|
|
@@ -67926,6 +68464,7 @@ var init_subagent_manager = __esm({
|
|
|
67926
68464
|
"resultConsumed",
|
|
67927
68465
|
"background",
|
|
67928
68466
|
"ownerSessionGeneration",
|
|
68467
|
+
"ownerSessionId",
|
|
67929
68468
|
"stuckNotified",
|
|
67930
68469
|
"blockedAt",
|
|
67931
68470
|
"turnCount",
|
|
@@ -67952,7 +68491,7 @@ var init_subagent_manager = __esm({
|
|
|
67952
68491
|
}
|
|
67953
68492
|
spawn(options, runner, signal) {
|
|
67954
68493
|
const record = {
|
|
67955
|
-
id: `agent_${Date.now().toString(36)}_${(++this.counter).toString(36)}`,
|
|
68494
|
+
id: `agent_${Date.now().toString(36)}_${randomUUID8().slice(0, 8)}_${(++this.counter).toString(36)}`,
|
|
67956
68495
|
type: options.type,
|
|
67957
68496
|
description: options.description,
|
|
67958
68497
|
prompt: options.prompt,
|
|
@@ -67962,6 +68501,7 @@ var init_subagent_manager = __esm({
|
|
|
67962
68501
|
skill: options.skill,
|
|
67963
68502
|
background: options.background,
|
|
67964
68503
|
ownerSessionGeneration: options.ownerSessionGeneration,
|
|
68504
|
+
ownerSessionId: options.ownerSessionId,
|
|
67965
68505
|
batchId: options.batchId
|
|
67966
68506
|
};
|
|
67967
68507
|
this.records.set(record.id, record);
|
|
@@ -68214,7 +68754,8 @@ var init_subagent_manager = __esm({
|
|
|
68214
68754
|
id: current.id,
|
|
68215
68755
|
runId: current.runId,
|
|
68216
68756
|
durationMs: Math.max(0, Date.now() - current.blockedAt),
|
|
68217
|
-
ownerSessionGeneration: current.ownerSessionGeneration
|
|
68757
|
+
ownerSessionGeneration: current.ownerSessionGeneration,
|
|
68758
|
+
ownerSessionId: current.ownerSessionId
|
|
68218
68759
|
});
|
|
68219
68760
|
savePersistedSubagentRecord(cwd, current);
|
|
68220
68761
|
};
|
|
@@ -68599,16 +69140,16 @@ async function importLiveRunSidebar() {
|
|
|
68599
69140
|
}
|
|
68600
69141
|
return _cachedLiveRunSidebar;
|
|
68601
69142
|
}
|
|
68602
|
-
async function installLiveSidebar(ctx, runId,
|
|
69143
|
+
async function installLiveSidebar(ctx, runId, state2, deps) {
|
|
68603
69144
|
const uiConfig = loadConfig(ctx.cwd).config.ui;
|
|
68604
69145
|
const autoOpen = uiConfig?.autoOpenDashboard === true;
|
|
68605
69146
|
const foregroundAutoOpen = uiConfig?.autoOpenDashboardForForegroundRuns ?? DEFAULT_UI.autoOpenDashboardForForegroundRuns;
|
|
68606
69147
|
if (!ctx.hasUI || !autoOpen || !foregroundAutoOpen || (uiConfig?.dashboardPlacement ?? DEFAULT_UI.dashboardPlacement) !== "right") {
|
|
68607
69148
|
return false;
|
|
68608
69149
|
}
|
|
68609
|
-
if (
|
|
68610
|
-
|
|
68611
|
-
|
|
69150
|
+
if (state2.liveSidebarRunId === runId) return true;
|
|
69151
|
+
state2.liveSidebarRunId = runId;
|
|
69152
|
+
state2.dashboardOpened = true;
|
|
68612
69153
|
const widgetPlacement = uiConfig?.widgetPlacement ?? DEFAULT_UI.widgetPlacement;
|
|
68613
69154
|
setExtensionWidget(ctx, "pi-crew", void 0, { placement: widgetPlacement });
|
|
68614
69155
|
setExtensionWidget(ctx, "pi-crew-active", void 0, { placement: widgetPlacement });
|
|
@@ -68644,7 +69185,7 @@ async function installLiveSidebar(ctx, runId, state, deps) {
|
|
|
68644
69185
|
}
|
|
68645
69186
|
}
|
|
68646
69187
|
).finally(() => {
|
|
68647
|
-
if (
|
|
69188
|
+
if (state2.liveSidebarRunId === runId) state2.liveSidebarRunId = void 0;
|
|
68648
69189
|
const c = deps.getCurrentCtx();
|
|
68649
69190
|
if (!c) return;
|
|
68650
69191
|
updateCrewWidget(
|
|
@@ -68661,12 +69202,12 @@ async function installLiveSidebar(ctx, runId, state, deps) {
|
|
|
68661
69202
|
return false;
|
|
68662
69203
|
}
|
|
68663
69204
|
}
|
|
68664
|
-
function clearDashboardPowerbar(
|
|
69205
|
+
function clearDashboardPowerbar(state2, deps) {
|
|
68665
69206
|
const c = deps.getCurrentCtx();
|
|
68666
69207
|
if (c) stopCrewWidget(c, deps.widgetState, loadConfig(c.cwd).config.ui);
|
|
68667
69208
|
clearPiCrewPowerbar(deps.pi.events);
|
|
68668
69209
|
resetPowerbarDedupState();
|
|
68669
|
-
|
|
69210
|
+
state2.dashboardOpened = false;
|
|
68670
69211
|
}
|
|
68671
69212
|
function registerPowerbarSegments(deps, uiConfig) {
|
|
68672
69213
|
registerPiCrewPowerbarSegments(deps.pi.events, uiConfig);
|
|
@@ -68872,29 +69413,31 @@ function markDeadAsyncRunIfNeeded(run, now = Date.now(), quietMs = 3e4) {
|
|
|
68872
69413
|
return failed;
|
|
68873
69414
|
});
|
|
68874
69415
|
}
|
|
68875
|
-
function startAsyncRunNotifier(ctx,
|
|
68876
|
-
if (
|
|
68877
|
-
const generation = options.generation ?? (
|
|
68878
|
-
|
|
69416
|
+
function startAsyncRunNotifier(ctx, state2, intervalMs = 5e3, options = {}) {
|
|
69417
|
+
if (state2.interval) clearInterval(state2.interval);
|
|
69418
|
+
const generation = options.generation ?? (state2.generation ?? 0) + 1;
|
|
69419
|
+
state2.generation = generation;
|
|
68879
69420
|
const startedAtMs = Date.now();
|
|
68880
|
-
const staleBeforeMs =
|
|
68881
|
-
|
|
69421
|
+
const staleBeforeMs = state2.lastStoppedAtMs ?? startedAtMs;
|
|
69422
|
+
const sid = extractSessionId(ctx);
|
|
69423
|
+
const ownsRun = (run) => !sid || !run.ownerSessionId || run.ownerSessionId === sid;
|
|
69424
|
+
for (const run of listRuns(ctx.cwd).filter(ownsRun)) {
|
|
68882
69425
|
const updatedAtMs = timeMs(run.updatedAt) ?? 0;
|
|
68883
|
-
if (isFinished2(run.status) && updatedAtMs < staleBeforeMs)
|
|
69426
|
+
if (isFinished2(run.status) && updatedAtMs < staleBeforeMs) state2.seenFinishedRunIds.add(run.runId);
|
|
68884
69427
|
}
|
|
68885
69428
|
let cachedRuns;
|
|
68886
|
-
|
|
69429
|
+
state2.interval = setInterval(() => {
|
|
68887
69430
|
try {
|
|
68888
69431
|
if (options.isCurrent && !options.isCurrent(generation)) return;
|
|
68889
69432
|
const nowMs3 = Date.now();
|
|
68890
|
-
if (cachedRuns === void 0 || nowMs3 - (
|
|
68891
|
-
cachedRuns = listRuns(ctx.cwd).slice(0, 20);
|
|
68892
|
-
|
|
69433
|
+
if (cachedRuns === void 0 || nowMs3 - (state2.lastListRunsMs ?? 0) > LIST_RUNS_DEBOUNCE_MS) {
|
|
69434
|
+
cachedRuns = listRuns(ctx.cwd).filter(ownsRun).slice(0, 20);
|
|
69435
|
+
state2.lastListRunsMs = nowMs3;
|
|
68893
69436
|
}
|
|
68894
69437
|
for (const run of cachedRuns) {
|
|
68895
69438
|
const current = markDeadAsyncRunIfNeeded(run) ?? run;
|
|
68896
|
-
if (!isFinished2(current.status) ||
|
|
68897
|
-
|
|
69439
|
+
if (!isFinished2(current.status) || state2.seenFinishedRunIds.has(current.runId)) continue;
|
|
69440
|
+
state2.seenFinishedRunIds.add(current.runId);
|
|
68898
69441
|
if (current.workflow === "goal-turn" && current.team.startsWith("goal-")) continue;
|
|
68899
69442
|
const level = current.status === "completed" ? "info" : current.status === "cancelled" ? "warning" : "error";
|
|
68900
69443
|
ctx.ui.notify(`pi-crew run ${current.status}: ${current.runId} (${current.team}/${current.workflow ?? "none"})`, level);
|
|
@@ -68907,13 +69450,13 @@ function startAsyncRunNotifier(ctx, state, intervalMs = 5e3, options = {}) {
|
|
|
68907
69450
|
logInternalError("async-notifier", error, `interval=${intervalMs}`);
|
|
68908
69451
|
}
|
|
68909
69452
|
}, intervalMs);
|
|
68910
|
-
if (typeof
|
|
69453
|
+
if (typeof state2.interval.unref === "function") state2.interval.unref();
|
|
68911
69454
|
}
|
|
68912
|
-
function stopAsyncRunNotifier(
|
|
68913
|
-
if (
|
|
68914
|
-
|
|
68915
|
-
|
|
68916
|
-
|
|
69455
|
+
function stopAsyncRunNotifier(state2) {
|
|
69456
|
+
if (state2.interval) clearInterval(state2.interval);
|
|
69457
|
+
state2.interval = void 0;
|
|
69458
|
+
state2.generation = (state2.generation ?? 0) + 1;
|
|
69459
|
+
state2.lastStoppedAtMs = Date.now();
|
|
68917
69460
|
}
|
|
68918
69461
|
var LIST_RUNS_DEBOUNCE_MS;
|
|
68919
69462
|
var init_async_notifier = __esm({
|
|
@@ -68925,6 +69468,7 @@ var init_async_notifier = __esm({
|
|
|
68925
69468
|
init_event_log();
|
|
68926
69469
|
init_state_store();
|
|
68927
69470
|
init_internal_error();
|
|
69471
|
+
init_session_utils();
|
|
68928
69472
|
init_run_index();
|
|
68929
69473
|
LIST_RUNS_DEBOUNCE_MS = 3e4;
|
|
68930
69474
|
}
|
|
@@ -69137,11 +69681,15 @@ var init_delivery_coordinator = __esm({
|
|
|
69137
69681
|
deps;
|
|
69138
69682
|
ttlTimer;
|
|
69139
69683
|
timerStarted = false;
|
|
69684
|
+
/** The session id passed to the most recent activate(); used by flushQueuedResults
|
|
69685
|
+
* to park deliveries owned by a different session (vector #12). */
|
|
69686
|
+
activeSessionId;
|
|
69140
69687
|
constructor(deps) {
|
|
69141
69688
|
this.deps = deps;
|
|
69142
69689
|
}
|
|
69143
69690
|
activate(sessionId) {
|
|
69144
69691
|
this.active = true;
|
|
69692
|
+
this.activeSessionId = sessionId;
|
|
69145
69693
|
this.flushQueuedResults();
|
|
69146
69694
|
}
|
|
69147
69695
|
deactivate() {
|
|
@@ -69154,7 +69702,7 @@ var init_delivery_coordinator = __esm({
|
|
|
69154
69702
|
getPendingCount() {
|
|
69155
69703
|
return this.pending.length;
|
|
69156
69704
|
}
|
|
69157
|
-
deliverResult(runId, result4) {
|
|
69705
|
+
deliverResult(runId, result4, ownerSessionId) {
|
|
69158
69706
|
if (this.active && this.deps.emit) {
|
|
69159
69707
|
try {
|
|
69160
69708
|
this.deps.emit("pi-crew:run-result", result4);
|
|
@@ -69168,10 +69716,11 @@ var init_delivery_coordinator = __esm({
|
|
|
69168
69716
|
runId,
|
|
69169
69717
|
payload: result4,
|
|
69170
69718
|
timestamp: Date.now(),
|
|
69171
|
-
type: "result"
|
|
69719
|
+
type: "result",
|
|
69720
|
+
ownerSessionId
|
|
69172
69721
|
});
|
|
69173
69722
|
}
|
|
69174
|
-
deliverNotification(notification) {
|
|
69723
|
+
deliverNotification(notification, ownerSessionId) {
|
|
69175
69724
|
let delivered = false;
|
|
69176
69725
|
if (this.active && this.deps.sendFollowUp) {
|
|
69177
69726
|
try {
|
|
@@ -69195,10 +69744,11 @@ var init_delivery_coordinator = __esm({
|
|
|
69195
69744
|
runId: notification.runId ?? "",
|
|
69196
69745
|
payload: notification,
|
|
69197
69746
|
timestamp: Date.now(),
|
|
69198
|
-
type: "notification"
|
|
69747
|
+
type: "notification",
|
|
69748
|
+
ownerSessionId
|
|
69199
69749
|
});
|
|
69200
69750
|
}
|
|
69201
|
-
deliverSteer(runId, message) {
|
|
69751
|
+
deliverSteer(runId, message, ownerSessionId) {
|
|
69202
69752
|
if (this.active && this.deps.sendWakeUp) {
|
|
69203
69753
|
try {
|
|
69204
69754
|
this.deps.sendWakeUp(message);
|
|
@@ -69212,7 +69762,8 @@ var init_delivery_coordinator = __esm({
|
|
|
69212
69762
|
runId,
|
|
69213
69763
|
payload: message,
|
|
69214
69764
|
timestamp: Date.now(),
|
|
69215
|
-
type: "steer"
|
|
69765
|
+
type: "steer",
|
|
69766
|
+
ownerSessionId
|
|
69216
69767
|
});
|
|
69217
69768
|
}
|
|
69218
69769
|
flushQueuedResults() {
|
|
@@ -69223,6 +69774,10 @@ var init_delivery_coordinator = __esm({
|
|
|
69223
69774
|
try {
|
|
69224
69775
|
const retryLater = [];
|
|
69225
69776
|
for (const delivery of batch) {
|
|
69777
|
+
if (delivery.ownerSessionId && this.activeSessionId && delivery.ownerSessionId !== this.activeSessionId) {
|
|
69778
|
+
retryLater.push(delivery);
|
|
69779
|
+
continue;
|
|
69780
|
+
}
|
|
69226
69781
|
if (delivery.type === "steer" && delivery.generation !== void 0 && delivery.generation !== this.generation) {
|
|
69227
69782
|
logInternalError("delivery-coordinator.flush.stale", void 0, `runId=${delivery.runId} type=${delivery.type}`);
|
|
69228
69783
|
continue;
|
|
@@ -69357,7 +69912,7 @@ var init_overflow_recovery = __esm({
|
|
|
69357
69912
|
default:
|
|
69358
69913
|
break;
|
|
69359
69914
|
}
|
|
69360
|
-
const
|
|
69915
|
+
const state2 = {
|
|
69361
69916
|
taskId,
|
|
69362
69917
|
runId,
|
|
69363
69918
|
phase,
|
|
@@ -69366,14 +69921,14 @@ var init_overflow_recovery = __esm({
|
|
|
69366
69921
|
compactionCount,
|
|
69367
69922
|
retryCount
|
|
69368
69923
|
};
|
|
69369
|
-
this.states.set(key,
|
|
69924
|
+
this.states.set(key, state2);
|
|
69370
69925
|
this.resetTimeout(key);
|
|
69371
69926
|
if (this.states.size > MAX_TRACKED_STATES) {
|
|
69372
69927
|
this.evictOldestTerminalState();
|
|
69373
69928
|
}
|
|
69374
69929
|
if (previousPhase !== phase && this.callbacks.onPhaseChange) {
|
|
69375
69930
|
try {
|
|
69376
|
-
this.callbacks.onPhaseChange(
|
|
69931
|
+
this.callbacks.onPhaseChange(state2, previousPhase);
|
|
69377
69932
|
} catch (error) {
|
|
69378
69933
|
logInternalError("overflow-recovery.onPhaseChange", error, `taskId=${taskId}`);
|
|
69379
69934
|
}
|
|
@@ -69382,13 +69937,13 @@ var init_overflow_recovery = __esm({
|
|
|
69382
69937
|
}
|
|
69383
69938
|
getState(taskId, runId) {
|
|
69384
69939
|
if (runId) return this.states.get(this.keyFor(taskId, runId));
|
|
69385
|
-
return [...this.states.values()].find((
|
|
69940
|
+
return [...this.states.values()].find((state2) => state2.taskId === taskId);
|
|
69386
69941
|
}
|
|
69387
69942
|
getPhase(taskId, runId) {
|
|
69388
69943
|
return this.getState(taskId, runId)?.phase ?? "none";
|
|
69389
69944
|
}
|
|
69390
69945
|
removeTask(taskId, runId) {
|
|
69391
|
-
const keys = runId ? [this.keyFor(taskId, runId)] : [...this.states.entries()].filter(([,
|
|
69946
|
+
const keys = runId ? [this.keyFor(taskId, runId)] : [...this.states.entries()].filter(([, state2]) => state2.taskId === taskId).map(([key]) => key);
|
|
69392
69947
|
for (const key of keys) this.removeKey(key);
|
|
69393
69948
|
}
|
|
69394
69949
|
/**
|
|
@@ -69400,10 +69955,10 @@ var init_overflow_recovery = __esm({
|
|
|
69400
69955
|
evictOldestTerminalState() {
|
|
69401
69956
|
let oldestKey;
|
|
69402
69957
|
let oldestTimestamp = Infinity;
|
|
69403
|
-
for (const [key,
|
|
69404
|
-
const isTerminal =
|
|
69405
|
-
if (isTerminal &&
|
|
69406
|
-
oldestTimestamp =
|
|
69958
|
+
for (const [key, state2] of this.states) {
|
|
69959
|
+
const isTerminal = state2.phase === "recovered" || state2.phase === "failed" || state2.phase === "none";
|
|
69960
|
+
if (isTerminal && state2.lastEventAt < oldestTimestamp) {
|
|
69961
|
+
oldestTimestamp = state2.lastEventAt;
|
|
69407
69962
|
oldestKey = key;
|
|
69408
69963
|
}
|
|
69409
69964
|
}
|
|
@@ -69432,27 +69987,27 @@ var init_overflow_recovery = __esm({
|
|
|
69432
69987
|
const timeoutMs = current?.phase === "recovered" || current?.phase === "failed" || current?.phase === "none" ? TERMINAL_STATE_TTL_MS : PHASE_TIMEOUT_MS;
|
|
69433
69988
|
const timer = setTimeout(() => {
|
|
69434
69989
|
this.timers.delete(key);
|
|
69435
|
-
const
|
|
69436
|
-
if (!
|
|
69437
|
-
if (
|
|
69990
|
+
const state2 = this.states.get(key);
|
|
69991
|
+
if (!state2) return;
|
|
69992
|
+
if (state2.phase === "recovered" || state2.phase === "failed" || state2.phase === "none") {
|
|
69438
69993
|
this.states.delete(key);
|
|
69439
69994
|
return;
|
|
69440
69995
|
}
|
|
69441
|
-
const previousPhase =
|
|
69442
|
-
|
|
69443
|
-
|
|
69996
|
+
const previousPhase = state2.phase;
|
|
69997
|
+
state2.phase = "failed";
|
|
69998
|
+
state2.lastEventAt = Date.now();
|
|
69444
69999
|
if (this.callbacks.onTimeout) {
|
|
69445
70000
|
try {
|
|
69446
|
-
this.callbacks.onTimeout(
|
|
70001
|
+
this.callbacks.onTimeout(state2);
|
|
69447
70002
|
} catch (error) {
|
|
69448
|
-
logInternalError("overflow-recovery.onTimeout", error, `taskId=${
|
|
70003
|
+
logInternalError("overflow-recovery.onTimeout", error, `taskId=${state2.taskId}`);
|
|
69449
70004
|
}
|
|
69450
70005
|
}
|
|
69451
70006
|
if (this.callbacks.onPhaseChange) {
|
|
69452
70007
|
try {
|
|
69453
|
-
this.callbacks.onPhaseChange(
|
|
70008
|
+
this.callbacks.onPhaseChange(state2, previousPhase);
|
|
69454
70009
|
} catch (error) {
|
|
69455
|
-
logInternalError("overflow-recovery.onPhaseChange-timeout", error, `taskId=${
|
|
70010
|
+
logInternalError("overflow-recovery.onPhaseChange-timeout", error, `taskId=${state2.taskId}`);
|
|
69456
70011
|
}
|
|
69457
70012
|
}
|
|
69458
70013
|
}, timeoutMs);
|
|
@@ -69473,10 +70028,10 @@ __export(lifecycle_exports, {
|
|
|
69473
70028
|
startLifecycleWatchers: () => startLifecycleWatchers,
|
|
69474
70029
|
stopLifecycleWatchers: () => stopLifecycleWatchers
|
|
69475
70030
|
});
|
|
69476
|
-
function startLifecycleWatchers(ctx,
|
|
69477
|
-
if (
|
|
70031
|
+
function startLifecycleWatchers(ctx, state2, deps) {
|
|
70032
|
+
if (state2.notifierStarted) return false;
|
|
69478
70033
|
const loadedConfig = loadConfig(ctx.cwd);
|
|
69479
|
-
|
|
70034
|
+
state2.notifierStarted = true;
|
|
69480
70035
|
try {
|
|
69481
70036
|
void Promise.resolve().then(() => (init_async_notifier(), async_notifier_exports)).then(({ startAsyncRunNotifier: startAsyncRunNotifier2 }) => {
|
|
69482
70037
|
if (deps.isCleanedUp()) return;
|
|
@@ -69487,14 +70042,14 @@ function startLifecycleWatchers(ctx, state, deps) {
|
|
|
69487
70042
|
});
|
|
69488
70043
|
return true;
|
|
69489
70044
|
} catch (error) {
|
|
69490
|
-
|
|
70045
|
+
state2.notifierStarted = false;
|
|
69491
70046
|
logInternalError("register.startLifecycleWatchers", error);
|
|
69492
70047
|
return false;
|
|
69493
70048
|
}
|
|
69494
70049
|
}
|
|
69495
|
-
function stopLifecycleWatchers(
|
|
69496
|
-
if (!
|
|
69497
|
-
|
|
70050
|
+
function stopLifecycleWatchers(state2, deps) {
|
|
70051
|
+
if (!state2.notifierStarted) return;
|
|
70052
|
+
state2.notifierStarted = false;
|
|
69498
70053
|
try {
|
|
69499
70054
|
void Promise.resolve().then(() => (init_async_notifier(), async_notifier_exports)).then(({ stopAsyncRunNotifier: stopAsyncRunNotifier2 }) => {
|
|
69500
70055
|
stopAsyncRunNotifier2(deps.notifierState);
|
|
@@ -69503,11 +70058,11 @@ function stopLifecycleWatchers(state, deps) {
|
|
|
69503
70058
|
logInternalError("register.stopLifecycleWatchers", error);
|
|
69504
70059
|
}
|
|
69505
70060
|
}
|
|
69506
|
-
async function configureNotifications(ctx,
|
|
69507
|
-
|
|
69508
|
-
|
|
69509
|
-
|
|
69510
|
-
|
|
70061
|
+
async function configureNotifications(ctx, state2, deps) {
|
|
70062
|
+
state2.notificationRouter?.dispose();
|
|
70063
|
+
state2.notificationSink?.dispose();
|
|
70064
|
+
state2.notificationRouter = void 0;
|
|
70065
|
+
state2.notificationSink = void 0;
|
|
69511
70066
|
const config = loadConfig(ctx.cwd).config;
|
|
69512
70067
|
if (config.notifications?.enabled === false) return;
|
|
69513
70068
|
const { NotificationRouter: NotificationRouter2 } = await Promise.resolve().then(() => (init_notification_router(), notification_router_exports));
|
|
@@ -69515,18 +70070,18 @@ async function configureNotifications(ctx, state, deps) {
|
|
|
69515
70070
|
const { sendFollowUp: sendFollowUp2 } = await Promise.resolve().then(() => (init_subagent_helpers(), subagent_helpers_exports));
|
|
69516
70071
|
const { updateCrewWidget: updateCrewWidget2 } = await Promise.resolve().then(() => (init_widget(), widget_exports));
|
|
69517
70072
|
if (config.telemetry?.enabled !== false) {
|
|
69518
|
-
|
|
70073
|
+
state2.notificationSink = createJsonlSink2(
|
|
69519
70074
|
projectCrewRoot(ctx.cwd),
|
|
69520
70075
|
config.notifications?.sinkRetentionDays ?? DEFAULT_NOTIFICATIONS.sinkRetentionDays
|
|
69521
70076
|
);
|
|
69522
70077
|
}
|
|
69523
|
-
|
|
70078
|
+
state2.notificationRouter = new NotificationRouter2(
|
|
69524
70079
|
{
|
|
69525
70080
|
dedupWindowMs: config.notifications?.dedupWindowMs ?? DEFAULT_NOTIFICATIONS.dedupWindowMs,
|
|
69526
70081
|
batchWindowMs: config.notifications?.batchWindowMs ?? DEFAULT_NOTIFICATIONS.batchWindowMs,
|
|
69527
70082
|
quietHours: config.notifications?.quietHours,
|
|
69528
70083
|
severityFilter: config.notifications?.severityFilter ?? [...DEFAULT_NOTIFICATIONS.severityFilter],
|
|
69529
|
-
sink: (notification) =>
|
|
70084
|
+
sink: (notification) => state2.notificationSink?.write(notification)
|
|
69530
70085
|
},
|
|
69531
70086
|
(notification) => {
|
|
69532
70087
|
deps.widgetState.notificationCount = (deps.widgetState.notificationCount ?? 0) + 1;
|
|
@@ -69557,20 +70112,20 @@ async function configureNotifications(ctx, state, deps) {
|
|
|
69557
70112
|
}
|
|
69558
70113
|
);
|
|
69559
70114
|
}
|
|
69560
|
-
function disposeNotifications(
|
|
69561
|
-
|
|
69562
|
-
|
|
69563
|
-
|
|
69564
|
-
|
|
69565
|
-
}
|
|
69566
|
-
async function configureDeliveryCoordinator(
|
|
69567
|
-
|
|
69568
|
-
|
|
69569
|
-
|
|
69570
|
-
|
|
70115
|
+
function disposeNotifications(state2) {
|
|
70116
|
+
state2.notificationRouter?.dispose();
|
|
70117
|
+
state2.notificationRouter = void 0;
|
|
70118
|
+
state2.notificationSink?.dispose();
|
|
70119
|
+
state2.notificationSink = void 0;
|
|
70120
|
+
}
|
|
70121
|
+
async function configureDeliveryCoordinator(state2, deps) {
|
|
70122
|
+
state2.deliveryCoordinator?.dispose();
|
|
70123
|
+
state2.deliveryCoordinator = void 0;
|
|
70124
|
+
state2.overflowTracker?.dispose();
|
|
70125
|
+
state2.overflowTracker = void 0;
|
|
69571
70126
|
const { DeliveryCoordinator: DeliveryCoordinator2 } = await Promise.resolve().then(() => (init_delivery_coordinator(), delivery_coordinator_exports));
|
|
69572
70127
|
const { OverflowRecoveryTracker: OverflowRecoveryTracker2 } = await Promise.resolve().then(() => (init_overflow_recovery(), overflow_recovery_exports));
|
|
69573
|
-
|
|
70128
|
+
state2.deliveryCoordinator = new DeliveryCoordinator2({
|
|
69574
70129
|
emit: (event, data) => {
|
|
69575
70130
|
deps.pi.events?.emit?.(event, data);
|
|
69576
70131
|
},
|
|
@@ -69581,7 +70136,7 @@ async function configureDeliveryCoordinator(state, deps) {
|
|
|
69581
70136
|
deps.sendAgentWakeUp(deps.pi, message);
|
|
69582
70137
|
}
|
|
69583
70138
|
});
|
|
69584
|
-
|
|
70139
|
+
state2.overflowTracker = new OverflowRecoveryTracker2({
|
|
69585
70140
|
onPhaseChange: (phaseState, previousPhase) => {
|
|
69586
70141
|
if (deps.observabilityState.metricRegistry) {
|
|
69587
70142
|
deps.observabilityState.metricRegistry.counter("crew.task.overflow_recovery_total", "Overflow recovery phase transitions").inc({
|
|
@@ -69608,11 +70163,11 @@ async function configureDeliveryCoordinator(state, deps) {
|
|
|
69608
70163
|
}
|
|
69609
70164
|
});
|
|
69610
70165
|
}
|
|
69611
|
-
function disposeDeliveryCoordinator(
|
|
69612
|
-
|
|
69613
|
-
|
|
69614
|
-
|
|
69615
|
-
|
|
70166
|
+
function disposeDeliveryCoordinator(state2) {
|
|
70167
|
+
state2.deliveryCoordinator?.dispose();
|
|
70168
|
+
state2.deliveryCoordinator = void 0;
|
|
70169
|
+
state2.overflowTracker?.dispose();
|
|
70170
|
+
state2.overflowTracker = void 0;
|
|
69616
70171
|
}
|
|
69617
70172
|
var init_lifecycle = __esm({
|
|
69618
70173
|
"src/extension/registration/lifecycle.ts"() {
|
|
@@ -70589,21 +71144,21 @@ async function importOTLPExporter() {
|
|
|
70589
71144
|
}
|
|
70590
71145
|
return _cachedOTLPExporter;
|
|
70591
71146
|
}
|
|
70592
|
-
async function configureObservability(ctx,
|
|
70593
|
-
await disposeObservability(
|
|
71147
|
+
async function configureObservability(ctx, state2, deps) {
|
|
71148
|
+
await disposeObservability(state2, deps.isCleanedUp());
|
|
70594
71149
|
const config = loadConfig(ctx.cwd).config;
|
|
70595
71150
|
if (config.observability?.enabled === false) return;
|
|
70596
71151
|
const { createMetricRegistry: createMetricRegistry2 } = await Promise.resolve().then(() => (init_metric_registry(), metric_registry_exports));
|
|
70597
71152
|
const { wireEventToMetrics: wireEventToMetrics2 } = await Promise.resolve().then(() => (init_event_to_metric(), event_to_metric_exports));
|
|
70598
71153
|
const { createMetricFileSink: createMetricFileSink2 } = await Promise.resolve().then(() => (init_metric_sink(), metric_sink_exports));
|
|
70599
|
-
|
|
71154
|
+
state2.metricRegistry = createMetricRegistry2();
|
|
70600
71155
|
if (deps.pi.events) {
|
|
70601
|
-
|
|
71156
|
+
state2.eventMetricSub = wireEventToMetrics2(deps.pi.events, state2.metricRegistry);
|
|
70602
71157
|
}
|
|
70603
71158
|
if (config.telemetry?.enabled !== false) {
|
|
70604
|
-
|
|
71159
|
+
state2.metricSink = createMetricFileSink2({
|
|
70605
71160
|
crewRoot: projectCrewRoot(ctx.cwd),
|
|
70606
|
-
registry:
|
|
71161
|
+
registry: state2.metricRegistry,
|
|
70607
71162
|
retentionDays: config.observability?.metricRetentionDays ?? 7
|
|
70608
71163
|
});
|
|
70609
71164
|
}
|
|
@@ -70611,10 +71166,10 @@ async function configureObservability(ctx, state, deps) {
|
|
|
70611
71166
|
const otlpEndpoint = config.otlp.endpoint;
|
|
70612
71167
|
const otlpHeaders = config.otlp.headers;
|
|
70613
71168
|
const otlpInterval = config.otlp.intervalMs;
|
|
70614
|
-
const owningRegistry =
|
|
71169
|
+
const owningRegistry = state2.metricRegistry;
|
|
70615
71170
|
void importOTLPExporter().then((Ctor) => {
|
|
70616
|
-
if (deps.isCleanedUp() ||
|
|
70617
|
-
|
|
71171
|
+
if (deps.isCleanedUp() || state2.metricRegistry !== owningRegistry || !owningRegistry) return;
|
|
71172
|
+
state2.otlpExporter = new Ctor(
|
|
70618
71173
|
{
|
|
70619
71174
|
endpoint: otlpEndpoint,
|
|
70620
71175
|
headers: otlpHeaders,
|
|
@@ -70622,15 +71177,15 @@ async function configureObservability(ctx, state, deps) {
|
|
|
70622
71177
|
},
|
|
70623
71178
|
owningRegistry
|
|
70624
71179
|
);
|
|
70625
|
-
|
|
71180
|
+
state2.otlpExporter?.start();
|
|
70626
71181
|
}).catch((error) => logInternalError("register.otlp-lazy-import", error));
|
|
70627
71182
|
}
|
|
70628
71183
|
const { HeartbeatWatcher: HeartbeatWatcher2 } = await Promise.resolve().then(() => (init_heartbeat_watcher(), heartbeat_watcher_exports));
|
|
70629
|
-
|
|
71184
|
+
state2.heartbeatWatcher = new HeartbeatWatcher2({
|
|
70630
71185
|
cwd: ctx.cwd,
|
|
70631
71186
|
pollIntervalMs: config.observability?.pollIntervalMs ?? 5e3,
|
|
70632
71187
|
manifestCache: deps.getManifestCache(ctx.cwd),
|
|
70633
|
-
registry:
|
|
71188
|
+
registry: state2.metricRegistry,
|
|
70634
71189
|
router: {
|
|
70635
71190
|
enqueue: (notification) => {
|
|
70636
71191
|
deps.notifyOperator(notification);
|
|
@@ -70646,7 +71201,7 @@ async function configureObservability(ctx, state, deps) {
|
|
|
70646
71201
|
attempts: 0,
|
|
70647
71202
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
70648
71203
|
});
|
|
70649
|
-
|
|
71204
|
+
state2.metricRegistry?.counter("crew.task.deadletter_total", "Deadletter triggers by reason").inc({ reason: "heartbeat-dead" });
|
|
70650
71205
|
deps.pi.events?.emit?.("crew.task.deadletter", {
|
|
70651
71206
|
runId: manifest.runId,
|
|
70652
71207
|
taskId,
|
|
@@ -70654,12 +71209,12 @@ async function configureObservability(ctx, state, deps) {
|
|
|
70654
71209
|
});
|
|
70655
71210
|
}
|
|
70656
71211
|
});
|
|
70657
|
-
|
|
71212
|
+
state2.heartbeatWatcher.start();
|
|
70658
71213
|
try {
|
|
70659
71214
|
deps.pi.on?.("before_agent_start", () => {
|
|
70660
71215
|
if (deps.isCleanedUp()) return;
|
|
70661
71216
|
try {
|
|
70662
|
-
deps.reconcileStaleRuns(ctx.cwd, deps.getManifestCache(ctx.cwd));
|
|
71217
|
+
deps.reconcileStaleRuns(ctx.cwd, deps.getManifestCache(ctx.cwd), extractSessionId(ctx));
|
|
70663
71218
|
} catch (error) {
|
|
70664
71219
|
logInternalError("register.autoRepair.turnHook", error);
|
|
70665
71220
|
}
|
|
@@ -70668,10 +71223,10 @@ async function configureObservability(ctx, state, deps) {
|
|
|
70668
71223
|
}
|
|
70669
71224
|
const autoRepairIntervalMs = config.reliability?.autoRepairIntervalMs ?? 3e5;
|
|
70670
71225
|
if (autoRepairIntervalMs > 0) {
|
|
70671
|
-
|
|
71226
|
+
state2.autoRepairTimer = setInterval(() => {
|
|
70672
71227
|
if (deps.isCleanedUp()) return;
|
|
70673
71228
|
try {
|
|
70674
|
-
const staleResults = deps.reconcileStaleRuns(ctx.cwd, deps.getManifestCache(ctx.cwd));
|
|
71229
|
+
const staleResults = deps.reconcileStaleRuns(ctx.cwd, deps.getManifestCache(ctx.cwd), extractSessionId(ctx));
|
|
70675
71230
|
if (Array.isArray(staleResults) && staleResults.length > 0) {
|
|
70676
71231
|
for (const result4 of staleResults) {
|
|
70677
71232
|
const repaired = result4.repaired;
|
|
@@ -70691,8 +71246,8 @@ async function configureObservability(ctx, state, deps) {
|
|
|
70691
71246
|
logInternalError("register.autoRepair", error);
|
|
70692
71247
|
}
|
|
70693
71248
|
}, autoRepairIntervalMs);
|
|
70694
|
-
|
|
70695
|
-
|
|
71249
|
+
state2.autoRepairTimer.unref();
|
|
71250
|
+
state2.tempReconcileTimer = setInterval(() => {
|
|
70696
71251
|
if (deps.isCleanedUp()) return;
|
|
70697
71252
|
try {
|
|
70698
71253
|
deps.reconcileOrphanedTempWorkspaces(Date.now(), {
|
|
@@ -70722,14 +71277,15 @@ async function configureObservability(ctx, state, deps) {
|
|
|
70722
71277
|
logInternalError("register.tempAutoRepair", error);
|
|
70723
71278
|
}
|
|
70724
71279
|
}, autoRepairIntervalMs * 5);
|
|
70725
|
-
|
|
71280
|
+
state2.tempReconcileTimer.unref();
|
|
70726
71281
|
}
|
|
70727
71282
|
if (config.reliability?.autoRecover === true) {
|
|
70728
71283
|
const cwdSnapshot = ctx.cwd;
|
|
70729
71284
|
const cacheSnapshot = deps.getManifestCache(cwdSnapshot);
|
|
70730
71285
|
void deps.importCrashRecovery().then(({ detectInterruptedRuns: detectInterruptedRuns2 }) => {
|
|
70731
71286
|
if (deps.isCleanedUp()) return;
|
|
70732
|
-
|
|
71287
|
+
const sid = extractSessionId(ctx);
|
|
71288
|
+
for (const plan of detectInterruptedRuns2(cwdSnapshot, cacheSnapshot, 3e5, sid)) {
|
|
70733
71289
|
deps.notifyOperator({
|
|
70734
71290
|
id: `recovery_prompt_${plan.runId}`,
|
|
70735
71291
|
severity: "warning",
|
|
@@ -70742,25 +71298,25 @@ async function configureObservability(ctx, state, deps) {
|
|
|
70742
71298
|
}).catch((error) => logInternalError("register.crash-recovery-lazy-import", error));
|
|
70743
71299
|
}
|
|
70744
71300
|
}
|
|
70745
|
-
async function disposeObservability(
|
|
70746
|
-
|
|
70747
|
-
|
|
70748
|
-
if (
|
|
70749
|
-
clearInterval(
|
|
70750
|
-
|
|
71301
|
+
async function disposeObservability(state2, _isCleanedUp) {
|
|
71302
|
+
state2.heartbeatWatcher?.dispose();
|
|
71303
|
+
state2.heartbeatWatcher = void 0;
|
|
71304
|
+
if (state2.autoRepairTimer) {
|
|
71305
|
+
clearInterval(state2.autoRepairTimer);
|
|
71306
|
+
state2.autoRepairTimer = void 0;
|
|
70751
71307
|
}
|
|
70752
|
-
if (
|
|
70753
|
-
clearInterval(
|
|
70754
|
-
|
|
71308
|
+
if (state2.tempReconcileTimer) {
|
|
71309
|
+
clearInterval(state2.tempReconcileTimer);
|
|
71310
|
+
state2.tempReconcileTimer = void 0;
|
|
70755
71311
|
}
|
|
70756
|
-
|
|
70757
|
-
|
|
70758
|
-
|
|
70759
|
-
|
|
70760
|
-
await
|
|
70761
|
-
|
|
70762
|
-
|
|
70763
|
-
|
|
71312
|
+
state2.metricSink?.dispose();
|
|
71313
|
+
state2.metricSink = void 0;
|
|
71314
|
+
state2.eventMetricSub?.dispose();
|
|
71315
|
+
state2.eventMetricSub = void 0;
|
|
71316
|
+
await state2.otlpExporter?.dispose();
|
|
71317
|
+
state2.otlpExporter = void 0;
|
|
71318
|
+
state2.metricRegistry?.dispose();
|
|
71319
|
+
state2.metricRegistry = void 0;
|
|
70764
71320
|
}
|
|
70765
71321
|
var _cachedOTLPExporter;
|
|
70766
71322
|
var init_observability = __esm({
|
|
@@ -70769,6 +71325,7 @@ var init_observability = __esm({
|
|
|
70769
71325
|
init_config();
|
|
70770
71326
|
init_internal_error();
|
|
70771
71327
|
init_paths();
|
|
71328
|
+
init_session_utils();
|
|
70772
71329
|
}
|
|
70773
71330
|
});
|
|
70774
71331
|
|
|
@@ -74346,7 +74903,10 @@ function buildRegistrationContext(pi) {
|
|
|
74346
74903
|
globalStore: globalThis,
|
|
74347
74904
|
runtimeCleanupStoreKey: RUNTIME_CLEANUP_STORE_KEY,
|
|
74348
74905
|
captureSessionGeneration: () => ctx.sessionGeneration,
|
|
74349
|
-
isOwnerSessionCurrent: (gen) =>
|
|
74906
|
+
isOwnerSessionCurrent: (gen, oid) => {
|
|
74907
|
+
const currentSid = ctx.currentCtx?.sessionManager?.getSessionId?.();
|
|
74908
|
+
return !ctx.cleanedUp && (oid === void 0 || oid === currentSid) && (gen === void 0 || gen === ctx.sessionGeneration);
|
|
74909
|
+
},
|
|
74350
74910
|
isContextCurrent: (c, gen) => !ctx.cleanedUp && ctx.currentCtx === c && ctx.sessionGeneration === gen,
|
|
74351
74911
|
telemetryEnabled: () => loadConfig(ctx.currentCtx?.cwd ?? process.cwd()).config.telemetry?.enabled !== false,
|
|
74352
74912
|
notifyOperator: void 0,
|
|
@@ -74358,7 +74918,7 @@ function buildRegistrationContext(pi) {
|
|
|
74358
74918
|
configureObservability: () => void 0,
|
|
74359
74919
|
configureDeliveryCoordinator: () => void 0,
|
|
74360
74920
|
importCrashRecovery: void 0,
|
|
74361
|
-
purgeStaleActiveRunIndexSyncIfLoaded: () => void 0,
|
|
74921
|
+
purgeStaleActiveRunIndexSyncIfLoaded: (_currentSessionId) => void 0,
|
|
74362
74922
|
startForegroundRun: void 0,
|
|
74363
74923
|
abortForegroundRun: () => false,
|
|
74364
74924
|
openLiveSidebar: () => void 0,
|
|
@@ -74405,10 +74965,10 @@ async function importCrashRecovery() {
|
|
|
74405
74965
|
}
|
|
74406
74966
|
return _cachedCrashRecovery;
|
|
74407
74967
|
}
|
|
74408
|
-
function purgeStaleActiveRunIndexSyncIfLoaded() {
|
|
74968
|
+
function purgeStaleActiveRunIndexSyncIfLoaded(currentSessionId) {
|
|
74409
74969
|
if (!_cachedCrashRecovery) return;
|
|
74410
74970
|
try {
|
|
74411
|
-
_cachedCrashRecovery.purgeStaleActiveRunIndex();
|
|
74971
|
+
_cachedCrashRecovery.purgeStaleActiveRunIndex(3e5, Date.now(), currentSessionId);
|
|
74412
74972
|
} catch (error) {
|
|
74413
74973
|
logInternalError("register.cleanupRuntime.purgeStale", error);
|
|
74414
74974
|
}
|
|
@@ -74761,7 +75321,7 @@ async function configureObservabilityImpl(pi, ctx, extCtx) {
|
|
|
74761
75321
|
getManifestCache: ctx.getManifestCache,
|
|
74762
75322
|
notifyOperator: ctx.notifyOperator,
|
|
74763
75323
|
isCleanedUp: () => ctx.cleanedUp,
|
|
74764
|
-
reconcileStaleRuns: (cwd, cache3) => reconcileAllStaleRuns(cwd, cache3),
|
|
75324
|
+
reconcileStaleRuns: (cwd, cache3, currentSessionId) => reconcileAllStaleRuns(cwd, cache3, void 0, currentSessionId),
|
|
74765
75325
|
reconcileOrphanedTempWorkspaces: (now, opts) => reconcileOrphanedTempWorkspaces(now, opts),
|
|
74766
75326
|
cleanupOrphanTempDirs,
|
|
74767
75327
|
cleanupLegacyOrphanTempDirs,
|
|
@@ -74805,9 +75365,9 @@ import * as fsp3 from "node:fs/promises";
|
|
|
74805
75365
|
import * as net2 from "node:net";
|
|
74806
75366
|
|
|
74807
75367
|
// src/runtime/broker/crew-broker-tokens.ts
|
|
74808
|
-
import { randomUUID as
|
|
75368
|
+
import { randomUUID as randomUUID9, timingSafeEqual as timingSafeEqual3 } from "node:crypto";
|
|
74809
75369
|
function newBrokerToken() {
|
|
74810
|
-
return
|
|
75370
|
+
return randomUUID9();
|
|
74811
75371
|
}
|
|
74812
75372
|
var BrokerTokenRegistry = class _BrokerTokenRegistry {
|
|
74813
75373
|
map = /* @__PURE__ */ new Map();
|
|
@@ -76096,7 +76656,10 @@ function safeStringify(value) {
|
|
|
76096
76656
|
// src/extension/registration/lifecycle-handlers.ts
|
|
76097
76657
|
init_child_pi();
|
|
76098
76658
|
init_live_agent_manager();
|
|
76659
|
+
init_model_fallback();
|
|
76099
76660
|
init_pi_args();
|
|
76661
|
+
init_provider_quota();
|
|
76662
|
+
init_session_model();
|
|
76100
76663
|
init_orphan_worker_registry();
|
|
76101
76664
|
init_crash_recovery();
|
|
76102
76665
|
init_scheduler();
|
|
@@ -76312,9 +76875,12 @@ function registerCrewAutocomplete(ctx) {
|
|
|
76312
76875
|
// src/extension/session-summary.ts
|
|
76313
76876
|
init_crew_agent_records();
|
|
76314
76877
|
init_process_status();
|
|
76878
|
+
init_session_utils();
|
|
76315
76879
|
init_run_index();
|
|
76316
76880
|
function notifyActiveRuns(ctx) {
|
|
76881
|
+
const sid = extractSessionId(ctx);
|
|
76317
76882
|
const active = listRuns(ctx.cwd).filter((run) => {
|
|
76883
|
+
if (sid && run.ownerSessionId && run.ownerSessionId !== sid) return false;
|
|
76318
76884
|
if (run.status !== "queued" && run.status !== "planning" && run.status !== "running") return false;
|
|
76319
76885
|
const agents = readCrewAgents(run);
|
|
76320
76886
|
return isDisplayActiveRun(run, agents);
|
|
@@ -76379,6 +76945,20 @@ function installSessionLifecycleHandlers(pi, ctx) {
|
|
|
76379
76945
|
installSessionShutdownHandler(pi, ctx);
|
|
76380
76946
|
installSessionStartHandler(pi, ctx);
|
|
76381
76947
|
installSessionBeforeSwitchHandler(pi, ctx);
|
|
76948
|
+
installModelTrackingHandlers(pi);
|
|
76949
|
+
}
|
|
76950
|
+
function installModelTrackingHandlers(pi) {
|
|
76951
|
+
pi.on("model_select", (event) => {
|
|
76952
|
+
noteSessionModel(event.model);
|
|
76953
|
+
});
|
|
76954
|
+
pi.on("thinking_level_select", (event) => {
|
|
76955
|
+
noteSessionThinking(event.level);
|
|
76956
|
+
});
|
|
76957
|
+
pi.on("after_provider_response", (event) => {
|
|
76958
|
+
const model = currentSessionModel();
|
|
76959
|
+
const provider = model ? providerOfModelRef(model) : void 0;
|
|
76960
|
+
if (provider) noteProviderResponse(provider, event.status, event.headers);
|
|
76961
|
+
});
|
|
76382
76962
|
}
|
|
76383
76963
|
function installSessionShutdownHandler(pi, ctx) {
|
|
76384
76964
|
pi.on("session_shutdown", (event) => {
|
|
@@ -76408,6 +76988,7 @@ function installSessionBeforeSwitchHandler(pi, ctx) {
|
|
|
76408
76988
|
ctx.lifecycleState.deliveryCoordinator?.deactivate();
|
|
76409
76989
|
resetPowerbarDedupState();
|
|
76410
76990
|
stopAsyncRunNotifier(ctx.notifierState);
|
|
76991
|
+
clearProviderQuotaCache();
|
|
76411
76992
|
ctx.stopSessionBoundSubagents();
|
|
76412
76993
|
});
|
|
76413
76994
|
}
|
|
@@ -76428,6 +77009,8 @@ function installSessionStartHandler(pi, ctx) {
|
|
|
76428
77009
|
ctx.sessionGeneration++;
|
|
76429
77010
|
const ownerGeneration = ctx.sessionGeneration;
|
|
76430
77011
|
ctx.currentCtx = extensionCtx;
|
|
77012
|
+
noteSessionModel(extensionCtx.model, "session_start");
|
|
77013
|
+
noteSessionThinking(extensionCtx.thinkingLevel);
|
|
76431
77014
|
if (!ctx.crewAutocompleteRegistered) {
|
|
76432
77015
|
ctx.crewAutocompleteRegistered = true;
|
|
76433
77016
|
registerCrewAutocomplete(extensionCtx);
|
|
@@ -76537,7 +77120,7 @@ async function runDeferredSessionCleanup(pi, ctx, ownerGeneration, currentSessio
|
|
|
76537
77120
|
logInternalError("register.sessionStart.orphanWorkers", error);
|
|
76538
77121
|
}
|
|
76539
77122
|
try {
|
|
76540
|
-
const { purged } = purgeStaleActiveRunIndexFn();
|
|
77123
|
+
const { purged } = purgeStaleActiveRunIndexFn(3e5, Date.now(), currentSessionId);
|
|
76541
77124
|
if (purged.length > 0) {
|
|
76542
77125
|
ctx.notifyOperator({
|
|
76543
77126
|
id: `active_index_purge`,
|
|
@@ -76551,7 +77134,7 @@ async function runDeferredSessionCleanup(pi, ctx, ownerGeneration, currentSessio
|
|
|
76551
77134
|
logInternalError("register.sessionStart.globalIndexPurge", error);
|
|
76552
77135
|
}
|
|
76553
77136
|
try {
|
|
76554
|
-
const staleResults = reconcileAllStaleRuns(extensionCtx.cwd, ctx.getManifestCache(extensionCtx.cwd)) ?? [];
|
|
77137
|
+
const staleResults = reconcileAllStaleRuns(extensionCtx.cwd, ctx.getManifestCache(extensionCtx.cwd), Date.now(), currentSessionId) ?? [];
|
|
76555
77138
|
if (staleResults.length > 0) {
|
|
76556
77139
|
ctx.notifyOperator({
|
|
76557
77140
|
id: "stale_reconcile",
|
|
@@ -76671,6 +77254,9 @@ function setupCrewScheduler(pi, ctx, extensionCtx, sessionId) {
|
|
|
76671
77254
|
});
|
|
76672
77255
|
return crewScheduler;
|
|
76673
77256
|
}
|
|
77257
|
+
function filterManifestsForHealthNotifications(manifests, currentSessionId) {
|
|
77258
|
+
return manifests.filter((run) => !run.ownerSessionId || run.ownerSessionId === currentSessionId);
|
|
77259
|
+
}
|
|
76674
77260
|
function setupRenderLoop(pi, ctx, extensionCtx, loadedConfig) {
|
|
76675
77261
|
ctx.disposeRenderSchedulerSubscriptions();
|
|
76676
77262
|
ctx.renderScheduler?.dispose();
|
|
@@ -76764,11 +77350,8 @@ function setupRenderLoop(pi, ctx, extensionCtx, loadedConfig) {
|
|
|
76764
77350
|
ctx.widgetState.notificationCount ?? 0,
|
|
76765
77351
|
manifests
|
|
76766
77352
|
);
|
|
76767
|
-
const
|
|
76768
|
-
const
|
|
76769
|
-
const sessionManifests = manifests.filter(
|
|
76770
|
-
(run) => !run.ownerSessionId || run.ownerSessionId === currentSessionId || run.ownerSessionGeneration === currentSessionGen
|
|
76771
|
-
);
|
|
77353
|
+
const currentSessionId = ctx.currentCtx?.sessionManager?.getSessionId();
|
|
77354
|
+
const sessionManifests = filterManifestsForHealthNotifications(manifests, currentSessionId);
|
|
76772
77355
|
const now = Date.now();
|
|
76773
77356
|
for (const run of sessionManifests) {
|
|
76774
77357
|
if (run.status !== "running") continue;
|
|
@@ -76998,6 +77581,7 @@ init_powerbar_publisher();
|
|
|
76998
77581
|
init_widget();
|
|
76999
77582
|
init_internal_error();
|
|
77000
77583
|
init_paths();
|
|
77584
|
+
init_session_utils();
|
|
77001
77585
|
init_async_notifier();
|
|
77002
77586
|
init_team_tool2();
|
|
77003
77587
|
init_lifecycle();
|
|
@@ -77021,6 +77605,7 @@ function buildCleanupSessionResourcesOnly(ctx) {
|
|
|
77021
77605
|
return () => {
|
|
77022
77606
|
if (ctx.cleanedUp) return;
|
|
77023
77607
|
ctx.cleanedUp = true;
|
|
77608
|
+
const sid = extractSessionId(ctx.currentCtx);
|
|
77024
77609
|
if (ctx.preloadTimer) {
|
|
77025
77610
|
clearTimeout(ctx.preloadTimer);
|
|
77026
77611
|
ctx.preloadTimer = void 0;
|
|
@@ -77032,7 +77617,7 @@ function buildCleanupSessionResourcesOnly(ctx) {
|
|
|
77032
77617
|
ctx.stopSessionBoundSubagents();
|
|
77033
77618
|
ctx.crewScheduler?.stop();
|
|
77034
77619
|
stopAsyncRunNotifier(ctx.notifierState);
|
|
77035
|
-
ctx.purgeStaleActiveRunIndexSyncIfLoaded();
|
|
77620
|
+
ctx.purgeStaleActiveRunIndexSyncIfLoaded(sid);
|
|
77036
77621
|
stopCrewWidget(ctx.currentCtx, ctx.widgetState, ctx.currentCtx ? loadConfig(ctx.currentCtx.cwd).config.ui : void 0);
|
|
77037
77622
|
clearPiCrewPowerbar(ctx.pi.events);
|
|
77038
77623
|
disposePowerbarCoalescer();
|
|
@@ -77064,6 +77649,7 @@ function buildCleanupRuntime(ctx) {
|
|
|
77064
77649
|
return () => {
|
|
77065
77650
|
if (ctx.cleanedUp) return;
|
|
77066
77651
|
ctx.cleanedUp = true;
|
|
77652
|
+
const sid = extractSessionId(ctx.currentCtx);
|
|
77067
77653
|
if (ctx.preloadTimer) {
|
|
77068
77654
|
clearTimeout(ctx.preloadTimer);
|
|
77069
77655
|
ctx.preloadTimer = void 0;
|
|
@@ -77078,7 +77664,7 @@ function buildCleanupRuntime(ctx) {
|
|
|
77078
77664
|
stopAllWatchdogs();
|
|
77079
77665
|
ctx.crewScheduler?.stop();
|
|
77080
77666
|
stopAsyncRunNotifier(ctx.notifierState);
|
|
77081
|
-
ctx.purgeStaleActiveRunIndexSyncIfLoaded();
|
|
77667
|
+
ctx.purgeStaleActiveRunIndexSyncIfLoaded(sid);
|
|
77082
77668
|
stopCrewWidget(ctx.currentCtx, ctx.widgetState, ctx.currentCtx ? loadConfig(ctx.currentCtx.cwd).config.ui : void 0);
|
|
77083
77669
|
clearPiCrewPowerbar(ctx.pi.events);
|
|
77084
77670
|
disposePowerbarCoalescer();
|
|
@@ -77155,7 +77741,7 @@ function createCompletionCoalescer(pi, ctx) {
|
|
|
77155
77741
|
const f = ctx.subagentManager.getRecord(c.agentId);
|
|
77156
77742
|
const p = ctx.currentCtx ? readPersistedSubagentRecord(ctx.currentCtx.cwd, c.agentId) : void 0;
|
|
77157
77743
|
if (f?.resultConsumed || p?.resultConsumed) return false;
|
|
77158
|
-
if (!ctx.isOwnerSessionCurrent(f?.ownerSessionGeneration ?? c.ownerGen)) return false;
|
|
77744
|
+
if (!ctx.isOwnerSessionCurrent(f?.ownerSessionGeneration ?? c.ownerGen, f?.ownerSessionId)) return false;
|
|
77159
77745
|
return true;
|
|
77160
77746
|
};
|
|
77161
77747
|
const flush = () => {
|
|
@@ -77263,7 +77849,7 @@ function onTerminalStatus(pi, ctx, record, coalescer) {
|
|
|
77263
77849
|
});
|
|
77264
77850
|
}
|
|
77265
77851
|
if (!record.background) return;
|
|
77266
|
-
if (!ctx.isOwnerSessionCurrent(record.ownerSessionGeneration)) return;
|
|
77852
|
+
if (!ctx.isOwnerSessionCurrent(record.ownerSessionGeneration, record.ownerSessionId)) return;
|
|
77267
77853
|
if (record.status !== "completed" && record.status !== "failed" && record.status !== "cancelled" && record.status !== "blocked" && record.status !== "error")
|
|
77268
77854
|
return;
|
|
77269
77855
|
const agentId = record.id;
|
|
@@ -77279,7 +77865,7 @@ function onTerminalStatus(pi, ctx, record, coalescer) {
|
|
|
77279
77865
|
const fresh = ctx.subagentManager.getRecord(agentId);
|
|
77280
77866
|
const persisted = ctx.currentCtx ? readPersistedSubagentRecord(ctx.currentCtx.cwd, agentId) : void 0;
|
|
77281
77867
|
if (fresh?.resultConsumed || persisted?.resultConsumed) return;
|
|
77282
|
-
if (!ctx.isOwnerSessionCurrent(fresh?.ownerSessionGeneration ?? ownerGen)) return;
|
|
77868
|
+
if (!ctx.isOwnerSessionCurrent(fresh?.ownerSessionGeneration ?? ownerGen, fresh?.ownerSessionId)) return;
|
|
77283
77869
|
const member = {
|
|
77284
77870
|
id: agentId,
|
|
77285
77871
|
description: agentDescription,
|
|
@@ -77314,7 +77900,8 @@ function onTerminalStatus(pi, ctx, record, coalescer) {
|
|
|
77314
77900
|
}
|
|
77315
77901
|
function onInternalEvent(pi, ctx, event, payload) {
|
|
77316
77902
|
const ownerGeneration = typeof payload?.ownerSessionGeneration === "number" ? payload.ownerSessionGeneration : void 0;
|
|
77317
|
-
|
|
77903
|
+
const ownerSessionId = typeof payload?.ownerSessionId === "string" ? payload.ownerSessionId : void 0;
|
|
77904
|
+
if (ownerGeneration !== void 0 && !ctx.isOwnerSessionCurrent(ownerGeneration, ownerSessionId)) return;
|
|
77318
77905
|
if (event === "subagent.stuck-blocked") {
|
|
77319
77906
|
const p = payload;
|
|
77320
77907
|
const id = typeof p.id === "string" ? p.id : "unknown";
|
|
@@ -77428,6 +78015,7 @@ function registerSubagentTools(pi, subagentManager, options = {}) {
|
|
|
77428
78015
|
spawnOptions.ownerSessionGeneration = options.ownerSessionGeneration?.();
|
|
77429
78016
|
if (!spawnOptions.prompt.trim()) return subagentToolResult(t("agent.requiresPrompt"), {}, true);
|
|
77430
78017
|
const ctxWithSession = withSessionId(ctx);
|
|
78018
|
+
spawnOptions.ownerSessionId = ctxWithSession.sessionId;
|
|
77431
78019
|
const runner = async (currentOptions, childSignal) => handleTeamTool6(
|
|
77432
78020
|
{
|
|
77433
78021
|
action: "run",
|
|
@@ -77549,6 +78137,10 @@ function registerSubagentTools(pi, subagentManager, options = {}) {
|
|
|
77549
78137
|
const inMemory = subagentManager.getRecord(p.agent_id);
|
|
77550
78138
|
const record = inMemory ?? readPersistedSubagentRecord(ctx.cwd, p.agent_id);
|
|
77551
78139
|
if (!record) return subagentToolResult(t("result.notFound", { id: p.agent_id }), {}, true);
|
|
78140
|
+
const currentSessionId = withSessionId(ctx).sessionId;
|
|
78141
|
+
if (record.ownerSessionId && record.ownerSessionId !== currentSessionId) {
|
|
78142
|
+
return subagentToolResult("Agent belongs to another session.", {}, true);
|
|
78143
|
+
}
|
|
77552
78144
|
let current = refreshPersistedSubagentRecord(ctx, record);
|
|
77553
78145
|
if (inMemory && current !== inMemory) Object.assign(inMemory, current);
|
|
77554
78146
|
if (!inMemory && !current.runId && (current.status === "running" || current.status === "queued")) {
|
|
@@ -77599,9 +78191,11 @@ function registerSubagentTools(pi, subagentManager, options = {}) {
|
|
|
77599
78191
|
}
|
|
77600
78192
|
const output = readSubagentRunResult(ctx, current);
|
|
77601
78193
|
if (current.status !== "running" && current.status !== "queued" && current.status !== "blocked") {
|
|
77602
|
-
current.
|
|
77603
|
-
|
|
77604
|
-
|
|
78194
|
+
if (!current.ownerSessionId || current.ownerSessionId === currentSessionId) {
|
|
78195
|
+
current.resultConsumed = true;
|
|
78196
|
+
if (inMemory) inMemory.resultConsumed = true;
|
|
78197
|
+
savePersistedSubagentRecord(ctx.cwd, current);
|
|
78198
|
+
}
|
|
77605
78199
|
}
|
|
77606
78200
|
const text = [
|
|
77607
78201
|
p.verbose ? formatSubagentRecord(current) : void 0,
|