mixdog 1.3.4 → 1.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSES/oh-my-pi-MIT.txt +23 -0
- package/NOTICE.md +15 -0
- package/README.md +11 -0
- package/package.json +2 -1
- package/scripts/embed-google-oauth-client.mjs +49 -0
- package/scripts/lib/test-spawn-ledger.mjs +46 -1
- package/scripts/test.mjs +5 -0
- package/src/defaults/shipped-history.json +8 -0
- package/src/defaults/skills/browser-use/SKILL.md +21 -9
- package/src/defaults/skills/setup/SKILL.md +7 -2
- package/src/defaults/skills/setup/references/actions.md +82 -27
- package/src/defaults/skills/setup/references/surfaces.md +67 -34
- package/src/defaults/skills/setup-guide/SKILL.md +3 -3
- package/src/lib/keychain-cjs.cjs +359 -29
- package/src/rules/agent-mode/AGENT_MODE.md +10 -2
- package/src/rules/agent-mode/MIXY.md +12 -0
- package/src/runtime/agent/orchestrator/agent-runtime/heartbeat-completion.mjs +193 -0
- package/src/runtime/agent/orchestrator/config.mjs +18 -0
- package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +2 -2
- package/src/runtime/agent/orchestrator/mcp/client.mjs +6 -2
- package/src/runtime/agent/orchestrator/mcp/oauth-callback.mjs +31 -2
- package/src/runtime/agent/orchestrator/mcp/oauth-client.mjs +16 -3
- package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +42 -7
- package/src/runtime/agent/orchestrator/mcp/oauth-provider.mjs +21 -2
- package/src/runtime/agent/orchestrator/mcp/oauth-shared.mjs +23 -0
- package/src/runtime/agent/orchestrator/mcp/oauth.mjs +11 -1
- package/src/runtime/agent/orchestrator/providers/account-pool.mjs +68 -23
- package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +22 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +18 -5
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +15 -3
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-catalog.mjs +52 -8
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +10 -1
- package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +22 -6
- package/src/runtime/agent/orchestrator/providers/antigravity-response.mjs +15 -4
- package/src/runtime/agent/orchestrator/providers/antigravity-stream.mjs +17 -2
- package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +19 -2
- package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/cursor-wire-normalization.mjs +10 -4
- package/src/runtime/agent/orchestrator/providers/cursor-wire-protobuf.mjs +10 -217
- package/src/runtime/agent/orchestrator/providers/cursor.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/devin-auth.mjs +409 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-families.mjs +187 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-identity.mjs +342 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-models.mjs +541 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-protobuf.mjs +305 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-request.mjs +558 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-schema.mjs +880 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-session.mjs +190 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-tool-args.mjs +351 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-transport.mjs +560 -0
- package/src/runtime/agent/orchestrator/providers/devin.mjs +491 -0
- package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/gemini-output-limit.mjs +46 -0
- package/src/runtime/agent/orchestrator/providers/gemini-reasoning-relay.mjs +30 -0
- package/src/runtime/agent/orchestrator/providers/gemini-request-body.mjs +7 -11
- package/src/runtime/agent/orchestrator/providers/gemini-retry-policy.mjs +61 -22
- package/src/runtime/agent/orchestrator/providers/gemini-sdk-request.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +71 -4
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +25 -2
- package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +96 -5
- package/src/runtime/agent/orchestrator/providers/lib/tail-reminder-split.mjs +23 -5
- package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +17 -0
- package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +39 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/openai-error-contract.mjs +6 -1
- package/src/runtime/agent/orchestrator/providers/opencode-go-gemini.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/provider-availability.mjs +19 -0
- package/src/runtime/agent/orchestrator/providers/registry.mjs +17 -3
- package/src/runtime/agent/orchestrator/providers/wire-protobuf-codec.mjs +368 -0
- package/src/runtime/agent/orchestrator/runtime-core/builtin-features.mjs +2 -0
- package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +1 -1
- package/src/runtime/agent/orchestrator/runtime-core/reaction-tool-defs.mjs +133 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-data.mjs +3 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +23 -5
- package/src/runtime/agent/orchestrator/runtime-core/tool-profile.mjs +35 -10
- package/src/runtime/agent/orchestrator/runtime-core/workflow/agents.mjs +57 -10
- package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +9 -6
- package/src/runtime/agent/orchestrator/runtime-core/workflow-ids.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +18 -0
- package/src/runtime/agent/orchestrator/session/agent-room.d.mts +0 -3
- package/src/runtime/agent/orchestrator/session/agent-room.mjs +10 -4
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +15 -3
- package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +5 -0
- package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +36 -4
- package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +15 -3
- package/src/runtime/agent/orchestrator/session/loop/assistant-commit.mjs +8 -0
- package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +8 -1
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/segments.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +50 -2
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +11 -2
- package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +11 -2
- package/src/runtime/agent/orchestrator/session/loop/turn-limits.mjs +60 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-commit.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +14 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/manager/session-tool-surface.mjs +13 -0
- package/src/runtime/agent/orchestrator/session/message-ids.mjs +5 -3
- package/src/runtime/agent/orchestrator/session/message-reactions.mjs +89 -0
- package/src/runtime/agent/orchestrator/session/message-reactions.test-support.mjs +75 -0
- package/src/runtime/agent/orchestrator/session/routine-result.d.mts +0 -2
- package/src/runtime/agent/orchestrator/session/routine-result.mjs +11 -2
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +20 -2
- package/src/runtime/agent/orchestrator/session/store-agent-worker-header-cache.mjs +85 -0
- package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +125 -12
- package/src/runtime/agent/orchestrator/session/tail-reminder-rules.mjs +11 -5
- package/src/runtime/agent/orchestrator/session/tool-batch/state.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/tool-batch.mjs +5 -0
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +7 -1
- package/src/runtime/channels/lib/schedule-errors.mjs +51 -0
- package/src/runtime/channels/lib/scheduler.mjs +109 -14
- package/src/runtime/channels/lib/schedules-db.mjs +49 -0
- package/src/runtime/channels/lib/tool-dispatch.mjs +13 -0
- package/src/runtime/channels/lib/worker-main.mjs +9 -0
- package/src/runtime/office/com/com-adapter.mjs +17 -1
- package/src/runtime/office/com/office-com-host.ps1 +51 -1
- package/src/runtime/office/pdf/cfb-fixture.mjs +328 -0
- package/src/runtime/office/pdf/cfb-protection.mjs +311 -0
- package/src/runtime/office/pdf/document-preview.mjs +326 -8
- package/src/runtime/office/pdf/ooxml-crypto.mjs +469 -0
- package/src/runtime/office/pdf/ooxml-key-worker.mjs +14 -0
- package/src/runtime/office/portable/portable-soffice.mjs +45 -5
- package/src/runtime/shared/bot-hops.mjs +62 -2
- package/src/runtime/shared/config.mjs +49 -22
- package/src/runtime/shared/connector-catalog.d.mts +4 -2
- package/src/runtime/shared/connector-catalog.mjs +16 -8
- package/src/runtime/shared/developer-options.mjs +32 -1
- package/src/runtime/shared/google-oauth-client.generated.json +4 -0
- package/src/runtime/shared/google-oauth-client.mjs +34 -9
- package/src/runtime/shared/llm/anthropic-betas.mjs +7 -0
- package/src/runtime/shared/llm/anthropic-thinking-contract.mjs +12 -0
- package/src/runtime/shared/llm/provider-catalog-registry.mjs +1 -0
- package/src/runtime/shared/llm/well-formed.mjs +66 -0
- package/src/runtime/shared/provider-accounts.mjs +224 -58
- package/src/runtime/shared/provider-api-key.mjs +19 -8
- package/src/runtime/shared/routine-trigger.mjs +42 -0
- package/src/runtime/shared/tool-execution-contract.mjs +2 -0
- package/src/runtime/shared/transcript-metadata.mjs +38 -0
- package/src/runtime/shared/user-data-guard.mjs +30 -2
- package/src/runtime/shared/worktrees/activity-gate.mjs +12 -5
- package/src/session-runtime/agent-home-delivery.mjs +57 -13
- package/src/session-runtime/agent-room-delivery.mjs +28 -1
- package/src/session-runtime/agent-task-sessions.mjs +292 -79
- package/src/session-runtime/boot/begin.mjs +3 -2
- package/src/session-runtime/boot/tools.mjs +2 -0
- package/src/session-runtime/bot-chain.mjs +32 -0
- package/src/session-runtime/bot-status.mjs +7 -4
- package/src/session-runtime/channel-config-api.mjs +63 -17
- package/src/session-runtime/internal-tool-executor/reaction-tool.mjs +45 -0
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +72 -5
- package/src/session-runtime/internal-tool-executor.mjs +4 -0
- package/src/session-runtime/mcp-glue/server-catalog.mjs +7 -1
- package/src/session-runtime/mixy-heartbeat.mjs +807 -0
- package/src/session-runtime/model-route/route-tuning.mjs +10 -6
- package/src/session-runtime/owned-automation-run.mjs +33 -12
- package/src/session-runtime/provider-auth/login.mjs +7 -0
- package/src/session-runtime/provider-models/row-hydration.mjs +13 -2
- package/src/session-runtime/routine-home-turn.mjs +33 -6
- package/src/session-runtime/schedule-session-run.mjs +7 -3
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-plan.mjs +8 -12
- package/src/session-runtime/services/channel-admin.mjs +66 -18
- package/src/session-runtime/services/channel-worker.mjs +1 -1
- package/src/session-runtime/services/provider-admin.mjs +128 -25
- package/src/session-runtime/services/schedule-access.mjs +14 -0
- package/src/session-runtime/services/usage-dashboard-model.mjs +1 -0
- package/src/session-runtime/session-create/session-options.mjs +7 -4
- package/src/session-runtime/settings-system-api.mjs +16 -1
- package/src/session-runtime/setup-tool/executor.mjs +49 -14
- package/src/session-runtime/setup-tool/extended-actions.mjs +29 -3
- package/src/session-runtime/setup-tool/settings-contract.mjs +65 -4
- package/src/session-runtime/setup-tool/tool-defs.mjs +30 -3
- package/src/session-runtime/task-session-route.mjs +99 -0
- package/src/session-runtime/tool-policy/policy-stage.mjs +5 -2
- package/src/session-runtime/tool-policy/surface-helpers.mjs +2 -3
- package/src/session-runtime/tool-surface.mjs +4 -0
- package/src/session-runtime/workflow-agents-api/agent-editor.mjs +98 -21
- package/src/session-runtime/workflow-agents-api/workflow-packs.mjs +6 -1
- package/src/session-runtime/workspace/transitions.mjs +10 -2
- package/src/standalone/daemon-session-control.mjs +88 -20
- package/src/standalone/daemon.mjs +8 -0
- package/src/standalone/session-protocol.mjs +5 -0
- package/src/standalone/session-service/agent-tree/agent-cancel.mjs +6 -2
- package/src/standalone/session-service/session-calls/session-turns.mjs +6 -0
- package/src/standalone/session-service.mjs +13 -0
- package/src/standalone/session-tool-output.mjs +8 -5
- package/src/tui/app/model-options.mjs +2 -0
- package/src/tui/dist/index.mjs +24 -2
- package/src/tui/session/notice-surface.mjs +17 -1
- package/src/tui/session/session-api/integrations.mjs +1 -0
- package/src/tui/session/session-api/lifecycle.mjs +8 -0
- package/src/tui/session/session-api/settings.mjs +14 -2
- package/src/tui/session/turn-stream.mjs +17 -1
- package/src/tui/session/turn.mjs +22 -2
|
@@ -33,10 +33,12 @@ import { clean } from '../runtime/agent/orchestrator/runtime-core/session-text.m
|
|
|
33
33
|
import { collectTurnArtifacts, handoffContent, sessionRoots, turnSlice } from './handoff-attachments.mjs';
|
|
34
34
|
import { roomMemberOfSession } from '../runtime/agent/orchestrator/session/agent-room.mjs';
|
|
35
35
|
import { wrapAgentMessage } from '../runtime/shared/agent-message-envelope.mjs';
|
|
36
|
-
import { sessionsWithPendingWork } from '../runtime/shared/pending-agent-work.mjs';
|
|
36
|
+
import { hasPendingAgentWork, sessionsWithPendingWork } from '../runtime/shared/pending-agent-work.mjs';
|
|
37
37
|
import { agentTaskIdOfExecution } from './services/agent-tool/restart-notify.mjs';
|
|
38
|
+
import { botChainOf } from '../runtime/shared/bot-hops.mjs';
|
|
38
39
|
import { worktreeError, worktreeNameError } from '../runtime/shared/worktrees/contract.mjs';
|
|
39
40
|
import { samePath } from '../runtime/shared/worktrees/paths.mjs';
|
|
41
|
+
import { readModelCatalog, routeByDisplayName, withEffort } from './task-session-route.mjs';
|
|
40
42
|
import { assertWorktreeMutationAllowed } from './services/worktree-access.mjs';
|
|
41
43
|
|
|
42
44
|
const TITLE_LIMIT = 60;
|
|
@@ -158,10 +160,25 @@ function consumedTaskIds(message) {
|
|
|
158
160
|
* submissionOutcome and when its end was recorded (0 when unknown); null
|
|
159
161
|
* when no turn took runId. */
|
|
160
162
|
export function latestTurnOutcome(messages, runId) {
|
|
163
|
+
const chain = turnChain(messages, runId);
|
|
164
|
+
return chain && { id: chain.id, outcome: chain.outcome, at: chain.at, ambiguous: chain.ambiguous };
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** latestTurnOutcome with what a report's wait needs besides: `taskIds`, the
|
|
168
|
+
* agent tasks the chain's turns started — the only tasks proven the chain's
|
|
169
|
+
* — and `busyIsOthers`: the session's last stored turn is one outside the
|
|
170
|
+
* chain that recorded no end yet, and a stored turn took the result of every
|
|
171
|
+
* one of `taskIds` (only those make a turn the chain's) — a busy session
|
|
172
|
+
* then runs that other turn, and no result of the chain can be on its way
|
|
173
|
+
* into a turn (woken by it, or steered into the running one) whose prompt or
|
|
174
|
+
* steer is not stored yet. */
|
|
175
|
+
function turnChain(messages, runId) {
|
|
161
176
|
const list = Array.isArray(messages) ? messages : [];
|
|
162
177
|
const start = list.findIndex((message) => submissionIdsOf(message).includes(runId));
|
|
163
178
|
if (start < 0) return null;
|
|
164
179
|
const started = new Set();
|
|
180
|
+
// The agent tasks whose results a stored turn took (as its prompt or steered in).
|
|
181
|
+
const taken = new Set();
|
|
165
182
|
const startingCalls = new Set();
|
|
166
183
|
let at = start;
|
|
167
184
|
let id = runId;
|
|
@@ -186,6 +203,7 @@ export function latestTurnOutcome(messages, runId) {
|
|
|
186
203
|
index > start && message?.role === 'user' && message.meta?.source === 'task-notification'
|
|
187
204
|
? consumedTaskIds(message)
|
|
188
205
|
: null;
|
|
206
|
+
for (const taskId of delivered || []) if (taskId) taken.add(taskId);
|
|
189
207
|
if (delivered && !inChain) {
|
|
190
208
|
if (delivered.some((taskId) => taskId && started.has(taskId))) {
|
|
191
209
|
inChain = true;
|
|
@@ -211,7 +229,32 @@ export function latestTurnOutcome(messages, runId) {
|
|
|
211
229
|
}
|
|
212
230
|
const turn = list.slice(at);
|
|
213
231
|
const ended = turn.find((message) => message?.meta?.transcript?.completion);
|
|
214
|
-
|
|
232
|
+
const last = turnId === id ? null : submissionOutcome(list.slice(turnAt), turnId);
|
|
233
|
+
return {
|
|
234
|
+
id,
|
|
235
|
+
outcome: submissionOutcome(turn, id),
|
|
236
|
+
at: Number(ended?.meta?.transcript?.at) || 0,
|
|
237
|
+
ambiguous,
|
|
238
|
+
taskIds: started,
|
|
239
|
+
busyIsOthers: last === 'open' && [...started].every((taskId) => taken.has(taskId)),
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/** The agent task an owed result reports; null when it names none. */
|
|
244
|
+
function owedTaskId(result) {
|
|
245
|
+
const executionId = result?.meta?.execution_id;
|
|
246
|
+
return executionId ? agentTaskIdOfExecution(executionId) : null;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/** The `owed` results that may be the chain's of agent tasks `taskIds`: its
|
|
250
|
+
* tasks' and those naming no task (their provenance is missing); every one
|
|
251
|
+
* for a null `taskIds` (the whole session). */
|
|
252
|
+
function owedOf(owed, taskIds) {
|
|
253
|
+
if (!taskIds) return owed;
|
|
254
|
+
return owed.filter((result) => {
|
|
255
|
+
const taskId = owedTaskId(result);
|
|
256
|
+
return !taskId || taskIds.has(taskId);
|
|
257
|
+
});
|
|
215
258
|
}
|
|
216
259
|
|
|
217
260
|
/** Whether task session `sessionId` still waits on agent work it handed off,
|
|
@@ -221,16 +264,72 @@ export function latestTurnOutcome(messages, runId) {
|
|
|
221
264
|
* `agentTaskOwners` (the sessions owning a queued, spawning or running agent
|
|
222
265
|
* task — one still waiting for admission has no turn in flight, and its pool
|
|
223
266
|
* row reads `unknown`). An agent task owned by an Agent session under it, at
|
|
224
|
-
* any depth, counts too: every descendant's pool row names the Lead as owner.
|
|
267
|
+
* any depth, counts too: every descendant's pool row names the Lead as owner.
|
|
268
|
+
* `taskIds` narrows it to one chain's agent tasks (chainPendingWork). */
|
|
225
269
|
export function pendingWorkOf(
|
|
226
270
|
sessionId,
|
|
227
|
-
{
|
|
271
|
+
{
|
|
272
|
+
agentWorking = new Set(),
|
|
273
|
+
owed = [],
|
|
274
|
+
pool = [],
|
|
275
|
+
agentTaskOwners = new Set(),
|
|
276
|
+
taskIds = null,
|
|
277
|
+
working = new Set(),
|
|
278
|
+
agentTasks = [],
|
|
279
|
+
}
|
|
228
280
|
) {
|
|
281
|
+
if (taskIds) return chainPendingWork(sessionId, taskIds, { owed, pool, agentTaskOwners, working, agentTasks });
|
|
229
282
|
if (agentWorking.has(sessionId) || owed.length > 0 || agentTaskOwners.has(sessionId)) return true;
|
|
230
283
|
if (sessionsWithPendingWork(pool).has(sessionId)) return true;
|
|
231
284
|
return pool.some((row) => row?.ownerSessionId === sessionId && agentTaskOwners.has(String(row.sessionId || '')));
|
|
232
285
|
}
|
|
233
286
|
|
|
287
|
+
/** The agent work of the chain of Lead `sessionId` whose own agent tasks are
|
|
288
|
+
* `taskIds`. `taskIds`: those tasks alone, the only ones proven the chain's
|
|
289
|
+
* (no record says which task started a child agent). `sessions`: the Agent
|
|
290
|
+
* sessions those tasks run in (`agentTasks` `sessionId`, a pool row's
|
|
291
|
+
* `taskId`) and every Agent session under one (pool `parentSessionId`),
|
|
292
|
+
* never the Lead — work there may be the chain's, so it only holds the
|
|
293
|
+
* report (it may be another's: a session the chain's agent ran in can be
|
|
294
|
+
* sent another task, which can spawn children of its own). */
|
|
295
|
+
export function chainClosure(sessionId, taskIds, { pool = [], agentTasks = [] } = {}) {
|
|
296
|
+
const owned = new Set(taskIds);
|
|
297
|
+
const sessions = new Set();
|
|
298
|
+
const join = (id) => {
|
|
299
|
+
if (!id || id === sessionId || sessions.has(id)) return false;
|
|
300
|
+
sessions.add(id);
|
|
301
|
+
return true;
|
|
302
|
+
};
|
|
303
|
+
for (const task of agentTasks) if (owned.has(task.taskId)) join(task.sessionId);
|
|
304
|
+
for (const row of pool) if (owned.has(String(row?.taskId || ''))) join(String(row.sessionId || ''));
|
|
305
|
+
for (let grown = true; grown; ) {
|
|
306
|
+
grown = false;
|
|
307
|
+
for (const row of pool) {
|
|
308
|
+
if (sessions.has(String(row?.parentSessionId || '')) && join(String(row.sessionId || ''))) grown = true;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
return { taskIds: owned, sessions };
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/** pendingWorkOf of the chain of agent tasks `taskIds` alone, with all its
|
|
315
|
+
* work (chainClosure): a task it owns or one of its Agent sessions runs
|
|
316
|
+
* (`agentTasks`: `{ taskId, sessionId, running }` of this process's agent
|
|
317
|
+
* tasks; a pool row's `taskId`) still queued or running, or its result owed
|
|
318
|
+
* (or an owed result naming no task), or one of those sessions with a turn
|
|
319
|
+
* in flight (`working`), owning a queued or running agent task, or pending
|
|
320
|
+
* on its pool row. Other work of the session never counts. */
|
|
321
|
+
function chainPendingWork(sessionId, taskIds, { owed, pool, agentTaskOwners, working, agentTasks }) {
|
|
322
|
+
const chain = chainClosure(sessionId, taskIds, { pool, agentTasks });
|
|
323
|
+
const sessionOf = new Map();
|
|
324
|
+
for (const row of pool) if (row?.taskId) sessionOf.set(String(row.taskId), String(row.sessionId || ''));
|
|
325
|
+
for (const task of agentTasks) if (task.sessionId) sessionOf.set(task.taskId, task.sessionId);
|
|
326
|
+
const ofChain = (taskId) => chain.taskIds.has(taskId) || chain.sessions.has(sessionOf.get(taskId));
|
|
327
|
+
if (owed.some((result) => !owedTaskId(result) || ofChain(owedTaskId(result)))) return true;
|
|
328
|
+
if (agentTasks.some((task) => task.running && ofChain(task.taskId))) return true;
|
|
329
|
+
for (const id of chain.sessions) if (working.has(id) || agentTaskOwners.has(id)) return true;
|
|
330
|
+
return pool.some((row) => chain.sessions.has(String(row?.sessionId || '')) && hasPendingAgentWork(row));
|
|
331
|
+
}
|
|
332
|
+
|
|
234
333
|
/** The agent results queued for `sessionId` that no turn has taken yet (its
|
|
235
334
|
* durable pending queue: an agent's completion, a restart failure): `{ text,
|
|
236
335
|
* meta }` as the notification bus delivers them. */
|
|
@@ -255,22 +354,37 @@ export async function owedAgentResults(sessionId) {
|
|
|
255
354
|
}
|
|
256
355
|
|
|
257
356
|
/** pendingWorkOf over this process's state: the in-flight runtimes, the
|
|
258
|
-
* durable pending queue, the agent pool and the background agent tasks
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
357
|
+
* durable pending queue, the agent pool and the background agent tasks;
|
|
358
|
+
* `taskIds` narrows it to one chain's agent tasks. */
|
|
359
|
+
export async function taskSessionPendingWork(sessionId, { owedResults = owedAgentResults, taskIds = null } = {}) {
|
|
360
|
+
const [
|
|
361
|
+
{ inFlightSessionActivity },
|
|
362
|
+
{ listStoredAgentWorkersAsync },
|
|
363
|
+
{ activeBackgroundTaskSessionIds, listBackgroundTasks },
|
|
364
|
+
] = await Promise.all([
|
|
365
|
+
import('../runtime/agent/orchestrator/session/manager/runtime-state.mjs'),
|
|
366
|
+
import('../runtime/agent/orchestrator/session/store-summary-reader.mjs'),
|
|
367
|
+
import('../runtime/shared/background-tasks.mjs'),
|
|
368
|
+
]);
|
|
266
369
|
const { working, agentWorking } = inFlightSessionActivity();
|
|
267
370
|
const agentTaskOwners = activeBackgroundTaskSessionIds({ surface: 'agent' });
|
|
268
|
-
|
|
269
|
-
|
|
371
|
+
const agentTasks = taskIds ? agentTaskRows(listBackgroundTasks) : [];
|
|
372
|
+
const live = { agentWorking, agentTaskOwners, taskIds, working, agentTasks };
|
|
373
|
+
if (pendingWorkOf(sessionId, live)) return true;
|
|
374
|
+
const owed = await owedResults(sessionId);
|
|
375
|
+
if (pendingWorkOf(sessionId, { ...live, owed })) return true;
|
|
270
376
|
const pool = await listStoredAgentWorkersAsync({ runningSessionIds: working });
|
|
271
|
-
return pendingWorkOf(sessionId, {
|
|
377
|
+
return pendingWorkOf(sessionId, { ...live, owed, pool });
|
|
272
378
|
}
|
|
273
379
|
|
|
380
|
+
/** This process's agent tasks as pendingWorkOf reads them. */
|
|
381
|
+
const agentTaskRows = (listBackgroundTasks) =>
|
|
382
|
+
listBackgroundTasks({ surface: 'agent' }).map((task) => ({
|
|
383
|
+
taskId: task.task_id,
|
|
384
|
+
sessionId: task.sessionId || null,
|
|
385
|
+
running: task.status === 'running',
|
|
386
|
+
}));
|
|
387
|
+
|
|
274
388
|
// A task session idle with nothing pending must stay so this long before its
|
|
275
389
|
// latest answer is final: a finished agent's result can start the Lead's next
|
|
276
390
|
// turn right after (the desktop notifier's quiet period).
|
|
@@ -305,8 +419,9 @@ const LAST_USED_ROUTE_MAX_PAGES = 20;
|
|
|
305
419
|
const roomFrom = (originSessionId) => (roomMemberOfSession(originSessionId) ? { from: originSessionId } : {});
|
|
306
420
|
|
|
307
421
|
/** The route of `modelRef`: a provider/model[@effort…] ref (a superseded
|
|
308
|
-
* model runs on its successor)
|
|
309
|
-
|
|
422
|
+
* model runs on its successor), a preset name, else whatever `byName` makes
|
|
423
|
+
* of it (a catalog display name; it throws when it names none). */
|
|
424
|
+
async function configModelRoute(modelRef, byName) {
|
|
310
425
|
const [{ modelRouteFields, parseScheduleModelRef }, { migrateCodexRoute }, { loadConfig }] = await Promise.all([
|
|
311
426
|
import('../runtime/shared/schedule-model-ref.mjs'),
|
|
312
427
|
import('../runtime/agent/orchestrator/providers/openai-oauth-catalog-store.mjs'),
|
|
@@ -315,13 +430,12 @@ async function configModelRoute(modelRef) {
|
|
|
315
430
|
const ref = parseScheduleModelRef(modelRef);
|
|
316
431
|
if (ref && typeof ref === 'object') return modelRouteFields(migrateCodexRoute(ref));
|
|
317
432
|
const preset = loadConfig({ secrets: false }).presets?.find((p) => p.id === ref || p.name === ref);
|
|
318
|
-
|
|
319
|
-
return modelRouteFields(preset);
|
|
433
|
+
return preset ? modelRouteFields(preset) : byName(ref);
|
|
320
434
|
}
|
|
321
435
|
|
|
322
436
|
/** The Lead (Main) route of the config — what a desktop New task starts on.
|
|
323
437
|
* Null while none is chosen. */
|
|
324
|
-
async function configLeadRoute() {
|
|
438
|
+
export async function configLeadRoute() {
|
|
325
439
|
const [{ modelRouteFields }, { resolveRoute }, { loadConfig }] = await Promise.all([
|
|
326
440
|
import('../runtime/shared/schedule-model-ref.mjs'),
|
|
327
441
|
import('./boot/shared.mjs'),
|
|
@@ -352,8 +466,12 @@ async function lastUsedSessionRoute(sessionService) {
|
|
|
352
466
|
/** Wait for the final answer of the task turn `runId` of `sessionId` ended
|
|
353
467
|
* completed: the end of the latest turn since it (a Lead that handed work
|
|
354
468
|
* to agents ends its turn and answers in the turn their results wake),
|
|
355
|
-
* once
|
|
356
|
-
*
|
|
469
|
+
* once that chain has no turn running and no pending agent work
|
|
470
|
+
* (pendingWork of its agent tasks) for `settleMs`. Work of the session
|
|
471
|
+
* outside the chain (a turn the user typed, its agents) never holds it;
|
|
472
|
+
* without the provenance to tell (no stored turn, `ambiguous`) the whole
|
|
473
|
+
* session must be idle with none pending. `immediate` is runId's own end,
|
|
474
|
+
* when this process saw it.
|
|
357
475
|
* `finish(status, text)` runs once; `check` re-evaluates (call it on every
|
|
358
476
|
* state change of `runtime`), `stop` ends the wait. */
|
|
359
477
|
function followTaskToFinal(
|
|
@@ -364,10 +482,10 @@ function followTaskToFinal(
|
|
|
364
482
|
let pendingSince = 0;
|
|
365
483
|
// Agent work still pending `pendingCapMs` after the chain's latest turn
|
|
366
484
|
// ended: `progress(text)` reports that answer once, marked still in
|
|
367
|
-
// progress, and the wait for the final one goes on.
|
|
368
|
-
|
|
485
|
+
// progress, and the wait for the final one goes on. `latest`: the chain's
|
|
486
|
+
// latest turn (turnChain).
|
|
487
|
+
async function reportProgress(latest) {
|
|
369
488
|
if (progressed || typeof progress !== 'function') return;
|
|
370
|
-
const latest = latestTurnOutcome((await loadSession(sessionId))?.messages, runId);
|
|
371
489
|
let end = latest?.outcome && latest.outcome !== 'open' ? latest.outcome : null;
|
|
372
490
|
if (immediate && (!latest || latest.id === runId)) end = immediate;
|
|
373
491
|
if (!end) return;
|
|
@@ -383,6 +501,7 @@ function followTaskToFinal(
|
|
|
383
501
|
let candidateAt = 0;
|
|
384
502
|
let timer = null;
|
|
385
503
|
let wakes = 0;
|
|
504
|
+
let otherWakes = 0;
|
|
386
505
|
let wokeAt = 0;
|
|
387
506
|
const unwoken = (why) => {
|
|
388
507
|
log(`${sessionId} could not take its agents' results after the restart: ${why}`);
|
|
@@ -391,23 +510,32 @@ function followTaskToFinal(
|
|
|
391
510
|
// `wakeOwed` (a restarted watch): deliver the agent results queued for
|
|
392
511
|
// the idle session through the notification bus so a turn takes them, at
|
|
393
512
|
// most OWED_WAKE_TRIES times, a poll apart; null while that turn is owed.
|
|
394
|
-
|
|
513
|
+
// Every owed result is delivered, but only those of the chain's agent tasks
|
|
514
|
+
// (owedOf `taskIds`) hold the report or fail it; deliveries of the others
|
|
515
|
+
// alone have a bound of their own (`otherWakes`).
|
|
516
|
+
async function wakeForOwed(taskIds) {
|
|
395
517
|
const owed = await owedResults(sessionId);
|
|
396
518
|
if (!owed.length) return undefined;
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
try {
|
|
403
|
-
if (typeof deliverCompletion === 'function') {
|
|
404
|
-
const results = await Promise.all(owed.map(({ text, meta }) => deliverCompletion(sessionId, text, meta)));
|
|
405
|
-
delivered = results.some((result) => result === true);
|
|
406
|
-
}
|
|
407
|
-
} catch (error) {
|
|
408
|
-
return unwoken(error?.message || error);
|
|
519
|
+
const chain = new Set(owedOf(owed, taskIds));
|
|
520
|
+
const hold = chain.size ? null : undefined;
|
|
521
|
+
if (Date.now() - wokeAt < pollMs) return hold;
|
|
522
|
+
if (chain.size ? wakes >= OWED_WAKE_TRIES : otherWakes >= OWED_WAKE_TRIES) {
|
|
523
|
+
return chain.size ? unwoken(`still not taken after ${wakes} deliveries`) : undefined;
|
|
409
524
|
}
|
|
410
|
-
|
|
525
|
+
if (chain.size) wakes += 1;
|
|
526
|
+
else otherWakes += 1;
|
|
527
|
+
wokeAt = Date.now();
|
|
528
|
+
const outcomes = await Promise.allSettled(
|
|
529
|
+
owed.map(
|
|
530
|
+
async ({ text, meta }) =>
|
|
531
|
+
typeof deliverCompletion === 'function' && (await deliverCompletion(sessionId, text, meta)) === true
|
|
532
|
+
)
|
|
533
|
+
);
|
|
534
|
+
if (!chain.size) return undefined;
|
|
535
|
+
const ofChain = outcomes.filter((_, index) => chain.has(owed[index]));
|
|
536
|
+
const failure = ofChain.find((outcome) => outcome.status === 'rejected');
|
|
537
|
+
if (failure) return unwoken(failure.reason?.message || failure.reason);
|
|
538
|
+
return ofChain.some((outcome) => outcome.value === true) ? null : unwoken('no session runtime took the delivery');
|
|
411
539
|
}
|
|
412
540
|
const arm = (ms) => {
|
|
413
541
|
clearTimeout(timer);
|
|
@@ -421,21 +549,25 @@ function followTaskToFinal(
|
|
|
421
549
|
const failed = (error) => log(`following ${sessionId} to its final answer failed: ${error?.message || error}`);
|
|
422
550
|
// The end to report now, or null while the task still works.
|
|
423
551
|
async function ready() {
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
552
|
+
const stored = await loadSession(sessionId);
|
|
553
|
+
const latest = turnChain(stored?.messages, runId);
|
|
554
|
+
// The chain's agent tasks; null (the whole session) without its provenance.
|
|
555
|
+
const taskIds = latest && !latest.ambiguous ? latest.taskIds : null;
|
|
556
|
+
// A busy session may run the chain's turn unless that turn is provably another's.
|
|
557
|
+
const busy = Boolean(runtime?.getState?.()?.busy);
|
|
558
|
+
if (busy && !(taskIds && latest.busyIsOthers)) return null;
|
|
559
|
+
if (wakeOwed && !busy) {
|
|
560
|
+
const woke = await wakeForOwed(taskIds);
|
|
427
561
|
if (woke !== undefined) return woke;
|
|
428
562
|
}
|
|
429
|
-
if (await pendingWork(sessionId)) {
|
|
430
|
-
await reportProgress();
|
|
563
|
+
if (await pendingWork(sessionId, { taskIds })) {
|
|
564
|
+
await reportProgress(latest);
|
|
431
565
|
return null;
|
|
432
566
|
}
|
|
433
|
-
const stored = await loadSession(sessionId);
|
|
434
567
|
if (!stored && !immediate) return { key: 'gone', status: 'cancelled', text: GONE_TEXT };
|
|
435
|
-
const latest = latestTurnOutcome(stored?.messages, runId);
|
|
436
568
|
// Provenance the chain needs is missing: its answer may not be final.
|
|
437
569
|
if (latest?.ambiguous) {
|
|
438
|
-
await reportProgress();
|
|
570
|
+
await reportProgress(latest);
|
|
439
571
|
return null;
|
|
440
572
|
}
|
|
441
573
|
if (immediate && (!latest || latest.id === runId)) return { key: runId, ...immediate };
|
|
@@ -635,9 +767,7 @@ async function rearmTaskWatch(
|
|
|
635
767
|
...(watch.hop > 0 ? { hop: watch.hop } : {}),
|
|
636
768
|
...roomFrom(watch.homeSessionId),
|
|
637
769
|
watchId: watch.runId,
|
|
638
|
-
...(watch.textOnly || text === GONE_TEXT || text === LOST_TEXT
|
|
639
|
-
? {}
|
|
640
|
-
: await artifactFields(sessionId, turnId)),
|
|
770
|
+
...(watch.textOnly || text === GONE_TEXT || text === LOST_TEXT ? {} : await artifactFields(sessionId, turnId)),
|
|
641
771
|
});
|
|
642
772
|
if (!(await loadSession(sessionId))) return finish('cancelled', GONE_TEXT);
|
|
643
773
|
let runtime;
|
|
@@ -737,6 +867,8 @@ export function createAgentTaskSessions({
|
|
|
737
867
|
newTaskDefault = async () =>
|
|
738
868
|
(await import('../runtime/agent/orchestrator/config.mjs')).loadConfig({ secrets: false }).worktree?.newTaskDefault,
|
|
739
869
|
modelRoute = configModelRoute,
|
|
870
|
+
// The model menu's catalog rows, read through the calling session's runtime.
|
|
871
|
+
modelCatalog = (sessionId) => readModelCatalog(service(), sessionId),
|
|
740
872
|
// The Lead (Main) route of the config — what a desktop New task starts on.
|
|
741
873
|
leadRoute = configLeadRoute,
|
|
742
874
|
// The model the user last used in a coding session.
|
|
@@ -796,18 +928,62 @@ export function createAgentTaskSessions({
|
|
|
796
928
|
follow
|
|
797
929
|
);
|
|
798
930
|
|
|
931
|
+
// Sends one turn answers settle together with the same `detail` object: a
|
|
932
|
+
// send steered into the turn while it ran, or queued and taken as one batch.
|
|
933
|
+
// That turn reports once, closing every one of them.
|
|
934
|
+
const settledTurns = new WeakMap();
|
|
935
|
+
let sendCount = 0;
|
|
936
|
+
|
|
937
|
+
/** Add send `member` (`{ seq, id, unwatch, settle }`) to the settlement
|
|
938
|
+
* `detail` of its turn, per reporting conversation. The sends of one turn
|
|
939
|
+
* settle in the same tick; after it the earliest (the turn's own, whose
|
|
940
|
+
* record holds the whole turn) settles with all their ids and the others
|
|
941
|
+
* stand down. */
|
|
942
|
+
function joinSettlement(detail, origin, member) {
|
|
943
|
+
const key = `${origin.agentId}\0${origin.sessionId}`;
|
|
944
|
+
let groups = settledTurns.get(detail);
|
|
945
|
+
if (!groups) {
|
|
946
|
+
groups = new Map();
|
|
947
|
+
settledTurns.set(detail, groups);
|
|
948
|
+
}
|
|
949
|
+
let members = groups.get(key);
|
|
950
|
+
if (!members) {
|
|
951
|
+
members = [];
|
|
952
|
+
groups.set(key, members);
|
|
953
|
+
const settling = members;
|
|
954
|
+
void Promise.resolve()
|
|
955
|
+
.then(() => {
|
|
956
|
+
const [first, ...others] = settling.sort((a, b) => a.seq - b.seq);
|
|
957
|
+
for (const other of others) other.unwatch();
|
|
958
|
+
first.settle(settling.map((entry) => entry.id));
|
|
959
|
+
})
|
|
960
|
+
.catch((error) => log(`settling the sends of one turn failed: ${error?.message || error}`));
|
|
961
|
+
}
|
|
962
|
+
members.push(member);
|
|
963
|
+
}
|
|
964
|
+
|
|
799
965
|
/** Submit `prompt` as the origin agent's turn; its end (and a wait for the
|
|
800
966
|
* user in it) reports to the agent's home. Resolves on intake. `submit`
|
|
801
967
|
* takes the turn in another way than a prompt to `sessionId` (a message
|
|
802
968
|
* into a group room, whose member answers in `sessionId`): given `{ id,
|
|
803
969
|
* onSettled }`, it resolves `{ accepted }`. */
|
|
804
|
-
async function agentTurn(
|
|
970
|
+
async function agentTurn(
|
|
971
|
+
sessionId,
|
|
972
|
+
prompt,
|
|
973
|
+
origin,
|
|
974
|
+
id = `atask_${randomUUID()}`,
|
|
975
|
+
beforeReport = null,
|
|
976
|
+
submit = null
|
|
977
|
+
) {
|
|
805
978
|
const sessions = service();
|
|
806
979
|
const target = origin.target === 'session' ? { target: 'session' } : {};
|
|
807
980
|
const { notify, noReport } = await watchTurn({ watches, owner: processToken, log }, sessionId, origin, id);
|
|
808
981
|
const dropWatch = () =>
|
|
809
982
|
watches.remove(id).catch((error) => log(`dropping the watch of ${sessionId} failed: ${error?.message || error}`));
|
|
810
983
|
let runtime = null;
|
|
984
|
+
// The sends this turn's end closes: this one, plus any that its turn
|
|
985
|
+
// answered too (joinSettlement).
|
|
986
|
+
let sendIds = [id];
|
|
811
987
|
// A report that cannot be queued is retried in this process; when every try
|
|
812
988
|
// fails the durable watch stays and restart recovery delivers it (the queue
|
|
813
989
|
// dedupes by runId, so a retry never posts twice).
|
|
@@ -828,7 +1004,11 @@ export function createAgentTaskSessions({
|
|
|
828
1004
|
// may receive them (reportOrigin `textOnly`).
|
|
829
1005
|
...(status === 'blocked' || status === 'working'
|
|
830
1006
|
? {}
|
|
831
|
-
: {
|
|
1007
|
+
: {
|
|
1008
|
+
watchId: id,
|
|
1009
|
+
...(sendIds.length > 1 ? { watchIds: sendIds } : {}),
|
|
1010
|
+
...(origin.textOnly ? {} : await artifactFields(sessionId, id, runtime)),
|
|
1011
|
+
}),
|
|
832
1012
|
})
|
|
833
1013
|
)
|
|
834
1014
|
.catch((error) => log(`report of ${sessionId} failed: ${error?.message || error}`));
|
|
@@ -836,6 +1016,31 @@ export function createAgentTaskSessions({
|
|
|
836
1016
|
// Set once the turn ended completed: the report waits for the final answer.
|
|
837
1017
|
let follow = null;
|
|
838
1018
|
let result;
|
|
1019
|
+
/** This send ends the turn `detail` for the sends `answered` (joinSettlement). */
|
|
1020
|
+
const settleTurn = (detail, answered) => {
|
|
1021
|
+
sendIds = answered;
|
|
1022
|
+
const status = turnStatus(detail);
|
|
1023
|
+
const text = detail?.result?.content || detail?.error || '';
|
|
1024
|
+
// A failed or cancelled turn reports at once; a completed one
|
|
1025
|
+
// reports the task's final answer (followToFinal).
|
|
1026
|
+
if (status !== 'completed') {
|
|
1027
|
+
unwatch();
|
|
1028
|
+
void report(status, text);
|
|
1029
|
+
return;
|
|
1030
|
+
}
|
|
1031
|
+
follow = followToFinal({
|
|
1032
|
+
sessionId,
|
|
1033
|
+
runId: id,
|
|
1034
|
+
runtime,
|
|
1035
|
+
immediate: { status, text },
|
|
1036
|
+
finish: async (endStatus, endText) => {
|
|
1037
|
+
unwatch();
|
|
1038
|
+
await report(endStatus, endText);
|
|
1039
|
+
},
|
|
1040
|
+
progress: (progressText) => report('working', progressText),
|
|
1041
|
+
});
|
|
1042
|
+
follow.check();
|
|
1043
|
+
};
|
|
839
1044
|
try {
|
|
840
1045
|
if (notify) {
|
|
841
1046
|
runtime = await sessions.materializeSession(sessionId);
|
|
@@ -854,6 +1059,9 @@ export function createAgentTaskSessions({
|
|
|
854
1059
|
? wrapAgentMessage(prompt, { name: origin.envelopeName, agentId: origin.agentId })
|
|
855
1060
|
: prompt;
|
|
856
1061
|
const take = submit ?? ((options) => sessions.submitSession({ sessionId, prompt: sent, options }));
|
|
1062
|
+
// Submission order: the earliest of the sends one turn answers reports for them.
|
|
1063
|
+
sendCount += 1;
|
|
1064
|
+
const seq = sendCount;
|
|
857
1065
|
result = await take({
|
|
858
1066
|
id,
|
|
859
1067
|
mode: 'prompt',
|
|
@@ -864,6 +1072,8 @@ export function createAgentTaskSessions({
|
|
|
864
1072
|
sender: 'lead',
|
|
865
1073
|
...(origin.envelopeName ? { fromAgent: origin.agentId } : {}),
|
|
866
1074
|
...(origin.hop > 0 ? { hop: origin.hop } : {}),
|
|
1075
|
+
// The bots that handed this work on, the sender last (bot-hops.mjs).
|
|
1076
|
+
...(origin.botChain?.length ? { botChain: origin.botChain } : {}),
|
|
867
1077
|
},
|
|
868
1078
|
...(notify
|
|
869
1079
|
? {
|
|
@@ -871,29 +1081,13 @@ export function createAgentTaskSessions({
|
|
|
871
1081
|
// id (one turn for several sends, or a fresh turn for one cut
|
|
872
1082
|
// short): the watch follows that turn, recorded before it runs.
|
|
873
1083
|
follow: (submissionId) => watches.follow?.(id, submissionId),
|
|
874
|
-
onSettled: (detail) =>
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
void report(status, text);
|
|
882
|
-
return;
|
|
883
|
-
}
|
|
884
|
-
follow = followToFinal({
|
|
885
|
-
sessionId,
|
|
886
|
-
runId: id,
|
|
887
|
-
runtime,
|
|
888
|
-
immediate: { status, text },
|
|
889
|
-
finish: async (endStatus, endText) => {
|
|
890
|
-
unwatch();
|
|
891
|
-
await report(endStatus, endText);
|
|
892
|
-
},
|
|
893
|
-
progress: (progressText) => report('working', progressText),
|
|
894
|
-
});
|
|
895
|
-
follow.check();
|
|
896
|
-
},
|
|
1084
|
+
onSettled: (detail) =>
|
|
1085
|
+
joinSettlement(detail, origin, {
|
|
1086
|
+
seq,
|
|
1087
|
+
id,
|
|
1088
|
+
unwatch: () => unwatch(),
|
|
1089
|
+
settle: (answered) => settleTurn(detail, answered),
|
|
1090
|
+
}),
|
|
897
1091
|
}
|
|
898
1092
|
: {}),
|
|
899
1093
|
});
|
|
@@ -909,6 +1103,8 @@ export function createAgentTaskSessions({
|
|
|
909
1103
|
}
|
|
910
1104
|
return {
|
|
911
1105
|
accepted,
|
|
1106
|
+
// Names this send; its terminal report carries it back (routine.sendId).
|
|
1107
|
+
...(accepted && notify ? { sendId: id } : {}),
|
|
912
1108
|
reportsBack: accepted && notify,
|
|
913
1109
|
...(accepted && !notify ? { reportsBackReason: noReport } : {}),
|
|
914
1110
|
};
|
|
@@ -954,7 +1150,15 @@ export function createAgentTaskSessions({
|
|
|
954
1150
|
* caller is refused and the New task default keeps it in the Project folder. */
|
|
955
1151
|
async function create(
|
|
956
1152
|
args = {},
|
|
957
|
-
{
|
|
1153
|
+
{
|
|
1154
|
+
callerSessionId,
|
|
1155
|
+
callerAgentId,
|
|
1156
|
+
callerCwd,
|
|
1157
|
+
callerHop = 0,
|
|
1158
|
+
callerBotChain = [],
|
|
1159
|
+
callerScope,
|
|
1160
|
+
attachmentParts = [],
|
|
1161
|
+
} = {}
|
|
958
1162
|
) {
|
|
959
1163
|
if (!callerAgentId) throw new Error('session: create is only for an agent conversation');
|
|
960
1164
|
const brief = typeof args.brief === 'string' ? args.brief.trim() : '';
|
|
@@ -984,10 +1188,18 @@ export function createAgentTaskSessions({
|
|
|
984
1188
|
// A task session runs in coding mode under Lead: an explicit model wins,
|
|
985
1189
|
// else the Lead route, else the last-used model. Never a model-less
|
|
986
1190
|
// session — the composer would show no model and its first turn would fail.
|
|
987
|
-
|
|
988
|
-
|
|
1191
|
+
// The catalog is read only when a display name or an effort needs it.
|
|
1192
|
+
let catalog = null;
|
|
1193
|
+
const catalogRows = async () => (catalog ??= await modelCatalog(callerSessionId));
|
|
1194
|
+
let route = clean(args.model)
|
|
1195
|
+
? await modelRoute(clean(args.model), async (text) =>
|
|
1196
|
+
routeByDisplayName(text, await catalogRows(), args.effort !== undefined)
|
|
1197
|
+
)
|
|
989
1198
|
: (await leadRoute()) || (await lastUsedRoute(service()));
|
|
990
1199
|
if (!route) throw new Error('session: no model to start the task session on. Choose a Lead model first.');
|
|
1200
|
+
// An explicit effort is the setting the menu's effort pick makes (session.effort,
|
|
1201
|
+
// Auto effort off); it wins over one a model ref or name carried.
|
|
1202
|
+
if (args.effort !== undefined) route = withEffort(route, args.effort, await catalogRows());
|
|
991
1203
|
const sessions = service();
|
|
992
1204
|
// Where it starts: the `worktree` option, else the user's New task default
|
|
993
1205
|
// for a caller that may provision one. Decided before the session exists,
|
|
@@ -1052,10 +1264,11 @@ export function createAgentTaskSessions({
|
|
|
1052
1264
|
});
|
|
1053
1265
|
// The brief carries the files the agent forwarded (workspace-tools.mjs).
|
|
1054
1266
|
const prompt = handoffContent(brief, attachmentParts);
|
|
1267
|
+
const botChain = botChainOf(callerBotChain);
|
|
1055
1268
|
const { accepted, reportsBack, reportsBackReason } = await agentTurn(
|
|
1056
1269
|
sessionId,
|
|
1057
1270
|
prompt,
|
|
1058
|
-
callerHop > 0 ? {
|
|
1271
|
+
{ ...taskOrigin, ...(callerHop > 0 ? { hop: callerHop } : {}), ...(botChain.length ? { botChain } : {}) },
|
|
1059
1272
|
requestId,
|
|
1060
1273
|
ready
|
|
1061
1274
|
);
|
|
@@ -8,7 +8,7 @@ import { createProviderReadiness } from '../provider-readiness.mjs';
|
|
|
8
8
|
import { createRoutePreparationGate } from '../route-preparation.mjs';
|
|
9
9
|
import { createRuntimeFeatureGates } from '../runtime-feature-gates.mjs';
|
|
10
10
|
import { createLazyRuntimeModules } from '../runtime-modules.mjs';
|
|
11
|
-
import {
|
|
11
|
+
import { sessionToolProfile } from '../../runtime/agent/orchestrator/runtime-core/tool-profile.mjs';
|
|
12
12
|
|
|
13
13
|
// The boot record: `rt` (shared mutable runtime state, promoted from closure
|
|
14
14
|
// `let`s so extracted modules read/write live values through one reference),
|
|
@@ -18,13 +18,14 @@ import { normalizeToolProfile } from '../../runtime/agent/orchestrator/runtime-c
|
|
|
18
18
|
export function beginBoot(params) {
|
|
19
19
|
const { provider, model, toolMode, cwd, toolProfile, approvalMode, disallowDelegation } = params;
|
|
20
20
|
const rt = {};
|
|
21
|
-
rt.toolProfile = normalizeToolProfile(toolProfile);
|
|
22
21
|
rt.approvalMode = approvalMode === 'implicit' ? 'implicit' : null;
|
|
23
22
|
rt.disallowDelegation = disallowDelegation === true;
|
|
24
23
|
rt.mcpScopeId = randomUUID();
|
|
25
24
|
rt.desktopSession = params.desktopSession;
|
|
26
25
|
rt.sessionProfile =
|
|
27
26
|
params.sessionProfile && typeof params.sessionProfile === 'object' ? { ...params.sessionProfile } : null;
|
|
27
|
+
// A heartbeat run's session is fenced to its few tools (tool-profile.mjs).
|
|
28
|
+
rt.toolProfile = sessionToolProfile({ toolProfile, sessionProfile: rt.sessionProfile });
|
|
28
29
|
bootProfile('session-runtime:start', { provider, model, toolMode, cwd });
|
|
29
30
|
// Last assistant text handed to the transcript writer (via onAssistantText),
|
|
30
31
|
// so the post-turn final-content append can skip an exact duplicate.
|
|
@@ -22,6 +22,7 @@ import { TOOL_DEFS as MEDIA_TOOL_DEFS } from '../../runtime/media/tool-defs.mjs'
|
|
|
22
22
|
import { TOOL_DEFS as TIDY_TOOL_DEFS } from '../../runtime/tidy/tool-defs.mjs';
|
|
23
23
|
import { TOOL_DEFS as VISUALIZE_BRIDGE_TOOL_DEFS } from '../../runtime/visualize-bridge/tool-defs.mjs';
|
|
24
24
|
import { ASK_USER_TOOL_DEFS } from '../../runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs';
|
|
25
|
+
import { REACTION_TOOL_DEFS } from '../../runtime/agent/orchestrator/runtime-core/reaction-tool-defs.mjs';
|
|
25
26
|
import { SETUP_TOOL_DEFS } from '../setup-tool/tool-defs.mjs';
|
|
26
27
|
import { createSetupToolExecutor } from '../setup-tool/executor.mjs';
|
|
27
28
|
import { bootProfile } from '../boot-profile.mjs';
|
|
@@ -167,6 +168,7 @@ function wireToolSurface(boot) {
|
|
|
167
168
|
visualizeToolDefs: VISUALIZE_BRIDGE_TOOL_DEFS,
|
|
168
169
|
setupToolDefs: SETUP_TOOL_DEFS,
|
|
169
170
|
askUserToolDefs: ASK_USER_TOOL_DEFS,
|
|
171
|
+
reactionToolDefs: REACTION_TOOL_DEFS,
|
|
170
172
|
goalTools: goalRuntime.tools,
|
|
171
173
|
agentTools: agentTool.tools,
|
|
172
174
|
skillToolEnabled: !envFlag('MIXDOG_DISABLE_SKILLS'),
|