mixdog 1.2.6 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSES/simple-icons-CC0-1.0.txt +30 -0
- package/NOTICE.md +19 -0
- package/package.json +4 -1
- package/scripts/check-model-recommendations.mjs +89 -0
- package/scripts/gate-local.mjs +7 -0
- package/scripts/lib/isolated-test-env.mjs +16 -0
- package/src/agents/reviewer/AGENT.md +4 -2
- package/src/agents/reviewer/agent.json +1 -1
- package/src/defaults/shipped-history.json +7 -0
- package/src/defaults/skills/browser-use/SKILL.md +5 -0
- package/src/defaults/skills/memory-management/SKILL.md +4 -1
- package/src/defaults/skills/setup/references/actions.md +11 -4
- package/src/defaults/skills/setup-guide/SKILL.md +175 -0
- package/src/defaults/skills/setup-guide/model-recommendations.json +333 -0
- package/src/lib/rules-builder.cjs +55 -6
- package/src/rules/agent/AGENT.md +4 -0
- package/src/rules/agent-mode/AGENT_MODE.md +24 -0
- package/src/rules/agent-mode/MIXY.md +21 -0
- package/src/rules/agent-mode/ROUTINE.md +6 -0
- package/src/rules/lead/LEAD.md +7 -3
- package/src/rules/shared/70-delivery.md +3 -0
- package/src/runtime/agent/orchestrator/agent-runtime/room-supervisor-completion.mjs +86 -0
- package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +77 -14
- package/src/runtime/agent/orchestrator/context/role-instructions.mjs +182 -13
- package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +4 -2
- package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +215 -0
- package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +5 -4
- package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +10 -1
- package/src/runtime/agent/orchestrator/mcp/client.mjs +115 -11
- package/src/runtime/agent/orchestrator/mcp/oauth-account.mjs +97 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-client.mjs +49 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-credentials.mjs +59 -9
- package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +32 -7
- package/src/runtime/agent/orchestrator/mcp/oauth-provider.mjs +45 -6
- package/src/runtime/agent/orchestrator/mcp/oauth.mjs +195 -6
- package/src/runtime/agent/orchestrator/mcp/server-connectors.mjs +17 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-login.mjs +10 -3
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-tokens.mjs +4 -0
- package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/openai-error-contract.mjs +17 -12
- package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +10 -0
- package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +96 -1
- package/src/runtime/agent/orchestrator/runtime-core/context-status-shape.mjs +4 -0
- package/src/runtime/agent/orchestrator/runtime-core/extension-scopes.mjs +6 -4
- package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +12 -0
- package/src/runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs +1 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +12 -6
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +32 -5
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +10 -4
- package/src/runtime/agent/orchestrator/runtime-core/workflow/agents.mjs +4 -1
- package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +50 -20
- package/src/runtime/agent/orchestrator/runtime-core/workflow-ids.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/agent-home.d.mts +4 -0
- package/src/runtime/agent/orchestrator/session/agent-home.mjs +33 -0
- package/src/runtime/agent/orchestrator/session/agent-mode.mjs +62 -0
- package/src/runtime/agent/orchestrator/session/agent-room-supervisor.d.mts +31 -0
- package/src/runtime/agent/orchestrator/session/agent-room-supervisor.mjs +241 -0
- package/src/runtime/agent/orchestrator/session/agent-room.d.mts +162 -0
- package/src/runtime/agent/orchestrator/session/agent-room.mjs +641 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +7 -1
- package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +6 -2
- package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +165 -0
- package/src/runtime/agent/orchestrator/session/human-gate/held-message.mjs +95 -0
- package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +221 -0
- package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +195 -0
- package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +167 -0
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +10 -0
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +16 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +74 -15
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +9 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +5 -4
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +13 -3
- package/src/runtime/agent/orchestrator/session/loop/usage.mjs +7 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager/pending-lifecycle-epoch.mjs +0 -8
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +44 -16
- package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +22 -5
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +39 -4
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +27 -5
- package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +55 -8
- package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +16 -0
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +3 -2
- package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/manager/workflow-switch-notice.mjs +58 -0
- package/src/runtime/agent/orchestrator/session/mixy-name.mjs +24 -0
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +32 -5
- package/src/runtime/agent/orchestrator/session/routine-result.d.mts +70 -0
- package/src/runtime/agent/orchestrator/session/routine-result.mjs +277 -0
- package/src/runtime/agent/orchestrator/session/store/sweep/sweep-row.mjs +8 -0
- package/src/runtime/agent/orchestrator/session/store-record-strict-scan.mjs +28 -9
- package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +16 -2
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +44 -12
- package/src/runtime/agent/orchestrator/session/store-summary-last-message.mjs +46 -0
- package/src/runtime/agent/orchestrator/session/store-summary-visibility.mjs +0 -4
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +44 -1
- package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +12 -0
- package/src/runtime/agent/orchestrator/tools/builtin/native-search-client.mjs +113 -5
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +4 -0
- package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +25 -1
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promote-job.mjs +8 -0
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +6 -0
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +3 -0
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +75 -57
- package/src/runtime/browser-bridge/client.mjs +24 -0
- package/src/runtime/channels/lib/scheduler.mjs +40 -12
- package/src/runtime/channels/lib/schedules-db.mjs +71 -12
- package/src/runtime/channels/lib/webhook/session-dispatch.mjs +36 -16
- package/src/runtime/channels/lib/webhook.mjs +0 -1
- package/src/runtime/channels/lib/webhooks-db.mjs +100 -10
- package/src/runtime/channels/lib/worker-main.mjs +17 -20
- package/src/runtime/computer-bridge/client.mjs +28 -1
- package/src/runtime/connectors/gmail-app-password.mjs +690 -0
- package/src/runtime/connectors/google-mcp.mjs +446 -0
- package/src/runtime/memory/lib/core-memory-access.mjs +69 -0
- package/src/runtime/memory/lib/core-memory-file.mjs +16 -8
- package/src/runtime/memory/lib/core-memory-index.mjs +15 -9
- package/src/runtime/memory/lib/core-memory-management.mjs +10 -4
- package/src/runtime/memory/lib/core-memory-store.mjs +53 -30
- package/src/runtime/memory/lib/core-memory-uniqueness.mjs +10 -6
- package/src/runtime/memory/lib/http-router/session-routes.mjs +3 -2
- package/src/runtime/memory/lib/memory-action-handlers/core-actions.mjs +44 -14
- package/src/runtime/memory/lib/memory-schema/core-and-meta.mjs +11 -0
- package/src/runtime/memory/lib/memory.mjs +7 -1
- package/src/runtime/memory/lib/query-core-recall.mjs +9 -1
- package/src/runtime/memory/lib/query-hybrid-search.mjs +2 -0
- package/src/runtime/memory/lib/tool-call-handler.mjs +2 -0
- package/src/runtime/memory/tool-defs.mjs +5 -0
- package/src/runtime/shared/agent-avatar-sprites.mjs +445 -0
- package/src/runtime/shared/agent-avatar.d.mts +64 -0
- package/src/runtime/shared/agent-avatar.mjs +320 -0
- package/src/runtime/shared/agent-route-config.d.mts +3 -0
- package/src/runtime/shared/agent-route-config.mjs +16 -0
- package/src/runtime/shared/atomic-file.mjs +14 -1
- package/src/runtime/shared/config.mjs +8 -1
- package/src/runtime/shared/connector-accounts.mjs +15 -0
- package/src/runtime/shared/connector-catalog.d.mts +52 -0
- package/src/runtime/shared/connector-catalog.mjs +230 -0
- package/src/runtime/shared/connector-triggers.d.mts +3 -0
- package/src/runtime/shared/connector-triggers.mjs +43 -0
- package/src/runtime/shared/google-oauth-client.mjs +24 -0
- package/src/runtime/shared/held-request-host.d.mts +12 -0
- package/src/runtime/shared/held-request-host.mjs +71 -0
- package/src/runtime/shared/plain-preview.d.mts +1 -0
- package/src/runtime/shared/plain-preview.mjs +28 -0
- package/src/runtime/shared/routine-limits.d.mts +11 -0
- package/src/runtime/shared/routine-limits.mjs +21 -0
- package/src/runtime/shared/routine-trigger.d.mts +10 -0
- package/src/runtime/shared/routine-trigger.mjs +44 -0
- package/src/runtime/shared/schedule-model-ref.mjs +9 -10
- package/src/runtime/shared/slow-span.mjs +4 -12
- package/src/runtime/shared/statusline/statusline-segments.mjs +13 -1
- package/src/runtime/shared/transcript-metadata.mjs +10 -0
- package/src/runtime/shared/turn-changes.mjs +0 -8
- package/src/runtime/shared/usage-readout-totals.mjs +56 -0
- package/src/runtime/shared/user-data-guard.mjs +114 -36
- package/src/session-runtime/agent-home-delivery.mjs +459 -0
- package/src/session-runtime/agent-profile-changes.mjs +21 -0
- package/src/session-runtime/agent-room-delivery.mjs +92 -0
- package/src/session-runtime/agent-task-sessions.mjs +200 -0
- package/src/session-runtime/boot/apis.mjs +50 -18
- package/src/session-runtime/boot/facade.mjs +5 -8
- package/src/session-runtime/boot/infrastructure.mjs +28 -17
- package/src/session-runtime/boot/tools.mjs +3 -0
- package/src/session-runtime/channel-config-api.mjs +53 -5
- package/src/session-runtime/cwd-plugins/core-memory-context.mjs +36 -20
- package/src/session-runtime/cwd-plugins/plugin-status.mjs +6 -1
- package/src/session-runtime/held-requests.mjs +228 -0
- package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +121 -0
- package/src/session-runtime/internal-tool-executor/feature-tools.mjs +5 -1
- package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +31 -2
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +31 -2
- package/src/session-runtime/internal-tool-executor.mjs +2 -0
- package/src/session-runtime/lifecycle/session-catalog.mjs +9 -0
- package/src/session-runtime/lifecycle/session-switch/resume.mjs +4 -0
- package/src/session-runtime/lifecycle/shared.mjs +6 -0
- package/src/session-runtime/mcp-glue/server-catalog.mjs +8 -0
- package/src/session-runtime/mcp-glue/server-input.mjs +3 -0
- package/src/session-runtime/mcp-oauth-login.mjs +112 -15
- package/src/session-runtime/model-route/set-route.mjs +14 -3
- package/src/session-runtime/owned-automation-run.mjs +229 -0
- package/src/session-runtime/resource-api.mjs +5 -0
- package/src/session-runtime/resource-mcp-api.mjs +107 -13
- package/src/session-runtime/resource-skills-api.mjs +12 -0
- package/src/session-runtime/room-member-gate.mjs +48 -0
- package/src/session-runtime/routine-home-turn.mjs +156 -0
- package/src/session-runtime/routine-owner.mjs +48 -0
- package/src/session-runtime/schedule-session-run.mjs +30 -64
- package/src/session-runtime/services/agent-tool/helpers.mjs +10 -0
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-plan.mjs +19 -4
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +2 -0
- package/src/session-runtime/services/channel-admin.mjs +93 -24
- package/src/session-runtime/services/memory-access.mjs +37 -0
- package/src/session-runtime/services/provider-admin.mjs +74 -39
- package/src/session-runtime/services/schedule-access.mjs +91 -0
- package/src/session-runtime/session-create/session-options.mjs +34 -7
- package/src/session-runtime/session-hooks.mjs +16 -8
- package/src/session-runtime/setup-tool/executor.mjs +68 -6
- package/src/session-runtime/setup-tool/extended-actions.mjs +72 -14
- package/src/session-runtime/setup-tool/settings-contract.mjs +8 -6
- package/src/session-runtime/setup-tool/tool-defs.mjs +8 -0
- package/src/session-runtime/skills-api.mjs +16 -1
- package/src/session-runtime/tool-policy-refresh.mjs +238 -29
- package/src/session-runtime/turn/session-ops.mjs +8 -1
- package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +10 -2
- package/src/session-runtime/turn/turn-run/turn-prepare.mjs +3 -0
- package/src/session-runtime/webhook-session-run.mjs +49 -67
- package/src/session-runtime/workflow-agents-api/agent-connectors.mjs +239 -0
- package/src/session-runtime/workflow-agents-api/agent-editor.mjs +556 -22
- package/src/session-runtime/workflow-agents-api/workflow-packs.mjs +11 -1
- package/src/session-runtime/workflow-agents-api.mjs +4 -1
- package/src/standalone/daemon-agent-control.mjs +1 -8
- package/src/standalone/daemon-session-control.mjs +11 -2
- package/src/standalone/daemon.mjs +18 -4
- package/src/standalone/session-protocol.mjs +27 -0
- package/src/standalone/session-service/session-calls/session-turns.mjs +9 -0
- package/src/standalone/session-service.mjs +19 -0
- package/src/tui/app/slash-commands.mjs +20 -0
- package/src/tui/dist/index.mjs +164 -86
- package/src/tui/session/context-state.mjs +6 -1
- package/src/tui/session/notice-surface.mjs +35 -4
- package/src/tui/session/queue-helpers.mjs +2 -1
- package/src/tui/session/session-api/extensions.mjs +4 -0
- package/src/tui/session/session-api/intake/submission.mjs +5 -0
- package/src/tui/session/session-api/integrations.mjs +8 -0
- package/src/tui/session/session-api/lifecycle/session-reset.mjs +13 -3
- package/src/tui/session/session-api/lifecycle.mjs +17 -1
- package/src/tui/session/session-api/routes.mjs +48 -3
- package/src/tui/session/session-flow/drain.mjs +12 -3
- package/src/tui/session/session-flow/queue/entry-shape.mjs +2 -1
- package/src/tui/session/session-flow/queue/take-entries.mjs +3 -2
- package/src/tui/session/session-flow/steering.mjs +58 -24
- package/src/tui/session/tool-approval.mjs +26 -5
- package/src/tui/session/transcript-intake.mjs +6 -2
- package/src/tui/session/transcript-spill.mjs +5 -1
- package/src/tui/session/tui-steering-persist.mjs +65 -42
- package/src/tui/session/turn-live-call.mjs +56 -0
- package/src/tui/session/turn.mjs +22 -6
- package/src/session-runtime/automation-agents.mjs +0 -94
- package/src/session-runtime/automation-workflow.mjs +0 -59
|
@@ -31,12 +31,43 @@ export function createNoticeSurface({ getState, set, isDisposed, pushItem, repla
|
|
|
31
31
|
return id;
|
|
32
32
|
};
|
|
33
33
|
const pushNotice = (text, tone = 'info', options = {}) => {
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
// A routine result is a finished sentence, never a raw error to polish;
|
|
35
|
+
// a group-room row is someone's words, kept verbatim (a pass has none).
|
|
36
|
+
const value =
|
|
37
|
+
options.routine || options.room || options.heldRequest ? String(text ?? '').trim() : polishNoticeText(text);
|
|
38
|
+
if (!value && !options.room) return null;
|
|
36
39
|
const forceTranscript = options.transcript === true;
|
|
37
40
|
if (!forceTranscript) return pushToast(value, tone, options.ttlMs, options);
|
|
38
|
-
const
|
|
39
|
-
|
|
41
|
+
const userRow = options.room?.kind === 'user';
|
|
42
|
+
// A room user row keeps its message id (the sender's submission id), as a
|
|
43
|
+
// turn's own prompt row keeps its submission id.
|
|
44
|
+
const id = (userRow && options.messageId) || nextId();
|
|
45
|
+
// One row per messageId: a routine's live-only run row (running, delivery
|
|
46
|
+
// failed) gives way to the row that follows it (routine-result.mjs).
|
|
47
|
+
if (options.messageId) {
|
|
48
|
+
const current = getState().items;
|
|
49
|
+
const items = current.filter((it) => !(it?.kind === 'notice' && it?.messageId === options.messageId));
|
|
50
|
+
if (items.length !== current.length) set({ items: replaceItems(items, { preserveStreamingTail: true }) });
|
|
51
|
+
}
|
|
52
|
+
pushItem({
|
|
53
|
+
// A group-room row (agent-room.mjs): the user's own words are their
|
|
54
|
+
// bubble; every member row is a notice the app renders from `room`.
|
|
55
|
+
kind: userRow ? 'user' : 'notice',
|
|
56
|
+
id,
|
|
57
|
+
text: value,
|
|
58
|
+
...(userRow ? {} : { tone }),
|
|
59
|
+
// A schedule result posted into a home thread (routine-result.mjs).
|
|
60
|
+
...(options.routine ? { routine: options.routine } : {}),
|
|
61
|
+
...(options.room ? { room: options.room } : {}),
|
|
62
|
+
// A room user row's files, in the fields a prompt row draws them from.
|
|
63
|
+
...(userRow && Array.isArray(options.attachments) && options.attachments.length
|
|
64
|
+
? { attachments: options.attachments }
|
|
65
|
+
: {}),
|
|
66
|
+
...(userRow && Array.isArray(options.images) && options.images.length ? { images: options.images } : {}),
|
|
67
|
+
// A held request of an unattended run (human-gate/held-message.mjs).
|
|
68
|
+
...(options.heldRequest ? { heldRequest: options.heldRequest } : {}),
|
|
69
|
+
...(options.messageId ? { messageId: options.messageId } : {}),
|
|
70
|
+
});
|
|
40
71
|
return id;
|
|
41
72
|
};
|
|
42
73
|
// Remove a transcript notice previously created via pushNotice(...,
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { agentJobResultText, parseAgentJob, parseSyntheticAgentMessage } from './agent-envelope.mjs';
|
|
7
7
|
import { attachmentTextForPart, isAttachmentReference } from '../../runtime/attachments/store.mjs';
|
|
8
|
+
import { recognisedSlashToken } from '../app/slash-commands.mjs';
|
|
8
9
|
|
|
9
10
|
const QUEUE_PRIORITY = { now: 0, next: 1, later: 2 };
|
|
10
11
|
|
|
@@ -52,7 +53,7 @@ export function isQueuedEntryVisible(entry) {
|
|
|
52
53
|
export function isSlashQueuedEntry(entry) {
|
|
53
54
|
if (entry?.skipSlashCommands) return false;
|
|
54
55
|
const text = promptContentText(entry?.content ?? entry?.text ?? '');
|
|
55
|
-
return text
|
|
56
|
+
return recognisedSlashToken(text) !== null;
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
function firstQueueLine(text) {
|
|
@@ -121,6 +121,10 @@ export function createSessionExtensionsApi(bag) {
|
|
|
121
121
|
(input) => runtime.saveSkill?.(input),
|
|
122
122
|
(result, input) => [`skill saved: ${result?.skill?.name || input?.name || 'skill'}`]
|
|
123
123
|
),
|
|
124
|
+
removeSkill: lockedChange(
|
|
125
|
+
(input) => runtime.removeSkill?.(input),
|
|
126
|
+
(result, input) => [`skill removed: ${result?.skill?.name || input?.name || 'skill'}`]
|
|
127
|
+
),
|
|
124
128
|
reloadSkills: lockedChange(
|
|
125
129
|
() => runtime.reloadSkills?.(),
|
|
126
130
|
(status) => [`skills reload: ${status?.count || 0} available`]
|
|
@@ -14,6 +14,11 @@ export function createSubmissionIntake(bag) {
|
|
|
14
14
|
text,
|
|
15
15
|
queueOptions: {
|
|
16
16
|
...options,
|
|
17
|
+
// A kickoff (a new agent's greeting turn) reaches the model only: no
|
|
18
|
+
// user bubble, no queue row, never restored into the composer.
|
|
19
|
+
...(options.kickoff === true
|
|
20
|
+
? { isMeta: true, suppressDisplay: true, skipSlashCommands: true, restorable: false }
|
|
21
|
+
: {}),
|
|
17
22
|
// Always mint a submission id so daemon retries / dual-path intake
|
|
18
23
|
// can dedupe instead of booking the same prompt twice.
|
|
19
24
|
id: String(options.id || '').trim() || nextId(),
|
|
@@ -191,6 +191,14 @@ export function createSessionIntegrationsApi(bag, { oauthFlows }) {
|
|
|
191
191
|
(name) => runtime.runScheduleNow(name),
|
|
192
192
|
(_result, name) => `schedule ran: ${name}`
|
|
193
193
|
),
|
|
194
|
+
runWebhookNow: announced(
|
|
195
|
+
(name) => runtime.runWebhookNow(name),
|
|
196
|
+
(_result, name) => `webhook ran: ${name}`
|
|
197
|
+
),
|
|
198
|
+
retryRoutineDelivery: announced(
|
|
199
|
+
(kind, name) => runtime.retryRoutineDelivery(kind, name),
|
|
200
|
+
(_result, kind, name) => `${kind} result delivered: ${name}`
|
|
201
|
+
),
|
|
194
202
|
saveWebhook: announced(
|
|
195
203
|
(entry) => runtime.saveWebhook(entry),
|
|
196
204
|
(result) => `webhook saved: ${result.name}`
|
|
@@ -78,18 +78,28 @@ export function createSessionResetActions(bag) {
|
|
|
78
78
|
};
|
|
79
79
|
|
|
80
80
|
return {
|
|
81
|
-
|
|
81
|
+
// `archiveAs`: the id the outgoing transcript is kept under (an agent
|
|
82
|
+
// home's fresh start); the clear fails rather than wipe without it.
|
|
83
|
+
// `rebuildPrompt` / `fork: false`: an agent chat's reset (session-ops clear).
|
|
84
|
+
clear: (options = {}) =>
|
|
82
85
|
withResetLock(async () => {
|
|
86
|
+
const archiveAs = typeof options?.archiveAs === 'string' ? options.archiveAs : '';
|
|
87
|
+
const reset = {
|
|
88
|
+
...(options?.rebuildPrompt === true ? { rebuildPrompt: true } : {}),
|
|
89
|
+
...(options?.fork === false ? { fork: false } : {}),
|
|
90
|
+
};
|
|
83
91
|
const clearedId = getState().sessionId;
|
|
84
92
|
// A cleared session has no failed turn left to resume.
|
|
85
93
|
bag.supersedeQuotaResume?.(clearedId);
|
|
86
94
|
const rollbackSnapshot = beginTuiReset();
|
|
87
95
|
set(blankSessionPatch());
|
|
88
96
|
try {
|
|
89
|
-
await runtime.clear({ recoverAgent: true });
|
|
97
|
+
const result = await runtime.clear({ recoverAgent: true, ...(archiveAs ? { archiveAs } : {}), ...reset });
|
|
98
|
+
if (result === false && (archiveAs || reset.rebuildPrompt))
|
|
99
|
+
throw new Error('The conversation could not be cleared.');
|
|
90
100
|
commitEmptySession(rollbackSnapshot);
|
|
91
101
|
flags.lastUserActivityAt = Date.now();
|
|
92
|
-
return true;
|
|
102
|
+
return reset.rebuildPrompt ? result : true;
|
|
93
103
|
} catch (error) {
|
|
94
104
|
restoreTuiAfterFailedSessionReset(rollbackSnapshot);
|
|
95
105
|
throw error;
|
|
@@ -9,6 +9,17 @@ import { createInheritanceActions } from './lifecycle/inheritance.mjs';
|
|
|
9
9
|
import { createResumeAction } from './lifecycle/resume.mjs';
|
|
10
10
|
import { createDisposeAction } from './lifecycle/dispose.mjs';
|
|
11
11
|
import { publishModelSwitch } from './routes.mjs';
|
|
12
|
+
import { resolveHeldRequest } from '../../../runtime/shared/held-request-host.mjs';
|
|
13
|
+
|
|
14
|
+
/** The human-response fields of a decision (never a value), or null. */
|
|
15
|
+
function humanDecisionFields(decision) {
|
|
16
|
+
if (!decision || typeof decision !== 'object') return null;
|
|
17
|
+
const fields = {};
|
|
18
|
+
for (const key of ['response', 'editedArgs', 'remember', 'scope']) {
|
|
19
|
+
if (decision[key] !== undefined) fields[key] = decision[key];
|
|
20
|
+
}
|
|
21
|
+
return Object.keys(fields).length ? fields : null;
|
|
22
|
+
}
|
|
12
23
|
|
|
13
24
|
export function createSessionLifecycleApi(bag, { restoreTranscriptItems, oauthFlows }) {
|
|
14
25
|
const { runtime, pushNotice, removeNotice, setProgressHint, finishToolApproval } = bag;
|
|
@@ -52,14 +63,19 @@ export function createSessionLifecycleApi(bag, { restoreTranscriptItems, oauthFl
|
|
|
52
63
|
return {
|
|
53
64
|
isSessionTerminal: () => bag.flags?.admission === 'terminal' || runtime.isSessionTerminal?.() === true,
|
|
54
65
|
resolveToolApproval: (id, decision = {}) => {
|
|
66
|
+
// A held request of an unattended run is answered from its owner's home
|
|
67
|
+
// thread, wherever the run itself lives (session-runtime/held-requests.mjs).
|
|
68
|
+
if (resolveHeldRequest(id, decision)) return true;
|
|
55
69
|
const approved = decision === true || decision?.approved === true;
|
|
70
|
+
const extra = humanDecisionFields(decision);
|
|
56
71
|
return finishToolApproval(
|
|
57
72
|
id,
|
|
58
73
|
approved,
|
|
59
74
|
decision?.reason || (approved ? 'approved by user' : 'denied by user'),
|
|
60
75
|
typeof decision?.device === 'string' ? decision.device : '',
|
|
61
76
|
decision?.answers,
|
|
62
|
-
typeof decision?.reply === 'string' ? decision.reply : undefined
|
|
77
|
+
typeof decision?.reply === 'string' ? decision.reply : undefined,
|
|
78
|
+
...(extra ? [extra] : [])
|
|
63
79
|
);
|
|
64
80
|
},
|
|
65
81
|
pushNotice,
|
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
* the serialized optimistic setRoute, web-search route, workflows, agent
|
|
4
4
|
* definitions, output style and orchestration mode.
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
modelSwitchItem,
|
|
8
|
+
workflowSwitchItem,
|
|
9
|
+
} from '../../../runtime/agent/orchestrator/session/transcript-restore/restore.mjs';
|
|
7
10
|
import { selectedModel } from '../../../session-runtime/model-route/model-switch-target.mjs';
|
|
8
11
|
import { createApiHelpers } from './shared.mjs';
|
|
9
12
|
|
|
@@ -272,8 +275,48 @@ export function createSessionRouteApi(bag) {
|
|
|
272
275
|
createWorkflow: async (payload) => runtime.createWorkflow?.(payload) ?? null,
|
|
273
276
|
deleteWorkflow: async (workflowId) => runtime.deleteWorkflow?.(workflowId) ?? null,
|
|
274
277
|
getAgentDefinition: (agentId) => runtime.getAgentDefinition?.(agentId) ?? null,
|
|
278
|
+
getAgentAvatarImage: (agentId) => runtime.getAgentAvatarImage?.(agentId) ?? null,
|
|
279
|
+
getAgentConnectors: (agentId) => runtime.getAgentConnectors?.(agentId) ?? [],
|
|
280
|
+
listConnectors: () => runtime.listConnectors?.() ?? [],
|
|
281
|
+
listConnectorTools: (connectorId) => runtime.listConnectorTools?.(connectorId) ?? [],
|
|
282
|
+
setAgentConnectors: async (agentId, connectors) => runtime.setAgentConnectors?.(agentId, connectors) ?? null,
|
|
283
|
+
setAgentConnectorEnabled: async (agentId, connectorId, enabled) =>
|
|
284
|
+
runtime.setAgentConnectorEnabled?.(agentId, connectorId, enabled) ?? [],
|
|
285
|
+
getProjectConnectors: (projectPath) => runtime.getProjectConnectors?.(projectPath) ?? [],
|
|
286
|
+
setProjectConnectorEnabled: async (projectPath, connectorId, enabled) =>
|
|
287
|
+
runtime.setProjectConnectorEnabled?.(projectPath, connectorId, enabled) ?? [],
|
|
288
|
+
setAgentConnectorToolPermission: async (agentId, connectorId, toolName, permission) =>
|
|
289
|
+
runtime.setAgentConnectorToolPermission?.(agentId, connectorId, toolName, permission) ?? null,
|
|
290
|
+
// Per-agent action-kind permissions (ask / allow / block) for the profile.
|
|
291
|
+
getAgentPermissions: async (agentId) =>
|
|
292
|
+
(await import('../../../runtime/agent/orchestrator/session/human-gate/store.mjs')).agentPermissions(agentId),
|
|
293
|
+
setAgentPermission: async (agentId, action, mode) =>
|
|
294
|
+
(await import('../../../runtime/agent/orchestrator/session/human-gate/store.mjs')).setAgentPermission(
|
|
295
|
+
agentId,
|
|
296
|
+
action,
|
|
297
|
+
mode
|
|
298
|
+
),
|
|
299
|
+
getLeadPersona: () => runtime.getLeadPersona?.() ?? '',
|
|
300
|
+
saveLeadPersona: async (text) => runtime.saveLeadPersona?.(text) ?? '',
|
|
301
|
+
getLeadAgentBody: () => runtime.getLeadAgentBody?.() ?? '',
|
|
302
|
+
saveLeadAgentBody: async (text) => runtime.saveLeadAgentBody?.(text) ?? '',
|
|
303
|
+
getLeadStatus: () => runtime.getLeadStatus?.() ?? { created: null, name: '', avatar: null },
|
|
304
|
+
saveLeadIdentity: async (payload) => runtime.saveLeadIdentity?.(payload) ?? null,
|
|
305
|
+
setLeadCreated: async (created, description) => runtime.setLeadCreated?.(created, description) ?? null,
|
|
275
306
|
saveAgentDefinition: async (payload) => runtime.saveAgentDefinition?.(payload) ?? null,
|
|
276
307
|
deleteAgentDefinition: async (agentId) => runtime.deleteAgentDefinition?.(agentId) ?? null,
|
|
308
|
+
agentDefinitionPresence: (agentIds) => runtime.agentDefinitionPresence?.(agentIds) ?? null,
|
|
309
|
+
restoreAgentDefinition: async (agentId) => runtime.restoreAgentDefinition?.(agentId) ?? null,
|
|
310
|
+
purgeAgentDefinition: async (agentId) => runtime.purgeAgentDefinition?.(agentId) ?? null,
|
|
311
|
+
archivedAgentIdentities: (agentIds) => runtime.archivedAgentIdentities?.(agentIds) ?? null,
|
|
312
|
+
// Group-room rows posted by the desktop host into a room's log session.
|
|
313
|
+
appendRoomRows: async (sessionId, rows) =>
|
|
314
|
+
(await import('../../../session-runtime/agent-room-delivery.mjs')).appendRoomRows(sessionId, rows),
|
|
315
|
+
// One group-room supervisor step on the Maintainer route.
|
|
316
|
+
decideRoomTurn: async (input) =>
|
|
317
|
+
(await import('../../../runtime/agent/orchestrator/agent-runtime/room-supervisor-completion.mjs')).decideRoomTurn(
|
|
318
|
+
input
|
|
319
|
+
),
|
|
277
320
|
getOutputStyle: () => {
|
|
278
321
|
return runtime.getOutputStyle?.() || runtime.listOutputStyles?.() || null;
|
|
279
322
|
},
|
|
@@ -291,8 +334,10 @@ export function createSessionRouteApi(bag) {
|
|
|
291
334
|
deferStatsRefresh();
|
|
292
335
|
return result;
|
|
293
336
|
}),
|
|
294
|
-
setWorkflow: withCommandLock(async (workflowId) => {
|
|
295
|
-
const result = await runtime.setWorkflow?.(workflowId);
|
|
337
|
+
setWorkflow: withCommandLock(async (workflowId, options) => {
|
|
338
|
+
const result = await runtime.setWorkflow?.(workflowId, options);
|
|
339
|
+
// A switch of this conversation leaves a divider (restored from message meta).
|
|
340
|
+
if (result?.workflowSwitch) bag.pushItem(workflowSwitchItem(bag.nextId(), result.workflowSwitch));
|
|
296
341
|
publishRoute();
|
|
297
342
|
return result;
|
|
298
343
|
}),
|
|
@@ -52,6 +52,13 @@ function renderBatchItems(batch, pushUserOrSyntheticItem) {
|
|
|
52
52
|
}
|
|
53
53
|
continue;
|
|
54
54
|
}
|
|
55
|
+
// A routine fire paints as the routine's marker row, not a user bubble.
|
|
56
|
+
if (entry.mode === 'routine') {
|
|
57
|
+
pushUserOrSyntheticItem(entry.text, entry.id, 'routine', {
|
|
58
|
+
routineTrigger: entry.transcriptMeta?.routineTrigger,
|
|
59
|
+
});
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
55
62
|
const sender = String(entry.transcriptMeta?.sender || '')
|
|
56
63
|
.trim()
|
|
57
64
|
.toLowerCase();
|
|
@@ -106,6 +113,10 @@ export function turnOptionsForBatch(batch, steering) {
|
|
|
106
113
|
Array.isArray(entry?.resumeCompletionKeys) ? entry.resumeCompletionKeys : []
|
|
107
114
|
),
|
|
108
115
|
transcriptMeta: batch[0]?.transcriptMeta || null,
|
|
116
|
+
// Only a routine turn names its selection text (never its payload).
|
|
117
|
+
...(batch[0]?.mode === 'routine' && typeof batch[0].skillSelection === 'string'
|
|
118
|
+
? { skillSelection: batch[0].skillSelection }
|
|
119
|
+
: {}),
|
|
109
120
|
context:
|
|
110
121
|
batch
|
|
111
122
|
.map((entry) => String(entry.context || '').trim())
|
|
@@ -191,9 +202,7 @@ export function createDrainLoop(
|
|
|
191
202
|
clearBlockedDrainRetry();
|
|
192
203
|
if (entries.length === 0) return;
|
|
193
204
|
queue.removeQueuedEntries(entries);
|
|
194
|
-
const mirrored = entries.filter(
|
|
195
|
-
(entry) => steering.shouldMirrorSteeringEntry(entry) && !entry.steeringPersistRestored
|
|
196
|
-
);
|
|
205
|
+
const mirrored = entries.filter((entry) => steering.shouldMirrorSteeringEntry(entry) && entry.steeringPersistId);
|
|
197
206
|
if (mirrored.length > 0) {
|
|
198
207
|
void Promise.resolve(dropTuiSteeringPersist(steering.leadSessionId(), mirrored)).catch(() => {});
|
|
199
208
|
}
|
|
@@ -43,8 +43,9 @@ export function makeQueueEntry(text, options = {}, nextId) {
|
|
|
43
43
|
? options.resumeCompletionKeys.filter((key) => key != null && String(key).trim())
|
|
44
44
|
: [],
|
|
45
45
|
steeringPersistId: options.steeringPersistId || null,
|
|
46
|
-
steeringPersistRestored: options.steeringPersistRestored === true,
|
|
47
46
|
isMeta: options.isMeta === true,
|
|
47
|
+
// A routine turn's trusted skill-selection text (its instructions only).
|
|
48
|
+
skillSelection: typeof options.skillSelection === 'string' ? options.skillSelection : null,
|
|
48
49
|
goalId: options.goalId || null,
|
|
49
50
|
// Retry of a failed turn: the transcript and the session rewind the
|
|
50
51
|
// failed turn's unanswered prompt before this entry runs.
|
|
@@ -43,7 +43,8 @@ export function createTakeEntriesOps({ pending, pendingNotificationKeys, removeQ
|
|
|
43
43
|
batch.push(entry);
|
|
44
44
|
pending.splice(i, 1);
|
|
45
45
|
if (entry.mode === 'task-notification' && entry.key) pendingNotificationKeys.delete(entry.key);
|
|
46
|
-
|
|
46
|
+
// A routine fire is a turn of its own, never merged with another.
|
|
47
|
+
if (entry.retryTurnId || entry.mode === 'routine' || batch.length >= limit) break;
|
|
47
48
|
} else {
|
|
48
49
|
i += 1;
|
|
49
50
|
}
|
|
@@ -73,7 +74,7 @@ export function createTakeEntriesOps({ pending, pendingNotificationKeys, removeQ
|
|
|
73
74
|
// A reclaimed prompt goes back to the draft, so its durable steering
|
|
74
75
|
// mirror goes with it; otherwise the next runtime restore re-queues a
|
|
75
76
|
// prompt the user already took back.
|
|
76
|
-
const mirrored = queued.filter((entry) => entry.steeringPersistId
|
|
77
|
+
const mirrored = queued.filter((entry) => entry.steeringPersistId);
|
|
77
78
|
if (mirrored.length > 0) void dropTuiSteeringPersist(leadSessionId(), mirrored);
|
|
78
79
|
const queuedText = queued
|
|
79
80
|
.map((item) => item.text)
|
|
@@ -12,10 +12,13 @@ import {
|
|
|
12
12
|
callCommitCallbacks,
|
|
13
13
|
STEERING_SUPPRESSED_DISPLAY,
|
|
14
14
|
} from '../queue-helpers.mjs';
|
|
15
|
-
import { dropTuiSteeringPersist,
|
|
15
|
+
import { dropTuiSteeringPersist, readTuiSteeringPersist } from '../tui-steering-persist.mjs';
|
|
16
16
|
import { getBackgroundTask } from '../../../runtime/shared/background-tasks.mjs';
|
|
17
17
|
|
|
18
18
|
const RESTORE_DEDUP_WINDOW = 80;
|
|
19
|
+
// An older build's process-local item counter: a later process issues the
|
|
20
|
+
// same ids again, so such an id proves nothing about delivery.
|
|
21
|
+
const PROCESS_LOCAL_ITEM_ID = /^it_\d+$/;
|
|
19
22
|
|
|
20
23
|
// Mid-chain drain converts queued prompt/task notification entries into
|
|
21
24
|
// model-visible "queued_command" style steering only at provider continuation
|
|
@@ -65,8 +68,28 @@ function steeringMessageFromEntry(entry) {
|
|
|
65
68
|
return String(content ?? '').trim().length > 0 ? { ...meta, text: displayText, content } : null;
|
|
66
69
|
}
|
|
67
70
|
|
|
68
|
-
//
|
|
69
|
-
//
|
|
71
|
+
// The submissions the transcript holds: a turn's prompt (`checkpointId`, plus
|
|
72
|
+
// `submissionIds` for a merged batch) and a steered message's `submissionIds`.
|
|
73
|
+
function deliveredSubmissionIds(session) {
|
|
74
|
+
const out = new Set();
|
|
75
|
+
for (const message of Array.isArray(session?.messages) ? session.messages : []) {
|
|
76
|
+
if (message?.role !== 'user') continue;
|
|
77
|
+
const meta = message.meta || {};
|
|
78
|
+
const transcript = meta.transcript || {};
|
|
79
|
+
for (const id of [
|
|
80
|
+
transcript.checkpointId,
|
|
81
|
+
...(Array.isArray(transcript.submissionIds) ? transcript.submissionIds : []),
|
|
82
|
+
...(Array.isArray(meta.submissionIds) ? meta.submissionIds : []),
|
|
83
|
+
]) {
|
|
84
|
+
if (typeof id === 'string' && id) out.add(id);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return out;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Rows of an older build carry no stable submission id; only their text can
|
|
91
|
+
// tell them delivered: substring (not equality) because steering delivery may
|
|
92
|
+
// wrap the raw text.
|
|
70
93
|
function recentUserTexts(session, window) {
|
|
71
94
|
const messages = Array.isArray(session?.messages) ? session.messages : [];
|
|
72
95
|
const out = [];
|
|
@@ -107,7 +130,7 @@ export function createSteeringOps(bag, { queue, submissions }) {
|
|
|
107
130
|
function commitSteeringQueueEntries(entries) {
|
|
108
131
|
callCommitCallbacks(entries);
|
|
109
132
|
const mirrored = (Array.isArray(entries) ? entries : []).filter(
|
|
110
|
-
(entry) => shouldMirrorSteeringEntry(entry) &&
|
|
133
|
+
(entry) => shouldMirrorSteeringEntry(entry) && entry.steeringPersistId
|
|
111
134
|
);
|
|
112
135
|
if (mirrored.length > 0) dropTuiSteeringPersist(leadSessionId(), mirrored);
|
|
113
136
|
}
|
|
@@ -205,46 +228,57 @@ export function createSteeringOps(bag, { queue, submissions }) {
|
|
|
205
228
|
async function restoreLeadSteeringFromDisk() {
|
|
206
229
|
// A closed/closing conversation never gets queue work restored into it.
|
|
207
230
|
if (!admissionOpen()) return;
|
|
208
|
-
const rows = await
|
|
231
|
+
const rows = await readTuiSteeringPersist(leadSessionId());
|
|
209
232
|
if (!rows.length) return;
|
|
210
233
|
const livePersistIds = new Set(pending.map((entry) => entry?.steeringPersistId).filter(Boolean));
|
|
211
|
-
// Crash-consumed dedup: a row whose
|
|
212
|
-
// transcript was consumed before the restart — only its disk drop
|
|
213
|
-
// lost (drop is an async fire-and-forget write). Re-queuing it
|
|
214
|
-
// re-injects a stale prompt into the next turn (user report:
|
|
215
|
-
// self-injection after a process restart).
|
|
216
|
-
|
|
217
|
-
const
|
|
234
|
+
// Crash-consumed dedup: a row whose submission already landed in the
|
|
235
|
+
// session transcript was consumed before the restart — only its disk drop
|
|
236
|
+
// was lost (drop is an async fire-and-forget write). Re-queuing it
|
|
237
|
+
// silently re-injects a stale prompt into the next turn (user report:
|
|
238
|
+
// surprise self-injection after a process restart). Identity, never text:
|
|
239
|
+
// the same words can be a new submission.
|
|
240
|
+
const delivered = deliveredSubmissionIds(runtime.session);
|
|
241
|
+
let legacyTexts = null;
|
|
242
|
+
const alreadyDelivered = (row, text) => {
|
|
243
|
+
if (row.submissionId && !PROCESS_LOCAL_ITEM_ID.test(row.submissionId)) return delivered.has(row.submissionId);
|
|
244
|
+
legacyTexts ??= recentUserTexts(runtime.session, RESTORE_DEDUP_WINDOW);
|
|
245
|
+
return legacyTexts.some((body) => body.includes(text));
|
|
246
|
+
};
|
|
218
247
|
const restored = [];
|
|
219
|
-
|
|
248
|
+
const consumed = [];
|
|
220
249
|
for (const row of rows) {
|
|
221
250
|
const text = String(row?.text || '').trim();
|
|
222
251
|
if (!text) continue;
|
|
223
252
|
// Live enqueue already holds this row (id assigned before the async
|
|
224
253
|
// disk write). Replaying it is the classic double-booked queue.
|
|
225
254
|
if (row.steeringPersistId && livePersistIds.has(row.steeringPersistId)) continue;
|
|
226
|
-
if (alreadyDelivered(text)) {
|
|
227
|
-
|
|
255
|
+
if (alreadyDelivered(row, text)) {
|
|
256
|
+
consumed.push(row);
|
|
228
257
|
continue;
|
|
229
258
|
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
259
|
+
// The row keeps its executable content and attachment links; a row
|
|
260
|
+
// of an older build has only its text.
|
|
261
|
+
const { content, text: displayText, submissionId, submittedAt, ...options } = row;
|
|
262
|
+
const entry = queue.makeQueueEntry(content ?? displayText, {
|
|
263
|
+
...options,
|
|
264
|
+
id: submissionId || undefined,
|
|
265
|
+
submittedAt: submittedAt || undefined,
|
|
266
|
+
displayText,
|
|
235
267
|
});
|
|
236
268
|
pending.push(entry);
|
|
237
|
-
if (
|
|
238
|
-
|
|
269
|
+
if (submissionId) submissions.remember(submissionId);
|
|
270
|
+
livePersistIds.add(entry.steeringPersistId);
|
|
239
271
|
if (isQueuedEntryVisible(entry)) restored.push(entry);
|
|
240
272
|
}
|
|
241
273
|
if (restored.length > 0) set({ queued: [...getState().queued, ...restored] });
|
|
274
|
+
// Reading keeps rows durable; a delivered row's lost drop is completed here.
|
|
275
|
+
if (consumed.length > 0) await dropTuiSteeringPersist(leadSessionId(), consumed);
|
|
242
276
|
// The conversation closed while the rows were being read: settle them
|
|
243
277
|
// explicitly (rejected + durable mirror released), never leave them parked.
|
|
244
278
|
if (bag.flags?.admission === 'terminal') bag.settleClosedQueue?.();
|
|
245
|
-
if (
|
|
279
|
+
if (consumed.length > 0) {
|
|
246
280
|
try {
|
|
247
|
-
process.stderr.write(`[tui] skipped ${
|
|
281
|
+
process.stderr.write(`[tui] skipped ${consumed.length} already-delivered steering row(s) on restore\n`);
|
|
248
282
|
} catch {
|
|
249
283
|
/* best effort */
|
|
250
284
|
}
|
|
@@ -6,6 +6,11 @@
|
|
|
6
6
|
* state, so the closure identifiers (state/set/nextId/disposed/timeout) are
|
|
7
7
|
* threaded through the factory argument via getters — never stale snapshots.
|
|
8
8
|
*/
|
|
9
|
+
import {
|
|
10
|
+
humanRequestFields,
|
|
11
|
+
normalizeHumanResponse,
|
|
12
|
+
} from '../../runtime/agent/orchestrator/session/human-gate/requests.mjs';
|
|
13
|
+
|
|
9
14
|
export function createToolApproval({ getState, set, nextId, getDisposed, timeoutMs }) {
|
|
10
15
|
const toolApprovalQueue = [];
|
|
11
16
|
let activeToolApproval = null;
|
|
@@ -32,6 +37,8 @@ export function createToolApproval({ getState, set, nextId, getDisposed, timeout
|
|
|
32
37
|
requestedAt: now,
|
|
33
38
|
timeoutMs: requestTimeoutMs,
|
|
34
39
|
expiresAt: requestTimeoutMs === null ? null : now + requestTimeoutMs,
|
|
40
|
+
// outbound / secret / takeover / permission fields (human-gate/requests.mjs).
|
|
41
|
+
...humanRequestFields(input),
|
|
35
42
|
};
|
|
36
43
|
}
|
|
37
44
|
// Every request expires from ITS OWN requestedAt, queued or active. Arming
|
|
@@ -41,18 +48,24 @@ export function createToolApproval({ getState, set, nextId, getDisposed, timeout
|
|
|
41
48
|
if (!entry || entry.timer || entry.request?.expiresAt === null) return;
|
|
42
49
|
const remainingMs = Math.max(0, Number(entry.request?.expiresAt || 0) - Date.now());
|
|
43
50
|
entry.timer = setTimeout(() => {
|
|
44
|
-
finishToolApproval(entry.id, false, 'approval timed out');
|
|
51
|
+
finishToolApproval(entry.id, false, 'approval timed out', '', undefined, undefined, { timedOut: true });
|
|
45
52
|
}, remainingMs);
|
|
46
53
|
entry.timer.unref?.();
|
|
47
54
|
}
|
|
48
55
|
// Settle one entry: drop its timeout, then hand the waiter its answer. The
|
|
49
56
|
// resolve stays guarded — a waiter whose caller already went away must never
|
|
50
57
|
// take the rest of the queue down with it.
|
|
51
|
-
function settleToolApproval(entry, approved, reason, answers, reply) {
|
|
58
|
+
function settleToolApproval(entry, approved, reason, answers, reply, extra, device = '') {
|
|
52
59
|
if (entry?.timer) clearTimeout(entry.timer);
|
|
53
60
|
const result = { approved, reason };
|
|
54
61
|
if (answers !== undefined) result.answers = answers;
|
|
55
62
|
if (reply !== undefined) result.reply = reply;
|
|
63
|
+
// A human-kind request answers with its canonical response (never a value).
|
|
64
|
+
if (entry?.request?.remember) {
|
|
65
|
+
Object.assign(result, normalizeHumanResponse(entry.request, approved, extra));
|
|
66
|
+
if (device) result.device = device;
|
|
67
|
+
}
|
|
68
|
+
if (extra?.timedOut === true) result.timedOut = true;
|
|
56
69
|
try {
|
|
57
70
|
entry.resolve(result);
|
|
58
71
|
} catch {}
|
|
@@ -68,7 +81,15 @@ export function createToolApproval({ getState, set, nextId, getDisposed, timeout
|
|
|
68
81
|
armToolApprovalTimeout(entry);
|
|
69
82
|
set({ toolApproval: entry.request });
|
|
70
83
|
}
|
|
71
|
-
function finishToolApproval(
|
|
84
|
+
function finishToolApproval(
|
|
85
|
+
id,
|
|
86
|
+
approved,
|
|
87
|
+
reason = '',
|
|
88
|
+
device = '',
|
|
89
|
+
answers = undefined,
|
|
90
|
+
reply = undefined,
|
|
91
|
+
extra = undefined
|
|
92
|
+
) {
|
|
72
93
|
const targetId = String(id || '');
|
|
73
94
|
if (activeToolApproval && activeToolApproval.id === targetId) {
|
|
74
95
|
const entry = activeToolApproval;
|
|
@@ -79,14 +100,14 @@ export function createToolApproval({ getState, set, nextId, getDisposed, timeout
|
|
|
79
100
|
? { id: targetId, approved: approved === true, device: String(device), at: Date.now() }
|
|
80
101
|
: null;
|
|
81
102
|
set({ toolApproval: null, toolApprovalResult });
|
|
82
|
-
settleToolApproval(entry, approved === true, String(reason || ''), answers, reply);
|
|
103
|
+
settleToolApproval(entry, approved === true, String(reason || ''), answers, reply, extra, String(device || ''));
|
|
83
104
|
presentNextToolApproval();
|
|
84
105
|
return true;
|
|
85
106
|
}
|
|
86
107
|
const index = toolApprovalQueue.findIndex((entry) => entry.id === targetId);
|
|
87
108
|
if (index >= 0) {
|
|
88
109
|
const [entry] = toolApprovalQueue.splice(index, 1);
|
|
89
|
-
settleToolApproval(entry, approved === true, String(reason || ''), answers, reply);
|
|
110
|
+
settleToolApproval(entry, approved === true, String(reason || ''), answers, reply, extra, String(device || ''));
|
|
90
111
|
return true;
|
|
91
112
|
}
|
|
92
113
|
return false;
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
import { isLateToolAnnouncement } from '../../runtime/agent/orchestrator/runtime-core/session-text.mjs';
|
|
13
13
|
import { appendPromptHistory } from '../prompt-history-store.mjs';
|
|
14
14
|
import { newTranscriptMessageId } from '../../runtime/shared/transcript-metadata.mjs';
|
|
15
|
+
import { routineTriggerField } from '../../runtime/shared/routine-trigger.mjs';
|
|
15
16
|
import {
|
|
16
17
|
buildExecutionResponseToolItem,
|
|
17
18
|
completionCardFromExecution,
|
|
@@ -107,16 +108,19 @@ export function createTranscriptIntake({ getState, flags, pushItem, patchItem, t
|
|
|
107
108
|
// message reports it) or, for a typed prompt, a new one that goes with
|
|
108
109
|
// the prompt into the model transcript. An injected row with no message
|
|
109
110
|
// id of its own carries none.
|
|
111
|
+
// A routine fire's row is the prompt the turn persists, like a typed one.
|
|
112
|
+
const ownsPrompt = origin === 'user' || origin === 'routine';
|
|
110
113
|
let messageId = '';
|
|
111
114
|
if (typeof extra?.messageId === 'string' && extra.messageId) messageId = extra.messageId;
|
|
112
|
-
else if (
|
|
115
|
+
else if (ownsPrompt) messageId = newTranscriptMessageId();
|
|
113
116
|
const transcriptMeta = { ...transcriptRouteMetadata(), ...(messageId ? { messageId } : {}) };
|
|
114
|
-
if (
|
|
117
|
+
if (ownsPrompt) flags.pendingTranscriptMeta = transcriptMeta;
|
|
115
118
|
pushItem({
|
|
116
119
|
kind: 'user',
|
|
117
120
|
id,
|
|
118
121
|
text,
|
|
119
122
|
...transcriptMeta,
|
|
123
|
+
...routineTriggerField(extra),
|
|
120
124
|
...(extra && typeof extra.sender === 'string' && extra.sender ? { sender: extra.sender } : {}),
|
|
121
125
|
...(extra && typeof extra.device === 'string' && extra.device ? { device: extra.device } : {}),
|
|
122
126
|
// Byte-free attachment metadata (name/mime/size) from the queue entry —
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Transcript spill storage, history paging, and session item-state mutations.
|
|
3
3
|
*/
|
|
4
|
+
import { randomBytes } from 'node:crypto';
|
|
4
5
|
import { readFileSync } from 'node:fs';
|
|
5
6
|
import { join } from 'node:path';
|
|
6
7
|
import { Worker } from 'node:worker_threads';
|
|
@@ -19,8 +20,11 @@ export const tuiDebug = (msg) => {
|
|
|
19
20
|
} catch {}
|
|
20
21
|
};
|
|
21
22
|
|
|
23
|
+
// Item ids outlive the process (queued-input mirror, turn change records), so
|
|
24
|
+
// a restarted process never issues an id again.
|
|
25
|
+
const ID_PREFIX = `it_${randomBytes(4).toString('hex')}_`;
|
|
22
26
|
let _idSeq = 0;
|
|
23
|
-
export const nextId = () =>
|
|
27
|
+
export const nextId = () => `${ID_PREFIX}${++_idSeq}`;
|
|
24
28
|
|
|
25
29
|
export const TRANSCRIPT_LIVE_ITEM_CAP = 512;
|
|
26
30
|
export const TRANSCRIPT_SPILL_CHUNK_ITEMS = 128;
|