mixdog 0.9.22 → 0.9.24
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/README.md +1 -4
- package/package.json +1 -1
- package/scripts/boot-smoke.mjs +1 -1
- package/scripts/channel-daemon-smoke.mjs +483 -0
- package/scripts/channel-daemon-stub.mjs +80 -0
- package/scripts/debounced-skills-async-save-test.mjs +57 -0
- package/scripts/explore-bench-tmp.mjs +17 -0
- package/scripts/find-fuzzy-hidden-test.mjs +145 -0
- package/scripts/mcp-grace-deferred-test.mjs +149 -0
- package/scripts/statusline-quota-hysteresis-test.mjs +37 -0
- package/scripts/tool-smoke.mjs +68 -47
- package/src/rules/agent/30-explorer.md +6 -0
- package/src/rules/shared/01-tool.md +11 -4
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +76 -1
- package/src/runtime/agent/orchestrator/config.mjs +33 -7
- package/src/runtime/agent/orchestrator/context/collect.mjs +43 -8
- package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +226 -0
- package/src/runtime/agent/orchestrator/mcp/client.mjs +184 -33
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +38 -1
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +39 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +5 -3
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +24 -7
- package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +3 -2
- package/src/runtime/agent/orchestrator/session/loop/deferred-call-through.mjs +6 -3
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +37 -4
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +11 -1
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +14 -3
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +40 -6
- package/src/runtime/agent/orchestrator/session/store.mjs +30 -14
- package/src/runtime/agent/orchestrator/session/tool-batch.mjs +2 -1
- package/src/runtime/agent/orchestrator/tools/bash-session.mjs +13 -5
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +62 -24
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +7 -6
- package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +20 -0
- package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +34 -3
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +220 -27
- package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +61 -0
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +67 -27
- package/src/runtime/agent/orchestrator/tools/builtin/shell-runtime.mjs +6 -5
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +54 -5
- package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +97 -54
- package/src/runtime/agent/orchestrator/tools/patch/paths.mjs +49 -31
- package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +35 -2
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +126 -22
- package/src/runtime/channels/lib/crash-log.mjs +4 -2
- package/src/runtime/channels/lib/output-forwarder.mjs +2 -1
- package/src/runtime/channels/lib/owned-runtime.mjs +67 -223
- package/src/runtime/channels/lib/owner-heartbeat.mjs +9 -62
- package/src/runtime/channels/lib/parent-bridge.mjs +16 -1
- package/src/runtime/channels/lib/runtime-paths.mjs +32 -113
- package/src/runtime/channels/lib/session-discovery.mjs +2 -1
- package/src/runtime/channels/lib/tool-dispatch.mjs +22 -14
- package/src/runtime/channels/lib/tool-format.mjs +7 -2
- package/src/runtime/channels/lib/worker-main.mjs +42 -30
- package/src/runtime/memory/index.mjs +54 -7
- package/src/runtime/memory/lib/embedding-warmup.mjs +7 -0
- package/src/runtime/memory/lib/pg/process.mjs +85 -40
- package/src/runtime/memory/lib/pg/supervisor.mjs +82 -17
- package/src/runtime/memory/lib/query-handlers.mjs +4 -1
- package/src/runtime/memory/lib/recall-format.mjs +7 -3
- package/src/runtime/memory/tool-defs.mjs +1 -1
- package/src/runtime/shared/atomic-file.mjs +150 -6
- package/src/runtime/shared/background-tasks.mjs +1 -1
- package/src/runtime/shared/config.mjs +53 -1
- package/src/runtime/shared/tool-execution-contract.mjs +1 -1
- package/src/runtime/shared/tool-primitives.mjs +31 -1
- package/src/runtime/shared/tool-surface.mjs +42 -13
- package/src/runtime/shared/update-checker.mjs +3 -0
- package/src/runtime/shared/user-data-guard.mjs +66 -0
- package/src/session-runtime/config-lifecycle.mjs +221 -20
- package/src/session-runtime/lifecycle-api.mjs +9 -0
- package/src/session-runtime/mcp-glue.mjs +93 -1
- package/src/session-runtime/resource-api.mjs +62 -8
- package/src/session-runtime/runtime-core.mjs +118 -4
- package/src/session-runtime/session-text.mjs +41 -0
- package/src/session-runtime/session-turn-api.mjs +50 -0
- package/src/session-runtime/settings-api.mjs +8 -1
- package/src/session-runtime/tool-catalog.mjs +350 -38
- package/src/session-runtime/tool-defs.mjs +7 -7
- package/src/session-runtime/workflow.mjs +2 -1
- package/src/standalone/channel-admin.mjs +32 -3
- package/src/standalone/channel-daemon-client.mjs +226 -0
- package/src/standalone/channel-daemon-transport.mjs +545 -0
- package/src/standalone/channel-daemon.mjs +176 -0
- package/src/standalone/channel-worker.mjs +224 -4
- package/src/standalone/explore-tool.mjs +87 -15
- package/src/standalone/hook-bus.mjs +71 -3
- package/src/tui/App.jsx +107 -19
- package/src/tui/app/clipboard.mjs +39 -19
- package/src/tui/app/doctor.mjs +57 -0
- package/src/tui/app/extension-pickers.mjs +53 -9
- package/src/tui/app/maintenance-pickers.mjs +0 -5
- package/src/tui/app/slash-dispatch.mjs +4 -4
- package/src/tui/app/text-layout.mjs +11 -0
- package/src/tui/app/use-mouse-input.mjs +235 -51
- package/src/tui/app/use-prompt-handlers.mjs +49 -30
- package/src/tui/app/use-transcript-scroll.mjs +124 -27
- package/src/tui/app/use-transcript-window.mjs +55 -1
- package/src/tui/components/Message.jsx +1 -1
- package/src/tui/components/PromptInput.jsx +3 -1
- package/src/tui/components/QueuedCommands.jsx +21 -10
- package/src/tui/components/StatusLine.jsx +3 -3
- package/src/tui/components/ToolExecution.jsx +16 -4
- package/src/tui/components/TranscriptItem.jsx +1 -1
- package/src/tui/dist/index.mjs +820 -326
- package/src/tui/engine/agent-job-feed.mjs +5 -0
- package/src/tui/engine/notification-plan.mjs +5 -0
- package/src/tui/engine/session-api.mjs +23 -8
- package/src/tui/engine/session-flow.mjs +6 -0
- package/src/tui/engine/tool-card-results.mjs +14 -5
- package/src/tui/engine/turn.mjs +9 -2
- package/src/tui/engine.mjs +32 -5
- package/src/tui/index.jsx +62 -18
- package/src/tui/paste-attachments.mjs +26 -0
- package/src/ui/statusline-agents.mjs +36 -0
- package/src/ui/statusline.mjs +60 -10
- package/src/ui/tool-card.mjs +8 -1
- package/src/vendor/statusline/bin/statusline-route.mjs +23 -7
- package/src/workflows/bench/WORKFLOW.md +46 -0
- package/src/workflows/default/WORKFLOW.md +6 -0
- package/src/workflows/solo/WORKFLOW.md +5 -0
- package/vendor/ink/build/ink.js +23 -1
- package/vendor/ink/build/output.js +154 -71
- package/vendor/ink/build/render-node-to-output.js +44 -2
- package/vendor/ink/build/render.js +4 -0
- package/vendor/ink/build/renderer.js +4 -1
package/src/ui/statusline.mjs
CHANGED
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
} from './statusline-format.mjs';
|
|
24
24
|
import { shellJobsStatus, memoryCycleStatus } from './statusline-segments.mjs';
|
|
25
25
|
import {
|
|
26
|
-
summarizeWorkerTags, agentStatuslinePayload, classifyAgentWorkers, activeHiddenAgentWorkers,
|
|
26
|
+
summarizeWorkerTags, agentStatuslinePayload, classifyAgentWorkers, activeHiddenAgentWorkers, agentWebSearchStatus,
|
|
27
27
|
} from './statusline-agents.mjs';
|
|
28
28
|
export { createSessionStats, applyUsageDelta } from './session-stats.mjs';
|
|
29
29
|
// Facade re-exports: keep these public symbols resolving from statusline.mjs.
|
|
@@ -90,6 +90,28 @@ function rememberNonEmptyQuotaSegments(key, segments) {
|
|
|
90
90
|
_lastNonEmptyQuotaSegmentsByKey.delete(oldestKey);
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
|
+
|
|
94
|
+
// Monotonic hysteresis for the quota/usage segment. Once a value has rendered
|
|
95
|
+
// (held), a new non-empty result replaces it ONLY when it is at least as fresh
|
|
96
|
+
// or is confirmed own-instance live data. This stops the 5H/7D values flapping
|
|
97
|
+
// when another mixdog instance overwrites the shared active-instance/usage cache
|
|
98
|
+
// with an OLDER snapshot: metricsMatch flips false, the source alternates to a
|
|
99
|
+
// provider-wide cache snapshot captured at a different time, and without this
|
|
100
|
+
// gate the two sources would oscillate tick-to-tick.
|
|
101
|
+
// - nothing displayed yet .................. accept
|
|
102
|
+
// - incoming is own-instance live data ..... accept (always wins)
|
|
103
|
+
// - either side lacks a comparable asOf .... accept (preserves prior behavior)
|
|
104
|
+
// - displayed value is own live data ....... accept only a STRICTLY newer snapshot
|
|
105
|
+
// - both shared-cache snapshots ............ accept same-or-newer asOf
|
|
106
|
+
export function acceptQuotaSnapshot(held, incoming) {
|
|
107
|
+
if (!held) return true;
|
|
108
|
+
if (incoming && incoming.owned) return true;
|
|
109
|
+
const incomingAsOf = num(incoming && incoming.asOf);
|
|
110
|
+
const heldAsOf = num(held.asOf);
|
|
111
|
+
if (!incomingAsOf || !heldAsOf) return true;
|
|
112
|
+
if (held.owned) return incomingAsOf > heldAsOf;
|
|
113
|
+
return incomingAsOf >= heldAsOf;
|
|
114
|
+
}
|
|
93
115
|
// Option A boot gate: the L1 usage/quota segment stays fully empty until THIS
|
|
94
116
|
// process has captured its FIRST confirmed (current-process) OAuth usage
|
|
95
117
|
// snapshot for THAT provider. The latch is monotonic PER PROVIDER — once a
|
|
@@ -310,10 +332,21 @@ function renderNativeStatusline({
|
|
|
310
332
|
if (usageReady && _oauthUsageArmedProviders.has(normalizedHoldProvider)) {
|
|
311
333
|
const holdKey = quotaSegmentsHoldKey({ provider, model, effort, fast, sessionId, clientHostPid });
|
|
312
334
|
if (quotaSegments.length) {
|
|
313
|
-
|
|
335
|
+
// Monotonic replace: keep the currently displayed value unless the new
|
|
336
|
+
// one is same-or-newer, or is confirmed own-instance live data.
|
|
337
|
+
const incoming = {
|
|
338
|
+
asOf: num(quotaStatus?.quotaWindowsAsOf),
|
|
339
|
+
owned: quotaStatus?.quotaWindowsOwned === true,
|
|
340
|
+
};
|
|
341
|
+
const held = _lastNonEmptyQuotaSegmentsByKey.get(holdKey);
|
|
342
|
+
if (acceptQuotaSnapshot(held, incoming)) {
|
|
343
|
+
rememberNonEmptyQuotaSegments(holdKey, { segments: quotaSegments, asOf: incoming.asOf, owned: incoming.owned });
|
|
344
|
+
} else if (held?.segments?.length) {
|
|
345
|
+
quotaSegments = held.segments;
|
|
346
|
+
}
|
|
314
347
|
} else {
|
|
315
348
|
const held = _lastNonEmptyQuotaSegmentsByKey.get(holdKey);
|
|
316
|
-
if (held
|
|
349
|
+
if (held?.segments?.length) quotaSegments = held.segments;
|
|
317
350
|
}
|
|
318
351
|
}
|
|
319
352
|
for (const seg of quotaSegments) addL1(seg);
|
|
@@ -329,7 +362,9 @@ function renderNativeStatusline({
|
|
|
329
362
|
const sp = l2SpinnerFrame(spinnerNow);
|
|
330
363
|
const spin = `${GRN}${sp}${R}`;
|
|
331
364
|
const elapsedSuffix = (label) => (label ? ` ${D}·${R} ${label}` : '');
|
|
332
|
-
// Segment order: Running Agents → Exploring → Searching → Running Shells.
|
|
365
|
+
// Segment order: Running Agents → Exploring → Web Searching → Running Shells.
|
|
366
|
+
// (activeTools.search counts WEB searches — category 'Web Research' — not
|
|
367
|
+
// local file search, which is intentionally not surfaced.)
|
|
333
368
|
if (runningWorkers.length) {
|
|
334
369
|
const n = runningWorkers.length;
|
|
335
370
|
const label = `Running ${n} Agent${n === 1 ? '' : 's'}`;
|
|
@@ -349,9 +384,17 @@ function renderNativeStatusline({
|
|
|
349
384
|
const elapsed = num(exploreInfo.startedAt) > 0 ? formatElapsed(Date.now() - num(exploreInfo.startedAt)) : '';
|
|
350
385
|
addL2(`${spin} ${B}Exploring${R}${elapsedSuffix(elapsed)}`);
|
|
351
386
|
}
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
387
|
+
// Web Searching = lead's own web searches (activeTools.search) PLUS any
|
|
388
|
+
// spawned agent sub-session whose current tool call is a web search
|
|
389
|
+
// (agentWebSearchStatus reads the live session-runtime map). Earliest start
|
|
390
|
+
// wins for the elapsed label.
|
|
391
|
+
const agentSearch = agentWebSearchStatus({ sessionId, clientHostPid });
|
|
392
|
+
const webSearchCount = (searchInfo ? num(searchInfo.count) : 0) + num(agentSearch.count);
|
|
393
|
+
if (webSearchCount > 0) {
|
|
394
|
+
const starts = [searchInfo ? num(searchInfo.startedAt) : 0, num(agentSearch.startedAt)].filter((v) => v > 0);
|
|
395
|
+
const searchStart = starts.length ? Math.min(...starts) : 0;
|
|
396
|
+
const elapsed = searchStart > 0 ? formatElapsed(Date.now() - searchStart) : '';
|
|
397
|
+
addL2(`${spin} ${B}Web Searching${R}${elapsedSuffix(elapsed)}`);
|
|
355
398
|
}
|
|
356
399
|
if (shellStatus.count > 0) {
|
|
357
400
|
const n = shellStatus.count;
|
|
@@ -530,6 +573,10 @@ function fallbackQuotaStatus({ provider, model } = {}) {
|
|
|
530
573
|
value = {
|
|
531
574
|
...routeInfo,
|
|
532
575
|
quotaWindows: limits.quotaWindows || [],
|
|
576
|
+
// Shared provider-wide OAuth usage cache snapshot: not owned by
|
|
577
|
+
// this instance. asOf = the snapshot's cachedAt for hysteresis.
|
|
578
|
+
quotaWindowsAsOf: num(usageSnapshot?.cachedAt),
|
|
579
|
+
quotaWindowsOwned: false,
|
|
533
580
|
balance: limits.balance || null,
|
|
534
581
|
routeSpend: limits.routeSpend || null,
|
|
535
582
|
};
|
|
@@ -556,12 +603,15 @@ function providerKindForQuota(provider) {
|
|
|
556
603
|
function mergeQuotaStatus(primary, fallback) {
|
|
557
604
|
if (!primary) return fallback || null;
|
|
558
605
|
if (!fallback) return primary;
|
|
606
|
+
const usePrimaryWindows = Array.isArray(primary.quotaWindows) && primary.quotaWindows.length;
|
|
559
607
|
return {
|
|
560
608
|
...fallback,
|
|
561
609
|
...primary,
|
|
562
|
-
quotaWindows:
|
|
563
|
-
|
|
564
|
-
|
|
610
|
+
quotaWindows: usePrimaryWindows ? primary.quotaWindows : (fallback.quotaWindows || []),
|
|
611
|
+
// Keep asOf/owned aligned with whichever windows won, so the hysteresis gate
|
|
612
|
+
// compares against the timestamp of the value actually being rendered.
|
|
613
|
+
quotaWindowsAsOf: usePrimaryWindows ? num(primary.quotaWindowsAsOf) : num(fallback.quotaWindowsAsOf),
|
|
614
|
+
quotaWindowsOwned: usePrimaryWindows ? primary.quotaWindowsOwned === true : fallback.quotaWindowsOwned === true,
|
|
565
615
|
balance: primary.balance || fallback.balance || null,
|
|
566
616
|
routeSpend: primary.routeSpend || fallback.routeSpend || null,
|
|
567
617
|
providerKind: primary.providerKind || fallback.providerKind || providerKindForQuota(primary.provider || fallback.provider),
|
package/src/ui/tool-card.mjs
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* malformed argument objects (the engine hands us `{ name, arguments, id }`).
|
|
10
10
|
*/
|
|
11
11
|
import { bold, cyan, gray } from './ansi.mjs';
|
|
12
|
+
import { parseMcpToolName, isSelfMcpToolName } from '../runtime/shared/tool-primitives.mjs';
|
|
12
13
|
|
|
13
14
|
const MAX_SUMMARY = 72;
|
|
14
15
|
|
|
@@ -38,13 +39,19 @@ export function renderToolCard(call) {
|
|
|
38
39
|
|
|
39
40
|
const summary = safeSummary(name, args);
|
|
40
41
|
const bullet = cyan('>');
|
|
41
|
-
const label = bold(name);
|
|
42
|
+
const label = bold(mcpCardLabel(name));
|
|
42
43
|
if (!summary) return ` ${bullet} ${label}`;
|
|
43
44
|
return ` ${bullet} ${label} ${gray(truncate(summary, MAX_SUMMARY))}`;
|
|
44
45
|
}
|
|
45
46
|
|
|
46
47
|
// --- helpers -----------------------------------------------------------------
|
|
47
48
|
|
|
49
|
+
function mcpCardLabel(name) {
|
|
50
|
+
const mcp = parseMcpToolName(name);
|
|
51
|
+
if (!mcp || isSelfMcpToolName(name)) return name;
|
|
52
|
+
return `MCP ${mcp.server}.${mcp.tool}`;
|
|
53
|
+
}
|
|
54
|
+
|
|
48
55
|
function safeSummary(name, args) {
|
|
49
56
|
try {
|
|
50
57
|
const fn = SUMMARIZERS[name];
|
|
@@ -343,27 +343,30 @@ function cachedQuotaWindowsFallback(provider, model) {
|
|
|
343
343
|
// OAuth quota is provider/account-scoped, not model-scoped. Prefer the
|
|
344
344
|
// current route key, then provider-wide cache, then the freshest same-provider
|
|
345
345
|
// route entry so a model switch keeps showing quota before the next live fetch.
|
|
346
|
-
|
|
346
|
+
// Returns the chosen entry's cachedAt alongside the windows so the caller can
|
|
347
|
+
// apply monotonic hysteresis (never displace a displayed value with an older
|
|
348
|
+
// shared-cache snapshot written by a different instance).
|
|
349
|
+
if (!provider || !model) return { quotaWindows: [], cachedAt: 0 };
|
|
347
350
|
try {
|
|
348
351
|
const cachePath = path.join(pluginDataDir(), 'gateway-oauth-usage-cache.json');
|
|
349
352
|
const cache = readJson(cachePath);
|
|
350
353
|
const routes = cache && typeof cache.routes === 'object' ? cache.routes : null;
|
|
351
|
-
if (!routes) return [];
|
|
354
|
+
if (!routes) return { quotaWindows: [], cachedAt: 0 };
|
|
352
355
|
const routeKey = `${String(provider).toLowerCase()}${String(model)}`;
|
|
353
356
|
const providerOnlyKey = String(provider).toLowerCase();
|
|
354
357
|
const routePrefix = `${providerOnlyKey}`;
|
|
355
358
|
const entry = routes[routeKey] || routes[providerOnlyKey] || Object.entries(routes)
|
|
356
359
|
.filter(([key, value]) => key.startsWith(routePrefix) && Array.isArray(value?.quotaWindows))
|
|
357
360
|
.sort((a, b) => (Number(b[1]?.cachedAt) || 0) - (Number(a[1]?.cachedAt) || 0))[0]?.[1];
|
|
358
|
-
if (!Array.isArray(entry?.quotaWindows)) return [];
|
|
361
|
+
if (!Array.isArray(entry?.quotaWindows)) return { quotaWindows: [], cachedAt: 0 };
|
|
359
362
|
// Boot guard: do not render previous-launch usage before the current runtime
|
|
360
363
|
// has captured at least one snapshot. Once captured in this process, hold it
|
|
361
364
|
// instead of blanking it during idle/network gaps.
|
|
362
365
|
const cachedAt = Number(entry.cachedAt);
|
|
363
|
-
if (!Number.isFinite(cachedAt) || cachedAt < STATUSLINE_PROCESS_STARTED_AT_MS) return [];
|
|
364
|
-
return entry.quotaWindows;
|
|
366
|
+
if (!Number.isFinite(cachedAt) || cachedAt < STATUSLINE_PROCESS_STARTED_AT_MS) return { quotaWindows: [], cachedAt: 0 };
|
|
367
|
+
return { quotaWindows: entry.quotaWindows, cachedAt };
|
|
365
368
|
} catch {
|
|
366
|
-
return [];
|
|
369
|
+
return { quotaWindows: [], cachedAt: 0 };
|
|
367
370
|
}
|
|
368
371
|
}
|
|
369
372
|
|
|
@@ -499,11 +502,18 @@ function configuredGatewayStatus(options = {}) {
|
|
|
499
502
|
|
|
500
503
|
export function loadGatewayStatus(options = {}) {
|
|
501
504
|
const configured = configuredGatewayStatus(options);
|
|
505
|
+
const quotaFallback = configured
|
|
506
|
+
? cachedQuotaWindowsFallback(configured.provider, configured.model)
|
|
507
|
+
: { quotaWindows: [], cachedAt: 0 };
|
|
502
508
|
const configuredStatus = configured ? {
|
|
503
509
|
...configured,
|
|
504
510
|
contextUsedPct: pctOf(options.activeContextTokens, compactBoundaryForStatus(configured)),
|
|
505
511
|
lastUsage: null,
|
|
506
|
-
quotaWindows:
|
|
512
|
+
quotaWindows: quotaFallback.quotaWindows,
|
|
513
|
+
// Shared provider-wide cache snapshot: not owned by this instance. asOf is
|
|
514
|
+
// the cache entry's cachedAt so an older snapshot can be rejected downstream.
|
|
515
|
+
quotaWindowsAsOf: num(quotaFallback.cachedAt, 0),
|
|
516
|
+
quotaWindowsOwned: false,
|
|
507
517
|
balance: null,
|
|
508
518
|
routeSpend: null,
|
|
509
519
|
} : null;
|
|
@@ -609,6 +619,12 @@ export function loadGatewayStatus(options = {}) {
|
|
|
609
619
|
: null,
|
|
610
620
|
lastUsage,
|
|
611
621
|
quotaWindows: activeQuotaWindows.length ? activeQuotaWindows : (configuredStatus?.quotaWindows || []),
|
|
622
|
+
// Provenance for monotonic hysteresis: live active-instance windows are
|
|
623
|
+
// own-instance data timestamped by the advert's updatedAt; otherwise inherit
|
|
624
|
+
// the shared-cache fallback's (unowned) asOf. Older/unowned snapshots must
|
|
625
|
+
// not displace a value already rendered from newer/owned data.
|
|
626
|
+
quotaWindowsAsOf: activeQuotaWindows.length ? (updatedAt || 0) : num(configuredStatus?.quotaWindowsAsOf, 0),
|
|
627
|
+
quotaWindowsOwned: activeQuotaWindows.length ? metricsMatch : false,
|
|
612
628
|
balance: metricsMatch && active.gateway_balance && typeof active.gateway_balance === 'object' ? active.gateway_balance : (configuredStatus?.balance || null),
|
|
613
629
|
routeSpend: metricsMatch && active.gateway_route_spend && typeof active.gateway_route_spend === 'object' ? active.gateway_route_spend : (configuredStatus?.routeSpend || null),
|
|
614
630
|
providerKind: statusProviderKind,
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: bench
|
|
3
|
+
name: Bench
|
|
4
|
+
description: "Autonomous benchmark workflow — loop to completion without user approval."
|
|
5
|
+
hidden: true
|
|
6
|
+
agents: worker, heavy-worker, reviewer, debugger, maintainer
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Bench Workflow
|
|
10
|
+
|
|
11
|
+
Autonomous run: no user is present. Never wait for approval or ask
|
|
12
|
+
questions — decide and proceed immediately. Loop until the task is verified
|
|
13
|
+
complete or provably blocked.
|
|
14
|
+
|
|
15
|
+
Lead supervises: delegates, coordinates, judges, decides. Route by complexity:
|
|
16
|
+
- Lead directly: simple 1–2 step work, plus coordination, pre-planning, config
|
|
17
|
+
changes, and final git deployment.
|
|
18
|
+
- Worker: implementation that takes several steps. Heavy Worker:
|
|
19
|
+
high-complexity, multi-step implementation.
|
|
20
|
+
- Reviewer: verify implementation scopes (diff, regressions, missing checks).
|
|
21
|
+
- Debugger: very high complexity, or when root-causing has already failed at
|
|
22
|
+
least once.
|
|
23
|
+
|
|
24
|
+
1. Plan — form the plan yourself and start executing immediately; no approval
|
|
25
|
+
step exists.
|
|
26
|
+
2. Delegate — split into the maximum number of independent scopes.
|
|
27
|
+
- PARALLEL across independent scopes by default (implementation, analysis,
|
|
28
|
+
review, debugging alike); spawn every scope in the SAME turn. Shared or
|
|
29
|
+
cross-cutting code does NOT justify merging — split per path and verify
|
|
30
|
+
the shared parts yourself. The only single scope is a genuinely
|
|
31
|
+
inseparable dependency; then state it.
|
|
32
|
+
- SEQUENTIAL within a single complex scope: ordered steps with a
|
|
33
|
+
build/test-green gate between them, not one shot.
|
|
34
|
+
- Write briefs per the Lead brief contract.
|
|
35
|
+
- After spawning async agents, END THE TURN — no polling, guessing, or
|
|
36
|
+
dependent work until the completion notification resumes you.
|
|
37
|
+
3. Review — pair one reviewer 1:1 with each implementation scope, spawned in the
|
|
38
|
+
same turn, never deferred or batched; wait for its result. Fact-check agent
|
|
39
|
+
responses and cross-check implementation and review results yourself before
|
|
40
|
+
acting. Send fixes back to the original scope and loop verify -> fix ->
|
|
41
|
+
re-verify until clean. Skip review only for simple, low-risk tasks. If a bug
|
|
42
|
+
survives 2+ fix cycles, have the debugger investigate first instead of
|
|
43
|
+
another fix round.
|
|
44
|
+
4. Finish — verify the final state yourself; only stop when the task is
|
|
45
|
+
complete and verified, or a hard blocker makes progress impossible. State
|
|
46
|
+
the outcome and evidence in the final message.
|
|
@@ -7,7 +7,13 @@ agents: worker, heavy-worker, reviewer, debugger, maintainer
|
|
|
7
7
|
|
|
8
8
|
# Default Workflow
|
|
9
9
|
|
|
10
|
+
HARD APPROVAL GATE — before the user gives an explicit go-ahead ("do it",
|
|
11
|
+
"proceed", "ㄱㄱ"), NO edits, apply_patch, state-changing shell commands, or
|
|
12
|
+
agent spawns. Read-only exploration only. Agreeing with a diagnosis or
|
|
13
|
+
pointing out a problem is NOT approval.
|
|
14
|
+
|
|
10
15
|
Lead supervises: delegates, coordinates, judges, decides. Route by complexity:
|
|
16
|
+
(routing applies only AFTER approval)
|
|
11
17
|
- Lead directly: simple 1–2 step work, plus coordination, pre-planning, config
|
|
12
18
|
changes, and final git deployment.
|
|
13
19
|
- Worker: implementation that takes several steps. Heavy Worker:
|
|
@@ -7,6 +7,11 @@ agents:
|
|
|
7
7
|
|
|
8
8
|
# Solo Workflow
|
|
9
9
|
|
|
10
|
+
HARD APPROVAL GATE — before the user gives an explicit go-ahead ("do it",
|
|
11
|
+
"proceed", "ㄱㄱ"), NO edits, apply_patch, or state-changing shell commands.
|
|
12
|
+
Read-only exploration only. Agreeing with a diagnosis or pointing out a
|
|
13
|
+
problem is NOT approval.
|
|
14
|
+
|
|
10
15
|
1. Plan — Lead discusses the request with the user, forms a plan, and waits for
|
|
11
16
|
approval before execution.
|
|
12
17
|
Only an explicit go-ahead is approval; diagnosis agreement is not. When
|
package/vendor/ink/build/ink.js
CHANGED
|
@@ -225,6 +225,10 @@ export default class Ink {
|
|
|
225
225
|
// refreshed every render; read back via getSelectionRows() so the app can
|
|
226
226
|
// stitch selections taller than the viewport. null when no selection.
|
|
227
227
|
lastSelectionRows = null;
|
|
228
|
+
// [mixdog fork] One-shot flag: the next interactive frame takes the full
|
|
229
|
+
// clearTerminal+rewrite path even when nothing changed. Set via
|
|
230
|
+
// forceFullRepaint(); consumed (and reset) in renderInteractiveFrame.
|
|
231
|
+
forceClearNextFrame = false;
|
|
228
232
|
constructor(options) {
|
|
229
233
|
autoBind(this);
|
|
230
234
|
this.options = options;
|
|
@@ -569,6 +573,19 @@ export default class Ink {
|
|
|
569
573
|
getSelectionRows = () => {
|
|
570
574
|
return this.lastSelectionRows;
|
|
571
575
|
};
|
|
576
|
+
// [mixdog fork] Force a one-shot whole-screen clear + rewrite. Windows
|
|
577
|
+
// Terminal keeps its NATIVE (shift+drag) selection overlay alive across
|
|
578
|
+
// incremental in-place repaints, so a stale native highlight can sit on top
|
|
579
|
+
// of the app-drawn selection; a real clearTerminal+full rewrite dismisses
|
|
580
|
+
// it. The frame is BSU/ESU-wrapped by renderInteractiveFrame, so there is
|
|
581
|
+
// no visible flash. Called by the app on mouse-press via the render handle.
|
|
582
|
+
forceFullRepaint = () => {
|
|
583
|
+
if (this.isUnmounted || this.isUnmounting) {
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
this.forceClearNextFrame = true;
|
|
587
|
+
this.rootNode.onImmediateRender();
|
|
588
|
+
};
|
|
572
589
|
restoreLastOutput = () => {
|
|
573
590
|
if (!this.interactive) {
|
|
574
591
|
return;
|
|
@@ -1115,7 +1132,11 @@ export default class Ink {
|
|
|
1115
1132
|
if (isFullscreen && outputToRender.endsWith('\n')) {
|
|
1116
1133
|
outputToRender += '\u001B[0m';
|
|
1117
1134
|
}
|
|
1118
|
-
|
|
1135
|
+
// [mixdog fork] One-shot forced clear (see forceFullRepaint): TTY-only,
|
|
1136
|
+
// consumed here so it applies to exactly one frame.
|
|
1137
|
+
const forcedClear = this.forceClearNextFrame && isTty;
|
|
1138
|
+
this.forceClearNextFrame = false;
|
|
1139
|
+
const shouldClearTerminal = forcedClear || shouldClearTerminalForFrame({
|
|
1119
1140
|
isTty,
|
|
1120
1141
|
viewportRows,
|
|
1121
1142
|
previousViewportRows,
|
|
@@ -1147,6 +1168,7 @@ export default class Ink {
|
|
|
1147
1168
|
// with identical output text but must still force a full clear to
|
|
1148
1169
|
// avoid ghosting stale cells left outside the new viewport.
|
|
1149
1170
|
if (shouldClearTerminal &&
|
|
1171
|
+
!forcedClear &&
|
|
1150
1172
|
!this.isUnmounting &&
|
|
1151
1173
|
output === this.lastOutput &&
|
|
1152
1174
|
staticOutput === '' &&
|