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
|
@@ -8,7 +8,7 @@ import { createProviderReadiness } from '../provider-readiness.mjs';
|
|
|
8
8
|
import { createRoutePreparationGate } from '../route-preparation.mjs';
|
|
9
9
|
import { createRuntimeFeatureGates } from '../runtime-feature-gates.mjs';
|
|
10
10
|
import { createLazyRuntimeModules } from '../runtime-modules.mjs';
|
|
11
|
-
import {
|
|
11
|
+
import { sessionToolProfile } from '../../runtime/agent/orchestrator/runtime-core/tool-profile.mjs';
|
|
12
12
|
|
|
13
13
|
// The boot record: `rt` (shared mutable runtime state, promoted from closure
|
|
14
14
|
// `let`s so extracted modules read/write live values through one reference),
|
|
@@ -18,13 +18,14 @@ import { normalizeToolProfile } from '../../runtime/agent/orchestrator/runtime-c
|
|
|
18
18
|
export function beginBoot(params) {
|
|
19
19
|
const { provider, model, toolMode, cwd, toolProfile, approvalMode, disallowDelegation } = params;
|
|
20
20
|
const rt = {};
|
|
21
|
-
rt.toolProfile = normalizeToolProfile(toolProfile);
|
|
22
21
|
rt.approvalMode = approvalMode === 'implicit' ? 'implicit' : null;
|
|
23
22
|
rt.disallowDelegation = disallowDelegation === true;
|
|
24
23
|
rt.mcpScopeId = randomUUID();
|
|
25
24
|
rt.desktopSession = params.desktopSession;
|
|
26
25
|
rt.sessionProfile =
|
|
27
26
|
params.sessionProfile && typeof params.sessionProfile === 'object' ? { ...params.sessionProfile } : null;
|
|
27
|
+
// A heartbeat run's session is fenced to its few tools (tool-profile.mjs).
|
|
28
|
+
rt.toolProfile = sessionToolProfile({ toolProfile, sessionProfile: rt.sessionProfile });
|
|
28
29
|
bootProfile('session-runtime:start', { provider, model, toolMode, cwd });
|
|
29
30
|
// Last assistant text handed to the transcript writer (via onAssistantText),
|
|
30
31
|
// so the post-turn final-content append can skip an exact duplicate.
|
|
@@ -22,6 +22,7 @@ import { TOOL_DEFS as MEDIA_TOOL_DEFS } from '../../runtime/media/tool-defs.mjs'
|
|
|
22
22
|
import { TOOL_DEFS as TIDY_TOOL_DEFS } from '../../runtime/tidy/tool-defs.mjs';
|
|
23
23
|
import { TOOL_DEFS as VISUALIZE_BRIDGE_TOOL_DEFS } from '../../runtime/visualize-bridge/tool-defs.mjs';
|
|
24
24
|
import { ASK_USER_TOOL_DEFS } from '../../runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs';
|
|
25
|
+
import { REACTION_TOOL_DEFS } from '../../runtime/agent/orchestrator/runtime-core/reaction-tool-defs.mjs';
|
|
25
26
|
import { SETUP_TOOL_DEFS } from '../setup-tool/tool-defs.mjs';
|
|
26
27
|
import { createSetupToolExecutor } from '../setup-tool/executor.mjs';
|
|
27
28
|
import { bootProfile } from '../boot-profile.mjs';
|
|
@@ -98,6 +99,8 @@ function wireAgent(boot) {
|
|
|
98
99
|
rt,
|
|
99
100
|
agentTool,
|
|
100
101
|
executeAgentControl: params.executeAgentControl,
|
|
102
|
+
// Lazy, as above.
|
|
103
|
+
listProviderModels: (options) => boot.collectProviderModels(options),
|
|
101
104
|
});
|
|
102
105
|
bootProfile('agent:ready', { ms: (performance.now() - agentToolStartedAt).toFixed(1) });
|
|
103
106
|
Object.assign(boot, { agentTool, routedAgentTool, agentStatusState });
|
|
@@ -165,6 +168,7 @@ function wireToolSurface(boot) {
|
|
|
165
168
|
visualizeToolDefs: VISUALIZE_BRIDGE_TOOL_DEFS,
|
|
166
169
|
setupToolDefs: SETUP_TOOL_DEFS,
|
|
167
170
|
askUserToolDefs: ASK_USER_TOOL_DEFS,
|
|
171
|
+
reactionToolDefs: REACTION_TOOL_DEFS,
|
|
168
172
|
goalTools: goalRuntime.tools,
|
|
169
173
|
agentTools: agentTool.tools,
|
|
170
174
|
skillToolEnabled: !envFlag('MIXDOG_DISABLE_SKILLS'),
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// The bot-to-bot chain a session acts in (bot-hops.mjs botChainOf), from
|
|
2
|
+
// trusted runtime state only: the session's own turn chain (stamped from the
|
|
3
|
+
// submission that started it) and its delegation ancestry (parentSessionId /
|
|
4
|
+
// ownerSessionId), never from tool input.
|
|
5
|
+
import { resolveSessionMode } from '../runtime/agent/orchestrator/session/agent-mode.mjs';
|
|
6
|
+
import { botChainOf, turnBotChainOf } from '../runtime/shared/bot-hops.mjs';
|
|
7
|
+
|
|
8
|
+
const clean = (value) => String(value ?? '').trim();
|
|
9
|
+
|
|
10
|
+
/** The agent `session` acts as, by the session-mode resolver: a Lead its
|
|
11
|
+
* resolved agent (a coding Lead Mixy), a delegated worker its role. */
|
|
12
|
+
export function actingAgentOf(session, sessionId = '') {
|
|
13
|
+
const { agentId } = resolveSessionMode({ session, sessionId });
|
|
14
|
+
return clean(agentId || session?.agent);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** The chain session `sessionId` acts in, oldest first, ending with the agent
|
|
18
|
+
* it acts as (when it has one): walking parentSessionId/ownerSessionId up
|
|
19
|
+
* from it, each session contributes its current turn's chain, then the
|
|
20
|
+
* agent it acts as. The visited set ends a malformed cycle. */
|
|
21
|
+
export function delegationChainOf(sessionId, getSession = () => null) {
|
|
22
|
+
const chain = [];
|
|
23
|
+
const seen = new Set();
|
|
24
|
+
for (let id = clean(sessionId); id && !seen.has(id); ) {
|
|
25
|
+
seen.add(id);
|
|
26
|
+
const session = getSession(id) || null;
|
|
27
|
+
const acting = actingAgentOf(session, id);
|
|
28
|
+
chain.unshift(...turnBotChainOf(session), ...(acting ? [acting] : []));
|
|
29
|
+
id = clean(session?.parentSessionId || session?.ownerSessionId);
|
|
30
|
+
}
|
|
31
|
+
return botChainOf(chain);
|
|
32
|
+
}
|
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
* — the human's own sessions (Mixy's home, an ordinary Lead session) see every
|
|
21
21
|
* bot; a bot's chat, a room seat or an unattended run (a routine, even one in
|
|
22
22
|
* Mixy's own chat) only its own bot; a background run none. `session list`
|
|
23
|
-
*
|
|
23
|
+
* names every session for every scope, but gives previews and bot details
|
|
24
|
+
* only for the sessions the scope sees here (a background run none).
|
|
24
25
|
*
|
|
25
26
|
* Titles and previews are other bots' words: tag-shaped text is escaped and
|
|
26
27
|
* the answer says they are data, not instructions (UNTRUSTED_NOTE).
|
|
@@ -63,8 +64,10 @@ const cut = (text, max) => (text.length > max ? `${text.slice(0, Math.max(0, max
|
|
|
63
64
|
export const quotedText = (value, max) => cut(escapeHarnessTags(oneLine(value)), max);
|
|
64
65
|
const plural = (count, word) => `${count} ${word}${count === 1 ? '' : 's'}`;
|
|
65
66
|
const outputBytes = (value) => Buffer.byteLength(JSON.stringify(value), 'utf8');
|
|
66
|
-
|
|
67
|
-
const
|
|
67
|
+
/** A catalog row whose session runs a turn now. */
|
|
68
|
+
export const isWorking = (row) => row?.working === true || row?.agentWorking === true;
|
|
69
|
+
/** A catalog row's last activity (epoch ms), 0 when unknown. */
|
|
70
|
+
export const activityOf = (row) => Number(row?.lastUsedAt) || Number(row?.updatedAt) || 0;
|
|
68
71
|
|
|
69
72
|
/** The bot a catalog row belongs to: its home chat, room seat or task origin; '' for none. */
|
|
70
73
|
export function sessionBotOf(row) {
|
|
@@ -309,7 +312,7 @@ export function createBotStatus({
|
|
|
309
312
|
const parts = [`${quotedText(bot.name, 40)} (${bot.id})`, `chat ${state.homeId}`];
|
|
310
313
|
if (bot.self) parts.push('you');
|
|
311
314
|
parts.push(state.working ? 'working' : 'idle');
|
|
312
|
-
if (state.flags.length) parts.push(
|
|
315
|
+
if (state.flags.length) parts.push(`attention: ${state.flags.join('; ')}`);
|
|
313
316
|
parts.push(state.lastAt ? `last ${ago(state.lastAt, now)}` : 'no chat yet');
|
|
314
317
|
if (state.running.length) {
|
|
315
318
|
const [first] = state.running;
|
|
@@ -3,19 +3,22 @@ import {
|
|
|
3
3
|
deleteSchedule,
|
|
4
4
|
deleteWebhook,
|
|
5
5
|
getWebhookSecret,
|
|
6
|
+
renameScheduleHere,
|
|
6
7
|
saveSchedule,
|
|
7
8
|
saveWebhook,
|
|
8
9
|
setScheduleEnabled,
|
|
9
10
|
setWebhookEnabled,
|
|
10
11
|
setWebhookConfigAsync,
|
|
11
12
|
testWebhook,
|
|
13
|
+
withScheduleRenameReserved,
|
|
12
14
|
} from './services/channel-admin.mjs';
|
|
13
|
-
import { getSchedule, markScheduleFailure, markScheduleSuccess } from '../runtime/channels/lib/schedules-db.mjs';
|
|
15
|
+
import { getSchedule, markScheduleFailure, markScheduleSkipped, markScheduleSuccess } from '../runtime/channels/lib/schedules-db.mjs';
|
|
16
|
+
import { scheduleRenameUncertainError } from '../runtime/channels/lib/schedule-errors.mjs';
|
|
14
17
|
import { loadEndpointConfig, readEndpointLastPayload } from '../runtime/channels/lib/webhooks-db.mjs';
|
|
15
18
|
import { webhookEventSummary, webhookRunPrompt } from '../runtime/channels/lib/webhook/session-dispatch.mjs';
|
|
16
19
|
import { runScheduleSession } from './schedule-session-run.mjs';
|
|
17
20
|
import { runWebhookSession } from './webhook-session-run.mjs';
|
|
18
|
-
import { retryRoutineDelivery } from './owned-automation-run.mjs';
|
|
21
|
+
import { beginAutomationRename, retryRoutineDelivery } from './owned-automation-run.mjs';
|
|
19
22
|
|
|
20
23
|
/** Why a "Run now" did not run: its owner bot is missing, or a run is going. */
|
|
21
24
|
function skippedRunMessage(name, outcome) {
|
|
@@ -27,6 +30,36 @@ function skippedRunMessage(name, outcome) {
|
|
|
27
30
|
// directly here and the runtime injects only the closure-owned callbacks
|
|
28
31
|
// (channel worker handle, soft reload).
|
|
29
32
|
export function createChannelConfigApi({ channels, reloadChannelsSoon, ensureAutomationRuntime = () => {} }) {
|
|
33
|
+
// The channels worker owns the scheduler and the runs it fires, so while it
|
|
34
|
+
// is up it performs the run check and the row move in one step
|
|
35
|
+
// (rename_schedule); this process's own runs ("Run now") are reserved around
|
|
36
|
+
// that call. Without a worker, the check and the move happen here.
|
|
37
|
+
async function renameSchedule(oldName, def, opts) {
|
|
38
|
+
if (!channels.status().running) return renameScheduleHere(oldName, def, opts);
|
|
39
|
+
return withScheduleRenameReserved(oldName, async () => {
|
|
40
|
+
try {
|
|
41
|
+
const reply = await channels.execute('rename_schedule', { name: oldName, def, ...opts });
|
|
42
|
+
const text = reply?.content?.[0]?.text ?? '';
|
|
43
|
+
if (reply?.isError === true) {
|
|
44
|
+
// The worker's refusal keeps its structured code and details.
|
|
45
|
+
throw Object.assign(
|
|
46
|
+
new Error(text.replace(/^rename_schedule failed: /, '')),
|
|
47
|
+
typeof reply.code === 'string' ? { code: reply.code } : {},
|
|
48
|
+
reply.details && typeof reply.details === 'object' ? { details: reply.details } : {}
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
return text ? JSON.parse(text) : null;
|
|
52
|
+
} catch (error) {
|
|
53
|
+
// Whatever the worker did, have it re-read the schedules.
|
|
54
|
+
reloadChannelsSoon();
|
|
55
|
+
// execute() already retried a transport failure (call timeout 120 s,
|
|
56
|
+
// same callId so the daemon runs the request once); what is left is
|
|
57
|
+
// an unanswered request.
|
|
58
|
+
if (error?.daemonTransportError) throw scheduleRenameUncertainError(error.message);
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
30
63
|
return {
|
|
31
64
|
async getChannelSetup() {
|
|
32
65
|
return channelSetup();
|
|
@@ -40,7 +73,7 @@ export function createChannelConfigApi({ channels, reloadChannelsSoon, ensureAut
|
|
|
40
73
|
return result;
|
|
41
74
|
},
|
|
42
75
|
async saveSchedule(entry) {
|
|
43
|
-
const result = await saveSchedule(entry);
|
|
76
|
+
const result = await saveSchedule(entry, { rename: renameSchedule });
|
|
44
77
|
reloadChannelsSoon();
|
|
45
78
|
// First automation created while the app is already running: the boot-
|
|
46
79
|
// time autostart check has passed, so kick the worker start path now
|
|
@@ -86,22 +119,35 @@ export function createChannelConfigApi({ channels, reloadChannelsSoon, ensureAut
|
|
|
86
119
|
},
|
|
87
120
|
async runScheduleNow(name) {
|
|
88
121
|
const id = String(name || '').trim();
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
//
|
|
92
|
-
|
|
93
|
-
|
|
122
|
+
// The name is held from the read of the row through the outcome stamps,
|
|
123
|
+
// so a rename can neither move the row this run read nor slip in before
|
|
124
|
+
// its stamps are written (they would land on no row).
|
|
125
|
+
const release = beginAutomationRename('schedule', id);
|
|
126
|
+
// A run or a rename holding it: not run, recorded as an overlap skip.
|
|
127
|
+
if (!release) {
|
|
128
|
+
await markScheduleSkipped(id, new Date()).catch(() => {});
|
|
129
|
+
throw new Error(skippedRunMessage(id, { reason: 'overlap' }));
|
|
130
|
+
}
|
|
94
131
|
try {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
132
|
+
const schedule = await getSchedule(id);
|
|
133
|
+
if (!schedule) throw new Error(`schedule "${id}" does not exist`);
|
|
134
|
+
// Runs in the engine process as a turn in the owner bot's chat, so it
|
|
135
|
+
// works even while the channels worker is off.
|
|
136
|
+
let outcome;
|
|
137
|
+
try {
|
|
138
|
+
// Asked for by a person: no failure backoff.
|
|
139
|
+
outcome = await runScheduleSession(schedule, { attended: true, reserved: true });
|
|
140
|
+
} catch (error) {
|
|
141
|
+
await markScheduleFailure(schedule.name, new Date()).catch(() => {});
|
|
142
|
+
throw error;
|
|
143
|
+
}
|
|
144
|
+
if (outcome.skipped) throw new Error(skippedRunMessage(schedule.name, outcome));
|
|
145
|
+
// The run's outcome shows as the schedule's last run, like a timed fire.
|
|
146
|
+
await markScheduleSuccess(schedule.name, new Date()).catch(() => {});
|
|
147
|
+
return { name: schedule.name, ok: true, sessionId: outcome.sessionId, result: outcome.result };
|
|
148
|
+
} finally {
|
|
149
|
+
release();
|
|
100
150
|
}
|
|
101
|
-
if (outcome.skipped) throw new Error(skippedRunMessage(schedule.name, outcome));
|
|
102
|
-
// The run's outcome shows as the schedule's last run, like a timed fire.
|
|
103
|
-
await markScheduleSuccess(schedule.name, new Date()).catch(() => {});
|
|
104
|
-
return { name: schedule.name, ok: true, sessionId: outcome.sessionId, result: outcome.result };
|
|
105
151
|
},
|
|
106
152
|
// Re-runs the endpoint on its latest received payload, with its current
|
|
107
153
|
// instructions, behind the same untrusted-data fence as a live delivery.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// react: a bot's emoji reaction on the user's message (message-reactions.mjs).
|
|
2
|
+
// Only a bot's own chat with the user may react: its home chat, where the
|
|
3
|
+
// message is in the chat's own transcript, or its seat in a group room, where
|
|
4
|
+
// the message is the user's row in the room's log. Never a coding session, a
|
|
5
|
+
// delegated worker, or a run nobody attends.
|
|
6
|
+
import { isAgentOwner } from '../../runtime/agent/orchestrator/agent-owner.mjs';
|
|
7
|
+
import { noteReactionTarget, validateReactionArgs } from '../../runtime/agent/orchestrator/runtime-core/reaction-tool-defs.mjs';
|
|
8
|
+
import { agentIdOfHomeSession } from '../../runtime/agent/orchestrator/session/agent-home.mjs';
|
|
9
|
+
import { roomMemberOfSession, roomSessionId } from '../../runtime/agent/orchestrator/session/agent-room.mjs';
|
|
10
|
+
import { reactInSession } from '../../runtime/agent/orchestrator/session/message-reactions.mjs';
|
|
11
|
+
import { agentHomeLiveSurface } from '../agent-home-delivery.mjs';
|
|
12
|
+
import { reactInRoom } from '../agent-room-delivery.mjs';
|
|
13
|
+
import { scheduleCallerScope } from '../services/schedule-access.mjs';
|
|
14
|
+
|
|
15
|
+
const DONE =
|
|
16
|
+
'The user sees it under their message. Say nothing about it; reply only if the message also needs an answer.';
|
|
17
|
+
|
|
18
|
+
export function createReactionToolHandlers({ rt, live = agentHomeLiveSurface, roomReact = reactInRoom }) {
|
|
19
|
+
return {
|
|
20
|
+
react: async (args, { callerCtx }) => {
|
|
21
|
+
const sessionId = callerCtx?.callerSessionId || rt.session?.id || '';
|
|
22
|
+
const session = rt.session?.id === sessionId ? rt.session : null;
|
|
23
|
+
const member = roomMemberOfSession(sessionId);
|
|
24
|
+
const agentId = member?.agentId || agentIdOfHomeSession(sessionId);
|
|
25
|
+
if (!session || !agentId || isAgentOwner(session)) {
|
|
26
|
+
throw new Error("react: reactions belong to a bot's own chat with the user");
|
|
27
|
+
}
|
|
28
|
+
if (scheduleCallerScope(session).unattended === true) {
|
|
29
|
+
throw new Error('react: nobody is in this run to see a reaction');
|
|
30
|
+
}
|
|
31
|
+
const { emoji, messagesBack } = validateReactionArgs(args);
|
|
32
|
+
const change = { agentId, emoji, messagesBack };
|
|
33
|
+
let result;
|
|
34
|
+
if (member) result = await roomReact(roomSessionId(member.roomId), change);
|
|
35
|
+
else {
|
|
36
|
+
result = reactInSession(session, change);
|
|
37
|
+
// The chat's own turn saves the transcript; its open views show it now.
|
|
38
|
+
if (result.changed) live()?.react?.(sessionId, result.messageId, result.reactions);
|
|
39
|
+
}
|
|
40
|
+
if (!emoji) return result.changed ? 'Removed your reaction.' : 'You had no reaction on that message.';
|
|
41
|
+
noteReactionTarget(sessionId, callerCtx?.toolCallId, { messageId: result.messageId, room: Boolean(member) });
|
|
42
|
+
return `Reacted ${emoji}. ${DONE}`;
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
@@ -6,11 +6,17 @@ import { listProjects } from '../../runtime/shared/projects.mjs';
|
|
|
6
6
|
import { applySessionSidebarChange } from '../../runtime/shared/session-sidebar-host.mjs';
|
|
7
7
|
import { clean } from '../../runtime/agent/orchestrator/runtime-core/session-text.mjs';
|
|
8
8
|
import { AGENT_MODE, resolveSessionMode } from '../../runtime/agent/orchestrator/session/agent-mode.mjs';
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
MIXY_AGENT_ID,
|
|
11
|
+
agentHomeSessionId,
|
|
12
|
+
agentIdOfHomeSession,
|
|
13
|
+
} from '../../runtime/agent/orchestrator/session/agent-home.mjs';
|
|
10
14
|
import { turnHopOf } from '../../runtime/shared/bot-hops.mjs';
|
|
15
|
+
import { isHeartbeatSession } from '../../runtime/shared/routine-trigger.mjs';
|
|
11
16
|
import { scheduleCallerScope } from '../services/schedule-access.mjs';
|
|
12
17
|
import { worktreeCallerScope } from '../services/worktree-access.mjs';
|
|
13
18
|
import { resolveHandoffAttachments, sessionRoots } from '../handoff-attachments.mjs';
|
|
19
|
+
import { delegationChainOf } from '../bot-chain.mjs';
|
|
14
20
|
|
|
15
21
|
const USER_TITLE_ERROR = 'session: the user already titled this session; the title was not changed';
|
|
16
22
|
|
|
@@ -53,6 +59,14 @@ export function createWorkspaceToolHandlers({
|
|
|
53
59
|
return rt.session.id;
|
|
54
60
|
};
|
|
55
61
|
|
|
62
|
+
// Mixy's hidden heartbeat session (mixy-heartbeat.mjs), by the in-process
|
|
63
|
+
// mark on the calling session, never by tool input or stored fields.
|
|
64
|
+
const callerIsHeartbeat = (callerCtx) => isHeartbeatSession(callerCtx?.callerSessionId || rt.session?.id);
|
|
65
|
+
const HEARTBEAT_NO_HANDOFF =
|
|
66
|
+
'a heartbeat turn cannot hand work off: its result would reach nobody. Propose it to the user instead (session action suggest).';
|
|
67
|
+
// What a heartbeat turn does with sessions: read them and the bots, and record proposals.
|
|
68
|
+
const HEARTBEAT_SESSION_ACTIONS = new Set(['read', 'status', 'list', 'bots', 'suggest']);
|
|
69
|
+
|
|
56
70
|
const SIDEBAR_ACTIONS = new Set(['favorite', 'archive']);
|
|
57
71
|
|
|
58
72
|
// Sidebar flags only (the desktop's favorite/archive metadata).
|
|
@@ -71,12 +85,26 @@ export function createWorkspaceToolHandlers({
|
|
|
71
85
|
const session = async (args, { callerCtx, callerCwd }) => {
|
|
72
86
|
const action = clean(args?.action || (args?.path ? 'set-project' : 'status')).toLowerCase();
|
|
73
87
|
const target = clean(args?.session_id);
|
|
88
|
+
if (callerIsHeartbeat(callerCtx) && !HEARTBEAT_SESSION_ACTIONS.has(action)) {
|
|
89
|
+
throw new Error(
|
|
90
|
+
`session: ${
|
|
91
|
+
action === 'create' || action === 'send'
|
|
92
|
+
? HEARTBEAT_NO_HANDOFF
|
|
93
|
+
: `a heartbeat turn only reads sessions and bots and records proposals; action=${action} is refused`
|
|
94
|
+
}`
|
|
95
|
+
);
|
|
96
|
+
}
|
|
74
97
|
if (
|
|
75
98
|
['list', 'bots', 'read', 'send', 'cancel', 'delete', 'create'].includes(action) ||
|
|
76
99
|
(action === 'status' && target)
|
|
77
100
|
) {
|
|
78
101
|
const callerSessionId = assertLead(action, callerCtx);
|
|
102
|
+
// `callerAgentId`: the bot whose conversation gets results reported
|
|
103
|
+
// back; `callerBotId`: the bot the caller acts as, attended or not,
|
|
104
|
+
// for what only a bot's own sessions allow (an effort change).
|
|
79
105
|
const callerAgentId = attendedAgentOf(rt.session);
|
|
106
|
+
const acting = resolveSessionMode({ session: rt.session });
|
|
107
|
+
const callerBotId = acting.mode === AGENT_MODE ? acting.agentId : '';
|
|
80
108
|
if (action === 'create' && !callerAgentId) {
|
|
81
109
|
throw new Error(
|
|
82
110
|
'session: create belongs to a bot conversation; delegated workers, agent task sessions and schedule/webhook runs cannot create agent task sessions'
|
|
@@ -105,8 +133,24 @@ export function createWorkspaceToolHandlers({
|
|
|
105
133
|
{
|
|
106
134
|
callerSessionId,
|
|
107
135
|
callerAgentId,
|
|
136
|
+
callerBotId,
|
|
108
137
|
callerCwd,
|
|
109
|
-
...(action === 'send' || action === 'create'
|
|
138
|
+
...(action === 'send' || action === 'create'
|
|
139
|
+
? {
|
|
140
|
+
callerHop: turnHopOf(rt.session),
|
|
141
|
+
// The bot chain this turn acts in (bot-chain.mjs), checked
|
|
142
|
+
// against the target and carried to the turn it starts —
|
|
143
|
+
// only when the sender is a bot conversation (a bot's chat,
|
|
144
|
+
// room seat or run); any other session hands work to any
|
|
145
|
+
// bot as before, whatever started its turn (the hop limit
|
|
146
|
+
// still bounds a loop).
|
|
147
|
+
callerBotChain: callerBotId
|
|
148
|
+
? delegationChainOf(callerSessionId, (id) =>
|
|
149
|
+
id === rt.session?.id ? rt.session : getSessionManager()?.getSession?.(id) || null
|
|
150
|
+
)
|
|
151
|
+
: [],
|
|
152
|
+
}
|
|
153
|
+
: {}),
|
|
110
154
|
// Whether a create may provision a worktree (worktree-access.mjs).
|
|
111
155
|
...(action === 'create' ? { callerScope: worktreeCallerScope(rt.session) } : {}),
|
|
112
156
|
// Which bots and sessions it may see (bot-status.mjs).
|
|
@@ -116,6 +160,16 @@ export function createWorkspaceToolHandlers({
|
|
|
116
160
|
)
|
|
117
161
|
);
|
|
118
162
|
}
|
|
163
|
+
// Mixy's proposals to the user (mixy-heartbeat.mjs): kept so none repeats.
|
|
164
|
+
if (action === 'suggest') {
|
|
165
|
+
const sessionId = assertLead(action, callerCtx);
|
|
166
|
+
if (sessionId !== agentHomeSessionId(MIXY_AGENT_ID) && !callerIsHeartbeat(callerCtx)) {
|
|
167
|
+
throw new Error("session: suggest belongs to Mixy's chat");
|
|
168
|
+
}
|
|
169
|
+
const { recordSuggestion } = await import('../mixy-heartbeat.mjs');
|
|
170
|
+
const receipt = recordSuggestion({ key: args?.key, text: args?.title, dismissed: args?.value === false });
|
|
171
|
+
return JSON.stringify({ action, ...receipt }, null, 2);
|
|
172
|
+
}
|
|
119
173
|
if (target && target !== assertLead(action, callerCtx)) {
|
|
120
174
|
throw new Error(`session: action=${action} only applies to the current session`);
|
|
121
175
|
}
|
|
@@ -184,6 +238,9 @@ export function createWorkspaceToolHandlers({
|
|
|
184
238
|
project: current?.workspace?.projectPath || current?.desktopSession?.projectPath || currentCwd,
|
|
185
239
|
provider: current?.provider || null,
|
|
186
240
|
model: current?.model || null,
|
|
241
|
+
effort: current?.effort || null,
|
|
242
|
+
autoEffort: current?.autoEffort !== false,
|
|
243
|
+
fast: current?.fast === true,
|
|
187
244
|
},
|
|
188
245
|
null,
|
|
189
246
|
2
|
|
@@ -229,12 +286,22 @@ export function createWorkspaceToolHandlers({
|
|
|
229
286
|
|
|
230
287
|
return {
|
|
231
288
|
session,
|
|
232
|
-
goal: async (args, { name, callerCtx }) =>
|
|
233
|
-
|
|
289
|
+
goal: async (args, { name, callerCtx }) => {
|
|
290
|
+
// A Goal keeps working in its session across turns: a heartbeat's would
|
|
291
|
+
// run on unseen in the hidden session.
|
|
292
|
+
if (['create', 'resume'].includes(clean(args?.action).toLowerCase()) && callerIsHeartbeat(callerCtx)) {
|
|
293
|
+
throw new Error(`goal: ${HEARTBEAT_NO_HANDOFF}`);
|
|
294
|
+
}
|
|
295
|
+
return await goalRuntime.executeTool(name, args || {}, {
|
|
234
296
|
callerSessionId: callerCtx?.callerSessionId || rt.session?.id || rt.reservedSessionId || null,
|
|
235
|
-
})
|
|
297
|
+
});
|
|
298
|
+
},
|
|
236
299
|
agent: async (args, { callerCtx, callerCwd }) => {
|
|
237
300
|
const callerSessionId = callerCtx?.callerSessionId || rt.session?.id || null;
|
|
301
|
+
// The agent tool's action is `type`, spawn when omitted (agent-tool/execute.mjs).
|
|
302
|
+
if (['spawn', 'send'].includes((clean(args?.type) || 'spawn').toLowerCase()) && callerIsHeartbeat(callerCtx)) {
|
|
303
|
+
throw new Error(`agent: ${HEARTBEAT_NO_HANDOFF}`);
|
|
304
|
+
}
|
|
238
305
|
// `attachmentParts` is never the model's to set: only the resolved
|
|
239
306
|
// `attachments` reach the agent's turn.
|
|
240
307
|
const { attachments, attachmentParts: _ignored, ...rest } = args || {};
|
|
@@ -10,6 +10,7 @@ import { createAskUserToolHandlers } from './internal-tool-executor/ask-user-too
|
|
|
10
10
|
import { createBridgeToolHandlers } from './internal-tool-executor/bridge-tools.mjs';
|
|
11
11
|
import { createFeatureToolHandlers } from './internal-tool-executor/feature-tools.mjs';
|
|
12
12
|
import { createKnowledgeToolHandlers } from './internal-tool-executor/knowledge-tools.mjs';
|
|
13
|
+
import { createReactionToolHandlers } from './internal-tool-executor/reaction-tool.mjs';
|
|
13
14
|
import { createWorkspaceToolHandlers } from './internal-tool-executor/workspace-tools.mjs';
|
|
14
15
|
|
|
15
16
|
export function createInternalToolExecutor({
|
|
@@ -40,6 +41,7 @@ export function createInternalToolExecutor({
|
|
|
40
41
|
const handlers = {
|
|
41
42
|
...createBridgeToolHandlers({ rt }),
|
|
42
43
|
...createAskUserToolHandlers({ rt }),
|
|
44
|
+
...createReactionToolHandlers({ rt }),
|
|
43
45
|
...createFeatureToolHandlers({ rt, setupTool, officeToolsEnabled, mediaToolEnabled, tidyToolEnabled }),
|
|
44
46
|
...createKnowledgeToolHandlers({
|
|
45
47
|
rt,
|
|
@@ -80,6 +82,8 @@ export function createInternalToolExecutor({
|
|
|
80
82
|
|
|
81
83
|
return async (name, args, callerCtx = {}) => {
|
|
82
84
|
const callerCwd = clean(callerCtx?.callerCwd) || rt.currentCwd;
|
|
85
|
+
// The Reactions setting gates the tool whoever calls it, not only the model.
|
|
86
|
+
if (name === 'react' && rt.config?.reactions === false) throw new Error('reactions are turned off in settings');
|
|
83
87
|
if (callerCtx?.invocationSource === 'model-tool') guardModelCall(name);
|
|
84
88
|
if (Object.hasOwn(handlers, name)) return handlers[name](args, { name, callerCtx, callerCwd });
|
|
85
89
|
if (channels.isChannelTool(name)) {
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// explicit owner metadata), per-server config reads, and the live/failed rollup.
|
|
4
4
|
import { clean } from '../../runtime/agent/orchestrator/runtime-core/session-text.mjs';
|
|
5
5
|
import { connectorOfServer, gmailAppPasswordAccount } from '../../runtime/shared/connector-catalog.mjs';
|
|
6
|
+
import { googleOAuthClient } from '../../runtime/shared/google-oauth-client.mjs';
|
|
6
7
|
import { envFlag } from '../../runtime/shared/env.mjs';
|
|
7
8
|
import { resolveMcpServerCatalog } from './plugin-servers.mjs';
|
|
8
9
|
|
|
@@ -153,6 +154,8 @@ export function createMcpServerCatalog({
|
|
|
153
154
|
// Gmail connected with an app password: the account it runs as.
|
|
154
155
|
const appPasswordAccount = gmailAppPasswordAccount(cfg);
|
|
155
156
|
if (appPasswordAccount) row.appPassword = { account: appPasswordAccount };
|
|
157
|
+
// A catalog connector running on the user's own Google client.
|
|
158
|
+
if (connector && typeof cfg?.oauthClientId === 'string' && cfg.oauthClientId.trim()) row.ownClient = true;
|
|
156
159
|
if (entry.owner) {
|
|
157
160
|
row.owner = entry.owner;
|
|
158
161
|
row.pluginEnabled = entry.pluginEnabled;
|
|
@@ -172,7 +175,7 @@ export function createMcpServerCatalog({
|
|
|
172
175
|
|
|
173
176
|
function mcpStatus() {
|
|
174
177
|
if (envFlag('MIXDOG_DISABLE_MCP')) {
|
|
175
|
-
return { servers: [], configuredCount: 0, connectedCount: 0, failedCount: 0 };
|
|
178
|
+
return { servers: [], configuredCount: 0, connectedCount: 0, failedCount: 0, googleClient: Boolean(googleOAuthClient()) };
|
|
176
179
|
}
|
|
177
180
|
const catalog = resolveCatalog();
|
|
178
181
|
const servers = statusRows(catalog);
|
|
@@ -186,6 +189,9 @@ export function createMcpServerCatalog({
|
|
|
186
189
|
failedCount: servers.filter((row) => row.status === 'failed').length,
|
|
187
190
|
warnings: catalog.warnings,
|
|
188
191
|
declarationErrors,
|
|
192
|
+
// Whether this build carries Mixdog's own Google client (else the
|
|
193
|
+
// Google sign-ins need the user's own client).
|
|
194
|
+
googleClient: Boolean(googleOAuthClient()),
|
|
189
195
|
};
|
|
190
196
|
}
|
|
191
197
|
|