mixdog 1.1.2 → 1.2.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/README.md +8 -5
- package/package.json +2 -1
- package/scripts/lib/run-node-tests.mjs +9 -0
- package/scripts/prune-desktop-runtime.mjs +3 -0
- package/scripts/test-direct.mjs +4 -2
- package/src/defaults/shipped-history.json +10 -0
- package/src/defaults/skills/pptx/SKILL.md +6 -6
- package/src/defaults/skills/pptx/references/review.md +6 -4
- package/src/defaults/skills/pptx/scripts/source-extract.mjs +1 -1
- package/src/defaults/skills/setup/references/actions.md +18 -5
- package/src/defaults/skills/visualize/SKILL.md +160 -0
- package/src/defaults/skills/visualize/references/craft.md +88 -0
- package/src/headless-command.mjs +56 -3
- package/src/headless-exec.mjs +17 -1
- package/src/help.mjs +2 -0
- package/src/rules/shared/60-verification.md +3 -1
- package/src/rules/shared/70-delivery.md +4 -2
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/preset.mjs +1 -0
- package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +1 -0
- package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +2 -0
- package/src/runtime/agent/orchestrator/cache-mtime.mjs +26 -5
- package/src/runtime/agent/orchestrator/config-presets.mjs +2 -0
- package/src/runtime/agent/orchestrator/config-providers-merge.mjs +17 -11
- package/src/runtime/agent/orchestrator/config-storage.mjs +1 -0
- package/src/runtime/agent/orchestrator/config.mjs +19 -5
- package/src/runtime/agent/orchestrator/context/collect.mjs +2 -1
- package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +11 -5
- package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +74 -21
- package/src/runtime/agent/orchestrator/mcp/client-config.mjs +133 -15
- package/src/runtime/agent/orchestrator/mcp/client-handshake.mjs +55 -8
- package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +86 -8
- package/src/runtime/agent/orchestrator/mcp/client.mjs +474 -164
- package/src/runtime/agent/orchestrator/mcp/lazy-output-validator.mjs +25 -0
- package/src/runtime/agent/orchestrator/mcp/plugin-placeholders.mjs +54 -0
- package/src/runtime/agent/orchestrator/mcp/reconnect-singleflight.mjs +4 -0
- package/src/runtime/agent/orchestrator/providers/account-pool.mjs +108 -17
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +15 -1
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +32 -12
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-tokens.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/auto-effort.mjs +9 -0
- package/src/runtime/agent/orchestrator/providers/command-code-oauth-login.mjs +337 -0
- package/src/runtime/agent/orchestrator/providers/command-code-oauth-tokens.mjs +159 -0
- package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +444 -0
- package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/cursor-wire-normalization.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +7 -4
- package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +21 -6
- package/src/runtime/agent/orchestrator/providers/effort-rejection.mjs +244 -0
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +41 -28
- package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/grok-oauth-tokens.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/lib/gemini-model-catalog.mjs +7 -1
- package/src/runtime/agent/orchestrator/providers/mimo-oauth-profile.mjs +8 -40
- package/src/runtime/agent/orchestrator/providers/mimo-oauth-tokens.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +51 -12
- package/src/runtime/agent/orchestrator/providers/model-cache.mjs +37 -2
- package/src/runtime/agent/orchestrator/providers/model-proxy-bridge.mjs +1080 -0
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +82 -0
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +556 -0
- package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +251 -0
- package/src/runtime/agent/orchestrator/providers/muse-code-oauth-tokens.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +69 -102
- package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +15 -0
- package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +6 -5
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +6 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-options.mjs +11 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +15 -4
- package/src/runtime/agent/orchestrator/providers/openai-oauth-catalog-store.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/openai-oauth-catalog.mjs +5 -2
- package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +4 -4
- package/src/runtime/agent/orchestrator/providers/registry.mjs +47 -7
- package/src/runtime/agent/orchestrator/runtime-core/builtin-features.mjs +10 -0
- package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +36 -3
- package/src/runtime/agent/orchestrator/runtime-core/effort.mjs +15 -11
- package/src/runtime/agent/orchestrator/runtime-core/extension-scopes.mjs +15 -55
- package/src/runtime/agent/orchestrator/runtime-core/goal-control.mjs +15 -2
- package/src/runtime/agent/orchestrator/runtime-core/model-capabilities.mjs +16 -1
- package/src/runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs +278 -84
- package/src/runtime/agent/orchestrator/runtime-core/skill-tool-loading.mjs +4 -1
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-schema.mjs +21 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +54 -3
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +10 -3
- package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +10 -0
- package/src/runtime/agent/orchestrator/session/cache/prefetch-cache.mjs +9 -5
- package/src/runtime/agent/orchestrator/session/cache/read-cache.mjs +18 -8
- package/src/runtime/agent/orchestrator/session/catalog-db/index.mjs +284 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +268 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/sqlite-gate.mjs +27 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/worker.mjs +628 -0
- package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +10 -3
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +24 -13
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +13 -5
- package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +362 -0
- package/src/runtime/agent/orchestrator/session/compaction-archive.mjs +253 -0
- package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +184 -0
- package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +136 -0
- package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +128 -0
- package/src/runtime/agent/orchestrator/session/conversation-import/imported-session.mjs +127 -0
- package/src/runtime/agent/orchestrator/session/conversation-import/turns.mjs +101 -0
- package/src/runtime/agent/orchestrator/session/explicit-skills.mjs +83 -5
- package/src/runtime/agent/orchestrator/session/imported-message.mjs +11 -0
- package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/continuation.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/diagnostic.mjs +1 -7
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/refusal.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/segments.mjs +3 -2
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +5 -3
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +7 -4
- package/src/runtime/agent/orchestrator/session/loop/steering.mjs +21 -10
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/loop/usage.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +12 -3
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +15 -3
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +22 -12
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +9 -2
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +9 -3
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +5 -2
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +37 -16
- package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +17 -4
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +90 -0
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +22 -12
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +32 -3
- package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +27 -0
- package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +16 -1
- package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager.mjs +0 -1
- package/src/runtime/agent/orchestrator/session/message-ids.mjs +123 -0
- package/src/runtime/agent/orchestrator/session/message-ids.test-support.mjs +29 -0
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +33 -22
- package/src/runtime/agent/orchestrator/session/store/listing.mjs +6 -2
- package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/store/summary-cache.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/store/sweep/sweep-finish.mjs +10 -3
- package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +11 -0
- package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +817 -0
- package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +36 -0
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +23 -12
- package/src/runtime/agent/orchestrator/session/store-summary-reader-catalog-fixture.mjs +158 -0
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +202 -374
- package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +20 -3
- package/src/runtime/agent/orchestrator/session/synthetic-user-envelope.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/tool-batch/pre-dispatch.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +48 -9
- package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +161 -0
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/invocation-plan.mjs +17 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/spawn-env.mjs +4 -0
- package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +52 -2
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +32 -22
- package/src/runtime/agent/orchestrator/tools/builtin/git-repo-rw-lock.mjs +10 -3
- package/src/runtime/agent/orchestrator/tools/builtin/lib/list-helpers.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/path-diagnostics.mjs +6 -6
- package/src/runtime/agent/orchestrator/tools/builtin/read-cache-policy.mjs +8 -0
- package/src/runtime/agent/orchestrator/tools/builtin/read-range-index.mjs +203 -33
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-fast-paths.mjs +32 -35
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-guards.mjs +8 -4
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-render.mjs +11 -12
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-stream.mjs +9 -6
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +83 -68
- package/src/runtime/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +8 -3
- package/src/runtime/agent/orchestrator/tools/builtin/read-source-windows.mjs +20 -3
- package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +16 -7
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-dispatch.mjs +2 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-publication.mjs +137 -0
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-snapshot.mjs +11 -10
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +21 -11
- package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +12 -1
- package/src/runtime/agent/orchestrator/tools/builtin/search-glob-tool.mjs +13 -10
- package/src/runtime/agent/orchestrator/tools/builtin/search-grep-tool.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +17 -12
- package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +56 -13
- package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +58 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/root-resolution.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/trusted-roots.mjs +25 -1
- package/src/runtime/agent/orchestrator/tools/patch/apply-patch/codex-batch.mjs +4 -4
- package/src/runtime/agent/orchestrator/tools/patch/apply-patch/route.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/patch/read-redirects.mjs +34 -30
- package/src/runtime/agent/orchestrator/tools/patch/sequence/units.mjs +7 -7
- package/src/runtime/agent/orchestrator/tools/patch/sequence.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/patch/ui-diff.mjs +78 -5
- package/src/runtime/agent/orchestrator/tools/progress-message.mjs +2 -0
- package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +4 -0
- package/src/runtime/attachments/store.d.mts +3 -0
- package/src/runtime/attachments/store.mjs +38 -0
- package/src/runtime/channels/lib/owned-runtime/owned-start.mjs +1 -1
- package/src/runtime/channels/lib/webhook/session-dispatch.mjs +16 -4
- package/src/runtime/channels/lib/worker-main.mjs +17 -14
- package/src/runtime/computer-bridge/linux-readiness.mjs +0 -2
- package/src/runtime/effort-judge/judge-client.mjs +56 -18
- package/src/runtime/memory/lib/memory.mjs +1 -1
- package/src/runtime/memory/lib/session-ingest.mjs +2 -2
- package/src/runtime/shared/atomic-file.mjs +33 -6
- package/src/runtime/shared/best-effort-stderr.mjs +8 -0
- package/src/runtime/shared/code-graph-state.mjs +12 -0
- package/src/runtime/shared/defaults-separation.mjs +2 -0
- package/src/runtime/shared/extension-scopes.mjs +5 -3
- package/src/runtime/shared/file-lock.mjs +37 -11
- package/src/runtime/shared/git-runner-worker.mjs +3 -2
- package/src/runtime/shared/git-runner.mjs +4 -3
- package/src/runtime/shared/llm/anthropic-effort.mjs +14 -0
- package/src/runtime/shared/llm/default-context-window.mjs +6 -0
- package/src/runtime/shared/llm/model-catalog-projection.mjs +5 -4
- package/src/runtime/shared/llm/model-catalog.mjs +240 -130
- package/src/runtime/shared/llm/provider-catalog-cache.mjs +36 -28
- package/src/runtime/shared/llm/provider-catalog-registry.mjs +162 -0
- package/src/runtime/shared/llm/provider-seed-rosters.mjs +130 -0
- package/src/runtime/shared/model-proxy-mode.d.mts +3 -0
- package/src/runtime/shared/model-proxy-mode.mjs +19 -0
- package/src/runtime/shared/plugin-manifest.mjs +18 -9
- package/src/runtime/shared/pristine-execution-contract.json +9 -2
- package/src/runtime/shared/pristine-execution.mjs +28 -4
- package/src/runtime/shared/provider-accounts.mjs +125 -21
- package/src/runtime/shared/provider-api-key.mjs +33 -1
- package/src/runtime/shared/schedule-model-ref.mjs +11 -2
- package/src/runtime/shared/session-usage-totals.mjs +19 -0
- package/src/runtime/shared/sharp-electron-linux.mjs +25 -0
- package/src/runtime/shared/shell-display.mjs +9 -3
- package/src/runtime/shared/singleton-owner.mjs +36 -6
- package/src/runtime/shared/skill-mentions.d.mts +10 -0
- package/src/runtime/shared/skill-mentions.mjs +133 -0
- package/src/runtime/shared/skill-script-runtime.mjs +264 -0
- package/src/runtime/shared/statusline/statusline-segments.mjs +68 -14
- package/src/runtime/shared/tool-primitives.mjs +35 -2
- package/src/runtime/shared/tool-surface.mjs +7 -0
- package/src/runtime/shared/tool-work-units.mjs +14 -0
- package/src/runtime/shared/transcript-metadata.mjs +17 -1
- package/src/runtime/shared/turn-changes.mjs +791 -0
- package/src/runtime/shared/turn-snapshot-store.mjs +346 -12
- package/src/runtime/shared/turn-snapshot.mjs +690 -81
- package/src/runtime/shared/turn-worktree-snapshot.mjs +185 -15
- package/src/runtime/shared/worker-requests.mjs +15 -2
- package/src/runtime/shared/worktrees/cache-cleanups.mjs +52 -0
- package/src/runtime/shared/worktrees/contract.mjs +551 -0
- package/src/runtime/shared/worktrees/disk.mjs +57 -0
- package/src/runtime/shared/worktrees/git.mjs +247 -0
- package/src/runtime/shared/worktrees/include.mjs +480 -0
- package/src/runtime/shared/worktrees/lock.mjs +46 -0
- package/src/runtime/shared/worktrees/maintenance.mjs +96 -0
- package/src/runtime/shared/worktrees/manager.mjs +1215 -0
- package/src/runtime/shared/worktrees/merge.mjs +182 -0
- package/src/runtime/shared/worktrees/owner.mjs +46 -0
- package/src/runtime/shared/worktrees/paths.mjs +62 -0
- package/src/runtime/shared/worktrees/registry.mjs +110 -0
- package/src/runtime/shared/worktrees/settings.mjs +84 -0
- package/src/runtime/shared/worktrees/slug.mjs +62 -0
- package/src/runtime/shared/worktrees/test-support.mjs +96 -0
- package/src/runtime/visualize-bridge/client.mjs +181 -0
- package/src/runtime/visualize-bridge/tool-defs.mjs +97 -0
- package/src/session-runtime/automation-agents.mjs +8 -2
- package/src/session-runtime/boot/apis.mjs +90 -0
- package/src/session-runtime/boot/config.mjs +7 -2
- package/src/session-runtime/boot/facade.mjs +21 -0
- package/src/session-runtime/boot/infrastructure.mjs +30 -18
- package/src/session-runtime/boot/session.mjs +1 -0
- package/src/session-runtime/boot/tools.mjs +4 -0
- package/src/session-runtime/config-lifecycle.mjs +10 -1
- package/src/session-runtime/conversation-import/discovery.mjs +698 -0
- package/src/session-runtime/conversation-import/service.mjs +369 -0
- package/src/session-runtime/cwd-plugins/cwd-apply.mjs +21 -6
- package/src/session-runtime/cwd-plugins/plugin-status.mjs +47 -42
- package/src/session-runtime/global-extensions.mjs +2 -2
- package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +27 -2
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +32 -1
- package/src/session-runtime/internal-tool-executor.mjs +9 -1
- package/src/session-runtime/lifecycle/session-catalog.mjs +8 -1
- package/src/session-runtime/lifecycle/session-switch/context-switch.mjs +5 -0
- package/src/session-runtime/lifecycle/session-switch/resume.mjs +21 -3
- package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +16 -0
- package/src/session-runtime/mcp-glue/connect.mjs +68 -16
- package/src/session-runtime/mcp-glue/plugin-servers.mjs +499 -0
- package/src/session-runtime/mcp-glue/server-catalog.mjs +122 -33
- package/src/session-runtime/mcp-glue/server-input.mjs +29 -0
- package/src/session-runtime/mcp-glue.mjs +22 -2
- package/src/session-runtime/model-route/route-persist.mjs +3 -0
- package/src/session-runtime/model-route/set-route.mjs +15 -2
- package/src/session-runtime/new-session-config.mjs +3 -2
- package/src/session-runtime/notification-bus.mjs +18 -3
- package/src/session-runtime/provider-auth/accounts.mjs +33 -15
- package/src/session-runtime/provider-models/row-hydration.mjs +2 -0
- package/src/session-runtime/provider-models/web-search-catalog.mjs +5 -1
- package/src/session-runtime/provider-readiness.mjs +16 -10
- package/src/session-runtime/resource-extension-sync.mjs +45 -7
- package/src/session-runtime/resource-mcp-api.mjs +121 -84
- package/src/session-runtime/resource-plugins-api.mjs +29 -41
- package/src/session-runtime/resource-scope-decorate.mjs +6 -8
- package/src/session-runtime/resource-skills-api.mjs +23 -2
- package/src/session-runtime/route-resolution.mjs +10 -1
- package/src/session-runtime/route-state.mjs +16 -2
- package/src/session-runtime/runtime-core.mjs +5 -0
- package/src/session-runtime/runtime-facade.mjs +8 -0
- package/src/session-runtime/runtime-review-api.mjs +9 -0
- package/src/session-runtime/schedule-session-run.mjs +1 -1
- package/src/session-runtime/services/agent-tool/helpers.mjs +5 -0
- package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +3 -0
- package/src/session-runtime/services/agent-tool/job-views/job-lookup.mjs +1 -0
- package/src/session-runtime/services/agent-tool/job-views/spawn-meta.mjs +1 -0
- package/src/session-runtime/services/agent-tool/lead-worker-index.mjs +15 -0
- package/src/session-runtime/services/agent-tool/send-flow.mjs +1 -0
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-plan.mjs +10 -2
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +19 -7
- package/src/session-runtime/services/agent-tool/spawn-preset.mjs +3 -0
- package/src/session-runtime/services/agent-tool/tag-registry.mjs +2 -1
- package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +5 -0
- package/src/session-runtime/services/agent-tool/worker-rows.mjs +1 -0
- package/src/session-runtime/services/agent-tool.mjs +1 -0
- package/src/session-runtime/services/channel-session-router.mjs +4 -1
- package/src/session-runtime/services/daemon-candidate.mjs +3 -2
- package/src/session-runtime/services/daemon-crash-capture/spawn-capture.mjs +55 -0
- package/src/session-runtime/services/hook-bus/command-handler.mjs +4 -1
- package/src/session-runtime/services/hook-bus/handlers.mjs +7 -3
- package/src/session-runtime/services/provider-admin.mjs +64 -18
- package/src/session-runtime/services/usage-dashboard-model.mjs +3 -0
- package/src/session-runtime/session-create/session-options.mjs +10 -1
- package/src/session-runtime/session-turn-api.mjs +1 -0
- package/src/session-runtime/settings-api.mjs +4 -2
- package/src/session-runtime/settings-builtin-tools-api.mjs +9 -2
- package/src/session-runtime/settings-worktree-api.mjs +17 -0
- package/src/session-runtime/setup-tool/executor.mjs +23 -5
- package/src/session-runtime/setup-tool/extended-actions.mjs +1 -0
- package/src/session-runtime/setup-tool/settings-contract.mjs +4 -1
- package/src/session-runtime/setup-tool/tool-defs.mjs +38 -4
- package/src/session-runtime/skills-api.mjs +7 -3
- package/src/session-runtime/turn/agent-ops.mjs +12 -1
- package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +14 -3
- package/src/session-runtime/turn/turn-run/snapshot-tracker.mjs +20 -15
- package/src/session-runtime/turn/turn-run/turn-open.mjs +4 -5
- package/src/session-runtime/turn/turn-run/turn-prepare.mjs +29 -1
- package/src/session-runtime/turn/turn-run/turn-settle.mjs +6 -1
- package/src/session-runtime/turn/turn-run.mjs +6 -7
- package/src/session-runtime/webhook-session-run.mjs +16 -4
- package/src/session-runtime/workflow-agents-api/agent-route.mjs +15 -3
- package/src/session-runtime/workspace/admission-gate.mjs +197 -0
- package/src/session-runtime/workspace/project-branches.mjs +136 -0
- package/src/session-runtime/workspace/transition-steps.mjs +364 -0
- package/src/session-runtime/workspace/transitions.mjs +651 -0
- package/src/session-runtime/workspace/workspace-state.mjs +112 -0
- package/src/skill-script-entry.mjs +36 -0
- package/src/standalone/channel-transport/http-routes.mjs +17 -0
- package/src/standalone/channel-transport.mjs +13 -1
- package/src/standalone/daemon-desktop-runtime.mjs +1 -0
- package/src/standalone/daemon-owner.mjs +372 -0
- package/src/standalone/daemon-stored-session-views.mjs +15 -4
- package/src/standalone/daemon.mjs +179 -8
- package/src/standalone/desktop-service-registry.mjs +20 -0
- package/src/standalone/local-session-runtime.mjs +11 -0
- package/src/standalone/session-client/attachment-calls.mjs +3 -1
- package/src/standalone/session-client/daemon-unavailable.mjs +62 -0
- package/src/standalone/session-client/lease-heartbeat.mjs +47 -0
- package/src/standalone/session-client.mjs +104 -13
- package/src/standalone/session-protocol.mjs +21 -0
- package/src/standalone/session-proxy.mjs +3 -0
- package/src/standalone/session-service/agent-tree/agent-child.mjs +4 -0
- package/src/standalone/session-service/agent-tree/agent-rehydrate.mjs +1 -0
- package/src/standalone/session-service/entries/lifecycle.mjs +6 -0
- package/src/standalone/session-service/projection/revisions.mjs +2 -0
- package/src/standalone/session-service/projection/transcript-window.mjs +149 -19
- package/src/standalone/session-service/projection/workspace.mjs +14 -0
- package/src/standalone/session-service/session-calls/session-action.mjs +17 -4
- package/src/standalone/session-transport/call-route.mjs +13 -0
- package/src/standalone/session-transport/client-registry/client-grace.mjs +45 -3
- package/src/standalone/session-transport/client-registry/registration-replays.mjs +1 -0
- package/src/standalone/session-transport/client-registry.mjs +20 -3
- package/src/standalone/session-transport/routes.mjs +79 -2
- package/src/standalone/session-transport/stack.mjs +2 -0
- package/src/standalone/session-transport.mjs +24 -1
- package/src/tui/App.jsx +9 -1
- package/src/tui/app/app-view/floating-panel.jsx +9 -1
- package/src/tui/app/app-view.jsx +6 -0
- package/src/tui/app/extension-pickers/plugins-pickers.mjs +35 -16
- package/src/tui/app/message-selector.mjs +2 -1
- package/src/tui/app/model-options.mjs +37 -5
- package/src/tui/app/model-picker/provider-models-list.mjs +2 -2
- package/src/tui/app/model-picker.mjs +4 -2
- package/src/tui/app/skill-mention-palette.mjs +88 -0
- package/src/tui/app/slash-palette.mjs +23 -7
- package/src/tui/app/transcript-row-estimate.mjs +11 -3
- package/src/tui/app/use-prompt-draft-flow.mjs +7 -2
- package/src/tui/app/use-prompt-state.mjs +5 -0
- package/src/tui/components/Message.jsx +22 -1
- package/src/tui/components/PromptInput.jsx +21 -3
- package/src/tui/components/SlashCommandPalette.jsx +12 -3
- package/src/tui/components/TranscriptItem.jsx +9 -1
- package/src/tui/components/prompt-input/key-handler.mjs +21 -8
- package/src/tui/components/user-message-skills.mjs +38 -0
- package/src/tui/dist/index.mjs +839 -207
- package/src/tui/index.jsx +10 -2
- package/src/tui/session/compaction-view.mjs +147 -0
- package/src/tui/session/context-state.mjs +6 -0
- package/src/tui/session/initial-state.mjs +3 -0
- package/src/tui/session/queue-helpers.mjs +7 -2
- package/src/tui/session/quota-resume.mjs +2 -1
- package/src/tui/session/session-api/commands.mjs +10 -2
- package/src/tui/session/session-api/controls.mjs +4 -0
- package/src/tui/session/session-api/extensions.mjs +4 -2
- package/src/tui/session/session-api/intake/rewind.mjs +2 -1
- package/src/tui/session/session-api/integrations.mjs +17 -0
- package/src/tui/session/session-api/lifecycle/dispose.mjs +6 -1
- package/src/tui/session/session-api/lifecycle/inheritance.mjs +2 -0
- package/src/tui/session/session-api/lifecycle/resume.mjs +3 -0
- package/src/tui/session/session-api/routes.mjs +10 -1
- package/src/tui/session/session-api/settings.mjs +9 -0
- package/src/tui/session/session-api/worktree.mjs +60 -0
- package/src/tui/session/session-api.mjs +2 -0
- package/src/tui/session/session-flow/auto-clear.mjs +31 -12
- package/src/tui/session/session-flow/drain.mjs +27 -3
- package/src/tui/session/session-flow/session-reset.mjs +4 -3
- package/src/tui/session/session-flow.mjs +2 -0
- package/src/tui/session/transcript-intake.mjs +14 -5
- package/src/tui/session/tui-steering-persist.mjs +25 -9
- package/src/tui/session/turn-stream.mjs +32 -4
- package/src/tui/session/turn-tool-cards.mjs +10 -2
- package/src/tui/session/turn.mjs +56 -10
- package/src/runtime/effort-judge/previous-human-request.mjs +0 -162
- package/src/runtime/effort-judge/verified-context-input.mjs +0 -10
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSONL lines of a foreign conversation → normalized plain-text turns, one
|
|
3
|
+
* line at a time: each parsed record is reduced to its kept plain text and
|
|
4
|
+
* dropped. Tolerates a BOM, CRLF line ends and individual malformed lines
|
|
5
|
+
* (reported as the `malformed` warning). Pure, no IO.
|
|
6
|
+
*/
|
|
7
|
+
import { createClaudeReducer } from './claude.mjs';
|
|
8
|
+
import { createCodexReducer } from './codex.mjs';
|
|
9
|
+
import { IMPORT_WARNING_MALFORMED, conversationTitle, normalizeTurns } from './turns.mjs';
|
|
10
|
+
|
|
11
|
+
export const IMPORT_SOURCES = Object.freeze(['codex', 'claude']);
|
|
12
|
+
/** A line or converted transcript beyond the import limits fails the file. */
|
|
13
|
+
export const IMPORT_ERROR_TOO_LARGE = 'tooLarge';
|
|
14
|
+
export const IMPORT_MAX_LINE_BYTES = 32 * 1024 * 1024;
|
|
15
|
+
/** Bytes read from one source file before it fails as `tooLarge`. */
|
|
16
|
+
export const IMPORT_MAX_FILE_BYTES = 256 * 1024 * 1024;
|
|
17
|
+
/** Normalized transcript text (the final turns, separators and stub included). */
|
|
18
|
+
export const IMPORT_MAX_TRANSCRIPT_CHARS = 4 * 1024 * 1024;
|
|
19
|
+
/** Entries a reducer may retain (Claude chain links, Codex turns). */
|
|
20
|
+
export const IMPORT_MAX_RETAINED_ENTRIES = 200_000;
|
|
21
|
+
/** Bookkeeping a reducer retains besides kept text (ids, links, timestamps). */
|
|
22
|
+
export const IMPORT_MAX_METADATA_BYTES = 16 * 1024 * 1024;
|
|
23
|
+
|
|
24
|
+
export function isImportSource(value) {
|
|
25
|
+
return IMPORT_SOURCES.includes(value);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const transcriptChars = (turns) => turns.reduce((total, turn) => total + turn.text.length, 0);
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Incremental converter. `pushLine(line)` returns false once the conversion
|
|
32
|
+
* is over a limit (`tooLarge`): kept text over `maxTranscriptChars`, retained
|
|
33
|
+
* entries over `maxRetainedEntries` or their bookkeeping over
|
|
34
|
+
* `maxMetadataBytes`; `markTooLarge()` records a limit enforced by the reader
|
|
35
|
+
* (an over-long line or file). `result()` may be called repeatedly and also
|
|
36
|
+
* holds the normalized turns to `maxTranscriptChars`.
|
|
37
|
+
* `errorCode` is null for an importable result, else one of `empty` |
|
|
38
|
+
* `malformed` | `unsupported` | `tooLarge` (whose turns are not returned).
|
|
39
|
+
*/
|
|
40
|
+
export function createConversationConverter(
|
|
41
|
+
source,
|
|
42
|
+
{
|
|
43
|
+
maxTranscriptChars = IMPORT_MAX_TRANSCRIPT_CHARS,
|
|
44
|
+
maxRetainedEntries = IMPORT_MAX_RETAINED_ENTRIES,
|
|
45
|
+
maxMetadataBytes = IMPORT_MAX_METADATA_BYTES,
|
|
46
|
+
} = {}
|
|
47
|
+
) {
|
|
48
|
+
if (!isImportSource(source)) throw new TypeError('source must be codex or claude.');
|
|
49
|
+
const reducer = source === 'codex' ? createCodexReducer() : createClaudeReducer();
|
|
50
|
+
let lines = 0;
|
|
51
|
+
let records = 0;
|
|
52
|
+
let malformed = 0;
|
|
53
|
+
let tooLarge = false;
|
|
54
|
+
return {
|
|
55
|
+
markTooLarge() {
|
|
56
|
+
tooLarge = true;
|
|
57
|
+
},
|
|
58
|
+
pushLine(raw) {
|
|
59
|
+
if (tooLarge) return false;
|
|
60
|
+
let line = String(raw);
|
|
61
|
+
if (lines++ === 0 && line.charCodeAt(0) === 0xfeff) line = line.slice(1);
|
|
62
|
+
if (line.endsWith('\r')) line = line.slice(0, -1);
|
|
63
|
+
if (!line.trim()) return true;
|
|
64
|
+
let value;
|
|
65
|
+
try {
|
|
66
|
+
value = JSON.parse(line);
|
|
67
|
+
} catch {
|
|
68
|
+
malformed += 1;
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
72
|
+
malformed += 1;
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
records += 1;
|
|
76
|
+
reducer.push(value);
|
|
77
|
+
if (
|
|
78
|
+
reducer.chars > maxTranscriptChars ||
|
|
79
|
+
reducer.entries > maxRetainedEntries ||
|
|
80
|
+
reducer.metadataBytes > maxMetadataBytes
|
|
81
|
+
) {
|
|
82
|
+
tooLarge = true;
|
|
83
|
+
}
|
|
84
|
+
return !tooLarge;
|
|
85
|
+
},
|
|
86
|
+
result() {
|
|
87
|
+
const parsed = reducer.result();
|
|
88
|
+
const turns = normalizeTurns(parsed.rawTurns);
|
|
89
|
+
// Joins and the user stub add text the reducer never counted.
|
|
90
|
+
if (transcriptChars(turns) > maxTranscriptChars) tooLarge = true;
|
|
91
|
+
const warnings = new Set(parsed.warnings);
|
|
92
|
+
if (malformed) warnings.add(IMPORT_WARNING_MALFORMED);
|
|
93
|
+
let errorCode = null;
|
|
94
|
+
if (tooLarge) errorCode = IMPORT_ERROR_TOO_LARGE;
|
|
95
|
+
else if (!records) errorCode = malformed ? 'malformed' : 'empty';
|
|
96
|
+
else if (!parsed.recognized) errorCode = 'unsupported';
|
|
97
|
+
else if (!turns.length) errorCode = 'empty';
|
|
98
|
+
return {
|
|
99
|
+
source,
|
|
100
|
+
errorCode,
|
|
101
|
+
foreignSessionId: parsed.foreignSessionId,
|
|
102
|
+
cwd: parsed.cwd,
|
|
103
|
+
turns: tooLarge ? [] : turns,
|
|
104
|
+
title: conversationTitle(turns),
|
|
105
|
+
messageCount: tooLarge ? 0 : turns.length,
|
|
106
|
+
warnings: [...warnings],
|
|
107
|
+
};
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** Convert an in-memory JSONL body; `complete:false` drops a trailing partial line. */
|
|
113
|
+
export function convertConversationText(source, text, { complete = true, ...limits } = {}) {
|
|
114
|
+
const converter = createConversationConverter(source, limits);
|
|
115
|
+
let body = String(text ?? '');
|
|
116
|
+
if (!complete) {
|
|
117
|
+
const end = body.lastIndexOf('\n');
|
|
118
|
+
body = end < 0 ? '' : body.slice(0, end);
|
|
119
|
+
}
|
|
120
|
+
let start = 0;
|
|
121
|
+
while (start < body.length) {
|
|
122
|
+
const newline = body.indexOf('\n', start);
|
|
123
|
+
const end = newline < 0 ? body.length : newline;
|
|
124
|
+
if (!converter.pushLine(body.slice(start, end))) break;
|
|
125
|
+
start = end + 1;
|
|
126
|
+
}
|
|
127
|
+
return converter.result();
|
|
128
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Materialize an imported conversation as an ordinary Lead session: the normal
|
|
3
|
+
* createSession initialization (route, tools, context limits, cache config,
|
|
4
|
+
* system messages, BP3 fields) followed by the converted plain-text turns,
|
|
5
|
+
* with `importOrigin` provenance. Persisted durably before returning, then
|
|
6
|
+
* dropped from the same-process live cache.
|
|
7
|
+
*/
|
|
8
|
+
import { createSession } from '../manager/session-lifecycle.mjs';
|
|
9
|
+
import { deleteSession, evictLiveSession, saveSessionAsync } from '../store.mjs';
|
|
10
|
+
import { newTranscriptMessageId } from '../../../../shared/transcript-metadata.mjs';
|
|
11
|
+
import { writeStderrBestEffort } from '../../../../shared/best-effort-stderr.mjs';
|
|
12
|
+
import { IMPORTED_MESSAGE_SOURCE } from '../imported-message.mjs';
|
|
13
|
+
import { ingestSessionTurn } from '../../../../session-search/session-search-ingest.mjs';
|
|
14
|
+
|
|
15
|
+
export const IMPORT_ORIGIN_VERSION = 1;
|
|
16
|
+
|
|
17
|
+
/** Dedupe identity: the foreign session id, else the canonical source path. */
|
|
18
|
+
export function importOriginKey(source, foreignSessionId, canonicalPath) {
|
|
19
|
+
return foreignSessionId ? `${source}:${foreignSessionId}` : `${source}:path:${canonicalPath}`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function buildImportOrigin({ source, sourcePath, foreignSessionId = null, sourceCwd = null, importedAt }) {
|
|
23
|
+
return {
|
|
24
|
+
version: IMPORT_ORIGIN_VERSION,
|
|
25
|
+
source,
|
|
26
|
+
sourcePath,
|
|
27
|
+
foreignSessionId: foreignSessionId || null,
|
|
28
|
+
sourceCwd: sourceCwd || null,
|
|
29
|
+
importedAt,
|
|
30
|
+
key: importOriginKey(source, foreignSessionId, sourcePath),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Lead identity every imported session carries (what a normal Lead session has).
|
|
35
|
+
const LEAD_FIELDS = Object.freeze({ owner: 'cli', agent: 'lead', lane: 'cli', sourceType: 'lead', sourceName: 'main' });
|
|
36
|
+
// Ownership, live binding and placement the importing runtime must not lend.
|
|
37
|
+
const NOT_INHERITED = [
|
|
38
|
+
'id',
|
|
39
|
+
'parentSessionId',
|
|
40
|
+
'ownerSessionId',
|
|
41
|
+
'visibility',
|
|
42
|
+
'agentTag',
|
|
43
|
+
'taskType',
|
|
44
|
+
'permission',
|
|
45
|
+
'permissionMode',
|
|
46
|
+
'clientHostPid',
|
|
47
|
+
'mcpScopeId',
|
|
48
|
+
'approvalMode',
|
|
49
|
+
'scopeKey',
|
|
50
|
+
'cwd',
|
|
51
|
+
'desktopSession',
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
/** Lead createSession options → options of a fresh, unbound imported session. */
|
|
55
|
+
export function importedSessionOptions(leadOptions, { cwd = null, desktopSession }) {
|
|
56
|
+
const options = { ...leadOptions };
|
|
57
|
+
for (const key of NOT_INHERITED) delete options[key];
|
|
58
|
+
Object.assign(options, LEAD_FIELDS);
|
|
59
|
+
if (cwd) options.cwd = cwd;
|
|
60
|
+
options.desktopSession = desktopSession;
|
|
61
|
+
return options;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Normalized turns → plain user/assistant transcript messages. `meta.source`
|
|
65
|
+
* marks them imported prose: restore and the model wire never read their text
|
|
66
|
+
* as runtime control content (reminders, task envelopes, summaries). */
|
|
67
|
+
export function importedTranscriptMessages(turns, importedAt) {
|
|
68
|
+
return turns.map((turn) => ({
|
|
69
|
+
role: turn.role,
|
|
70
|
+
content: turn.text,
|
|
71
|
+
meta: {
|
|
72
|
+
source: IMPORTED_MESSAGE_SOURCE,
|
|
73
|
+
transcript: { messageId: newTranscriptMessageId(), at: turn.at || importedAt },
|
|
74
|
+
},
|
|
75
|
+
}));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
79
|
+
|
|
80
|
+
// createSession's own debounced save keeps the id pinned until it settles.
|
|
81
|
+
async function evictWhenSettled(id, timeoutMs) {
|
|
82
|
+
const deadline = Date.now() + timeoutMs;
|
|
83
|
+
while (!evictLiveSession(id)) {
|
|
84
|
+
if (Date.now() >= deadline) return false;
|
|
85
|
+
await sleep(25);
|
|
86
|
+
}
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Create, fill and persist one imported session. Save failures reject (the
|
|
92
|
+
* half-made record is removed); once saved it is handed to content-search
|
|
93
|
+
* ingestion (the same fire-and-forget ingest a finished turn gets) so it is
|
|
94
|
+
* searchable right away. The live-cache eviction afterwards is bookkeeping only.
|
|
95
|
+
*/
|
|
96
|
+
export async function createImportedSession({
|
|
97
|
+
leadOptions,
|
|
98
|
+
conversation,
|
|
99
|
+
origin,
|
|
100
|
+
cwd = null,
|
|
101
|
+
desktopSession,
|
|
102
|
+
createSessionFn = createSession,
|
|
103
|
+
ingestFn = ingestSessionTurn,
|
|
104
|
+
evictTimeoutMs = 5_000,
|
|
105
|
+
}) {
|
|
106
|
+
const session = createSessionFn(importedSessionOptions(leadOptions, { cwd, desktopSession }));
|
|
107
|
+
try {
|
|
108
|
+
// Append after the system layer createSession seeded; never replace it.
|
|
109
|
+
session.messages.push(...importedTranscriptMessages(conversation.turns, origin.importedAt));
|
|
110
|
+
session.importOrigin = origin;
|
|
111
|
+
if (conversation.title) session.title = conversation.title;
|
|
112
|
+
session.updatedAt = Date.now();
|
|
113
|
+
await saveSessionAsync(session);
|
|
114
|
+
} catch (error) {
|
|
115
|
+
try {
|
|
116
|
+
deleteSession(session.id);
|
|
117
|
+
} catch (cleanupError) {
|
|
118
|
+
writeStderrBestEffort(
|
|
119
|
+
`[conversation-import] cleanup of ${session.id} failed: ${cleanupError?.message || cleanupError}\n`
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
throw error;
|
|
123
|
+
}
|
|
124
|
+
ingestFn(session);
|
|
125
|
+
await evictWhenSettled(session.id, evictTimeoutMs);
|
|
126
|
+
return session;
|
|
127
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared shape of an imported conversation: plain-text user/assistant turns,
|
|
3
|
+
* the markers that stand in for dropped content, and the title rule. Codex and
|
|
4
|
+
* Claude Code parsers both emit raw turns `{ role, parts: [{ text, note }], at }`
|
|
5
|
+
* and normalize them here. Pure, leaf, no IO.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/** Language-neutral warning codes attached to an imported conversation. */
|
|
9
|
+
export const IMPORT_WARNING_IMAGE_OMITTED = 'imageOmitted';
|
|
10
|
+
export const IMPORT_WARNING_MALFORMED = 'malformed';
|
|
11
|
+
export const IMPORT_WARNING_CWD_MISSING = 'cwdMissing';
|
|
12
|
+
|
|
13
|
+
/** Stands in for an image the importer does not carry over. */
|
|
14
|
+
export const IMAGE_OMITTED_TEXT = '[image omitted]';
|
|
15
|
+
/** Leads a conversation whose first kept turn is the assistant's. */
|
|
16
|
+
export const IMPORTED_USER_STUB_TEXT = '[Imported conversation continues from an earlier assistant reply.]';
|
|
17
|
+
|
|
18
|
+
const TITLE_MAX_CHARS = 100;
|
|
19
|
+
|
|
20
|
+
/** Plain-text note for a tool call: its name only, never its arguments. */
|
|
21
|
+
export function toolNote(name) {
|
|
22
|
+
const clean = String(name || '')
|
|
23
|
+
.replace(/\s+/g, ' ')
|
|
24
|
+
.trim();
|
|
25
|
+
return { text: `[ran tool: ${clean || 'unknown'}]`, note: true };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function textPart(text) {
|
|
29
|
+
return { text: String(text), note: false };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Whole-text match of one XML-style envelope element (`<tag>…</tag>`). */
|
|
33
|
+
export function isWholeElement(text, tags) {
|
|
34
|
+
const trimmed = String(text || '').trim();
|
|
35
|
+
return tags.some(
|
|
36
|
+
(tag) => (trimmed.startsWith(`<${tag}>`) || trimmed.startsWith(`<${tag} `)) && trimmed.endsWith(`</${tag}>`)
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function timestampMs(value) {
|
|
41
|
+
const ms = typeof value === 'number' ? value : Date.parse(String(value || ''));
|
|
42
|
+
return Number.isFinite(ms) && ms > 0 ? ms : null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Consecutive tool notes stay one per line; every other boundary is a blank line.
|
|
46
|
+
function joinParts(parts) {
|
|
47
|
+
let text = '';
|
|
48
|
+
let previous = null;
|
|
49
|
+
for (const part of parts) {
|
|
50
|
+
const piece = String(part.text || '').trim();
|
|
51
|
+
if (!piece) continue;
|
|
52
|
+
if (previous) text += previous.note && part.note ? '\n' : '\n\n';
|
|
53
|
+
text += piece;
|
|
54
|
+
previous = part;
|
|
55
|
+
}
|
|
56
|
+
return { text, last: previous };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Raw turns → normalized turns `{ role, text, at, stub? }`: empty turns dropped,
|
|
61
|
+
* consecutive same-role turns merged, and a short user stub inserted when the
|
|
62
|
+
* conversation would otherwise begin with the assistant.
|
|
63
|
+
*/
|
|
64
|
+
export function normalizeTurns(rawTurns) {
|
|
65
|
+
const merged = [];
|
|
66
|
+
for (const raw of rawTurns) {
|
|
67
|
+
if (raw?.role !== 'user' && raw?.role !== 'assistant') continue;
|
|
68
|
+
const parts = (Array.isArray(raw.parts) ? raw.parts : []).filter((part) => String(part?.text || '').trim());
|
|
69
|
+
if (!parts.length) continue;
|
|
70
|
+
const previous = merged[merged.length - 1];
|
|
71
|
+
if (previous && previous.role === raw.role) previous.parts.push(...parts);
|
|
72
|
+
else merged.push({ role: raw.role, parts: [...parts], at: timestampMs(raw.at) });
|
|
73
|
+
}
|
|
74
|
+
const turns = merged.map(({ role, parts, at }) => {
|
|
75
|
+
const turn = { role, text: joinParts(parts).text, at };
|
|
76
|
+
// A carried-over compaction summary never names the conversation.
|
|
77
|
+
if (parts.some((part) => part.summary)) turn.titleText = joinParts(parts.filter((part) => !part.summary)).text;
|
|
78
|
+
return turn;
|
|
79
|
+
});
|
|
80
|
+
if (turns[0]?.role === 'assistant') {
|
|
81
|
+
turns.unshift({ role: 'user', text: IMPORTED_USER_STUB_TEXT, at: turns[0].at, stub: true });
|
|
82
|
+
}
|
|
83
|
+
return turns;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const titleLines = (turn) =>
|
|
87
|
+
(turn.titleText ?? turn.text)
|
|
88
|
+
.split('\n')
|
|
89
|
+
.map((value) => value.replace(/\s+/g, ' ').trim())
|
|
90
|
+
.filter(Boolean);
|
|
91
|
+
|
|
92
|
+
/** First line of the first genuine user turn, whitespace-normalized, ≤100 chars
|
|
93
|
+
* (an image marker only when the turn has no text line). */
|
|
94
|
+
export function conversationTitle(turns) {
|
|
95
|
+
const first = turns.find((turn) => turn.role === 'user' && !turn.stub && titleLines(turn).length);
|
|
96
|
+
if (!first) return null;
|
|
97
|
+
const lines = titleLines(first);
|
|
98
|
+
const line = lines.find((value) => value !== IMAGE_OMITTED_TEXT) || lines[0];
|
|
99
|
+
if (!line) return null;
|
|
100
|
+
return Array.from(line).slice(0, TITLE_MAX_CHARS).join('').trim() || null;
|
|
101
|
+
}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
2
|
import { selectedSkillName } from '../../../shared/skill-selection.mjs';
|
|
3
|
+
import { mentionedSkillNames, skillMentionCandidates } from '../../../shared/skill-mentions.mjs';
|
|
4
|
+
import { isAttachmentReference } from '../../../attachments/store.mjs';
|
|
5
|
+
import { collectPromptSkillsCached } from '../context/collect.mjs';
|
|
3
6
|
import { executeTool } from './loop/tool-exec.mjs';
|
|
7
|
+
import { preDispatchDenyForSession } from './loop/pre-dispatch-deny.mjs';
|
|
4
8
|
import { parseNativeToolSearchPayload } from './loop/tool-helpers.mjs';
|
|
5
9
|
import { normalizeToolEnvelope } from './tool-envelope.mjs';
|
|
6
10
|
import { throwIfAborted } from '../../../shared/abort-race.mjs';
|
|
@@ -20,19 +24,91 @@ function nativeDefinitionsPresent(native, messages) {
|
|
|
20
24
|
);
|
|
21
25
|
}
|
|
22
26
|
|
|
27
|
+
// At most this many skills are prepared for one user turn; further selections
|
|
28
|
+
// stay prose.
|
|
29
|
+
const MAX_EXPLICIT_SKILLS = 5;
|
|
30
|
+
const LOADED_SKILL_RE = /^Loaded (?:built-in )?skill: /;
|
|
31
|
+
|
|
32
|
+
// The user's own typed text: plain strings and text parts, never attachment
|
|
33
|
+
// references (file contents the user attached, not words they typed).
|
|
34
|
+
function userPromptText(prompt) {
|
|
35
|
+
if (typeof prompt === 'string') return prompt;
|
|
36
|
+
if (!Array.isArray(prompt)) return '';
|
|
37
|
+
return prompt
|
|
38
|
+
.map((part) => {
|
|
39
|
+
if (typeof part === 'string') return part;
|
|
40
|
+
if (part?.type === 'text' && typeof part.text === 'string' && !isAttachmentReference(part)) return part.text;
|
|
41
|
+
return '';
|
|
42
|
+
})
|
|
43
|
+
.join('\n');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** The skills a prompt explicitly selects: the leading `Skill: "name"` header,
|
|
47
|
+
* then every inline `$name`, each only when it names a skill enabled and in
|
|
48
|
+
* scope for `cwd`, in first-mention order, deduplicated and capped. */
|
|
49
|
+
export function explicitSkillNames(prompt, cwd) {
|
|
50
|
+
const header = selectedSkillName(prompt);
|
|
51
|
+
const text = userPromptText(prompt);
|
|
52
|
+
if (!header && !skillMentionCandidates(text).length) return [];
|
|
53
|
+
const available = new Set(collectPromptSkillsCached(cwd).map((skill) => skill.name));
|
|
54
|
+
const isSkill = (name) => available.has(name);
|
|
55
|
+
const names = header && isSkill(header) ? [header] : [];
|
|
56
|
+
for (const name of mentionedSkillNames(text, isSkill)) {
|
|
57
|
+
if (!names.includes(name)) names.push(name);
|
|
58
|
+
}
|
|
59
|
+
return names.slice(0, MAX_EXPLICIT_SKILLS);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Stamps the skills a user turn actually loaded on its transcript metadata,
|
|
63
|
+
* where the live row, the persisted transcript and restore all read it.
|
|
64
|
+
* The stamped message REPLACES `message` in `messages` (returned): the
|
|
65
|
+
* session store ships reference-identical messages as an append-only delta,
|
|
66
|
+
* so an in-place edit of a user message an earlier save already sent would
|
|
67
|
+
* never reach disk. */
|
|
68
|
+
export function recordAppliedSkills(messages, message, names) {
|
|
69
|
+
if (!message || !Array.isArray(names) || !names.length) return message;
|
|
70
|
+
const meta = message.meta && typeof message.meta === 'object' ? message.meta : {};
|
|
71
|
+
const transcript = meta.transcript && typeof meta.transcript === 'object' ? meta.transcript : {};
|
|
72
|
+
const stamped = { ...message, meta: { ...meta, transcript: { ...transcript, appliedSkills: [...names] } } };
|
|
73
|
+
const index = Array.isArray(messages) ? messages.indexOf(message) : -1;
|
|
74
|
+
if (index >= 0) messages[index] = stamped;
|
|
75
|
+
return stamped;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Tells the host which skills a user turn loaded; a host callback failure
|
|
79
|
+
* never fails the turn. */
|
|
80
|
+
export function notifySkillsApplied(onSkillsApplied, applied, message) {
|
|
81
|
+
if (!applied.length) return;
|
|
82
|
+
try {
|
|
83
|
+
onSkillsApplied?.(applied, { messageId: message?.meta?.transcript?.messageId || null });
|
|
84
|
+
} catch {
|
|
85
|
+
/* best-effort host callback */
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
23
89
|
// Called before the first model request for a real user turn. This performs the
|
|
24
90
|
// same policy-checked Skill operation, without asking the model to request it.
|
|
25
91
|
// Native schemas still travel as a properly paired loader execution, never as
|
|
26
|
-
// new eager definitions or an orphan tool_search_output.
|
|
92
|
+
// new eager definitions or an orphan tool_search_output. Returns the names of
|
|
93
|
+
// the skills that loaded successfully, in selection order.
|
|
94
|
+
// `selection`, when given (even empty), is the turn's TRUSTED selection input
|
|
95
|
+
// and the only text skills are selected from: a host whose model-visible
|
|
96
|
+
// prompt carries external data (webhook payloads) passes just the
|
|
97
|
+
// operator-authored part, so nothing inside the data can load a skill.
|
|
27
98
|
export async function prepareExplicitSkills(
|
|
28
99
|
prompt,
|
|
29
100
|
messages,
|
|
30
101
|
session,
|
|
31
|
-
{ cwd = session?.cwd, signal, execute = executeTool } = {}
|
|
102
|
+
{ cwd = session?.cwd, signal, execute = executeTool, selection } = {}
|
|
32
103
|
) {
|
|
33
104
|
throwIfAborted(signal);
|
|
34
|
-
const
|
|
35
|
-
|
|
105
|
+
const applied = [];
|
|
106
|
+
// A session that cannot call Skill (schema allowlist, denied tools, read-only
|
|
107
|
+
// surface — the dispatcher's own gate) has nothing to select: preparing would
|
|
108
|
+
// only push a refused-tool reminder into history on every matching turn.
|
|
109
|
+
if (preDispatchDenyForSession(session, { name: 'Skill' })) return applied;
|
|
110
|
+
const source = selection === undefined || selection === null ? prompt : selection;
|
|
111
|
+
for (const name of explicitSkillNames(source, cwd)) {
|
|
36
112
|
const id = `skill-selection-${randomUUID()}`;
|
|
37
113
|
const args = { name };
|
|
38
114
|
const raw = await execute('Skill', args, cwd, session?.id, session, { signal, toolCallId: id });
|
|
@@ -55,6 +131,8 @@ export async function prepareExplicitSkills(
|
|
|
55
131
|
});
|
|
56
132
|
}
|
|
57
133
|
messages.push(...envelope.newMessages);
|
|
134
|
+
if (LOADED_SKILL_RE.test(String(summary))) applied.push(name);
|
|
135
|
+
throwIfAborted(signal);
|
|
58
136
|
}
|
|
59
|
-
|
|
137
|
+
return applied;
|
|
60
138
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provenance of messages carried over from another app's conversation
|
|
3
|
+
* (conversation-import). Their text is prose someone wrote elsewhere: it may
|
|
4
|
+
* look like a runtime reminder, a task envelope or a compaction summary, but
|
|
5
|
+
* restore and the model wire treat it as plain user/assistant text. Leaf, no IO.
|
|
6
|
+
*/
|
|
7
|
+
export const IMPORTED_MESSAGE_SOURCE = 'import';
|
|
8
|
+
|
|
9
|
+
export function isImportedMessage(message) {
|
|
10
|
+
return message?.meta?.source === IMPORTED_MESSAGE_SOURCE;
|
|
11
|
+
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// (signature + stub) and the edit-progress classification.
|
|
3
3
|
// Pure string/signature helpers; the loop body only wires state + messages.
|
|
4
4
|
// No provider/manager coupling.
|
|
5
|
+
import { knownMcpToolIdentity } from '../../../../shared/tool-primitives.mjs';
|
|
5
6
|
|
|
6
7
|
// Deterministic, key-sorted stringify for cross-turn call signatures (recursive,
|
|
7
8
|
// unlike tool-classify's top-level-only _canonicalArgs).
|
|
@@ -43,7 +44,7 @@ const NON_PROGRESS_TOOLS = new Set(['Skill', 'recall', 'agent', 'task', 'cwd', '
|
|
|
43
44
|
// not in the meta/non-progress set. apply_patch and shell/bash always count.
|
|
44
45
|
export function isEditProgressTool(name, isEager) {
|
|
45
46
|
if (isEager) return false;
|
|
46
|
-
const bare = name?.startsWith('mcp__') ? name.split('__').pop() : name;
|
|
47
|
+
const bare = name?.startsWith('mcp__') ? knownMcpToolIdentity(name)?.tool || name.split('__').pop() : name;
|
|
47
48
|
if (bare === 'apply_patch' || bare === 'shell' || bare === 'bash' || bare === 'bash_session') return true;
|
|
48
49
|
return !NON_PROGRESS_TOOLS.has(bare);
|
|
49
50
|
}
|
|
@@ -146,7 +146,7 @@ export function createLoopState({ provider, messages, model, tools, cwd, sendOpt
|
|
|
146
146
|
messages,
|
|
147
147
|
opts,
|
|
148
148
|
sessionId,
|
|
149
|
-
onSkillPrompt: (content) => state.pendingSkillPrompts.push(content),
|
|
149
|
+
onSkillPrompt: (content, message) => state.pendingSkillPrompts.push({ content, message }),
|
|
150
150
|
});
|
|
151
151
|
// Bounded recovery for provider turns that returned no client tool calls
|
|
152
152
|
// (max-output ladder, refusal retry, provider continuation, empty-turn
|
|
@@ -34,7 +34,7 @@ export function resumeContinuation(
|
|
|
34
34
|
return false;
|
|
35
35
|
}
|
|
36
36
|
if (!segments.commitIntermediate(response)) return false;
|
|
37
|
-
if (hasContent) segments.record(response.content);
|
|
37
|
+
if (hasContent) segments.record(response.content, { committed: true });
|
|
38
38
|
state.providerContinuationCount += 1;
|
|
39
39
|
state.continuationsSinceToolBatch += 1;
|
|
40
40
|
state.emptyNudgeStreak = 0;
|
|
@@ -1,8 +1,2 @@
|
|
|
1
1
|
// stderr diagnostics for the no-tool recovery ladders; never throws.
|
|
2
|
-
export
|
|
3
|
-
try {
|
|
4
|
-
process.stderr.write(line);
|
|
5
|
-
} catch {
|
|
6
|
-
/* diagnostics only */
|
|
7
|
-
}
|
|
8
|
-
}
|
|
2
|
+
export { writeStderrBestEffort as writeLoopDiagnostic } from '../../../../../shared/best-effort-stderr.mjs';
|
|
@@ -15,7 +15,7 @@ export function resolveRefusal(response, hasContent, { state, segments, messages
|
|
|
15
15
|
// A provider may emit harmless narration before its safety classifier
|
|
16
16
|
// terminates the turn. Preserve that partial turn and its stop reason,
|
|
17
17
|
// but never mistake the non-empty text for a successful completion.
|
|
18
|
-
if (hasContent && segments.commitIntermediate(response)) segments.record(response.content);
|
|
18
|
+
if (hasContent && segments.commitIntermediate(response)) segments.record(response.content, { committed: true });
|
|
19
19
|
messages.push({
|
|
20
20
|
role: 'user',
|
|
21
21
|
content: REFUSAL_RECOVERY_PROMPT,
|
|
@@ -21,14 +21,15 @@ export function createTurnSegments({ messages, opts, suppressMidTurnText }) {
|
|
|
21
21
|
// live. A suppressed sub-agent session neither surfaces nor accumulates
|
|
22
22
|
// text — except on the max-output ladder, whose parts must still rebuild
|
|
23
23
|
// the complete answer the caller receives (keepWhenSuppressed).
|
|
24
|
-
|
|
24
|
+
// `committed: true` marks text that is the just-committed message's own.
|
|
25
|
+
record(text, { keepWhenSuppressed = false, committed = false } = {}) {
|
|
25
26
|
if (suppressMidTurnText) {
|
|
26
27
|
if (keepWhenSuppressed) parts.push(text);
|
|
27
28
|
return;
|
|
28
29
|
}
|
|
29
30
|
parts.push(text);
|
|
30
31
|
try {
|
|
31
|
-
opts.onAssistantText?.(text);
|
|
32
|
+
opts.onAssistantText?.(text, { committed });
|
|
32
33
|
} catch {
|
|
33
34
|
/* best-effort */
|
|
34
35
|
}
|
|
@@ -34,8 +34,9 @@ export function createNoToolTurnResolver({
|
|
|
34
34
|
refusalRetryUsed: false,
|
|
35
35
|
emptyNudgeStreak: 0,
|
|
36
36
|
// Structured provider continuation signals honored this turn. Diagnostic
|
|
37
|
-
// only
|
|
38
|
-
//
|
|
37
|
+
// only: the loop has no iteration cap, so this count bounds nothing. A
|
|
38
|
+
// text-only continuation runaway is bounded by continuationsSinceToolBatch
|
|
39
|
+
// below; continuations interleaved with tool batches are not limited.
|
|
39
40
|
providerContinuationCount: 0,
|
|
40
41
|
// Continuations since the last executed tool batch — bounds the text-only
|
|
41
42
|
// continuation runaway (see PROVIDER_CONTINUATION_NO_TOOL_LIMIT).
|
|
@@ -108,7 +109,8 @@ export function createNoToolTurnResolver({
|
|
|
108
109
|
drainSteering('terminal', {
|
|
109
110
|
maxPriority: 'next',
|
|
110
111
|
beforeAppend: () => {
|
|
111
|
-
if (segments.commitIntermediate(response) && hasContent)
|
|
112
|
+
if (segments.commitIntermediate(response) && hasContent)
|
|
113
|
+
segments.record(response.content, { committed: true });
|
|
112
114
|
},
|
|
113
115
|
})
|
|
114
116
|
) {
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { traceCacheBreak } from '../../cache-break-trace.mjs';
|
|
7
7
|
import { resolveLiveToolCwd } from './tool-exec.mjs';
|
|
8
|
-
import { prepareExplicitSkills } from '../explicit-skills.mjs';
|
|
8
|
+
import { notifySkillsApplied, prepareExplicitSkills, recordAppliedSkills } from '../explicit-skills.mjs';
|
|
9
9
|
import { addUsage, usageDeltaEvent } from './usage.mjs';
|
|
10
10
|
import { prepareProviderRequest } from './request-boundary.mjs';
|
|
11
11
|
import { projectProviderRequest } from './request-projection.mjs';
|
|
@@ -44,10 +44,13 @@ export async function beginIteration(state) {
|
|
|
44
44
|
// policy-checked selection before the next provider snapshot instead of
|
|
45
45
|
// starting detached work from a drain callback.
|
|
46
46
|
while (state.pendingSkillPrompts.length) {
|
|
47
|
-
|
|
47
|
+
const { content, message: queued } = state.pendingSkillPrompts.shift();
|
|
48
|
+
const applied = await prepareExplicitSkills(content, messages, sessionRef, {
|
|
48
49
|
cwd: state.cwd,
|
|
49
50
|
signal: opts.signal,
|
|
50
51
|
});
|
|
52
|
+
const message = recordAppliedSkills(messages, queued, applied);
|
|
53
|
+
notifySkillsApplied(opts.onSkillsApplied, applied, message);
|
|
51
54
|
}
|
|
52
55
|
const boundary = await prepareProviderRequest({
|
|
53
56
|
provider,
|
|
@@ -263,8 +266,7 @@ function traceActualCacheMiss(state, sent, response, { previousGuardState, previ
|
|
|
263
266
|
const ttlMs = MESSAGES_CACHE_TTL_MS[sessionRef.providerCacheOpts?.cacheStrategy?.messages];
|
|
264
267
|
const idleMs = sent.sendStartedAt - previousSendAt;
|
|
265
268
|
const requestPrefixChanged =
|
|
266
|
-
!!previousGuardState &&
|
|
267
|
-
previousGuardState.requestPrefixHash !== sent.prefixGuardCandidate?.requestPrefixHash;
|
|
269
|
+
!!previousGuardState && previousGuardState.requestPrefixHash !== sent.prefixGuardCandidate?.requestPrefixHash;
|
|
268
270
|
if (
|
|
269
271
|
baseline > 0 &&
|
|
270
272
|
ttlMs &&
|
|
@@ -375,6 +377,7 @@ export function settleSendResult(state, round, sent) {
|
|
|
375
377
|
model: response.model || model,
|
|
376
378
|
usage: reportedUsage,
|
|
377
379
|
sendTools: round.sendTools,
|
|
380
|
+
toolCalls: response.toolCalls?.length || 0,
|
|
378
381
|
})
|
|
379
382
|
)
|
|
380
383
|
);
|