mixdog 1.2.6 → 1.3.1
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/simple-icons-CC0-1.0.txt +30 -0
- package/NOTICE.md +71 -9
- package/package.json +4 -1
- package/scripts/check-model-recommendations.mjs +89 -0
- package/scripts/gate-local.mjs +7 -0
- package/scripts/lib/isolated-test-env.mjs +16 -0
- package/src/agents/reviewer/AGENT.md +4 -2
- package/src/agents/reviewer/agent.json +1 -1
- package/src/defaults/shipped-history.json +10 -0
- package/src/defaults/skills/browser-use/SKILL.md +5 -0
- package/src/defaults/skills/memory-management/SKILL.md +4 -1
- package/src/defaults/skills/setup/references/actions.md +14 -4
- package/src/defaults/skills/setup-guide/SKILL.md +178 -0
- package/src/defaults/skills/setup-guide/model-recommendations.json +333 -0
- package/src/lib/rules-builder.cjs +55 -6
- package/src/output-styles/common.md +13 -8
- package/src/rules/agent/AGENT.md +4 -0
- package/src/rules/agent-mode/AGENT_MODE.md +24 -0
- package/src/rules/agent-mode/MIXY.md +28 -0
- package/src/rules/agent-mode/ROUTINE.md +6 -0
- package/src/rules/lead/LEAD.md +7 -3
- package/src/rules/shared/70-delivery.md +3 -0
- package/src/runtime/agent/orchestrator/agent-runtime/room-supervisor-completion.mjs +86 -0
- package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +77 -14
- package/src/runtime/agent/orchestrator/context/role-instructions.mjs +241 -14
- package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +4 -2
- package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +216 -0
- package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +5 -4
- package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +10 -1
- package/src/runtime/agent/orchestrator/mcp/client.mjs +115 -11
- package/src/runtime/agent/orchestrator/mcp/oauth-account.mjs +97 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-client.mjs +49 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-credentials.mjs +59 -9
- package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +32 -7
- package/src/runtime/agent/orchestrator/mcp/oauth-provider.mjs +45 -6
- package/src/runtime/agent/orchestrator/mcp/oauth.mjs +195 -6
- package/src/runtime/agent/orchestrator/mcp/server-connectors.mjs +17 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-catalog.mjs +7 -2
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-login.mjs +10 -3
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-tokens.mjs +4 -0
- package/src/runtime/agent/orchestrator/providers/auto-effort.mjs +7 -8
- package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/lib/tool-call-wire-ids.mjs +65 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-retry-profile.mjs +8 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +9 -2
- package/src/runtime/agent/orchestrator/providers/openai-error-contract.mjs +17 -12
- package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +10 -0
- package/src/runtime/agent/orchestrator/providers/openai-responses-input.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +14 -2
- package/src/runtime/agent/orchestrator/providers/retry-executor.mjs +17 -0
- package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +96 -1
- package/src/runtime/agent/orchestrator/runtime-core/context-status-shape.mjs +4 -0
- package/src/runtime/agent/orchestrator/runtime-core/extension-scopes.mjs +6 -4
- package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +14 -0
- package/src/runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs +1 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +12 -6
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +32 -5
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +21 -5
- package/src/runtime/agent/orchestrator/runtime-core/workflow/agents.mjs +40 -7
- package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +52 -20
- package/src/runtime/agent/orchestrator/runtime-core/workflow-ids.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/agent-home.d.mts +4 -0
- package/src/runtime/agent/orchestrator/session/agent-home.mjs +33 -0
- package/src/runtime/agent/orchestrator/session/agent-mode.mjs +62 -0
- package/src/runtime/agent/orchestrator/session/agent-room-supervisor.d.mts +31 -0
- package/src/runtime/agent/orchestrator/session/agent-room-supervisor.mjs +241 -0
- package/src/runtime/agent/orchestrator/session/agent-room.d.mts +166 -0
- package/src/runtime/agent/orchestrator/session/agent-room.mjs +650 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +7 -1
- package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +6 -2
- package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +192 -0
- package/src/runtime/agent/orchestrator/session/human-gate/held-message.mjs +95 -0
- package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +221 -0
- package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +195 -0
- package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +167 -0
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +9 -0
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +10 -0
- package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +108 -6
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +10 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +74 -15
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +9 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +5 -4
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +13 -3
- package/src/runtime/agent/orchestrator/session/loop/usage.mjs +49 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-commit.mjs +18 -8
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager/pending-lifecycle-epoch.mjs +0 -8
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +44 -16
- package/src/runtime/agent/orchestrator/session/manager/provider-replay.mjs +50 -0
- package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +22 -5
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +39 -4
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +33 -24
- package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +58 -8
- package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +16 -0
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +3 -2
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +7 -1
- package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/manager/workflow-switch-notice.mjs +58 -0
- package/src/runtime/agent/orchestrator/session/mixy-name.mjs +27 -0
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +32 -5
- package/src/runtime/agent/orchestrator/session/routine-result.d.mts +86 -0
- package/src/runtime/agent/orchestrator/session/routine-result.mjs +329 -0
- package/src/runtime/agent/orchestrator/session/store/sweep/sweep-row.mjs +8 -0
- package/src/runtime/agent/orchestrator/session/store-record-strict-scan.mjs +28 -9
- package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +16 -2
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +44 -12
- package/src/runtime/agent/orchestrator/session/store-summary-last-message.mjs +46 -0
- package/src/runtime/agent/orchestrator/session/store-summary-visibility.mjs +0 -4
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +58 -10
- package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +92 -15
- package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +12 -0
- package/src/runtime/agent/orchestrator/tools/builtin/native-search-client.mjs +113 -5
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +4 -0
- package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +25 -1
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promote-job.mjs +8 -0
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +6 -0
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +3 -0
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +75 -57
- package/src/runtime/attachments/store.mjs +6 -0
- package/src/runtime/browser-bridge/client.mjs +24 -0
- package/src/runtime/channels/lib/scheduler.mjs +40 -12
- package/src/runtime/channels/lib/schedules-db.mjs +71 -12
- package/src/runtime/channels/lib/webhook/session-dispatch.mjs +36 -16
- package/src/runtime/channels/lib/webhook.mjs +0 -1
- package/src/runtime/channels/lib/webhooks-db.mjs +100 -10
- package/src/runtime/channels/lib/worker-main.mjs +17 -20
- package/src/runtime/computer-bridge/client.mjs +28 -1
- package/src/runtime/connectors/gmail-app-password.mjs +690 -0
- package/src/runtime/connectors/google-mcp.mjs +446 -0
- package/src/runtime/effort-judge/judge-client.mjs +1 -1
- package/src/runtime/effort-judge/model-manifest.json +7 -7
- package/src/runtime/memory/lib/core-memory-access.mjs +69 -0
- package/src/runtime/memory/lib/core-memory-file.mjs +16 -8
- package/src/runtime/memory/lib/core-memory-index.mjs +15 -9
- package/src/runtime/memory/lib/core-memory-management.mjs +10 -4
- package/src/runtime/memory/lib/core-memory-store.mjs +53 -30
- package/src/runtime/memory/lib/core-memory-uniqueness.mjs +10 -6
- package/src/runtime/memory/lib/http-router/session-routes.mjs +3 -2
- package/src/runtime/memory/lib/memory-action-handlers/core-actions.mjs +44 -14
- package/src/runtime/memory/lib/memory-schema/core-and-meta.mjs +11 -0
- package/src/runtime/memory/lib/memory.mjs +7 -1
- package/src/runtime/memory/lib/query-core-recall.mjs +9 -1
- package/src/runtime/memory/lib/query-hybrid-search.mjs +2 -0
- package/src/runtime/memory/lib/tool-call-handler.mjs +2 -0
- package/src/runtime/memory/tool-defs.mjs +5 -0
- package/src/runtime/shared/agent-avatar-sprites.mjs +445 -0
- package/src/runtime/shared/agent-avatar.d.mts +64 -0
- package/src/runtime/shared/agent-avatar.mjs +320 -0
- package/src/runtime/shared/agent-kind.mjs +152 -0
- package/src/runtime/shared/agent-route-config.d.mts +4 -0
- package/src/runtime/shared/agent-route-config.mjs +30 -0
- package/src/runtime/shared/agent-store.mjs +132 -0
- package/src/runtime/shared/atomic-file.mjs +14 -1
- package/src/runtime/shared/bot-split-migration.mjs +457 -0
- package/src/runtime/shared/config-patch.mjs +20 -1
- package/src/runtime/shared/config.mjs +8 -1
- package/src/runtime/shared/connector-accounts.mjs +15 -0
- package/src/runtime/shared/connector-catalog.d.mts +51 -0
- package/src/runtime/shared/connector-catalog.mjs +228 -0
- package/src/runtime/shared/connector-triggers.d.mts +3 -0
- package/src/runtime/shared/connector-triggers.mjs +45 -0
- package/src/runtime/shared/err-text.mjs +3 -1
- package/src/runtime/shared/error-presentation.mjs +91 -6
- package/src/runtime/shared/google-oauth-client.mjs +24 -0
- package/src/runtime/shared/handoff-artifacts.d.mts +20 -0
- package/src/runtime/shared/handoff-artifacts.mjs +61 -0
- package/src/runtime/shared/held-request-host.d.mts +12 -0
- package/src/runtime/shared/held-request-host.mjs +71 -0
- package/src/runtime/shared/llm/provider-model-identities.mjs +6 -1
- package/src/runtime/shared/llm/usage-accounting.mjs +7 -1
- package/src/runtime/shared/model-route-fallback.mjs +79 -7
- package/src/runtime/shared/plain-preview.d.mts +1 -0
- package/src/runtime/shared/plain-preview.mjs +33 -0
- package/src/runtime/shared/routine-limits.d.mts +11 -0
- package/src/runtime/shared/routine-limits.mjs +21 -0
- package/src/runtime/shared/routine-trigger.d.mts +10 -0
- package/src/runtime/shared/routine-trigger.mjs +44 -0
- package/src/runtime/shared/schedule-model-ref.mjs +9 -10
- package/src/runtime/shared/sensitive-path-rules.d.mts +15 -0
- package/src/runtime/shared/sensitive-path-rules.mjs +256 -0
- package/src/runtime/shared/slow-span.mjs +4 -12
- package/src/runtime/shared/statusline/statusline-segments.mjs +13 -1
- package/src/runtime/shared/transcript-metadata.mjs +10 -0
- package/src/runtime/shared/turn-changes.mjs +0 -8
- package/src/runtime/shared/usage-readout-totals.mjs +58 -0
- package/src/runtime/shared/user-data-guard.mjs +119 -38
- package/src/session-runtime/agent-home-delivery.mjs +826 -0
- package/src/session-runtime/agent-profile-changes.mjs +21 -0
- package/src/session-runtime/agent-room-delivery.mjs +133 -0
- package/src/session-runtime/agent-task-sessions.mjs +460 -0
- package/src/session-runtime/boot/apis.mjs +63 -18
- package/src/session-runtime/boot/facade.mjs +5 -8
- package/src/session-runtime/boot/infrastructure.mjs +28 -17
- package/src/session-runtime/boot/providers.mjs +14 -4
- package/src/session-runtime/boot/tools.mjs +3 -0
- package/src/session-runtime/channel-config-api.mjs +53 -5
- package/src/session-runtime/config-lifecycle.mjs +43 -2
- package/src/session-runtime/cwd-plugins/core-memory-context.mjs +36 -20
- package/src/session-runtime/cwd-plugins/plugin-status.mjs +6 -1
- package/src/session-runtime/handoff-attachments.mjs +593 -0
- package/src/session-runtime/held-requests.mjs +228 -0
- package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +121 -0
- package/src/session-runtime/internal-tool-executor/feature-tools.mjs +5 -1
- package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +31 -2
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +56 -3
- package/src/session-runtime/internal-tool-executor.mjs +2 -0
- package/src/session-runtime/lifecycle/session-catalog.mjs +9 -0
- package/src/session-runtime/lifecycle/session-switch/resume.mjs +4 -0
- package/src/session-runtime/lifecycle/shared.mjs +6 -0
- package/src/session-runtime/mcp-glue/server-catalog.mjs +8 -0
- package/src/session-runtime/mcp-glue/server-input.mjs +3 -0
- package/src/session-runtime/mcp-oauth-login.mjs +112 -15
- package/src/session-runtime/model-route/route-persist.mjs +2 -2
- package/src/session-runtime/model-route/route-tuning.mjs +7 -1
- package/src/session-runtime/model-route/set-route.mjs +35 -4
- package/src/session-runtime/native-web-search.mjs +2 -2
- package/src/session-runtime/new-session-config.mjs +6 -1
- package/src/session-runtime/owned-automation-run.mjs +229 -0
- package/src/session-runtime/resource-api.mjs +5 -0
- package/src/session-runtime/resource-mcp-api.mjs +107 -13
- package/src/session-runtime/resource-skills-api.mjs +12 -0
- package/src/session-runtime/room-member-gate.mjs +48 -0
- package/src/session-runtime/route-resolution.mjs +5 -11
- package/src/session-runtime/routine-home-turn.mjs +156 -0
- package/src/session-runtime/routine-owner.mjs +48 -0
- package/src/session-runtime/schedule-session-run.mjs +30 -64
- package/src/session-runtime/services/agent-tool/helpers.mjs +30 -1
- package/src/session-runtime/services/agent-tool/send-flow.mjs +11 -3
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-plan.mjs +19 -4
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +2 -0
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +4 -1
- package/src/session-runtime/services/agent-tool/spawn-preset.mjs +5 -8
- package/src/session-runtime/services/agent-tool/tool-def.mjs +7 -0
- package/src/session-runtime/services/channel-admin.mjs +93 -24
- package/src/session-runtime/services/memory-access.mjs +37 -0
- package/src/session-runtime/services/provider-admin.mjs +74 -39
- package/src/session-runtime/services/schedule-access.mjs +91 -0
- package/src/session-runtime/session-create/session-options.mjs +34 -7
- package/src/session-runtime/session-hooks.mjs +16 -8
- package/src/session-runtime/setup-tool/executor.mjs +79 -6
- package/src/session-runtime/setup-tool/extended-actions.mjs +90 -20
- package/src/session-runtime/setup-tool/settings-contract.mjs +14 -6
- package/src/session-runtime/setup-tool/tool-defs.mjs +8 -0
- package/src/session-runtime/skills-api.mjs +16 -1
- package/src/session-runtime/tool-policy-refresh.mjs +249 -29
- package/src/session-runtime/turn/session-ops.mjs +8 -1
- package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +10 -2
- package/src/session-runtime/turn/turn-run/turn-prepare.mjs +14 -0
- package/src/session-runtime/webhook-session-run.mjs +49 -67
- package/src/session-runtime/workflow-agents-api/agent-connectors.mjs +239 -0
- package/src/session-runtime/workflow-agents-api/agent-editor.mjs +650 -25
- package/src/session-runtime/workflow-agents-api/agent-route.mjs +67 -7
- package/src/session-runtime/workflow-agents-api/onboarding.mjs +11 -4
- package/src/session-runtime/workflow-agents-api/workflow-packs.mjs +11 -1
- package/src/session-runtime/workflow-agents-api.mjs +4 -1
- package/src/standalone/daemon-agent-control.mjs +1 -8
- package/src/standalone/daemon-session-control.mjs +21 -4
- package/src/standalone/daemon.mjs +31 -5
- package/src/standalone/session-protocol.mjs +27 -0
- package/src/standalone/session-service/agent-tree/agent-turns.mjs +46 -3
- package/src/standalone/session-service/agent-tree.mjs +4 -3
- package/src/standalone/session-service/session-calls/session-turns.mjs +28 -0
- package/src/standalone/session-service.mjs +19 -0
- package/src/tui/app/slash-commands.mjs +20 -0
- package/src/tui/dist/index.mjs +234 -95
- package/src/tui/session/context-state.mjs +6 -1
- package/src/tui/session/notice-surface.mjs +34 -4
- package/src/tui/session/queue-helpers.mjs +2 -1
- package/src/tui/session/session-api/extensions.mjs +4 -0
- package/src/tui/session/session-api/intake/submission.mjs +5 -0
- package/src/tui/session/session-api/integrations.mjs +8 -0
- package/src/tui/session/session-api/lifecycle/session-reset.mjs +13 -3
- package/src/tui/session/session-api/lifecycle.mjs +17 -1
- package/src/tui/session/session-api/routes.mjs +61 -5
- package/src/tui/session/session-flow/drain.mjs +12 -3
- package/src/tui/session/session-flow/queue/entry-shape.mjs +2 -1
- package/src/tui/session/session-flow/queue/take-entries.mjs +3 -2
- package/src/tui/session/session-flow/steering.mjs +58 -24
- package/src/tui/session/tool-approval.mjs +26 -5
- package/src/tui/session/transcript-intake.mjs +8 -2
- package/src/tui/session/transcript-spill.mjs +5 -1
- package/src/tui/session/tui-steering-persist.mjs +65 -42
- package/src/tui/session/turn-live-call.mjs +64 -0
- package/src/tui/session/turn.mjs +34 -8
- package/src/session-runtime/automation-agents.mjs +0 -94
- package/src/session-runtime/automation-workflow.mjs +0 -59
|
@@ -11,6 +11,9 @@ import { resolveMcpOAuthUrl } from '../runtime/agent/orchestrator/mcp/client-con
|
|
|
11
11
|
import { withAdoptedCopies, withPinnedCollisionNames, withPluginServerIntent } from './mcp-glue/plugin-servers.mjs';
|
|
12
12
|
import { findRegisteredPlugin } from './resource-plugins-api.mjs';
|
|
13
13
|
import { createMcpOAuthLogins } from './mcp-oauth-login.mjs';
|
|
14
|
+
import { connectorById, gmailAppPasswordAccount, gmailAppPasswordValue } from '../runtime/shared/connector-catalog.mjs';
|
|
15
|
+
import { saveMcpOAuthClientSecret } from '../runtime/agent/orchestrator/mcp/oauth-client.mjs';
|
|
16
|
+
import { forgetGmailAppPassword, saveGmailAppPassword } from '../runtime/connectors/gmail-app-password.mjs';
|
|
14
17
|
|
|
15
18
|
/** The URL a server's OAuth credentials are stored under, or null. */
|
|
16
19
|
function oauthUrlOf(cfg) {
|
|
@@ -83,8 +86,17 @@ export function createMcpResourceApi({ deps, sync, decorate }) {
|
|
|
83
86
|
completeMcpLogin,
|
|
84
87
|
getMcpServerOAuthState = () => null,
|
|
85
88
|
probeMcpServerAuth,
|
|
89
|
+
startMcpServerOAuthSwitch,
|
|
90
|
+
commitMcpServerOAuthSwitch,
|
|
91
|
+
gmailAppPassword = { save: saveGmailAppPassword, forget: forgetGmailAppPassword },
|
|
92
|
+
saveOAuthClientSecret = saveMcpOAuthClientSecret,
|
|
86
93
|
} = deps;
|
|
87
94
|
|
|
95
|
+
const reconnectOne = async (name) => {
|
|
96
|
+
const status = await connectConfiguredMcp({ only: name, enabled: true });
|
|
97
|
+
const row = status?.servers?.find((server) => server.name === name);
|
|
98
|
+
return { connected: row?.connected === true, error: row?.error || null };
|
|
99
|
+
};
|
|
88
100
|
// Explicit OAuth sign-in: the one interactive MCP connect. On success the
|
|
89
101
|
// other runtimes reconnect the server non-interactively with the new tokens.
|
|
90
102
|
const oauthLogins = createMcpOAuthLogins({
|
|
@@ -95,6 +107,22 @@ export function createMcpResourceApi({ deps, sync, decorate }) {
|
|
|
95
107
|
},
|
|
96
108
|
cancel: (name) => cancelMcpLogin(name),
|
|
97
109
|
completeCallback: completeMcpLogin,
|
|
110
|
+
// 계정 바꾸기, transactionally: the working connection stays until the new
|
|
111
|
+
// sign-in succeeded and connects; a cancel or failure changes nothing, and
|
|
112
|
+
// a new sign-in that cannot connect is rolled back.
|
|
113
|
+
async switchAccount(name, launch, attempt) {
|
|
114
|
+
const cfg = resolveMcpCatalog().servers[name];
|
|
115
|
+
const switching = startMcpServerOAuthSwitch(cfg, launch, name);
|
|
116
|
+
attempt.abort = () => switching.cancel();
|
|
117
|
+
attempt.answer = (callbackUrl) => switching.answer(callbackUrl);
|
|
118
|
+
const signIn = await switching.done;
|
|
119
|
+
const rollback = await commitMcpServerOAuthSwitch(cfg, signIn);
|
|
120
|
+
const outcome = await reconnectOne(name);
|
|
121
|
+
if (outcome.connected) return outcome;
|
|
122
|
+
await rollback();
|
|
123
|
+
await reconnectOne(name);
|
|
124
|
+
return { connected: false, error: outcome.error || 'The new sign-in could not connect' };
|
|
125
|
+
},
|
|
98
126
|
onComplete: (name) => sync.announce('mcp', { detail: { mcpChanges: [{ name, enabled: true }] } }),
|
|
99
127
|
});
|
|
100
128
|
/** The effective config of a server that signs in with MCP OAuth (HTTP/SSE
|
|
@@ -130,6 +158,49 @@ export function createMcpResourceApi({ deps, sync, decorate }) {
|
|
|
130
158
|
const owned = name ? pluginMcpServer(name) : null;
|
|
131
159
|
if (owned) throw pluginOwnedError(name, owned.owner);
|
|
132
160
|
};
|
|
161
|
+
// The user's OAuth client secret (custom connector → 고급 설정 → 비밀값)
|
|
162
|
+
// goes to the keychain only; clearing the client id forgets it.
|
|
163
|
+
const storeOAuthClientSecret = async (input, cfg, previous = {}) => {
|
|
164
|
+
if (!cfg?.url) return;
|
|
165
|
+
const secret = typeof input.oauthClientSecret === 'string' ? input.oauthClientSecret.trim() : '';
|
|
166
|
+
if (cfg.oauthClientId && secret) await saveOAuthClientSecret(cfg.url, secret);
|
|
167
|
+
else if (!cfg.oauthClientId && previous.oauthClientId) await saveOAuthClientSecret(previous.url || cfg.url, '');
|
|
168
|
+
};
|
|
169
|
+
// Gmail with an app password (connector-catalog.mjs GMAIL_APP_PASSWORD): the
|
|
170
|
+
// password is checked with Gmail before anything is saved and goes to the
|
|
171
|
+
// keychain only. A new connection needs one; an edit without one keeps it.
|
|
172
|
+
const storeGmailAppPassword = async (input, cfg, required) => {
|
|
173
|
+
const account = gmailAppPasswordAccount(cfg);
|
|
174
|
+
if (!account) return;
|
|
175
|
+
const password = gmailAppPasswordValue(input.appPassword);
|
|
176
|
+
if (password) await gmailAppPassword.save(account, password);
|
|
177
|
+
else if (required || input.appPassword !== undefined) {
|
|
178
|
+
throw new Error('GMAIL_APP_PASSWORD_FORMAT: An app password has 16 characters.');
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
// A server's OAuth tokens and client registration go with it, unless another
|
|
182
|
+
// configured server (plugin or disabled ones included, even with MCP
|
|
183
|
+
// disabled for this runtime) still uses them through the same exact URL.
|
|
184
|
+
// `signIn` tells the caller what happened to the sign-in: `removed` (local
|
|
185
|
+
// secrets deleted, `revoked` when the provider confirmed the revocation
|
|
186
|
+
// too), `shared` (another server still uses it), or none (null).
|
|
187
|
+
const releaseOAuth = async (cfg) => {
|
|
188
|
+
const url = oauthUrlOf(cfg);
|
|
189
|
+
if (!url) return null;
|
|
190
|
+
if (Object.values(resolveConfiguredMcpCatalog().servers).some((other) => oauthUrlOf(other) === url)) {
|
|
191
|
+
return { signIn: 'shared', revoked: false };
|
|
192
|
+
}
|
|
193
|
+
const { revoked = false } = (await clearMcpServerOAuth(cfg, { revoke: true })) || {};
|
|
194
|
+
return { signIn: 'removed', revoked };
|
|
195
|
+
};
|
|
196
|
+
// An app password is deleted once no configured server uses its account.
|
|
197
|
+
const forgetUnusedGmailAppPassword = async (account) => {
|
|
198
|
+
if (!account) return false;
|
|
199
|
+
const servers = Object.values(resolveConfiguredMcpCatalog().servers);
|
|
200
|
+
if (servers.some((cfg) => gmailAppPasswordAccount(cfg) === account)) return false;
|
|
201
|
+
await gmailAppPassword.forget(account);
|
|
202
|
+
return true;
|
|
203
|
+
};
|
|
133
204
|
// Peers reload config from disk on receipt: land the write before publishing.
|
|
134
205
|
const persisted = async () => {
|
|
135
206
|
await flushSkillsSave?.();
|
|
@@ -164,11 +235,18 @@ export function createMcpResourceApi({ deps, sync, decorate }) {
|
|
|
164
235
|
async beginMcpOAuthLogin(name, options = {}) {
|
|
165
236
|
const { serverName, cfg } = await oauthServerConfig(name);
|
|
166
237
|
if (cfg.enabled === false) throw new Error(`MCP server is disabled: ${serverName}`);
|
|
238
|
+
// `mode`: `connect` (a normal authorization), `reauth` (repair the same
|
|
239
|
+
// connection; a refresh is tried first) or `switch` (계정 바꾸기).
|
|
240
|
+
if (options?.mode === 'switch' && !getMcpServerOAuthState(cfg)?.signedIn) {
|
|
241
|
+
throw new Error(`MCP server "${serverName}" is not signed in`);
|
|
242
|
+
}
|
|
167
243
|
return oauthLogins.begin(serverName, options);
|
|
168
244
|
},
|
|
169
|
-
/** `{ flowId, state: 'pending' | 'complete' | 'failed' | 'cancelled',
|
|
170
|
-
|
|
171
|
-
|
|
245
|
+
/** `{ flowId, state: 'pending' | 'complete' | 'failed' | 'cancelled',
|
|
246
|
+
* step?, error?, reason? }`; with `{ since, waitMs }` it resolves on the
|
|
247
|
+
* flow's next change (mcp-oauth-login.mjs). */
|
|
248
|
+
getMcpOAuthLoginStatus(flowId, options) {
|
|
249
|
+
return oauthLogins.status(flowId, options);
|
|
172
250
|
},
|
|
173
251
|
cancelMcpOAuthLogin(flowId) {
|
|
174
252
|
return oauthLogins.cancel(flowId);
|
|
@@ -186,8 +264,9 @@ export function createMcpResourceApi({ deps, sync, decorate }) {
|
|
|
186
264
|
const { serverName, cfg } = await oauthServerConfig(name);
|
|
187
265
|
// A begin issued meanwhile waits until the credentials are gone.
|
|
188
266
|
return oauthLogins.cancelServer(serverName, async () => {
|
|
189
|
-
await clearMcpServerOAuth(cfg);
|
|
190
|
-
|
|
267
|
+
const { revoked = false } = (await clearMcpServerOAuth(cfg, { revoke: true })) || {};
|
|
268
|
+
const status = await sync.reconnectMcpAndAnnounce({ only: serverName, enabled: cfg.enabled !== false });
|
|
269
|
+
return { ...status, revoked };
|
|
191
270
|
});
|
|
192
271
|
},
|
|
193
272
|
/** `{ auth: 'oauth' | 'none' | 'configured' | 'unreachable', error? }` for an
|
|
@@ -203,10 +282,18 @@ export function createMcpResourceApi({ deps, sync, decorate }) {
|
|
|
203
282
|
},
|
|
204
283
|
async addMcpServer(input = {}) {
|
|
205
284
|
const { name, config: serverConfig } = normalizeMcpServerInput(input);
|
|
285
|
+
// A catalog connector's server keeps its catalog id (connector-catalog.mjs).
|
|
286
|
+
const connector = clean(input.connector);
|
|
287
|
+
if (connector) {
|
|
288
|
+
if (!connectorById(connector)) throw new Error(`Unknown connector: ${connector}`);
|
|
289
|
+
serverConfig.connector = connector;
|
|
290
|
+
}
|
|
206
291
|
const servers = userServers();
|
|
207
292
|
if (Object.hasOwn(servers, name)) throw new Error(`MCP server already exists: ${name}`);
|
|
208
293
|
const owned = pluginMcpServer(name);
|
|
209
294
|
if (owned) throw new Error(`MCP server already exists: ${name} (provided by plugin "${owned.owner.id}")`);
|
|
295
|
+
await storeOAuthClientSecret(input, serverConfig);
|
|
296
|
+
await storeGmailAppPassword(input, serverConfig, true);
|
|
210
297
|
saveServers({ ...servers, [name]: serverConfig });
|
|
211
298
|
const status = await sync.reconnectMcpAndAnnounce({ only: name, enabled: serverConfig.enabled !== false });
|
|
212
299
|
return { name, status };
|
|
@@ -232,7 +319,14 @@ export function createMcpResourceApi({ deps, sync, decorate }) {
|
|
|
232
319
|
}
|
|
233
320
|
if (originalName && originalName !== name) delete servers[originalName];
|
|
234
321
|
servers[name] = mergeMcpServerConfig(existing, normalizedInput.config);
|
|
322
|
+
await storeOAuthClientSecret(input, servers[name], existing);
|
|
323
|
+
await storeGmailAppPassword(input, servers[name], false);
|
|
235
324
|
saveServers(servers);
|
|
325
|
+
const previousAccount = gmailAppPasswordAccount(existing);
|
|
326
|
+
const account = gmailAppPasswordAccount(servers[name]);
|
|
327
|
+
if (previousAccount !== account) await forgetUnusedGmailAppPassword(previousAccount);
|
|
328
|
+
// Gmail switched from the Google sign-in to an app password keeps no sign-in.
|
|
329
|
+
if (account && !previousAccount) await releaseOAuth(existing);
|
|
236
330
|
const status = await sync.reconnectMcpAndAnnounce({
|
|
237
331
|
only: name,
|
|
238
332
|
enabled: servers[name].enabled !== false,
|
|
@@ -252,15 +346,15 @@ export function createMcpResourceApi({ deps, sync, decorate }) {
|
|
|
252
346
|
delete servers[serverName];
|
|
253
347
|
saveServers(servers);
|
|
254
348
|
const status = await sync.reconnectMcpAndAnnounce({ drop: [serverName] });
|
|
255
|
-
// A removed server keeps no
|
|
256
|
-
//
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
await clearMcpServerOAuth(removed);
|
|
349
|
+
// A removed server keeps no sign-in another server does not share: an
|
|
350
|
+
// app password leaves the keychain (it is revoked at Google only).
|
|
351
|
+
const account = gmailAppPasswordAccount(removed);
|
|
352
|
+
if (account) {
|
|
353
|
+
const forgotten = await forgetUnusedGmailAppPassword(account);
|
|
354
|
+
return { ...status, signIn: forgotten ? 'removed' : 'shared', revoked: false };
|
|
262
355
|
}
|
|
263
|
-
|
|
356
|
+
const released = await releaseOAuth(removed);
|
|
357
|
+
return released ? { ...status, ...released } : status;
|
|
264
358
|
},
|
|
265
359
|
async setMcpServerEnabled(name, enabled) {
|
|
266
360
|
const serverName = clean(name);
|
|
@@ -24,6 +24,7 @@ export function createSkillsResourceApi({ deps, sync, decorate }) {
|
|
|
24
24
|
skillContent,
|
|
25
25
|
addGlobalSkill,
|
|
26
26
|
saveSkillDocument,
|
|
27
|
+
removeGlobalSkill,
|
|
27
28
|
invalidateSkills,
|
|
28
29
|
getDisabledSkills,
|
|
29
30
|
setDisabledSkills,
|
|
@@ -56,6 +57,17 @@ export function createSkillsResourceApi({ deps, sync, decorate }) {
|
|
|
56
57
|
await sync.announce('skills', { invalidate: false });
|
|
57
58
|
return { skill, status: skillsStatus() };
|
|
58
59
|
},
|
|
60
|
+
async removeSkill(input = {}) {
|
|
61
|
+
const removed = removeGlobalSkill(input);
|
|
62
|
+
// A removed skill leaves no stale disabled entry behind.
|
|
63
|
+
const disabled = getDisabledSkills?.().disabled;
|
|
64
|
+
if (Array.isArray(disabled) && disabled.includes(removed.name)) {
|
|
65
|
+
setDisabledSkills?.(disabled.filter((name) => name !== removed.name));
|
|
66
|
+
await flushSkillsSave?.();
|
|
67
|
+
}
|
|
68
|
+
await sync.announce('skills');
|
|
69
|
+
return { skill: removed, status: skillsStatus() };
|
|
70
|
+
},
|
|
59
71
|
async setDisabledSkills(names = []) {
|
|
60
72
|
const result = setDisabledSkills?.(names);
|
|
61
73
|
await flushSkillsSave?.();
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// A group-room member's tool calls (session/agent-room.mjs). Reads run freely;
|
|
2
|
+
// anything that can change files or state asks the user through the ordinary
|
|
3
|
+
// approval flow, whatever the agent's own permission or hook rules say — even
|
|
4
|
+
// an agent that normally runs auto-approved. A hook's deny still wins.
|
|
5
|
+
import { analyzeShellCommandEffects } from '../runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs';
|
|
6
|
+
|
|
7
|
+
const READ_ONLY_TOOLS = new Set([
|
|
8
|
+
'read',
|
|
9
|
+
'grep',
|
|
10
|
+
'glob',
|
|
11
|
+
'find',
|
|
12
|
+
'list',
|
|
13
|
+
'code_graph',
|
|
14
|
+
'load_tool',
|
|
15
|
+
'skill',
|
|
16
|
+
'Skill',
|
|
17
|
+
'recall',
|
|
18
|
+
'search_memories',
|
|
19
|
+
'web_search',
|
|
20
|
+
'web_fetch',
|
|
21
|
+
'local_fetch',
|
|
22
|
+
'image_fetch',
|
|
23
|
+
'ask_user',
|
|
24
|
+
]);
|
|
25
|
+
|
|
26
|
+
export const ROOM_APPROVAL_REASON = 'Group-room members need your approval before changing anything.';
|
|
27
|
+
|
|
28
|
+
/** Whether `name(args)` only reads: a read-only tool, or a shell command whose
|
|
29
|
+
* every stage the shell analysis classifies as non-mutating. */
|
|
30
|
+
export async function isReadOnlyRoomCall({ name, args, cwd }, analyze = analyzeShellCommandEffects) {
|
|
31
|
+
if (READ_ONLY_TOOLS.has(name)) return true;
|
|
32
|
+
if (name !== 'shell' || typeof args?.command !== 'string') return false;
|
|
33
|
+
try {
|
|
34
|
+
return (await analyze(args.command, cwd))?.mutationMode === 'none';
|
|
35
|
+
} catch {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** The PreToolUse decision for a room member's call: the hook's own `decision`
|
|
41
|
+
* when it denies or the call only reads; otherwise an approval ask that keeps
|
|
42
|
+
* any rewrite the hook made. */
|
|
43
|
+
export async function roomMemberToolDecision(call, decision, analyze) {
|
|
44
|
+
const action = String(decision?.action || decision?.decision || '').toLowerCase();
|
|
45
|
+
if (action === 'deny' || action === 'block') return decision;
|
|
46
|
+
if (await isReadOnlyRoomCall(call, analyze)) return decision;
|
|
47
|
+
return { ...(decision && typeof decision === 'object' ? decision : {}), action: 'ask', reason: ROOM_APPROVAL_REASON };
|
|
48
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
// Route resolution against the live runtime record:
|
|
2
|
-
//
|
|
1
|
+
// Route resolution against the live runtime record: refuse a model-less first
|
|
2
|
+
// turn (no auto-pick) and refresh the route's effort / Fast / context
|
|
3
3
|
// fields from provider metadata.
|
|
4
4
|
import { ensureProviderEnabled } from '../runtime/agent/orchestrator/runtime-core/config-helpers.mjs';
|
|
5
5
|
import { clean } from '../runtime/agent/orchestrator/runtime-core/session-text.mjs';
|
|
@@ -22,17 +22,11 @@ export function createRouteResolution({ rt, collectProviderModels, ensureProvide
|
|
|
22
22
|
if (routeHasModel()) return rt.route;
|
|
23
23
|
const models = await runAbortable(signal, () => collectProviderModels());
|
|
24
24
|
throwIfAborted(signal);
|
|
25
|
-
|
|
26
|
-
if (!picked) {
|
|
25
|
+
if (!models?.length) {
|
|
27
26
|
throw new Error('No provider models available. Open /providers to sign in, then /model to choose a model.');
|
|
28
27
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
provider: picked.provider,
|
|
32
|
-
model: picked.id,
|
|
33
|
-
preset: null,
|
|
34
|
-
};
|
|
35
|
-
return rt.route;
|
|
28
|
+
// Never auto-pick: the user chooses the model.
|
|
29
|
+
throw new Error('No model selected. Choose a model with /model first.');
|
|
36
30
|
}
|
|
37
31
|
|
|
38
32
|
async function refreshRouteEffort(modelMetaOverride = null, expectedRoute = null, signal = null, lease = null) {
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A routine fire (schedule or webhook) is a TURN in its owner bot's home
|
|
3
|
+
* thread (`sess_home_<agentId>`): the routine's instructions are delivered
|
|
4
|
+
* there as a queued turn the bot handles itself — doing it or delegating as
|
|
5
|
+
* in any turn — and its reply in the chat is the result. Nothing else is
|
|
6
|
+
* created: no run session, no result card.
|
|
7
|
+
*
|
|
8
|
+
* The turn waits behind the thread's own work (a user turn, another routine):
|
|
9
|
+
* it rides the thread's queue as its own `routine` entry (one per turn, never
|
|
10
|
+
* merged or steered into another), shows as a routine marker row, and selects
|
|
11
|
+
* skills only from the routine's instructions (`skillSelection`), never from a
|
|
12
|
+
* webhook payload. While it runs the thread counts as unattended, so its
|
|
13
|
+
* approvals are held for the owner (held-requests.mjs). Resolves with the
|
|
14
|
+
* reply once the turn ends; rejects when the turn fails, is cancelled, or the
|
|
15
|
+
* caller's `signal` aborts (which also aborts the turn once it runs).
|
|
16
|
+
*/
|
|
17
|
+
import { randomUUID } from 'node:crypto';
|
|
18
|
+
import { agentHomeSessionId } from '../runtime/agent/orchestrator/session/agent-home.mjs';
|
|
19
|
+
import { routineHumanNeededText } from '../runtime/agent/orchestrator/session/routine-result.mjs';
|
|
20
|
+
import { automationPromptContent } from '../runtime/shared/automation-attachments.mjs';
|
|
21
|
+
import { ROUTINE_LIMITS } from '../runtime/shared/routine-limits.mjs';
|
|
22
|
+
import { normalizeRoutineTrigger, routineTriggerHeader } from '../runtime/shared/routine-trigger.mjs';
|
|
23
|
+
import { agentHomeLiveSurface, scheduleOwnerAgentId } from './agent-home-delivery.mjs';
|
|
24
|
+
import { markUnattendedRun, unattendedRunOf } from './held-requests.mjs';
|
|
25
|
+
|
|
26
|
+
// Routine turns waiting or running per home thread.
|
|
27
|
+
const waiting = new Map();
|
|
28
|
+
|
|
29
|
+
const abortError = (signal) =>
|
|
30
|
+
signal?.reason instanceof Error ? signal.reason : new Error(String(signal?.reason || 'routine run aborted'));
|
|
31
|
+
|
|
32
|
+
async function defaultLoadSession(sessionId) {
|
|
33
|
+
const { loadSession } = await import('../runtime/agent/orchestrator/session/store.mjs');
|
|
34
|
+
return loadSession(sessionId);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function settledError(detail) {
|
|
38
|
+
if (detail?.status === 'cancelled') return new Error('the routine turn was cancelled');
|
|
39
|
+
if (detail?.status === 'rejected') return new Error("the bot's chat closed before the routine ran");
|
|
40
|
+
const error = detail?.error;
|
|
41
|
+
return new Error(String(error?.message || error || 'the routine turn failed'));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Deliver one fire of routine `name` (`kind` schedule | webhook) to its
|
|
46
|
+
* owner's home thread. `prompt` is the model-visible body (instructions, plus
|
|
47
|
+
* a webhook's fenced payload); `skillSelection` its trusted part (default:
|
|
48
|
+
* the whole body); `event` a short label for the marker (webhook event).
|
|
49
|
+
*/
|
|
50
|
+
export async function runRoutineTurn(
|
|
51
|
+
{
|
|
52
|
+
kind,
|
|
53
|
+
name,
|
|
54
|
+
ownerAgentId = null,
|
|
55
|
+
prompt,
|
|
56
|
+
skillSelection = null,
|
|
57
|
+
attachments = null,
|
|
58
|
+
cwd = null,
|
|
59
|
+
event = '',
|
|
60
|
+
signal = null,
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
surface = agentHomeLiveSurface(),
|
|
64
|
+
loadSession = defaultLoadSession,
|
|
65
|
+
limit = ROUTINE_LIMITS.homeQueueLimit,
|
|
66
|
+
log = (line) => process.stderr.write(`mixdog routines: ${line}\n`),
|
|
67
|
+
} = {}
|
|
68
|
+
) {
|
|
69
|
+
const body = String(prompt || '').trim();
|
|
70
|
+
if (!body) throw new Error(`${kind} "${name}" has no instructions`);
|
|
71
|
+
if (signal?.aborted) throw abortError(signal);
|
|
72
|
+
if (typeof surface?.submit !== 'function') throw new Error('routine run: the session service is not running');
|
|
73
|
+
const agentId = scheduleOwnerAgentId({ ownerAgentId });
|
|
74
|
+
const homeId = agentHomeSessionId(agentId);
|
|
75
|
+
const home = await loadSession(homeId);
|
|
76
|
+
if (!home || home.closed === true) {
|
|
77
|
+
throw new Error(`the bot "${agentId}" has no chat yet: open it once in the app`);
|
|
78
|
+
}
|
|
79
|
+
const queued = waiting.get(homeId) || 0;
|
|
80
|
+
if (queued >= limit) throw new Error(`${limit} routine runs already wait in this bot's chat`);
|
|
81
|
+
waiting.set(homeId, queued + 1);
|
|
82
|
+
const trigger = normalizeRoutineTrigger({ kind, name, event });
|
|
83
|
+
const header = routineTriggerHeader({ kind, name, project: cwd ? String(cwd) : '' });
|
|
84
|
+
const content = automationPromptContent(`${header}\n\n${body}`, attachments);
|
|
85
|
+
return new Promise((resolve, reject) => {
|
|
86
|
+
let started = false;
|
|
87
|
+
let aborted = false;
|
|
88
|
+
let done = false;
|
|
89
|
+
let unmark = () => {};
|
|
90
|
+
let released = false;
|
|
91
|
+
const release = () => {
|
|
92
|
+
if (released) return;
|
|
93
|
+
released = true;
|
|
94
|
+
const left = (waiting.get(homeId) || 1) - 1;
|
|
95
|
+
if (left > 0) waiting.set(homeId, left);
|
|
96
|
+
else waiting.delete(homeId);
|
|
97
|
+
signal?.removeEventListener?.('abort', onAbort);
|
|
98
|
+
};
|
|
99
|
+
const finish = (fn, value) => {
|
|
100
|
+
if (done) return;
|
|
101
|
+
done = true;
|
|
102
|
+
fn(value);
|
|
103
|
+
};
|
|
104
|
+
const abortTurn = () =>
|
|
105
|
+
Promise.resolve()
|
|
106
|
+
.then(() => surface.abort?.(homeId))
|
|
107
|
+
.catch((error) => log(`aborting ${kind} "${name}" in ${homeId} failed: ${error?.message || error}`));
|
|
108
|
+
function onAbort() {
|
|
109
|
+
aborted = true;
|
|
110
|
+
if (started) void abortTurn();
|
|
111
|
+
finish(reject, abortError(signal));
|
|
112
|
+
}
|
|
113
|
+
signal?.addEventListener?.('abort', onAbort, { once: true });
|
|
114
|
+
const options = {
|
|
115
|
+
id: `routine_${randomUUID()}`,
|
|
116
|
+
mode: 'routine',
|
|
117
|
+
// Runs after the thread's current turn, never steered into it.
|
|
118
|
+
priority: 'later',
|
|
119
|
+
isMeta: true,
|
|
120
|
+
skipSlashCommands: true,
|
|
121
|
+
restorable: false,
|
|
122
|
+
// Esc ends this fire; it is never put back in the queue.
|
|
123
|
+
abortDiscardOnAbort: true,
|
|
124
|
+
skillSelection: String(skillSelection ?? body),
|
|
125
|
+
transcriptMeta: { routineTrigger: trigger },
|
|
126
|
+
onCommitted: () => {
|
|
127
|
+
if (started) return;
|
|
128
|
+
started = true;
|
|
129
|
+
// Nobody watches this turn: its approvals are held for the owner.
|
|
130
|
+
unmark = markUnattendedRun(homeId, { kind, agentId, name });
|
|
131
|
+
if (aborted) void abortTurn();
|
|
132
|
+
},
|
|
133
|
+
onSettled: (detail) => {
|
|
134
|
+
// Read before unmarking: a step only a person can do ended the turn.
|
|
135
|
+
const human = unattendedRunOf(homeId)?.humanNeeded;
|
|
136
|
+
unmark();
|
|
137
|
+
release();
|
|
138
|
+
if (human) finish(reject, new Error(routineHumanNeededText(human.outcome, human.site)));
|
|
139
|
+
else if (detail?.status === 'done') {
|
|
140
|
+
finish(resolve, { sessionId: homeId, result: String(detail?.result?.content || '') });
|
|
141
|
+
} else finish(reject, settledError(detail));
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
Promise.resolve()
|
|
145
|
+
.then(() => surface.submit(homeId, content, options))
|
|
146
|
+
.then((accepted) => {
|
|
147
|
+
if (accepted === true) return;
|
|
148
|
+
release();
|
|
149
|
+
finish(reject, new Error(`the bot's chat did not accept the ${kind} "${name}"`));
|
|
150
|
+
})
|
|
151
|
+
.catch((error) => {
|
|
152
|
+
release();
|
|
153
|
+
finish(reject, error);
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Routines are bot-based: a schedule or webhook runs in its owner bot's home
|
|
3
|
+
* thread (routine-home-turn.mjs) and nowhere else. An owner that does not
|
|
4
|
+
* exist — Mixy before the representative bot was created, a bot archived or
|
|
5
|
+
* deleted — makes the routine paused: it does not fire, the lists show why,
|
|
6
|
+
* and a new routine cannot name it.
|
|
7
|
+
*/
|
|
8
|
+
import { MIXY_AGENT_ID } from '../runtime/agent/orchestrator/session/agent-home.mjs';
|
|
9
|
+
import { scheduleOwnerAgentId } from './agent-home-delivery.mjs';
|
|
10
|
+
|
|
11
|
+
/** `ownerProblem` codes and the error each one raises. */
|
|
12
|
+
export const ROUTINE_OWNER_PROBLEMS = Object.freeze({
|
|
13
|
+
'no-mixy': 'Create the representative bot first.',
|
|
14
|
+
'owner-deleted': 'The bot that owns this routine no longer exists. Pick another bot.',
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
let editor = null;
|
|
18
|
+
async function defaultProbe() {
|
|
19
|
+
if (!editor) {
|
|
20
|
+
const [{ getPluginData }, { createAgentEditorApi }] = await Promise.all([
|
|
21
|
+
import('../runtime/agent/orchestrator/config.mjs'),
|
|
22
|
+
import('./workflow-agents-api/agent-editor.mjs'),
|
|
23
|
+
]);
|
|
24
|
+
editor = createAgentEditorApi({ cfgMod: { getPluginData } });
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
// An undecided profile (null) counts as having Mixy: nobody is blocked by
|
|
28
|
+
// a flag the host has not written yet.
|
|
29
|
+
mixyExists: () => editor.getLeadStatus().created !== false,
|
|
30
|
+
presence: (agentId) => editor.agentDefinitionPresence([agentId])[agentId],
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Why `ownerAgentId` (none = Mixy) cannot own or run a routine: a
|
|
35
|
+
* ROUTINE_OWNER_PROBLEMS code, '' when it can. Only a proven deletion
|
|
36
|
+
* counts for a bot; a read fault never pauses anything. */
|
|
37
|
+
export async function routineOwnerProblem(ownerAgentId, probe = null) {
|
|
38
|
+
const owner = scheduleOwnerAgentId({ ownerAgentId });
|
|
39
|
+
const check = probe || (await defaultProbe());
|
|
40
|
+
if (owner === MIXY_AGENT_ID) return check.mixyExists() ? '' : 'no-mixy';
|
|
41
|
+
return check.presence(owner) === 'deleted' ? 'owner-deleted' : '';
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Rejects with the owner's problem; resolves when it can own a routine. */
|
|
45
|
+
export async function assertRoutineOwner(ownerAgentId, probe = null) {
|
|
46
|
+
const problem = await routineOwnerProblem(ownerAgentId, probe);
|
|
47
|
+
if (problem) throw new Error(ROUTINE_OWNER_PROBLEMS[problem]);
|
|
48
|
+
}
|
|
@@ -1,71 +1,37 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Run a schedule
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* filter as its own type, so it shows in desktop Recent / TUI resume), the
|
|
7
|
-
* schedule's provider/model route, and the schedule's project cwd.
|
|
2
|
+
* Run a schedule fire: its instructions become one turn in its owner bot's
|
|
3
|
+
* home thread (routine-home-turn.mjs), under the owned-routine guards
|
|
4
|
+
* (owned-automation-run.mjs). Shared by the engine's "Run now" and the
|
|
5
|
+
* channels-worker scheduler.
|
|
8
6
|
*/
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import { modelRouteFields, parseScheduleModelRef } from '../runtime/shared/schedule-model-ref.mjs';
|
|
13
|
-
import { migrateCodexRoute } from '../runtime/agent/orchestrator/providers/openai-oauth-catalog-store.mjs';
|
|
14
|
-
import { automationWorkflowOpts } from './automation-workflow.mjs';
|
|
15
|
-
import { runAutomationTurns } from './automation-agents.mjs';
|
|
16
|
-
import { automationPromptContent } from '../runtime/shared/automation-attachments.mjs';
|
|
17
|
-
|
|
18
|
-
/** Resolve schedule.model into a {provider,model,effort?,fast?} route. */
|
|
19
|
-
function scheduleRouteFromModelRef(modelRef, config = null) {
|
|
20
|
-
const ref = parseScheduleModelRef(modelRef);
|
|
21
|
-
// A model the provider catalog superseded runs on its successor.
|
|
22
|
-
if (ref && typeof ref === 'object') return migrateCodexRoute(ref);
|
|
23
|
-
const cfg = config || loadConfig({ secrets: false });
|
|
24
|
-
const preset = cfg.presets?.find((p) => p.id === ref || p.name === ref);
|
|
25
|
-
if (!preset) {
|
|
26
|
-
throw new Error(`schedule model "${ref}" is neither a provider/model route nor a known preset name`);
|
|
27
|
-
}
|
|
28
|
-
return modelRouteFields(preset);
|
|
29
|
-
}
|
|
7
|
+
import { markScheduleSkipped } from '../runtime/channels/lib/schedules-db.mjs';
|
|
8
|
+
import { runOwnedAutomation } from './owned-automation-run.mjs';
|
|
9
|
+
import { runRoutineTurn } from './routine-home-turn.mjs';
|
|
30
10
|
|
|
31
11
|
/**
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
* while the fallback gets a small origin header.
|
|
12
|
+
* One fire of `schedule`. `prompt` overrides schedule.prompt when the caller
|
|
13
|
+
* (channels worker) already resolved the body; `attended` marks a person's
|
|
14
|
+
* "Run now" (no failure backoff).
|
|
36
15
|
*/
|
|
37
|
-
export async function runScheduleSession(schedule, {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
sourceType: 'schedule',
|
|
53
|
-
sourceName: schedule.name,
|
|
54
|
-
...(cwd ? { cwd } : {}),
|
|
55
|
-
desktopSession: cwd
|
|
56
|
-
? { classification: 'project', projectPath: cwd }
|
|
57
|
-
: { classification: 'task', projectPath: null },
|
|
58
|
-
...automationWorkflowOpts(schedule.workflow),
|
|
59
|
-
});
|
|
60
|
-
// The user message is the schedule's instructions verbatim: the desktop
|
|
61
|
-
// title/preview derive from it, so no "[Scheduled task: …]" header noise.
|
|
62
|
-
// Stored attachments ride along as composer-style content parts.
|
|
63
|
-
const content = automationPromptContent(prompt, schedule.attachments);
|
|
64
|
-
// Agents the run delegates to report back; each result is the next turn.
|
|
65
|
-
const result = await runAutomationTurns(
|
|
66
|
-
session.id,
|
|
67
|
-
() => askSession(session.id, content, null, null, cwd || undefined),
|
|
68
|
-
(completions, askOpts) => askSession(session.id, completions, null, null, cwd || undefined, undefined, askOpts)
|
|
16
|
+
export async function runScheduleSession(schedule, { attended = false, prompt = null, signal = null } = {}) {
|
|
17
|
+
const body = String(prompt ?? schedule.prompt ?? '').trim();
|
|
18
|
+
return runOwnedSchedule(
|
|
19
|
+
schedule,
|
|
20
|
+
({ signal: runSignal }) =>
|
|
21
|
+
runRoutineTurn({
|
|
22
|
+
kind: 'schedule',
|
|
23
|
+
name: schedule.name,
|
|
24
|
+
ownerAgentId: schedule.ownerAgentId,
|
|
25
|
+
prompt: body,
|
|
26
|
+
attachments: schedule.attachments,
|
|
27
|
+
cwd: schedule.cwd || null,
|
|
28
|
+
signal: runSignal,
|
|
29
|
+
}),
|
|
30
|
+
{ attended, signal }
|
|
69
31
|
);
|
|
70
|
-
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** One owned run of `schedule` (owned-automation-run.mjs). */
|
|
35
|
+
export function runOwnedSchedule(schedule, run, { recordSkip = markScheduleSkipped, ...deps } = {}) {
|
|
36
|
+
return runOwnedAutomation('schedule', schedule, run, { recordSkip, ...deps });
|
|
71
37
|
}
|