mixdog 1.4.0 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/scripts/check-voice-runtime-inputs.mjs +25 -0
- package/scripts/gate-local.mjs +12 -3
- package/scripts/latest-guard.mjs +21 -9
- package/scripts/lib/isolated-test-env.mjs +10 -5
- package/scripts/lib/run-node-tests.mjs +37 -37
- package/scripts/lib/scoped-temp.d.mts +16 -0
- package/scripts/lib/scoped-temp.mjs +123 -0
- package/scripts/lib/stale-temp.mjs +44 -0
- package/scripts/post-release-check.mjs +1 -6
- package/scripts/pptx-kit-index.mjs +17 -4
- package/scripts/suite-health.mjs +10 -1
- package/scripts/test-data-dir.mjs +10 -7
- package/src/app.mjs +4 -0
- package/src/defaults/shipped-history.json +5 -0
- package/src/defaults/skills/browser-use/SKILL.md +6 -4
- package/src/defaults/skills/computer-use/SKILL.md +4 -2
- package/src/lib/rules-builder.cjs +29 -12
- package/src/output-styles/common.md +2 -2
- package/src/output-styles/simple.md +1 -1
- package/src/rules/agent/AGENT.md +5 -4
- package/src/rules/lead/LEAD.md +10 -5
- package/src/rules/shared/10-tool-workflow.md +3 -1
- package/src/rules/shared/45-destructive.md +0 -2
- package/src/rules/shared/50-execution.md +2 -0
- package/src/rules/shared/70-delivery.md +7 -9
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +29 -6
- package/src/runtime/agent/orchestrator/cache-break-trace.mjs +2 -0
- package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +1 -1
- package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +3 -1
- package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +5 -2
- package/src/runtime/agent/orchestrator/providers/account-pool.mjs +11 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-api-recovery.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +7 -3
- package/src/runtime/agent/orchestrator/providers/anthropic-api-transport.mjs +8 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens-recovery.mjs +6 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/initial-status.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +14 -3
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +6 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/command-code-oauth-login.mjs +1 -10
- package/src/runtime/agent/orchestrator/providers/cursor.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-transport.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/devin.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +210 -20
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-prefix-trace.mjs +76 -11
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +45 -6
- package/src/runtime/agent/orchestrator/providers/lib/first-output-window.mjs +58 -0
- package/src/runtime/agent/orchestrator/providers/lib/listen-on.mjs +14 -0
- package/src/runtime/agent/orchestrator/providers/mimo-oauth-login.mjs +1 -10
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +9 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +14 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +4 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream-common.mjs +5 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +16 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/output-items.mjs +5 -2
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +13 -3
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +13 -2
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-watchdogs.mjs +81 -42
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +8 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +136 -5
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +20 -12
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +7 -1
- package/src/runtime/agent/orchestrator/providers/openai-ws-watchdogs.mjs +48 -22
- package/src/runtime/agent/orchestrator/providers/registry.mjs +12 -0
- package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +41 -13
- package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +7 -6
- package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +7 -3
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +3 -1
- package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +5 -2
- package/src/runtime/agent/orchestrator/session/cache/scoped-cache.mjs +1 -3
- package/src/runtime/agent/orchestrator/session/cache-warm.mjs +8 -2
- package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +1 -8
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +23 -0
- package/src/runtime/agent/orchestrator/session/compact/observation-supersession.mjs +103 -23
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +160 -36
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +13 -1
- package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +163 -22
- package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +132 -17
- package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +38 -7
- package/src/runtime/agent/orchestrator/session/conversation-import/imported-session.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/conversation-import/turns.mjs +90 -22
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +33 -3
- package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +7 -2
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +13 -5
- package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +14 -0
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +13 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +39 -21
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +3 -3
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/manager/route-fallback-chain.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +27 -9
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +61 -6
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +19 -8
- package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +12 -5
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +3 -3
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-context.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +9 -0
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +5 -1
- package/src/runtime/agent/orchestrator/session/prompt-cache-touch-store.mjs +30 -7
- package/src/runtime/agent/orchestrator/session/restart-prefix-parity.fixture.mjs +154 -0
- package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +7 -0
- package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +15 -0
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +37 -8
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +9 -1
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +61 -2
- package/src/runtime/agent/orchestrator/stall-policy.mjs +23 -0
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/invocation-plan.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/spawn-env.mjs +4 -2
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +6 -1
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +46 -20
- package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +7 -1
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +31 -5
- package/src/runtime/agent/orchestrator/tools/builtin/git-worktree-tool.mjs +22 -8
- package/src/runtime/agent/orchestrator/tools/builtin/lib/scoped-search-flight.mjs +9 -5
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-stream.mjs +7 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +6 -3
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-dispatch.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-publication.mjs +23 -12
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-snapshot.mjs +6 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +1 -8
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/federation.mjs +3 -1
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/graph-manifest.json +13 -13
- package/src/runtime/agent/orchestrator/tools/lib/isolated-test-env.mjs +4 -3
- package/src/runtime/agent/orchestrator/tools/lib/shell-warm-standby.mjs +26 -4
- package/src/runtime/channels/lib/webhook/signature.mjs +4 -1
- package/src/runtime/channels/lib/whisper-port.mjs +10 -1
- package/src/runtime/computer-bridge/action-schema.mjs +2 -2
- package/src/runtime/computer-bridge/core-actions.mjs +2 -2
- package/src/runtime/computer-bridge/error-recovery.mjs +5 -4
- package/src/runtime/computer-bridge/linux-action-capabilities.mjs +24 -5
- package/src/runtime/computer-bridge/linux-readiness.d.mts +7 -2
- package/src/runtime/local-provider/server-process.mjs +2 -1
- package/src/runtime/media/adapters/gemini-video.mjs +4 -1
- package/src/runtime/media/adapters/xai-media.mjs +2 -1
- package/src/runtime/memory/lib/embedding-worker.mjs +6 -3
- package/src/runtime/memory/lib/memory-action-handlers/cycle-backfill-action.mjs +1 -7
- package/src/runtime/memory/lib/memory-chunk-quality.mjs +1 -6
- package/src/runtime/office/authoring/pdf-author-action.mjs +1 -6
- package/src/runtime/office/design/design-art-direction.mjs +29 -27
- package/src/runtime/office/pdf/pdf-writer.mjs +6 -1
- package/src/runtime/office/portable/portable-snapshot-docx.mjs +7 -1
- package/src/runtime/office/portable/portable-snapshot-shared.mjs +6 -1
- package/src/runtime/office/quality/assurance-rendered.mjs +9 -2
- package/src/runtime/office/quality/assurance-structure-docx.mjs +3 -1
- package/src/runtime/office/quality/design-review.mjs +1 -5
- package/src/runtime/office/quality/document-brief.mjs +1 -5
- package/src/runtime/office/quality/forced-break-pages.mjs +9 -6
- package/src/runtime/office/quality/quality-pipeline.mjs +1 -1
- package/src/runtime/shared/bot-owned-work.mjs +56 -0
- package/src/runtime/shared/defaults-separation.mjs +1 -4
- package/src/runtime/shared/fetch-safe-listen.d.mts +16 -0
- package/src/runtime/shared/fetch-safe-listen.mjs +50 -0
- package/src/runtime/shared/handoff-artifacts.d.mts +2 -1
- package/src/runtime/shared/handoff-artifacts.mjs +18 -8
- package/src/runtime/shared/idle-auto-clear-gate.mjs +29 -7
- package/src/runtime/shared/llm/cost.mjs +5 -1
- package/src/runtime/shared/loopback-listener.mjs +25 -2
- package/src/runtime/shared/model-route-chain.d.mts +7 -2
- package/src/runtime/shared/model-route-chain.mjs +22 -4
- package/src/runtime/shared/open-url.mjs +10 -0
- package/src/runtime/shared/session-activity-signal.mjs +14 -0
- package/src/runtime/shared/session-scratch.mjs +37 -0
- package/src/runtime/shared/tool-card-model/agent-surface.mjs +1 -3
- package/src/runtime/shared/tool-card-model/card-status.mjs +15 -2
- package/src/runtime/shared/tool-result-summary.mjs +1 -1
- package/src/runtime/shared/tool-surface.mjs +3 -0
- package/src/runtime/shared/tool-work-units.mjs +2 -0
- package/src/runtime/shared/turn-snapshot-store.mjs +13 -6
- package/src/runtime/shared/worktrees/contract.mjs +4 -1
- package/src/runtime/shared/worktrees/git.mjs +15 -0
- package/src/runtime/shared/worktrees/manager.mjs +87 -38
- package/src/runtime/shared/worktrees/test-support.mjs +6 -10
- package/src/runtime/visualize-bridge/client.mjs +2 -0
- package/src/session-runtime/agent-home-delivery.mjs +41 -0
- package/src/session-runtime/agent-task-sessions.mjs +152 -63
- package/src/session-runtime/agent-tool-routing.mjs +8 -41
- package/src/session-runtime/boot/tools.mjs +5 -1
- package/src/session-runtime/bot-status.mjs +12 -6
- package/src/session-runtime/bot-target.mjs +38 -0
- package/src/session-runtime/channel-config-api.mjs +6 -1
- package/src/session-runtime/conversation-import/codex-titles.mjs +88 -0
- package/src/session-runtime/conversation-import/discovery.mjs +271 -25
- package/src/session-runtime/conversation-import/service.mjs +26 -5
- package/src/session-runtime/handoff-attachments.mjs +82 -31
- package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +12 -13
- package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +1 -0
- package/src/session-runtime/internal-tool-executor/reaction-tool.mjs +4 -1
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +8 -2
- package/src/session-runtime/lifecycle/cold-heir.mjs +11 -6
- package/src/session-runtime/lifecycle/session-catalog.mjs +9 -1
- package/src/session-runtime/model-route/set-route.mjs +11 -2
- package/src/session-runtime/provider-catalog-cache.mjs +82 -36
- package/src/session-runtime/provider-models/catalog-load.mjs +23 -6
- package/src/session-runtime/provider-models.mjs +4 -2
- package/src/session-runtime/resource-recall-api.mjs +6 -1
- package/src/session-runtime/runtime-feature-gates.mjs +5 -1
- package/src/session-runtime/services/agent-tool/execute.mjs +14 -9
- package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +20 -4
- package/src/session-runtime/services/agent-tool/model-select.mjs +48 -7
- package/src/session-runtime/services/agent-tool/render.mjs +71 -17
- package/src/session-runtime/services/agent-tool/spawn-preset.mjs +6 -2
- package/src/session-runtime/services/agent-tool/tool-def.mjs +9 -9
- package/src/session-runtime/services/provider-admin.mjs +2 -1
- package/src/session-runtime/tool-policy/policy-stage.mjs +1 -0
- package/src/session-runtime/workflow-agents-api/agent-editor.mjs +5 -1
- package/src/session-runtime/workflow-agents-api/agent-route.mjs +15 -4
- package/src/session-runtime/workflow-agents-api/onboarding.mjs +3 -3
- package/src/session-runtime/workspace/transitions.mjs +25 -0
- package/src/session-runtime/workspace/worktree-tool.mjs +26 -9
- package/src/standalone/daemon-session-control.mjs +18 -2
- package/src/standalone/session-service/agent-tree/agent-turns.mjs +16 -12
- package/src/standalone/session-service/session-calls/session-action.mjs +10 -3
- package/src/standalone/session-transport/client-registry/client-grace.mjs +12 -9
- package/src/tui/app/app-view/floating-panel.jsx +7 -4
- package/src/tui/app/model-picker/route-selection/fast-parameters.mjs +1 -3
- package/src/tui/app/model-picker/route-selection.mjs +5 -6
- package/src/tui/app/provider-setup/oauth-actions.mjs +38 -20
- package/src/tui/app/provider-setup/provider-items.mjs +8 -2
- package/src/tui/app/provider-setup-picker.mjs +9 -5
- package/src/tui/components/QuestionPanel.jsx +13 -4
- package/src/tui/dist/index.mjs +75 -28
- package/src/tui/session/session-flow/queue/take-entries.mjs +11 -0
- package/src/ui/statusline-agents.mjs +1 -3
|
@@ -15,6 +15,12 @@
|
|
|
15
15
|
* answer or approval posts one faint line instead; the card stays in the task
|
|
16
16
|
* session for the user.
|
|
17
17
|
*
|
|
18
|
+
* Whether a turn reports is apart from whose work it is: every accepted send
|
|
19
|
+
* of an agent is the agent's work in that session (bot-owned-work.mjs, shown
|
|
20
|
+
* as the bot working) until its chain ends, the point a report would be
|
|
21
|
+
* final, and with `notify: false` or an unstored watch it reports nothing.
|
|
22
|
+
* A turn the user types there is owned by no agent.
|
|
23
|
+
*
|
|
18
24
|
* An agent's send to any other session reports the same way, by an origin for
|
|
19
25
|
* that send only (reportOrigin). Every reporting turn is also watched durably
|
|
20
26
|
* (agent-home-delivery.mjs `watches`) until its end is queued, so a daemon
|
|
@@ -41,6 +47,7 @@ import { wrapAgentMessage } from '../runtime/shared/agent-message-envelope.mjs';
|
|
|
41
47
|
import { hasPendingAgentWork, sessionsWithPendingWork } from '../runtime/shared/pending-agent-work.mjs';
|
|
42
48
|
import { agentTaskIdOfExecution } from './services/agent-tool/restart-notify.mjs';
|
|
43
49
|
import { botChainOf } from '../runtime/shared/bot-hops.mjs';
|
|
50
|
+
import { endBotWork, noteBotWork } from '../runtime/shared/bot-owned-work.mjs';
|
|
44
51
|
import { worktreeError, worktreeNameError } from '../runtime/shared/worktrees/contract.mjs';
|
|
45
52
|
import { samePath } from '../runtime/shared/worktrees/paths.mjs';
|
|
46
53
|
import { readModelCatalog, routeByDisplayName, withEffort } from './task-session-route.mjs';
|
|
@@ -813,17 +820,24 @@ async function sendReportOrigin({ loadSession, agentName }, sessionId, agentId,
|
|
|
813
820
|
}
|
|
814
821
|
|
|
815
822
|
/** Re-attach one watch a stopped daemon left: report the turn's end once
|
|
816
|
-
* from the target's record, or watch it until it ends.
|
|
823
|
+
* from the target's record, or watch it until it ends. `quiet`: `watch` is
|
|
824
|
+
* the record of work that reports nothing (no home, no title): the same
|
|
825
|
+
* follow, but its end is only the end of the agent's work, dropped from
|
|
826
|
+
* `disown`, and no progress update or report goes out. */
|
|
817
827
|
async function rearmTaskWatch(
|
|
818
|
-
{ deliver, loadSession, materializeSession, queuedSubmissionIds, artifactFields, followToFinal, log },
|
|
819
|
-
watch
|
|
828
|
+
{ deliver, disown, loadSession, materializeSession, queuedSubmissionIds, artifactFields, followToFinal, log },
|
|
829
|
+
watch,
|
|
830
|
+
{ quiet = false } = {}
|
|
820
831
|
) {
|
|
821
832
|
const sessionId = watch.taskSessionId;
|
|
822
833
|
// The turn that answers: the watched submission's own, or the one it
|
|
823
834
|
// follows (a room member's turn under another submission id).
|
|
824
835
|
const turnId = watch.follows || watch.runId;
|
|
825
|
-
|
|
826
|
-
|
|
836
|
+
// The work is the agent's until its end is reported (or cannot be).
|
|
837
|
+
const finish = async (status, text = '', answerId = '') => {
|
|
838
|
+
endBotWork(watch.runId);
|
|
839
|
+
if (quiet) return disown(watch.runId);
|
|
840
|
+
return deliver(watch.agentId, {
|
|
827
841
|
runId: `${watch.runId}_end`,
|
|
828
842
|
taskSessionId: sessionId,
|
|
829
843
|
title: watch.title,
|
|
@@ -836,6 +850,7 @@ async function rearmTaskWatch(
|
|
|
836
850
|
...progressOf(watch.runId, answerId),
|
|
837
851
|
...(watch.textOnly || text === GONE_TEXT || text === LOST_TEXT ? {} : await artifactFields(sessionId, turnId)),
|
|
838
852
|
});
|
|
853
|
+
};
|
|
839
854
|
if (!(await loadSession(sessionId))) return finish('cancelled', GONE_TEXT);
|
|
840
855
|
let runtime;
|
|
841
856
|
try {
|
|
@@ -845,6 +860,7 @@ async function rearmTaskWatch(
|
|
|
845
860
|
log(`${sessionId} could not be loaded after the restart: ${error?.message || error}`);
|
|
846
861
|
return finish('failed', RESUME_FAILED_TEXT);
|
|
847
862
|
}
|
|
863
|
+
noteBotWork({ runId: watch.runId, sessionId, agentId: watch.agentId });
|
|
848
864
|
let done = false;
|
|
849
865
|
let checking = false;
|
|
850
866
|
let again = false;
|
|
@@ -878,22 +894,26 @@ async function rearmTaskWatch(
|
|
|
878
894
|
sessionId,
|
|
879
895
|
runId: turnId,
|
|
880
896
|
runtime,
|
|
881
|
-
|
|
897
|
+
// Only a report wakes the session for the results the restart left
|
|
898
|
+
// it; work that reports nothing is only observed, never driven.
|
|
899
|
+
wakeOwed: !quiet,
|
|
882
900
|
// The turns an earlier process already reported as progress.
|
|
883
901
|
progressedThrough: watch.progressed || '',
|
|
884
|
-
interim:
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
902
|
+
interim: quiet
|
|
903
|
+
? null
|
|
904
|
+
: (answer) =>
|
|
905
|
+
deliver(watch.agentId, {
|
|
906
|
+
runId: progressRunId(watch.runId, answer.id),
|
|
907
|
+
taskSessionId: sessionId,
|
|
908
|
+
title: watch.title,
|
|
909
|
+
status: 'working',
|
|
910
|
+
text: answer.text,
|
|
911
|
+
progressWatchId: watch.runId,
|
|
912
|
+
answerId: answer.id,
|
|
913
|
+
...(watch.target === 'session' ? { target: 'session' } : {}),
|
|
914
|
+
...(watch.hop > 0 ? { hop: watch.hop } : {}),
|
|
915
|
+
...roomFrom(watch.homeSessionId),
|
|
916
|
+
}),
|
|
897
917
|
finish: async (status, text, answerId) => {
|
|
898
918
|
done = true;
|
|
899
919
|
unsubscribe();
|
|
@@ -925,13 +945,16 @@ function createSendSettlement(log) {
|
|
|
925
945
|
const settledTurns = new WeakMap();
|
|
926
946
|
let sendCount = 0;
|
|
927
947
|
|
|
928
|
-
/** Add send `member` (`{ seq, id, unwatch, settle }`) to the settlement
|
|
948
|
+
/** Add send `member` (`{ seq, id, notify, unwatch, settle }`) to the settlement
|
|
929
949
|
* `detail` of its turn, per reporting conversation. The sends of one turn
|
|
930
950
|
* settle in the same tick; after it the earliest (the turn's own, whose
|
|
931
951
|
* record holds the whole turn) settles with all their ids and the others
|
|
932
952
|
* stand down. */
|
|
933
953
|
function join(detail, origin, member) {
|
|
934
|
-
|
|
954
|
+
// Sends that report and sends that do not settle apart: the group's first
|
|
955
|
+
// member settles for the group, so a quiet one must never stand for a
|
|
956
|
+
// send that owes a report.
|
|
957
|
+
const key = `${origin.agentId}\0${origin.sessionId}\0${member.notify ? 'report' : 'quiet'}`;
|
|
935
958
|
let groups = settledTurns.get(detail);
|
|
936
959
|
if (!groups) {
|
|
937
960
|
groups = new Map();
|
|
@@ -1074,13 +1097,19 @@ function startedGate() {
|
|
|
1074
1097
|
export function createAgentTaskSessions({
|
|
1075
1098
|
getSessionService,
|
|
1076
1099
|
deliver = async (agentId, report) => (await import('./agent-home-delivery.mjs')).deliverAgentTask(agentId, report),
|
|
1077
|
-
// The durable watch of each reporting turn until its end is queued.
|
|
1100
|
+
// The durable watch of each reporting turn until its end is queued. A bot's
|
|
1101
|
+
// turn that reports nothing (reports off, or a watch that could not be
|
|
1102
|
+
// stored) keeps a record of its work instead (`own`, `owned`, `disown`),
|
|
1103
|
+
// which no report and no hand-off follows: a restart re-attaches both.
|
|
1078
1104
|
watches = {
|
|
1079
1105
|
add: async (watch) => (await import('./agent-home-delivery.mjs')).watchAgentTask(watch),
|
|
1080
1106
|
list: async () => (await import('./agent-home-delivery.mjs')).agentTaskWatches(),
|
|
1081
1107
|
remove: async (runId) => (await import('./agent-home-delivery.mjs')).unwatchAgentTask(runId),
|
|
1082
1108
|
follow: async (runId, submissionId) =>
|
|
1083
1109
|
(await import('./agent-home-delivery.mjs')).followAgentTask(runId, submissionId),
|
|
1110
|
+
own: async (work) => (await import('./agent-home-delivery.mjs')).ownAgentTask(work),
|
|
1111
|
+
owned: async () => (await import('./agent-home-delivery.mjs')).agentOwnedTasks(),
|
|
1112
|
+
disown: async (runId) => (await import('./agent-home-delivery.mjs')).disownAgentTask(runId),
|
|
1084
1113
|
},
|
|
1085
1114
|
// Submissions still queued (durably) for a session after a restart.
|
|
1086
1115
|
queuedSubmissionIds = async (sessionId) =>
|
|
@@ -1209,7 +1238,35 @@ export function createAgentTaskSessions({
|
|
|
1209
1238
|
log(`report of ${sessionId} failed: ${error?.message || error}`)
|
|
1210
1239
|
);
|
|
1211
1240
|
let unwatch = () => {};
|
|
1212
|
-
//
|
|
1241
|
+
// The agent's work in `sessionId`: owned from the send's acceptance until
|
|
1242
|
+
// it settles or is cancelled, whether or not it reports (`notify`). A send
|
|
1243
|
+
// that holds no report watch keeps a durable record of the work instead
|
|
1244
|
+
// (`watches.own`), so a restart re-attaches it (recover); the writes of
|
|
1245
|
+
// one send go in order, so a record never outlives its work.
|
|
1246
|
+
let owning = true;
|
|
1247
|
+
let recorded = false;
|
|
1248
|
+
let recording = Promise.resolve();
|
|
1249
|
+
const record = (write) => {
|
|
1250
|
+
recording = recording
|
|
1251
|
+
.then(write)
|
|
1252
|
+
.catch((error) => log(`recording the work of ${sessionId} failed: ${error?.message || error}`));
|
|
1253
|
+
return recording;
|
|
1254
|
+
};
|
|
1255
|
+
// The work of every send `ids` this turn answered ends together: one
|
|
1256
|
+
// write drops all their records (a send standing down for the turn's
|
|
1257
|
+
// first leaves its record to that one write).
|
|
1258
|
+
const disown = (ids = [id]) => {
|
|
1259
|
+
owning = false;
|
|
1260
|
+
for (const done of ids) endBotWork(done);
|
|
1261
|
+
if (recorded || ids.length > 1) void record(() => watches.disown?.(ids));
|
|
1262
|
+
};
|
|
1263
|
+
const standDown = () => {
|
|
1264
|
+
unwatch();
|
|
1265
|
+
owning = false;
|
|
1266
|
+
endBotWork(id);
|
|
1267
|
+
};
|
|
1268
|
+
// Set once the turn ended completed: the work and any report wait for the
|
|
1269
|
+
// final answer.
|
|
1213
1270
|
let follow = null;
|
|
1214
1271
|
let result;
|
|
1215
1272
|
/** This send ends the turn `detail` for the sends `answered` (createSendSettlement). */
|
|
@@ -1217,11 +1274,12 @@ export function createAgentTaskSessions({
|
|
|
1217
1274
|
sendIds = answered;
|
|
1218
1275
|
const status = turnStatus(detail);
|
|
1219
1276
|
const text = detail?.result?.content || detail?.error || '';
|
|
1220
|
-
// A failed or cancelled turn
|
|
1221
|
-
//
|
|
1277
|
+
// A failed or cancelled turn ends at once; a completed one ends with
|
|
1278
|
+
// the task's final answer (followToFinal).
|
|
1222
1279
|
if (status !== 'completed') {
|
|
1223
1280
|
unwatch();
|
|
1224
|
-
|
|
1281
|
+
disown(sendIds);
|
|
1282
|
+
if (notify) void report(status, text);
|
|
1225
1283
|
return;
|
|
1226
1284
|
}
|
|
1227
1285
|
follow = followToFinal({
|
|
@@ -1231,23 +1289,23 @@ export function createAgentTaskSessions({
|
|
|
1231
1289
|
immediate: { status, text },
|
|
1232
1290
|
finish: async (endStatus, endText, answerId) => {
|
|
1233
1291
|
unwatch();
|
|
1234
|
-
|
|
1292
|
+
disown(sendIds);
|
|
1293
|
+
if (notify) await report(endStatus, endText, { answerId });
|
|
1235
1294
|
},
|
|
1236
|
-
// Each other answer of its chain: a progress update that wakes nobody
|
|
1237
|
-
|
|
1295
|
+
// Each other answer of its chain: a progress update that wakes nobody
|
|
1296
|
+
// (only a send that reports gives them).
|
|
1297
|
+
...(notify ? { interim: (answer) => deliverReport('working', answer.text, { answerId: answer.id }) } : {}),
|
|
1238
1298
|
});
|
|
1239
1299
|
follow.check();
|
|
1240
1300
|
};
|
|
1241
1301
|
try {
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
}) || unwatch;
|
|
1250
|
-
}
|
|
1302
|
+
runtime = await sessions.materializeSession(sessionId);
|
|
1303
|
+
const onState = notify ? approvalWaitReporter(id, report) : null;
|
|
1304
|
+
unwatch =
|
|
1305
|
+
runtime?.subscribe?.(() => {
|
|
1306
|
+
follow?.check();
|
|
1307
|
+
onState?.(runtime.getState?.() || {});
|
|
1308
|
+
}) || unwatch;
|
|
1251
1309
|
// A bot's send to a session that is not its own task session reaches the
|
|
1252
1310
|
// model inside the sender envelope (`envelopeName`, set by reportOrigin).
|
|
1253
1311
|
const sent = origin.envelopeName
|
|
@@ -1259,39 +1317,55 @@ export function createAgentTaskSessions({
|
|
|
1259
1317
|
id,
|
|
1260
1318
|
mode: 'prompt',
|
|
1261
1319
|
transcriptMeta: sendTranscriptMeta(origin),
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
}
|
|
1276
|
-
: {}),
|
|
1320
|
+
// A room answers this send in a turn under another submission id (one
|
|
1321
|
+
// turn for several sends, or a fresh turn for one cut short): the
|
|
1322
|
+
// watch follows that turn, recorded before it runs.
|
|
1323
|
+
...(notify ? { follow: (submissionId) => watches.follow?.(id, submissionId) } : {}),
|
|
1324
|
+
onSettled: (detail) =>
|
|
1325
|
+
settlement.join(detail, origin, {
|
|
1326
|
+
seq,
|
|
1327
|
+
id,
|
|
1328
|
+
notify,
|
|
1329
|
+
// Standing down for the turn's first send, which owns the same work.
|
|
1330
|
+
unwatch: standDown,
|
|
1331
|
+
settle: (answered) => settleTurn(detail, answered),
|
|
1332
|
+
}),
|
|
1277
1333
|
});
|
|
1278
1334
|
} catch (error) {
|
|
1279
1335
|
unwatch();
|
|
1336
|
+
disown();
|
|
1280
1337
|
if (notify) await dropWatch();
|
|
1281
1338
|
throw error;
|
|
1282
1339
|
}
|
|
1283
1340
|
const accepted = result?.accepted === true;
|
|
1284
1341
|
if (!accepted) {
|
|
1285
1342
|
unwatch();
|
|
1343
|
+
disown();
|
|
1286
1344
|
if (notify) await dropWatch();
|
|
1345
|
+
} else if (owning) {
|
|
1346
|
+
// Unless it already settled: nothing then is left to end the work.
|
|
1347
|
+
noteBotWork({ runId: id, sessionId, agentId: origin.agentId });
|
|
1348
|
+
if (!notify) {
|
|
1349
|
+
recorded = true;
|
|
1350
|
+
await record(() =>
|
|
1351
|
+
watches.own?.({
|
|
1352
|
+
runId: id,
|
|
1353
|
+
agentId: origin.agentId,
|
|
1354
|
+
taskSessionId: sessionId,
|
|
1355
|
+
owner: processToken,
|
|
1356
|
+
at: Date.now(),
|
|
1357
|
+
})
|
|
1358
|
+
);
|
|
1359
|
+
}
|
|
1287
1360
|
}
|
|
1288
1361
|
return sendResult(accepted, notify, id, noReport);
|
|
1289
1362
|
}
|
|
1290
1363
|
|
|
1291
|
-
const rearm = (watch) =>
|
|
1364
|
+
const rearm = (watch, options) =>
|
|
1292
1365
|
rearmTaskWatch(
|
|
1293
1366
|
{
|
|
1294
1367
|
deliver,
|
|
1368
|
+
disown: async (runId) => watches.disown?.(runId),
|
|
1295
1369
|
loadSession,
|
|
1296
1370
|
materializeSession: (sessionId) => service().materializeSession(sessionId),
|
|
1297
1371
|
queuedSubmissionIds,
|
|
@@ -1299,13 +1373,25 @@ export function createAgentTaskSessions({
|
|
|
1299
1373
|
followToFinal,
|
|
1300
1374
|
log,
|
|
1301
1375
|
},
|
|
1302
|
-
watch
|
|
1376
|
+
watch,
|
|
1377
|
+
options
|
|
1303
1378
|
);
|
|
1304
1379
|
|
|
1305
|
-
/** Daemon start: re-attach every reporting turn an earlier process watched
|
|
1380
|
+
/** Daemon start: re-attach every reporting turn an earlier process watched,
|
|
1381
|
+
* and every turn of a bot's that reported nothing but whose work it owned
|
|
1382
|
+
* (nothing is reported for those, they are only the bot's work again).
|
|
1306
1383
|
* Resolves once each is reported or watched. */
|
|
1307
1384
|
async function recover() {
|
|
1308
|
-
const
|
|
1385
|
+
const earlier = (list) => list.filter((entry) => entry?.owner !== processToken);
|
|
1386
|
+
const pending = earlier(await watches.list());
|
|
1387
|
+
const quiet = earlier(
|
|
1388
|
+
await Promise.resolve(watches.owned?.() ?? [])
|
|
1389
|
+
.then((list) => (Array.isArray(list) ? list : []))
|
|
1390
|
+
.catch((error) => {
|
|
1391
|
+
log(`reading the records of bot work failed: ${error?.message || error}`);
|
|
1392
|
+
return [];
|
|
1393
|
+
})
|
|
1394
|
+
);
|
|
1309
1395
|
// The agents the restart killed become queued failure results first, so
|
|
1310
1396
|
// each re-attached task session has them to take (followToFinal wakeOwed).
|
|
1311
1397
|
if (pending.length) {
|
|
@@ -1313,12 +1399,15 @@ export function createAgentTaskSessions({
|
|
|
1313
1399
|
.then(settleAgentResults)
|
|
1314
1400
|
.catch((error) => log(`settling the agents of the previous process failed: ${error?.message || error}`));
|
|
1315
1401
|
}
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
)
|
|
1320
|
-
|
|
1321
|
-
|
|
1402
|
+
const reattach = (work, options) =>
|
|
1403
|
+
rearm(work, options).catch((error) =>
|
|
1404
|
+
log(`re-attaching ${work?.taskSessionId} failed: ${error?.message || error}`)
|
|
1405
|
+
);
|
|
1406
|
+
await Promise.all([
|
|
1407
|
+
...pending.map((watch) => reattach(watch)),
|
|
1408
|
+
...quiet.map((work) => reattach(work, { quiet: true })),
|
|
1409
|
+
]);
|
|
1410
|
+
return pending.length + quiet.length;
|
|
1322
1411
|
}
|
|
1323
1412
|
|
|
1324
1413
|
/** Create the task session for `brief` and start it; returns at once.
|
|
@@ -5,55 +5,22 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { clean } from '../runtime/shared/clean.mjs';
|
|
8
|
-
|
|
9
|
-
const lower = (value) => clean(value).toLowerCase();
|
|
10
|
-
|
|
11
|
-
// A pinned model named by its catalog display name ("GPT-6.1-Sol") is not a
|
|
12
|
-
// wire id. Canonicalize it only on an unambiguous EXACT match: the id itself,
|
|
13
|
-
// or a display name / label, case-insensitively, within the requested provider
|
|
14
|
-
// (or across providers when none is given and exactly one row matches).
|
|
15
|
-
// Anything else — no match, several matches, an empty or partial catalog —
|
|
16
|
-
// returns the args unchanged: the catalog never refuses a model and never
|
|
17
|
-
// substitutes a near one.
|
|
18
|
-
export function canonicalizePinnedModel(args, rows) {
|
|
19
|
-
const model = clean(args?.model);
|
|
20
|
-
if (!model || !Array.isArray(rows)) return args;
|
|
21
|
-
const provider = lower(args.provider);
|
|
22
|
-
const pool = rows.filter((row) => row && (!provider || lower(row.provider) === provider));
|
|
23
|
-
const wanted = model.toLowerCase();
|
|
24
|
-
const exactId = pool.filter((row) => row.id === model);
|
|
25
|
-
// A partial catalog may lack the real id a display name collides with, so
|
|
26
|
-
// only an exact id can pass there; aliases wait for a complete catalog.
|
|
27
|
-
const partial = pool.some((row) => row.catalogPartial === true);
|
|
28
|
-
const hits = exactId.length
|
|
29
|
-
? exactId
|
|
30
|
-
: partial
|
|
31
|
-
? []
|
|
32
|
-
: pool.filter((row) => lower(row.id) === wanted || lower(row.display) === wanted || lower(row.label) === wanted);
|
|
33
|
-
const distinct = [...new Map(hits.map((row) => [`${row.provider}\n${row.id}`, row])).values()];
|
|
34
|
-
if (distinct.length !== 1) return args;
|
|
35
|
-
const [hit] = distinct;
|
|
36
|
-
if (hit.id === model && hit.provider === args.provider) return args;
|
|
37
|
-
return { ...args, provider: hit.provider, model: hit.id };
|
|
38
|
-
}
|
|
8
|
+
import { resolveRequestedModel } from './services/agent-tool/model-select.mjs';
|
|
39
9
|
|
|
40
10
|
// `listProviderModels` is this runtime's provider catalog. The agent control
|
|
41
11
|
// behind `executeAgentControl` (the daemon's canonical agent) has none, so a
|
|
42
|
-
//
|
|
43
|
-
//
|
|
12
|
+
// pinned model is resolved here, by the same rule the in-process agent tool
|
|
13
|
+
// applies (exact id, display name, then every query word; ambiguous or
|
|
14
|
+
// unknown names fail with candidates), and forwarded as the exact route.
|
|
44
15
|
export function createRoutedAgentTool({ rt, agentTool, executeAgentControl = null, listProviderModels = null }) {
|
|
45
16
|
const routedAgentTool = {
|
|
46
17
|
...agentTool,
|
|
47
18
|
async execute(args, context = {}) {
|
|
48
19
|
if (typeof executeAgentControl !== 'function') return agentTool.execute(args, context);
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
} catch {
|
|
54
|
-
routedArgs = args;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
20
|
+
const routedArgs =
|
|
21
|
+
typeof listProviderModels === 'function' && clean(args?.model) && (clean(args.type) || 'spawn') === 'spawn'
|
|
22
|
+
? await resolveRequestedModel(args, listProviderModels)
|
|
23
|
+
: args;
|
|
57
24
|
return executeAgentControl(routedArgs, context);
|
|
58
25
|
},
|
|
59
26
|
closeAll(reason, scope = {}) {
|
|
@@ -22,7 +22,10 @@ import { TOOL_DEFS as OFFICE_TOOL_DEFS } from '../../runtime/office/tool-defs.mj
|
|
|
22
22
|
import { TOOL_DEFS as MEDIA_TOOL_DEFS } from '../../runtime/media/tool-defs.mjs';
|
|
23
23
|
import { TOOL_DEFS as TIDY_TOOL_DEFS } from '../../runtime/tidy/tool-defs.mjs';
|
|
24
24
|
import { TOOL_DEFS as VISUALIZE_BRIDGE_TOOL_DEFS } from '../../runtime/visualize-bridge/tool-defs.mjs';
|
|
25
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
ASK_USER_TOOL_DEFS,
|
|
27
|
+
HANDOFF_USER_TOOL_DEFS,
|
|
28
|
+
} from '../../runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs';
|
|
26
29
|
import { REACTION_TOOL_DEFS } from '../../runtime/agent/orchestrator/runtime-core/reaction-tool-defs.mjs';
|
|
27
30
|
import { SETUP_TOOL_DEFS } from '../setup-tool/tool-defs.mjs';
|
|
28
31
|
import { createSetupToolExecutor } from '../setup-tool/executor.mjs';
|
|
@@ -171,6 +174,7 @@ function wireToolSurface(boot) {
|
|
|
171
174
|
visualizeToolDefs: VISUALIZE_BRIDGE_TOOL_DEFS,
|
|
172
175
|
setupToolDefs: SETUP_TOOL_DEFS,
|
|
173
176
|
askUserToolDefs: ASK_USER_TOOL_DEFS,
|
|
177
|
+
handoffUserToolDefs: HANDOFF_USER_TOOL_DEFS,
|
|
174
178
|
reactionToolDefs: REACTION_TOOL_DEFS,
|
|
175
179
|
goalTools: goalRuntime.tools,
|
|
176
180
|
agentTools: agentTool.tools,
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `session bots`: the state of the user's bots, read on demand (never injected
|
|
3
3
|
* into a turn). One line per bot: its home chat (`sess_home_<id>`), the last
|
|
4
|
-
* activity there and its latest line, whether it works now (its chat, or
|
|
5
|
-
*
|
|
4
|
+
* activity there and its latest line, whether it works now (its chat, or work
|
|
5
|
+
* it sent running in a task session; a turn the user typed there is the
|
|
6
|
+
* user's), approvals waiting on the user, its routines and what
|
|
6
7
|
* needs attention. With one bot (`session_id`: its id or chat id): its
|
|
7
8
|
* routines, its held requests and its recent sessions.
|
|
8
9
|
*
|
|
@@ -143,7 +144,7 @@ function span(ms) {
|
|
|
143
144
|
const ago = (at, now) => `${span(now - at)} ago`;
|
|
144
145
|
const until = (at, now) => (at <= now ? 'due now' : `in ${span(at - now)}`);
|
|
145
146
|
|
|
146
|
-
async function defaultListBots() {
|
|
147
|
+
export async function defaultListBots() {
|
|
147
148
|
const [{ workflowHelpers }, { getPluginData, loadConfig }, { isAgentDisabled, isContactAgentId }] = await Promise.all(
|
|
148
149
|
[
|
|
149
150
|
import('./boot/shared.mjs'),
|
|
@@ -264,7 +265,12 @@ export function createBotStatus({
|
|
|
264
265
|
const homeId = agentHomeSessionId(bot.id);
|
|
265
266
|
const owned = ctx.owned.get(bot.id) || [];
|
|
266
267
|
const home = ctx.rows.get(homeId) || null;
|
|
267
|
-
//
|
|
268
|
+
// Work this bot sent that is running now: a session working with the bot's
|
|
269
|
+
// send in flight (`handedBy`, bot-owned-work.mjs). A turn the user typed
|
|
270
|
+
// in the bot's task session is the user's, not the bot working.
|
|
271
|
+
const sentWork = (row) => isWorking(row) && row.handedBy?.includes(bot.id) === true;
|
|
272
|
+
// Running task sessions: a turn whose report is still owed, or the bot's
|
|
273
|
+
// work running now.
|
|
268
274
|
const tasks = new Map();
|
|
269
275
|
const addTask = (id, title, at) => {
|
|
270
276
|
const known = tasks.get(id);
|
|
@@ -276,7 +282,7 @@ export function createBotStatus({
|
|
|
276
282
|
addTask(watch.taskSessionId, watch.title, Number(watch.at) || 0);
|
|
277
283
|
}
|
|
278
284
|
for (const row of owned) {
|
|
279
|
-
if (row.id === homeId || roomMemberOfSession(row.id) || !
|
|
285
|
+
if (row.id === homeId || roomMemberOfSession(row.id) || !sentWork(row)) continue;
|
|
280
286
|
addTask(row.id, row.title, Number(row.createdAt) || 0);
|
|
281
287
|
}
|
|
282
288
|
const running = [...tasks.values()].sort((left, right) => left.at - right.at || (left.id < right.id ? -1 : 1));
|
|
@@ -302,7 +308,7 @@ export function createBotStatus({
|
|
|
302
308
|
approvals,
|
|
303
309
|
routines,
|
|
304
310
|
flags,
|
|
305
|
-
working: isWorking(home) || running.some((task) =>
|
|
311
|
+
working: isWorking(home) || running.some((task) => sentWork(ctx.rows.get(task.id))),
|
|
306
312
|
lastAt: activityOf(home),
|
|
307
313
|
};
|
|
308
314
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// A bot id as the `session_id` of read, send and status: a bot's chat is a
|
|
2
|
+
// session of its own (`sess_home_<id>`), but the schema also lets a model name
|
|
3
|
+
// the bot (`bots` takes either), so the id resolves to that home chat — before
|
|
4
|
+
// any check of the target, so the bot-chain, hop and task-origin checks see
|
|
5
|
+
// the chat they would for `sess_home_<id>`. A minted session id always has an
|
|
6
|
+
// underscore, a normalized bot id never does (agent-home.mjs), so only ids
|
|
7
|
+
// shaped like a bot id are looked up.
|
|
8
|
+
import { MIXY_AGENT_ID, agentHomeSessionId } from '../runtime/agent/orchestrator/session/agent-home.mjs';
|
|
9
|
+
import { defaultListBots, quotedText } from './bot-status.mjs';
|
|
10
|
+
|
|
11
|
+
const MAX_NAMED_BOTS = 30;
|
|
12
|
+
/** The actions whose `session_id` may be a bot id. */
|
|
13
|
+
export const BOT_TARGET_ACTIONS = new Set(['read', 'send', 'status']);
|
|
14
|
+
|
|
15
|
+
/** The ids of the bots a send can reach: Mixy and every contact agent. */
|
|
16
|
+
async function botIds(listBots) {
|
|
17
|
+
return [MIXY_AGENT_ID, ...(await listBots()).map((bot) => bot.id)];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** `target`'s home chat when it is a known bot id, else `target` unchanged. */
|
|
21
|
+
export async function resolveBotTarget(target, listBots = defaultListBots) {
|
|
22
|
+
const home = agentHomeSessionId(target);
|
|
23
|
+
return home && (await botIds(listBots)).includes(target) ? home : target;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Whether `target` is shaped like a bot id (so, with no session, a mistyped one). */
|
|
27
|
+
export const isBotShaped = (target) => agentHomeSessionId(target) !== '';
|
|
28
|
+
|
|
29
|
+
/** The error for a bot-shaped `target` that is neither a known bot nor a session. */
|
|
30
|
+
export async function unknownBotError(target, listBots = defaultListBots, cause) {
|
|
31
|
+
const ids = await botIds(listBots);
|
|
32
|
+
const named = ids.slice(0, MAX_NAMED_BOTS).join(', ');
|
|
33
|
+
const more = ids.length > MAX_NAMED_BOTS ? `, … (${ids.length - MAX_NAMED_BOTS} more)` : '';
|
|
34
|
+
return new Error(
|
|
35
|
+
`session: "${quotedText(target, 64)}" is neither a session nor a known bot id; bot ids: ${named}${more}; session action=list finds session ids`,
|
|
36
|
+
cause ? { cause } : undefined
|
|
37
|
+
);
|
|
38
|
+
}
|
|
@@ -12,7 +12,12 @@ import {
|
|
|
12
12
|
testWebhook,
|
|
13
13
|
withScheduleRenameReserved,
|
|
14
14
|
} from './services/channel-admin.mjs';
|
|
15
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
getSchedule,
|
|
17
|
+
markScheduleFailure,
|
|
18
|
+
markScheduleSkipped,
|
|
19
|
+
markScheduleSuccess,
|
|
20
|
+
} from '../runtime/channels/lib/schedules-db.mjs';
|
|
16
21
|
import { scheduleRenameUncertainError } from '../runtime/channels/lib/schedule-errors.mjs';
|
|
17
22
|
import { loadEndpointConfig, readEndpointLastPayload } from '../runtime/channels/lib/webhooks-db.mjs';
|
|
18
23
|
import { webhookEventSummary, webhookRunPrompt } from '../runtime/channels/lib/webhook/session-dispatch.mjs';
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The thread titles Codex itself stores, by thread id (read-only):
|
|
3
|
+
*
|
|
4
|
+
* state_<N>.sqlite `threads.title`, overridden by `threads.name` (a rename)
|
|
5
|
+
* session_index.jsonl append-only `{ id, thread_name }`; the latest
|
|
6
|
+
* non-empty name of an id wins and overrides the DB
|
|
7
|
+
*
|
|
8
|
+
* Anything unreadable is skipped: a missing or locked store simply leaves the
|
|
9
|
+
* conversation to the title derived from its messages.
|
|
10
|
+
*/
|
|
11
|
+
import { DatabaseSync } from 'node:sqlite';
|
|
12
|
+
import { readFile, readdir } from 'node:fs/promises';
|
|
13
|
+
import { join } from 'node:path';
|
|
14
|
+
import { titleFromText } from '../../runtime/agent/orchestrator/session/conversation-import/turns.mjs';
|
|
15
|
+
|
|
16
|
+
const STATE_DB_RE = /^state_(\d+)\.sqlite$/i;
|
|
17
|
+
|
|
18
|
+
async function newestStateDb(home) {
|
|
19
|
+
let newest = null;
|
|
20
|
+
for (const name of await readdir(home).catch(() => [])) {
|
|
21
|
+
const version = STATE_DB_RE.exec(name)?.[1];
|
|
22
|
+
if (version !== undefined && (!newest || Number(version) > newest.version)) {
|
|
23
|
+
newest = { name, version: Number(version) };
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return newest ? join(home, newest.name) : null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function readStateRows(path) {
|
|
30
|
+
const db = new DatabaseSync(path, { readOnly: true });
|
|
31
|
+
try {
|
|
32
|
+
const columns = new Set(
|
|
33
|
+
db
|
|
34
|
+
.prepare('PRAGMA table_info(threads)')
|
|
35
|
+
.all()
|
|
36
|
+
.map((column) => column.name)
|
|
37
|
+
);
|
|
38
|
+
const wanted = ['title', 'name'].filter((column) => columns.has(column));
|
|
39
|
+
if (!wanted.length) return [];
|
|
40
|
+
return db.prepare(`SELECT id, ${wanted.join(', ')} FROM threads`).all();
|
|
41
|
+
} finally {
|
|
42
|
+
db.close();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function readStateTitles(home, titles) {
|
|
47
|
+
try {
|
|
48
|
+
const path = await newestStateDb(home);
|
|
49
|
+
if (!path) return;
|
|
50
|
+
const rows = readStateRows(path);
|
|
51
|
+
for (const column of ['title', 'name']) {
|
|
52
|
+
for (const row of rows) {
|
|
53
|
+
const title = titleFromText(row[column]);
|
|
54
|
+
if (title && typeof row.id === 'string') titles.set(row.id, title);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
} catch {
|
|
58
|
+
/* unreadable store: fall back to the session index and derived titles */
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async function readIndexTitles(home, titles) {
|
|
63
|
+
let text;
|
|
64
|
+
try {
|
|
65
|
+
text = await readFile(join(home, 'session_index.jsonl'), 'utf8');
|
|
66
|
+
} catch {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
for (const line of text.split('\n')) {
|
|
70
|
+
if (!line.trim()) continue;
|
|
71
|
+
let entry;
|
|
72
|
+
try {
|
|
73
|
+
entry = JSON.parse(line);
|
|
74
|
+
} catch {
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
const title = titleFromText(entry?.thread_name);
|
|
78
|
+
if (title && typeof entry.id === 'string') titles.set(entry.id, title);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Map of Codex thread id → its stored title (whitespace-normalized, ≤100 chars). */
|
|
83
|
+
export async function loadCodexThreadTitles(home) {
|
|
84
|
+
const titles = new Map();
|
|
85
|
+
await readStateTitles(home, titles);
|
|
86
|
+
await readIndexTitles(home, titles);
|
|
87
|
+
return titles;
|
|
88
|
+
}
|