pikiloom 0.4.35 → 0.4.37
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/dashboard/dist/assets/AgentTab-DbFzaIyZ.js +1 -0
- package/dashboard/dist/assets/{ConnectionModal-DI0f63Bc.js → ConnectionModal-QNYOWHCU.js} +1 -1
- package/dashboard/dist/assets/{DirBrowser-CzkwSAi6.js → DirBrowser-BGaKHjFT.js} +1 -1
- package/dashboard/dist/assets/{ExtensionsTab-BdK8ylOy.js → ExtensionsTab-D4Gv9b8z.js} +1 -1
- package/dashboard/dist/assets/{IMAccessTab-BdkwcX7w.js → IMAccessTab-DxxoZd84.js} +1 -1
- package/dashboard/dist/assets/{Modal-CaNqa2E7.js → Modal-Nm2e93s5.js} +1 -1
- package/dashboard/dist/assets/{Modals-2DgJPKGI.js → Modals-BwxZmU0U.js} +1 -1
- package/dashboard/dist/assets/{Select-izl0nVH0.js → Select-SGlII0yx.js} +1 -1
- package/dashboard/dist/assets/SessionPanel-DYfSlreh.js +1 -0
- package/dashboard/dist/assets/{SystemTab-DKuQIo87.js → SystemTab-BF6lIAYM.js} +1 -1
- package/dashboard/dist/assets/index-BP8R_bLT.js +3 -0
- package/dashboard/dist/assets/index-CtS48Jn-.css +1 -0
- package/dashboard/dist/assets/index-DZiAiRNt.js +23 -0
- package/dashboard/dist/assets/{shared-CZ_jczjY.js → shared-S0kcs5yP.js} +1 -1
- package/dashboard/dist/index.html +2 -2
- package/dist/agent/artifacts.js +12 -24
- package/dist/agent/drivers/codex.js +20 -3
- package/dist/agent/index.js +2 -2
- package/dist/agent/stream.js +41 -1
- package/dist/bot/commands.js +5 -27
- package/dist/core/config/runtime-config.js +35 -0
- package/dist/core/constants.js +0 -1
- package/dist/dashboard/routes/agents.js +2 -1
- package/dist/dashboard/routes/sessions.js +11 -12
- package/dist/dashboard/runtime.js +11 -18
- package/package.json +1 -1
- package/dashboard/dist/assets/AgentTab-BFGZXRX9.js +0 -1
- package/dashboard/dist/assets/SessionPanel-BKde6FwO.js +0 -1
- package/dashboard/dist/assets/index-B4_qsasx.js +0 -3
- package/dashboard/dist/assets/index-CkriX56t.css +0 -1
- package/dashboard/dist/assets/index-CwfalaN2.js +0 -23
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
<link rel="icon" type="image/png" href="/logo.png">
|
|
7
7
|
<title>Pikiloom</title>
|
|
8
8
|
<link href="https://fonts.googleapis.com/css2?family=Geist:wght@400;500;600;700&family=Geist+Mono:wght@400;500&display=swap" rel="stylesheet">
|
|
9
|
-
<script type="module" crossorigin src="/assets/index-
|
|
9
|
+
<script type="module" crossorigin src="/assets/index-BP8R_bLT.js"></script>
|
|
10
10
|
<link rel="modulepreload" crossorigin href="/assets/react-vendor-C7Sl8SE7.js">
|
|
11
11
|
<link rel="modulepreload" crossorigin href="/assets/router-DHISdpPk.js">
|
|
12
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
12
|
+
<link rel="stylesheet" crossorigin href="/assets/index-CtS48Jn-.css">
|
|
13
13
|
</head>
|
|
14
14
|
<body>
|
|
15
15
|
<div id="root"></div>
|
package/dist/agent/artifacts.js
CHANGED
|
@@ -101,31 +101,19 @@ export function latestDeliveredTaskId(agent, sessionId) {
|
|
|
101
101
|
}
|
|
102
102
|
return latest?.taskId;
|
|
103
103
|
}
|
|
104
|
-
export function tailDeliveredBlocks(agent, sessionId,
|
|
105
|
-
//
|
|
106
|
-
//
|
|
107
|
-
//
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
: [];
|
|
113
|
-
}
|
|
114
|
-
// Idle / cold reload: the delivering turn is long over and task ids are not persisted
|
|
115
|
-
// per turn. Fall back to the most recent delivery, suppressed once the session has kept
|
|
116
|
-
// running well past it.
|
|
117
|
-
const latestTask = latestDeliveredTaskId(agent, sessionId);
|
|
118
|
-
const filter = latestTask ? (a) => a.taskId === latestTask : undefined;
|
|
119
|
-
const scoped = readDeliveredArtifacts(agent, sessionId).filter(a => !filter || filter(a));
|
|
120
|
-
if (!scoped.length)
|
|
121
|
-
return [];
|
|
122
|
-
let newestTs = scoped[0].ts;
|
|
123
|
-
for (const a of scoped)
|
|
124
|
-
if (a.ts > newestTs)
|
|
125
|
-
newestTs = a.ts;
|
|
126
|
-
if (opts.lastActivityMs != null && opts.lastActivityMs - newestTs > opts.staleAfterMs)
|
|
104
|
+
export function tailDeliveredBlocks(agent, sessionId, lastRunTaskId) {
|
|
105
|
+
// Surface only the files the LAST-RUN turn delivered, scoped to that turn's task id (the live
|
|
106
|
+
// stream snapshot's taskId, which persists after the turn ends). Keying on the last *run* task
|
|
107
|
+
// — not the last *delivering* task — is what stops the bleed: once any newer turn runs, even one
|
|
108
|
+
// that delivered nothing, the filter matches nothing and a prior turn's image stops re-appending
|
|
109
|
+
// onto the reply. With no known task (cold start / snapshot lost) show nothing rather than guess
|
|
110
|
+
// by an elapsed-time threshold, which is what previously let stale deliveries leak for an hour.
|
|
111
|
+
if (!lastRunTaskId)
|
|
127
112
|
return [];
|
|
128
|
-
|
|
113
|
+
const filter = (a) => a.taskId === lastRunTaskId;
|
|
114
|
+
return readDeliveredArtifacts(agent, sessionId).some(filter)
|
|
115
|
+
? deliveredArtifactBlocks(agent, sessionId, filter)
|
|
116
|
+
: [];
|
|
129
117
|
}
|
|
130
118
|
export function deliveredArtifactBlocks(agent, sessionId, filter) {
|
|
131
119
|
const blocks = [];
|
|
@@ -773,6 +773,23 @@ function createCodexStreamState(opts) {
|
|
|
773
773
|
generatingImages: 0,
|
|
774
774
|
};
|
|
775
775
|
}
|
|
776
|
+
// Codex on a ChatGPT-account login rejects non-OpenAI models with a raw upstream JSON blob
|
|
777
|
+
// (e.g. {"detail":"The 'deepseek-v4-pro' model is not supported when using Codex with a
|
|
778
|
+
// ChatGPT account."}). When a third-party model is correctly bound to a Provider profile the
|
|
779
|
+
// injector routes around this; this only fires when no binding exists, so turn the opaque
|
|
780
|
+
// upstream error into an actionable instruction instead of leaking JSON to the chat bubble.
|
|
781
|
+
export function humanizeCodexError(raw) {
|
|
782
|
+
if (!raw)
|
|
783
|
+
return null;
|
|
784
|
+
const match = raw.match(/The '([^']+)' model is not supported when using Codex with a ChatGPT account/i);
|
|
785
|
+
if (match) {
|
|
786
|
+
const model = match[1];
|
|
787
|
+
return `Codex 正在使用 ChatGPT 账号登录,无法运行第三方模型「${model}」。`
|
|
788
|
+
+ `请在「智能体配置」页接入该模型的供应商(填入 Base URL 与 API Key),添加对应模型档案并绑定到 Codex,`
|
|
789
|
+
+ `或改用 Codex 原生模型(如 gpt-5.5)。`;
|
|
790
|
+
}
|
|
791
|
+
return raw;
|
|
792
|
+
}
|
|
776
793
|
function codexErrorResult(error, start, sessionId, model, thinkingEffort) {
|
|
777
794
|
return {
|
|
778
795
|
ok: false, message: error, thinking: null,
|
|
@@ -1119,7 +1136,7 @@ export async function doCodexStream(opts) {
|
|
|
1119
1136
|
if (threadResp.error) {
|
|
1120
1137
|
const errMsg = threadResp.error.message || 'thread/start failed';
|
|
1121
1138
|
agentWarn(`[codex-rpc] thread error: ${errMsg}`);
|
|
1122
|
-
return codexErrorResult(errMsg, start, opts.sessionId, opts.model, opts.thinkingEffort);
|
|
1139
|
+
return codexErrorResult(humanizeCodexError(errMsg) ?? errMsg, start, opts.sessionId, opts.model, opts.thinkingEffort);
|
|
1123
1140
|
}
|
|
1124
1141
|
const threadResult = threadResp.result;
|
|
1125
1142
|
s.sessionId = threadResult.thread?.id ?? s.sessionId;
|
|
@@ -1210,7 +1227,7 @@ export async function doCodexStream(opts) {
|
|
|
1210
1227
|
unsubscribeRequests();
|
|
1211
1228
|
const errMsg = turnResp.error.message || 'turn/start failed';
|
|
1212
1229
|
agentWarn(`[codex-rpc] turn/start error: ${errMsg}`);
|
|
1213
|
-
return codexErrorResult(errMsg, start, s.sessionId, s.model, s.thinkingEffort);
|
|
1230
|
+
return codexErrorResult(humanizeCodexError(errMsg) ?? errMsg, start, s.sessionId, s.model, s.thinkingEffort);
|
|
1214
1231
|
}
|
|
1215
1232
|
s.turnId = turnResp.result?.turn?.id ?? null;
|
|
1216
1233
|
publishTurnControl();
|
|
@@ -1226,7 +1243,7 @@ export async function doCodexStream(opts) {
|
|
|
1226
1243
|
tryEmitCodexImageBlock(s, callId);
|
|
1227
1244
|
}
|
|
1228
1245
|
const ok = s.turnStatus === 'completed' && !timedOut && !interrupted;
|
|
1229
|
-
const error = s.turnError
|
|
1246
|
+
const error = humanizeCodexError(s.turnError)
|
|
1230
1247
|
|| (interrupted ? 'Interrupted by user.' : null)
|
|
1231
1248
|
|| (timedOut ? `Timed out after ${opts.timeout}s waiting for turn completion.` : null)
|
|
1232
1249
|
|| (!ok ? `Turn ${s.turnStatus || 'unknown'}.` : null);
|
package/dist/agent/index.js
CHANGED
|
@@ -7,7 +7,7 @@ export { attachAgentImage, attachInlineImage, materializeImage, rewriteAttachmen
|
|
|
7
7
|
export { deliverArtifact, readDeliveredArtifacts, deliveredArtifactBlocks, tailDeliveredBlocks, latestDeliveredTaskId, mimeForArtifact, } from './artifacts.js';
|
|
8
8
|
export { Q, agentLog, agentWarn, agentError, dedupeStrings, numberOrNull, normalizeStreamPreviewPlan, parseTodoWriteAsPlan, normalizeActivityLine, pushRecentActivity, detectClaudeApiError, isRetryableClaudeApiError, detectClaudeModelError, claudeModelErrorMessage, firstNonEmptyLine, shortValue, normalizeErrorMessage, joinErrorMessages, appendSystemPrompt, mimeForExt, computeContext, buildStreamPreviewMeta, summarizeClaudeToolUse, summarizeClaudeToolResult, previewToolCallInput, previewToolCallResult, roundPercent, toIsoFromEpochSeconds, normalizeUsageStatus, labelFromWindowMinutes, usageWindowFromRateLimit, parseJsonTail, modelFamily, normalizeClaudeModelId, emptyUsage, readTailLines, stripInjectedPrompts, sanitizeSessionUserPreviewText, SESSION_PREVIEW_IMAGE_PLACEHOLDER_RE, CLAUDE_AT_MENTION_IMAGE_RE, extractClaudeAtMentionImagePaths, stripClaudeAtMentionImages, isPendingSessionId, emitSessionIdUpdate, sessionListDisplayTitle, } from './utils.js';
|
|
9
9
|
export { updateSessionMeta, promoteSessionId, recordFork, resolveCanonicalSessionId, getSessionPromotions, listPikiloomSessions, findPikiloomSession, getSessionStoredConfig, ensureManagedSession, findManagedThreadSession, stageSessionFiles, mergeManagedAndNativeSessions, managedRecordToSessionInfo, getSessions, getSessionTail, getSessionMessages, applyTurnWindow, applyTurnFilter, classifySession, deriveUserStatus, exportSession, importSession, deleteAgentSession, isProcessAlive, isRunningSessionStale, reconcileOrphanedRunningSessions, } from './session.js';
|
|
10
|
-
export { detectAgentBin, listAgents, resolveDefaultAgent, run, doStream, listModels, resolveAgentModels, getUsage, getAgentBoundModelId, setAgentBoundModelId, } from './stream.js';
|
|
10
|
+
export { detectAgentBin, listAgents, resolveDefaultAgent, run, doStream, recoverProfileIdForModel, listModels, resolveAgentModels, getUsage, getAgentBoundModelId, setAgentBoundModelId, } from './stream.js';
|
|
11
11
|
export { registerDriver, getDriver, getDriverCapabilities, allDrivers, allDriverIds, hasDriver, shutdownAllDrivers, } from './driver.js';
|
|
12
12
|
export { getProjectSkillPaths, initializeProjectSkills, listSkills, getGlobalSkillsRoot, collapseSkillPrompt, } from './skills.js';
|
|
13
13
|
export { readGoal, writeGoal, clearGoal, setGoal, pauseGoal, resumeGoal, completeGoal, accountTurn, bumpContinuationCount, shouldContinueAfterTurn, renderContinuationPrompt, renderBudgetLimitPrompt, sessionGoalPath, DEFAULT_MAX_CONTINUATIONS, } from './goal.js';
|
|
@@ -19,6 +19,6 @@ export { getMcpToken, saveMcpToken, deleteMcpToken, hasValidMcpToken, startAutho
|
|
|
19
19
|
export { installSkill, removeSkill, checkSkillUpdates, getGlobalSkillsDir, recordSkillInstall, getSkillLedgerEntry, forgetSkillInstall, normalizeSkillSourceKey, } from './skill-installer.js';
|
|
20
20
|
export { getRecommendedClis, getRecommendedCli, detectCli, getCachedCliStatus, invalidateCliStatus, currentPlatform, getCliCatalog, refreshCliStatus, startCliAuthSession, getAuthSession, cancelAuthSession, applyCliToken, logoutCli, startCliInstallSession, } from './cli/index.js';
|
|
21
21
|
export { doClaudeStream } from './drivers/claude.js';
|
|
22
|
-
export { doCodexStream, buildCodexTurnInput, shutdownCodexServer, getCodexUsageLive } from './drivers/codex.js';
|
|
22
|
+
export { doCodexStream, buildCodexTurnInput, shutdownCodexServer, getCodexUsageLive, humanizeCodexError } from './drivers/codex.js';
|
|
23
23
|
export { doGeminiStream } from './drivers/gemini.js';
|
|
24
24
|
export { doHermesStream } from './drivers/hermes.js';
|
package/dist/agent/stream.js
CHANGED
|
@@ -388,6 +388,35 @@ function finalizeStreamResult(result, workdir, prompt, session, workflowEnabled,
|
|
|
388
388
|
saveSessionRecord(workdir, session.record);
|
|
389
389
|
return { ...result, sessionId: session.sessionId, workspacePath: session.workspacePath };
|
|
390
390
|
}
|
|
391
|
+
function requestedModelForAgent(opts) {
|
|
392
|
+
switch (opts.agent) {
|
|
393
|
+
case 'claude': return (opts.claudeModel || opts.model || '').trim();
|
|
394
|
+
case 'codex': return (opts.codexModel || opts.model || '').trim();
|
|
395
|
+
case 'gemini': return (opts.geminiModel || opts.model || '').trim();
|
|
396
|
+
case 'hermes': return (opts.hermesModel || opts.model || '').trim();
|
|
397
|
+
}
|
|
398
|
+
return (opts.model || '').trim();
|
|
399
|
+
}
|
|
400
|
+
// When a session carries a third-party model id but its profile binding was lost
|
|
401
|
+
// (stale/deleted profileId, or a model selected without a binding), resolveAgentInjection
|
|
402
|
+
// returns null and the agent silently falls back to its native account — e.g. Codex on a
|
|
403
|
+
// ChatGPT login rejects "deepseek-v4-pro". If exactly one configured profile (for a provider
|
|
404
|
+
// kind this agent accepts) declares that model id, the intent is unambiguous: recover it.
|
|
405
|
+
export function recoverProfileIdForModel(agent, modelId) {
|
|
406
|
+
const model = modelId.trim();
|
|
407
|
+
if (!model)
|
|
408
|
+
return null;
|
|
409
|
+
const accepted = new Set(getAcceptedProviderKinds(agent));
|
|
410
|
+
if (accepted.size === 0)
|
|
411
|
+
return null;
|
|
412
|
+
const matches = listProfiles().filter(profile => {
|
|
413
|
+
if (profile.modelId !== model)
|
|
414
|
+
return false;
|
|
415
|
+
const provider = getProvider(profile.providerId);
|
|
416
|
+
return !!provider && accepted.has(provider.kind);
|
|
417
|
+
});
|
|
418
|
+
return matches.length === 1 ? matches[0].id : null;
|
|
419
|
+
}
|
|
391
420
|
export async function doStream(opts) {
|
|
392
421
|
let session;
|
|
393
422
|
let prepared;
|
|
@@ -446,7 +475,18 @@ export async function doStream(opts) {
|
|
|
446
475
|
agentWarn(`[mcp] bridge start failed: ${e.message} — proceeding without MCP`);
|
|
447
476
|
}
|
|
448
477
|
try {
|
|
449
|
-
|
|
478
|
+
let injection = await resolveAgentInjection(prepared.agent, prepared.profileId);
|
|
479
|
+
if (!injection) {
|
|
480
|
+
const requestedModel = requestedModelForAgent(prepared);
|
|
481
|
+
const recoveredProfileId = recoverProfileIdForModel(prepared.agent, requestedModel);
|
|
482
|
+
if (recoveredProfileId) {
|
|
483
|
+
injection = await resolveAgentInjection(prepared.agent, recoveredProfileId);
|
|
484
|
+
if (injection) {
|
|
485
|
+
prepared.profileId = recoveredProfileId;
|
|
486
|
+
agentLog(`[byok] recovered lost provider binding: agent=${prepared.agent} model=${requestedModel} → profile=${recoveredProfileId}`);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
450
490
|
if (injection) {
|
|
451
491
|
prepared.extraEnv = { ...(prepared.extraEnv || {}), ...injection.env };
|
|
452
492
|
if (injection.modelOverride) {
|
package/dist/bot/commands.js
CHANGED
|
@@ -3,6 +3,7 @@ import fs from 'node:fs';
|
|
|
3
3
|
import { fmtTokens, fmtUptime, fmtBytes } from './bot.js';
|
|
4
4
|
import { getProjectSkillPaths, normalizeClaudeModelId, sessionListDisplayTitle, listAllMcpExtensions, listSkills as listAllSkills, } from '../agent/index.js';
|
|
5
5
|
import { getDriver } from '../agent/driver.js';
|
|
6
|
+
import { effortOptionsFor } from '../core/config/runtime-config.js';
|
|
6
7
|
import { getActiveProfile, getProvider } from '../model/index.js';
|
|
7
8
|
import { buildWelcomeIntro, buildSkillCommandName, indexSkillsByCommand, SKILL_CMD_PREFIX } from './menu.js';
|
|
8
9
|
import { buildBotMenuState } from './orchestration.js';
|
|
@@ -380,35 +381,12 @@ export function modelMatchesSelection(agent, selection, currentModel) {
|
|
|
380
381
|
const b = claudeModelFamily(currentModel);
|
|
381
382
|
return !!a && a === b;
|
|
382
383
|
}
|
|
383
|
-
|
|
384
|
-
claude: [
|
|
385
|
-
{ id: 'low', label: 'Low' },
|
|
386
|
-
{ id: 'medium', label: 'Medium' },
|
|
387
|
-
{ id: 'high', label: 'High' },
|
|
388
|
-
{ id: 'xhigh', label: 'Very High' },
|
|
389
|
-
{ id: 'max', label: 'Max' },
|
|
390
|
-
{ id: 'ultra', label: 'Ultra' },
|
|
391
|
-
],
|
|
392
|
-
codex: [
|
|
393
|
-
{ id: 'low', label: 'Low' },
|
|
394
|
-
{ id: 'medium', label: 'Medium' },
|
|
395
|
-
{ id: 'high', label: 'High' },
|
|
396
|
-
{ id: 'xhigh', label: 'Very High' },
|
|
397
|
-
],
|
|
398
|
-
hermes: [
|
|
399
|
-
{ id: 'minimal', label: 'Minimal' },
|
|
400
|
-
{ id: 'low', label: 'Low' },
|
|
401
|
-
{ id: 'medium', label: 'Medium' },
|
|
402
|
-
{ id: 'high', label: 'High' },
|
|
403
|
-
{ id: 'xhigh', label: 'Very High' },
|
|
404
|
-
],
|
|
405
|
-
};
|
|
406
|
-
function buildEffortData(bot, agent) {
|
|
384
|
+
function buildEffortData(bot, agent, model) {
|
|
407
385
|
const currentEffort = bot.effortSelectionForAgent(agent);
|
|
408
386
|
if (!currentEffort)
|
|
409
387
|
return null;
|
|
410
|
-
const levels =
|
|
411
|
-
if (!levels)
|
|
388
|
+
const levels = effortOptionsFor(agent, model);
|
|
389
|
+
if (!levels.length)
|
|
412
390
|
return null;
|
|
413
391
|
return {
|
|
414
392
|
current: currentEffort,
|
|
@@ -440,7 +418,7 @@ export async function getModelsListData(bot, chatId) {
|
|
|
440
418
|
providerName: m.providerName ?? null,
|
|
441
419
|
};
|
|
442
420
|
}),
|
|
443
|
-
effort: buildEffortData(bot, cs.agent),
|
|
421
|
+
effort: buildEffortData(bot, cs.agent, currentModel),
|
|
444
422
|
};
|
|
445
423
|
}
|
|
446
424
|
export async function getStatusDataAsync(bot, chatId) {
|
|
@@ -164,3 +164,38 @@ export function decomposeEffortSelection(raw) {
|
|
|
164
164
|
return { effort: 'max', workflow: true };
|
|
165
165
|
return { effort: value, workflow: false };
|
|
166
166
|
}
|
|
167
|
+
// Single source of truth for reasoning-effort levels, ordered low→high. BOTH the dashboard
|
|
168
|
+
// (which receives them via the agent/model API payload) and the IM bot consume this — do not
|
|
169
|
+
// reintroduce a second copy. Resolve options through effortOptionsFor(), never index directly,
|
|
170
|
+
// so per-model/provider rules stay in one place.
|
|
171
|
+
const AGENT_EFFORT_LEVELS = {
|
|
172
|
+
claude: [
|
|
173
|
+
{ id: 'low', label: 'Low' },
|
|
174
|
+
{ id: 'medium', label: 'Medium' },
|
|
175
|
+
{ id: 'high', label: 'High' },
|
|
176
|
+
{ id: 'xhigh', label: 'Very High' },
|
|
177
|
+
{ id: 'max', label: 'Max' },
|
|
178
|
+
{ id: ULTRA_EFFORT, label: 'Ultra' },
|
|
179
|
+
],
|
|
180
|
+
codex: [
|
|
181
|
+
{ id: 'low', label: 'Low' },
|
|
182
|
+
{ id: 'medium', label: 'Medium' },
|
|
183
|
+
{ id: 'high', label: 'High' },
|
|
184
|
+
{ id: 'xhigh', label: 'Very High' },
|
|
185
|
+
],
|
|
186
|
+
// gemini intentionally has no UI-exposed effort levels: pikiloom sends it no reasoning-effort
|
|
187
|
+
// (see the gemini→null guards in InputComposer). Add a gemini entry here to surface low/high.
|
|
188
|
+
hermes: [
|
|
189
|
+
{ id: 'minimal', label: 'Minimal' },
|
|
190
|
+
{ id: 'low', label: 'Low' },
|
|
191
|
+
{ id: 'medium', label: 'Medium' },
|
|
192
|
+
{ id: 'high', label: 'High' },
|
|
193
|
+
{ id: 'xhigh', label: 'Very High' },
|
|
194
|
+
],
|
|
195
|
+
};
|
|
196
|
+
// Valid effort levels for a given (agent, model, providerKind). Returns [] when reasoning
|
|
197
|
+
// effort does not apply (the UI then hides the selector entirely). model/providerKind are the
|
|
198
|
+
// seam for per-model rules — add them here and nowhere else.
|
|
199
|
+
export function effortOptionsFor(agent, _model, _providerKind) {
|
|
200
|
+
return AGENT_EFFORT_LEVELS[agent] ?? [];
|
|
201
|
+
}
|
package/dist/core/constants.js
CHANGED
|
@@ -8,7 +8,6 @@ export const MCP_TIMEOUTS = {
|
|
|
8
8
|
codexMcpRemove: 5_000,
|
|
9
9
|
};
|
|
10
10
|
export const MCP_ARTIFACT_MAX_BYTES = 20 * 1024 * 1024;
|
|
11
|
-
export const DELIVERED_ARTIFACT_TAIL_STALE_MS = 60 * 60_000;
|
|
12
11
|
export const DASHBOARD_TIMEOUTS = {
|
|
13
12
|
agentStatusModels: 4_000,
|
|
14
13
|
agentStatusUsage: 1_500,
|
|
@@ -7,7 +7,7 @@ import { loadUserConfig, saveUserConfig, applyUserConfig } from '../../core/conf
|
|
|
7
7
|
import { setAgentBoundModelId } from '../../agent/index.js';
|
|
8
8
|
import { getAgentUpdateState, checkAgentLatestVersion, manualAgentUpdate } from '../../agent/auto-update.js';
|
|
9
9
|
import { getDriver, getDriverCapabilities } from '../../agent/driver.js';
|
|
10
|
-
import { decomposeEffortSelection } from '../../core/config/runtime-config.js';
|
|
10
|
+
import { decomposeEffortSelection, effortOptionsFor } from '../../core/config/runtime-config.js';
|
|
11
11
|
import { getActiveProfile, getProvider, peekProviderModelList, prefetchProviderModels, } from '../../model/index.js';
|
|
12
12
|
import { DASHBOARD_TIMEOUTS } from '../../core/constants.js';
|
|
13
13
|
import { withTimeoutFallback } from '../../core/utils.js';
|
|
@@ -195,6 +195,7 @@ async function buildAgentStatusResponse(config = loadUserConfig(), agentOptions
|
|
|
195
195
|
...agentState,
|
|
196
196
|
selectedModel,
|
|
197
197
|
selectedEffort,
|
|
198
|
+
effortOptions: effortOptionsFor(agentId, selectedModel, byokProvider?.kind ?? null),
|
|
198
199
|
nativeSelectedModel,
|
|
199
200
|
nativeSelectedEffort,
|
|
200
201
|
workflowEnabled: runtime.getRuntimeWorkflowEnabled(agentId, config),
|
|
@@ -10,7 +10,7 @@ import { findPikiloomSession } from '../../agent/session.js';
|
|
|
10
10
|
import { readAwaitResume } from '../../agent/await-resume.js';
|
|
11
11
|
import { cancelSessionTask, stopSessionTasks, getSessionStreamState, queueDashboardSessionTask, forkDashboardSessionTask, steerSessionTask, interactionSelectOption, interactionSubmitText, interactionSkip, interactionCancel, getInteractionPrompt, } from '../session-control.js';
|
|
12
12
|
import { querySessions, querySessionTail, querySessionMessages, getWorkspaceOverviews, updateSession, linkSessions, buildMigrationContext, exportSession, importSession, deleteSession, loadWorkspaces, addWorkspace, removeWorkspace, updateWorkspace, } from '../../bot/session-hub.js';
|
|
13
|
-
import { DASHBOARD_PAGINATION
|
|
13
|
+
import { DASHBOARD_PAGINATION } from '../../core/constants.js';
|
|
14
14
|
import { runtime } from '../runtime.js';
|
|
15
15
|
const DEFAULT_SESSION_PAGE_SIZE = DASHBOARD_PAGINATION.defaultPageSize;
|
|
16
16
|
const MAX_SESSION_PAGE_SIZE = DASHBOARD_PAGINATION.maxPageSize;
|
|
@@ -394,16 +394,17 @@ app.post('/api/session-hub/session/messages', async (c) => {
|
|
|
394
394
|
turnLimit: Number.isFinite(turnLimit) ? turnLimit : undefined,
|
|
395
395
|
rich,
|
|
396
396
|
});
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
397
|
+
// Scope the delivered-artifact tail to the session's last-run turn (its task id, from the
|
|
398
|
+
// live snapshot which persists after the turn). A later turn that delivered nothing then
|
|
399
|
+
// yields no tail blocks, so a prior turn's image can't re-append onto the newest reply.
|
|
400
|
+
const lastRunTaskId = runtime.getBotRef()?.getStreamSnapshot(`${agent}:${sessionId}`)?.taskId ?? null;
|
|
401
|
+
return c.json(prepareSessionMessagesForDashboard(result, agent, sessionId, lastRunTaskId));
|
|
401
402
|
}
|
|
402
403
|
catch (e) {
|
|
403
404
|
return c.json({ ok: false, error: e.message }, 500);
|
|
404
405
|
}
|
|
405
406
|
});
|
|
406
|
-
function prepareSessionMessagesForDashboard(result, agent, sessionId,
|
|
407
|
+
function prepareSessionMessagesForDashboard(result, agent, sessionId, lastRunTaskId) {
|
|
407
408
|
if (result.richMessages === undefined)
|
|
408
409
|
return result;
|
|
409
410
|
const richMessages = result.richMessages.map(message => ({
|
|
@@ -412,12 +413,10 @@ function prepareSessionMessagesForDashboard(result, agent, sessionId, lastActivi
|
|
|
412
413
|
}));
|
|
413
414
|
const includesTail = !result.window || !result.window.hasNewer;
|
|
414
415
|
if (includesTail) {
|
|
415
|
-
//
|
|
416
|
-
//
|
|
417
|
-
//
|
|
418
|
-
|
|
419
|
-
// content for the turn that sent it, not for later turns that delivered nothing.
|
|
420
|
-
const delivered = rewriteAttachmentBlocksForTransport(tailDeliveredBlocks(agent, sessionId, { lastActivityMs, staleAfterMs: DELIVERED_ARTIFACT_TAIL_STALE_MS }), { agent, sessionId });
|
|
416
|
+
// Out-of-band delivered files (im_send_file) aren't in the transcript, so the latest turn's
|
|
417
|
+
// deliveries are appended as a trailing message. tailDeliveredBlocks scopes strictly to the
|
|
418
|
+
// last-run task id — only THIS turn's files surface, never a prior turn's (no time heuristic).
|
|
419
|
+
const delivered = rewriteAttachmentBlocksForTransport(tailDeliveredBlocks(agent, sessionId, lastRunTaskId), { agent, sessionId });
|
|
421
420
|
if (delivered.length) {
|
|
422
421
|
const text = deliveredSummaryText(delivered);
|
|
423
422
|
richMessages.push({ role: 'assistant', text, blocks: delivered });
|
|
@@ -284,24 +284,17 @@ class Runtime {
|
|
|
284
284
|
}
|
|
285
285
|
return { channel, key, cached: null, livePromise: this.validateChannel(channel, config), fallback: fallback[idx] };
|
|
286
286
|
});
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
plans.
|
|
287
|
+
// Never block /api/state on live network validation — Feishu/Weixin round-trips can take
|
|
288
|
+
// seconds and this is the sole caller. Return fresh-cached states where available, local
|
|
289
|
+
// fallback otherwise, and validate uncached channels in the background to populate the
|
|
290
|
+
// cache. The dashboard re-polls while any channel is pending (hasPendingChannelValidation)
|
|
291
|
+
// and converges to the live result on the next poll.
|
|
292
|
+
const resolved = plans.map(plan => plan.cached ?? plan.fallback);
|
|
293
|
+
for (const plan of plans) {
|
|
293
294
|
if (!plan.livePromise)
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
this.channelStateCache.set(plan.channel, {
|
|
298
|
-
key: plan.key,
|
|
299
|
-
expiresAt: now + CHANNEL_STATUS_CACHE_TTL_MS,
|
|
300
|
-
state,
|
|
301
|
-
});
|
|
302
|
-
return;
|
|
303
|
-
}
|
|
304
|
-
void plan.livePromise.then(bgState => {
|
|
295
|
+
continue;
|
|
296
|
+
void withTimeoutFallback(plan.livePromise, CHANNEL_STATUS_VALIDATION_TIMEOUT_MS, plan.fallback)
|
|
297
|
+
.then(bgState => {
|
|
305
298
|
if (!shouldCacheChannelStates([bgState]))
|
|
306
299
|
return;
|
|
307
300
|
const current = this.channelStateCache.get(plan.channel);
|
|
@@ -313,7 +306,7 @@ class Runtime {
|
|
|
313
306
|
state: bgState,
|
|
314
307
|
});
|
|
315
308
|
}).catch(() => { });
|
|
316
|
-
}
|
|
309
|
+
}
|
|
317
310
|
return resolved;
|
|
318
311
|
}
|
|
319
312
|
getSetupState(config = loadUserConfig(), agentOptions = {}) {
|
package/package.json
CHANGED