mixdog 1.3.2 → 1.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +7 -7
- package/scripts/verify-runtime-dependency-versions.mjs +158 -0
- package/src/agents/reviewer/AGENT.md +5 -7
- package/src/agents/reviewer/agent.json +1 -1
- package/src/defaults/shipped-history.json +4 -0
- package/src/defaults/skills/browser-use/SKILL.md +6 -3
- package/src/defaults/skills/local-provider/SKILL.md +33 -6
- package/src/rules/lead/LEAD.md +5 -8
- package/src/rules/shared/45-destructive.md +5 -1
- package/src/rules/shared/60-verification.md +6 -4
- package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +4 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +38 -11
- package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +15 -3
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +17 -4
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +14 -1
- package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +14 -1
- package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/registry.mjs +10 -0
- package/src/runtime/agent/orchestrator/providers/wire-gateway.mjs +13 -1
- package/src/runtime/agent/orchestrator/runtime-core/goal-runtime.mjs +5 -0
- package/src/runtime/agent/orchestrator/runtime-core/goal-turns.mjs +10 -1
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +40 -5
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +32 -11
- package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +6 -3
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/agent-room.d.mts +13 -0
- package/src/runtime/agent/orchestrator/session/agent-room.mjs +68 -12
- package/src/runtime/agent/orchestrator/session/compact/continuation.mjs +15 -6
- package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +31 -8
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +44 -0
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +117 -19
- package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/human-gate/delegation.mjs +147 -137
- package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +37 -74
- package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +0 -2
- package/src/runtime/agent/orchestrator/session/human-gate/run-policy.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +13 -7
- package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +5 -0
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/continuation.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/empty-nudge.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/output-limit.mjs +62 -13
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +22 -6
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +7 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +12 -20
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/call-prep.mjs +5 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +11 -5
- package/src/runtime/agent/orchestrator/session/manager/agent-listing-notice.mjs +102 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +13 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +24 -15
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +8 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-steering-drain.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/manager/idle-cleanup.mjs +32 -1
- package/src/runtime/agent/orchestrator/session/manager/orchestration-mode-switch-notice.mjs +17 -1
- package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +36 -0
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +28 -9
- package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +7 -5
- package/src/runtime/agent/orchestrator/session/manager/session-tool-surface.mjs +7 -3
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +10 -1
- package/src/runtime/agent/orchestrator/session/routine-result.mjs +7 -4
- package/src/runtime/agent/orchestrator/session/runtime-user-context.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +11 -2
- package/src/runtime/agent/orchestrator/session/store/sweep/stale-sweep.mjs +5 -4
- package/src/runtime/agent/orchestrator/session/store/sweep/sub-agent-retention.mjs +140 -0
- package/src/runtime/agent/orchestrator/session/store/sweep/sweep-plan.mjs +5 -0
- package/src/runtime/agent/orchestrator/session/store/sweep/sweep-record.mjs +7 -2
- package/src/runtime/agent/orchestrator/session/store/sweep/sweep-row.mjs +13 -0
- package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +76 -0
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +33 -1
- package/src/runtime/agent/orchestrator/session/store-summary-visibility.d.mts +5 -0
- package/src/runtime/agent/orchestrator/session/store-summary-visibility.mjs +37 -0
- package/src/runtime/agent/orchestrator/session/store.mjs +1 -0
- package/src/runtime/agent/orchestrator/tools/builtin/advisory-lock.mjs +18 -7
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +2 -0
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +127 -20
- package/src/runtime/agent/orchestrator/tools/builtin/git-worktree-tool.mjs +557 -0
- package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +6 -0
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +8 -2
- package/src/runtime/attachments/legacy-office.mjs +110 -0
- package/src/runtime/attachments/prompt-restore.mjs +70 -0
- package/src/runtime/attachments/store.mjs +60 -3
- package/src/runtime/effort-judge/judge-client.mjs +61 -14
- package/src/runtime/effort-judge/model-install.mjs +34 -2
- package/src/runtime/memory/lib/memory-chunk-quality.mjs +14 -2
- package/src/runtime/office/portable/portable-soffice.mjs +132 -30
- package/src/runtime/shared/agent-avatar-sprites.mjs +23 -8
- package/src/runtime/shared/agent-avatar.mjs +3 -2
- package/src/runtime/shared/agent-room-host.d.mts +31 -0
- package/src/runtime/shared/agent-room-host.mjs +58 -0
- package/src/runtime/shared/bot-hops.mjs +16 -11
- package/src/runtime/shared/redact-directories.mjs +18 -0
- package/src/runtime/shared/remove-directory.mjs +28 -0
- package/src/runtime/shared/session-sidebar-host.d.mts +11 -2
- package/src/runtime/shared/session-sidebar-host.mjs +16 -3
- package/src/runtime/shared/tool-execution-contract.mjs +4 -0
- package/src/runtime/shared/tool-work-units.mjs +12 -9
- package/src/runtime/shared/turn-snapshot.mjs +233 -122
- package/src/runtime/shared/user-data-guard.mjs +51 -19
- package/src/runtime/shared/worktrees/activity-gate.mjs +209 -227
- package/src/runtime/shared/worktrees/contract.mjs +11 -3
- package/src/runtime/shared/worktrees/maintenance.mjs +11 -2
- package/src/runtime/shared/worktrees/manager.mjs +419 -52
- package/src/runtime/shared/worktrees/registry.mjs +3 -1
- package/src/runtime/shared/worktrees/tool-host.mjs +39 -0
- package/src/runtime/tidy/process.mjs +41 -8
- package/src/runtime/tidy/resolve.mjs +10 -7
- package/src/session-runtime/agent-home-delivery.mjs +73 -91
- package/src/session-runtime/agent-profile-changes.mjs +16 -0
- package/src/session-runtime/agent-room-delivery.mjs +2 -63
- package/src/session-runtime/agent-task-sessions.mjs +194 -64
- package/src/session-runtime/boot/apis.mjs +3 -0
- package/src/session-runtime/boot/facade.mjs +5 -6
- package/src/session-runtime/boot/tools.mjs +21 -1
- package/src/session-runtime/held-requests.mjs +20 -25
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +5 -22
- package/src/session-runtime/model-recency.mjs +1 -0
- package/src/session-runtime/provider-models/row-hydration.mjs +8 -1
- package/src/session-runtime/quick-model-rows/model-meta.mjs +2 -0
- package/src/session-runtime/quick-model-rows/provider-rows.mjs +7 -1
- package/src/session-runtime/runtime-review-api.mjs +4 -1
- package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +34 -19
- package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +3 -0
- package/src/session-runtime/services/provider-admin.mjs +12 -2
- package/src/session-runtime/services/worktree-access.mjs +27 -0
- package/src/session-runtime/session-hooks.mjs +9 -17
- package/src/session-runtime/settings-builtin-tools-api.mjs +2 -0
- package/src/session-runtime/setup-tool/executor.mjs +4 -0
- package/src/session-runtime/setup-tool/settings-contract.mjs +3 -0
- package/src/session-runtime/tool-policy/agent-profile-fold.mjs +98 -14
- package/src/session-runtime/tool-policy/policy-stage.mjs +23 -5
- package/src/session-runtime/tool-policy-refresh.mjs +1 -0
- package/src/session-runtime/tool-surface.mjs +9 -5
- package/src/session-runtime/turn/session-ops.mjs +8 -0
- package/src/session-runtime/turn/turn-run.mjs +5 -1
- package/src/session-runtime/workflow-agents-api/agent-editor.mjs +10 -2
- package/src/session-runtime/workflow-agents-api/agent-route.mjs +39 -17
- package/src/session-runtime/workspace/transition-steps.mjs +1 -18
- package/src/session-runtime/workspace/transitions.mjs +279 -67
- package/src/session-runtime/workspace/worktree-tool.mjs +194 -0
- package/src/standalone/daemon-lag-profiler.mjs +3 -1
- package/src/standalone/daemon-session-control.mjs +140 -23
- package/src/standalone/daemon-telemetry.mjs +9 -0
- package/src/standalone/daemon.mjs +5 -0
- package/src/standalone/local-session-runtime.mjs +11 -2
- package/src/standalone/session-protocol.mjs +1 -0
- package/src/standalone/session-runtime-inline-host/boot-loaders.mjs +2 -0
- package/src/standalone/session-service/agent-route-follow.mjs +69 -0
- package/src/standalone/session-service/session-calls/session-turns.mjs +33 -4
- package/src/standalone/session-service.mjs +10 -0
- package/src/standalone/session-tool-output.mjs +1 -0
- package/src/standalone/staged-import.mjs +47 -0
- package/src/tui/App.jsx +2 -0
- package/src/tui/app/message-selector.mjs +15 -2
- package/src/tui/app/model-options.mjs +16 -2
- package/src/tui/app/model-picker.mjs +2 -1
- package/src/tui/app/route-pickers.mjs +3 -1
- package/src/tui/dist/index.mjs +92 -40
- package/src/tui/paste-attachments.mjs +0 -1
- package/src/tui/paste-text-policy.mjs +4 -5
- package/src/tui/session/agent-job-feed/execution-delivery.mjs +4 -1
- package/src/tui/session/notice-surface.mjs +3 -0
- package/src/tui/session/session-api/intake/abort.mjs +33 -17
- package/src/tui/session/session-api/intake/rewind.mjs +39 -4
- package/src/tui/session/session-api/routes-agent-definitions.mjs +1 -0
- package/src/tui/session/tool-approval.mjs +11 -48
- package/src/session-runtime/room-member-gate.mjs +0 -61
|
@@ -5,10 +5,12 @@ import { getModelMetadataSync } from '../../runtime/agent/orchestrator/providers
|
|
|
5
5
|
import { isCustomProviderName } from '../../runtime/agent/orchestrator/providers/custom-reasoning.mjs';
|
|
6
6
|
|
|
7
7
|
const CUSTOM_DECLARED_FIELDS = [
|
|
8
|
+
'supportsVision',
|
|
8
9
|
'supportsReasoning',
|
|
9
10
|
'reasoningOptions',
|
|
10
11
|
'reasoningLevels',
|
|
11
12
|
'reasoningContentField',
|
|
13
|
+
'providerName',
|
|
12
14
|
'hidden',
|
|
13
15
|
'manual',
|
|
14
16
|
'customReasoning',
|
|
@@ -22,7 +22,10 @@ function customProviderModels(provider, providerConfig, discovered = providerCac
|
|
|
22
22
|
} catch {
|
|
23
23
|
return [];
|
|
24
24
|
}
|
|
25
|
-
|
|
25
|
+
const providerName = clean(config.name);
|
|
26
|
+
return effectiveCustomModelRows(config, discovered, { providerId: provider }).map((row) =>
|
|
27
|
+
providerName ? { ...row, providerName } : row
|
|
28
|
+
);
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
export function createQuickProviderRows({
|
|
@@ -53,6 +56,8 @@ export function createQuickProviderRows({
|
|
|
53
56
|
display: model.display || model.name || modelId,
|
|
54
57
|
contextWindow: model.contextWindow || meta.contextWindow || null,
|
|
55
58
|
outputTokens: model.outputTokens || meta.outputTokens || null,
|
|
59
|
+
// An explicit value (an override's false included) wins; the catalog fills the rest.
|
|
60
|
+
supportsVision: model.supportsVision ?? meta.supportsVision,
|
|
56
61
|
supportsReasoning: model.supportsReasoning === true || meta.supportsReasoning === true,
|
|
57
62
|
supportsFunctionCalling: model.supportsFunctionCalling === true || meta.supportsFunctionCalling === true,
|
|
58
63
|
supportsPromptCaching: model.supportsPromptCaching === true || meta.supportsPromptCaching === true,
|
|
@@ -78,6 +83,7 @@ export function createQuickProviderRows({
|
|
|
78
83
|
contextWindow: meta.contextWindow || null,
|
|
79
84
|
outputTokens: meta.outputTokens || null,
|
|
80
85
|
latest: routeLike.latest === true,
|
|
86
|
+
supportsVision: meta.supportsVision,
|
|
81
87
|
supportsReasoning: !!routeLike.effort || meta.supportsReasoning === true,
|
|
82
88
|
supportsFunctionCalling: meta.supportsFunctionCalling === true,
|
|
83
89
|
supportsPromptCaching: meta.supportsPromptCaching === true,
|
|
@@ -2,6 +2,7 @@ import { registerCacheInvalidationListener } from '../runtime/agent/orchestrator
|
|
|
2
2
|
import { turnReviewReply } from '../runtime/shared/turn-review-reply.mjs';
|
|
3
3
|
import {
|
|
4
4
|
clearSessionChangeHistory,
|
|
5
|
+
forgetTurnSnapshotSession,
|
|
5
6
|
getSessionReviewDiff,
|
|
6
7
|
getSessionReviewFilePatch,
|
|
7
8
|
getTurnChanges,
|
|
@@ -19,8 +20,10 @@ import { registerSessionPurgeHook } from '../runtime/agent/orchestrator/session/
|
|
|
19
20
|
|
|
20
21
|
// A deleted history stays deleted for as long as its session exists; the
|
|
21
22
|
// session's own deletion takes the marker — and its stored first-seen file
|
|
22
|
-
// contents — with it
|
|
23
|
+
// contents — with it, and its in-memory turn, parked worker reviews and
|
|
24
|
+
// bound worker trackers.
|
|
23
25
|
registerSessionPurgeHook((sessionId) => {
|
|
26
|
+
forgetTurnSnapshotSession(sessionId);
|
|
24
27
|
void forgetSessionChangeHistoryMarker(sessionId);
|
|
25
28
|
void forgetSessionBaselines(sessionId);
|
|
26
29
|
});
|
|
@@ -25,11 +25,16 @@ export function isActiveWorkerRow(row = {}) {
|
|
|
25
25
|
return !isTerminalWorkerStatus(effective);
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
function
|
|
28
|
+
function rowState(session, runtime) {
|
|
29
29
|
const status = session.closed === true ? 'closed' : session.status || 'idle';
|
|
30
30
|
const stage =
|
|
31
31
|
session.stage ||
|
|
32
32
|
(status === 'idle' || status === 'error' || status === 'closed' ? status : runtime?.stage || status);
|
|
33
|
+
return { status, stage };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function agentRow({ tag, session, runtime, progress, now, indexRow = null }) {
|
|
37
|
+
const { status, stage } = rowState(session, runtime);
|
|
33
38
|
// Additive: the current turn's start (worker index, stamped at dispatch) so
|
|
34
39
|
// surfaces time a continued worker from its turn, not from createdAt.
|
|
35
40
|
const turnStartedAt = indexRow?.turnStartedAt || session.turnStartedAt || null;
|
|
@@ -76,24 +81,34 @@ export function createAgentList({ mgr, refreshTagsFromSessions, agentSessionEntr
|
|
|
76
81
|
return function list({ scanSessions = false, context = {}, includeTerminal = false } = {}) {
|
|
77
82
|
refreshTagsFromSessions({ scanSessions, context });
|
|
78
83
|
const now = Date.now();
|
|
79
|
-
const rows =
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
84
|
+
const rows = [];
|
|
85
|
+
for (const { tag, session, indexRow, loaded } of agentSessionEntries({ scanSessions, context })) {
|
|
86
|
+
const runtime = mgr.getSessionRuntime?.(session.id);
|
|
87
|
+
// Whether a row is listed depends on its status and stage alone, so a
|
|
88
|
+
// finished worker is dropped before its progress is read: that read
|
|
89
|
+
// loads the worker's stored session, and a Lead's status pulse (every
|
|
90
|
+
// 2 s) would otherwise read every worker it ever ran, whole transcripts
|
|
91
|
+
// on first sight, synchronously on the daemon loop.
|
|
92
|
+
if (!includeTerminal && !isActiveWorkerRow(rowState(session, runtime))) continue;
|
|
93
|
+
rows.push(
|
|
94
|
+
agentRow({
|
|
95
|
+
tag,
|
|
96
|
+
session,
|
|
97
|
+
indexRow,
|
|
98
|
+
runtime,
|
|
99
|
+
// A tag-bound entry is the getSession answer itself; an index row or
|
|
100
|
+
// a scanned summary is only a projection and still reads the session.
|
|
101
|
+
progress: progress.sessionProgressExtras(
|
|
102
|
+
session.id,
|
|
103
|
+
session.agent || null,
|
|
104
|
+
now,
|
|
105
|
+
null,
|
|
106
|
+
loaded ? { loaded: session } : {}
|
|
107
|
+
),
|
|
90
108
|
now,
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
})
|
|
96
|
-
);
|
|
97
|
-
return includeTerminal ? rows : rows.filter((row) => isActiveWorkerRow(row));
|
|
109
|
+
})
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
return rows;
|
|
98
113
|
};
|
|
99
114
|
}
|
|
@@ -9,6 +9,7 @@ import { resourceAdmission } from '../../../../runtime/shared/resource-admission
|
|
|
9
9
|
import { clean, terminalPidForContext } from '../helpers.mjs';
|
|
10
10
|
import { renderResult } from '../render.mjs';
|
|
11
11
|
import { closeManagedSession } from '../../../lifecycle/shared.mjs';
|
|
12
|
+
import { hopOf } from '../../../../runtime/shared/bot-hops.mjs';
|
|
12
13
|
|
|
13
14
|
/** The completion callback that hands a worker's result back to its caller session; null without a handoff. */
|
|
14
15
|
function handoffNotifier(handoffSessionId, notifySessionCompletion) {
|
|
@@ -30,6 +31,8 @@ export function createJobStarter({ mgr, notifyStatusChange, notifySessionComplet
|
|
|
30
31
|
ownerSessionId: ownerSessionId || null,
|
|
31
32
|
clientHostPid: clientHostPid || null,
|
|
32
33
|
notifyFn: handoffNotifier(handoffSessionId, notifySessionCompletion),
|
|
34
|
+
// The turn that started the job: its completion returns at that bot-chain hop (bot-hops.mjs).
|
|
35
|
+
...(hopOf(notifyContext?.hop) ? { hop: notifyContext.hop } : {}),
|
|
33
36
|
};
|
|
34
37
|
const jobMeta = sanitizeTaskMeta({
|
|
35
38
|
...(meta || {}),
|
|
@@ -442,6 +442,7 @@ function publicCustomModelRows(rows) {
|
|
|
442
442
|
model.hidden = item.hidden === true;
|
|
443
443
|
model.manual = item.manual === true;
|
|
444
444
|
for (const key of [
|
|
445
|
+
'supportsVision',
|
|
445
446
|
'supportsReasoning',
|
|
446
447
|
'reasoningOptions',
|
|
447
448
|
'reasoningLevels',
|
|
@@ -588,7 +589,8 @@ async function modelProxyProviderSetup(options) {
|
|
|
588
589
|
});
|
|
589
590
|
const custom = Object.entries(providers)
|
|
590
591
|
.filter(([id, entry]) => isCustomProviderId(id) && entry?.type === 'custom')
|
|
591
|
-
|
|
592
|
+
// Older desktops advertise no name: the id is the fallback.
|
|
593
|
+
.map(([id, entry]) => ({ id, name: (typeof entry.name === 'string' && entry.name.trim()) || id }));
|
|
592
594
|
return {
|
|
593
595
|
managedByClient: true,
|
|
594
596
|
api: [...project(API_PROVIDERS, 'api', 'api-key'), ...project(custom, 'api', 'custom')],
|
|
@@ -1111,7 +1113,15 @@ export async function saveCustomProvider(cfgMod, rawInput) {
|
|
|
1111
1113
|
return typed ? serializeCustomKeySave(id, commit) : commit();
|
|
1112
1114
|
}
|
|
1113
1115
|
|
|
1114
|
-
const MODEL_PATCH_KEYS = new Set([
|
|
1116
|
+
const MODEL_PATCH_KEYS = new Set([
|
|
1117
|
+
'manual',
|
|
1118
|
+
'hidden',
|
|
1119
|
+
'name',
|
|
1120
|
+
'contextWindow',
|
|
1121
|
+
'maxOutputTokens',
|
|
1122
|
+
'supportsVision',
|
|
1123
|
+
'reasoning',
|
|
1124
|
+
]);
|
|
1115
1125
|
const REASONING_PATCH_KEYS = new Set(['levels', 'wireFormat', 'wireValues', 'replay']);
|
|
1116
1126
|
|
|
1117
1127
|
// Tri-state merge: undefined preserves, null deletes the property, a value replaces it.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// Who may change worktrees from a chat (the git tool's managed-worktree
|
|
2
|
+
// operations, the daemon's cross-session `worktree` control). A worktree
|
|
3
|
+
// mutation — entering, returning, merging, removing, pruning, `git worktree
|
|
4
|
+
// add/remove`, a snapshot — runs only in a conversation session: a Lead
|
|
5
|
+
// session of the desktop or TUI, or a bot's chat. Unattended runs (schedules,
|
|
6
|
+
// webhooks, routines, room seats) are refused by the run's trusted identity;
|
|
7
|
+
// delegated workers have no worktree host at all. Listing stays open.
|
|
8
|
+
//
|
|
9
|
+
// The scope is the session-level caller scope (schedule-access.mjs
|
|
10
|
+
// scheduleCallerScope: the session's provenance and its unattended-run mark),
|
|
11
|
+
// never anything the model or a message supplies.
|
|
12
|
+
import { scheduleCallerScope } from './schedule-access.mjs';
|
|
13
|
+
|
|
14
|
+
/** `{ kind, agentId?, unattended?, attended }` of a calling session record. */
|
|
15
|
+
export function worktreeCallerScope(session) {
|
|
16
|
+
const scope = scheduleCallerScope(session);
|
|
17
|
+
const attended = (scope.kind === 'human' || scope.kind === 'agent') && scope.unattended !== true;
|
|
18
|
+
return { ...scope, attended };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Throws unless `scope` (worktreeCallerScope) may change worktrees; `what` names the refused action. */
|
|
22
|
+
export function assertWorktreeMutationAllowed(scope, what) {
|
|
23
|
+
if (scope?.attended === true) return;
|
|
24
|
+
throw new Error(
|
|
25
|
+
`${what} changes worktrees only in a conversation session (a Lead session or a bot chat); schedule, webhook, routine and room runs may only list them`
|
|
26
|
+
);
|
|
27
|
+
}
|
|
@@ -5,9 +5,6 @@
|
|
|
5
5
|
// facade's hookCommonPayload / currentCwd accessor + the session itself. All
|
|
6
6
|
// properties are non-enumerable/configurable/writable so the loop/manager can
|
|
7
7
|
// read them by name without changing session enumeration.
|
|
8
|
-
import { roomMemberOfSession } from '../runtime/agent/orchestrator/session/agent-room.mjs';
|
|
9
|
-
import { roomMemberToolDecision } from './room-member-gate.mjs';
|
|
10
|
-
|
|
11
8
|
export function attachSessionHooks(session, { hooks, hookCommonPayload, getCwd }) {
|
|
12
9
|
const identity = (input) => ({
|
|
13
10
|
session_id: input?.sessionId || input?.session_id || session?.id,
|
|
@@ -28,26 +25,21 @@ export function attachSessionHooks(session, { hooks, hookCommonPayload, getCwd }
|
|
|
28
25
|
// rejection cannot escape the tool loop or the compaction flow.
|
|
29
26
|
const dispatcher = (event, payload) => (input) => hooks.dispatch(event, hookCommonPayload(payload(input)));
|
|
30
27
|
|
|
31
|
-
// PreToolUse bridge. A group-room member
|
|
32
|
-
//
|
|
33
|
-
define('beforeToolHook', async (input, options = {}) =>
|
|
34
|
-
|
|
35
|
-
const args = input?.args || input?.tool_input;
|
|
36
|
-
const cwd = input?.cwd || getCwd();
|
|
37
|
-
const decision = await hooks.beforeTool(
|
|
28
|
+
// PreToolUse bridge. A group-room member follows the same rules as its
|
|
29
|
+
// agent's home thread: no room-only approval on top.
|
|
30
|
+
define('beforeToolHook', async (input, options = {}) =>
|
|
31
|
+
hooks.beforeTool(
|
|
38
32
|
hookCommonPayload({
|
|
39
33
|
...input,
|
|
40
34
|
session_id: input?.sessionId || input?.session_id || session?.id,
|
|
41
|
-
tool_name: name,
|
|
42
|
-
tool_input: args,
|
|
35
|
+
tool_name: input?.name || input?.tool_name,
|
|
36
|
+
tool_input: input?.args || input?.tool_input,
|
|
43
37
|
tool_use_id: input?.toolCallId || input?.tool_use_id,
|
|
44
|
-
cwd,
|
|
38
|
+
cwd: input?.cwd || getCwd(),
|
|
45
39
|
}),
|
|
46
40
|
options
|
|
47
|
-
)
|
|
48
|
-
|
|
49
|
-
return roomMemberToolDecision({ name, args, cwd }, decision);
|
|
50
|
-
});
|
|
41
|
+
)
|
|
42
|
+
);
|
|
51
43
|
// PostToolUse: bridge runtime tool completions to the standard hook bus.
|
|
52
44
|
define('afterToolHook', dispatcher('PostToolUse', toolPayload));
|
|
53
45
|
// PostToolUseFailure: dispatched by loop.mjs only when a tool execution
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
effortJudgeAvailable,
|
|
20
20
|
effortJudgeInfo,
|
|
21
21
|
effortJudgeInstalling,
|
|
22
|
+
effortJudgeLastError,
|
|
22
23
|
effortJudgeReady,
|
|
23
24
|
shutdownEffortJudge,
|
|
24
25
|
} from '../runtime/effort-judge/judge-client.mjs';
|
|
@@ -178,6 +179,7 @@ export function createBuiltinToolSettings(
|
|
|
178
179
|
installed: effortJudgeAvailable(),
|
|
179
180
|
installing: effortJudgeInstalling(),
|
|
180
181
|
ready: effortJudgeReady(),
|
|
182
|
+
lastError: effortJudgeLastError(),
|
|
181
183
|
info: effortJudgeInfo(),
|
|
182
184
|
},
|
|
183
185
|
};
|
|
@@ -38,6 +38,10 @@ const PROFILE_VALUE_IDS = Object.freeze({
|
|
|
38
38
|
});
|
|
39
39
|
|
|
40
40
|
const ACTION_APPLIES_TO = {
|
|
41
|
+
set_agent_route:
|
|
42
|
+
"the bot's chats from their next turn (a running turn finishes on the old model); an agent switched on or off reaches open conversations from their next turn as an agent list reminder",
|
|
43
|
+
set_orchestration_mode:
|
|
44
|
+
"new sessions and an empty current one (the default mode); a conversation keeps its own mode until it is switched with the composer's mode selector",
|
|
41
45
|
set_recap_enabled: 'background Memory cycles; no restart required',
|
|
42
46
|
set_auto_update: 'subsequent automatic update checks',
|
|
43
47
|
set_worktree_settings: 'new tasks and worktrees created afterwards; existing worktrees are unchanged',
|
|
@@ -223,6 +223,9 @@ export const SETUP_EXTENDED_PROPERTIES = Object.freeze({
|
|
|
223
223
|
turnNotifications: boolean,
|
|
224
224
|
usagePinned: boolean,
|
|
225
225
|
usageRailShortLong: boolean,
|
|
226
|
+
usageAccountsExpanded: boolean,
|
|
227
|
+
usageRailSlotsThree: boolean,
|
|
228
|
+
usageRailSlotsAll: boolean,
|
|
226
229
|
computerObserveOnly: boolean,
|
|
227
230
|
browserAutoSignIn: boolean,
|
|
228
231
|
notificationVolume: {
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
// changes (a switched workflow or mode, moved tool gates, changed shipped
|
|
3
3
|
// rules) in where the provider cache is cold anyway.
|
|
4
4
|
import { sessionHasConversationMessages } from '../../runtime/agent/orchestrator/runtime-core/session-text.mjs';
|
|
5
|
+
import { ORCHESTRATION_MODES } from '../../runtime/shared/orchestration.mjs';
|
|
6
|
+
import { orchestrationModeFromPrompt } from '../../runtime/agent/orchestrator/session/manager/orchestration-mode-switch-notice.mjs';
|
|
5
7
|
import { promptRulesRevision } from '../../runtime/agent/orchestrator/session/manager/rules-cache.mjs';
|
|
6
8
|
import { isAgentOwner } from '../../runtime/agent/orchestrator/agent-owner.mjs';
|
|
7
9
|
import { AGENT_MODE, sessionModeOf } from '../../runtime/agent/orchestrator/session/agent-mode.mjs';
|
|
@@ -15,6 +17,7 @@ import {
|
|
|
15
17
|
agentMcpToolPermission,
|
|
16
18
|
connectorPolicyAgentOf,
|
|
17
19
|
} from '../../runtime/agent/orchestrator/mcp/agent-connectors.mjs';
|
|
20
|
+
import { agentListEntries } from '../../runtime/agent/orchestrator/session/manager/prompt-utils.mjs';
|
|
18
21
|
import { markToolSurfaceMigrated } from '../../runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs';
|
|
19
22
|
import {
|
|
20
23
|
agentProfileRevision,
|
|
@@ -50,6 +53,27 @@ const promptPrefix = (session) =>
|
|
|
50
53
|
const rulesStale = (session) =>
|
|
51
54
|
sessionModeOf(session) === AGENT_MODE && session.rulesRevision !== promptRulesRevision();
|
|
52
55
|
|
|
56
|
+
// Before session-local modes were used by profile refreshes, changing the
|
|
57
|
+
// roster could replace a coding Lead's mode rules with the configured default
|
|
58
|
+
// while leaving its selected mode intact. This is not a normal mode overlay:
|
|
59
|
+
// repair only a known generated mismatch, with no user switch still pending.
|
|
60
|
+
function orchestrationRecoveryNeeded(session) {
|
|
61
|
+
if (
|
|
62
|
+
!session?.promptIdentity ||
|
|
63
|
+
session.promptIdentity.skipAgentRules ||
|
|
64
|
+
isAgentOwner(session) ||
|
|
65
|
+
sessionModeOf(session) === AGENT_MODE ||
|
|
66
|
+
!ORCHESTRATION_MODES.includes(session.orchestrationMode) ||
|
|
67
|
+
session.workflowOverlay ||
|
|
68
|
+
session.orchestrationModeOverlay ||
|
|
69
|
+
session.pendingWorkflowId !== undefined ||
|
|
70
|
+
session.pendingOrchestrationMode !== undefined
|
|
71
|
+
)
|
|
72
|
+
return false;
|
|
73
|
+
const presented = orchestrationModeFromPrompt(session);
|
|
74
|
+
return presented !== null && presented !== session.orchestrationMode;
|
|
75
|
+
}
|
|
76
|
+
|
|
53
77
|
// Changes the prompt does not carry yet: a workflow switched mid-conversation,
|
|
54
78
|
// a bot's tool gates that moved, or shipped rules a release changed.
|
|
55
79
|
const foldPending = (session) =>
|
|
@@ -130,6 +154,35 @@ function stageLiveToolChanges({ liveDeniedTools, modelStandaloneTools }, session
|
|
|
130
154
|
return true;
|
|
131
155
|
}
|
|
132
156
|
|
|
157
|
+
// A coding Lead keeps `agent` whichever agents exist (workflow.mjs
|
|
158
|
+
// workflowDisallowsAgentTool); a denial a session recorded under the older
|
|
159
|
+
// rule, because no agent was enabled, is lifted as the live policy no longer
|
|
160
|
+
// holds it: the tool becomes reachable by deferred loading and the next
|
|
161
|
+
// turn's deferred_tools_delta names it. Nothing in the request prefix moves.
|
|
162
|
+
// Returns true when the session changed.
|
|
163
|
+
function liftAgentDenial({ liveDeniedTools, modelStandaloneTools }, session) {
|
|
164
|
+
const disallowed = (session.disallowedTools || []).map(String);
|
|
165
|
+
const overlayDenied = (session.workflowOverlay?.deniedTools || []).includes('agent');
|
|
166
|
+
if (!disallowed.includes('agent') && !overlayDenied) return false;
|
|
167
|
+
if (liveDeniedTools(session, session.workflow).includes('agent')) return false;
|
|
168
|
+
const known = new Map(schemaTools(session).map((tool) => [toolName(tool), tool]));
|
|
169
|
+
if (!known.has('agent')) {
|
|
170
|
+
const fresh = modelStandaloneTools().filter((tool) => toolName(tool) === 'agent');
|
|
171
|
+
if (!fresh.length) return false;
|
|
172
|
+
session.deferredToolCatalog = [...(session.deferredToolCatalog || []), ...fresh];
|
|
173
|
+
known.set('agent', fresh[0]);
|
|
174
|
+
}
|
|
175
|
+
session.disallowedTools = disallowed.filter((name) => name !== 'agent');
|
|
176
|
+
if (overlayDenied) {
|
|
177
|
+
session.workflowOverlay.deniedTools = session.workflowOverlay.deniedTools.filter((name) => name !== 'agent');
|
|
178
|
+
}
|
|
179
|
+
mergePendingDeferredToolDelta(session, {
|
|
180
|
+
added: [{ name: 'agent', description: known.get('agent')?.description || '' }],
|
|
181
|
+
});
|
|
182
|
+
session.updatedAt = Date.now();
|
|
183
|
+
return true;
|
|
184
|
+
}
|
|
185
|
+
|
|
133
186
|
export function createAgentProfileFold({
|
|
134
187
|
getSession,
|
|
135
188
|
getConfig,
|
|
@@ -189,9 +242,11 @@ export function createAgentProfileFold({
|
|
|
189
242
|
* anything it embeds changed; a delegated worker keeps its prompt. The core
|
|
190
243
|
* is rebuilt like a workflow switch's (the session's own workflow, the
|
|
191
244
|
* workspace slices the model was told) and replaces the stored one, so the
|
|
192
|
-
* next turn sends it with one provider cache break
|
|
193
|
-
* turn
|
|
194
|
-
*
|
|
245
|
+
* next turn sends it with one provider cache break. The prompt is not re-embedded
|
|
246
|
+
* per turn, but currentAgents() does resolve the agent catalog at each
|
|
247
|
+
* turn's preparation (for the listing reminder). That rewrite is immediate
|
|
248
|
+
* (one tagged cache break) and takes every pending fold with it
|
|
249
|
+
* (foldPrompt), moving all stamps.
|
|
195
250
|
* What is not an edit never touches the prefix: a bot thread's feature gates
|
|
196
251
|
* and connectors are staged live (stageLiveToolChanges), and shipped rules a
|
|
197
252
|
* release changed wait silently; both are folded in at the next cold point
|
|
@@ -210,22 +265,29 @@ export function createAgentProfileFold({
|
|
|
210
265
|
const workflowConfig = configForSession(getConfig(), promptWorkflowOf(session)?.id, promptMode(session));
|
|
211
266
|
const { leadPromptParts, summary: workflow } = activeWorkflowContext(workflowConfig, getDataDir());
|
|
212
267
|
const roster = leadRosterRevision(leadPartsFor(session, leadPromptParts));
|
|
213
|
-
const
|
|
268
|
+
const recoverMode = orchestrationRecoveryNeeded(session);
|
|
269
|
+
// A coding Lead hears of its agents through the listing reminder each
|
|
270
|
+
// turn carries (agent-listing-notice.mjs), never a rewritten prompt;
|
|
271
|
+
// syncAgentListing only lifts a stale `agent` denial.
|
|
272
|
+
const edited =
|
|
273
|
+
recoverMode || session.agentProfileRevision !== revision || (agentMode && session.agentRosterRevision !== roster);
|
|
214
274
|
// At a turn's start its own turn is the one counted; nothing runs yet.
|
|
215
275
|
const busy = () => getSession?.() !== session || (trigger !== 'turn' && turnInFlight(session));
|
|
216
276
|
if (busy()) {
|
|
217
277
|
agentProfilePending = true;
|
|
218
278
|
return false;
|
|
219
279
|
}
|
|
220
|
-
const toolsChanged =
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
280
|
+
const toolsChanged = agentMode
|
|
281
|
+
? stageLiveToolChanges({ liveDeniedTools, modelStandaloneTools }, session, workflow, {
|
|
282
|
+
edited: session.agentProfileRevision !== revision,
|
|
283
|
+
})
|
|
284
|
+
: trigger === 'resume' && syncAgentListing(session);
|
|
225
285
|
if (!edited) return (trigger === 'resume' && (await foldPromptIfCold())) || toolsChanged;
|
|
226
286
|
// A coding session from before profile stamps keeps its prompt when it
|
|
227
287
|
// is reopened; a saved edit still reaches it.
|
|
228
|
-
if (session.agentProfileRevision === undefined && trigger === 'resume' && !agentMode)
|
|
288
|
+
if (session.agentProfileRevision === undefined && trigger === 'resume' && !agentMode && !recoverMode) {
|
|
289
|
+
return toolsChanged;
|
|
290
|
+
}
|
|
229
291
|
if (foldPending(session)) {
|
|
230
292
|
await foldPrompt(session, AGENT_PROFILE_INTENT);
|
|
231
293
|
return true;
|
|
@@ -241,7 +303,7 @@ export function createAgentProfileFold({
|
|
|
241
303
|
if (staged.core === presentedBp3Core(session)) return toolsChanged;
|
|
242
304
|
rewriteConversationBp3Core(session, staged.core);
|
|
243
305
|
// The request prefix changed: one cache break, tagged on the next send.
|
|
244
|
-
markToolSurfaceMigrated(session, AGENT_PROFILE_INTENT);
|
|
306
|
+
markToolSurfaceMigrated(session, recoverMode ? PROMPT_FOLD_INTENT : AGENT_PROFILE_INTENT);
|
|
245
307
|
return true;
|
|
246
308
|
}
|
|
247
309
|
|
|
@@ -254,12 +316,34 @@ export function createAgentProfileFold({
|
|
|
254
316
|
if (session?.id && sessionModeOf(session) === AGENT_MODE && !Array.isArray(session.agentConnectorTools)) {
|
|
255
317
|
session.agentConnectorTools = connectorTools(session, schemaTools(session).map(toolName));
|
|
256
318
|
}
|
|
257
|
-
if (agentProfilePending) {
|
|
319
|
+
if (agentProfilePending || orchestrationRecoveryNeeded(session)) {
|
|
258
320
|
agentProfilePending = false;
|
|
259
321
|
changed = await refreshAgentProfile({ trigger: 'turn' });
|
|
260
322
|
}
|
|
261
|
-
|
|
323
|
+
changed = (await foldPromptIfCold()) || changed;
|
|
324
|
+
return syncAgentListing(session) || changed;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// A coding Lead hears of its agents through the agent list reminder.
|
|
328
|
+
const codingLead = (session) =>
|
|
329
|
+
Boolean(session?.id && session.promptIdentity && !isAgentOwner(session) && sessionModeOf(session) !== AGENT_MODE);
|
|
330
|
+
|
|
331
|
+
/** A coding Lead keeps `agent` reachable (liftAgentDenial). Returns true
|
|
332
|
+
* when the session changed. */
|
|
333
|
+
function syncAgentListing(session) {
|
|
334
|
+
return codingLead(session) && liftAgentDenial({ liveDeniedTools, modelStandaloneTools }, session);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/** The agents a coding Lead may delegate to now, as its prompt would list
|
|
338
|
+
* them: the turn's input to the agent list reminder (manager/
|
|
339
|
+
* agent-listing-notice.mjs); undefined for any other session. */
|
|
340
|
+
function currentAgents() {
|
|
341
|
+
const session = getSession?.();
|
|
342
|
+
if (!codingLead(session)) return undefined;
|
|
343
|
+
const config = configForSession(getConfig(), session.workflow?.id, currentMode(session));
|
|
344
|
+
const { leadPromptParts } = activeWorkflowContext(config, getDataDir());
|
|
345
|
+
return agentListEntries(leadPartsFor(session, leadPromptParts).agentCatalogContext) || [];
|
|
262
346
|
}
|
|
263
347
|
|
|
264
|
-
return { refreshAgentProfile, applyPendingAgentProfile };
|
|
348
|
+
return { refreshAgentProfile, applyPendingAgentProfile, currentAgents };
|
|
265
349
|
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
// Staging and applying the tool/prompt policy a config gives a Lead session:
|
|
2
2
|
// the tools it is denied, its BP1 rules and its BP3 core.
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
toSessionWorkflowMeta,
|
|
5
|
+
workflowDisallowsAgentTool,
|
|
6
|
+
} from '../../runtime/agent/orchestrator/runtime-core/workflow.mjs';
|
|
4
7
|
import {
|
|
5
8
|
applyDeferredToolSurface,
|
|
6
9
|
filterDisallowedTools,
|
|
@@ -20,7 +23,10 @@ import { finalizeSessionToolList } from '../../runtime/agent/orchestrator/sessio
|
|
|
20
23
|
import { isAgentOwner } from '../../runtime/agent/orchestrator/agent-owner.mjs';
|
|
21
24
|
import { AGENT_MODE, sessionModeOf } from '../../runtime/agent/orchestrator/session/agent-mode.mjs';
|
|
22
25
|
import { unusedModelEditToolName } from '../../runtime/shared/edit-tool-dialect.mjs';
|
|
23
|
-
import {
|
|
26
|
+
import {
|
|
27
|
+
presentedAgentCatalogSlice,
|
|
28
|
+
withPresentedWorkspaceSlices,
|
|
29
|
+
} from '../../runtime/agent/orchestrator/session/manager/prompt-utils.mjs';
|
|
24
30
|
import { leadPartsFor, rewriteSystemHeading, toolNames } from './surface-helpers.mjs';
|
|
25
31
|
|
|
26
32
|
export function createPolicyStage({
|
|
@@ -34,12 +40,14 @@ export function createPolicyStage({
|
|
|
34
40
|
activeWorkflowContext,
|
|
35
41
|
}) {
|
|
36
42
|
// The tools `session` is denied now: its caller's denials, the feature gates
|
|
37
|
-
// as they stand, and
|
|
43
|
+
// as they stand, and an agent-mode Lead's agent tool when its workflow
|
|
44
|
+
// delegates to no one (a coding Lead keeps it: workflow.mjs
|
|
45
|
+
// workflowDisallowsAgentTool).
|
|
38
46
|
function liveDeniedTools(session, workflow) {
|
|
39
47
|
return [
|
|
40
48
|
...session.callerDisallowedTools,
|
|
41
49
|
...featureDisallowedTools(),
|
|
42
|
-
...(workflow
|
|
50
|
+
...(workflowDisallowsAgentTool(workflow, { agentMode: sessionModeOf(session) === AGENT_MODE }) ? ['agent'] : []),
|
|
43
51
|
]
|
|
44
52
|
.map((name) => String(name || ''))
|
|
45
53
|
.filter(Boolean);
|
|
@@ -100,7 +108,17 @@ export function createPolicyStage({
|
|
|
100
108
|
} = activeWorkflowContext(config, getDataDir());
|
|
101
109
|
const sessionMode = sessionModeOf(session);
|
|
102
110
|
const agentMode = sessionMode === AGENT_MODE;
|
|
103
|
-
|
|
111
|
+
// A coding Lead's conversation keeps the agent list it was built with, as
|
|
112
|
+
// it keeps the workspace slices: agents switched on or off since reach it
|
|
113
|
+
// as reminders on top of that list (agent-listing-notice.mjs). A slice
|
|
114
|
+
// that is not one generated list is rebuilt as before.
|
|
115
|
+
const presentedCatalog = conversation && !agentMode ? presentedAgentCatalogSlice(session) : null;
|
|
116
|
+
const leadWorkflowParts = ownerIsAgent
|
|
117
|
+
? {}
|
|
118
|
+
: {
|
|
119
|
+
...leadPartsFor(session, leadPromptParts),
|
|
120
|
+
...(presentedCatalog !== null ? { agentCatalogContext: presentedCatalog } : {}),
|
|
121
|
+
};
|
|
104
122
|
const denied = liveDeniedTools(session, workflow);
|
|
105
123
|
|
|
106
124
|
const baseRules =
|
|
@@ -101,5 +101,6 @@ export function createToolPolicyRefresh({
|
|
|
101
101
|
applyPendingSessionSwitches: switches.applyPendingSessionSwitches,
|
|
102
102
|
refreshAgentProfile: profile.refreshAgentProfile,
|
|
103
103
|
applyPendingAgentProfile: profile.applyPendingAgentProfile,
|
|
104
|
+
currentAgents: profile.currentAgents,
|
|
104
105
|
};
|
|
105
106
|
}
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
import { workflowDisallowsAgentTool } from '../runtime/agent/orchestrator/runtime-core/workflow.mjs';
|
|
18
18
|
import { promptWorkflowOf } from '../runtime/agent/orchestrator/session/manager/workflow-switch-notice.mjs';
|
|
19
19
|
import { configuredOrchestrationMode } from '../runtime/shared/orchestration.mjs';
|
|
20
|
+
import { isAgentModeSession } from '../runtime/agent/orchestrator/session/agent-mode.mjs';
|
|
20
21
|
|
|
21
22
|
export function createToolSurface({
|
|
22
23
|
mgr,
|
|
@@ -36,15 +37,18 @@ export function createToolSurface({
|
|
|
36
37
|
}) {
|
|
37
38
|
let preSessionSurface = null;
|
|
38
39
|
|
|
39
|
-
// A
|
|
40
|
-
//
|
|
41
|
-
//
|
|
40
|
+
// A coding Lead keeps `agent` whichever agents exist (the agents reach it as
|
|
41
|
+
// a listing reminder); Solo still offers it (deferred) for explicit user
|
|
42
|
+
// requests. An agent-mode Lead keeps its frozen capability once live (the
|
|
43
|
+
// workflow its prompt was built with, even after a mid-conversation switch)
|
|
44
|
+
// and a new one gets it with current agents.
|
|
42
45
|
function workflowAllowsAgents() {
|
|
43
46
|
const session = getSession();
|
|
47
|
+
const agentMode = isAgentModeSession(session || {});
|
|
44
48
|
if (session?.id || session?.workflow || session?.orchestrationMode) {
|
|
45
|
-
return !workflowDisallowsAgentTool(promptWorkflowOf(session));
|
|
49
|
+
return !workflowDisallowsAgentTool(promptWorkflowOf(session), { agentMode });
|
|
46
50
|
}
|
|
47
|
-
return (delegatableAgentIds?.(getConfig(), cfgMod.getPluginData?.() || dataDir).length ?? 1) > 0;
|
|
51
|
+
return !agentMode || (delegatableAgentIds?.(getConfig(), cfgMod.getPluginData?.() || dataDir).length ?? 1) > 0;
|
|
48
52
|
}
|
|
49
53
|
|
|
50
54
|
function modelStandaloneTools() {
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import { normalizeToolMode } from '../../runtime/agent/orchestrator/runtime-core/effort.mjs';
|
|
6
6
|
import { interruptTaskWaitForSession } from '../../runtime/agent/orchestrator/session/task-wait-control.mjs';
|
|
7
7
|
import { markPendingGoalReminder } from '../../runtime/agent/orchestrator/runtime-core/goal-reminder.mjs';
|
|
8
|
+
import { rearmLateDeferredTools } from '../../runtime/agent/orchestrator/runtime-core/tool-catalog.mjs';
|
|
8
9
|
|
|
9
10
|
export function createSessionOps(deps) {
|
|
10
11
|
const {
|
|
@@ -55,6 +56,7 @@ export function createSessionOps(deps) {
|
|
|
55
56
|
if (!session?.id) return null;
|
|
56
57
|
const result = await mgr.rewindSessionMessagesTo?.(session.id, options);
|
|
57
58
|
if (!result) return null;
|
|
59
|
+
if (options.dryRun === true) return result;
|
|
58
60
|
setSession(mgr.getSession(session.id) || session);
|
|
59
61
|
invalidateContextStatusCache();
|
|
60
62
|
return result;
|
|
@@ -96,6 +98,12 @@ export function createSessionOps(deps) {
|
|
|
96
98
|
} catch {
|
|
97
99
|
/* best-effort: a Goal reminder must never break manual compact */
|
|
98
100
|
}
|
|
101
|
+
// The same compaction strips late MCP tool announcements from older turns.
|
|
102
|
+
try {
|
|
103
|
+
rearmLateDeferredTools(getSession());
|
|
104
|
+
} catch {
|
|
105
|
+
/* best-effort: a tool reminder must never break manual compact */
|
|
106
|
+
}
|
|
99
107
|
if (options.recoverAgent === true) recoverAgentWorkers();
|
|
100
108
|
invalidateContextStatusCache();
|
|
101
109
|
return result;
|
|
@@ -28,6 +28,7 @@ export function createTurnRunner(deps) {
|
|
|
28
28
|
completeTurnSnapshotForTurn = completeTurnSnapshot,
|
|
29
29
|
turnCleanupSettleMs = 2_000,
|
|
30
30
|
getConfig = () => null,
|
|
31
|
+
currentAgents,
|
|
31
32
|
} = deps;
|
|
32
33
|
const transcript = createTurnTranscript(deps);
|
|
33
34
|
const openTurn = createTurnOpener({
|
|
@@ -81,7 +82,10 @@ export function createTurnRunner(deps) {
|
|
|
81
82
|
plannedToolCalls(session0, options),
|
|
82
83
|
getCurrentCwd(),
|
|
83
84
|
options.prefetch || null,
|
|
84
|
-
|
|
85
|
+
// The agents enabled now (coding Leads), asked for each user turn
|
|
86
|
+
// the ask prepares, for its agent list reminder
|
|
87
|
+
// (manager/agent-listing-notice.mjs).
|
|
88
|
+
{ ...callbacks, agentsNow: currentAgents }
|
|
85
89
|
);
|
|
86
90
|
} finally {
|
|
87
91
|
releaseWebSearchUsage?.();
|