mixdog 0.9.166 → 0.9.167
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 +1 -1
- package/scripts/audit-usage-ledger.mjs +86 -0
- package/scripts/generate-voice-runtime-manifest.mjs +22 -17
- package/scripts/memory-chunk-quality.mjs +128 -0
- package/scripts/memory-layer2-check.mjs +133 -0
- package/scripts/model-catalog-audit.mjs +6 -0
- package/scripts/native-tool-download.mjs +14 -0
- package/scripts/prepare-native-assets.mjs +2 -7
- package/scripts/tool-failures.mjs +7 -2
- package/scripts/verify-release-assets.mjs +48 -90
- package/src/defaults/skills/browser-use/SKILL.md +33 -18
- package/src/defaults/skills/computer-use/SKILL.md +16 -8
- package/src/lib/keychain-cjs.cjs +0 -1
- package/src/lib/rules-builder.cjs +4 -1
- package/src/rules/agent/40-cycle1-agent.md +21 -13
- package/src/rules/lead/01-general.md +8 -3
- package/src/rules/shared/10-tool-workflow.md +20 -11
- package/src/rules/shared/30-exploration.md +11 -4
- package/src/rules/shared/40-editing.md +2 -3
- package/src/rules/shared/50-execution.md +6 -3
- package/src/rules/shared/60-verification.md +3 -2
- package/src/rules/shared/70-delivery.md +0 -4
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +51 -45
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +3 -4
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +44 -7
- package/src/runtime/agent/orchestrator/agent-trace.mjs +9 -0
- package/src/runtime/agent/orchestrator/config.mjs +9 -4
- package/src/runtime/agent/orchestrator/context/collect.mjs +25 -1055
- package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +272 -0
- package/src/runtime/agent/orchestrator/context/role-instructions.mjs +296 -0
- package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +523 -0
- package/src/runtime/agent/orchestrator/providers/account-pool.mjs +7 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +13 -14
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-catalog.mjs +214 -0
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-login.mjs +220 -116
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-tokens.mjs +74 -44
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +191 -29
- package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +13 -12
- package/src/runtime/agent/orchestrator/providers/cursor-wire-guards.mjs +1 -9
- package/src/runtime/agent/orchestrator/providers/cursor-wire-protobuf.mjs +5 -2
- package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +48 -25
- package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +28 -2
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/grok-oauth-tokens.mjs +1 -8
- package/src/runtime/agent/orchestrator/providers/lib/action-input-contract.mjs +32 -0
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +21 -6
- package/src/runtime/agent/orchestrator/providers/lib/grok-tool-schema.mjs +61 -13
- package/src/runtime/agent/orchestrator/providers/model-catalog-projection.mjs +6 -1
- package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +123 -118
- package/src/runtime/agent/orchestrator/providers/model-pricing-rates.mjs +54 -0
- package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +5 -2
- package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +34 -3
- package/src/runtime/agent/orchestrator/providers/openai-codex-endpoints.mjs +21 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-catalog.mjs +154 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +322 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +243 -702
- package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/openai-startup-prewarm.mjs +221 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +3 -2
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/provider-catalog-cache.mjs +19 -0
- package/src/runtime/agent/orchestrator/providers/provider-model-identities.mjs +40 -0
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +120 -710
- package/src/runtime/agent/orchestrator/session/cache/read-cache.mjs +36 -15
- package/src/runtime/agent/orchestrator/session/cache/util.mjs +9 -0
- package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +17 -4
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +36 -31
- package/src/runtime/agent/orchestrator/session/compact.mjs +4 -5
- package/src/runtime/agent/orchestrator/session/context-compaction-policy.mjs +6 -5
- package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +8 -6
- package/src/runtime/agent/orchestrator/session/loop/assistant-commit.mjs +121 -0
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +2 -3
- package/src/runtime/agent/orchestrator/session/loop/diagnostics.mjs +74 -0
- package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +44 -17
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +266 -0
- package/src/runtime/agent/orchestrator/session/loop/request-boundary.mjs +111 -0
- package/src/runtime/agent/orchestrator/session/loop/request-projection.mjs +99 -0
- package/src/runtime/agent/orchestrator/session/loop/steering.mjs +91 -0
- package/src/runtime/agent/orchestrator/session/loop/usage.mjs +36 -0
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +3 -3
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +12 -0
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +20 -11
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +3 -5
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +9 -3
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +39 -0
- package/src/runtime/agent/orchestrator/session/store-transcript-cache.mjs +3 -2
- package/src/runtime/agent/orchestrator/session/tool-batch.mjs +21 -3
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +2 -3
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +25 -25
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +62 -10
- package/src/runtime/agent/orchestrator/tools/builtin/path-utils.mjs +17 -0
- package/src/runtime/agent/orchestrator/tools/builtin/read-batch.mjs +61 -4
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +13 -9
- package/src/runtime/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +12 -2
- package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +3 -2
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +8 -3
- package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +69 -6
- package/src/runtime/agent/orchestrator/tools/builtin.mjs +14 -17
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/source-access.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +6 -6
- package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +1 -1
- package/src/runtime/bridge-discovery.mjs +8 -0
- package/src/runtime/browser-bridge/action-schema.mjs +37 -43
- package/src/runtime/browser-bridge/client.mjs +111 -99
- package/src/runtime/browser-bridge/input-fields.mjs +187 -0
- package/src/runtime/browser-bridge/tool-defs.mjs +12 -227
- package/src/runtime/computer-bridge/action-schema.mjs +2 -46
- package/src/runtime/computer-bridge/client.mjs +69 -77
- package/src/runtime/computer-bridge/core-actions.mjs +7 -5
- package/src/runtime/computer-bridge/error-recovery.mjs +41 -37
- package/src/runtime/computer-bridge/tool-defs.mjs +1 -1
- package/src/runtime/github/commands.mjs +4 -1
- package/src/runtime/github/contract.mjs +1 -1
- package/src/runtime/github/tool.mjs +2 -2
- package/src/runtime/media/adapters/antigravity-image.mjs +72 -0
- package/src/runtime/media/adapters/gemini-image.mjs +20 -11
- package/src/runtime/media/auth.mjs +12 -0
- package/src/runtime/media/catalog.mjs +30 -10
- package/src/runtime/media/jobs.mjs +4 -0
- package/src/runtime/media/lanes.mjs +40 -31
- package/src/runtime/memory/index.mjs +2 -9
- package/src/runtime/memory/lib/compact-handoff.mjs +26 -18
- package/src/runtime/memory/lib/memory-chunk-audit.mjs +41 -0
- package/src/runtime/memory/lib/memory-chunk-quality.mjs +388 -0
- package/src/runtime/memory/lib/memory-cycle1.mjs +95 -394
- package/src/runtime/memory/lib/memory-recall-scope-filter.mjs +29 -4
- package/src/runtime/memory/lib/memory-recall-store.mjs +8 -12
- package/src/runtime/memory/lib/memory-retrievers.mjs +8 -11
- package/src/runtime/memory/lib/memory.mjs +2 -0
- package/src/runtime/memory/lib/query-handlers.mjs +98 -144
- package/src/runtime/memory/lib/query-ranking.mjs +23 -9
- package/src/runtime/memory/lib/recall-format.mjs +37 -27
- package/src/runtime/memory/lib/session-ingest-runtime.mjs +14 -8
- package/src/runtime/memory/lib/session-ingest.mjs +45 -84
- package/src/runtime/memory/lib/transcript-ingest.mjs +19 -21
- package/src/runtime/memory/tool-defs.mjs +2 -2
- package/src/runtime/office/authoring/pptx-author-session.mjs +3 -7
- package/src/runtime/office/core/office-actions-batch.mjs +2 -8
- package/src/runtime/office/core/office-actions-open.mjs +124 -0
- package/src/runtime/office/core/office-actions-read.mjs +127 -0
- package/src/runtime/office/core/office-core.mjs +80 -1
- package/src/runtime/office/core/office-sessions.mjs +116 -138
- package/src/runtime/office/index.mjs +18 -271
- package/src/runtime/office/pdf/pdf-analysis.mjs +2 -2
- package/src/runtime/office/pdf/pdf-limits.mjs +2 -0
- package/src/runtime/office/tool-defs.mjs +7 -6
- package/src/runtime/shared/agent-route-config.mjs +8 -8
- package/src/runtime/shared/child-spawn-gate.mjs +10 -13
- package/src/runtime/shared/config.mjs +2 -7
- package/src/runtime/shared/env.mjs +3 -3
- package/src/runtime/shared/err-text.mjs +1 -1
- package/src/runtime/shared/extension-scopes.mjs +3 -2
- package/src/runtime/shared/heap-cap.mjs +10 -6
- package/src/runtime/shared/llm/cost.mjs +35 -10
- package/src/runtime/shared/llm/usage-accounting.mjs +11 -1
- package/src/runtime/shared/llm/usage-context.mjs +6 -0
- package/src/runtime/shared/llm/usage-ledger-import.mjs +5 -0
- package/src/runtime/shared/llm/usage-ledger-repair.mjs +109 -0
- package/src/runtime/shared/llm/usage-ledger.mjs +59 -20
- package/src/runtime/shared/llm/usage-pricing-refresh.mjs +26 -0
- package/src/runtime/shared/llm/usage-rollup.mjs +17 -15
- package/src/runtime/shared/native-asset.mjs +3 -4
- package/src/runtime/shared/object.mjs +4 -0
- package/src/runtime/shared/pg-schema-db.mjs +21 -0
- package/src/runtime/shared/plugin-metadata.mjs +3 -1
- package/src/runtime/shared/resource-admission.mjs +1 -6
- package/src/runtime/shared/schedules-db.mjs +8 -24
- package/src/runtime/shared/schema-value-error.d.mts +2 -0
- package/src/runtime/shared/schema-value-error.mjs +68 -0
- package/src/runtime/shared/singleton-owner.mjs +11 -24
- package/src/runtime/shared/staged-update.mjs +1 -10
- package/src/runtime/shared/tool-card-model.mjs +8 -5
- package/src/runtime/shared/tool-execution-contract.mjs +1 -1
- package/src/runtime/shared/tool-surface.mjs +1 -5
- package/src/runtime/shared/user-data-guard.mjs +1 -4
- package/src/runtime/shared/webhooks-db.mjs +8 -24
- package/src/runtime/web-search/index.mjs +32 -8
- package/src/runtime/web-search/tool-defs.mjs +4 -4
- package/src/session-runtime/config-lifecycle.mjs +34 -0
- package/src/session-runtime/context-status.mjs +19 -0
- package/src/session-runtime/cwd-plugins.mjs +12 -29
- package/src/session-runtime/goal-tasks.mjs +2 -2
- package/src/session-runtime/goal-tool-defs.mjs +3 -2
- package/src/session-runtime/internal-tool-executor.mjs +7 -0
- package/src/session-runtime/lifecycle-api.mjs +5 -11
- package/src/session-runtime/notification-bus.mjs +41 -0
- package/src/session-runtime/prewarm.mjs +22 -0
- package/src/session-runtime/runtime-bootstrap.mjs +90 -0
- package/src/session-runtime/runtime-core.mjs +88 -202
- package/src/session-runtime/runtime-feature-gates.mjs +37 -0
- package/src/session-runtime/session-lifecycle.mjs +5 -5
- package/src/session-runtime/session-turn-api.mjs +1 -12
- package/src/session-runtime/setup-tool/executor.mjs +13 -5
- package/src/session-runtime/setup-tool/tool-defs.mjs +52 -46
- package/src/session-runtime/tool-defs.mjs +73 -4
- package/src/session-runtime/usage-stats-api.mjs +4 -1
- package/src/session-runtime/warmup-schedulers.mjs +8 -2
- package/src/session-runtime/workflow.mjs +14 -0
- package/src/standalone/channel-session-router.mjs +3 -1
- package/src/standalone/channel-worker.mjs +18 -9
- package/src/standalone/daemon-crash-capture.mjs +534 -0
- package/src/standalone/daemon-telemetry.mjs +175 -0
- package/src/standalone/daemon.mjs +31 -21
- package/src/standalone/session-client.mjs +31 -23
- package/src/standalone/usage-dashboard-model.mjs +2 -0
- package/src/standalone/usage-stats-hours.mjs +18 -11
- package/src/standalone/usage-stats-model.mjs +39 -28
- package/src/standalone/usage-stats-period.mjs +43 -45
- package/src/tui/app/use-transcript-window.mjs +1 -1
- package/src/tui/components/StatusLine.jsx +13 -42
- package/src/tui/dist/index.mjs +256 -239
- package/src/tui/session/session-api-ext.mjs +44 -41
- package/src/tui/session/session-api.mjs +4 -0
- package/src/tui/session/tool-card-results.mjs +25 -78
- package/src/tui/session/tool-result-status.mjs +94 -1
- package/src/tui/session/turn-aggregate-cards.mjs +17 -54
- package/src/ui/statusline-format.mjs +6 -26
- package/src/workflows/default/WORKFLOW.md +5 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// JSON Schema counts Unicode code points, not JavaScript's UTF-16 code units.
|
|
2
|
+
export function schemaStringLength(value) {
|
|
3
|
+
let length = 0;
|
|
4
|
+
for (const _ of value) length++;
|
|
5
|
+
return length;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
// Shared schema checks; tools retain their action-specific target relationships.
|
|
9
|
+
export function schemaValueError(value, schema, path) {
|
|
10
|
+
if (schema.enum && !schema.enum.includes(value)) {
|
|
11
|
+
return `${path} must be one of: ${schema.enum.join(', ')}`;
|
|
12
|
+
}
|
|
13
|
+
if (schema.type === 'string') {
|
|
14
|
+
if (typeof value !== 'string') return `${path} must be a string`;
|
|
15
|
+
const length = schemaStringLength(value);
|
|
16
|
+
if (schema.minLength !== undefined && length < schema.minLength) {
|
|
17
|
+
return `${path} requires at least ${schema.minLength} characters`;
|
|
18
|
+
}
|
|
19
|
+
if (schema.maxLength !== undefined && length > schema.maxLength) {
|
|
20
|
+
return `${path} accepts at most ${schema.maxLength} characters`;
|
|
21
|
+
}
|
|
22
|
+
if (schema.pattern !== undefined && !new RegExp(schema.pattern).test(value)) {
|
|
23
|
+
return `${path} must match the required format`;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
if (schema.type === 'boolean' && typeof value !== 'boolean') return `${path} must be a boolean`;
|
|
27
|
+
if (schema.type === 'number' && (typeof value !== 'number' || !Number.isFinite(value))) {
|
|
28
|
+
return `${path} must be a finite number`;
|
|
29
|
+
}
|
|
30
|
+
if (schema.type === 'integer' && !Number.isInteger(value)) return `${path} must be an integer`;
|
|
31
|
+
if (schema.type === 'array') {
|
|
32
|
+
if (!Array.isArray(value)) return `${path} must be an array`;
|
|
33
|
+
if (schema.minItems !== undefined && value.length < schema.minItems) {
|
|
34
|
+
return `${path} requires at least ${schema.minItems} items`;
|
|
35
|
+
}
|
|
36
|
+
if (schema.maxItems !== undefined && value.length > schema.maxItems) {
|
|
37
|
+
return `${path} accepts at most ${schema.maxItems} items`;
|
|
38
|
+
}
|
|
39
|
+
if (schema.uniqueItems && new Set(value.map((item) => JSON.stringify(item))).size !== value.length) {
|
|
40
|
+
return `${path} must contain unique items`;
|
|
41
|
+
}
|
|
42
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
43
|
+
const error = schemaValueError(value[index], schema.items || {}, `${path}[${index}]`);
|
|
44
|
+
if (error) return error;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (schema.type === 'object') {
|
|
48
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return `${path} must be an object`;
|
|
49
|
+
const properties = schema.properties || {};
|
|
50
|
+
const missing = (schema.required || []).filter((name) => !Object.hasOwn(value, name));
|
|
51
|
+
if (missing.length) return `${path} requires field(s): ${missing.join(', ')}`;
|
|
52
|
+
if (schema.additionalProperties === false) {
|
|
53
|
+
const extras = Object.keys(value).filter((name) => !Object.hasOwn(properties, name));
|
|
54
|
+
if (extras.length) return `${path} does not accept field(s): ${extras.join(', ')}`;
|
|
55
|
+
}
|
|
56
|
+
for (const [name, item] of Object.entries(value)) {
|
|
57
|
+
const field = Object.hasOwn(properties, name) ? properties[name]
|
|
58
|
+
: (typeof schema.additionalProperties === 'object' ? schema.additionalProperties : {});
|
|
59
|
+
const error = schemaValueError(item, field, `${path}.${name}`);
|
|
60
|
+
if (error) return error;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (typeof value === 'number') {
|
|
64
|
+
if (schema.minimum !== undefined && value < schema.minimum) return `${path} must be at least ${schema.minimum}`;
|
|
65
|
+
if (schema.maximum !== undefined && value > schema.maximum) return `${path} must be at most ${schema.maximum}`;
|
|
66
|
+
}
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
@@ -1,15 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { mkdirSync, renameSync, rmSync, statSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { dirname } from 'node:path';
|
|
3
|
+
import { readJsonSafe } from './json-file.mjs';
|
|
3
4
|
import { isPidAlive } from './pid-liveness.mjs';
|
|
4
|
-
|
|
5
|
-
function readJson(path) {
|
|
6
|
-
try {
|
|
7
|
-
if (!existsSync(path)) return null;
|
|
8
|
-
return JSON.parse(readFileSync(path, 'utf8'));
|
|
9
|
-
} catch {
|
|
10
|
-
return null;
|
|
11
|
-
}
|
|
12
|
-
}
|
|
5
|
+
import { sleepSync } from './sleep.mjs';
|
|
13
6
|
|
|
14
7
|
function writeJsonAtomic(path, value) {
|
|
15
8
|
mkdirSync(dirname(path), { recursive: true });
|
|
@@ -18,12 +11,6 @@ function writeJsonAtomic(path, value) {
|
|
|
18
11
|
renameSync(tmp, path);
|
|
19
12
|
}
|
|
20
13
|
|
|
21
|
-
function waitSync(ms) {
|
|
22
|
-
try {
|
|
23
|
-
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
24
|
-
} catch {}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
14
|
function acquireClaimLock(path) {
|
|
28
15
|
const lockPath = `${path}.claim.lock`;
|
|
29
16
|
for (let i = 0; i < 20; i++) {
|
|
@@ -39,14 +26,14 @@ function acquireClaimLock(path) {
|
|
|
39
26
|
continue;
|
|
40
27
|
}
|
|
41
28
|
} catch {}
|
|
42
|
-
|
|
29
|
+
sleepSync(25);
|
|
43
30
|
}
|
|
44
31
|
}
|
|
45
32
|
return null;
|
|
46
33
|
}
|
|
47
34
|
|
|
48
35
|
export function readSingletonOwner(path) {
|
|
49
|
-
const owner =
|
|
36
|
+
const owner = readJsonSafe(path);
|
|
50
37
|
if (!owner || typeof owner !== 'object') return { owner: null, alive: false };
|
|
51
38
|
return { owner, alive: isPidAlive(owner.pid) };
|
|
52
39
|
}
|
|
@@ -63,15 +50,15 @@ export function claimSingletonOwner(path, {
|
|
|
63
50
|
...meta,
|
|
64
51
|
};
|
|
65
52
|
|
|
66
|
-
const current =
|
|
53
|
+
const current = readJsonSafe(path);
|
|
67
54
|
if (current?.pid && Number(current.pid) !== Number(pid) && isPidAlive(current.pid)) {
|
|
68
55
|
return { owned: false, owner: current };
|
|
69
56
|
}
|
|
70
57
|
|
|
71
58
|
const lockPath = acquireClaimLock(path);
|
|
72
|
-
if (!lockPath) return { owned: false, owner:
|
|
59
|
+
if (!lockPath) return { owned: false, owner: readJsonSafe(path) };
|
|
73
60
|
try {
|
|
74
|
-
const lockedCurrent =
|
|
61
|
+
const lockedCurrent = readJsonSafe(path);
|
|
75
62
|
if (lockedCurrent?.pid && Number(lockedCurrent.pid) !== Number(pid) && isPidAlive(lockedCurrent.pid)) {
|
|
76
63
|
return { owned: false, owner: lockedCurrent };
|
|
77
64
|
}
|
|
@@ -83,7 +70,7 @@ export function claimSingletonOwner(path, {
|
|
|
83
70
|
}
|
|
84
71
|
|
|
85
72
|
export function releaseSingletonOwner(path, pid = process.pid) {
|
|
86
|
-
const current =
|
|
73
|
+
const current = readJsonSafe(path);
|
|
87
74
|
if (!current?.pid || Number(current.pid) !== Number(pid)) return false;
|
|
88
75
|
try {
|
|
89
76
|
rmSync(path, { force: true });
|
|
@@ -101,9 +88,9 @@ export function releaseSingletonOwner(path, pid = process.pid) {
|
|
|
101
88
|
// should fall back to the winner rather than overwrite it.
|
|
102
89
|
export function handoffSingletonOwner(path, fromPid, toOwner = {}) {
|
|
103
90
|
const lockPath = acquireClaimLock(path);
|
|
104
|
-
if (!lockPath) return { owned: false, owner:
|
|
91
|
+
if (!lockPath) return { owned: false, owner: readJsonSafe(path) };
|
|
105
92
|
try {
|
|
106
|
-
const current =
|
|
93
|
+
const current = readJsonSafe(path);
|
|
107
94
|
if (current?.pid && Number(current.pid) !== Number(fromPid)) {
|
|
108
95
|
return { owned: false, owner: current };
|
|
109
96
|
}
|
|
@@ -38,6 +38,7 @@ import { spawn } from 'node:child_process';
|
|
|
38
38
|
import { resolvePluginData } from './plugin-paths.mjs';
|
|
39
39
|
// Same probe every discovery/lock reader uses; `process.pid` is trivially alive.
|
|
40
40
|
import { isPidAlive as pidAlive } from './pid-liveness.mjs';
|
|
41
|
+
import { sleepSync } from './sleep.mjs';
|
|
41
42
|
import { detachedSpawnOpts, hiddenSpawnOpts } from './spawn-flags.mjs';
|
|
42
43
|
import { renameWithRetrySync } from './atomic-file.mjs';
|
|
43
44
|
import {
|
|
@@ -69,16 +70,6 @@ function rmDir(dir) {
|
|
|
69
70
|
try { rmSync(dir, { recursive: true, force: true }); } catch { /* best-effort */ }
|
|
70
71
|
}
|
|
71
72
|
|
|
72
|
-
function sleepSync(ms) {
|
|
73
|
-
const dur = Math.max(1, Number(ms) || 1);
|
|
74
|
-
try {
|
|
75
|
-
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, dur);
|
|
76
|
-
} catch {
|
|
77
|
-
const end = Date.now() + dur;
|
|
78
|
-
while (Date.now() < end) { /* spin fallback */ }
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
73
|
// ── Live-session refcount (pid files) ─────────────────────────────────────
|
|
83
74
|
// Each runtime registers a `<pid>.pid` file on boot and removes it on close.
|
|
84
75
|
// The swap consults these to skip while another mixdog is running. Liveness is
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
summarizeToolResult,
|
|
22
22
|
toolLoadingTargets,
|
|
23
23
|
} from './tool-surface.mjs';
|
|
24
|
+
import { pluralize } from './tool-primitives.mjs';
|
|
24
25
|
import { backgroundTaskFailureStatusLabel, isBackgroundErrorOnlyBody } from './err-text.mjs';
|
|
25
26
|
import { normalizeToolTerminalStatus, toolResultTerminalStatus } from './tool-status.mjs';
|
|
26
27
|
import { formatElapsed } from './time-format.mjs';
|
|
@@ -67,9 +68,7 @@ export function normalizeCountMap(value = {}) {
|
|
|
67
68
|
return out;
|
|
68
69
|
}
|
|
69
70
|
|
|
70
|
-
export
|
|
71
|
-
return count === 1 ? singular : pluralText;
|
|
72
|
-
}
|
|
71
|
+
export { pluralize as plural };
|
|
73
72
|
|
|
74
73
|
export function shellResultStatus(value) {
|
|
75
74
|
const match = String(value || '').match(/(?:^|\b)status:\s*(running|pending|queued|completed|failed|cancelled|canceled)\b/im);
|
|
@@ -188,7 +187,7 @@ export function shellDisplayStatus({ pending = false, failedCount = 0, exitFaile
|
|
|
188
187
|
|
|
189
188
|
export function shellHeader(status, count = 1, verifying = false) {
|
|
190
189
|
const n = Math.max(1, Number(count) || 1);
|
|
191
|
-
const object = `${n} ${
|
|
190
|
+
const object = `${n} ${pluralize(n, 'command')}`;
|
|
192
191
|
// A shell that follows an edit in the same batch IS the verification —
|
|
193
192
|
// label it as such while healthy; failures keep the neutral Ran + detail.
|
|
194
193
|
if (verifying && status === 'running') return `Verifying ${object}`;
|
|
@@ -205,8 +204,12 @@ export const SKILL_SURFACE_NAMES = new Set([
|
|
|
205
204
|
'skill', 'skill_execute', 'skill_view', 'skills_list', 'use_skill',
|
|
206
205
|
]);
|
|
207
206
|
|
|
207
|
+
const BACKGROUND_TASK_TOOL_NAMES = new Set([
|
|
208
|
+
'web_search', 'shell', 'bash', 'bash_session', 'shell_command', 'task',
|
|
209
|
+
]);
|
|
210
|
+
|
|
208
211
|
export function isBackgroundTaskTool(normalizedName) {
|
|
209
|
-
return
|
|
212
|
+
return BACKGROUND_TASK_TOOL_NAMES.has(String(normalizedName || '').toLowerCase());
|
|
210
213
|
}
|
|
211
214
|
|
|
212
215
|
const AGENT_DISPLAY_NAMES = new Map([
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { clean } from './clean.mjs';
|
|
2
2
|
|
|
3
3
|
export const TOOL_SYNC_EXECUTION_CONTRACT =
|
|
4
|
-
'
|
|
4
|
+
'Returns final results in this call.';
|
|
5
5
|
|
|
6
6
|
export const TOOL_ASYNC_EXECUTION_CONTRACT =
|
|
7
7
|
'Runs sync inline by default; async returns a background task_id and delivers a completion notification.';
|
|
@@ -493,11 +493,7 @@ const TOOL_CATEGORY = new Map([
|
|
|
493
493
|
export function classifyToolCategory(name, args = {}) {
|
|
494
494
|
if (isExternalMcpToolName(name)) return 'MCP';
|
|
495
495
|
const normalized = normalizeToolName(name);
|
|
496
|
-
if (normalized === 'code_graph')
|
|
497
|
-
const mode = String(args.mode || args.action || '').toLowerCase();
|
|
498
|
-
if (mode === 'prewarm' || mode === 'index' || mode === 'build' || mode === 'refresh') return 'Setup';
|
|
499
|
-
return (mode === 'search' || mode === 'find_symbol' || mode === 'references' || mode === 'callers' || mode === 'callees') ? 'Search' : 'Read';
|
|
500
|
-
}
|
|
496
|
+
if (normalized === 'code_graph') return codeGraphLabel(args);
|
|
501
497
|
return TOOL_CATEGORY.get(normalized) || 'Other';
|
|
502
498
|
}
|
|
503
499
|
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
import { dirname, join } from 'path';
|
|
19
19
|
import { homedir } from 'os';
|
|
20
20
|
import { createHash } from 'crypto';
|
|
21
|
+
import { isPlainObject } from './object.mjs';
|
|
21
22
|
|
|
22
23
|
function mixdogConfigBaseDir() {
|
|
23
24
|
return process.env.MIXDOG_CONFIG_DIR || join(homedir(), '.mixdog');
|
|
@@ -53,10 +54,6 @@ function initMarkerPath(dataDir) {
|
|
|
53
54
|
return join(getBackupRoot(), `.initialized-${id}.json`);
|
|
54
55
|
}
|
|
55
56
|
|
|
56
|
-
function isPlainObject(value) {
|
|
57
|
-
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
57
|
export function hasUserDataInitMarker(dataDir) {
|
|
61
58
|
return existsSync(initMarkerPath(dataDir));
|
|
62
59
|
}
|
|
@@ -15,16 +15,11 @@
|
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
import { ensurePgInstance, withSchemaBootstrapLock } from '../memory/lib/pg/adapter.mjs';
|
|
18
|
+
import { createPgSchemaDb } from './pg-schema-db.mjs';
|
|
18
19
|
import { resolvePluginData } from './plugin-paths.mjs';
|
|
19
20
|
|
|
20
21
|
const SCHEMA = 'webhooks';
|
|
21
22
|
|
|
22
|
-
// ---------------------------------------------------------------------------
|
|
23
|
-
// Lazy connection + one-shot idempotent DDL (keyed per resolved dataDir).
|
|
24
|
-
// ---------------------------------------------------------------------------
|
|
25
|
-
|
|
26
|
-
const _ready = new Map(); // dataDir → Promise<db>
|
|
27
|
-
|
|
28
23
|
const DDL = `
|
|
29
24
|
CREATE SCHEMA IF NOT EXISTS webhooks;
|
|
30
25
|
CREATE TABLE IF NOT EXISTS webhooks.endpoints (
|
|
@@ -59,24 +54,13 @@ CREATE TABLE IF NOT EXISTS webhooks.deliveries (
|
|
|
59
54
|
);
|
|
60
55
|
`;
|
|
61
56
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
await withSchemaBootstrapLock(pool, () => db.exec(DDL));
|
|
70
|
-
return db;
|
|
71
|
-
})();
|
|
72
|
-
_ready.set(dataDir, p);
|
|
73
|
-
try {
|
|
74
|
-
return await p;
|
|
75
|
-
} catch (err) {
|
|
76
|
-
_ready.delete(dataDir); // let the next call retry DDL after a transient failure
|
|
77
|
-
throw err;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
57
|
+
const getDb = createPgSchemaDb({
|
|
58
|
+
schema: SCHEMA,
|
|
59
|
+
ddl: DDL,
|
|
60
|
+
defaultDataDir: resolvePluginData,
|
|
61
|
+
ensurePg: ensurePgInstance,
|
|
62
|
+
withLock: withSchemaBootstrapLock,
|
|
63
|
+
});
|
|
80
64
|
|
|
81
65
|
// ---------------------------------------------------------------------------
|
|
82
66
|
// Row <-> def mapping
|
|
@@ -75,11 +75,8 @@ function normalizeWebSearchArgs(rawArgs) {
|
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
77
|
if (Array.isArray(args.keywords)) {
|
|
78
|
-
|
|
78
|
+
args.keywords = args.keywords
|
|
79
79
|
.map(value => typeof value === 'string' ? value.trim() : value)
|
|
80
|
-
.filter(value => typeof value === 'string' ? value.length > 0 : Boolean(value))
|
|
81
|
-
if (keywords.length > 0) args.keywords = keywords
|
|
82
|
-
else delete args.keywords
|
|
83
80
|
}
|
|
84
81
|
return args
|
|
85
82
|
}
|
|
@@ -203,16 +200,35 @@ function buildAgentWebSearchPrompt(args) {
|
|
|
203
200
|
return lines.join('\n')
|
|
204
201
|
}
|
|
205
202
|
|
|
203
|
+
// Native providers disagree on citation field names. These lists are the
|
|
204
|
+
// accepted shapes, not a guess: the first present string wins.
|
|
205
|
+
const NATIVE_SOURCE_URL_FIELDS = ['url', 'uri', 'href', 'source_url']
|
|
206
|
+
const NATIVE_SOURCE_TITLE_FIELDS = ['title', 'query', 'name']
|
|
207
|
+
const NATIVE_SOURCE_SNIPPET_FIELDS = ['snippet', 'text', 'description']
|
|
208
|
+
|
|
209
|
+
function firstSourceField(source, names, fallback = '') {
|
|
210
|
+
let raw = fallback
|
|
211
|
+
if (source && typeof source === 'object') {
|
|
212
|
+
for (const name of names) {
|
|
213
|
+
if (source[name]) {
|
|
214
|
+
raw = source[name]
|
|
215
|
+
break
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return String(raw || '').trim()
|
|
220
|
+
}
|
|
221
|
+
|
|
206
222
|
function sourceUrl(source) {
|
|
207
|
-
return
|
|
223
|
+
return firstSourceField(source, NATIVE_SOURCE_URL_FIELDS)
|
|
208
224
|
}
|
|
209
225
|
|
|
210
226
|
function sourceTitle(source, fallbackUrl = '') {
|
|
211
|
-
return
|
|
227
|
+
return firstSourceField(source, NATIVE_SOURCE_TITLE_FIELDS, fallbackUrl || '(untitled)')
|
|
212
228
|
}
|
|
213
229
|
|
|
214
230
|
function sourceSnippet(source) {
|
|
215
|
-
return
|
|
231
|
+
return firstSourceField(source, NATIVE_SOURCE_SNIPPET_FIELDS).replace(/\s+/g, ' ').trim()
|
|
216
232
|
}
|
|
217
233
|
|
|
218
234
|
function collectNativeWebSearchSources(result) {
|
|
@@ -457,16 +473,24 @@ async function handleToolCall(name, rawArgs, options = {}) {
|
|
|
457
473
|
const keywords = [...new Set(args.keywords.map(kw => String(kw || '').trim()).filter(Boolean))]
|
|
458
474
|
const sections = new Array(keywords.length)
|
|
459
475
|
let cursor = 0
|
|
476
|
+
let failed = 0
|
|
460
477
|
await Promise.all(Array.from({ length: Math.min(concurrency, keywords.length) }, async () => {
|
|
461
478
|
while (cursor < keywords.length) {
|
|
462
479
|
const index = cursor++
|
|
463
480
|
const kw = keywords[index]
|
|
464
481
|
const sub = await handleToolCall('web_search', { ...rawArgs, keywords: kw }, { signal, nativeWebSearch })
|
|
465
482
|
const text = (sub.content || []).filter(p => p.type === 'text').map(p => p.text).join('\n')
|
|
483
|
+
if (sub.isError) failed++
|
|
466
484
|
sections[index] = `### Query: ${kw}\n\n${text}`
|
|
467
485
|
}
|
|
468
486
|
}))
|
|
469
|
-
|
|
487
|
+
const summary = failed
|
|
488
|
+
? `[web_search] ${failed}/${keywords.length} queries failed; successful results are retained.\n\n`
|
|
489
|
+
: ''
|
|
490
|
+
return {
|
|
491
|
+
content: [{ type: 'text', text: summary + sections.join('\n\n---\n\n') }],
|
|
492
|
+
...(failed ? { isError: true } : {}),
|
|
493
|
+
}
|
|
470
494
|
}
|
|
471
495
|
try {
|
|
472
496
|
const result = await _webSearchCore(args, { cacheState, nativeWebSearch, signal })
|
|
@@ -8,11 +8,11 @@ export const TOOL_DEFS = [
|
|
|
8
8
|
{
|
|
9
9
|
name: 'web_search',
|
|
10
10
|
title: 'Mixdog Web Search',
|
|
11
|
-
description: `Cached web/docs/current-info search. ${TOOL_SYNC_EXECUTION_CONTRACT}`,
|
|
11
|
+
description: `Cached web/docs/current-info search; batch queries sharing filters. ${TOOL_SYNC_EXECUTION_CONTRACT}`,
|
|
12
12
|
inputSchema: {
|
|
13
13
|
type: 'object',
|
|
14
14
|
properties: {
|
|
15
|
-
query: { anyOf: [{ type: 'string' }, { type: 'array', items: { type: 'string' } }], description: '
|
|
15
|
+
query: { anyOf: [{ type: 'string', minLength: 1, pattern: '\\S' }, { type: 'array', minItems: 1, items: { type: 'string', minLength: 1, pattern: '\\S' } }], description: 'Non-empty query or array for lossless fan-out.' },
|
|
16
16
|
site: { type: 'string', description: 'Site/domain filter.' },
|
|
17
17
|
type: { type: 'string', enum: ['web', 'news', 'images'], description: 'Default web.' },
|
|
18
18
|
maxResults: { type: 'integer', minimum: 1, maximum: 20, description: 'Result count; default 10; 1-20.' },
|
|
@@ -27,11 +27,11 @@ export const TOOL_DEFS = [
|
|
|
27
27
|
{
|
|
28
28
|
name: 'web_fetch',
|
|
29
29
|
title: 'Mixdog Web Fetch',
|
|
30
|
-
description: 'Fetch page/docs body from URL.',
|
|
30
|
+
description: 'Fetch page/docs body from URL; batch known URLs with shared slice options.',
|
|
31
31
|
inputSchema: {
|
|
32
32
|
type: 'object',
|
|
33
33
|
properties: {
|
|
34
|
-
url: { anyOf: [{ type: 'string', format: 'uri' }, { type: 'array', items: { type: 'string', format: 'uri' }, maxItems: 10 }], description: 'Public HTTP(S) URL or array of up to 10 URLs.' },
|
|
34
|
+
url: { anyOf: [{ type: 'string', format: 'uri' }, { type: 'array', items: { type: 'string', format: 'uri' }, minItems: 1, maxItems: 10 }], description: 'Public HTTP(S) URL or array of up to 10 URLs.' },
|
|
35
35
|
startIndex: { type: 'integer', minimum: 0, description: 'Character offset; default 0.' },
|
|
36
36
|
maxLength: { type: 'integer', minimum: 0, description: 'Maximum characters; default 50000; 0 unlimited.' },
|
|
37
37
|
},
|
|
@@ -18,8 +18,42 @@
|
|
|
18
18
|
const CONFIG_SAVE_DEBOUNCE_MS = 150;
|
|
19
19
|
|
|
20
20
|
import { withGrandfatheredBuiltins } from './builtin-features.mjs';
|
|
21
|
+
import { webSearchRouteOrDefault } from './workflow.mjs';
|
|
21
22
|
import { createDebouncedWriter } from '../runtime/shared/debounced-writer.mjs';
|
|
22
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Boot-time config/route state for one runtime. Caller overrides are applied
|
|
26
|
+
* ONLY when supplied, so an option left out keeps the persisted route value
|
|
27
|
+
* (`effort: undefined` must not erase a stored effort, and `fast` is a tri-state
|
|
28
|
+
* where only an explicit boolean overrides).
|
|
29
|
+
*/
|
|
30
|
+
export function resolveInitialConfigState({
|
|
31
|
+
initialConfig,
|
|
32
|
+
loadConfig,
|
|
33
|
+
resolveRoute,
|
|
34
|
+
provider,
|
|
35
|
+
model,
|
|
36
|
+
effort,
|
|
37
|
+
fast,
|
|
38
|
+
modelParameters,
|
|
39
|
+
}) {
|
|
40
|
+
const config = withGrandfatheredBuiltins(
|
|
41
|
+
initialConfig && typeof initialConfig === 'object' ? initialConfig : loadConfig(),
|
|
42
|
+
);
|
|
43
|
+
return {
|
|
44
|
+
config,
|
|
45
|
+
route: {
|
|
46
|
+
...resolveRoute(config, { provider, model }),
|
|
47
|
+
...(effort === undefined ? {} : { effort: effort || null }),
|
|
48
|
+
...(fast === true || fast === false ? { fast } : {}),
|
|
49
|
+
...(modelParameters && typeof modelParameters === 'object'
|
|
50
|
+
? { modelParameters: { ...modelParameters } }
|
|
51
|
+
: {}),
|
|
52
|
+
},
|
|
53
|
+
webSearchRoute: webSearchRouteOrDefault(config.webSearchRoute),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
23
57
|
export function createConfigLifecycle({
|
|
24
58
|
// config mutable-state injection
|
|
25
59
|
getConfig,
|
|
@@ -393,3 +393,22 @@ export function createContextStatus({
|
|
|
393
393
|
|
|
394
394
|
return { contextStatus, invalidateContextStatusCache };
|
|
395
395
|
}
|
|
396
|
+
|
|
397
|
+
// One-shot gauge for a session that is NOT the runtime's current session (a
|
|
398
|
+
// listed/foreign session). The route is read off the session itself and the
|
|
399
|
+
// calculator is thrown away with its cache, so a foreign read can never poison
|
|
400
|
+
// the live gauge's memoization.
|
|
401
|
+
export function contextStatusForSession(session, { getMode, fallbackCwd = '' } = {}) {
|
|
402
|
+
if (!session || typeof session !== 'object') return null;
|
|
403
|
+
const { contextStatus } = createContextStatus({
|
|
404
|
+
getSession: () => session,
|
|
405
|
+
getRoute: () => ({
|
|
406
|
+
provider: session.provider || '',
|
|
407
|
+
model: session.model || '',
|
|
408
|
+
contextWindow: session.contextWindow || null,
|
|
409
|
+
}),
|
|
410
|
+
getCurrentCwd: () => session.cwd || fallbackCwd,
|
|
411
|
+
getMode,
|
|
412
|
+
});
|
|
413
|
+
return contextStatus();
|
|
414
|
+
}
|
|
@@ -23,7 +23,6 @@ export function createCwdPlugins({
|
|
|
23
23
|
isCodeGraphPrewarmLazy,
|
|
24
24
|
isCodeGraphFirstTurnPrewarmDone,
|
|
25
25
|
getCodeGraphPrewarmDelayMs,
|
|
26
|
-
setSessionNeedsCwdRefresh,
|
|
27
26
|
// callbacks / deps
|
|
28
27
|
scheduleCodeGraphPrewarm,
|
|
29
28
|
hooks,
|
|
@@ -99,11 +98,11 @@ export function createCwdPlugins({
|
|
|
99
98
|
return next;
|
|
100
99
|
}
|
|
101
100
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
} = {}) {
|
|
101
|
+
// cwd changes NEVER recreate the session and never reload MCP or Skills
|
|
102
|
+
// (extension settings are global): a mid-conversation cwd switch must
|
|
103
|
+
// preserve the full message history. The live execution cwd is retargeted in
|
|
104
|
+
// place; the BP3 session snapshot remains the session-start environment.
|
|
105
|
+
function applyResolvedCwd(nextCwd, { persistProjectSelection = false } = {}) {
|
|
107
106
|
const resolved = resolve(nextCwd);
|
|
108
107
|
const stat = statSync(resolved);
|
|
109
108
|
if (!stat.isDirectory()) throw new Error(`cwd: not a directory: ${resolved}`);
|
|
@@ -114,22 +113,18 @@ export function createCwdPlugins({
|
|
|
114
113
|
if (session) session.cwd = currentCwd;
|
|
115
114
|
updateCurrentCwdOverride?.(currentCwd);
|
|
116
115
|
writeLastSessionCwd(currentCwd, session?.clientHostPid);
|
|
117
|
-
if (persistProjectSelection
|
|
118
|
-
const desktop = getDesktopSession?.() || session
|
|
116
|
+
if (persistProjectSelection) {
|
|
117
|
+
const desktop = getDesktopSession?.() || session?.desktopSession;
|
|
119
118
|
if (desktop && typeof desktop === 'object') {
|
|
120
119
|
const nextDesktop = { classification: 'project', projectPath: currentCwd };
|
|
121
120
|
setDesktopSession?.(nextDesktop);
|
|
122
|
-
session.desktopSession = nextDesktop;
|
|
121
|
+
if (session) session.desktopSession = nextDesktop;
|
|
122
|
+
}
|
|
123
|
+
if (session) {
|
|
124
|
+
session.updatedAt = Date.now();
|
|
125
|
+
persistSession(session);
|
|
123
126
|
}
|
|
124
|
-
session.updatedAt = Date.now();
|
|
125
|
-
persistSession(session);
|
|
126
127
|
}
|
|
127
|
-
// cwd changes NEVER recreate the session: a mid-conversation cwd switch must
|
|
128
|
-
// preserve the full message history. We retarget the live execution cwd in
|
|
129
|
-
// place; the BP3 session snapshot remains the session-start environment.
|
|
130
|
-
// `markRefresh`/`changed` are kept only for signature compatibility with
|
|
131
|
-
// existing callers.
|
|
132
|
-
void markRefresh;
|
|
133
128
|
// Lazy mode: before the first turn (e.g. the initial project-selection
|
|
134
129
|
// cwd set), do NOT prewarm — that is exactly the post-first-frame freeze
|
|
135
130
|
// we are avoiding. Once a turn has run, an in-session cwd switch DOES
|
|
@@ -140,8 +135,6 @@ export function createCwdPlugins({
|
|
|
140
135
|
const delay = getCodeGraphPrewarmDelayMs();
|
|
141
136
|
scheduleCodeGraphPrewarm(changed ? 0 : delay, changed ? 'cwd-change' : 'cwd');
|
|
142
137
|
}
|
|
143
|
-
// Extension settings are global; cwd changes never reload MCP or Skills.
|
|
144
|
-
void waitForMcpReset;
|
|
145
138
|
// CwdChanged: bridge an effective cwd switch to the standard hook bus.
|
|
146
139
|
// No matcher event — payload is minimal { cwd }. Fire-and-forget.
|
|
147
140
|
if (changed) {
|
|
@@ -150,15 +143,6 @@ export function createCwdPlugins({
|
|
|
150
143
|
return currentCwd;
|
|
151
144
|
}
|
|
152
145
|
|
|
153
|
-
async function refreshSessionForCwdIfNeeded(reason = 'cwd-change') {
|
|
154
|
-
// No-op: cwd changes are applied in place by applyResolvedCwd and never
|
|
155
|
-
// tear down the session. Retained as a stable hook for ask()'s pre-turn
|
|
156
|
-
// call so the surrounding turn flow is unchanged.
|
|
157
|
-
void reason;
|
|
158
|
-
setSessionNeedsCwdRefresh(false);
|
|
159
|
-
return getSession();
|
|
160
|
-
}
|
|
161
|
-
|
|
162
146
|
function pluginsStatus() {
|
|
163
147
|
const config = getConfig();
|
|
164
148
|
const dataDir = cfgMod.getPluginData?.();
|
|
@@ -272,7 +256,6 @@ export function createCwdPlugins({
|
|
|
272
256
|
return {
|
|
273
257
|
resolveCwdPath,
|
|
274
258
|
applyResolvedCwd,
|
|
275
|
-
refreshSessionForCwdIfNeeded,
|
|
276
259
|
pluginsStatus,
|
|
277
260
|
formatCoreMemoryLines,
|
|
278
261
|
loadCoreMemoryContext,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { GOAL_TASK_SETTLED, GOAL_TASK_STATUSES, MAX_GOAL_TASKS } from './goal-tool-defs.mjs';
|
|
1
|
+
import { GOAL_TASK_SETTLED, GOAL_TASK_STATUSES, MAX_GOAL_TASKS, MAX_GOAL_TASK_TEXT_LENGTH } from './goal-tool-defs.mjs';
|
|
2
2
|
import { clean } from '../runtime/shared/clean.mjs';
|
|
3
3
|
|
|
4
4
|
// Dropped rows remain in the record but are no longer requested work.
|
|
@@ -42,7 +42,7 @@ export function normalizeGoalTasks(input, previous = [], { strict = false } = {}
|
|
|
42
42
|
if (!source || typeof source !== 'object') throw new Error(`goal task ${index + 1} is invalid`);
|
|
43
43
|
const text = clean(source.text);
|
|
44
44
|
if (!text) throw new Error('goal task text is required');
|
|
45
|
-
if ([...text].length >
|
|
45
|
+
if ([...text].length > MAX_GOAL_TASK_TEXT_LENGTH) throw new Error(`goal task exceeds ${MAX_GOAL_TASK_TEXT_LENGTH} characters`);
|
|
46
46
|
if (seenText.has(text)) throw new Error(`duplicate goal task: ${text}`);
|
|
47
47
|
seenText.add(text);
|
|
48
48
|
const id = clean(source.id) || clean(previousByText.get(text)?.id) || `task_${nextId++}`;
|
|
@@ -4,10 +4,11 @@ export const GOAL_TASK_STATUSES = Object.freeze([
|
|
|
4
4
|
]);
|
|
5
5
|
export const GOAL_TASK_SETTLED = Object.freeze(['completed', 'dropped']);
|
|
6
6
|
export const MAX_GOAL_TASKS = 20;
|
|
7
|
+
export const MAX_GOAL_TASK_TEXT_LENGTH = 500;
|
|
7
8
|
|
|
8
9
|
const taskFields = {
|
|
9
10
|
id: { type: 'string', description: 'Stable task id; omit only for new tasks.' },
|
|
10
|
-
text: { type: 'string', description: 'Required work or verification outcome.' },
|
|
11
|
+
text: { type: 'string', minLength: 1, maxLength: MAX_GOAL_TASK_TEXT_LENGTH, description: 'Required work or verification outcome.' },
|
|
11
12
|
status: {
|
|
12
13
|
type: 'string',
|
|
13
14
|
enum: GOAL_TASK_STATUSES,
|
|
@@ -47,7 +48,7 @@ export const GOAL_TOOL_DEFS = Object.freeze([{
|
|
|
47
48
|
tasks: {
|
|
48
49
|
type: 'array', minItems: 1, maxItems: MAX_GOAL_TASKS,
|
|
49
50
|
items: { type: 'object', properties: taskFields, required: ['text', 'status', 'kind'], additionalProperties: false },
|
|
50
|
-
description:
|
|
51
|
+
description: `create/set_tasks: full list; update_tasks/resume: new tasks, no ids. Max ${MAX_GOAL_TASKS} total, including completed/dropped.`,
|
|
51
52
|
},
|
|
52
53
|
updates: {
|
|
53
54
|
type: 'array', minItems: 1, maxItems: MAX_GOAL_TASKS,
|
|
@@ -15,6 +15,7 @@ import { featureEnvOverride } from './config-helpers.mjs';
|
|
|
15
15
|
import { renderToolSearch } from './tool-catalog.mjs';
|
|
16
16
|
import { clean } from './session-text.mjs';
|
|
17
17
|
import { STANDALONE_DATA_DIR } from './runtime-paths.mjs';
|
|
18
|
+
import { listProjects } from '../standalone/projects.mjs';
|
|
18
19
|
import {
|
|
19
20
|
dispatchWebSearchRuntimeTool,
|
|
20
21
|
memoryToolArgsForCaller,
|
|
@@ -149,6 +150,12 @@ export function createInternalToolExecutor({
|
|
|
149
150
|
if (name === 'cwd') {
|
|
150
151
|
const action = clean(args?.action || (args?.path ? 'set' : 'get')).toLowerCase();
|
|
151
152
|
let currentCwd = callerCwd;
|
|
153
|
+
if (action === 'list') {
|
|
154
|
+
return JSON.stringify({
|
|
155
|
+
cwd: currentCwd,
|
|
156
|
+
projects: listProjects().map((project) => ({ name: project.name, path: project.path })),
|
|
157
|
+
}, null, 2);
|
|
158
|
+
}
|
|
152
159
|
if (action === 'set') {
|
|
153
160
|
const rawPath = clean(args?.path);
|
|
154
161
|
if (!rawPath) throw new Error('cwd: path is required for action=set');
|