zelari-code 2.37.1 → 2.37.3
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/dist/cli/headless/liveTurnAbort.js +1 -1
- package/dist/cli/headless/liveTurnAbort.js.map +1 -1
- package/dist/cli/headless/runOneTurn.js +15 -5
- package/dist/cli/headless/runOneTurn.js.map +1 -1
- package/dist/cli/main.bundled.js +287 -89
- package/dist/cli/main.bundled.js.map +4 -4
- package/dist/cli/provider/capabilities.js +14 -0
- package/dist/cli/provider/capabilities.js.map +1 -1
- package/dist/cli/provider/openai-compatible.js +244 -58
- package/dist/cli/provider/openai-compatible.js.map +1 -1
- package/dist/cli/serve/harnessServer.js +6 -2
- package/dist/cli/serve/harnessServer.js.map +1 -1
- package/dist/cli/serve/sessionControl.js.map +1 -1
- package/dist/cli/tools/taskTool.js +64 -40
- package/dist/cli/tools/taskTool.js.map +1 -1
- package/dist/cli/tools/tentacleHeartbeat.js +47 -0
- package/dist/cli/tools/tentacleHeartbeat.js.map +1 -0
- package/package.json +2 -2
package/dist/cli/main.bundled.js
CHANGED
|
@@ -28993,6 +28993,9 @@ var init_AgentHarness = __esm({
|
|
|
28993
28993
|
maxToolLoopIterations;
|
|
28994
28994
|
maxToolLoopHardCap;
|
|
28995
28995
|
cancelled = false;
|
|
28996
|
+
/** Host-supplied cancel cause (`turn_timeout` vs user Stop). */
|
|
28997
|
+
cancelReason;
|
|
28998
|
+
cancelEventEmitted = false;
|
|
28996
28999
|
activeController = null;
|
|
28997
29000
|
queue = [];
|
|
28998
29001
|
/**
|
|
@@ -29347,13 +29350,28 @@ ${shared.content}`,
|
|
|
29347
29350
|
* After cancel() returns, the harness should be discarded by the
|
|
29348
29351
|
* caller (the run() generator finishes after the current turn ends).
|
|
29349
29352
|
* For mid-stream interrupt + new-prompt injection, see Task C.3.2.
|
|
29353
|
+
*
|
|
29354
|
+
* `reason` is optional host context: `turn_timeout` is the Desktop sidecar
|
|
29355
|
+
* idle watchdog (not a user Stop). The error event message must not claim
|
|
29356
|
+
* the user cancelled when they did not.
|
|
29350
29357
|
*/
|
|
29351
|
-
cancel() {
|
|
29358
|
+
cancel(reason) {
|
|
29352
29359
|
if (this.cancelled)
|
|
29353
29360
|
return;
|
|
29354
29361
|
this.cancelled = true;
|
|
29362
|
+
if (reason)
|
|
29363
|
+
this.cancelReason = reason;
|
|
29355
29364
|
this.activeController?.abort();
|
|
29356
29365
|
}
|
|
29366
|
+
buildCancelEvent() {
|
|
29367
|
+
this.cancelEventEmitted = true;
|
|
29368
|
+
const watchdog = this.cancelReason === "turn_timeout";
|
|
29369
|
+
return createBrainEvent("error", this.sessionId, {
|
|
29370
|
+
severity: "cancelled",
|
|
29371
|
+
message: watchdog ? "Turn cancelled: Desktop idle watchdog saw no events (silent model thinking or a tentacle with no tools). This was not a user Stop." : "Run cancelled by user.",
|
|
29372
|
+
code: watchdog ? "turn_timeout" : "cancelled"
|
|
29373
|
+
});
|
|
29374
|
+
}
|
|
29357
29375
|
/** Current size of the queued user-prompt buffer. */
|
|
29358
29376
|
get queueLength() {
|
|
29359
29377
|
return this.queue.length;
|
|
@@ -29398,6 +29416,7 @@ ${shared.content}`,
|
|
|
29398
29416
|
async *run() {
|
|
29399
29417
|
const startTime = Date.now();
|
|
29400
29418
|
this.activeController = new AbortController();
|
|
29419
|
+
this.cancelEventEmitted = false;
|
|
29401
29420
|
this.toolCallCache = /* @__PURE__ */ new Map();
|
|
29402
29421
|
this.toolCallCounts = /* @__PURE__ */ new Map();
|
|
29403
29422
|
this.textToolReentries = 0;
|
|
@@ -29643,6 +29662,11 @@ ${shared.content}`,
|
|
|
29643
29662
|
} catch {
|
|
29644
29663
|
}
|
|
29645
29664
|
}
|
|
29665
|
+
if (this.cancelled && !this.cancelEventEmitted) {
|
|
29666
|
+
const cancelEvent = this.buildCancelEvent();
|
|
29667
|
+
this.emit(cancelEvent);
|
|
29668
|
+
yield cancelEvent;
|
|
29669
|
+
}
|
|
29646
29670
|
const agentEnd = createBrainEvent("agent_end", this.sessionId, {
|
|
29647
29671
|
reason: hadError ? "error" : this.cancelled ? "cancelled" : "completed",
|
|
29648
29672
|
durationMs: Date.now() - startTime,
|
|
@@ -29754,11 +29778,7 @@ ${shared.content}`,
|
|
|
29754
29778
|
let lastLoopCheckLen = 0;
|
|
29755
29779
|
for await (const delta of stream) {
|
|
29756
29780
|
if (this.cancelled) {
|
|
29757
|
-
const cancelEvent =
|
|
29758
|
-
severity: "cancelled",
|
|
29759
|
-
message: "Run cancelled by user.",
|
|
29760
|
-
code: "cancelled"
|
|
29761
|
-
});
|
|
29781
|
+
const cancelEvent = this.buildCancelEvent();
|
|
29762
29782
|
this.emit(cancelEvent);
|
|
29763
29783
|
yield cancelEvent;
|
|
29764
29784
|
break;
|
|
@@ -38416,7 +38436,7 @@ var CORE_VERSION;
|
|
|
38416
38436
|
var init_version = __esm({
|
|
38417
38437
|
"packages/core/dist/version.js"() {
|
|
38418
38438
|
"use strict";
|
|
38419
|
-
CORE_VERSION = "2.37.
|
|
38439
|
+
CORE_VERSION = "2.37.3";
|
|
38420
38440
|
}
|
|
38421
38441
|
});
|
|
38422
38442
|
|
|
@@ -43086,6 +43106,43 @@ var init_metrics2 = __esm({
|
|
|
43086
43106
|
}
|
|
43087
43107
|
});
|
|
43088
43108
|
|
|
43109
|
+
// src/cli/tools/tentacleHeartbeat.ts
|
|
43110
|
+
function resolveTentacleHeartbeatMs(env = process.env) {
|
|
43111
|
+
const raw = env.ZELARI_TENTACLE_HEARTBEAT_MS?.trim();
|
|
43112
|
+
if (raw === "0" || raw === "off") return 0;
|
|
43113
|
+
if (!raw) return TENTACLE_HEARTBEAT_DEFAULT_MS;
|
|
43114
|
+
const n = Number.parseInt(raw, 10);
|
|
43115
|
+
return Number.isFinite(n) && n >= 0 ? n : TENTACLE_HEARTBEAT_DEFAULT_MS;
|
|
43116
|
+
}
|
|
43117
|
+
function formatTentacleElapsed(ms) {
|
|
43118
|
+
const totalSec = Math.max(0, Math.floor(ms / 1e3));
|
|
43119
|
+
const m = Math.floor(totalSec / 60);
|
|
43120
|
+
const s = totalSec % 60;
|
|
43121
|
+
return m > 0 ? `${m}m ${s}s` : `${s}s`;
|
|
43122
|
+
}
|
|
43123
|
+
function tentacleHeartbeatCaption(elapsedMs) {
|
|
43124
|
+
return `reasoning \xB7 ${formatTentacleElapsed(elapsedMs)}`;
|
|
43125
|
+
}
|
|
43126
|
+
function startTentacleHeartbeat(onBeat, opts) {
|
|
43127
|
+
const intervalMs = opts?.intervalMs ?? resolveTentacleHeartbeatMs();
|
|
43128
|
+
if (intervalMs <= 0) return () => {
|
|
43129
|
+
};
|
|
43130
|
+
const now = opts?.now ?? Date.now;
|
|
43131
|
+
const started = now();
|
|
43132
|
+
const timer = setInterval(() => {
|
|
43133
|
+
onBeat(tentacleHeartbeatCaption(now() - started));
|
|
43134
|
+
}, intervalMs);
|
|
43135
|
+
timer.unref?.();
|
|
43136
|
+
return () => clearInterval(timer);
|
|
43137
|
+
}
|
|
43138
|
+
var TENTACLE_HEARTBEAT_DEFAULT_MS;
|
|
43139
|
+
var init_tentacleHeartbeat = __esm({
|
|
43140
|
+
"src/cli/tools/tentacleHeartbeat.ts"() {
|
|
43141
|
+
"use strict";
|
|
43142
|
+
TENTACLE_HEARTBEAT_DEFAULT_MS = 15e3;
|
|
43143
|
+
}
|
|
43144
|
+
});
|
|
43145
|
+
|
|
43089
43146
|
// src/cli/kraken/tentacle.ts
|
|
43090
43147
|
var tentacle_exports = {};
|
|
43091
43148
|
__export(tentacle_exports, {
|
|
@@ -46875,36 +46932,54 @@ ${taskUserContent}`,
|
|
|
46875
46932
|
emitActivity({ type: "agent_tool", agentId: liveId, toolCallId: ev.toolCallId, tool: startedTools.get(ev.toolCallId) ?? "unknown", status: ev.isError ? "failed" : "completed", durationMs: ev.durationMs, ts: Date.now() });
|
|
46876
46933
|
}
|
|
46877
46934
|
};
|
|
46878
|
-
|
|
46879
|
-
|
|
46880
|
-
|
|
46935
|
+
const stopHeartbeat = startTentacleHeartbeat((caption) => {
|
|
46936
|
+
emitActivity({
|
|
46937
|
+
type: "agent_status",
|
|
46938
|
+
agentId: liveId,
|
|
46939
|
+
status: "running",
|
|
46940
|
+
message: caption,
|
|
46941
|
+
ts: Date.now()
|
|
46942
|
+
});
|
|
46881
46943
|
});
|
|
46882
|
-
|
|
46883
|
-
|
|
46884
|
-
|
|
46885
|
-
|
|
46886
|
-
|
|
46887
|
-
|
|
46888
|
-
|
|
46889
|
-
|
|
46890
|
-
|
|
46891
|
-
|
|
46892
|
-
|
|
46893
|
-
|
|
46894
|
-
|
|
46895
|
-
|
|
46896
|
-
|
|
46897
|
-
|
|
46898
|
-
|
|
46899
|
-
|
|
46900
|
-
|
|
46901
|
-
|
|
46902
|
-
|
|
46903
|
-
|
|
46904
|
-
|
|
46905
|
-
|
|
46944
|
+
let result;
|
|
46945
|
+
let error51;
|
|
46946
|
+
let aborted2;
|
|
46947
|
+
let usage;
|
|
46948
|
+
let toolTrace;
|
|
46949
|
+
try {
|
|
46950
|
+
({ result, error: error51, aborted: aborted2, usage, toolTrace } = await runSubAgent(harness, {
|
|
46951
|
+
...opts.signal ? { signal: opts.signal } : {},
|
|
46952
|
+
onEvent: onHarnessEvent
|
|
46953
|
+
}));
|
|
46954
|
+
if (!aborted2 && !result && sub.fallback && sub.fallback.model !== sub.model) {
|
|
46955
|
+
const { isUnknownModelError: isUnknownModelError2 } = await Promise.resolve().then(() => (init_krakenModel(), krakenModel_exports));
|
|
46956
|
+
if (isUnknownModelError2(error51)) {
|
|
46957
|
+
emitPhase(`model ${sub.model} unavailable \u2014 retrying with ${sub.fallback.model}`);
|
|
46958
|
+
const retryConfig = {
|
|
46959
|
+
...config2,
|
|
46960
|
+
model: sub.fallback.model,
|
|
46961
|
+
provider: sub.fallback.provider,
|
|
46962
|
+
providerStream: sub.fallback.providerStream
|
|
46963
|
+
};
|
|
46964
|
+
try {
|
|
46965
|
+
harness = deps.harnessFactory ? deps.harnessFactory(retryConfig) : new (await Promise.resolve().then(() => (init_harness(), harness_exports))).AgentHarness(retryConfig);
|
|
46966
|
+
const retry = await runSubAgent(harness, {
|
|
46967
|
+
...opts.signal ? { signal: opts.signal } : {},
|
|
46968
|
+
onEvent: onHarnessEvent
|
|
46969
|
+
});
|
|
46970
|
+
result = retry.result;
|
|
46971
|
+
error51 = retry.error;
|
|
46972
|
+
aborted2 = retry.aborted;
|
|
46973
|
+
usage = retry.usage;
|
|
46974
|
+
toolTrace = retry.toolTrace;
|
|
46975
|
+
sub = { ...sub, model: sub.fallback.model, provider: sub.fallback.provider };
|
|
46976
|
+
} catch (err) {
|
|
46977
|
+
error51 = err instanceof Error ? err.message : String(err);
|
|
46978
|
+
}
|
|
46906
46979
|
}
|
|
46907
46980
|
}
|
|
46981
|
+
} finally {
|
|
46982
|
+
stopHeartbeat();
|
|
46908
46983
|
}
|
|
46909
46984
|
const durationMs = Date.now() - started;
|
|
46910
46985
|
if (aborted2) {
|
|
@@ -46914,7 +46989,7 @@ ${taskUserContent}`,
|
|
|
46914
46989
|
agent,
|
|
46915
46990
|
thoroughness,
|
|
46916
46991
|
description: args.description,
|
|
46917
|
-
detail: "cancelled
|
|
46992
|
+
detail: "cancelled by parent",
|
|
46918
46993
|
model: sub.model,
|
|
46919
46994
|
worktree: worktree?.path ?? null,
|
|
46920
46995
|
durationMs,
|
|
@@ -46926,7 +47001,7 @@ ${taskUserContent}`,
|
|
|
46926
47001
|
detail: "cancelled",
|
|
46927
47002
|
durationMs
|
|
46928
47003
|
});
|
|
46929
|
-
return { ok: false, agent, error: "task: sub-agent cancelled
|
|
47004
|
+
return { ok: false, agent, error: "task: sub-agent cancelled by parent", cancelled: true };
|
|
46930
47005
|
}
|
|
46931
47006
|
if (!result) {
|
|
46932
47007
|
if (worktree) await cleanupKrakenWorktree(worktree);
|
|
@@ -47204,6 +47279,7 @@ var init_taskTool = __esm({
|
|
|
47204
47279
|
init_verifyReport();
|
|
47205
47280
|
init_metrics2();
|
|
47206
47281
|
init_dist();
|
|
47282
|
+
init_tentacleHeartbeat();
|
|
47207
47283
|
TASK_TOOL_TIMEOUT_MS = 27e5;
|
|
47208
47284
|
EXPLORE_PROMPT = [
|
|
47209
47285
|
"You are a focused EXPLORE tentacle of Kraken (parent super-agent).",
|
|
@@ -48283,6 +48359,7 @@ function frozenProfile(input) {
|
|
|
48283
48359
|
Object.freeze(input.buildRecovery);
|
|
48284
48360
|
Object.freeze(input.sampling);
|
|
48285
48361
|
Object.freeze(input.compaction);
|
|
48362
|
+
if (input.stream) Object.freeze(input.stream);
|
|
48286
48363
|
return Object.freeze(input);
|
|
48287
48364
|
}
|
|
48288
48365
|
function resolveHarnessProfile(model, providerId) {
|
|
@@ -48355,6 +48432,18 @@ var init_capabilities = __esm({
|
|
|
48355
48432
|
buildRecovery: { forceToolChoice: true, maxForcedTurns: 1 },
|
|
48356
48433
|
sampling: { temperature: 0.7 },
|
|
48357
48434
|
compaction: { ...SHARED_COMPACTION },
|
|
48435
|
+
// Copied from Grok Build (`~/.grok/config.toml` + user-guide):
|
|
48436
|
+
// inference_idle_timeout_secs = 600
|
|
48437
|
+
// max_retries = 8
|
|
48438
|
+
// xAI SDK timeout = 3600s on reasoning models
|
|
48439
|
+
// grok-4.6 xhigh reasons with hidden tokens + SSE keep-alives and no
|
|
48440
|
+
// content for minutes; a 5-min useful-token idle kills BUILD.
|
|
48441
|
+
stream: {
|
|
48442
|
+
idleMs: 6e5,
|
|
48443
|
+
firstTokenIdleMs: 6e5,
|
|
48444
|
+
maxMs: 36e5,
|
|
48445
|
+
maxRetries: 8
|
|
48446
|
+
},
|
|
48358
48447
|
profile: "grok"
|
|
48359
48448
|
});
|
|
48360
48449
|
MINIMAX_M3_CAPS = frozenProfile({
|
|
@@ -50979,18 +51068,40 @@ var openai_compatible_exports = {};
|
|
|
50979
51068
|
__export(openai_compatible_exports, {
|
|
50980
51069
|
PROVIDER_CONNECT_TIMEOUT_MS: () => PROVIDER_CONNECT_TIMEOUT_MS,
|
|
50981
51070
|
PROVIDER_ENDPOINTS: () => PROVIDER_ENDPOINTS,
|
|
51071
|
+
PROVIDER_FIRST_TOKEN_IDLE_MS: () => PROVIDER_FIRST_TOKEN_IDLE_MS,
|
|
50982
51072
|
PROVIDER_STREAM_IDLE_MS: () => PROVIDER_STREAM_IDLE_MS,
|
|
50983
51073
|
PROVIDER_STREAM_MAX_MS: () => PROVIDER_STREAM_MAX_MS,
|
|
50984
51074
|
dataUriFromImage: () => dataUriFromImage,
|
|
51075
|
+
glmModelLooksVision: () => glmModelLooksVision,
|
|
51076
|
+
isGlmCodingEndpoint: () => isGlmCodingEndpoint,
|
|
51077
|
+
isTextOnlyContentRejection: () => isTextOnlyContentRejection,
|
|
50985
51078
|
modelSupportsVision: () => modelSupportsVision,
|
|
50986
51079
|
openaiCompatibleProvider: () => openaiCompatibleProvider,
|
|
50987
51080
|
parseCachedPromptTokens: () => parseCachedPromptTokens,
|
|
50988
51081
|
providerConfigFor: () => providerConfigFor,
|
|
50989
51082
|
providerFromEnv: () => providerFromEnv,
|
|
50990
51083
|
readChunkWithTimeout: () => readChunkWithTimeout,
|
|
51084
|
+
resetTextOnlyVisionMemory: () => resetTextOnlyVisionMemory,
|
|
50991
51085
|
resolveActiveProvider: () => resolveActiveProvider2,
|
|
50992
|
-
resolveBaseUrl: () => resolveBaseUrl
|
|
50993
|
-
|
|
51086
|
+
resolveBaseUrl: () => resolveBaseUrl,
|
|
51087
|
+
resolveStreamTimeouts: () => resolveStreamTimeouts
|
|
51088
|
+
});
|
|
51089
|
+
function resolveStreamTimeouts(capabilities) {
|
|
51090
|
+
const envIdle = process.env.ZELARI_PROVIDER_STREAM_IDLE_MS ?? process.env.ZELARI_PROVIDER_TIMEOUT_MS;
|
|
51091
|
+
const envFirst = process.env.ZELARI_PROVIDER_FIRST_TOKEN_IDLE_MS;
|
|
51092
|
+
const envMax = process.env.ZELARI_PROVIDER_STREAM_MAX_MS;
|
|
51093
|
+
const envRetries = process.env.ZELARI_PROVIDER_MAX_RETRIES;
|
|
51094
|
+
const idleMs = envIdle ? PROVIDER_STREAM_IDLE_MS : capabilities.stream?.idleMs ?? PROVIDER_STREAM_IDLE_MS;
|
|
51095
|
+
const firstRaw = envFirst ? PROVIDER_FIRST_TOKEN_IDLE_MS : capabilities.stream?.firstTokenIdleMs ?? PROVIDER_FIRST_TOKEN_IDLE_MS;
|
|
51096
|
+
const maxMs = envMax ? PROVIDER_STREAM_MAX_MS : capabilities.stream?.maxMs ?? PROVIDER_STREAM_MAX_MS;
|
|
51097
|
+
const maxRetries = envRetries ? MAX_RETRIES : capabilities.stream?.maxRetries ?? MAX_RETRIES;
|
|
51098
|
+
return {
|
|
51099
|
+
idleMs,
|
|
51100
|
+
firstTokenIdleMs: Math.max(firstRaw, idleMs),
|
|
51101
|
+
maxMs,
|
|
51102
|
+
maxRetries
|
|
51103
|
+
};
|
|
51104
|
+
}
|
|
50994
51105
|
function abortableSleep(ms, signal) {
|
|
50995
51106
|
return new Promise((resolve9) => {
|
|
50996
51107
|
if (signal?.aborted) return resolve9();
|
|
@@ -51009,6 +51120,12 @@ function isTimeoutAbortMessage(msg) {
|
|
|
51009
51120
|
const m = msg.toLowerCase();
|
|
51010
51121
|
return m.includes("aborted due to timeout") || m.includes("timeout") || m.includes("the operation was aborted");
|
|
51011
51122
|
}
|
|
51123
|
+
function formatStreamIdleError(elapsedMs, budgetMs, kind2) {
|
|
51124
|
+
const elapsedS = Math.max(1, Math.round(elapsedMs / 1e3));
|
|
51125
|
+
const budgetS = Math.max(1, Math.round(budgetMs / 1e3));
|
|
51126
|
+
const why = kind2 === "keep-alive" ? "no content tokens \u2014 keep-alive frames don't count" : "no tokens";
|
|
51127
|
+
return `Provider stream idle for ${elapsedS}s of ${budgetS}s (${why}). The model/gateway stalled \u2014 try again or switch model. Override with ZELARI_PROVIDER_STREAM_IDLE_MS or ZELARI_PROVIDER_FIRST_TOKEN_IDLE_MS.`;
|
|
51128
|
+
}
|
|
51012
51129
|
async function readChunkWithTimeout(reader, opts) {
|
|
51013
51130
|
if (opts.signal?.aborted) {
|
|
51014
51131
|
throw new Error("aborted");
|
|
@@ -51022,9 +51139,7 @@ async function readChunkWithTimeout(reader, opts) {
|
|
|
51022
51139
|
}
|
|
51023
51140
|
const idleElapsed = now - opts.lastUsefulAt();
|
|
51024
51141
|
if (idleElapsed >= opts.idleMs) {
|
|
51025
|
-
throw new Error(
|
|
51026
|
-
`Provider stream idle for ${Math.round(idleElapsed / 1e3)}s (no content tokens \u2014 keep-alive frames don't count). The model/gateway stalled \u2014 try again or switch model. Override with ZELARI_PROVIDER_STREAM_IDLE_MS.`
|
|
51027
|
-
);
|
|
51142
|
+
throw new Error(formatStreamIdleError(idleElapsed, opts.idleMs, "keep-alive"));
|
|
51028
51143
|
}
|
|
51029
51144
|
const waitMs = Math.min(opts.idleMs - idleElapsed, remaining);
|
|
51030
51145
|
let idleTimer;
|
|
@@ -51034,11 +51149,8 @@ async function readChunkWithTimeout(reader, opts) {
|
|
|
51034
51149
|
reader.read(),
|
|
51035
51150
|
new Promise((_, reject) => {
|
|
51036
51151
|
idleTimer = setTimeout(() => {
|
|
51037
|
-
|
|
51038
|
-
|
|
51039
|
-
`Provider stream idle for ${Math.round(waitMs / 1e3)}s (no tokens). The model/gateway stalled \u2014 try again or switch model. Override with ZELARI_PROVIDER_STREAM_IDLE_MS.`
|
|
51040
|
-
)
|
|
51041
|
-
);
|
|
51152
|
+
const elapsed = Date.now() - opts.lastUsefulAt();
|
|
51153
|
+
reject(new Error(formatStreamIdleError(elapsed, opts.idleMs, "silence")));
|
|
51042
51154
|
}, waitMs);
|
|
51043
51155
|
if (opts.signal) {
|
|
51044
51156
|
onAbort = () => reject(new Error("aborted"));
|
|
@@ -51060,9 +51172,52 @@ function backoffDelay(attempt, retryAfterHeader) {
|
|
|
51060
51172
|
}
|
|
51061
51173
|
return Math.min(BACKOFF_BASE_MS * 2 ** attempt, BACKOFF_CAP_MS);
|
|
51062
51174
|
}
|
|
51063
|
-
function
|
|
51064
|
-
|
|
51065
|
-
|
|
51175
|
+
function visionMemoryKey(model, probe) {
|
|
51176
|
+
return `${probe?.providerId ?? ""}::${model}::${probe?.baseUrl ?? ""}`;
|
|
51177
|
+
}
|
|
51178
|
+
function resetTextOnlyVisionMemory() {
|
|
51179
|
+
textOnlyVisionMemory.clear();
|
|
51180
|
+
}
|
|
51181
|
+
function isGlmCodingEndpoint(baseUrl) {
|
|
51182
|
+
if (!baseUrl) return false;
|
|
51183
|
+
return /\/coding(\/|$)/i.test(baseUrl);
|
|
51184
|
+
}
|
|
51185
|
+
function glmModelLooksVision(model) {
|
|
51186
|
+
const n = model.trim().toLowerCase();
|
|
51187
|
+
if (!n) return false;
|
|
51188
|
+
if (/(?:^|[-_/.])(?:vl|vision)(?:[-_/.]|$)/.test(n)) return true;
|
|
51189
|
+
return /^glm[-_.]?[\w.]*v(?:-|$)/.test(n);
|
|
51190
|
+
}
|
|
51191
|
+
function glmModelIsTextOnly(model) {
|
|
51192
|
+
const n = model.trim().toLowerCase();
|
|
51193
|
+
if (!n.startsWith("glm")) return false;
|
|
51194
|
+
return !glmModelLooksVision(n);
|
|
51195
|
+
}
|
|
51196
|
+
function isTextOnlyContentRejection(status, body) {
|
|
51197
|
+
if (status !== 400) return false;
|
|
51198
|
+
if (!/messages\.content\.type/i.test(body)) return false;
|
|
51199
|
+
return /allowed values/i.test(body) || /\[\s*['"]text['"]\s*\]/.test(body) || /取值范围/.test(body) || /is invalid/i.test(body);
|
|
51200
|
+
}
|
|
51201
|
+
function messagesHaveImageUrl(messages) {
|
|
51202
|
+
for (const m of messages) {
|
|
51203
|
+
const content = m.content;
|
|
51204
|
+
if (!Array.isArray(content)) continue;
|
|
51205
|
+
for (const part of content) {
|
|
51206
|
+
if (part && typeof part === "object" && part.type === "image_url") {
|
|
51207
|
+
return true;
|
|
51208
|
+
}
|
|
51209
|
+
}
|
|
51210
|
+
}
|
|
51211
|
+
return false;
|
|
51212
|
+
}
|
|
51213
|
+
function modelSupportsVision(model, probe) {
|
|
51214
|
+
const force = (process.env.ZELARI_VISION ?? "").trim().toLowerCase();
|
|
51215
|
+
if (force === "0" || force === "false" || force === "off") return false;
|
|
51216
|
+
if (force === "1" || force === "true" || force === "on") return true;
|
|
51217
|
+
if (textOnlyVisionMemory.has(visionMemoryKey(model, probe))) return false;
|
|
51218
|
+
if (isGlmCodingEndpoint(probe?.baseUrl)) return false;
|
|
51219
|
+
if (glmModelIsTextOnly(model)) return false;
|
|
51220
|
+
return true;
|
|
51066
51221
|
}
|
|
51067
51222
|
function dataUriFromImage(img) {
|
|
51068
51223
|
return `data:${img.mime};base64,${img.dataBase64}`;
|
|
@@ -51182,29 +51337,38 @@ function positiveEnvInt(name) {
|
|
|
51182
51337
|
function openaiCompatibleProvider(config2) {
|
|
51183
51338
|
return async function* (params) {
|
|
51184
51339
|
const capabilities = capabilitiesFor(params.model, config2.providerId);
|
|
51185
|
-
const
|
|
51340
|
+
const streamTimeouts = resolveStreamTimeouts(capabilities);
|
|
51341
|
+
const visionProbe = {
|
|
51342
|
+
providerId: config2.providerId,
|
|
51343
|
+
baseUrl: config2.baseUrl
|
|
51344
|
+
};
|
|
51345
|
+
let vision = modelSupportsVision(params.model, visionProbe);
|
|
51186
51346
|
const msgsIn = params.messages;
|
|
51187
|
-
|
|
51188
|
-
|
|
51189
|
-
|
|
51190
|
-
|
|
51191
|
-
|
|
51192
|
-
|
|
51193
|
-
|
|
51194
|
-
|
|
51195
|
-
|
|
51196
|
-
|
|
51197
|
-
if (m.
|
|
51198
|
-
|
|
51199
|
-
|
|
51200
|
-
|
|
51201
|
-
|
|
51202
|
-
|
|
51203
|
-
|
|
51204
|
-
|
|
51205
|
-
|
|
51206
|
-
|
|
51207
|
-
|
|
51347
|
+
const wireMessages = (visionFlag) => {
|
|
51348
|
+
let toolRunImages = [];
|
|
51349
|
+
return msgsIn.flatMap((m, i) => {
|
|
51350
|
+
const cacheable = visionFlag && !(m.role === "user" && m.images && m.images.length > 0);
|
|
51351
|
+
if (cacheable) {
|
|
51352
|
+
const cached2 = messageMappingCache.get(m);
|
|
51353
|
+
if (cached2) return [cached2];
|
|
51354
|
+
}
|
|
51355
|
+
const mapped = mapAgentMessage(m, visionFlag);
|
|
51356
|
+
if (cacheable) messageMappingCache.set(m, mapped);
|
|
51357
|
+
if (m.role === "tool") {
|
|
51358
|
+
if (m.images && m.images.length > 0) toolRunImages.push(...m.images);
|
|
51359
|
+
const next = msgsIn[i + 1];
|
|
51360
|
+
const runEnds = !next || next.role !== "tool";
|
|
51361
|
+
if (runEnds && visionFlag && toolRunImages.length > 0) {
|
|
51362
|
+
const followUp = imagesFollowUpMessage(toolRunImages);
|
|
51363
|
+
toolRunImages = [];
|
|
51364
|
+
return [mapped, followUp];
|
|
51365
|
+
}
|
|
51366
|
+
if (runEnds) toolRunImages = [];
|
|
51367
|
+
}
|
|
51368
|
+
return [mapped];
|
|
51369
|
+
});
|
|
51370
|
+
};
|
|
51371
|
+
let messages = wireMessages(vision);
|
|
51208
51372
|
const generation = params.generation;
|
|
51209
51373
|
const body = {
|
|
51210
51374
|
// Use `params.model` (per-call override from AgentHarness, e.g. for
|
|
@@ -51254,6 +51418,7 @@ function openaiCompatibleProvider(config2) {
|
|
|
51254
51418
|
const recoveryAttempt = generation?.recoveryAttempt ?? 1;
|
|
51255
51419
|
const forceRecoveryTool = generation?.toolChoice === "required" && capabilities.buildRecovery.forceToolChoice && recoveryAttempt <= capabilities.buildRecovery.maxForcedTurns;
|
|
51256
51420
|
body.tool_choice = forceRecoveryTool ? "required" : "auto";
|
|
51421
|
+
if (config2.providerId === "glm") body.tool_stream = true;
|
|
51257
51422
|
}
|
|
51258
51423
|
const headers3 = {
|
|
51259
51424
|
"Content-Type": "application/json",
|
|
@@ -51268,7 +51433,7 @@ function openaiCompatibleProvider(config2) {
|
|
|
51268
51433
|
let response;
|
|
51269
51434
|
let lastErrText = "";
|
|
51270
51435
|
let lastStatus = 0;
|
|
51271
|
-
for (let attempt = 0; attempt <=
|
|
51436
|
+
for (let attempt = 0; attempt <= streamTimeouts.maxRetries; attempt += 1) {
|
|
51272
51437
|
if (params.signal?.aborted) {
|
|
51273
51438
|
yield { kind: "error", message: "aborted" };
|
|
51274
51439
|
return;
|
|
@@ -51309,7 +51474,7 @@ function openaiCompatibleProvider(config2) {
|
|
|
51309
51474
|
yield { kind: "error", message: "aborted" };
|
|
51310
51475
|
return;
|
|
51311
51476
|
}
|
|
51312
|
-
const maxAttempts = isTimeoutAbortMessage(lastErrText) ? Math.min(1,
|
|
51477
|
+
const maxAttempts = isTimeoutAbortMessage(lastErrText) ? Math.min(1, streamTimeouts.maxRetries) : streamTimeouts.maxRetries;
|
|
51313
51478
|
if (attempt < maxAttempts) {
|
|
51314
51479
|
await abortableSleep(backoffDelay(attempt, null), params.signal);
|
|
51315
51480
|
continue;
|
|
@@ -51320,7 +51485,14 @@ function openaiCompatibleProvider(config2) {
|
|
|
51320
51485
|
if (response.ok && response.body) break;
|
|
51321
51486
|
lastStatus = response.status;
|
|
51322
51487
|
lastErrText = await response.text().catch(() => "");
|
|
51323
|
-
if (
|
|
51488
|
+
if (vision && isTextOnlyContentRejection(lastStatus, lastErrText) && messagesHaveImageUrl(messages)) {
|
|
51489
|
+
textOnlyVisionMemory.add(visionMemoryKey(params.model, visionProbe));
|
|
51490
|
+
vision = false;
|
|
51491
|
+
messages = wireMessages(false);
|
|
51492
|
+
body.messages = messages;
|
|
51493
|
+
continue;
|
|
51494
|
+
}
|
|
51495
|
+
if (!RETRYABLE_STATUSES.has(response.status) || attempt >= streamTimeouts.maxRetries) break;
|
|
51324
51496
|
const retryAfter = response.headers.get("retry-after");
|
|
51325
51497
|
await abortableSleep(backoffDelay(attempt, retryAfter), params.signal);
|
|
51326
51498
|
}
|
|
@@ -51366,17 +51538,19 @@ function openaiCompatibleProvider(config2) {
|
|
|
51366
51538
|
}
|
|
51367
51539
|
toolCallAccumulator.clear();
|
|
51368
51540
|
};
|
|
51369
|
-
const streamDeadline = Date.now() +
|
|
51541
|
+
const streamDeadline = Date.now() + streamTimeouts.maxMs;
|
|
51370
51542
|
let lastUsefulAt = Date.now();
|
|
51543
|
+
let emittedUseful = false;
|
|
51371
51544
|
const markUseful = () => {
|
|
51372
51545
|
lastUsefulAt = Date.now();
|
|
51546
|
+
emittedUseful = true;
|
|
51373
51547
|
};
|
|
51374
51548
|
try {
|
|
51375
51549
|
while (true) {
|
|
51376
51550
|
let chunk;
|
|
51377
51551
|
try {
|
|
51378
51552
|
chunk = await readChunkWithTimeout(reader, {
|
|
51379
|
-
idleMs:
|
|
51553
|
+
idleMs: emittedUseful ? streamTimeouts.idleMs : streamTimeouts.firstTokenIdleMs,
|
|
51380
51554
|
deadlineMs: streamDeadline,
|
|
51381
51555
|
signal: params.signal,
|
|
51382
51556
|
lastUsefulAt: () => lastUsefulAt
|
|
@@ -51431,17 +51605,33 @@ function openaiCompatibleProvider(config2) {
|
|
|
51431
51605
|
}
|
|
51432
51606
|
};
|
|
51433
51607
|
}
|
|
51434
|
-
|
|
51608
|
+
const content = delta?.content;
|
|
51609
|
+
if (typeof content === "string" && content.length > 0) {
|
|
51435
51610
|
markUseful();
|
|
51436
|
-
yield { kind: "text", delta:
|
|
51611
|
+
yield { kind: "text", delta: content };
|
|
51612
|
+
} else if (Array.isArray(content)) {
|
|
51613
|
+
for (const part of content) {
|
|
51614
|
+
if (!part || typeof part !== "object") continue;
|
|
51615
|
+
const p3 = part;
|
|
51616
|
+
const text = typeof p3.text === "string" ? p3.text : "";
|
|
51617
|
+
if (!text) continue;
|
|
51618
|
+
markUseful();
|
|
51619
|
+
const partType = typeof p3.type === "string" ? p3.type : "text";
|
|
51620
|
+
yield {
|
|
51621
|
+
kind: partType === "reasoning" || partType === "thinking" ? "thinking" : "text",
|
|
51622
|
+
delta: text
|
|
51623
|
+
};
|
|
51624
|
+
}
|
|
51437
51625
|
}
|
|
51438
|
-
const
|
|
51439
|
-
|
|
51626
|
+
const reasoningRaw = delta?.reasoning_content ?? delta?.reasoning ?? delta?.thinking;
|
|
51627
|
+
const reasoning = typeof reasoningRaw === "string" ? reasoningRaw : reasoningRaw && typeof reasoningRaw === "object" && typeof reasoningRaw.text === "string" ? reasoningRaw.text : reasoningRaw && typeof reasoningRaw === "object" && typeof reasoningRaw.content === "string" ? reasoningRaw.content : "";
|
|
51628
|
+
if (reasoning.length > 0) {
|
|
51440
51629
|
markUseful();
|
|
51441
51630
|
yield { kind: "thinking", delta: reasoning };
|
|
51442
51631
|
}
|
|
51443
51632
|
const details = delta?.reasoning_details;
|
|
51444
|
-
if (Array.isArray(details)) {
|
|
51633
|
+
if (Array.isArray(details) && details.length > 0) {
|
|
51634
|
+
markUseful();
|
|
51445
51635
|
for (const d of details) {
|
|
51446
51636
|
if (!d || typeof d !== "object") continue;
|
|
51447
51637
|
const t = d.text;
|
|
@@ -51461,6 +51651,7 @@ function openaiCompatibleProvider(config2) {
|
|
|
51461
51651
|
}
|
|
51462
51652
|
}
|
|
51463
51653
|
if (Array.isArray(delta?.tool_calls)) {
|
|
51654
|
+
markUseful();
|
|
51464
51655
|
for (const tc of delta.tool_calls) {
|
|
51465
51656
|
const idx = tc.index ?? 0;
|
|
51466
51657
|
const existing = toolCallAccumulator.get(idx) ?? {
|
|
@@ -51527,7 +51718,7 @@ async function providerConfigFor(providerId) {
|
|
|
51527
51718
|
...extraFromStored(providerId)
|
|
51528
51719
|
};
|
|
51529
51720
|
}
|
|
51530
|
-
var RETRYABLE_STATUSES, MAX_RETRIES, BACKOFF_BASE_MS, BACKOFF_CAP_MS, PROVIDER_CONNECT_TIMEOUT_MS, PROVIDER_STREAM_IDLE_MS, PROVIDER_STREAM_MAX_MS, PROVIDER_ENDPOINTS, messageMappingCache;
|
|
51721
|
+
var RETRYABLE_STATUSES, MAX_RETRIES, BACKOFF_BASE_MS, BACKOFF_CAP_MS, PROVIDER_CONNECT_TIMEOUT_MS, PROVIDER_STREAM_IDLE_MS, PROVIDER_FIRST_TOKEN_IDLE_MS, PROVIDER_STREAM_MAX_MS, textOnlyVisionMemory, PROVIDER_ENDPOINTS, messageMappingCache;
|
|
51531
51722
|
var init_openai_compatible = __esm({
|
|
51532
51723
|
"src/cli/provider/openai-compatible.ts"() {
|
|
51533
51724
|
"use strict";
|
|
@@ -51553,11 +51744,17 @@ var init_openai_compatible = __esm({
|
|
|
51553
51744
|
const n = raw ? Number.parseInt(raw, 10) : 3e5;
|
|
51554
51745
|
return Number.isFinite(n) && n >= 15e3 ? n : 3e5;
|
|
51555
51746
|
})();
|
|
51747
|
+
PROVIDER_FIRST_TOKEN_IDLE_MS = (() => {
|
|
51748
|
+
const raw = process.env.ZELARI_PROVIDER_FIRST_TOKEN_IDLE_MS;
|
|
51749
|
+
const n = raw ? Number.parseInt(raw, 10) : 6e5;
|
|
51750
|
+
return Number.isFinite(n) && n >= 15e3 ? n : 6e5;
|
|
51751
|
+
})();
|
|
51556
51752
|
PROVIDER_STREAM_MAX_MS = (() => {
|
|
51557
51753
|
const raw = process.env.ZELARI_PROVIDER_STREAM_MAX_MS;
|
|
51558
51754
|
const n = raw ? Number.parseInt(raw, 10) : 18e5;
|
|
51559
51755
|
return Number.isFinite(n) && n >= 6e4 ? n : 18e5;
|
|
51560
51756
|
})();
|
|
51757
|
+
textOnlyVisionMemory = /* @__PURE__ */ new Set();
|
|
51561
51758
|
PROVIDER_ENDPOINTS = {
|
|
51562
51759
|
"openai-compatible": "https://api.x.ai/v1",
|
|
51563
51760
|
"minimax": "https://api.minimax.io/v1",
|
|
@@ -67393,7 +67590,7 @@ var init_controlBridge = __esm({
|
|
|
67393
67590
|
function attachHeadlessLiveCancel(opts) {
|
|
67394
67591
|
const abort = new AbortController();
|
|
67395
67592
|
const controlQueue = new RuntimeControlQueue();
|
|
67396
|
-
const cancel = () => {
|
|
67593
|
+
const cancel = (_reason) => {
|
|
67397
67594
|
if (!abort.signal.aborted) abort.abort();
|
|
67398
67595
|
return true;
|
|
67399
67596
|
};
|
|
@@ -67951,10 +68148,10 @@ async function runOneTurn(opts, provider, model, providerStream, extras) {
|
|
|
67951
68148
|
})() : void 0;
|
|
67952
68149
|
const unregisterLiveTurnControl = process.env.ZELARI_SERVE_HARNESS === "1" ? registerLiveTurnControl({
|
|
67953
68150
|
queue: controlQueue,
|
|
67954
|
-
cancel: () => {
|
|
68151
|
+
cancel: (reason) => {
|
|
67955
68152
|
const cancelHook = harnessHolder.cancel;
|
|
67956
68153
|
if (!cancelHook) return false;
|
|
67957
|
-
cancelHook();
|
|
68154
|
+
cancelHook(reason);
|
|
67958
68155
|
return true;
|
|
67959
68156
|
}
|
|
67960
68157
|
}) : void 0;
|
|
@@ -68240,7 +68437,7 @@ async function runOneTurn(opts, provider, model, providerStream, extras) {
|
|
|
68240
68437
|
memoryContextChars: 2e3
|
|
68241
68438
|
} : {}
|
|
68242
68439
|
});
|
|
68243
|
-
harnessHolder.cancel = () => harness.cancel();
|
|
68440
|
+
harnessHolder.cancel = (reason) => harness.cancel(reason);
|
|
68244
68441
|
const readBuildProgress = () => {
|
|
68245
68442
|
const getter = harness.getBuildProgress;
|
|
68246
68443
|
return typeof getter === "function" ? getter.call(harness) : { mutationsAttempted: 0, mutationsSucceeded: 0 };
|
|
@@ -68427,11 +68624,11 @@ async function runOneTurn(opts, provider, model, providerStream, extras) {
|
|
|
68427
68624
|
process.stdout.write(pass.textBuffer.join(""));
|
|
68428
68625
|
}
|
|
68429
68626
|
process.stdout.write("");
|
|
68430
|
-
if (pass.finalReason !== "error" && opts.output === "json" && wantWrites && pass.successfulWrites === 0) {
|
|
68627
|
+
if (pass.finalReason !== "error" && pass.finalReason !== "cancelled" && opts.output === "json" && wantWrites && pass.successfulWrites === 0) {
|
|
68431
68628
|
emitEvent({ type: "log", message: "[headless] BUILD failed: zero successful mutations after liveness recovery" });
|
|
68432
68629
|
}
|
|
68433
68630
|
try {
|
|
68434
|
-
const closeStatus = pass.finalReason === "error" ? "error" : strictExit !== 0 ? "stopped" : "completed";
|
|
68631
|
+
const closeStatus = pass.finalReason === "error" ? "error" : pass.finalReason === "cancelled" ? "cancelled" : strictExit !== 0 ? "stopped" : "completed";
|
|
68435
68632
|
await spine.close(closeStatus);
|
|
68436
68633
|
} catch {
|
|
68437
68634
|
}
|
|
@@ -72765,7 +72962,8 @@ function startHarnessServer(options = {}) {
|
|
|
72765
72962
|
};
|
|
72766
72963
|
}
|
|
72767
72964
|
if (isCancel) {
|
|
72768
|
-
const
|
|
72965
|
+
const reason = typeof params.reason === "string" && params.reason.trim().length > 0 ? params.reason.trim() : void 0;
|
|
72966
|
+
const delivered = live.cancel(reason);
|
|
72769
72967
|
if (delivered) {
|
|
72770
72968
|
write(JSON.stringify(controlAppliedEvent(controlId, "cancel", "cancel")));
|
|
72771
72969
|
}
|