mixdog 0.9.40 → 0.9.43
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/package.json +2 -1
- package/scripts/agent-tag-reuse-smoke.mjs +124 -10
- package/scripts/anthropic-oauth-refresh-race-test.mjs +397 -0
- package/scripts/arg-guard-test.mjs +16 -0
- package/scripts/compact-pressure-test.mjs +383 -0
- package/scripts/compact-trigger-migration-smoke.mjs +8 -8
- package/scripts/internal-comms-bench.mjs +0 -1
- package/scripts/internal-comms-smoke.mjs +54 -31
- package/scripts/internal-tools-normalization-test.mjs +52 -0
- package/scripts/max-output-recovery-persist-test.mjs +81 -0
- package/scripts/max-output-recovery-test.mjs +271 -0
- package/scripts/mcp-client-normalization-test.mjs +45 -0
- package/scripts/provider-toolcall-test.mjs +55 -0
- package/scripts/result-classification-test.mjs +75 -0
- package/scripts/smoke.mjs +1 -1
- package/scripts/submit-commandbusy-race-test.mjs +99 -7
- package/scripts/tui-transcript-perf-test.mjs +56 -1
- package/src/agents/reviewer/AGENT.md +8 -0
- package/src/rules/lead/lead-brief.md +11 -3
- package/src/rules/lead/lead-tool.md +0 -2
- package/src/rules/shared/01-tool.md +4 -0
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +20 -2
- package/src/runtime/agent/orchestrator/context/collect.mjs +7 -3
- package/src/runtime/agent/orchestrator/internal-tools.mjs +16 -3
- package/src/runtime/agent/orchestrator/mcp/client.mjs +17 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +193 -13
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +15 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +14 -0
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +59 -2
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +57 -25
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +72 -12
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +144 -84
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +124 -8
- package/src/runtime/agent/orchestrator/session/loop/steering-ladder.mjs +12 -1
- package/src/runtime/agent/orchestrator/session/loop/termination.mjs +25 -7
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +12 -3
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +28 -1
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +31 -4
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +11 -2
- package/src/runtime/agent/orchestrator/session/result-classification.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +45 -0
- package/src/runtime/agent/orchestrator/session/tool-batch.mjs +7 -2
- package/src/runtime/agent/orchestrator/session/tool-envelope.mjs +8 -6
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +15 -3
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +22 -21
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +9 -10
- package/src/runtime/memory/tool-defs.mjs +4 -3
- package/src/runtime/shared/tool-surface.mjs +1 -2
- package/src/session-runtime/context-status.mjs +8 -2
- package/src/standalone/agent-tool/render.mjs +2 -0
- package/src/standalone/agent-tool.mjs +202 -22
- package/src/tui/components/StatusLine.jsx +4 -26
- package/src/tui/dist/index.mjs +46 -31
- package/src/tui/engine/session-api.mjs +3 -0
- package/src/tui/engine/session-flow.mjs +19 -8
- package/src/tui/engine/turn.mjs +24 -2
- package/src/tui/engine.mjs +0 -1
- package/src/ui/statusline-agents.mjs +0 -8
- package/src/ui/statusline.mjs +2 -4
- package/src/workflows/default/WORKFLOW.md +38 -24
- package/src/workflows/solo-review/WORKFLOW.md +47 -0
- package/src/workflows/bench/WORKFLOW.md +0 -36
|
@@ -344,6 +344,15 @@ function stripTrailingPatternArtifacts(v) {
|
|
|
344
344
|
return out;
|
|
345
345
|
}
|
|
346
346
|
|
|
347
|
+
function coercePatternStringValues(v) {
|
|
348
|
+
const coerce = (value) => (
|
|
349
|
+
(typeof value === 'number' && Number.isFinite(value)) || typeof value === 'boolean'
|
|
350
|
+
? String(value)
|
|
351
|
+
: value
|
|
352
|
+
);
|
|
353
|
+
return Array.isArray(v) ? v.map(coerce) : coerce(v);
|
|
354
|
+
}
|
|
355
|
+
|
|
347
356
|
// ---- per-tool guards ----
|
|
348
357
|
|
|
349
358
|
function guardGrep(a) {
|
|
@@ -355,9 +364,10 @@ function guardGrep(a) {
|
|
|
355
364
|
// Lossless cleanup of trailing artifacts before validation (item 5b).
|
|
356
365
|
for (const k of patternKeys) {
|
|
357
366
|
if (hasOwn(a, k)) {
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
367
|
+
const value = coercePatternStringValues(a[k]);
|
|
368
|
+
a[k] = Array.isArray(value)
|
|
369
|
+
? value.map(stripTrailingPatternArtifacts)
|
|
370
|
+
: stripTrailingPatternArtifacts(value);
|
|
361
371
|
}
|
|
362
372
|
}
|
|
363
373
|
|
|
@@ -376,6 +386,7 @@ function guardGrep(a) {
|
|
|
376
386
|
// rg's PCRE2 engine (-P/--pcre2) when the installed rg build supports it,
|
|
377
387
|
// falling back to this same error text only when PCRE2 is unavailable.
|
|
378
388
|
for (const k of globKeys) {
|
|
389
|
+
if (hasOwn(a, k)) a[k] = coercePatternStringValues(a[k]);
|
|
379
390
|
if (hasOwn(a, k) && !isStringOrStringArray(a[k])) {
|
|
380
391
|
return `Error: grep arg "${k}" must be string or string[] (got ${describeType(a[k])})`;
|
|
381
392
|
}
|
|
@@ -755,6 +766,7 @@ function guardGlob(a) {
|
|
|
755
766
|
}
|
|
756
767
|
}
|
|
757
768
|
for (const k of globPatternKeys) {
|
|
769
|
+
if (hasOwn(a, k)) a[k] = coercePatternStringValues(a[k]);
|
|
758
770
|
if (hasOwn(a, k) && !isStringOrStringArray(a[k])) {
|
|
759
771
|
return `Error: glob arg "${k}" must be string or string[] (got ${describeType(a[k])})`;
|
|
760
772
|
}
|
|
@@ -297,10 +297,9 @@ export async function executeBashTool(args, workDir, options = {}) {
|
|
|
297
297
|
// Keep foreground commands on a long tool-owned timeout. The MCP dispatch
|
|
298
298
|
// layer must not add a shorter fallback ceiling when timeout is omitted.
|
|
299
299
|
// Reference-CLI parity (opencode/codex/claude-code): sync-first, no hard
|
|
300
|
-
// upper ceiling on a caller-provided timeout. Default 120 s (2 min)
|
|
301
|
-
// omitted; BASH_DEFAULT_TIMEOUT_MS / BASH_MAX_TIMEOUT_MS env overrides
|
|
302
|
-
//
|
|
303
|
-
// default — an explicit args.timeout is honored UNCAPPED.
|
|
300
|
+
// upper ceiling on a caller-provided total timeout. Default 120 s (2 min)
|
|
301
|
+
// when omitted; BASH_DEFAULT_TIMEOUT_MS / BASH_MAX_TIMEOUT_MS env overrides
|
|
302
|
+
// bound the blocking window when timeout promotion is available.
|
|
304
303
|
const _envDefaultTimeout = parseInt(process.env.BASH_DEFAULT_TIMEOUT_MS ?? '', 10);
|
|
305
304
|
const DEFAULT_BASH_TIMEOUT_MS = _envDefaultTimeout > 0 ? _envDefaultTimeout : 120_000;
|
|
306
305
|
// Background (async / run_in_background) jobs get NO omitted default: 0
|
|
@@ -315,8 +314,14 @@ export async function executeBashTool(args, workDir, options = {}) {
|
|
|
315
314
|
: DEFAULT_BASH_TIMEOUT_MS;
|
|
316
315
|
const hasExplicitTimeout = typeof args.timeout === 'number' && args.timeout > 0;
|
|
317
316
|
const timeoutMs = hasExplicitTimeout ? args.timeout : defaultTimeoutMs;
|
|
318
|
-
|
|
319
|
-
|
|
317
|
+
const _bgTasksDisabled = /^(1|true|yes|on)$/i.test(
|
|
318
|
+
String(process.env.MIXDOG_SHELL_DISABLE_BACKGROUND_TASKS || '').trim(),
|
|
319
|
+
);
|
|
320
|
+
const backgroundOnTimeout = !runInBackground
|
|
321
|
+
&& !_bgTasksDisabled
|
|
322
|
+
&& isAutobackgroundingAllowed(command, resolvedSpec.shellType);
|
|
323
|
+
// Explicit caller timeout remains the total deadline. When promotion is
|
|
324
|
+
// available, cap only its foreground blocking portion at MAX.
|
|
320
325
|
// JS timers (setTimeout) and PS WaitForExit(ms) are 32-bit: a delay above
|
|
321
326
|
// 2^31-1 wraps to a tiny/negative value and fires immediately. Clamp the
|
|
322
327
|
// uncapped explicit timeout once here (~24.8 days ceiling) so every
|
|
@@ -325,11 +330,15 @@ export async function executeBashTool(args, workDir, options = {}) {
|
|
|
325
330
|
const TIMER_MAX_MS = 2_147_483_647;
|
|
326
331
|
// timeoutMs <= 0 (omitted background default) means unlimited: pass it
|
|
327
332
|
// through untouched — the min() clamps below must not turn 0 into a bound.
|
|
328
|
-
const
|
|
333
|
+
const totalTimeout = timeoutMs <= 0
|
|
329
334
|
? 0
|
|
330
|
-
: (hasExplicitTimeout
|
|
331
|
-
|
|
332
|
-
|
|
335
|
+
: Math.min(timeoutMs, wmicRewrite?.timeoutMs || (hasExplicitTimeout ? TIMER_MAX_MS : MAX_BASH_TIMEOUT_MS));
|
|
336
|
+
const timeout = hasExplicitTimeout && backgroundOnTimeout
|
|
337
|
+
? Math.min(totalTimeout, MAX_BASH_TIMEOUT_MS)
|
|
338
|
+
: totalTimeout;
|
|
339
|
+
const promotedTimeoutMs = hasExplicitTimeout && backgroundOnTimeout
|
|
340
|
+
? Math.max(0, totalTimeout - timeout)
|
|
341
|
+
: 0;
|
|
333
342
|
const mergeStderr = args.merge_stderr === true;
|
|
334
343
|
const longForegroundHint = foregroundLongCommandHint(command, timeout, { ...args, run_in_background: runInBackground });
|
|
335
344
|
if (longForegroundHint) return formatShellToolFailure(longForegroundHint);
|
|
@@ -458,12 +467,6 @@ export async function executeBashTool(args, workDir, options = {}) {
|
|
|
458
467
|
// base commands (isAutobackgroundingAllowed), (b) the truthy
|
|
459
468
|
// MIXDOG_SHELL_DISABLE_BACKGROUND_TASKS env. Never applies to
|
|
460
469
|
// run_in_background (already detached, handled above).
|
|
461
|
-
const _bgTasksDisabled = /^(1|true|yes|on)$/i.test(
|
|
462
|
-
String(process.env.MIXDOG_SHELL_DISABLE_BACKGROUND_TASKS || '').trim(),
|
|
463
|
-
);
|
|
464
|
-
const backgroundOnTimeout = !runInBackground
|
|
465
|
-
&& !_bgTasksDisabled
|
|
466
|
-
&& isAutobackgroundingAllowed(command, shellType);
|
|
467
470
|
const result = await execShellCommand({
|
|
468
471
|
shell, shellArg, shellArgs, command: syncCommand,
|
|
469
472
|
env: spawnEnv,
|
|
@@ -474,6 +477,7 @@ export async function executeBashTool(args, workDir, options = {}) {
|
|
|
474
477
|
// On a foreground timeout, promote the still-running child to a
|
|
475
478
|
// tracked background job (unlimited) instead of killing it.
|
|
476
479
|
backgroundOnTimeout,
|
|
480
|
+
promotedTimeoutMs,
|
|
477
481
|
// Threaded so an auto-backgrounded foreground job is stamped with
|
|
478
482
|
// the dispatching terminal's claude.exe pid (per-terminal scope).
|
|
479
483
|
clientHostPid: options?.clientHostPid,
|
|
@@ -507,10 +511,7 @@ export async function executeBashTool(args, workDir, options = {}) {
|
|
|
507
511
|
stdout: result.stdoutPath ? normalizeOutputPath(result.stdoutPath) : null,
|
|
508
512
|
stderr: (!mergeStderr && result.stderrPath) ? normalizeOutputPath(result.stderrPath) : null,
|
|
509
513
|
cwd: bashWorkDir,
|
|
510
|
-
|
|
511
|
-
// — matches the async default; the original
|
|
512
|
-
// foreground timeout no longer bounds the promoted job.
|
|
513
|
-
timeoutMs: 0,
|
|
514
|
+
timeoutMs: result.backgroundTimeoutMs || 0,
|
|
514
515
|
},
|
|
515
516
|
resultType: 'shell_task_result',
|
|
516
517
|
cancel: () => killShellJob(result.jobId),
|
|
@@ -530,7 +531,7 @@ export async function executeBashTool(args, workDir, options = {}) {
|
|
|
530
531
|
const lines = [
|
|
531
532
|
task ? renderBackgroundTask(task) : (result.jobId ? `[task_id: ${result.jobId}]` : null),
|
|
532
533
|
'',
|
|
533
|
-
result.backgroundMessage || 'auto-backgrounded; still running',
|
|
534
|
+
result.backgroundMessage || 'auto-backgrounded; still running — judge from the partial output whether waiting can finish in budget, or diagnose and pursue an alternative.',
|
|
534
535
|
partialStdout ? `\n[partial stdout]\n${partialStdout}` : '',
|
|
535
536
|
(!mergeStderr && partialStderr) ? `\n[partial stderr]\n${partialStderr}` : '',
|
|
536
537
|
].filter((l) => l !== null && l !== '');
|
|
@@ -83,7 +83,7 @@ export const BUILTIN_TOOLS = [
|
|
|
83
83
|
properties: {
|
|
84
84
|
command: { type: 'string', description: 'Command.' },
|
|
85
85
|
cwd: { type: 'string', description: 'Working directory; persists across calls. Omit to reuse; absolute path changes it.' },
|
|
86
|
-
timeout: { type: 'number', description: `Timeout ms. Default ${_shellDefaultTimeoutMs()} (${_shellDefaultTimeoutMs() / 60000} min) when omitted
|
|
86
|
+
timeout: { type: 'number', description: `Timeout ms. Default ${_shellDefaultTimeoutMs()} (${_shellDefaultTimeoutMs() / 60000} min) when omitted. On sync timeout the command is MOVED TO BACKGROUND (a task_id you can wait/status/read/cancel) and keeps running instead of being killed; an explicit timeout blocks for at most BASH_MAX_TIMEOUT_MS (default 10 min), and only its remainder beyond that cap is still enforced as a background deadline. Sleep-like commands and MIXDOG_SHELL_DISABLE_BACKGROUND_TASKS opt out (killed with a [timeout] marker after blocking for the full explicit timeout). async/background runs with timeout omitted have NO timeout (run until done/cancelled); an explicit timeout is still enforced.` },
|
|
87
87
|
merge_stderr: { type: 'boolean', description: 'Merge stderr.' },
|
|
88
88
|
mode: { type: 'string', enum: ['sync', 'async'], description: executionModeSchemaDescription('sync') },
|
|
89
89
|
shell: { type: 'string', enum: ['bash', 'powershell'], description: 'Force shell. Windows defaults to PowerShell; bash = Git Bash/POSIX.' },
|
|
@@ -773,7 +773,7 @@ export function foregroundLongCommandHint(command, timeoutMs, args = {}) {
|
|
|
773
773
|
const longSleep = /\bsleep\s+(?:[3-9]\d|\d{3,}|\d+[mh])\b/i.test(cmd) || longPowerShellSleep;
|
|
774
774
|
if (!watchLike && !longSleep) return '';
|
|
775
775
|
if (!longTimeout && !watchLike && !longSleep) return '';
|
|
776
|
-
return 'Error: long foreground command detected.';
|
|
776
|
+
return 'Error: long foreground command detected. Use mode:"async" (background task) or task wait instead of blocking sleeps/watch loops.';
|
|
777
777
|
}
|
|
778
778
|
|
|
779
779
|
// Commands that must NOT be promoted to background on a foreground timeout —
|
|
@@ -500,6 +500,7 @@ export function execShellCommand({
|
|
|
500
500
|
onProgress,
|
|
501
501
|
clientHostPid,
|
|
502
502
|
backgroundOnTimeout,
|
|
503
|
+
promotedTimeoutMs = 0,
|
|
503
504
|
}) {
|
|
504
505
|
return new Promise(async (resolve) => {
|
|
505
506
|
const taskId = `shell_${randomUUID().slice(0, 8)}`;
|
|
@@ -741,9 +742,8 @@ export function execShellCommand({
|
|
|
741
742
|
// BACKGROUND_MS opt-in) — an EARLIER promotion before the timeout, and
|
|
742
743
|
// 2. the foreground timeout deadline (backgroundOnTimeout) — the default
|
|
743
744
|
// promote-on-timeout that replaces the old tree-kill.
|
|
744
|
-
//
|
|
745
|
-
//
|
|
746
|
-
// the adopted-job cap poll still enforces the 100 MB output ceiling.
|
|
745
|
+
// A capped explicit foreground timeout supplies its remaining deadline to
|
|
746
|
+
// the adopted job; otherwise adoption remains unlimited as before.
|
|
747
747
|
// Mutually exclusive with settle() via the autoBackgrounded flag set
|
|
748
748
|
// synchronously at the top before any await.
|
|
749
749
|
const _autoBackground = async ({ reason = 'threshold' } = {}) => {
|
|
@@ -754,8 +754,7 @@ export function execShellCommand({
|
|
|
754
754
|
if (child.exitCode != null || child.signalCode != null) return;
|
|
755
755
|
autoBackgrounded = true;
|
|
756
756
|
// The foreground capture is over; stop the local watchdogs/timers so
|
|
757
|
-
// they cannot treeKill the now-adopted child.
|
|
758
|
-
// unlimited; refreshShellJob only enforces the output cap.
|
|
757
|
+
// they cannot treeKill the now-adopted child.
|
|
759
758
|
if (timer) { clearTimeout(timer); timer = null; }
|
|
760
759
|
_clearProgressTimer();
|
|
761
760
|
if (sizeWatchdog) { clearInterval(sizeWatchdog); sizeWatchdog = null; }
|
|
@@ -777,14 +776,13 @@ export function execShellCommand({
|
|
|
777
776
|
const stdoutPath = taskOutput.spilled ? taskOutput.stdoutPath : null;
|
|
778
777
|
const stderrPath = taskOutput.spilled ? taskOutput.stderrPath : null;
|
|
779
778
|
let job = null;
|
|
779
|
+
const adoptedTimeoutMs = reason === 'timeout' ? promotedTimeoutMs : 0;
|
|
780
780
|
try {
|
|
781
781
|
job = adoptForegroundShellJob({
|
|
782
782
|
command,
|
|
783
783
|
cwd,
|
|
784
784
|
pid: child.pid,
|
|
785
|
-
|
|
786
|
-
// timeout (matches the async omitted-default behavior).
|
|
787
|
-
timeoutMs: 0,
|
|
785
|
+
timeoutMs: adoptedTimeoutMs,
|
|
788
786
|
mergeStderr: false,
|
|
789
787
|
stdoutPath,
|
|
790
788
|
stderrPath,
|
|
@@ -854,9 +852,10 @@ export function execShellCommand({
|
|
|
854
852
|
outputCaptureError: taskOutput.writeError,
|
|
855
853
|
backgrounded: true,
|
|
856
854
|
jobId,
|
|
855
|
+
backgroundTimeoutMs: adoptedTimeoutMs,
|
|
857
856
|
backgroundMessage: jobId
|
|
858
|
-
? `${_verb}; still running —
|
|
859
|
-
: `${_verb}; still running
|
|
857
|
+
? `${_verb}; still running. Waiting is a decision, not a default: judge from the partial output whether this will finish within your remaining budget — if progress looks slow or stalled, diagnose the cause and pursue an alternative instead of waiting. Completion will be delivered as a background task notification; use task with task_id:${jobId} only for manual wait/status/read/cancel.`
|
|
858
|
+
: `${_verb}; still running — judge from the partial output whether waiting can finish in budget, or diagnose and pursue an alternative.`,
|
|
860
859
|
}),
|
|
861
860
|
);
|
|
862
861
|
};
|
|
@@ -8,21 +8,22 @@ export const TOOL_DEFS = [
|
|
|
8
8
|
name: 'memory',
|
|
9
9
|
title: 'Memory Cycle',
|
|
10
10
|
annotations: { title: 'Memory Cycle', readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },
|
|
11
|
-
description: 'Core-memory mutation and status. Use recall for retrieval. Persist with action:"core" op:"add" when: user corrects/confirms your approach, states a durable preference, or says "remember". Only what code/git/docs cannot derive; include the why. Never transient task state. No prior recall dedup needed — the cycle dedups.',
|
|
11
|
+
description: 'Core-memory mutation and status. Use recall for retrieval. Persist with action:"core" op:"add" when: user corrects/confirms your approach, states a durable preference, or says "remember". Core add/edit requires project_id + category + element + summary. Only what code/git/docs cannot derive; include the why. Never transient task state. No prior recall dedup needed — the cycle dedups.',
|
|
12
12
|
inputSchema: {
|
|
13
13
|
type: 'object',
|
|
14
14
|
properties: {
|
|
15
15
|
action: { type: 'string', enum: ['core','status'], description: 'Operation.' },
|
|
16
16
|
op: { type: 'string', enum: ['add','edit','delete','list','candidates','promote','dismiss'], description: 'Mutation op. candidates/promote/dismiss drive core-memory proposal approval.' },
|
|
17
17
|
id: { type: 'number', description: 'Exact memory id.' },
|
|
18
|
-
element: { type: 'string', description: 'Memory key/title. Max 40 chars.' },
|
|
19
|
-
summary: { type: 'string', description: 'Memory content: 1 fact, 1-2 sentences, max 100 chars.' },
|
|
18
|
+
element: { type: 'string', maxLength: 40, description: 'Memory key/title. Max 40 chars.' },
|
|
19
|
+
summary: { type: 'string', maxLength: 100, description: 'Memory content: 1 fact, 1-2 sentences, max 100 chars.' },
|
|
20
20
|
category: { type: 'string', enum: ['rule','constraint','decision','fact','goal','preference','task','issue'], description: 'Category.' },
|
|
21
21
|
status: { type: 'string', enum: ['pending','active','archived'], description: 'Lifecycle status.' },
|
|
22
22
|
limit: { type: 'number', description: 'Max rows/items.' },
|
|
23
23
|
confirm: { type: 'string', description: 'Exact confirmation phrase for destructive actions.' },
|
|
24
24
|
project_id: { type: 'string', description: 'Core pool: common, slug, or *. Required for core add/edit; there is no default pool.' },
|
|
25
25
|
},
|
|
26
|
+
additionalProperties: false,
|
|
26
27
|
required: ['action'],
|
|
27
28
|
},
|
|
28
29
|
},
|
|
@@ -514,8 +514,7 @@ export function toolWorkUnit(name, args = {}, category = '') {
|
|
|
514
514
|
case 'memory': {
|
|
515
515
|
const action = String(a.action || '').toLowerCase();
|
|
516
516
|
const op = String(a.op || '').toLowerCase();
|
|
517
|
-
const isMutation = op === 'add' || op === 'edit' || op === 'delete' || op === 'promote' || op === 'dismiss'
|
|
518
|
-
|| (action === 'core' && !op);
|
|
517
|
+
const isMutation = op === 'add' || op === 'edit' || op === 'delete' || op === 'promote' || op === 'dismiss';
|
|
519
518
|
if (isMutation) return unitDescriptor('Memory', { count: queryCount(a, 'entries', 'items', 'memories', 'query', 'text', 'value') || 1, active: 'Writing', done: 'Wrote', noun: 'memory item' });
|
|
520
519
|
return unitDescriptor('Memory', { count: queryCount(a, 'entries', 'items', 'memories', 'query', 'text', 'value') || 1, active: 'Checking', done: 'Checked', noun: 'memory item' });
|
|
521
520
|
}
|
|
@@ -2,8 +2,10 @@ import {
|
|
|
2
2
|
estimateRequestReserveTokens,
|
|
3
3
|
estimateToolSchemaTokens,
|
|
4
4
|
estimateTranscriptContextUsage,
|
|
5
|
+
contextMessagesRevision,
|
|
5
6
|
resolveSessionCompactPolicy,
|
|
6
7
|
summarizeContextMessages,
|
|
8
|
+
toolSchemaSignature,
|
|
7
9
|
} from '../runtime/agent/orchestrator/session/context-utils.mjs';
|
|
8
10
|
import { estimateToolSchemaBreakdown } from './tool-catalog.mjs';
|
|
9
11
|
|
|
@@ -16,7 +18,7 @@ export function createContextStatus({ getSession, getRoute, getCurrentCwd, getMo
|
|
|
16
18
|
let contextStatusCacheKey = null;
|
|
17
19
|
let contextStatusCacheValue = null;
|
|
18
20
|
|
|
19
|
-
function contextStatusCacheKeyFor({ messages, tools }) {
|
|
21
|
+
function contextStatusCacheKeyFor({ messages, tools, messagesRevision, toolsSignature }) {
|
|
20
22
|
const session = getSession();
|
|
21
23
|
const route = getRoute();
|
|
22
24
|
const compaction = session?.compaction || {};
|
|
@@ -30,11 +32,13 @@ export function createContextStatus({ getSession, getRoute, getCurrentCwd, getMo
|
|
|
30
32
|
mode: getMode(),
|
|
31
33
|
messages,
|
|
32
34
|
messageCount: messages.length,
|
|
35
|
+
messagesRevision,
|
|
33
36
|
lastMessage,
|
|
34
37
|
lastMessageRole: lastMessage?.role || null,
|
|
35
38
|
lastMessageContent: lastMessage?.content || null,
|
|
36
39
|
tools,
|
|
37
40
|
toolCount: tools.length,
|
|
41
|
+
toolsSignature,
|
|
38
42
|
contextWindow: session?.contextWindow || null,
|
|
39
43
|
rawContextWindow: session?.rawContextWindow || null,
|
|
40
44
|
effectiveContextWindowPercent: session?.effectiveContextWindowPercent || null,
|
|
@@ -85,7 +89,9 @@ export function createContextStatus({ getSession, getRoute, getCurrentCwd, getMo
|
|
|
85
89
|
const liveTurnMessages = Array.isArray(session?.liveTurnMessages) ? session.liveTurnMessages : null;
|
|
86
90
|
const messages = liveTurnMessages || (Array.isArray(session?.messages) ? session.messages : []);
|
|
87
91
|
const tools = Array.isArray(session?.tools) ? session.tools : [];
|
|
88
|
-
const
|
|
92
|
+
const messagesRevision = contextMessagesRevision(messages);
|
|
93
|
+
const toolsSignature = toolSchemaSignature(tools);
|
|
94
|
+
const cacheKey = contextStatusCacheKeyFor({ messages, tools, messagesRevision, toolsSignature });
|
|
89
95
|
if (contextStatusCacheValue && sameContextStatusCacheKey(cacheKey, contextStatusCacheKey)) {
|
|
90
96
|
return contextStatusCacheValue;
|
|
91
97
|
}
|
|
@@ -37,6 +37,8 @@ export function abnormalEmptyFinishError(result, agent) {
|
|
|
37
37
|
// Only tagged for PUBLIC agents (hidden agents legitimately emit empty
|
|
38
38
|
// terminal turns and are left untagged by the loop).
|
|
39
39
|
return `agent '${agent}' finished without a final answer (stopReason=${stopReason ?? 'none'}, ${iterations} iterations, ${toolCallsTotal} tool calls)`;
|
|
40
|
+
case 'refusal':
|
|
41
|
+
return `agent '${agent}' response was refused by the safety classifier (${iterations} iterations, ${toolCallsTotal} tool calls)`;
|
|
40
42
|
default:
|
|
41
43
|
return null;
|
|
42
44
|
}
|