mixdog 1.2.6 → 1.3.0
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 +19 -0
- 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 +7 -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 +11 -4
- package/src/defaults/skills/setup-guide/SKILL.md +175 -0
- package/src/defaults/skills/setup-guide/model-recommendations.json +333 -0
- package/src/lib/rules-builder.cjs +55 -6
- 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 +21 -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 +182 -13
- package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +4 -2
- package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +215 -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-login.mjs +10 -3
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-tokens.mjs +4 -0
- package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
- 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/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 +12 -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 +10 -4
- package/src/runtime/agent/orchestrator/runtime-core/workflow/agents.mjs +4 -1
- package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +50 -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 +162 -0
- package/src/runtime/agent/orchestrator/session/agent-room.mjs +641 -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 +165 -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/pre-dispatch-deny.mjs +10 -0
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +16 -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 +7 -0
- 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/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 +27 -5
- package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +55 -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/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 +24 -0
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +32 -5
- package/src/runtime/agent/orchestrator/session/routine-result.d.mts +70 -0
- package/src/runtime/agent/orchestrator/session/routine-result.mjs +277 -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 +44 -1
- 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/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/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-route-config.d.mts +3 -0
- package/src/runtime/shared/agent-route-config.mjs +16 -0
- package/src/runtime/shared/atomic-file.mjs +14 -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 +52 -0
- package/src/runtime/shared/connector-catalog.mjs +230 -0
- package/src/runtime/shared/connector-triggers.d.mts +3 -0
- package/src/runtime/shared/connector-triggers.mjs +43 -0
- package/src/runtime/shared/google-oauth-client.mjs +24 -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/plain-preview.d.mts +1 -0
- package/src/runtime/shared/plain-preview.mjs +28 -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/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 +56 -0
- package/src/runtime/shared/user-data-guard.mjs +114 -36
- package/src/session-runtime/agent-home-delivery.mjs +459 -0
- package/src/session-runtime/agent-profile-changes.mjs +21 -0
- package/src/session-runtime/agent-room-delivery.mjs +92 -0
- package/src/session-runtime/agent-task-sessions.mjs +200 -0
- package/src/session-runtime/boot/apis.mjs +50 -18
- package/src/session-runtime/boot/facade.mjs +5 -8
- package/src/session-runtime/boot/infrastructure.mjs +28 -17
- package/src/session-runtime/boot/tools.mjs +3 -0
- package/src/session-runtime/channel-config-api.mjs +53 -5
- 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/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 +31 -2
- 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/set-route.mjs +14 -3
- 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/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 +10 -0
- 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/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 +68 -6
- package/src/session-runtime/setup-tool/extended-actions.mjs +72 -14
- package/src/session-runtime/setup-tool/settings-contract.mjs +8 -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 +238 -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 +3 -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 +556 -22
- 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 +11 -2
- package/src/standalone/daemon.mjs +18 -4
- package/src/standalone/session-protocol.mjs +27 -0
- package/src/standalone/session-service/session-calls/session-turns.mjs +9 -0
- package/src/standalone/session-service.mjs +19 -0
- package/src/tui/app/slash-commands.mjs +20 -0
- package/src/tui/dist/index.mjs +164 -86
- package/src/tui/session/context-state.mjs +6 -1
- package/src/tui/session/notice-surface.mjs +35 -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 +48 -3
- 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 +6 -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 +56 -0
- package/src/tui/session/turn.mjs +22 -6
- package/src/session-runtime/automation-agents.mjs +0 -94
- package/src/session-runtime/automation-workflow.mjs +0 -59
|
@@ -23,13 +23,14 @@ import {
|
|
|
23
23
|
setEndpointEnabled as dbSetEndpointEnabled,
|
|
24
24
|
} from '../../runtime/channels/lib/webhooks-db.mjs';
|
|
25
25
|
import { normalizeAutomationAttachments } from '../../runtime/shared/automation-attachments.mjs';
|
|
26
|
-
import {
|
|
26
|
+
import { assertRoutineOwner, routineOwnerProblem } from '../routine-owner.mjs';
|
|
27
27
|
import { hasEnabledAutomation } from '../../runtime/shared/automation-presence.mjs';
|
|
28
28
|
import { resolveScheduleTimezone, validateScheduleCron } from '../../runtime/shared/schedule-time.mjs';
|
|
29
29
|
import { pgClusterExists } from '../../runtime/memory/lib/pg/adapter.mjs';
|
|
30
30
|
import { resolvePluginData } from '../../runtime/shared/plugin-paths.mjs';
|
|
31
31
|
import { readHookPublicBase } from '../../runtime/channels/lib/webhook/relay-tunnel.mjs';
|
|
32
32
|
import { WEBHOOK_TEST_FIELD, WEBHOOK_TEST_HEADER } from '../../runtime/channels/lib/webhook/request-handler.mjs';
|
|
33
|
+
import { MIXY_AGENT_ID, agentHomeSessionId } from '../../runtime/agent/orchestrator/session/agent-home.mjs';
|
|
33
34
|
|
|
34
35
|
const NAME_RE = /^[a-z0-9][a-z0-9-]{0,63}$/;
|
|
35
36
|
|
|
@@ -187,7 +188,7 @@ function parseAtDatetime(at) {
|
|
|
187
188
|
// Map the store's def shape onto the flat display shape every schedule reader
|
|
188
189
|
// (channelSetup, renderChannelStatus, TUI pickers) consumes: `.time` renders
|
|
189
190
|
// the cron or the one-shot datetime, `.route` the channel/session target.
|
|
190
|
-
function scheduleToDisplay(s) {
|
|
191
|
+
function scheduleToDisplay(s, ownerProblem = '') {
|
|
191
192
|
return {
|
|
192
193
|
name: s.name,
|
|
193
194
|
description: s.description || '',
|
|
@@ -195,26 +196,85 @@ function scheduleToDisplay(s) {
|
|
|
195
196
|
whenAt: s.whenAt || undefined,
|
|
196
197
|
whenCron: s.whenCron || undefined,
|
|
197
198
|
timezone: s.timezone || undefined,
|
|
198
|
-
// Shown (and re-saved by the editor) on the catalog successor of a superseded model.
|
|
199
|
-
model: s.model ? migrateCodexModelRef(s.model) : undefined,
|
|
200
199
|
cwd: s.cwd || undefined,
|
|
201
|
-
workflow: s.workflow || undefined,
|
|
202
200
|
attachments: s.attachments || undefined,
|
|
203
201
|
enabled: s.enabled !== false,
|
|
204
202
|
instructions: s.prompt,
|
|
205
203
|
route: 'session',
|
|
204
|
+
// The bot it belongs to: each run is a turn in that bot's chat.
|
|
205
|
+
owner: s.ownerAgentId || MIXY_AGENT_ID,
|
|
206
|
+
// Why it is paused for its owner (routine-owner.mjs); absent when it runs.
|
|
207
|
+
...(ownerProblem ? { ownerProblem } : {}),
|
|
208
|
+
nextFireAt: isoOrUndefined(s.nextFireAt),
|
|
209
|
+
lastSuccessAt: isoOrUndefined(s.lastSuccessAt),
|
|
210
|
+
lastFailedAt: isoOrUndefined(s.lastFailedAt),
|
|
211
|
+
lastSkippedAt: isoOrUndefined(s.lastSkippedAt),
|
|
212
|
+
...routineHealth(s),
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Failures in a row (the routine pauses itself at the limit) and the results
|
|
217
|
+
// of finished runs still not queued for the owner: `{ count, failed, error }`,
|
|
218
|
+
// `failed` when the queue refused one (the app offers a retry).
|
|
219
|
+
function routineHealth(row) {
|
|
220
|
+
const entries = Object.values(row.resultDelivery || {}).filter(Boolean);
|
|
221
|
+
const refused = entries.filter((entry) => entry.state === 'failed').at(-1);
|
|
222
|
+
return {
|
|
223
|
+
consecutiveFailures: Number(row.consecutiveFailures) || 0,
|
|
224
|
+
...(entries.length
|
|
225
|
+
? {
|
|
226
|
+
undelivered: {
|
|
227
|
+
count: entries.length,
|
|
228
|
+
failed: Boolean(refused),
|
|
229
|
+
...(refused?.error ? { error: String(refused.error) } : {}),
|
|
230
|
+
},
|
|
231
|
+
}
|
|
232
|
+
: {}),
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function isoOrUndefined(value) {
|
|
237
|
+
if (!value) return undefined;
|
|
238
|
+
const at = new Date(value);
|
|
239
|
+
return Number.isNaN(at.getTime()) ? undefined : at.toISOString();
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// A schedule's or webhook's owner: an explicit agent id, else the saved owner
|
|
243
|
+
// on an overwrite, else Mixy.
|
|
244
|
+
function scheduleOwner(owner, existing) {
|
|
245
|
+
const value = String(owner ?? '').trim();
|
|
246
|
+
if (value) {
|
|
247
|
+
if (!agentHomeSessionId(value)) throw new Error(`schedule owner "${value}" is not a valid agent id`);
|
|
248
|
+
return value;
|
|
249
|
+
}
|
|
250
|
+
return existing?.ownerAgentId || MIXY_AGENT_ID;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/** Each owner's problem (routine-owner.mjs), probed once per owner. */
|
|
254
|
+
function ownerProblems() {
|
|
255
|
+
const seen = new Map();
|
|
256
|
+
return (ownerAgentId) => {
|
|
257
|
+
const owner = ownerAgentId || MIXY_AGENT_ID;
|
|
258
|
+
if (!seen.has(owner))
|
|
259
|
+
seen.set(
|
|
260
|
+
owner,
|
|
261
|
+
routineOwnerProblem(owner).catch(() => '')
|
|
262
|
+
);
|
|
263
|
+
return seen.get(owner);
|
|
206
264
|
};
|
|
207
265
|
}
|
|
208
266
|
|
|
209
267
|
export async function listSchedules() {
|
|
210
268
|
const rows = await dbListSchedules();
|
|
211
|
-
|
|
269
|
+
const problemOf = ownerProblems();
|
|
270
|
+
return Promise.all(rows.map(async (row) => scheduleToDisplay(row, await problemOf(row.ownerAgentId))));
|
|
212
271
|
}
|
|
213
272
|
|
|
214
273
|
// Register or update a schedule in the PG store. Recurring input maps `time`
|
|
215
274
|
// (+ optional `days`) to a cron; one-shot input maps an `at` datetime; the two
|
|
216
275
|
// are mutually exclusive (also enforced by the store's when_at/when_cron XOR).
|
|
217
|
-
// Every schedule runs as a
|
|
276
|
+
// Every schedule runs as a turn in its owner bot's chat; a bot that does not
|
|
277
|
+
// exist (no representative bot yet, an archived bot) cannot own one.
|
|
218
278
|
export async function saveSchedule({
|
|
219
279
|
name,
|
|
220
280
|
description = '',
|
|
@@ -222,26 +282,26 @@ export async function saveSchedule({
|
|
|
222
282
|
at,
|
|
223
283
|
timezone,
|
|
224
284
|
days,
|
|
225
|
-
model,
|
|
226
285
|
cwd,
|
|
227
|
-
workflow,
|
|
228
286
|
attachments,
|
|
229
287
|
enabled,
|
|
230
288
|
instructions,
|
|
289
|
+
owner,
|
|
231
290
|
overwrite = false,
|
|
232
291
|
} = {}) {
|
|
233
292
|
const id = assertScheduleName(name);
|
|
234
293
|
const body = String(instructions || '').trim();
|
|
235
294
|
if (!body) throw new Error('schedule instructions are required');
|
|
236
|
-
const scheduleModel = String(model || '').trim();
|
|
237
|
-
if (!scheduleModel) throw new Error('schedule model is required — choose a model before saving');
|
|
238
295
|
const hasTime = time != null && String(time).trim() !== '';
|
|
239
296
|
const hasAt = at != null && String(at).trim() !== '';
|
|
240
297
|
if (hasTime && hasAt) throw new Error('provide either `time` (recurring) or `at` (one-shot), not both');
|
|
241
298
|
if (!hasTime && !hasAt) throw new Error('either `time` (recurring cron) or `at` (one-shot datetime) is required');
|
|
242
|
-
|
|
299
|
+
const existing = await dbGetSchedule(id);
|
|
300
|
+
if (overwrite !== true && existing) {
|
|
243
301
|
throw new Error(`schedule "${id}" already exists`);
|
|
244
302
|
}
|
|
303
|
+
const ownerAgentId = scheduleOwner(owner, existing);
|
|
304
|
+
await assertRoutineOwner(ownerAgentId);
|
|
245
305
|
const whenCron = hasTime ? validateScheduleCron(foldDaysIntoCron(normalizeCron(time), days)) : null;
|
|
246
306
|
const whenAt = hasAt ? parseAtDatetime(at) : null;
|
|
247
307
|
const scheduleTimezone = hasTime ? resolveScheduleTimezone(timezone) : null;
|
|
@@ -253,14 +313,13 @@ export async function saveSchedule({
|
|
|
253
313
|
timezone: scheduleTimezone,
|
|
254
314
|
target: 'session',
|
|
255
315
|
channelId: null,
|
|
256
|
-
model: scheduleModel,
|
|
257
316
|
cwd: cwd ? String(cwd).trim() : null,
|
|
258
|
-
workflow: workflow ? String(workflow).trim() : null,
|
|
259
317
|
attachments: normalizeAutomationAttachments(attachments),
|
|
260
318
|
delivery: 'app',
|
|
261
319
|
prompt: body,
|
|
262
320
|
enabled: enabled !== false,
|
|
263
321
|
nextFireAt: whenAt,
|
|
322
|
+
ownerAgentId,
|
|
264
323
|
});
|
|
265
324
|
automationProbe.invalidate();
|
|
266
325
|
return scheduleToDisplay(saved);
|
|
@@ -287,13 +346,13 @@ export async function setScheduleEnabled(name, enabled) {
|
|
|
287
346
|
// store's migration hook.
|
|
288
347
|
async function listWebhooks() {
|
|
289
348
|
const endpoints = await dbListEndpoints();
|
|
290
|
-
|
|
349
|
+
const problemOf = ownerProblems();
|
|
350
|
+
const problems = await Promise.all(endpoints.map((ep) => problemOf(ep.ownerAgentId)));
|
|
351
|
+
return endpoints.map((ep, index) => ({
|
|
291
352
|
name: ep.name,
|
|
292
353
|
description: ep.description || '',
|
|
293
354
|
parser: ep.parser || 'github',
|
|
294
|
-
...(ep.model ? { model: migrateCodexModelRef(ep.model) } : {}),
|
|
295
355
|
...(ep.cwd ? { cwd: ep.cwd } : {}),
|
|
296
|
-
...(ep.workflow ? { workflow: ep.workflow } : {}),
|
|
297
356
|
...(ep.attachments ? { attachments: ep.attachments } : {}),
|
|
298
357
|
enabled: ep.enabled,
|
|
299
358
|
// The store never projects the plaintext secret through list paths; it
|
|
@@ -302,6 +361,16 @@ async function listWebhooks() {
|
|
|
302
361
|
secret: undefined,
|
|
303
362
|
instructions: ep.instructions,
|
|
304
363
|
route: 'session',
|
|
364
|
+
// The bot it belongs to: each run is a turn in that bot's chat.
|
|
365
|
+
owner: ep.ownerAgentId || MIXY_AGENT_ID,
|
|
366
|
+
...(problems[index] ? { ownerProblem: problems[index] } : {}),
|
|
367
|
+
lastEventAt: isoOrUndefined(ep.lastEventAt),
|
|
368
|
+
lastSuccessAt: isoOrUndefined(ep.lastSuccessAt),
|
|
369
|
+
lastFailedAt: isoOrUndefined(ep.lastFailedAt),
|
|
370
|
+
lastSkippedAt: isoOrUndefined(ep.lastSkippedAt),
|
|
371
|
+
// Whether "Run now" has a received payload to re-run (never the payload).
|
|
372
|
+
hasLastPayload: ep.hasLastPayload === true,
|
|
373
|
+
...routineHealth(ep),
|
|
305
374
|
}));
|
|
306
375
|
}
|
|
307
376
|
|
|
@@ -310,12 +379,11 @@ export async function saveWebhook({
|
|
|
310
379
|
description = '',
|
|
311
380
|
parser = 'github',
|
|
312
381
|
secret,
|
|
313
|
-
model,
|
|
314
382
|
cwd,
|
|
315
|
-
workflow,
|
|
316
383
|
attachments,
|
|
317
384
|
enabled,
|
|
318
385
|
instructions,
|
|
386
|
+
owner,
|
|
319
387
|
overwrite = false,
|
|
320
388
|
} = {}) {
|
|
321
389
|
const id = assertName(name, 'webhook name');
|
|
@@ -327,9 +395,12 @@ export async function saveWebhook({
|
|
|
327
395
|
}
|
|
328
396
|
const body = String(instructions || '').trim();
|
|
329
397
|
if (!body) throw new Error('webhook instructions are required');
|
|
330
|
-
|
|
398
|
+
const existing = await dbLoadEndpoint(id);
|
|
399
|
+
if (overwrite !== true && existing) {
|
|
331
400
|
throw new Error(`webhook "${id}" already exists`);
|
|
332
401
|
}
|
|
402
|
+
const ownerAgentId = scheduleOwner(owner, existing);
|
|
403
|
+
await assertRoutineOwner(ownerAgentId);
|
|
333
404
|
// Secret semantics: an explicit value always wins; an EMPTY value on an
|
|
334
405
|
// overwrite PRESERVES the stored secret (editing instructions must not
|
|
335
406
|
// silently rotate the key the external service was configured with); only
|
|
@@ -343,23 +414,21 @@ export async function saveWebhook({
|
|
|
343
414
|
description: String(description || '').trim(),
|
|
344
415
|
parser: nextParser,
|
|
345
416
|
channelId: null,
|
|
346
|
-
model: model ? String(model).trim() : null,
|
|
347
417
|
cwd: cwd ? String(cwd).trim() : null,
|
|
348
|
-
workflow: workflow ? String(workflow).trim() : null,
|
|
349
418
|
attachments: normalizeAutomationAttachments(attachments),
|
|
350
419
|
delivery: 'app',
|
|
351
420
|
secret: secretValue,
|
|
352
421
|
instructions: body,
|
|
353
422
|
enabled: enabled !== false,
|
|
423
|
+
ownerAgentId,
|
|
354
424
|
});
|
|
355
425
|
automationProbe.invalidate();
|
|
356
426
|
return {
|
|
357
427
|
name: id,
|
|
428
|
+
owner: saved.ownerAgentId || ownerAgentId,
|
|
358
429
|
description: saved.description,
|
|
359
430
|
parser: saved.parser,
|
|
360
|
-
...(saved.model ? { model: saved.model } : {}),
|
|
361
431
|
...(saved.cwd ? { cwd: saved.cwd } : {}),
|
|
362
|
-
...(saved.workflow ? { workflow: saved.workflow } : {}),
|
|
363
432
|
...(saved.attachments ? { attachments: saved.attachments } : {}),
|
|
364
433
|
...(enabled === false ? { enabled: false } : {}),
|
|
365
434
|
secret: secretValue,
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Whose memory a session reads and writes (runtime/memory/lib/
|
|
2
|
+
// core-memory-access.mjs enforces it). Mixy is the Lead configuration, so the
|
|
3
|
+
// user's own sessions (an ordinary Lead session, Mixy's home) act as Mixy; an
|
|
4
|
+
// agent's home thread and its room member sessions act as that agent; a
|
|
5
|
+
// schedule or webhook run acts for its owning agent (`ownerAgentId`, stamped
|
|
6
|
+
// at creation); a delegated agent as its own agent id.
|
|
7
|
+
import { MIXY_AGENT_ID, agentHomeSessionId } from '../../runtime/agent/orchestrator/session/agent-home.mjs';
|
|
8
|
+
import { resolveSessionMode } from '../../runtime/agent/orchestrator/session/agent-mode.mjs';
|
|
9
|
+
import { isAgentOwner } from '../../runtime/agent/orchestrator/agent-owner.mjs';
|
|
10
|
+
import { scheduleCallerScope } from './schedule-access.mjs';
|
|
11
|
+
|
|
12
|
+
const validAgentId = (value) => {
|
|
13
|
+
const id = typeof value === 'string' ? value.trim() : '';
|
|
14
|
+
return id && agentHomeSessionId(id) ? id : '';
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/** The `memory_caller` `{ kind, agentId }` of a calling session record:
|
|
18
|
+
* 'human' and 'agent' are conversations with the user, 'room-member' and
|
|
19
|
+
* 'headless' unattended runs. */
|
|
20
|
+
export function memoryCallerScope(session) {
|
|
21
|
+
const scope = scheduleCallerScope(session);
|
|
22
|
+
if (scope.kind === 'human') return { kind: 'human', agentId: MIXY_AGENT_ID };
|
|
23
|
+
if (scope.kind !== 'headless') return scope;
|
|
24
|
+
const delegated = isAgentOwner(session) || Boolean(session?.parentSessionId);
|
|
25
|
+
return {
|
|
26
|
+
kind: 'headless',
|
|
27
|
+
agentId: validAgentId(session?.ownerAgentId) || (delegated ? validAgentId(session?.agent) : ''),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** The agent whose own memory a session's prompt carries: its home thread's,
|
|
32
|
+
* room member's or run owner's agent, Mixy for the user's Lead sessions, and
|
|
33
|
+
* none for a delegated agent. `session` is the live record when there is
|
|
34
|
+
* one, else the reserved id and creation profile. */
|
|
35
|
+
export function injectedMemoryAgentId({ session = null, sessionId = '', profile = null } = {}) {
|
|
36
|
+
return resolveSessionMode({ session, sessionId, profile }).agentId;
|
|
37
|
+
}
|
|
@@ -367,6 +367,7 @@ function oauthAuthFields(p, auth) {
|
|
|
367
367
|
status: auth.status || (authenticated ? 'Set' : 'Not Set'),
|
|
368
368
|
detail: auth.detail || p.desc,
|
|
369
369
|
expiresAt: auth.expiresAt || null,
|
|
370
|
+
plan: auth.plan || null,
|
|
370
371
|
usable: auth.usable === true || (auth.usable == null && authenticated),
|
|
371
372
|
refreshable: auth.refreshable === true,
|
|
372
373
|
reauthRequired: auth.reauthRequired === true,
|
|
@@ -958,8 +959,8 @@ async function withTimeout(run, ms, label) {
|
|
|
958
959
|
}
|
|
959
960
|
}
|
|
960
961
|
|
|
961
|
-
function existingCustomEntry(
|
|
962
|
-
const entry =
|
|
962
|
+
function existingCustomEntry(config, id) {
|
|
963
|
+
const entry = config.providers?.[id];
|
|
963
964
|
if (!isCustomProviderId(id) || !isCustomProviderConfig(entry)) throw new Error(`unknown custom provider "${id}"`);
|
|
964
965
|
return entry;
|
|
965
966
|
}
|
|
@@ -1021,16 +1022,15 @@ function withStoredSettings(input, entry) {
|
|
|
1021
1022
|
return out;
|
|
1022
1023
|
}
|
|
1023
1024
|
|
|
1024
|
-
// Validated, secret-free config for `input
|
|
1025
|
-
//
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1025
|
+
// Validated, secret-free config for `input` over one config snapshot (the
|
|
1026
|
+
// stored entry supplies an edit's omitted settings); the key (typed, else
|
|
1027
|
+
// stored for an existing provider) is resolved separately and never enters
|
|
1028
|
+
// the config. Synchronous, so a caller can merge and commit one snapshot.
|
|
1029
|
+
function customInputAt(snapshot, input, normalizeCustomProviderConfig, options) {
|
|
1029
1030
|
const id = input.id === undefined ? null : input.id;
|
|
1030
|
-
|
|
1031
|
-
const
|
|
1032
|
-
const
|
|
1033
|
-
const typed = typeof input.apiKey === 'string' ? input.apiKey.trim() : '';
|
|
1031
|
+
const merged = id ? withStoredSettings(input, existingCustomEntry(snapshot, id)) : input;
|
|
1032
|
+
const config = normalizeCustomProviderConfig(merged, options);
|
|
1033
|
+
const typed = typeof merged.apiKey === 'string' ? merged.apiKey.trim() : '';
|
|
1034
1034
|
const keyless = config.auth === 'none';
|
|
1035
1035
|
const envName = keyless ? '' : config.apiKeyEnv || '';
|
|
1036
1036
|
let apiKey = '';
|
|
@@ -1039,36 +1039,71 @@ async function resolveCustomInput(cfgMod, rawInput, options) {
|
|
|
1039
1039
|
return { id, config, typed: keyless || envName ? '' : typed, apiKey, keyless, envName };
|
|
1040
1040
|
}
|
|
1041
1041
|
|
|
1042
|
+
async function resolveCustomInput(cfgMod, rawInput, options) {
|
|
1043
|
+
const input = inputObject(rawInput);
|
|
1044
|
+
validateCustomTransportFields(input);
|
|
1045
|
+
const { normalizeCustomProviderConfig } = await loadCustomProviderModule();
|
|
1046
|
+
return customInputAt(cfgMod.loadConfig(), input, normalizeCustomProviderConfig, options);
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
function requireCustomKey({ apiKey, keyless, envName }) {
|
|
1050
|
+
if (!keyless && !envName && !apiKey) throw new Error('API key is required');
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
// One key transaction per provider, from reading the rollback key to the
|
|
1054
|
+
// config commit or rollback: a failed save restores the key the preceding
|
|
1055
|
+
// save committed and never one a newer save wrote. Settings-only saves and
|
|
1056
|
+
// other providers never wait.
|
|
1057
|
+
const customKeySaves = new Map();
|
|
1058
|
+
|
|
1059
|
+
function serializeCustomKeySave(id, run) {
|
|
1060
|
+
const result = (customKeySaves.get(id) || Promise.resolve()).then(run);
|
|
1061
|
+
const settled = result.then(
|
|
1062
|
+
() => {},
|
|
1063
|
+
() => {}
|
|
1064
|
+
);
|
|
1065
|
+
customKeySaves.set(id, settled);
|
|
1066
|
+
settled.then(() => {
|
|
1067
|
+
if (customKeySaves.get(id) === settled) customKeySaves.delete(id);
|
|
1068
|
+
});
|
|
1069
|
+
return result;
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1042
1072
|
export async function saveCustomProvider(cfgMod, rawInput) {
|
|
1043
1073
|
requireLocalProviderManagement();
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
apiKey,
|
|
1049
|
-
keyless,
|
|
1050
|
-
envName,
|
|
1051
|
-
} = await resolveCustomInput(cfgMod, rawInput, { requireModels: false });
|
|
1052
|
-
if (!keyless && !envName && !apiKey) throw new Error('API key is required');
|
|
1074
|
+
// Early verdict: an invalid save never touches the keychain.
|
|
1075
|
+
const resolved = await resolveCustomInput(cfgMod, rawInput, { requireModels: false });
|
|
1076
|
+
requireCustomKey(resolved);
|
|
1077
|
+
const { id: existingId, typed } = resolved;
|
|
1053
1078
|
const id = existingId || `custom-${randomUUID()}`;
|
|
1054
|
-
const
|
|
1055
|
-
const
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1079
|
+
const { normalizeCustomProviderConfig } = await loadCustomProviderModule();
|
|
1080
|
+
const commit = async () => {
|
|
1081
|
+
const account = SECRET_ACCOUNTS.agentApiKey(id);
|
|
1082
|
+
const previousKey = typed && existingId ? getAgentApiKey(id) : null;
|
|
1083
|
+
if (typed) await saveSecretAsync(account, typed);
|
|
1084
|
+
let entry;
|
|
1085
|
+
try {
|
|
1086
|
+
// Synchronous from here: omitted settings come from the config this
|
|
1087
|
+
// save commits over, so an edit that landed during the awaits above (a
|
|
1088
|
+
// model's visibility, a peer save, a removal) is never reverted.
|
|
1089
|
+
const current = cfgMod.loadConfig();
|
|
1090
|
+
const latest = customInputAt(current, rawInput, normalizeCustomProviderConfig, { requireModels: false });
|
|
1091
|
+
requireCustomKey(latest);
|
|
1092
|
+
entry = { ...latest.config, type: 'custom', enabled: latest.config.enabled !== false };
|
|
1093
|
+
cfgMod.saveConfig(
|
|
1094
|
+
{ ...current, providers: { ...(current.providers || {}), [id]: entry } },
|
|
1095
|
+
{ baseConfig: current }
|
|
1096
|
+
);
|
|
1097
|
+
} catch (error) {
|
|
1098
|
+
if (typed) {
|
|
1099
|
+
if (previousKey) await saveSecretAsync(account, previousKey);
|
|
1100
|
+
else await deleteSecretAsync(account);
|
|
1101
|
+
}
|
|
1102
|
+
throw error;
|
|
1068
1103
|
}
|
|
1069
|
-
|
|
1070
|
-
}
|
|
1071
|
-
return
|
|
1104
|
+
return customProviderRow(id, entry);
|
|
1105
|
+
};
|
|
1106
|
+
return typed ? serializeCustomKeySave(id, commit) : commit();
|
|
1072
1107
|
}
|
|
1073
1108
|
|
|
1074
1109
|
const MODEL_PATCH_KEYS = new Set(['manual', 'hidden', 'name', 'contextWindow', 'maxOutputTokens', 'reasoning']);
|
|
@@ -1104,7 +1139,7 @@ export async function updateCustomProviderModel(cfgMod, rawId, rawModelId, patch
|
|
|
1104
1139
|
// Everything below is synchronous: the provider is read, patched and saved
|
|
1105
1140
|
// without yielding, so a concurrent update can never be overwritten by a
|
|
1106
1141
|
// provider object read before the await above.
|
|
1107
|
-
const entry = existingCustomEntry(cfgMod, id);
|
|
1142
|
+
const entry = existingCustomEntry(cfgMod.loadConfig(), id);
|
|
1108
1143
|
const current = normalizeCustomProviderConfig(entry, { requireModels: false });
|
|
1109
1144
|
const overrides = new Map(Object.entries(current.modelOverrides || {}));
|
|
1110
1145
|
if (patch === null) {
|
|
@@ -1241,7 +1276,7 @@ function withoutProviderReferences(config, id) {
|
|
|
1241
1276
|
export async function removeCustomProvider(cfgMod, rawId) {
|
|
1242
1277
|
requireLocalProviderManagement();
|
|
1243
1278
|
const id = String(rawId || '').trim();
|
|
1244
|
-
existingCustomEntry(cfgMod, id);
|
|
1279
|
+
existingCustomEntry(cfgMod.loadConfig(), id);
|
|
1245
1280
|
// Secret first: a real keychain failure (locked, permission) leaves the
|
|
1246
1281
|
// provider registered so the removal can be retried. A missing secret
|
|
1247
1282
|
// (keyless provider) is not an error.
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// Who may manage automations (schedules and webhooks) from a chat, by the
|
|
2
|
+
// calling session's provenance. Enforced by every setup automation action
|
|
3
|
+
// (setup-tool/extended-actions.mjs), the chat-facing door of channel-admin:
|
|
4
|
+
// - the human's own sessions (an ordinary Lead session, Mixy's home) manage
|
|
5
|
+
// every automation and may move one to an agent that exists;
|
|
6
|
+
// - another agent's home thread manages only the automations it owns and can
|
|
7
|
+
// never give one away;
|
|
8
|
+
// - a headless run (schedule, webhook, delegated agent, or a session that
|
|
9
|
+
// cannot be resolved) manages none — nor does it change any other setting
|
|
10
|
+
// (setup-tool/executor.mjs refuses every mutating setup action for it).
|
|
11
|
+
// - a group-room member session is no background run, but it speaks for the
|
|
12
|
+
// room, not for the user: it creates or changes no agent, automation or
|
|
13
|
+
// other setting either.
|
|
14
|
+
// The app's own panels call channel-admin directly and act for the human.
|
|
15
|
+
import { MIXY_AGENT_ID, agentIdOfHomeSession } from '../../runtime/agent/orchestrator/session/agent-home.mjs';
|
|
16
|
+
import { roomMemberOfSession } from '../../runtime/agent/orchestrator/session/agent-room.mjs';
|
|
17
|
+
import { mixyDisplayName } from '../../runtime/agent/orchestrator/session/mixy-name.mjs';
|
|
18
|
+
import { isAgentOwner } from '../../runtime/agent/orchestrator/agent-owner.mjs';
|
|
19
|
+
|
|
20
|
+
const INTERACTIVE_SOURCE_TYPES = new Set(['', 'lead', 'cli']);
|
|
21
|
+
|
|
22
|
+
/** `{ kind: 'human' | 'agent' | 'room-member' | 'headless', agentId? }` of a session record. */
|
|
23
|
+
export function scheduleCallerScope(session) {
|
|
24
|
+
const id = String(session?.id || '');
|
|
25
|
+
const member = roomMemberOfSession(id);
|
|
26
|
+
if (member) return { kind: 'room-member', agentId: member.agentId };
|
|
27
|
+
const homeAgent = agentIdOfHomeSession(id);
|
|
28
|
+
if (homeAgent) return homeAgent === MIXY_AGENT_ID ? { kind: 'human' } : { kind: 'agent', agentId: homeAgent };
|
|
29
|
+
const headless =
|
|
30
|
+
!id ||
|
|
31
|
+
isAgentOwner(session) ||
|
|
32
|
+
Boolean(session.parentSessionId) ||
|
|
33
|
+
Boolean(session.ownerSessionId && session.ownerSessionId !== id) ||
|
|
34
|
+
!INTERACTIVE_SOURCE_TYPES.has(String(session.sourceType || '').toLowerCase());
|
|
35
|
+
return headless ? { kind: 'headless' } : { kind: 'human' };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Throws unless `scope` may `action` the `kind` ('schedule' | 'webhook')
|
|
39
|
+
* automation `current` (null when creating) with `owner` the requested owner
|
|
40
|
+
* ('' keeps the saved one). `agentIds` are the agents a transfer may name;
|
|
41
|
+
* Mixy always counts. */
|
|
42
|
+
export function assertAutomationAccess(
|
|
43
|
+
scope,
|
|
44
|
+
{ kind = 'schedule', action, current = null, owner = '', agentIds = [] }
|
|
45
|
+
) {
|
|
46
|
+
if (scope?.kind === 'room-member') throw new Error(`A group-room member cannot ${action} ${kind}s`);
|
|
47
|
+
if (scope?.kind !== 'human' && scope?.kind !== 'agent') {
|
|
48
|
+
throw new Error(`A background run cannot ${action} ${kind}s`);
|
|
49
|
+
}
|
|
50
|
+
const requested = String(owner ?? '').trim();
|
|
51
|
+
const saved = current ? current.owner || MIXY_AGENT_ID : '';
|
|
52
|
+
if (scope.kind === 'agent') {
|
|
53
|
+
if (current && saved !== scope.agentId) {
|
|
54
|
+
throw new Error(
|
|
55
|
+
`${kind === 'webhook' ? 'Webhook' : 'Schedule'} "${current.name}" belongs to another agent; this thread manages only its own ${kind}s`
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
if (requested && requested !== scope.agentId) throw new Error(`This thread cannot give a ${kind} to another agent`);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (requested && requested !== saved && requested !== MIXY_AGENT_ID && !agentIds.includes(requested)) {
|
|
62
|
+
throw new Error(`Agent "${requested}" does not exist`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Throws unless `caller` (scope `scope`) is Mixy — its home thread or an
|
|
67
|
+
* ordinary Lead session acting as 'lead': only Mixy creates, edits or
|
|
68
|
+
* deletes agent definitions from a chat. The app's own panels call the agent
|
|
69
|
+
* editor directly and act for the human. */
|
|
70
|
+
export function assertAgentEditorAccess(scope, caller, action) {
|
|
71
|
+
const acting = String(caller?.agent || '').trim();
|
|
72
|
+
if (scope?.kind === 'human' && (!acting || acting === MIXY_AGENT_ID)) return;
|
|
73
|
+
const name = mixyDisplayName();
|
|
74
|
+
throw new Error(
|
|
75
|
+
`Only ${name} (the representative agent) can create, edit or delete agents (setup ${action}); ask the user to do it with ${name} or in the Agents settings`
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Throws when an agent's home thread changes another agent's settings. */
|
|
80
|
+
export function assertOwnAgentSettings(scope, agentId, action) {
|
|
81
|
+
if (scope?.kind === 'agent' && String(agentId || '').trim() !== scope.agentId) {
|
|
82
|
+
throw new Error(`This thread manages only its own agent's settings (setup ${action})`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Throws when `scope` is a headless run: it reads settings, never changes them. */
|
|
87
|
+
export function assertInteractiveCaller(scope, action) {
|
|
88
|
+
if (scope?.kind === 'human' || scope?.kind === 'agent') return;
|
|
89
|
+
if (scope?.kind === 'room-member') throw new Error(`A group-room member cannot change settings (setup ${action})`);
|
|
90
|
+
throw new Error(`A background run cannot change settings (setup ${action})`);
|
|
91
|
+
}
|
|
@@ -2,11 +2,14 @@
|
|
|
2
2
|
// the current runtime state: route, tool spec, ownership/lane, workflow pack,
|
|
3
3
|
// memory context and compaction.
|
|
4
4
|
import { normalizeCompactionConfig } from '../../runtime/agent/orchestrator/runtime-core/config-helpers.mjs';
|
|
5
|
-
import { hasOwn } from '../../runtime/agent/orchestrator/runtime-core/session-text.mjs';
|
|
5
|
+
import { clean, hasOwn } from '../../runtime/agent/orchestrator/runtime-core/session-text.mjs';
|
|
6
6
|
import { toolSpecForMode } from '../../runtime/agent/orchestrator/runtime-core/effort.mjs';
|
|
7
|
-
import { pluginDataDir } from '../runtime-paths.mjs';
|
|
7
|
+
import { STANDALONE_ROOT, pluginDataDir } from '../runtime-paths.mjs';
|
|
8
|
+
import { readAgentFrontmatterPermission } from '../services/agent-tool/helpers.mjs';
|
|
8
9
|
import { LEAD_DISALLOWED_TOOLS } from '../../runtime/agent/orchestrator/runtime-core/tool-defs.mjs';
|
|
9
10
|
import { HEADLESS_TOOL_PROFILE } from '../../runtime/agent/orchestrator/runtime-core/tool-profile.mjs';
|
|
11
|
+
import { AGENT_MODE, resolveSessionMode } from '../../runtime/agent/orchestrator/session/agent-mode.mjs';
|
|
12
|
+
import { MIXY_AGENT_ID } from '../../runtime/agent/orchestrator/session/agent-home.mjs';
|
|
10
13
|
|
|
11
14
|
// Ownership and permission fields an agent-owned session inherits from its profile.
|
|
12
15
|
function agentOwnedSessionFields(sessionProfile) {
|
|
@@ -31,14 +34,30 @@ export function sessionOptions(deps, coreMemoryContext) {
|
|
|
31
34
|
// Load the active WORKFLOW.md pack once for both summary + context block.
|
|
32
35
|
const {
|
|
33
36
|
summary: workflow,
|
|
34
|
-
|
|
37
|
+
leadPromptParts,
|
|
35
38
|
packContext,
|
|
36
39
|
orchestrationMode,
|
|
37
40
|
} = activeWorkflowContext(rt.config, dataDir);
|
|
38
41
|
const sessionProfile = rt.sessionProfile && typeof rt.sessionProfile === 'object' ? rt.sessionProfile : null;
|
|
39
42
|
const agentOwned = sessionProfile?.owner === 'agent' || sessionProfile?.visibility === 'agent-only';
|
|
40
|
-
// Agent
|
|
41
|
-
const
|
|
43
|
+
// Agent or coding mode, from the one resolver (agent-mode.mjs).
|
|
44
|
+
const { mode: sessionMode, agentId: actingAgent } = resolveSessionMode({
|
|
45
|
+
sessionId: rt.reservedSessionId,
|
|
46
|
+
profile: sessionProfile,
|
|
47
|
+
});
|
|
48
|
+
// An agent's home thread: a user conversation that runs AS that agent (its
|
|
49
|
+
// AGENT.md role and permission); route comes with the create call.
|
|
50
|
+
const homeAgent = agentOwned ? '' : clean(sessionProfile?.homeAgent);
|
|
51
|
+
const homePermission = homeAgent ? readAgentFrontmatterPermission(homeAgent, dataDir, STANDALONE_ROOT) : null;
|
|
52
|
+
// A coding task session an agent created (session create): the user's own
|
|
53
|
+
// Lead, tagged with its origin; `sourceName` lets the Agents list file it.
|
|
54
|
+
const taskOrigin = agentOwned ? null : sessionProfile?.taskOrigin || null;
|
|
55
|
+
// Every session leads BP3 with the Active Workflow block; a Lead adds its
|
|
56
|
+
// orchestration and catalog slices (coding, agent mode or Mixy's).
|
|
57
|
+
const workflowContext = packContext;
|
|
58
|
+
const leadWorkflowParts = agentOwned
|
|
59
|
+
? undefined
|
|
60
|
+
: leadPromptParts({ agentMode: sessionMode === AGENT_MODE, mixy: actingAgent === MIXY_AGENT_ID });
|
|
42
61
|
const callerDisallowedTools = [
|
|
43
62
|
...(agentOwned ? [] : LEAD_DISALLOWED_TOOLS),
|
|
44
63
|
...(!agentOwned && rt.disallowDelegation ? ['agent'] : []),
|
|
@@ -51,7 +70,8 @@ export function sessionOptions(deps, coreMemoryContext) {
|
|
|
51
70
|
tools: toolSpecForMode(rt.mode),
|
|
52
71
|
...(Array.isArray(schemaAllowedTools) ? { schemaAllowedTools } : {}),
|
|
53
72
|
owner: agentOwned ? 'agent' : 'cli',
|
|
54
|
-
agent: agentOwned ? sessionProfile?.agent || 'worker' : 'lead',
|
|
73
|
+
agent: agentOwned ? sessionProfile?.agent || 'worker' : homeAgent || 'lead',
|
|
74
|
+
...(homePermission ? { permission: homePermission } : {}),
|
|
55
75
|
lane: agentOwned ? 'agent' : 'cli',
|
|
56
76
|
// One-shot `mixdog exec` runs carry their own source so session catalogs
|
|
57
77
|
// can keep them out of the user's resumable list.
|
|
@@ -60,7 +80,12 @@ export function sessionOptions(deps, coreMemoryContext) {
|
|
|
60
80
|
: rt.toolProfile === HEADLESS_TOOL_PROFILE
|
|
61
81
|
? 'exec'
|
|
62
82
|
: 'lead',
|
|
63
|
-
sourceName: agentOwned
|
|
83
|
+
sourceName: agentOwned
|
|
84
|
+
? sessionProfile?.sourceName || sessionProfile?.agent || 'agent'
|
|
85
|
+
: taskOrigin
|
|
86
|
+
? `agent:${taskOrigin.agentId}`
|
|
87
|
+
: 'main',
|
|
88
|
+
...(taskOrigin ? { taskOrigin } : {}),
|
|
64
89
|
...(rt.approvalMode ? { approvalMode: rt.approvalMode } : {}),
|
|
65
90
|
clientHostPid: sessionProfile?.clientHostPid || process.pid,
|
|
66
91
|
mcpScopeId: rt.mcpScopeId,
|
|
@@ -71,6 +96,8 @@ export function sessionOptions(deps, coreMemoryContext) {
|
|
|
71
96
|
coreMemoryContext,
|
|
72
97
|
workflow,
|
|
73
98
|
workflowContext,
|
|
99
|
+
...(leadWorkflowParts ? { leadWorkflowParts } : {}),
|
|
100
|
+
sessionMode,
|
|
74
101
|
orchestrationMode,
|
|
75
102
|
fast: rt.route.fast === true,
|
|
76
103
|
autoEffort: rt.route.autoEffort !== false,
|