mixdog 1.3.3 → 1.3.5
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/LICENSES/oh-my-pi-MIT.txt +23 -0
- package/NOTICE.md +15 -0
- package/README.md +11 -0
- package/package.json +3 -2
- package/scripts/embed-google-oauth-client.mjs +49 -0
- package/scripts/lib/test-spawn-ledger.mjs +46 -1
- package/scripts/test.mjs +5 -0
- package/src/defaults/shipped-history.json +8 -0
- package/src/defaults/skills/browser-use/SKILL.md +21 -9
- package/src/defaults/skills/setup/SKILL.md +7 -2
- package/src/defaults/skills/setup/references/actions.md +82 -27
- package/src/defaults/skills/setup/references/surfaces.md +67 -34
- package/src/defaults/skills/setup-guide/SKILL.md +3 -3
- package/src/lib/keychain-cjs.cjs +359 -29
- package/src/rules/agent-mode/AGENT_MODE.md +10 -2
- package/src/rules/agent-mode/MIXY.md +12 -0
- package/src/runtime/agent/orchestrator/agent-runtime/heartbeat-completion.mjs +193 -0
- package/src/runtime/agent/orchestrator/config.mjs +18 -0
- package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +2 -2
- package/src/runtime/agent/orchestrator/mcp/client.mjs +6 -2
- package/src/runtime/agent/orchestrator/mcp/oauth-callback.mjs +31 -2
- package/src/runtime/agent/orchestrator/mcp/oauth-client.mjs +16 -3
- package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +42 -7
- package/src/runtime/agent/orchestrator/mcp/oauth-provider.mjs +21 -2
- package/src/runtime/agent/orchestrator/mcp/oauth-shared.mjs +23 -0
- package/src/runtime/agent/orchestrator/mcp/oauth.mjs +11 -1
- package/src/runtime/agent/orchestrator/providers/account-pool.mjs +68 -23
- package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +22 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +18 -5
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +15 -3
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-catalog.mjs +52 -8
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +10 -1
- package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +22 -6
- package/src/runtime/agent/orchestrator/providers/antigravity-response.mjs +15 -4
- package/src/runtime/agent/orchestrator/providers/antigravity-stream.mjs +17 -2
- package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +19 -2
- package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/cursor-wire-normalization.mjs +10 -4
- package/src/runtime/agent/orchestrator/providers/cursor-wire-protobuf.mjs +10 -217
- package/src/runtime/agent/orchestrator/providers/cursor.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/devin-auth.mjs +409 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-families.mjs +187 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-identity.mjs +342 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-models.mjs +541 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-protobuf.mjs +305 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-request.mjs +558 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-schema.mjs +880 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-session.mjs +190 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-tool-args.mjs +351 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-transport.mjs +560 -0
- package/src/runtime/agent/orchestrator/providers/devin.mjs +491 -0
- package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/gemini-output-limit.mjs +46 -0
- package/src/runtime/agent/orchestrator/providers/gemini-reasoning-relay.mjs +30 -0
- package/src/runtime/agent/orchestrator/providers/gemini-request-body.mjs +7 -11
- package/src/runtime/agent/orchestrator/providers/gemini-retry-policy.mjs +61 -22
- package/src/runtime/agent/orchestrator/providers/gemini-sdk-request.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +71 -4
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +25 -2
- package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +96 -5
- package/src/runtime/agent/orchestrator/providers/lib/tail-reminder-split.mjs +23 -5
- package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +17 -0
- package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +39 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/openai-error-contract.mjs +6 -1
- package/src/runtime/agent/orchestrator/providers/opencode-go-gemini.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/provider-availability.mjs +19 -0
- package/src/runtime/agent/orchestrator/providers/registry.mjs +17 -3
- package/src/runtime/agent/orchestrator/providers/wire-protobuf-codec.mjs +368 -0
- package/src/runtime/agent/orchestrator/runtime-core/builtin-features.mjs +2 -0
- package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +1 -1
- package/src/runtime/agent/orchestrator/runtime-core/reaction-tool-defs.mjs +133 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-data.mjs +3 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +23 -5
- package/src/runtime/agent/orchestrator/runtime-core/tool-profile.mjs +35 -10
- package/src/runtime/agent/orchestrator/runtime-core/workflow/agents.mjs +57 -10
- package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +9 -6
- package/src/runtime/agent/orchestrator/runtime-core/workflow-ids.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +18 -0
- package/src/runtime/agent/orchestrator/session/agent-room.d.mts +0 -3
- package/src/runtime/agent/orchestrator/session/agent-room.mjs +10 -4
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +15 -3
- package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +5 -0
- package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +36 -4
- package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +15 -3
- package/src/runtime/agent/orchestrator/session/loop/assistant-commit.mjs +8 -0
- package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +8 -1
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/segments.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +50 -2
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +11 -2
- package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +11 -2
- package/src/runtime/agent/orchestrator/session/loop/turn-limits.mjs +60 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-commit.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +14 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/manager/session-tool-surface.mjs +13 -0
- package/src/runtime/agent/orchestrator/session/message-ids.mjs +5 -3
- package/src/runtime/agent/orchestrator/session/message-reactions.mjs +89 -0
- package/src/runtime/agent/orchestrator/session/message-reactions.test-support.mjs +75 -0
- package/src/runtime/agent/orchestrator/session/routine-result.d.mts +0 -2
- package/src/runtime/agent/orchestrator/session/routine-result.mjs +11 -2
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +20 -2
- package/src/runtime/agent/orchestrator/session/store-agent-worker-header-cache.mjs +85 -0
- package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +125 -12
- package/src/runtime/agent/orchestrator/session/tail-reminder-rules.mjs +11 -5
- package/src/runtime/agent/orchestrator/session/tool-batch/state.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/tool-batch.mjs +5 -0
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +7 -1
- package/src/runtime/channels/lib/schedule-errors.mjs +51 -0
- package/src/runtime/channels/lib/scheduler.mjs +109 -14
- package/src/runtime/channels/lib/schedules-db.mjs +49 -0
- package/src/runtime/channels/lib/tool-dispatch.mjs +13 -0
- package/src/runtime/channels/lib/worker-main.mjs +9 -0
- package/src/runtime/office/com/com-adapter.mjs +17 -1
- package/src/runtime/office/com/office-com-host.ps1 +51 -1
- package/src/runtime/office/pdf/cfb-fixture.mjs +328 -0
- package/src/runtime/office/pdf/cfb-protection.mjs +311 -0
- package/src/runtime/office/pdf/document-preview.mjs +326 -8
- package/src/runtime/office/pdf/ooxml-crypto.mjs +469 -0
- package/src/runtime/office/pdf/ooxml-key-worker.mjs +14 -0
- package/src/runtime/office/portable/portable-soffice.mjs +45 -5
- package/src/runtime/shared/agent-room-host.mjs +1 -1
- package/src/runtime/shared/bot-hops.mjs +62 -2
- package/src/runtime/shared/config.mjs +49 -22
- package/src/runtime/shared/connector-catalog.d.mts +4 -2
- package/src/runtime/shared/connector-catalog.mjs +16 -8
- package/src/runtime/shared/developer-options.mjs +32 -1
- package/src/runtime/shared/google-oauth-client.generated.json +4 -0
- package/src/runtime/shared/google-oauth-client.mjs +34 -9
- package/src/runtime/shared/llm/anthropic-betas.mjs +7 -0
- package/src/runtime/shared/llm/anthropic-thinking-contract.mjs +12 -0
- package/src/runtime/shared/llm/provider-catalog-registry.mjs +1 -0
- package/src/runtime/shared/llm/usage-rollup.mjs +4 -383
- package/src/runtime/shared/llm/well-formed.mjs +66 -0
- package/src/runtime/shared/provider-accounts.mjs +224 -58
- package/src/runtime/shared/provider-api-key.mjs +19 -8
- package/src/runtime/shared/routine-trigger.mjs +42 -0
- package/src/runtime/shared/tool-execution-contract.mjs +2 -0
- package/src/runtime/shared/transcript-metadata.mjs +38 -0
- package/src/runtime/shared/user-data-guard.mjs +30 -2
- package/src/runtime/shared/worktrees/activity-gate.mjs +12 -5
- package/src/session-runtime/agent-home-delivery.mjs +57 -13
- package/src/session-runtime/agent-room-delivery.mjs +28 -1
- package/src/session-runtime/agent-task-sessions.mjs +292 -79
- package/src/session-runtime/agent-tool-routing.mjs +49 -4
- package/src/session-runtime/boot/begin.mjs +3 -2
- package/src/session-runtime/boot/tools.mjs +4 -0
- package/src/session-runtime/bot-chain.mjs +32 -0
- package/src/session-runtime/bot-status.mjs +7 -4
- package/src/session-runtime/channel-config-api.mjs +63 -17
- package/src/session-runtime/internal-tool-executor/reaction-tool.mjs +45 -0
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +72 -5
- package/src/session-runtime/internal-tool-executor.mjs +4 -0
- package/src/session-runtime/mcp-glue/server-catalog.mjs +7 -1
- package/src/session-runtime/mixy-heartbeat.mjs +807 -0
- package/src/session-runtime/model-route/route-tuning.mjs +10 -6
- package/src/session-runtime/owned-automation-run.mjs +33 -12
- package/src/session-runtime/provider-auth/login.mjs +7 -0
- package/src/session-runtime/provider-models/row-hydration.mjs +13 -2
- package/src/session-runtime/routine-home-turn.mjs +33 -6
- package/src/session-runtime/schedule-session-run.mjs +7 -3
- package/src/session-runtime/services/agent-tool/close-flow.mjs +14 -2
- package/src/session-runtime/services/agent-tool/execute.mjs +6 -1
- package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +11 -2
- package/src/session-runtime/services/agent-tool/render.mjs +3 -1
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-plan.mjs +8 -12
- package/src/session-runtime/services/agent-tool/tool-def.mjs +1 -1
- package/src/session-runtime/services/channel-admin.mjs +66 -18
- package/src/session-runtime/services/channel-worker.mjs +1 -1
- package/src/session-runtime/services/provider-admin.mjs +128 -25
- package/src/session-runtime/services/schedule-access.mjs +14 -0
- package/src/session-runtime/services/usage-dashboard-model.mjs +1 -0
- package/src/session-runtime/session-create/session-options.mjs +7 -4
- package/src/session-runtime/settings-system-api.mjs +16 -1
- package/src/session-runtime/setup-tool/executor.mjs +49 -14
- package/src/session-runtime/setup-tool/extended-actions.mjs +29 -3
- package/src/session-runtime/setup-tool/settings-contract.mjs +65 -4
- package/src/session-runtime/setup-tool/tool-defs.mjs +30 -3
- package/src/session-runtime/task-session-route.mjs +99 -0
- package/src/session-runtime/tool-policy/policy-stage.mjs +5 -2
- package/src/session-runtime/tool-policy/surface-helpers.mjs +2 -3
- package/src/session-runtime/tool-surface.mjs +4 -0
- package/src/session-runtime/usage-stats-api.mjs +1 -1
- package/src/session-runtime/workflow-agents-api/agent-editor.mjs +98 -21
- package/src/session-runtime/workflow-agents-api/workflow-packs.mjs +6 -1
- package/src/session-runtime/workspace/transitions.mjs +10 -2
- package/src/standalone/daemon-session-control.mjs +88 -20
- package/src/standalone/daemon.mjs +8 -0
- package/src/standalone/session-protocol.mjs +5 -0
- package/src/standalone/session-service/agent-tree/agent-cancel.mjs +6 -2
- package/src/standalone/session-service/session-calls/session-turns.mjs +6 -0
- package/src/standalone/session-service.mjs +13 -0
- package/src/standalone/session-tool-output.mjs +8 -5
- package/src/tui/app/model-options.mjs +2 -0
- package/src/tui/dist/index.mjs +335 -172
- package/src/tui/session/notice-surface.mjs +17 -1
- package/src/tui/session/session-api/integrations.mjs +1 -0
- package/src/tui/session/session-api/lifecycle.mjs +8 -0
- package/src/tui/session/session-api/settings.mjs +14 -2
- package/src/tui/session/turn-stream.mjs +17 -1
- package/src/tui/session/turn.mjs +22 -2
- package/src/vendor/statusline/src/gateway/claude-current.mjs +0 -58
- package/src/vendor/statusline/src/gateway/route-meta.mjs +1 -361
- package/src/vendor/statusline/src/gateway/session-routes.mjs +0 -55
- package/src/vendor/statusline/scripts/lib/gateway-settings.mjs +0 -285
|
@@ -38,7 +38,7 @@ export const SETUP_EXTENDED_ACTION_FIELDS = Object.freeze({
|
|
|
38
38
|
add_hook_rule: 'hookRule',
|
|
39
39
|
set_hook_rule_enabled: 'ruleIndex enabled',
|
|
40
40
|
delete_hook_rule: 'ruleIndex',
|
|
41
|
-
set_media_default: 'kind lane model?',
|
|
41
|
+
set_media_default: 'kind lane model? accountId?',
|
|
42
42
|
});
|
|
43
43
|
|
|
44
44
|
/** Mirrors RULE_ACTIONS in services/hook-bus/rule-store.mjs. */
|
|
@@ -177,6 +177,10 @@ export const SETUP_EXTENDED_PROPERTIES = Object.freeze({
|
|
|
177
177
|
entry: object(
|
|
178
178
|
{
|
|
179
179
|
name: text,
|
|
180
|
+
newName: {
|
|
181
|
+
...text,
|
|
182
|
+
description: 'Schedule only, with overwrite: rename the schedule `name` to this; its history is kept.',
|
|
183
|
+
},
|
|
180
184
|
description: text,
|
|
181
185
|
instructions: text,
|
|
182
186
|
time: { ...text, description: 'Schedule cron expression. Mutually exclusive with at.' },
|
|
@@ -234,6 +238,30 @@ export const SETUP_EXTENDED_PROPERTIES = Object.freeze({
|
|
|
234
238
|
maximum: 400,
|
|
235
239
|
description: 'Windows only: notification sound volume percent.',
|
|
236
240
|
},
|
|
241
|
+
botOverlay: object(
|
|
242
|
+
{
|
|
243
|
+
enabled: {
|
|
244
|
+
...boolean,
|
|
245
|
+
description: 'Show the representative bot overlay; refused while no representative bot exists.',
|
|
246
|
+
},
|
|
247
|
+
position: {
|
|
248
|
+
type: 'string',
|
|
249
|
+
enum: ['pinned', 'pinnedHere', 'free', 'onScreen'],
|
|
250
|
+
description:
|
|
251
|
+
'pinned = pin above the message box (default); pinnedHere = pin here; free = free in the app window; onScreen = free on screen.',
|
|
252
|
+
},
|
|
253
|
+
keepAbove: { ...boolean, description: 'Keep above other windows (default off).' },
|
|
254
|
+
collapsed: { ...boolean, description: 'Collapse the bubble so only the bot shows (default off).' },
|
|
255
|
+
autoAdvance: { ...boolean, description: 'Auto-advance long replies (default on).' },
|
|
256
|
+
autoClose: { ...boolean, description: 'Close replies automatically once read (default on).' },
|
|
257
|
+
scale: {
|
|
258
|
+
type: 'integer',
|
|
259
|
+
enum: [50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160, 170, 180, 190, 200],
|
|
260
|
+
description: 'Bot size in percent (default 100).',
|
|
261
|
+
},
|
|
262
|
+
},
|
|
263
|
+
[]
|
|
264
|
+
),
|
|
237
265
|
}),
|
|
238
266
|
appearance: object({
|
|
239
267
|
theme: { type: 'string', enum: ['system', 'dark', 'white'] },
|
|
@@ -242,6 +270,34 @@ export const SETUP_EXTENDED_PROPERTIES = Object.freeze({
|
|
|
242
270
|
description: 'Desktop UI language id from status appearance; independent of profile language.',
|
|
243
271
|
},
|
|
244
272
|
sidePanels: { type: 'string', enum: ['close-left', 'close-right', 'close-both', 'keep-open'] },
|
|
273
|
+
sidePanelPosition: {
|
|
274
|
+
type: 'string',
|
|
275
|
+
enum: ['right', 'bottom', 'auto'],
|
|
276
|
+
description: 'Where the side dock sits on desktop: beside the chat, below it, or by window shape (default right).',
|
|
277
|
+
},
|
|
278
|
+
timeFormat: {
|
|
279
|
+
type: 'string',
|
|
280
|
+
enum: ['system', '12h', '24h'],
|
|
281
|
+
description: 'Clock style for every time of day shown (default system = the UI language own clock).',
|
|
282
|
+
},
|
|
283
|
+
uiScale: {
|
|
284
|
+
type: 'number',
|
|
285
|
+
minimum: 0.85,
|
|
286
|
+
maximum: 1.2,
|
|
287
|
+
description: 'UI size multiplier, 0.85-1.2 in steps of 0.05 (default 1).',
|
|
288
|
+
},
|
|
289
|
+
codeSize: {
|
|
290
|
+
type: 'integer',
|
|
291
|
+
minimum: 10,
|
|
292
|
+
maximum: 20,
|
|
293
|
+
description: 'Code text size in px, 10-20 (default 13).',
|
|
294
|
+
},
|
|
295
|
+
codeLigatures: { ...boolean, description: 'Join symbols such as => into one glyph in code (default on).' },
|
|
296
|
+
chatTextSize: {
|
|
297
|
+
type: 'string',
|
|
298
|
+
enum: ['small', 'default', 'large', 'extra-large'],
|
|
299
|
+
description: 'Conversation and composer text size (default default).',
|
|
300
|
+
},
|
|
245
301
|
markdownDefaultView: { type: 'string', enum: ['preview', 'rich', 'source'] },
|
|
246
302
|
linkPreview: boolean,
|
|
247
303
|
toolActivityExpansion: { type: 'string', enum: ['collapsed', 'commands', 'all'] },
|
|
@@ -290,10 +346,15 @@ export const SETUP_EXTENDED_PROPERTIES = Object.freeze({
|
|
|
290
346
|
minimum: 0,
|
|
291
347
|
description: 'set_hook_rule_enabled/delete_hook_rule: rule index from status hooks.',
|
|
292
348
|
},
|
|
293
|
-
lane: {
|
|
349
|
+
lane: {
|
|
350
|
+
...text,
|
|
351
|
+
description:
|
|
352
|
+
'set_media_default: lane id from the media catalog to pin as the default generator for the kind; "" clears the pin (back to Auto).',
|
|
353
|
+
},
|
|
294
354
|
model: {
|
|
295
355
|
...nonBlank,
|
|
296
|
-
description:
|
|
356
|
+
description:
|
|
357
|
+
'set_media_default: model id offered by the lane for the kind; omitted = the lane default. set_model_preferences: model id of the provider.',
|
|
297
358
|
},
|
|
298
359
|
project: object({ path: text, alias: text }, ['path']),
|
|
299
360
|
projectPath: { ...text, description: 'Exact registered Project root.' },
|
|
@@ -306,7 +367,7 @@ export const SETUP_EXTENDED_PROPERTIES = Object.freeze({
|
|
|
306
367
|
riskAccepted: {
|
|
307
368
|
...boolean,
|
|
308
369
|
description:
|
|
309
|
-
"set_developer_option: the user's explicit acceptance of the option warning; required to enable an option that has one.",
|
|
370
|
+
"set_developer_option: the user's explicit acceptance of the option warning; required to enable an option that has one. Exception: allowAllActions (allow all actions except payments and permanent deletes) can only be turned on by the person in the desktop app; it is rejected regardless of riskAccepted, and remote enablement is rejected too.",
|
|
310
371
|
},
|
|
311
372
|
});
|
|
312
373
|
|
|
@@ -9,6 +9,7 @@ import { INSTRUCTION_FILES_MODES } from '../../runtime/shared/instruction-files-
|
|
|
9
9
|
import { AUTO_EFFORT_BASES } from '../../runtime/agent/orchestrator/providers/auto-effort.mjs';
|
|
10
10
|
import { WORKTREE_NEW_TASK_DEFAULTS } from '../../runtime/shared/worktrees/settings.mjs';
|
|
11
11
|
import { CONNECTOR_IDS } from '../../runtime/shared/connector-catalog.mjs';
|
|
12
|
+
import { COMPACTION_SUMMARY_MODES } from '../../runtime/agent/orchestrator/session/compact/summary-mode.mjs';
|
|
12
13
|
|
|
13
14
|
/** Built-in `setup` tool: the model's handle on a Mixdog user's persisted
|
|
14
15
|
* configuration. The description is a contract only; how to use each action
|
|
@@ -99,6 +100,11 @@ export const SETUP_ACTION_FIELDS = Object.freeze({
|
|
|
99
100
|
delete_local_model: 'confirmationToken',
|
|
100
101
|
set_system_shell: 'command',
|
|
101
102
|
set_auto_update: 'enabled',
|
|
103
|
+
set_ai_session_titles: 'enabled',
|
|
104
|
+
set_quota_auto_resume: 'enabled',
|
|
105
|
+
set_model_preferences: 'provider model hidden? favorite?',
|
|
106
|
+
set_heartbeat_enabled: 'enabled',
|
|
107
|
+
set_reactions_enabled: 'enabled',
|
|
102
108
|
forget_provider_auth: 'name accountId?',
|
|
103
109
|
set_provider_account: 'name providerAccount',
|
|
104
110
|
add_mcp_server: 'server',
|
|
@@ -130,6 +136,7 @@ export const SETUP_BUILTIN_TOGGLE_FEATURES = Object.freeze([
|
|
|
130
136
|
'computer',
|
|
131
137
|
'voice',
|
|
132
138
|
'visualize',
|
|
139
|
+
'media',
|
|
133
140
|
]);
|
|
134
141
|
|
|
135
142
|
const ROUTE_SCHEMA = {
|
|
@@ -248,13 +255,33 @@ export const SETUP_TOOL_DEFS = Object.freeze([
|
|
|
248
255
|
properties: {
|
|
249
256
|
mainBufferTokens: { type: 'integer', minimum: 0 },
|
|
250
257
|
mainBufferPercent: { type: 'number', minimum: 0, maximum: 100 },
|
|
258
|
+
onModelChange: {
|
|
259
|
+
type: 'boolean',
|
|
260
|
+
description: 'Auto-compact a conversation on its current model before it switches to another one.',
|
|
261
|
+
},
|
|
262
|
+
summaryMode: {
|
|
263
|
+
type: 'string',
|
|
264
|
+
enum: [...COMPACTION_SUMMARY_MODES],
|
|
265
|
+
description:
|
|
266
|
+
'Which model writes compaction summaries: smart (default), maintainer (the Maintainer), self (the conversation model).',
|
|
267
|
+
},
|
|
251
268
|
},
|
|
252
|
-
description:
|
|
269
|
+
description:
|
|
270
|
+
'set_compaction: reserved Main context budget (give tokens OR percent, not both), onModelChange and summaryMode; read back with status compaction.',
|
|
253
271
|
},
|
|
254
272
|
enabled: {
|
|
255
273
|
type: 'boolean',
|
|
256
274
|
description:
|
|
257
|
-
'Boolean setters (set_*_enabled, set_compaction, set_auto_update, set_mcp_enabled, set_plugin_enabled).',
|
|
275
|
+
'Boolean setters (set_*_enabled, set_compaction, set_auto_update, set_ai_session_titles, set_quota_auto_resume, set_heartbeat_enabled, set_mcp_enabled, set_plugin_enabled).',
|
|
276
|
+
},
|
|
277
|
+
provider: { type: 'string', description: 'set_model_preferences: provider id.' },
|
|
278
|
+
hidden: {
|
|
279
|
+
type: 'boolean',
|
|
280
|
+
description: 'set_model_preferences: false lists the model in /model, true hides it; omitted = unchanged.',
|
|
281
|
+
},
|
|
282
|
+
favorite: {
|
|
283
|
+
type: 'boolean',
|
|
284
|
+
description: 'set_model_preferences: favorite star on or off; omitted = unchanged.',
|
|
258
285
|
},
|
|
259
286
|
instructionFiles: {
|
|
260
287
|
type: 'string',
|
|
@@ -275,7 +302,7 @@ export const SETUP_TOOL_DEFS = Object.freeze([
|
|
|
275
302
|
accountId: {
|
|
276
303
|
type: 'string',
|
|
277
304
|
description:
|
|
278
|
-
'forget_provider_auth: OAuth account id from status providers with detail:true; required when the provider has several accounts.',
|
|
305
|
+
'forget_provider_auth: OAuth account id from status providers with detail:true; required when the provider has several accounts. set_media_default: optional account of the OAuth lane to pin; omitted = the lane provider\'s selected account.',
|
|
279
306
|
},
|
|
280
307
|
providerAccount: {
|
|
281
308
|
type: 'object',
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* task-session-route.mjs — the model and reasoning effort a bot asks of a
|
|
3
|
+
* task session (the session tool's `model` / `effort`), checked against the
|
|
4
|
+
* provider catalog the desktop model menu lists (listProviderModels rows:
|
|
5
|
+
* provider, id, display, effortOptions).
|
|
6
|
+
*/
|
|
7
|
+
import { clean } from '../runtime/agent/orchestrator/runtime-core/session-text.mjs';
|
|
8
|
+
import { normalizeEffortInput } from '../runtime/agent/orchestrator/runtime-core/effort.mjs';
|
|
9
|
+
|
|
10
|
+
// Choices an error names: a short list, never the whole catalog.
|
|
11
|
+
const CHOICE_LIMIT = 6;
|
|
12
|
+
|
|
13
|
+
const lower = (value) => clean(value).toLowerCase();
|
|
14
|
+
const rowId = (row) => clean(row.id || row.model);
|
|
15
|
+
const sameModel = (row, route) => row.provider === route.provider && lower(rowId(row)) === lower(route.model);
|
|
16
|
+
const effortsOf = (row) => (Array.isArray(row?.effortOptions) ? row.effortOptions.map((option) => option.value) : []);
|
|
17
|
+
const choiceLabel = (row) => `${row.display || rowId(row)} (${row.provider}/${rowId(row)})`;
|
|
18
|
+
|
|
19
|
+
function choices(rows) {
|
|
20
|
+
const shown = rows.slice(0, CHOICE_LIMIT).map(choiceLabel).join('; ');
|
|
21
|
+
return rows.length > CHOICE_LIMIT ? `${shown}; … (${rows.length} total)` : shown;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** An effort level word ("max") or null; "auto" is no level. */
|
|
25
|
+
function effortWord(word) {
|
|
26
|
+
try {
|
|
27
|
+
return normalizeEffortInput(word);
|
|
28
|
+
} catch {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** The catalog rows of the model menu, read through session `sessionId`'s runtime. */
|
|
34
|
+
export async function readModelCatalog(service, sessionId) {
|
|
35
|
+
const result = await service.readSession({ sessionId, action: 'listProviderModels', args: [{}] });
|
|
36
|
+
return Array.isArray(result?.value) ? result.value : [];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** The route of a catalog display name (case-insensitive, exact), with a
|
|
40
|
+
* trailing effort word ("Claude Opus 5.5 max", or auto) read as the effort
|
|
41
|
+
* only when the whole text names no model. With `explicitEffort` the word
|
|
42
|
+
* only selects the name: the caller applies its own effort. Throws with the
|
|
43
|
+
* choices when the text names no model or several. */
|
|
44
|
+
export function routeByDisplayName(text, rows, explicitEffort = false) {
|
|
45
|
+
const wanted = lower(text);
|
|
46
|
+
const cut = wanted.lastIndexOf(' ');
|
|
47
|
+
const word = cut > 0 ? wanted.slice(cut + 1) : '';
|
|
48
|
+
const suffix = word === 'auto' || effortWord(word) ? word : null;
|
|
49
|
+
let hits = rows.filter((row) => lower(row.display) === wanted);
|
|
50
|
+
let effort = null;
|
|
51
|
+
if (!hits.length && suffix) {
|
|
52
|
+
const base = wanted.slice(0, cut).trim();
|
|
53
|
+
hits = rows.filter((row) => lower(row.display) === base);
|
|
54
|
+
effort = suffix;
|
|
55
|
+
}
|
|
56
|
+
if (!hits.length) {
|
|
57
|
+
const words = wanted.split(/\s+/).filter(Boolean);
|
|
58
|
+
const near = rows.filter((row) => words.every((word) => lower(row.display).includes(word)));
|
|
59
|
+
throw new Error(
|
|
60
|
+
`session: model "${clean(text)}" is neither a provider/model route, a preset name nor a model display name. ` +
|
|
61
|
+
`Choices: ${choices(near.length ? near : rows)}${suffix ? '. To set the reasoning effort, pass it as the effort field' : ''}`
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
const distinct = [...new Map(hits.map((row) => [`${row.provider}/${rowId(row)}`, row])).values()];
|
|
65
|
+
if (distinct.length > 1) {
|
|
66
|
+
throw new Error(`session: model "${clean(text)}" is ambiguous; use provider/model: ${choices(distinct)}`);
|
|
67
|
+
}
|
|
68
|
+
const route = { provider: distinct[0].provider, model: rowId(distinct[0]) };
|
|
69
|
+
return effort && !explicitEffort ? withEffort(route, effort, rows) : route;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** `route` with the effort level `value`, checked against the levels its
|
|
73
|
+
* model supports ("auto" clears it). A chosen level turns the route's Auto
|
|
74
|
+
* effort off, as the menu's effort pick does, or Auto would replace it. */
|
|
75
|
+
export function withEffort(route, value, rows) {
|
|
76
|
+
const level = checkEffort(value, route, rows);
|
|
77
|
+
const { effort: _previous, ...rest } = route;
|
|
78
|
+
return level ? { ...rest, effort: level, autoEffort: false } : rest;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** The effort level `value` names for `route` ("auto" is null); throws with
|
|
82
|
+
* the model's levels when it is unknown or unsupported. */
|
|
83
|
+
export function checkEffort(value, route, rows) {
|
|
84
|
+
const row = rows.find((candidate) => sameModel(candidate, route));
|
|
85
|
+
const allowed = row ? effortsOf(row) : null;
|
|
86
|
+
const offer = allowed
|
|
87
|
+
? `; ${route.provider}/${route.model} supports: auto${allowed.map((level) => `, ${level}`).join('')}`
|
|
88
|
+
: '';
|
|
89
|
+
let level;
|
|
90
|
+
try {
|
|
91
|
+
level = normalizeEffortInput(value);
|
|
92
|
+
} catch (error) {
|
|
93
|
+
throw new Error(`session: ${error.message}${offer}`);
|
|
94
|
+
}
|
|
95
|
+
if (level && allowed && !allowed.includes(level)) {
|
|
96
|
+
throw new Error(`session: effort "${level}" is not supported${offer}`);
|
|
97
|
+
}
|
|
98
|
+
return level;
|
|
99
|
+
}
|
|
@@ -18,7 +18,9 @@ import { sessionRolePromptFields } from '../../runtime/agent/orchestrator/sessio
|
|
|
18
18
|
import {
|
|
19
19
|
delegationDisabled,
|
|
20
20
|
honoredSchemaAllowlist,
|
|
21
|
+
reactionDisabled,
|
|
21
22
|
} from '../../runtime/agent/orchestrator/session/manager/session-tool-surface.mjs';
|
|
23
|
+
import { REACTION_TOOL_NAME } from '../../runtime/agent/orchestrator/runtime-core/reaction-tool-defs.mjs';
|
|
22
24
|
import { finalizeSessionToolList } from '../../runtime/agent/orchestrator/session/manager/tool-resolution.mjs';
|
|
23
25
|
import { isAgentOwner } from '../../runtime/agent/orchestrator/agent-owner.mjs';
|
|
24
26
|
import { AGENT_MODE, sessionModeOf } from '../../runtime/agent/orchestrator/session/agent-mode.mjs';
|
|
@@ -40,14 +42,15 @@ export function createPolicyStage({
|
|
|
40
42
|
activeWorkflowContext,
|
|
41
43
|
}) {
|
|
42
44
|
// The tools `session` is denied now: its caller's denials, the feature gates
|
|
43
|
-
// as they stand,
|
|
45
|
+
// as they stand, an agent-mode Lead's agent tool when its workflow
|
|
44
46
|
// delegates to no one (a coding Lead keeps it: workflow.mjs
|
|
45
|
-
// workflowDisallowsAgentTool).
|
|
47
|
+
// workflowDisallowsAgentTool), and react outside a bot's chat with the user.
|
|
46
48
|
function liveDeniedTools(session, workflow) {
|
|
47
49
|
return [
|
|
48
50
|
...session.callerDisallowedTools,
|
|
49
51
|
...featureDisallowedTools(),
|
|
50
52
|
...(workflowDisallowsAgentTool(workflow, { agentMode: sessionModeOf(session) === AGENT_MODE }) ? ['agent'] : []),
|
|
53
|
+
...(reactionDisabled(session, isAgentOwner(session)) ? [REACTION_TOOL_NAME] : []),
|
|
51
54
|
]
|
|
52
55
|
.map((name) => String(name || ''))
|
|
53
56
|
.filter(Boolean);
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
// rewriting its system blocks (BP1 heading, BP3 core), and the configs a
|
|
3
3
|
// session's workflow and orchestration mode give.
|
|
4
4
|
import { AGENT_MODE, resolveSessionMode, sessionModeOf } from '../../runtime/agent/orchestrator/session/agent-mode.mjs';
|
|
5
|
-
import { MIXY_AGENT_ID } from '../../runtime/agent/orchestrator/session/agent-home.mjs';
|
|
6
5
|
import { refreshSessionBp3Environment } from '../../runtime/agent/orchestrator/session/manager/prompt-utils.mjs';
|
|
7
6
|
import { promptOrchestrationModeOf } from '../../runtime/agent/orchestrator/session/manager/orchestration-mode-switch-notice.mjs';
|
|
8
7
|
import { sessionOrchestrationMode } from '../../runtime/shared/orchestration.mjs';
|
|
@@ -77,11 +76,11 @@ export function rewriteBp3Core(session, nextCore) {
|
|
|
77
76
|
}
|
|
78
77
|
|
|
79
78
|
// A Lead's workflow slices (orchestration, agent and contact catalogs) for its
|
|
80
|
-
// mode
|
|
79
|
+
// mode; an agent-mode Lead's contacts are every bot but itself.
|
|
81
80
|
export function leadPartsFor(session, leadPromptParts) {
|
|
82
81
|
return leadPromptParts({
|
|
83
82
|
agentMode: sessionModeOf(session) === AGENT_MODE,
|
|
84
|
-
|
|
83
|
+
agentId: resolveSessionMode({ session }).agentId,
|
|
85
84
|
});
|
|
86
85
|
}
|
|
87
86
|
|
|
@@ -18,6 +18,9 @@ import { workflowDisallowsAgentTool } from '../runtime/agent/orchestrator/runtim
|
|
|
18
18
|
import { promptWorkflowOf } from '../runtime/agent/orchestrator/session/manager/workflow-switch-notice.mjs';
|
|
19
19
|
import { configuredOrchestrationMode } from '../runtime/shared/orchestration.mjs';
|
|
20
20
|
import { isAgentModeSession } from '../runtime/agent/orchestrator/session/agent-mode.mjs';
|
|
21
|
+
import { isAgentOwner } from '../runtime/agent/orchestrator/agent-owner.mjs';
|
|
22
|
+
import { reactionDisabled } from '../runtime/agent/orchestrator/session/manager/session-tool-surface.mjs';
|
|
23
|
+
import { REACTION_TOOL_NAME } from '../runtime/agent/orchestrator/runtime-core/reaction-tool-defs.mjs';
|
|
21
24
|
|
|
22
25
|
export function createToolSurface({
|
|
23
26
|
mgr,
|
|
@@ -78,6 +81,7 @@ export function createToolSurface({
|
|
|
78
81
|
...disallowedModelToolNamesForProfile(profileCandidates, getToolProfile()),
|
|
79
82
|
...getFeatureDisallowedTools(),
|
|
80
83
|
...(workflowAllowsAgents() ? [] : [...agentToolNames]),
|
|
84
|
+
...(reactionDisabled(session, isAgentOwner(session)) ? [REACTION_TOOL_NAME] : []),
|
|
81
85
|
];
|
|
82
86
|
}
|
|
83
87
|
|
|
@@ -203,7 +203,7 @@ export function createUsageStatsApi({
|
|
|
203
203
|
// beyond a clock captured before the import started.
|
|
204
204
|
const now = Date.now();
|
|
205
205
|
const period = usagePeriodFor(options, now);
|
|
206
|
-
// The rollup query runs off the event loop (usage-
|
|
206
|
+
// The rollup query runs off the event loop (usage-ledger-worker.mjs).
|
|
207
207
|
const rollup = await store.rollupAsync(usageRollupQuery(period));
|
|
208
208
|
// The ledger returns the same rollup object while nothing was committed
|
|
209
209
|
// (and while a rolling window lost no record), so identical back-to-back
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { join } from 'node:path';
|
|
1
|
+
import { dirname, isAbsolute, join, relative, sep } from 'node:path';
|
|
2
2
|
import { createHash } from 'node:crypto';
|
|
3
3
|
import {
|
|
4
4
|
accessSync,
|
|
@@ -7,18 +7,19 @@ import {
|
|
|
7
7
|
mkdirSync,
|
|
8
8
|
readFileSync,
|
|
9
9
|
readdirSync,
|
|
10
|
+
realpathSync,
|
|
10
11
|
renameSync,
|
|
11
12
|
rmSync,
|
|
12
13
|
statSync,
|
|
13
14
|
} from 'node:fs';
|
|
14
15
|
import {
|
|
15
16
|
holdsNoData,
|
|
16
|
-
parseJsonText,
|
|
17
17
|
readJsonStateSync,
|
|
18
18
|
writeFileAtomicSync,
|
|
19
19
|
writeJsonAtomicSync,
|
|
20
20
|
} from '../../runtime/shared/atomic-file.mjs';
|
|
21
21
|
import { clean } from '../../runtime/agent/orchestrator/runtime-core/session-text.mjs';
|
|
22
|
+
import { agentEntryFile, agentEntryPath, parseManifestText } from '../../runtime/agent/orchestrator/runtime-core/workflow/agents.mjs';
|
|
22
23
|
import { hasOwn } from '../../runtime/shared/object.mjs';
|
|
23
24
|
import { serializeFrontmatterDoc } from '../../runtime/shared/markdown-frontmatter.mjs';
|
|
24
25
|
import { isHiddenAgent } from '../../runtime/agent/orchestrator/internal-agents.mjs';
|
|
@@ -79,9 +80,43 @@ import {
|
|
|
79
80
|
|
|
80
81
|
const isFixedAgent = (id) => FIXED_AGENT_SLOTS.some((agent) => agent.id === id);
|
|
81
82
|
const userDir = userDefinitionDir;
|
|
82
|
-
|
|
83
|
-
//
|
|
84
|
-
|
|
83
|
+
// The entry file of the agent folder `dir`, exactly as the loader reads it
|
|
84
|
+
// (agent.json `entry`, AGENT.md by default).
|
|
85
|
+
const entryIn = (dir) => agentEntryPath(dir, readManifest(dir));
|
|
86
|
+
const userEntryPath = (dataDir, id) => entryIn(userDir(dataDir, id));
|
|
87
|
+
// An agent exists where its folder has an AGENT.md, as discovery lists it.
|
|
88
|
+
const hasEntryFile = (dir) => existsSync(join(dir, 'AGENT.md'));
|
|
89
|
+
const hasUserAgentFile = (dataDir, id) => hasEntryFile(userDir(dataDir, id));
|
|
90
|
+
const leavesFolder = (rel) => rel === '..' || rel.startsWith(`..${sep}`) || isAbsolute(rel);
|
|
91
|
+
// The file delete moves aside and restore moves back: the agent's entry when
|
|
92
|
+
// it is named inside `dir` and its folder resolves inside too (a symlink as the
|
|
93
|
+
// last part moves as the link alone), else HEAD's AGENT.md. An entry outside the
|
|
94
|
+
// folder (`..`, or through a linked folder) is shared and never moved.
|
|
95
|
+
const archivableEntry = (dir, entry) => {
|
|
96
|
+
try {
|
|
97
|
+
const rel = relative(dir, entry);
|
|
98
|
+
if (rel && !leavesFolder(rel) && !leavesFolder(relative(realpathSync(dir), realpathSync(dirname(entry))))) {
|
|
99
|
+
return entry;
|
|
100
|
+
}
|
|
101
|
+
} catch {
|
|
102
|
+
// an unresolvable folder holds no entry to move
|
|
103
|
+
}
|
|
104
|
+
return join(dir, 'AGENT.md');
|
|
105
|
+
};
|
|
106
|
+
// The entry file the editor may hand out to edit in place: it resolves (symlinks
|
|
107
|
+
// followed) to a file inside its own agent folder, else null. The loader still
|
|
108
|
+
// reads an entry outside; only the editable path is contained.
|
|
109
|
+
const editablePathIn = (dir, path) => {
|
|
110
|
+
try {
|
|
111
|
+
return leavesFolder(relative(realpathSync(dir), realpathSync(path))) ? null : path;
|
|
112
|
+
} catch {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
// An archived (deleted) agent's entry file, moved aside beside the tombstone
|
|
117
|
+
// as `<entry>.archived`: no loader reads it, and restoring moves it back.
|
|
118
|
+
const archivedEntryPath = (entryPath) => `${entryPath}.archived`;
|
|
119
|
+
// The default entry's archive (all that HEAD wrote); a save clears it.
|
|
85
120
|
const ARCHIVED_AGENT_FILE = 'AGENT.md.archived';
|
|
86
121
|
|
|
87
122
|
// Frontmatter the editor does not show (e.g. `permission`), taken from the
|
|
@@ -123,15 +158,19 @@ const writeManifest = (file, manifest) => writeJsonAtomicSync(file, manifest, DE
|
|
|
123
158
|
// no data (empty, or NUL-filled by a crash) or is not a JSON object.
|
|
124
159
|
function readManifestStrict(file) {
|
|
125
160
|
try {
|
|
126
|
-
|
|
127
|
-
if (holdsNoData(text)) return null;
|
|
128
|
-
const value = parseJsonText(text);
|
|
129
|
-
return value && typeof value === 'object' && !Array.isArray(value) ? value : null;
|
|
161
|
+
return readManifestFile(file);
|
|
130
162
|
} catch {
|
|
131
163
|
return null;
|
|
132
164
|
}
|
|
133
165
|
}
|
|
134
166
|
|
|
167
|
+
// The same, but a file that cannot be read throws its error.
|
|
168
|
+
function readManifestFile(file) {
|
|
169
|
+
const text = readFileSync(file, 'utf8');
|
|
170
|
+
if (holdsNoData(text)) return null;
|
|
171
|
+
return parseManifestText(text);
|
|
172
|
+
}
|
|
173
|
+
|
|
135
174
|
// A read-only agent.json is a standing refusal, not a transient lock: the
|
|
136
175
|
// atomic write's rename onto it fails with EPERM and is retried for seconds,
|
|
137
176
|
// synchronously on the host's event loop. Refuse it up front (one stat, no wait),
|
|
@@ -559,10 +598,16 @@ function definitionPresence({ dataDir, rootDir, stat }, agentIds) {
|
|
|
559
598
|
// A definition folder's revision: a hash of the three files an editor writes.
|
|
560
599
|
function definitionDirRevision(dir) {
|
|
561
600
|
const hash = createHash('sha256');
|
|
562
|
-
|
|
601
|
+
const manifest = readManifest(dir);
|
|
602
|
+
const files = [
|
|
603
|
+
['agent.json', join(dir, 'agent.json')],
|
|
604
|
+
[agentEntryFile(manifest), agentEntryPath(dir, manifest)],
|
|
605
|
+
[PERSONA_FILE, join(dir, PERSONA_FILE)],
|
|
606
|
+
];
|
|
607
|
+
for (const [name, file] of files) {
|
|
563
608
|
hash.update(`${name}\0`);
|
|
564
609
|
try {
|
|
565
|
-
hash.update(readFileSync(
|
|
610
|
+
hash.update(readFileSync(file));
|
|
566
611
|
} catch {
|
|
567
612
|
hash.update('\u0001absent');
|
|
568
613
|
}
|
|
@@ -660,13 +705,27 @@ function createAgentArchiveApi({
|
|
|
660
705
|
await announceBotChange(dataDir);
|
|
661
706
|
return { id, deleted: true, revertedToBuiltIn: Boolean(sourceFallback) };
|
|
662
707
|
}
|
|
663
|
-
// A custom agent is archived: its AGENT.md
|
|
664
|
-
//
|
|
665
|
-
// starter from coming back, and the
|
|
666
|
-
// persona, memory, its model and on/off)
|
|
667
|
-
// until restoreAgentDefinition brings it back
|
|
708
|
+
// A custom agent is archived: its entry file (AGENT.md, or the one its
|
|
709
|
+
// agent.json names; what makes it an agent anywhere) moves aside, the
|
|
710
|
+
// tombstone keeps the packaged copy of a starter from coming back, and the
|
|
711
|
+
// rest (agent.json: name and face, persona, memory, its model and on/off)
|
|
712
|
+
// stays for its archived chat — until restoreAgentDefinition brings it back
|
|
713
|
+
// or purgeAgentDefinition.
|
|
714
|
+
// AGENT.md always moves aside; an AGENT.md over an existing archive is
|
|
715
|
+
// refused, since the move would overwrite it. Another selected entry
|
|
716
|
+
// moves too when its own archive is free, and otherwise stays in place
|
|
717
|
+
// under the tombstone.
|
|
718
|
+
const agentFile = join(dir, 'AGENT.md');
|
|
719
|
+
const agentArchive = archivedEntryPath(agentFile);
|
|
720
|
+
if (existsSync(agentFile) && existsSync(agentArchive)) {
|
|
721
|
+
throw new Error(`agent "${id}" cannot be deleted: ${agentArchive} already exists; move or remove it first`);
|
|
722
|
+
}
|
|
723
|
+
const entry = archivableEntry(dir, entryIn(dir));
|
|
668
724
|
mkdirSync(dir, { recursive: true });
|
|
669
|
-
if (existsSync(
|
|
725
|
+
if (existsSync(agentFile)) renameSync(agentFile, agentArchive);
|
|
726
|
+
if (entry !== agentFile && existsSync(entry) && !existsSync(archivedEntryPath(entry))) {
|
|
727
|
+
renameSync(entry, archivedEntryPath(entry));
|
|
728
|
+
}
|
|
670
729
|
writeFileAtomicSync(join(dir, AGENT_DELETED_MARKER), 'deleted\n');
|
|
671
730
|
clearAgentDefinitionCache(id);
|
|
672
731
|
// The Leads listing it drop it from their roster from their next turn.
|
|
@@ -683,11 +742,22 @@ function createAgentArchiveApi({
|
|
|
683
742
|
const dataDir = dataDirOf();
|
|
684
743
|
if (!isArchivedAgent(dataDir, id)) throw new Error(`agent "${id}" is not deleted`);
|
|
685
744
|
const dir = userDir(dataDir, id);
|
|
686
|
-
|
|
687
|
-
|
|
745
|
+
// AGENT.md.archived back to AGENT.md, and the selected entry's archive back
|
|
746
|
+
// to that entry. Nothing is overwritten: an archive whose file exists again
|
|
747
|
+
// stays as it is, and when every kept archive collides the restore is refused.
|
|
748
|
+
const agentFile = join(dir, 'AGENT.md');
|
|
749
|
+
const entry = archivableEntry(dir, entryIn(dir));
|
|
750
|
+
const moves = [{ from: join(dir, ARCHIVED_AGENT_FILE), to: agentFile }];
|
|
751
|
+
if (entry !== agentFile) moves.push({ from: archivedEntryPath(entry), to: entry });
|
|
752
|
+
const kept = moves.filter((move) => existsSync(move.from));
|
|
753
|
+
if (kept.length === 0 && !hasEntryFile(shippedDir(id))) {
|
|
688
754
|
throw new Error(`agent "${id}" was deleted before deleted agents were kept, so it cannot be restored`);
|
|
689
755
|
}
|
|
690
|
-
|
|
756
|
+
const free = kept.filter((move) => !existsSync(move.to));
|
|
757
|
+
if (kept.length > 0 && free.length === 0) {
|
|
758
|
+
throw new Error(`agent "${id}" cannot be restored: ${kept[0].to} already exists; move or remove it first`);
|
|
759
|
+
}
|
|
760
|
+
for (const move of free) renameSync(move.from, move.to);
|
|
691
761
|
rmSync(join(dir, AGENT_DELETED_MARKER), { force: true });
|
|
692
762
|
clearAgentDefinitionCache(id);
|
|
693
763
|
// Back in the roster of the Leads that list it, from their next turn.
|
|
@@ -795,13 +865,20 @@ export function createAgentEditorApi(deps) {
|
|
|
795
865
|
const dataDir = resolveDataDir(deps);
|
|
796
866
|
const definition = loadAgentDefinition(dataDir, id);
|
|
797
867
|
if (!definition) throw new Error(`agent "${agentId}" not found`);
|
|
868
|
+
const userOverride = hasUserAgentFile(dataDir, id);
|
|
798
869
|
return {
|
|
799
870
|
id,
|
|
800
871
|
name: definition.name,
|
|
801
872
|
description: definition.description,
|
|
802
873
|
body: definition.body,
|
|
803
874
|
custom: !isFixedAgent(id),
|
|
804
|
-
userOverride
|
|
875
|
+
userOverride,
|
|
876
|
+
// The user copy's entry file, editable in place; a shipped-only agent has
|
|
877
|
+
// none (an install-dir edit would be lost on update), nor has an entry
|
|
878
|
+
// that resolves outside its folder (the body is still returned).
|
|
879
|
+
...(userOverride && editablePathIn(userDir(dataDir, id), userEntryPath(dataDir, id))
|
|
880
|
+
? { path: userEntryPath(dataDir, id) }
|
|
881
|
+
: {}),
|
|
805
882
|
route: routeFor(getConfig(), id),
|
|
806
883
|
routeConfigured: routeConfigured(getConfig(), id),
|
|
807
884
|
kind: kindOf(definition),
|
|
@@ -77,13 +77,18 @@ export function createWorkflowPacksApi(deps) {
|
|
|
77
77
|
const dataDir = resolveDataDir(deps);
|
|
78
78
|
const pack = loadWorkflowPack(dataDir, id);
|
|
79
79
|
if (!pack || pack.id !== id) throw new Error(`workflow "${workflowId}" not found`);
|
|
80
|
+
// The user copy's file, so a part of a long body can be edited in place;
|
|
81
|
+
// a shipped-only pack has none (its install copy is replaced on update).
|
|
82
|
+
const file = join(dataDir, 'workflows', id, 'WORKFLOW.md');
|
|
83
|
+
const userOverride = existsSync(file);
|
|
80
84
|
return {
|
|
81
85
|
id: pack.id,
|
|
82
86
|
name: pack.name,
|
|
83
87
|
description: pack.description,
|
|
84
88
|
source: pack.source,
|
|
85
89
|
body: pack.body,
|
|
86
|
-
userOverride
|
|
90
|
+
userOverride,
|
|
91
|
+
...(userOverride ? { path: file } : {}),
|
|
87
92
|
};
|
|
88
93
|
}
|
|
89
94
|
|
|
@@ -426,7 +426,7 @@ export function createWorkspaceTransitions({
|
|
|
426
426
|
* refuses while a session or worker runs in an affected folder, or a
|
|
427
427
|
* background command does; `removing` refuses while any session still uses
|
|
428
428
|
* one. Then holds the folders — Mixdog checkouts only; an external checkout
|
|
429
|
-
*
|
|
429
|
+
* takes no lease from this profile — so new Mixdog work there waits until `run`
|
|
430
430
|
* settles. `actor`: the session acting on its own checkout, whose own turn
|
|
431
431
|
* and workers its caller already checked (assertLeadIdle).
|
|
432
432
|
*/
|
|
@@ -985,7 +985,15 @@ export function createWorkspaceTransitions({
|
|
|
985
985
|
* `ownTurn` (internal: the Lead's own tool call) tolerates the calling turn.
|
|
986
986
|
*/
|
|
987
987
|
async function requestWorktree(
|
|
988
|
-
{
|
|
988
|
+
{
|
|
989
|
+
name = null,
|
|
990
|
+
confirmLarge = false,
|
|
991
|
+
provision = false,
|
|
992
|
+
carryChanges,
|
|
993
|
+
prompt = '',
|
|
994
|
+
txId = '',
|
|
995
|
+
expectedRevision,
|
|
996
|
+
} = {},
|
|
989
997
|
{ ownTurn = false } = {}
|
|
990
998
|
) {
|
|
991
999
|
const nameHint = validName(name);
|