mixdog 0.9.86 → 0.9.87
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 -2
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +4 -2
- package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +125 -16
- package/src/runtime/agent/orchestrator/session/manager.mjs +3 -0
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +9 -2
- package/src/runtime/agent/orchestrator/tools/env-scrub.mjs +8 -0
- package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +4 -3
- package/src/runtime/agent/orchestrator/tools/token-manifest.json +23 -2
- package/src/runtime/memory/lib/memory-action-handlers.mjs +4 -1
- package/src/runtime/memory/tool-defs.mjs +19 -3
- package/src/session-runtime/lifecycle-api.mjs +15 -0
- package/src/session-runtime/provider-auth-api.mjs +8 -0
- package/src/session-runtime/provider-usage.mjs +30 -5
- package/src/session-runtime/runtime-core.mjs +267 -481
- package/src/session-runtime/session-lifecycle.mjs +378 -0
- package/src/standalone/agent-shard/shard-pool.mjs +6 -5
- package/src/standalone/agent-tool/job-views.mjs +329 -0
- package/src/standalone/agent-tool/spawn-flow.mjs +629 -0
- package/src/standalone/agent-tool/tag-registry.mjs +340 -0
- package/src/standalone/agent-tool.mjs +100 -1151
- package/src/standalone/usage-dashboard.mjs +23 -1
- package/src/tui/App.jsx +362 -2576
- package/src/tui/app/app-view.jsx +504 -0
- package/src/tui/app/create-app-pickers.mjs +313 -0
- package/src/tui/app/prompt-submit.mjs +501 -0
- package/src/tui/app/shell-layout.mjs +563 -0
- package/src/tui/app/usage-context-panels.mjs +288 -0
- package/src/tui/app/use-copy-selection.mjs +56 -0
- package/src/tui/app/use-global-key-input.mjs +151 -0
- package/src/tui/app/use-pasted-buffers.mjs +115 -0
- package/src/tui/app/use-prompt-draft-flow.mjs +167 -0
- package/src/tui/app/use-prompt-hint.mjs +58 -0
- package/src/tui/app/use-prompt-queue-history.mjs +100 -0
- package/src/tui/app/use-terminal-chrome.mjs +72 -0
- package/src/tui/app/use-transcript-activity.mjs +131 -0
- package/src/tui/app/use-welcome-prompt-hint.mjs +96 -0
- package/src/tui/dist/index.mjs +9802 -9024
- package/src/tui/engine/session-api-ext.mjs +23 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mixdog",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.87",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Standalone mixdog coding-agent CLI/TUI workspace.",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"test:tool-contracts": "node scripts/tool-smoke.mjs",
|
|
56
56
|
"smoke:patch": "node scripts/apply-patch-edit-smoke.mjs",
|
|
57
57
|
"smoke:output": "node scripts/output-style-smoke.mjs",
|
|
58
|
-
"smoke:tui": "node scripts/tui-render-smoke.mjs && npm run test:tui-input-render && npm run test:tui-streaming-window && npm run test:tui-queue",
|
|
58
|
+
"smoke:tui": "node scripts/build-tui.mjs && node scripts/tui-render-smoke.mjs && npm run test:tui-input-render && npm run test:tui-streaming-window && npm run test:tui-queue",
|
|
59
59
|
"smoke:freevars": "node scripts/freevar-smoke.mjs",
|
|
60
60
|
"smoke:logguard": "node scripts/log-writer-guard-smoke.mjs",
|
|
61
61
|
"smoke:live-worker": "node scripts/live-worker-smoke.mjs",
|
|
@@ -282,12 +282,14 @@ function classifyToolFailure(resultText, toolName) {
|
|
|
282
282
|
return 'command-exit';
|
|
283
283
|
}
|
|
284
284
|
if (/\[tool-input-validation\]|compacted-history placeholder/.test(text)) return 'schema/args';
|
|
285
|
-
if (/
|
|
285
|
+
if (/hunk rejected|patch failed|context mismatch|context not found/.test(text)
|
|
286
|
+
|| /expected first old(?:\/context| line)/.test(text)) return 'patch/context';
|
|
287
|
+
if (/requires either|invalid arguments|unknown parameter|unknown memory action/.test(text)
|
|
288
|
+
|| /must be|schema|required|old_string is .*>?=/.test(text)) return 'schema/args';
|
|
286
289
|
if (/not in allow-list|not allowed/.test(text)) return 'permission';
|
|
287
290
|
if (String(toolName || '') === 'shell' || /^\s*\[exit code:\s*\d+\]/i.test(raw)) return 'command-exit';
|
|
288
291
|
if (/enoent|cannot find|not found at this path|path does not exist|no such file|file not found in graph|unreadable/.test(text)) return 'path/enoent';
|
|
289
292
|
if (/timed out|timeout|interrupted|aborted/.test(text)) return 'timeout/abort';
|
|
290
|
-
if (/hunk rejected|patch failed|context mismatch|expected first old\/context|context not found/.test(text)) return 'patch/context';
|
|
291
293
|
if (/permission|denied|forbidden/.test(text)) return 'permission';
|
|
292
294
|
if (/unknown tool|tool.*not.*available|missing.*tool/.test(text)) return 'tool-surface';
|
|
293
295
|
return 'runtime/failure';
|
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
existsSync,
|
|
3
3
|
readFileSync,
|
|
4
4
|
} from 'fs';
|
|
5
|
+
import { createHash } from 'crypto';
|
|
5
6
|
import { join } from 'path';
|
|
6
7
|
import { updateJsonAtomicSync } from '../../../shared/atomic-file.mjs';
|
|
7
8
|
import { resolvePluginData } from '../../../shared/plugin-paths.mjs';
|
|
@@ -15,6 +16,8 @@ const STALE_DISK_CACHE_TTL_MS = 7 * 24 * 60 * 60_000;
|
|
|
15
16
|
const NEGATIVE_CACHE_TTL_MS = 5 * 60_000;
|
|
16
17
|
const FETCH_TIMEOUT_MS = 4500;
|
|
17
18
|
const WARN_TTL_MS = 5 * 60_000;
|
|
19
|
+
const CODEX_RESET_CREDITS_URL = 'https://chatgpt.com/backend-api/wham/rate-limit-reset-credits';
|
|
20
|
+
const CODEX_RESET_CONSUME_URL = `${CODEX_RESET_CREDITS_URL}/consume`;
|
|
18
21
|
|
|
19
22
|
const memoryCache = new Map();
|
|
20
23
|
const inflight = new Map();
|
|
@@ -199,6 +202,110 @@ function resetAtMs(value, fallbackSeconds = null) {
|
|
|
199
202
|
return secs > 0 ? Date.now() + secs * 1000 : null;
|
|
200
203
|
}
|
|
201
204
|
|
|
205
|
+
function codexAuthShape(auth) {
|
|
206
|
+
const token = auth?.access_token || auth?.accessToken;
|
|
207
|
+
if (!token) return null;
|
|
208
|
+
return {
|
|
209
|
+
token,
|
|
210
|
+
accountId: cleanString(auth?.account_id || auth?.accountId),
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function codexHeaders(auth, beta = 'codex-1') {
|
|
215
|
+
return {
|
|
216
|
+
Authorization: `Bearer ${auth.token}`,
|
|
217
|
+
originator: 'Codex Desktop',
|
|
218
|
+
...(auth.accountId ? { 'chatgpt-account-id': auth.accountId } : {}),
|
|
219
|
+
'OpenAI-Beta': beta,
|
|
220
|
+
Accept: 'application/json',
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function normalizedResetCreditRows(value) {
|
|
225
|
+
return (Array.isArray(value) ? value : []).map((credit) => ({
|
|
226
|
+
status: cleanString(credit?.status).toLowerCase(),
|
|
227
|
+
expiresAt: resetAtMs(credit?.expires_at ?? credit?.expiresAt),
|
|
228
|
+
grantedAt: resetAtMs(credit?.granted_at ?? credit?.grantedAt),
|
|
229
|
+
}));
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function normalizeOpenAICodexResetCredits(data, accountId = '') {
|
|
233
|
+
if (!data || typeof data !== 'object') return null;
|
|
234
|
+
const credits = normalizedResetCreditRows(data.credits);
|
|
235
|
+
const explicitCount = num(data.available_count ?? data.availableCount, null);
|
|
236
|
+
const availableRows = credits.filter((credit) => credit.status === 'available');
|
|
237
|
+
if (explicitCount === null && !credits.length) return null;
|
|
238
|
+
const availableCount = Math.max(0, Math.floor(explicitCount ?? availableRows.length));
|
|
239
|
+
const expiryCandidates = availableRows
|
|
240
|
+
.map((credit) => credit.expiresAt)
|
|
241
|
+
.filter((value) => Number.isFinite(value) && value > 0);
|
|
242
|
+
const nextExpiresAt = resetAtMs(data.next_expires_at ?? data.nextExpiresAt)
|
|
243
|
+
|| (expiryCandidates.length ? Math.min(...expiryCandidates) : null);
|
|
244
|
+
const offerRevision = `v1:${createHash('sha256').update(JSON.stringify({
|
|
245
|
+
accountId,
|
|
246
|
+
availableCount,
|
|
247
|
+
nextExpiresAt,
|
|
248
|
+
credits,
|
|
249
|
+
})).digest('hex')}`;
|
|
250
|
+
return {
|
|
251
|
+
availableCount,
|
|
252
|
+
...(nextExpiresAt ? { nextExpiresAt } : {}),
|
|
253
|
+
offerRevision,
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
async function resolveOpenAICodexAuth(providerObj) {
|
|
258
|
+
return codexAuthShape(await providerObj?.ensureAuth?.({ reason: 'usage' }));
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
async function fetchOpenAICodexResetCreditsWithAuth(auth) {
|
|
262
|
+
const response = await fetch(CODEX_RESET_CREDITS_URL, fetchOptions(codexHeaders(auth)));
|
|
263
|
+
if (!response.ok) return null;
|
|
264
|
+
return normalizeOpenAICodexResetCredits(await response.json(), auth.accountId);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export async function fetchOpenAICodexResetCredits(providerObj) {
|
|
268
|
+
const auth = await resolveOpenAICodexAuth(providerObj);
|
|
269
|
+
return auth ? await fetchOpenAICodexResetCreditsWithAuth(auth) : null;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function codexResetOutcome(code) {
|
|
273
|
+
if (code === 'reset') return 'reset';
|
|
274
|
+
if (code === 'nothing_to_reset') return 'nothingToReset';
|
|
275
|
+
if (code === 'no_credit') return 'noCredit';
|
|
276
|
+
if (code === 'already_redeemed') return 'alreadyRedeemed';
|
|
277
|
+
throw new Error(`Unknown Codex reset outcome: ${cleanString(code) || 'missing'}`);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export async function consumeOpenAICodexResetCredit(providerObj, options = {}) {
|
|
281
|
+
const expectedOfferRevision = cleanString(options?.expectedOfferRevision);
|
|
282
|
+
const idempotencyKey = cleanString(options?.idempotencyKey);
|
|
283
|
+
if (!/^v1:[a-f0-9]{64}$/i.test(expectedOfferRevision)) {
|
|
284
|
+
throw new TypeError('Codex reset offer revision is invalid');
|
|
285
|
+
}
|
|
286
|
+
if (!/^[a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$/i.test(idempotencyKey)) {
|
|
287
|
+
throw new TypeError('Codex reset idempotency key is invalid');
|
|
288
|
+
}
|
|
289
|
+
const auth = await resolveOpenAICodexAuth(providerObj);
|
|
290
|
+
if (!auth) throw new Error('Codex is not signed in');
|
|
291
|
+
const current = await fetchOpenAICodexResetCreditsWithAuth(auth);
|
|
292
|
+
if (!current || current.availableCount < 1 || current.offerRevision !== expectedOfferRevision) {
|
|
293
|
+
return { status: 'offerChanged', resetCredits: current };
|
|
294
|
+
}
|
|
295
|
+
const response = await fetch(CODEX_RESET_CONSUME_URL, {
|
|
296
|
+
...fetchOptions({
|
|
297
|
+
...codexHeaders(auth),
|
|
298
|
+
'Content-Type': 'application/json',
|
|
299
|
+
}, 15_000),
|
|
300
|
+
method: 'POST',
|
|
301
|
+
body: JSON.stringify({ redeem_request_id: idempotencyKey }),
|
|
302
|
+
});
|
|
303
|
+
if (!response.ok) throw new Error(`Codex reset failed: HTTP ${response.status}`);
|
|
304
|
+
const outcome = codexResetOutcome((await response.json())?.code);
|
|
305
|
+
const resetCredits = await fetchOpenAICodexResetCreditsWithAuth(auth).catch(() => null);
|
|
306
|
+
return { outcome, resetCredits };
|
|
307
|
+
}
|
|
308
|
+
|
|
202
309
|
function labelForDuration(seconds, fallback) {
|
|
203
310
|
const s = num(seconds, 0);
|
|
204
311
|
if (s > 0) {
|
|
@@ -482,19 +589,18 @@ function latestClaudeStatuslineUsage() {
|
|
|
482
589
|
}
|
|
483
590
|
|
|
484
591
|
async function fetchOpenAICodexUsage(providerObj) {
|
|
485
|
-
const auth = await providerObj
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
Accept: 'application/json',
|
|
494
|
-
}));
|
|
592
|
+
const auth = await resolveOpenAICodexAuth(providerObj);
|
|
593
|
+
if (!auth) return null;
|
|
594
|
+
const [res, resetCredits] = await Promise.all([
|
|
595
|
+
fetch('https://chatgpt.com/backend-api/wham/usage', fetchOptions(
|
|
596
|
+
codexHeaders(auth, 'responses=experimental'),
|
|
597
|
+
)),
|
|
598
|
+
fetchOpenAICodexResetCreditsWithAuth(auth).catch(() => null),
|
|
599
|
+
]);
|
|
495
600
|
if (!res.ok) throw new Error(`openai-oauth usage ${res.status}`);
|
|
496
601
|
const data = await res.json();
|
|
497
|
-
|
|
602
|
+
const usage = normalizeOpenAIWhamUsage(data);
|
|
603
|
+
return usage && resetCredits ? { ...usage, resetCredits } : usage;
|
|
498
604
|
}
|
|
499
605
|
|
|
500
606
|
async function fetchAnthropicUsage(providerObj) {
|
|
@@ -589,15 +695,18 @@ async function fetchGrokUsage(providerObj, routeInfo) {
|
|
|
589
695
|
return null;
|
|
590
696
|
}
|
|
591
697
|
|
|
592
|
-
export async function fetchOAuthUsageSnapshot(routeInfo, providerObj, log = () => {}) {
|
|
698
|
+
export async function fetchOAuthUsageSnapshot(routeInfo, providerObj, log = () => {}, options = {}) {
|
|
593
699
|
const provider = providerKey(routeInfo);
|
|
594
700
|
if (!provider.includes('oauth')) return null;
|
|
595
701
|
const key = routeKey(routeInfo);
|
|
596
702
|
const providerOnly = providerKey(routeInfo);
|
|
597
|
-
const
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
703
|
+
const force = options?.force === true;
|
|
704
|
+
if (!force) {
|
|
705
|
+
const cached = freshSnapshot(memoryCache.get(key), LIVE_CACHE_TTL_MS)
|
|
706
|
+
|| freshSnapshot(memoryCache.get(providerOnly), LIVE_CACHE_TTL_MS);
|
|
707
|
+
if (cached) return cached;
|
|
708
|
+
if (negativeFresh(key) || negativeFresh(providerOnly)) return null;
|
|
709
|
+
}
|
|
601
710
|
if (inflight.has(key)) return inflight.get(key);
|
|
602
711
|
|
|
603
712
|
const task = (async () => {
|
|
@@ -123,5 +123,8 @@ export {
|
|
|
123
123
|
flushSessionMetrics,
|
|
124
124
|
} from './manager/session-crud.mjs';
|
|
125
125
|
export { deleteSession } from './store.mjs';
|
|
126
|
+
// Read-only parsed-session access (desktop pane peek): no resume, no
|
|
127
|
+
// ownership, no liveness side effects.
|
|
128
|
+
export { loadSession } from './store.mjs';
|
|
126
129
|
export { closeSession, abortSessionTurn } from './manager/session-close.mjs';
|
|
127
130
|
export { sweepTombstones, startIdleCleanup, stopIdleCleanup } from './manager/idle-cleanup.mjs';
|
|
@@ -74,13 +74,20 @@ export const BUILTIN_TOOLS = [
|
|
|
74
74
|
name: 'shell',
|
|
75
75
|
title: 'Mixdog Shell',
|
|
76
76
|
annotations: { title: 'Mixdog Shell', readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true, compressible: true },
|
|
77
|
-
description:
|
|
77
|
+
description: 'Run programs/change state; not file inspection. Calls in one turn run parallel; '
|
|
78
|
+
+ 'combine order-dependent commands. Use async for sleep/watch/dev loops.'
|
|
79
|
+
+ `${_shellSyntaxCheat} ${TOOL_ASYNC_EXECUTION_CONTRACT}`,
|
|
78
80
|
inputSchema: {
|
|
79
81
|
type: 'object',
|
|
80
82
|
properties: {
|
|
81
83
|
command: { type: 'string', description: 'Command.' },
|
|
82
84
|
cwd: { type: 'string', description: 'Working directory; persists across calls. Omit to reuse; absolute path changes it.' },
|
|
83
|
-
timeout: {
|
|
85
|
+
timeout: {
|
|
86
|
+
type: 'number',
|
|
87
|
+
description: `Timeout ms; default ${_shellDefaultTimeoutMs()}. `
|
|
88
|
+
+ 'Sync timeout may return task_id; explicit timeout becomes its deadline. '
|
|
89
|
+
+ 'Sleeps are killed, not promoted.',
|
|
90
|
+
},
|
|
84
91
|
merge_stderr: { type: 'boolean', description: 'Merge stderr.' },
|
|
85
92
|
mode: { type: 'string', enum: ['sync', 'async'], description: executionModeSchemaDescription('sync') },
|
|
86
93
|
shell: { type: 'string', enum: ['bash', 'powershell'], description: 'Force shell. Windows defaults to PowerShell; bash = Git Bash/POSIX.' },
|
|
@@ -110,5 +110,13 @@ export function scrubProviderSecrets(env) {
|
|
|
110
110
|
export function scrubRuntimeRootVars(env) {
|
|
111
111
|
if (!env || typeof env !== 'object') return env;
|
|
112
112
|
delete env.MIXDOG_ROOT;
|
|
113
|
+
// Host-runtime leak: a packaged Electron/daemon host runs with
|
|
114
|
+
// NODE_ENV=production, and inheriting it silently corrupts model-spawned
|
|
115
|
+
// shells — `npm install` prunes devDependencies, React resolves its
|
|
116
|
+
// production build (no `act`), test suites flip red on developer machines
|
|
117
|
+
// while CI stays green. The value is OUR process's, not the user's OS
|
|
118
|
+
// env, so shells must not see it. A command that needs NODE_ENV sets it
|
|
119
|
+
// explicitly.
|
|
120
|
+
delete env.NODE_ENV;
|
|
113
121
|
return env;
|
|
114
122
|
}
|
|
@@ -19,14 +19,15 @@ eof_line: "*** End of File" LF
|
|
|
19
19
|
%import common.LF
|
|
20
20
|
`;
|
|
21
21
|
|
|
22
|
-
const APPLY_PATCH_FREEFORM_DESCRIPTION =
|
|
22
|
+
const APPLY_PATCH_FREEFORM_DESCRIPTION =
|
|
23
|
+
'Apply an atomic FREEFORM patch, not JSON. Use exact current context; roll back on failure.';
|
|
23
24
|
|
|
24
25
|
export const PATCH_TOOL_DEFS = [
|
|
25
26
|
{
|
|
26
27
|
name: 'apply_patch',
|
|
27
28
|
title: 'Mixdog Apply Patch',
|
|
28
29
|
annotations: { title: 'Mixdog Apply Patch', readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false, compressible: false, compressibleLossless: true },
|
|
29
|
-
description: 'Apply
|
|
30
|
+
description: 'Apply one atomic patch; roll back all touched paths on failure.',
|
|
30
31
|
freeformDescription: APPLY_PATCH_FREEFORM_DESCRIPTION,
|
|
31
32
|
freeform: {
|
|
32
33
|
type: 'grammar',
|
|
@@ -36,7 +37,7 @@ export const PATCH_TOOL_DEFS = [
|
|
|
36
37
|
inputSchema: {
|
|
37
38
|
type: 'object',
|
|
38
39
|
properties: {
|
|
39
|
-
patch: { type: 'string', description: '
|
|
40
|
+
patch: { type: 'string', description: 'V4A patch text; use exact current context; atomic rollback.' },
|
|
40
41
|
format: { type: 'string', enum: ['unified', 'v4a'], description: 'Auto-detected.' },
|
|
41
42
|
base_path: { type: 'string', description: 'Repo root.' },
|
|
42
43
|
dry_run: { type: 'boolean', description: 'Default false. true = validate only, no write.' },
|
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": "0.1.0",
|
|
3
|
-
"_comment": "Synced from immutable token-v release assets.
|
|
4
|
-
"assets": {
|
|
3
|
+
"_comment": "Synced from immutable token-v release assets.",
|
|
4
|
+
"assets": {
|
|
5
|
+
"darwin-arm64": {
|
|
6
|
+
"url": "https://github.com/tribgames/mixdog/releases/download/token-v0.1.0/mixdog-token-darwin-arm64",
|
|
7
|
+
"sha256": "1d5259803207dd5ace466c506d8db4087460a20c1e3f4f6cc249832a8a996f6b"
|
|
8
|
+
},
|
|
9
|
+
"darwin-x64": {
|
|
10
|
+
"url": "https://github.com/tribgames/mixdog/releases/download/token-v0.1.0/mixdog-token-darwin-x64",
|
|
11
|
+
"sha256": "2212a434a89419ce0a4122ce4647e2feb2b4642e26d3e74e11b4de2819a6c2e3"
|
|
12
|
+
},
|
|
13
|
+
"linux-arm64": {
|
|
14
|
+
"url": "https://github.com/tribgames/mixdog/releases/download/token-v0.1.0/mixdog-token-linux-arm64",
|
|
15
|
+
"sha256": "332839da398050218fbed4b6700a3e34dbdcba800893b9a3ba16ebca3e88b9ee"
|
|
16
|
+
},
|
|
17
|
+
"linux-x64": {
|
|
18
|
+
"url": "https://github.com/tribgames/mixdog/releases/download/token-v0.1.0/mixdog-token-linux-x64",
|
|
19
|
+
"sha256": "ac79201259985705cf378d225a25a779591d1f75fefd626e470b398e3d8fdd9c"
|
|
20
|
+
},
|
|
21
|
+
"win32-x64": {
|
|
22
|
+
"url": "https://github.com/tribgames/mixdog/releases/download/token-v0.1.0/mixdog-token-win32-x64.exe",
|
|
23
|
+
"sha256": "da6e39974811123b3e0c72f539c9e5b424261b66a3f2a30eb0e9cc837036d1ba"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
5
26
|
}
|
|
@@ -877,7 +877,10 @@ export function createMemoryActionHandlers({
|
|
|
877
877
|
return { text: `retro_eval_active: total=${total} archived=${archived} kept=${kept} updated=${updated} merged=${merged} errors=${errors}` }
|
|
878
878
|
}
|
|
879
879
|
|
|
880
|
-
return {
|
|
880
|
+
return {
|
|
881
|
+
text: `unknown memory action: ${action}; valid: core, status. Mutation verbs belong in op.`,
|
|
882
|
+
isError: true,
|
|
883
|
+
}
|
|
881
884
|
}
|
|
882
885
|
|
|
883
886
|
const handleToolCall = createToolCallHandler({ handleSearch, handleMemoryAction })
|
|
@@ -8,12 +8,24 @@ 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:
|
|
11
|
+
description: [
|
|
12
|
+
'Core-memory mutation/status; recall retrieves.',
|
|
13
|
+
'Mutations use action=core with op; status uses action=status.',
|
|
14
|
+
'Store only durable compact ENGLISH facts/rules/preferences.',
|
|
15
|
+
].join(' '),
|
|
12
16
|
inputSchema: {
|
|
13
17
|
type: 'object',
|
|
14
18
|
properties: {
|
|
15
|
-
action: {
|
|
16
|
-
|
|
19
|
+
action: {
|
|
20
|
+
type: 'string',
|
|
21
|
+
enum: ['core','status'],
|
|
22
|
+
description: 'core for mutations; status otherwise. Mutation verbs belong in op.',
|
|
23
|
+
},
|
|
24
|
+
op: {
|
|
25
|
+
type: 'string',
|
|
26
|
+
enum: ['add','edit','delete','list','candidates','promote','dismiss'],
|
|
27
|
+
description: 'Required for action=core.',
|
|
28
|
+
},
|
|
17
29
|
id: { type: 'number', description: 'Exact memory id.' },
|
|
18
30
|
element: { type: 'string', maxLength: 40, description: 'Memory key/title. Defaults to the first 40 chars of summary. Max 40 chars.' },
|
|
19
31
|
summary: { type: 'string', maxLength: 100, description: 'Memory content: one short English clause, max 100 chars.' },
|
|
@@ -22,6 +34,10 @@ export const TOOL_DEFS = [
|
|
|
22
34
|
confirm: { type: 'string', description: 'Exact confirmation phrase for destructive actions.' },
|
|
23
35
|
project_id: { type: 'string', description: 'Core pool: explicit common or slug. Required for core add only (edit uses the id\'s stored pool); there is no default pool.' },
|
|
24
36
|
},
|
|
37
|
+
anyOf: [
|
|
38
|
+
{ properties: { action: { const: 'status' } }, required: ['action'] },
|
|
39
|
+
{ properties: { action: { const: 'core' } }, required: ['action', 'op'] },
|
|
40
|
+
],
|
|
25
41
|
additionalProperties: false,
|
|
26
42
|
required: ['action'],
|
|
27
43
|
},
|
|
@@ -407,6 +407,21 @@ export function createLifecycleApi(deps) {
|
|
|
407
407
|
prefetchSession(id) {
|
|
408
408
|
return mgr.prefetchSession?.(id, toolSpecForMode(getMode())) === true;
|
|
409
409
|
},
|
|
410
|
+
// Read-only session peek (desktop split panes treat every visible pane as
|
|
411
|
+
// foreground): the parsed session object without resuming, attaching, or
|
|
412
|
+
// touching ownership/liveness. The current session returns live memory.
|
|
413
|
+
peekSession(id) {
|
|
414
|
+
const sessionId = clean(id);
|
|
415
|
+
if (!sessionId || !/^[A-Za-z0-9_-]+$/.test(sessionId)) return null;
|
|
416
|
+
const current = getSession();
|
|
417
|
+
if (current?.id === sessionId) return current;
|
|
418
|
+
const session = mgr.loadSession?.(sessionId);
|
|
419
|
+
// `closed` means the previous live owner released this durable session;
|
|
420
|
+
// it is still a valid historical transcript. Desktop pane peeks are
|
|
421
|
+
// strictly read-only, so they may inspect it without resuming or
|
|
422
|
+
// changing ownership. Deleted/tombstoned sessions have no load result.
|
|
423
|
+
return session || null;
|
|
424
|
+
},
|
|
410
425
|
async resume(id) {
|
|
411
426
|
clearRoutePreparation?.();
|
|
412
427
|
const prev = getSession();
|
|
@@ -31,6 +31,7 @@ export function createProviderAuthApi({
|
|
|
31
31
|
refreshProviderCatalogs,
|
|
32
32
|
cachedProviderSetup,
|
|
33
33
|
getUsageDashboard,
|
|
34
|
+
consumeCodexRateLimitResetCredit,
|
|
34
35
|
collectProviderModels,
|
|
35
36
|
}) {
|
|
36
37
|
function refreshProviderCatalogsSoon() {
|
|
@@ -79,6 +80,13 @@ export function createProviderAuthApi({
|
|
|
79
80
|
async getUsageDashboard(options = {}) {
|
|
80
81
|
return await getUsageDashboard(options);
|
|
81
82
|
},
|
|
83
|
+
async consumeCodexRateLimitResetCredit(options = {}) {
|
|
84
|
+
await awaitKeychainPrewarm();
|
|
85
|
+
if (typeof consumeCodexRateLimitResetCredit !== 'function') {
|
|
86
|
+
throw new Error('Codex reset is unavailable');
|
|
87
|
+
}
|
|
88
|
+
return await consumeCodexRateLimitResetCredit(options);
|
|
89
|
+
},
|
|
82
90
|
async authenticateProvider(providerId, secret) {
|
|
83
91
|
await awaitKeychainPrewarm();
|
|
84
92
|
const result = String(secret || '').trim()
|
|
@@ -14,6 +14,7 @@ export function createProviderUsage({
|
|
|
14
14
|
providerSetup,
|
|
15
15
|
createUsageDashboard,
|
|
16
16
|
fetchOAuthUsageSnapshot,
|
|
17
|
+
consumeOpenAICodexResetCredit,
|
|
17
18
|
isCloseRequested,
|
|
18
19
|
getProviderSetupWarmupTimer,
|
|
19
20
|
scheduleProviderSetupWarmup,
|
|
@@ -64,8 +65,9 @@ export function createProviderUsage({
|
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
async function getUsageDashboard(options = {}) {
|
|
67
|
-
const
|
|
68
|
-
|
|
68
|
+
const refreshUsage = options?.refresh === true;
|
|
69
|
+
const forceSetup = options?.force === true || (refreshUsage && options?.refreshSetup !== false);
|
|
70
|
+
if (!forceSetup && !refreshUsage && caches.usageDashboardCache.dashboard) {
|
|
69
71
|
const cached = {
|
|
70
72
|
...caches.usageDashboardCache.dashboard,
|
|
71
73
|
refresh: false,
|
|
@@ -78,7 +80,7 @@ export function createProviderUsage({
|
|
|
78
80
|
}
|
|
79
81
|
return cached;
|
|
80
82
|
}
|
|
81
|
-
if (!forceSetup && caches.usageDashboardPromise) return await caches.usageDashboardPromise;
|
|
83
|
+
if (!forceSetup && !refreshUsage && caches.usageDashboardPromise) return await caches.usageDashboardPromise;
|
|
82
84
|
const quickSetup = options?.quickSetup !== false;
|
|
83
85
|
const getProvider = (providerId) => reg().getProvider(providerId);
|
|
84
86
|
const log = (message) => {
|
|
@@ -98,16 +100,26 @@ export function createProviderUsage({
|
|
|
98
100
|
});
|
|
99
101
|
}
|
|
100
102
|
const buildDashboard = async () => {
|
|
103
|
+
let setup;
|
|
104
|
+
try {
|
|
105
|
+
setup = await cachedProviderSetup({ force: forceSetup, quick: false });
|
|
106
|
+
} catch {
|
|
107
|
+
// One unavailable keychain/provider descriptor must not take down the
|
|
108
|
+
// whole dashboard. The no-secrets/no-local snapshot still lets cached
|
|
109
|
+
// and provider-native quota windows refresh.
|
|
110
|
+
log('provider setup failed; falling back to quick setup');
|
|
111
|
+
setup = await cachedProviderSetup({ force: forceSetup, quick: true });
|
|
112
|
+
}
|
|
101
113
|
const dashboard = await createUsageDashboard(displayConfig(), {
|
|
102
114
|
...(options || {}),
|
|
103
|
-
setup
|
|
115
|
+
setup,
|
|
104
116
|
getProvider,
|
|
105
117
|
log,
|
|
106
118
|
});
|
|
107
119
|
caches.usageDashboardCache = { dashboard, at: Date.now() };
|
|
108
120
|
return dashboard;
|
|
109
121
|
};
|
|
110
|
-
if (forceSetup) return await buildDashboard();
|
|
122
|
+
if (forceSetup || refreshUsage) return await buildDashboard();
|
|
111
123
|
caches.usageDashboardPromise = buildDashboard()
|
|
112
124
|
.finally(() => {
|
|
113
125
|
caches.usageDashboardPromise = null;
|
|
@@ -115,6 +127,18 @@ export function createProviderUsage({
|
|
|
115
127
|
return await caches.usageDashboardPromise;
|
|
116
128
|
}
|
|
117
129
|
|
|
130
|
+
async function consumeCodexRateLimitResetCredit(options = {}) {
|
|
131
|
+
if (typeof consumeOpenAICodexResetCredit !== 'function') {
|
|
132
|
+
throw new Error('Codex reset is unavailable');
|
|
133
|
+
}
|
|
134
|
+
const providerObj = reg().getProvider('openai-oauth');
|
|
135
|
+
if (!providerObj) throw new Error('Codex is not signed in');
|
|
136
|
+
const result = await consumeOpenAICodexResetCredit(providerObj, options);
|
|
137
|
+
caches.usageDashboardCache = {};
|
|
138
|
+
const dashboard = await getUsageDashboard({ refresh: true, refreshSetup: false });
|
|
139
|
+
return { ...result, dashboard };
|
|
140
|
+
}
|
|
141
|
+
|
|
118
142
|
return {
|
|
119
143
|
refreshStatuslineUsageSnapshot,
|
|
120
144
|
cachedProviderSetup,
|
|
@@ -122,5 +146,6 @@ export function createProviderUsage({
|
|
|
122
146
|
// (settings hydration) can serve the quick snapshot until then.
|
|
123
147
|
hasProviderSetupCached: () => Boolean(caches.providerSetupCache.setup),
|
|
124
148
|
getUsageDashboard,
|
|
149
|
+
consumeCodexRateLimitResetCredit,
|
|
125
150
|
};
|
|
126
151
|
}
|