mixdog 1.3.0 → 1.3.2
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/NOTICE.md +52 -9
- package/README.md +8 -4
- package/package.json +2 -2
- package/scripts/native-release-status.mjs +238 -0
- package/src/defaults/shipped-history.json +8 -1
- package/src/defaults/skills/pptx/scripts/qc-pages.mjs +3 -2
- package/src/defaults/skills/setup/SKILL.md +23 -1
- package/src/defaults/skills/setup/references/actions.md +54 -4
- package/src/defaults/skills/setup/references/surfaces.md +23 -3
- package/src/defaults/skills/setup-guide/SKILL.md +17 -10
- package/src/defaults/skills/visualize/SKILL.md +9 -6
- package/src/defaults/skills/visualize/references/craft.md +8 -1
- package/src/help.mjs +1 -1
- package/src/lib/keychain-cjs.cjs +4 -1
- package/src/lib/rules-builder.cjs +14 -7
- package/src/output-styles/common.md +13 -8
- package/src/rules/agent-mode/AGENT_MODE.md +9 -6
- package/src/rules/agent-mode/MIXY.md +11 -6
- package/src/rules/lead/LEAD.md +2 -2
- package/src/rules/shared/10-tool-workflow.md +4 -0
- package/src/rules/shared/20-research.md +2 -0
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/preset.mjs +5 -0
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/session-prep.mjs +8 -0
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +6 -1
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +114 -26
- package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +8 -0
- package/src/runtime/agent/orchestrator/agent-runtime/room-supervisor-completion.mjs +5 -4
- package/src/runtime/agent/orchestrator/agent-trace.mjs +3 -0
- package/src/runtime/agent/orchestrator/cache-break-trace.mjs +4 -0
- package/src/runtime/agent/orchestrator/config-presets.mjs +2 -0
- package/src/runtime/agent/orchestrator/config-storage.mjs +10 -3
- package/src/runtime/agent/orchestrator/config.mjs +11 -0
- package/src/runtime/agent/orchestrator/context/role-instructions.mjs +81 -16
- package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +97 -4
- package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +6 -10
- package/src/runtime/agent/orchestrator/mcp/client.mjs +7 -4
- package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +80 -31
- package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +67 -38
- package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-catalog.mjs +7 -2
- package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +6 -2
- package/src/runtime/agent/orchestrator/providers/auto-effort.mjs +34 -16
- package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +31 -4
- package/src/runtime/agent/orchestrator/providers/cursor.mjs +4 -1
- package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +11 -8
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-prefix-trace.mjs +76 -0
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +19 -1
- package/src/runtime/agent/orchestrator/providers/lib/tail-reminder-split.mjs +13 -0
- package/src/runtime/agent/orchestrator/providers/lib/tool-call-wire-ids.mjs +65 -0
- package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +16 -1
- package/src/runtime/agent/orchestrator/providers/mixdog-local.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-codex-metadata.mjs +5 -5
- package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +9 -5
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +9 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-retry-profile.mjs +8 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +10 -2
- package/src/runtime/agent/orchestrator/providers/openai-direct-request.mjs +30 -3
- package/src/runtime/agent/orchestrator/providers/openai-model-capabilities.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/openai-responses-input.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/opencode-go-oauth.mjs +61 -8
- package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +14 -2
- package/src/runtime/agent/orchestrator/providers/registry.mjs +9 -0
- package/src/runtime/agent/orchestrator/providers/retry-executor.mjs +17 -0
- package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +90 -24
- package/src/runtime/agent/orchestrator/runtime-core/fs-utils.mjs +8 -1
- package/src/runtime/agent/orchestrator/runtime-core/goal-mutations.mjs +1 -1
- package/src/runtime/agent/orchestrator/runtime-core/goal-storage.mjs +68 -22
- package/src/runtime/agent/orchestrator/runtime-core/goal-tool-exec.mjs +1 -1
- package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +5 -5
- package/src/runtime/agent/orchestrator/runtime-core/route-context-state.mjs +81 -0
- package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +2 -1
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +12 -1
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +17 -6
- package/src/runtime/agent/orchestrator/runtime-core/workflow/agents.mjs +41 -10
- package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +6 -4
- package/src/runtime/agent/orchestrator/runtime-core/workflow/packs.mjs +8 -4
- package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/agent-mode.mjs +0 -6
- package/src/runtime/agent/orchestrator/session/agent-room-supervisor.d.mts +1 -1
- package/src/runtime/agent/orchestrator/session/agent-room-supervisor.mjs +22 -4
- package/src/runtime/agent/orchestrator/session/agent-room.d.mts +21 -2
- package/src/runtime/agent/orchestrator/session/agent-room.mjs +149 -45
- package/src/runtime/agent/orchestrator/session/attachment-preflight.mjs +80 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +14 -11
- package/src/runtime/agent/orchestrator/session/compact/forked-summary.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +68 -18
- package/src/runtime/agent/orchestrator/session/compact/summary-mode.mjs +16 -0
- package/src/runtime/agent/orchestrator/session/compact/summary-route.mjs +87 -0
- package/src/runtime/agent/orchestrator/session/compact/summary.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +5 -1
- package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +41 -10
- package/src/runtime/agent/orchestrator/session/human-gate/delegation.mjs +230 -0
- package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +156 -25
- package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +20 -0
- package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +21 -9
- package/src/runtime/agent/orchestrator/session/human-gate/run-policy.mjs +73 -0
- package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +55 -27
- package/src/runtime/agent/orchestrator/session/human-gate/unattended-port.mjs +15 -0
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +189 -105
- package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +9 -0
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +6 -2
- package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +111 -9
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +11 -14
- package/src/runtime/agent/orchestrator/session/loop/steering.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +38 -5
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +21 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +12 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +11 -5
- package/src/runtime/agent/orchestrator/session/loop/usage.mjs +46 -4
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +13 -2
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-commit.mjs +18 -8
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +44 -18
- package/src/runtime/agent/orchestrator/session/manager/orchestration-mode-switch-notice.mjs +99 -0
- package/src/runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs +42 -0
- package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +3 -9
- package/src/runtime/agent/orchestrator/session/manager/pending-spool-file.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/pending-spool-shard.mjs +5 -3
- package/src/runtime/agent/orchestrator/session/manager/provider-replay.mjs +50 -0
- package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +40 -1
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +12 -0
- package/src/runtime/agent/orchestrator/session/manager/runtime-state.mjs +20 -0
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +62 -47
- package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +6 -8
- package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +12 -1
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +6 -15
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +7 -1
- package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +24 -8
- package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/workflow-switch-notice.mjs +94 -13
- package/src/runtime/agent/orchestrator/session/mixy-name.mjs +5 -2
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +42 -10
- package/src/runtime/agent/orchestrator/session/provider-prefix-guard.mjs +21 -3
- package/src/runtime/agent/orchestrator/session/routine-result.d.mts +35 -4
- package/src/runtime/agent/orchestrator/session/routine-result.mjs +179 -16
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +5 -1
- package/src/runtime/agent/orchestrator/session/side-question-store.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/store/canonical-reader.mjs +41 -7
- package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +10 -2
- package/src/runtime/agent/orchestrator/session/store/load-cache.mjs +14 -2
- package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +15 -1
- package/src/runtime/agent/orchestrator/session/store/sweep/sweep-record.mjs +8 -3
- package/src/runtime/agent/orchestrator/session/store/sweep/sweep-row.mjs +36 -1
- package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +40 -5
- package/src/runtime/agent/orchestrator/session/store-summary-last-message.mjs +47 -2
- package/src/runtime/agent/orchestrator/session/tail-reminder-rules.mjs +125 -0
- package/src/runtime/agent/orchestrator/session/tool-batch/finalize.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +78 -43
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +49 -14
- package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +134 -17
- package/src/runtime/agent/orchestrator/tools/builtin/advisory-lock.mjs +35 -13
- package/src/runtime/agent/orchestrator/tools/builtin/atomic-write.mjs +25 -5
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +4 -3
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +13 -14
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/transport-artifacts.mjs +9 -0
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +19 -14
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +7 -1
- package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +6 -14
- package/src/runtime/agent/orchestrator/tools/builtin/pdf-test-fixtures.mjs +5 -3
- package/src/runtime/agent/orchestrator/tools/builtin/read-image.mjs +4 -2
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-media.mjs +47 -2
- package/src/runtime/agent/orchestrator/tools/builtin/read-special-files.mjs +264 -87
- package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +10 -4
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +34 -21
- package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +8 -4
- package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +9 -30
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +24 -12
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-spawn.mjs +3 -2
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +6 -2
- package/src/runtime/agent/orchestrator/tools/patch/delete-quarantine.mjs +2 -1
- package/src/runtime/agent/orchestrator/tools/patch/replay-capture.mjs +4 -2
- package/src/runtime/agent/orchestrator/tools/patch/rollback-state.mjs +33 -2
- package/src/runtime/agent/orchestrator/tools/patch-manifest.json +13 -13
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +5 -3
- package/src/runtime/agent/orchestrator/tools/shell-exec-output.mjs +3 -1
- package/src/runtime/attachments/attachment-reference.mjs +155 -0
- package/src/runtime/attachments/limits.mjs +5 -1
- package/src/runtime/attachments/office-extract.mjs +3 -6
- package/src/runtime/attachments/pdf-extract.mjs +84 -10
- package/src/runtime/attachments/store.mjs +55 -30
- package/src/runtime/browser-bridge/client.mjs +73 -20
- package/src/runtime/channels/lib/runtime-paths.mjs +1 -1
- package/src/runtime/channels/lib/scheduler.mjs +14 -7
- package/src/runtime/channels/lib/state-file.mjs +15 -15
- package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +14 -13
- package/src/runtime/channels/lib/webhook/relay-tunnel.mjs +22 -18
- package/src/runtime/channels/lib/webhook/session-dispatch.mjs +14 -36
- package/src/runtime/channels/lib/worker-main.mjs +3 -2
- package/src/runtime/effort-judge/compact-tokenizer.mjs +8 -2
- package/src/runtime/effort-judge/judge-client.mjs +6 -3
- package/src/runtime/effort-judge/model-install.mjs +16 -8
- package/src/runtime/effort-judge/model-manifest.json +7 -7
- package/src/runtime/local-provider/asset-installer.mjs +10 -4
- package/src/runtime/local-provider/catalog.mjs +5 -2
- package/src/runtime/local-provider/registered-models.mjs +64 -27
- package/src/runtime/local-provider/server.mjs +2 -3
- package/src/runtime/media/adapters/image-api.mjs +89 -0
- package/src/runtime/media/auth.mjs +64 -1
- package/src/runtime/media/catalog.mjs +66 -4
- package/src/runtime/media/jobs.mjs +4 -0
- package/src/runtime/media/lanes.mjs +46 -5
- package/src/runtime/media/media-usage.mjs +17 -0
- package/src/runtime/media/renditions.mjs +15 -15
- package/src/runtime/media/store.mjs +36 -35
- package/src/runtime/media/tool.mjs +8 -1
- package/src/runtime/memory/lib/cycle-scheduler/health-ledger.mjs +8 -2
- package/src/runtime/memory/lib/embedding-model-files.mjs +30 -7
- package/src/runtime/memory/lib/ingest/transcript-tail.mjs +16 -7
- package/src/runtime/memory/lib/pg/process.mjs +8 -4
- package/src/runtime/memory/lib/project-id-resolver.mjs +5 -1
- package/src/runtime/memory/lib/runtime-fetcher.mjs +6 -1
- package/src/runtime/office/core/journal.mjs +7 -14
- package/src/runtime/office/design/library/design-library-core.mjs +4 -16
- package/src/runtime/office/pdf/ocr-engine-worker.mjs +37 -0
- package/src/runtime/office/pdf/ocr-engine.mjs +146 -0
- package/src/runtime/office/pdf/pdf-ocr.mjs +51 -21
- package/src/runtime/office/pdf/pdf-render.mjs +30 -18
- package/src/runtime/office/portable/ooxml-validator.mjs +2 -2
- package/src/runtime/office/portable/portable-soffice.mjs +4 -0
- package/src/runtime/shared/agent-kind-migration.mjs +200 -0
- package/src/runtime/shared/agent-kind.mjs +152 -0
- package/src/runtime/shared/agent-message-envelope.d.mts +6 -0
- package/src/runtime/shared/agent-message-envelope.mjs +44 -0
- package/src/runtime/shared/agent-reap-state.d.mts +4 -0
- package/src/runtime/shared/agent-route-config.d.mts +1 -0
- package/src/runtime/shared/agent-route-config.mjs +22 -8
- package/src/runtime/shared/agent-store.mjs +149 -0
- package/src/runtime/shared/agent-task-starts.mjs +27 -0
- package/src/runtime/shared/atomic-file.d.mts +50 -0
- package/src/runtime/shared/atomic-file.mjs +394 -132
- package/src/runtime/shared/background-tasks.mjs +7 -4
- package/src/runtime/shared/bot-hops.mjs +30 -0
- package/src/runtime/shared/bot-split-migration.mjs +457 -0
- package/src/runtime/shared/bounded-download.mjs +4 -2
- package/src/runtime/shared/config-patch.mjs +20 -1
- package/src/runtime/shared/config.mjs +39 -9
- package/src/runtime/shared/connector-catalog.d.mts +1 -2
- package/src/runtime/shared/connector-catalog.mjs +2 -4
- package/src/runtime/shared/connector-triggers.mjs +2 -0
- package/src/runtime/shared/defaults-separation.mjs +6 -2
- package/src/runtime/shared/delegated-approval-host.mjs +74 -0
- package/src/runtime/shared/err-text.mjs +7 -1
- package/src/runtime/shared/error-presentation.mjs +113 -10
- package/src/runtime/shared/file-has-data.mjs +28 -0
- package/src/runtime/shared/handoff-artifacts.d.mts +20 -0
- package/src/runtime/shared/handoff-artifacts.mjs +61 -0
- package/src/runtime/shared/llm/provider-model-identities.mjs +6 -1
- package/src/runtime/shared/llm/usage-accounting.mjs +7 -1
- package/src/runtime/shared/llm/usage-ledger-import.mjs +37 -18
- package/src/runtime/shared/model-route-fallback.mjs +79 -7
- package/src/runtime/shared/pending-agent-work.mjs +54 -0
- package/src/runtime/shared/plain-preview.mjs +22 -17
- package/src/runtime/shared/process-lifecycle.mjs +8 -12
- package/src/runtime/shared/projects.mjs +50 -41
- package/src/runtime/shared/provider-accounts.mjs +17 -12
- package/src/runtime/shared/routine-trigger.d.mts +6 -1
- package/src/runtime/shared/routine-trigger.mjs +14 -6
- package/src/runtime/shared/run-child.d.mts +3 -0
- package/src/runtime/shared/run-child.mjs +24 -3
- package/src/runtime/shared/sensitive-path-rules.d.mts +15 -0
- package/src/runtime/shared/sensitive-path-rules.mjs +256 -0
- package/src/runtime/shared/session-baseline-store.mjs +8 -2
- package/src/runtime/shared/singleton-owner.mjs +7 -6
- package/src/runtime/shared/skill-tool-dependencies.mjs +4 -10
- package/src/runtime/shared/staged-update.mjs +4 -2
- package/src/runtime/shared/statusline/statusline-segments.mjs +4 -15
- package/src/runtime/shared/tool-execution-contract.mjs +6 -0
- package/src/runtime/shared/transcript-metadata.mjs +28 -3
- package/src/runtime/shared/transcript-writer/session-record.mjs +7 -6
- package/src/runtime/shared/turn-snapshot-store.mjs +5 -4
- package/src/runtime/shared/turn-snapshot.mjs +44 -2
- package/src/runtime/shared/usage-readout-totals.mjs +15 -13
- package/src/runtime/shared/user-cwd.mjs +9 -4
- package/src/runtime/shared/user-data-guard.mjs +46 -44
- package/src/runtime/shared/utf8-cut.mjs +10 -0
- package/src/runtime/shared/worktrees/activity-gate.mjs +269 -61
- package/src/runtime/shared/worktrees/registry.mjs +26 -26
- package/src/runtime/tidy/core-install.mjs +26 -4
- package/src/runtime/tidy/install.mjs +91 -23
- package/src/runtime/web-search/lib/config.mjs +4 -16
- package/src/session-runtime/agent-home-delivery.mjs +859 -136
- package/src/session-runtime/agent-room-delivery.mjs +167 -19
- package/src/session-runtime/agent-task-sessions.mjs +831 -69
- package/src/session-runtime/agent-tool-routing.mjs +1 -0
- package/src/session-runtime/boot/apis.mjs +58 -24
- package/src/session-runtime/boot/config.mjs +1 -0
- package/src/session-runtime/boot/facade.mjs +11 -1
- package/src/session-runtime/boot/infrastructure.mjs +14 -2
- package/src/session-runtime/boot/providers.mjs +16 -6
- package/src/session-runtime/boot/shared.mjs +12 -6
- package/src/session-runtime/boot/tools.mjs +2 -2
- package/src/session-runtime/bot-status.mjs +432 -0
- package/src/session-runtime/config-lifecycle/lead-route-sync.mjs +29 -0
- package/src/session-runtime/config-lifecycle.mjs +78 -32
- package/src/session-runtime/cwd-plugins/core-memory-context.mjs +2 -4
- package/src/session-runtime/handoff-attachments.mjs +593 -0
- package/src/session-runtime/held-requests.mjs +36 -33
- package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +10 -1
- package/src/session-runtime/internal-tool-executor/feature-tools.mjs +2 -2
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +45 -6
- package/src/session-runtime/lifecycle/session-catalog.mjs +12 -2
- package/src/session-runtime/lifecycle/teardown.mjs +11 -3
- package/src/session-runtime/mcp-glue/connect.mjs +100 -186
- package/src/session-runtime/mcp-glue/initial-connect-gate.mjs +57 -0
- package/src/session-runtime/mcp-glue/server-connection.mjs +71 -0
- package/src/session-runtime/media-api.mjs +5 -2
- package/src/session-runtime/model-route/route-persist.mjs +2 -2
- package/src/session-runtime/model-route/route-tuning.mjs +11 -2
- package/src/session-runtime/model-route/set-route.mjs +82 -2
- package/src/session-runtime/native-web-search.mjs +2 -2
- package/src/session-runtime/new-session-config.mjs +6 -1
- package/src/session-runtime/owned-automation-run.mjs +8 -1
- package/src/session-runtime/room-member-gate.mjs +16 -3
- package/src/session-runtime/route-resolution.mjs +21 -19
- package/src/session-runtime/route-state.mjs +1 -59
- package/src/session-runtime/routine-home-turn.mjs +5 -3
- package/src/session-runtime/runtime-bootstrap.mjs +5 -3
- package/src/session-runtime/runtime-paths.mjs +11 -5
- package/src/session-runtime/runtime-tunables.mjs +6 -0
- package/src/session-runtime/services/agent-tool/close-flow/close-all.mjs +11 -2
- package/src/session-runtime/services/agent-tool/close-flow.mjs +4 -2
- package/src/session-runtime/services/agent-tool/execute-send.mjs +1 -0
- package/src/session-runtime/services/agent-tool/execute-spawn.mjs +1 -0
- package/src/session-runtime/services/agent-tool/helpers.mjs +24 -5
- package/src/session-runtime/services/agent-tool/restart-notify.mjs +10 -0
- package/src/session-runtime/services/agent-tool/send-flow.mjs +36 -24
- package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +4 -0
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +6 -3
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +12 -6
- package/src/session-runtime/services/agent-tool/spawn-preset.mjs +5 -8
- package/src/session-runtime/services/agent-tool/tool-def.mjs +7 -0
- package/src/session-runtime/services/agent-tool/worker-index/row-liveness.mjs +22 -2
- package/src/session-runtime/services/agent-tool/worker-index.mjs +4 -6
- package/src/session-runtime/services/agent-tool.mjs +6 -0
- package/src/session-runtime/services/daemon-crash-capture/daemon-exit-record.mjs +1 -1
- package/src/session-runtime/services/daemon-crash-capture/retention.mjs +4 -3
- package/src/session-runtime/services/hook-bus/config-loader.mjs +14 -5
- package/src/session-runtime/services/hook-bus/rule-file.mjs +49 -11
- package/src/session-runtime/services/plugin-admin.mjs +26 -26
- package/src/session-runtime/services/provider-admin.mjs +14 -4
- package/src/session-runtime/services/schedule-access.mjs +33 -4
- package/src/session-runtime/services/seeds.mjs +4 -3
- package/src/session-runtime/services/usage-dashboard.mjs +1 -1
- package/src/session-runtime/services/usage-stats-model.mjs +2 -1
- package/src/session-runtime/session-create/materialize.mjs +2 -3
- package/src/session-runtime/session-create/session-options.mjs +15 -2
- package/src/session-runtime/settings-builtin-tools-api.mjs +63 -1
- package/src/session-runtime/settings-compaction-api.mjs +86 -39
- package/src/session-runtime/setup-tool/executor.mjs +20 -3
- package/src/session-runtime/setup-tool/extended-actions.mjs +19 -7
- package/src/session-runtime/setup-tool/settings-contract.mjs +37 -1
- package/src/session-runtime/setup-tool/tool-defs.mjs +20 -3
- package/src/session-runtime/skills-api.mjs +12 -5
- package/src/session-runtime/tool-policy/agent-profile-fold.mjs +265 -0
- package/src/session-runtime/tool-policy/mode-switch.mjs +54 -0
- package/src/session-runtime/tool-policy/pending-switches.mjs +59 -0
- package/src/session-runtime/tool-policy/policy-stage.mjs +165 -0
- package/src/session-runtime/tool-policy/session-switches.mjs +169 -0
- package/src/session-runtime/tool-policy/surface-helpers.mjs +114 -0
- package/src/session-runtime/tool-policy/workflow-overlay.mjs +145 -0
- package/src/session-runtime/tool-policy-refresh.mjs +47 -358
- package/src/session-runtime/tool-surface.mjs +5 -3
- package/src/session-runtime/turn/turn-run/turn-prepare.mjs +99 -81
- package/src/session-runtime/turn/turn-run/turn-timing.mjs +4 -3
- package/src/session-runtime/turn/turn-run.mjs +2 -1
- package/src/session-runtime/workflow-agents-api/agent-connectors.mjs +54 -7
- package/src/session-runtime/workflow-agents-api/agent-editor.mjs +578 -301
- package/src/session-runtime/workflow-agents-api/agent-route.mjs +113 -53
- package/src/session-runtime/workflow-agents-api/onboarding.mjs +11 -4
- package/src/session-runtime/workflow-agents-api/shared.mjs +5 -1
- package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +14 -1
- package/src/session-runtime/workflow-agents-api/workflow-packs.mjs +6 -3
- package/src/session-runtime/workspace/workspace-state.mjs +7 -6
- package/src/standalone/daemon-agent-control.mjs +4 -1
- package/src/standalone/daemon-session-control.mjs +60 -11
- package/src/standalone/daemon.mjs +34 -4
- package/src/standalone/local-session-runtime.mjs +22 -14
- package/src/standalone/session-client.mjs +7 -2
- package/src/standalone/session-protocol.mjs +7 -0
- package/src/standalone/session-service/agent-tree/agent-turns.mjs +46 -3
- package/src/standalone/session-service/agent-tree.mjs +4 -3
- package/src/standalone/session-service/session-calls/session-turns.mjs +19 -0
- package/src/standalone/session-service.mjs +11 -0
- package/src/standalone/session-state-projection.mjs +19 -9
- package/src/standalone/session-tool-output.mjs +35 -9
- package/src/tui/app/maintenance-pickers/auto-clear-picker.mjs +19 -13
- package/src/tui/app/settings-picker/settings-rows.mjs +2 -2
- package/src/tui/app/slash-commands.mjs +1 -1
- package/src/tui/app/usage-context-panels/context-model.mjs +8 -2
- package/src/tui/dist/index.mjs +599 -342
- package/src/tui/prompt-history-store.mjs +16 -21
- package/src/tui/session/context-state.mjs +4 -0
- package/src/tui/session/notice-surface.mjs +3 -4
- package/src/tui/session/session-api/intake/abort.mjs +19 -1
- package/src/tui/session/session-api/media.mjs +2 -1
- package/src/tui/session/session-api/routes-agent-definitions.mjs +75 -0
- package/src/tui/session/session-api/routes-write.mjs +203 -0
- package/src/tui/session/session-api/routes.mjs +23 -236
- package/src/tui/session/session-api/settings.mjs +14 -1
- package/src/tui/session/session-flow/drain.mjs +28 -3
- package/src/tui/session/session-flow/failed-turn-rewind.mjs +4 -2
- package/src/tui/session/session-flow/queue/take-entries.mjs +8 -0
- package/src/tui/session/session-flow/steering.mjs +8 -3
- package/src/tui/session/tool-approval.mjs +185 -20
- package/src/tui/session/transcript-intake.mjs +4 -0
- package/src/tui/session/turn-live-call.mjs +11 -3
- package/src/tui/session/turn.mjs +36 -17
- package/src/tui/transcript-tool-failures.mjs +8 -2
- package/src/vendor/statusline/src/gateway/route-meta.mjs +7 -8
- package/src/vendor/statusline/src/gateway/session-routes.mjs +6 -7
- package/src/rules/agent-mode/ROUTINE.md +0 -6
|
@@ -11,11 +11,40 @@
|
|
|
11
11
|
* stringify it to "[object Object]"; scalar reads leave it unset and get the
|
|
12
12
|
* rich block shapes.
|
|
13
13
|
*/
|
|
14
|
+
import { open } from 'node:fs/promises';
|
|
14
15
|
import { extname } from 'node:path';
|
|
15
16
|
import { extractOoxmlText } from '../../../../attachments/office-extract.mjs';
|
|
17
|
+
import { readImageAsContent } from './read-image.mjs';
|
|
16
18
|
|
|
17
19
|
const OOXML_EXTENSIONS = new Set(['.docx', '.pptx', '.xlsx', '.xlsm']);
|
|
18
20
|
|
|
21
|
+
// File signatures of the media an extension-less path may hold: the stored
|
|
22
|
+
// attachments a reference points at keep no extension.
|
|
23
|
+
const SNIFFED_IMAGE_TYPES = [
|
|
24
|
+
{ mimeType: 'image/png', matches: (head) => head.subarray(0, 8).equals(Buffer.from('89504e470d0a1a0a', 'hex')) },
|
|
25
|
+
{ mimeType: 'image/jpeg', matches: (head) => head[0] === 0xff && head[1] === 0xd8 && head[2] === 0xff },
|
|
26
|
+
{ mimeType: 'image/gif', matches: (head) => head.subarray(0, 4).toString('latin1') === 'GIF8' },
|
|
27
|
+
{
|
|
28
|
+
mimeType: 'image/webp',
|
|
29
|
+
matches: (head) =>
|
|
30
|
+
head.subarray(0, 4).toString('latin1') === 'RIFF' && head.subarray(8, 12).toString('latin1') === 'WEBP',
|
|
31
|
+
},
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
async function sniffedMediaType(fullPath, st) {
|
|
35
|
+
if (!st?.isFile?.() || st.size < 5) return null;
|
|
36
|
+
const handle = await open(fullPath, 'r');
|
|
37
|
+
try {
|
|
38
|
+
const head = Buffer.alloc(12);
|
|
39
|
+
const { bytesRead } = await handle.read(head, 0, head.length, 0);
|
|
40
|
+
const bytes = head.subarray(0, bytesRead);
|
|
41
|
+
if (bytes.subarray(0, 5).toString('latin1') === '%PDF-') return 'application/pdf';
|
|
42
|
+
return SNIFFED_IMAGE_TYPES.find((type) => type.matches(bytes))?.mimeType || null;
|
|
43
|
+
} finally {
|
|
44
|
+
await handle.close();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
19
48
|
/**
|
|
20
49
|
* @returns {Promise<{ result: unknown } | null>} null when the path is not a
|
|
21
50
|
* media file.
|
|
@@ -24,8 +53,24 @@ export async function readMediaFile({ fullPath, st, args, readStateScope, option
|
|
|
24
53
|
const { extractPdfText, extractIpynbText, READ_MAX_OUTPUT_BYTES, _recordReadSnapshot } = helpers;
|
|
25
54
|
const textOnly = options?.mediaTextOnly === true;
|
|
26
55
|
const ext = extname(fullPath).toLowerCase();
|
|
27
|
-
|
|
28
|
-
|
|
56
|
+
const sniffed = ext === '' ? await sniffedMediaType(fullPath, st) : null;
|
|
57
|
+
if (ext === '.pdf' || sniffed === 'application/pdf') {
|
|
58
|
+
return {
|
|
59
|
+
result: await extractPdfText(fullPath, args.pages, {
|
|
60
|
+
maxOutputBytes: READ_MAX_OUTPUT_BYTES,
|
|
61
|
+
textOnly,
|
|
62
|
+
// The caller's model decides how a scanned page arrives: as a page
|
|
63
|
+
// image for one that reads images, as OCR text for a text-only one;
|
|
64
|
+
// a whole document block only for one that reads PDFs natively.
|
|
65
|
+
pageMode: options?.pdfPageMode === 'ocr' ? 'ocr' : 'image',
|
|
66
|
+
nativeDocument: options?.pdfNativeDocument !== false,
|
|
67
|
+
signal: options?.signal || null,
|
|
68
|
+
}),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
if (sniffed && !textOnly) {
|
|
72
|
+
const image = await readImageAsContent(fullPath, args.path || fullPath, st, sniffed);
|
|
73
|
+
if (image) return { result: image };
|
|
29
74
|
}
|
|
30
75
|
if (OOXML_EXTENSIONS.has(ext)) {
|
|
31
76
|
// OOXML text extraction — always a flat string, so it is batch-safe
|
|
@@ -1,18 +1,25 @@
|
|
|
1
1
|
import { readFile, stat } from 'node:fs/promises';
|
|
2
|
-
import { MAX_PDF_BYTES,
|
|
2
|
+
import { MAX_PDF_BYTES, isPdfBuffer } from '../../../../attachments/limits.mjs';
|
|
3
|
+
import { READ_PDF_MAX_PAGES_PER_CALL } from '../../../../attachments/attachment-reference.mjs';
|
|
3
4
|
import { READ_MAX_SIZE_BYTES } from './read-constants.mjs';
|
|
4
5
|
import { capMarker } from './tool-output-limit.mjs';
|
|
5
6
|
import { imageBlocksFromBuffer } from './read-image-resize.mjs';
|
|
6
|
-
import { inspectPdfBuffer } from '../../../../attachments/pdf-extract.mjs';
|
|
7
|
+
import { boundedUtf8, inspectPdfBuffer, pageHasTextLayer } from '../../../../attachments/pdf-extract.mjs';
|
|
7
8
|
|
|
8
9
|
const DEFAULT_READ_MAX_OUTPUT_BYTES = 100 * 1024;
|
|
9
10
|
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
const
|
|
11
|
+
// A whole-PDF read (no pages) returns a PDF of up to this many pages whole —
|
|
12
|
+
// as the canonical base64 document block for a model that reads PDFs natively
|
|
13
|
+
// (providers convert it to their native file shape; MAX_PDF_BYTES keeps it
|
|
14
|
+
// under the request cap). A longer PDF returns its outline and first pages:
|
|
15
|
+
// the model then reads the pages it needs.
|
|
16
|
+
const PDF_WHOLE_READ_MAX_PAGES = 10;
|
|
17
|
+
const PDF_PREVIEW_PAGES = 5;
|
|
18
|
+
// Scanned pages render at this width: past the point where text in a page
|
|
19
|
+
// image stays legible, below the vision ceiling that would downscale it again.
|
|
20
|
+
const PDF_PAGE_IMAGE_WIDTH = 1200;
|
|
21
|
+
// The renderer takes at most 12 explicit pages per call.
|
|
22
|
+
const PDF_RENDER_BATCH = 12;
|
|
16
23
|
|
|
17
24
|
// Per-output text ceiling inside a notebook. A single cell output larger than
|
|
18
25
|
// this is replaced with a jq hint (large-notebook guidance) so a
|
|
@@ -40,15 +47,16 @@ function parsePagesArg(pagesArg) {
|
|
|
40
47
|
if (to < from) {
|
|
41
48
|
return { error: `Error: pages "${trimmed}" inverted — end (${to}) precedes start (${from})` };
|
|
42
49
|
}
|
|
43
|
-
if (to - from + 1 >
|
|
44
|
-
return {
|
|
50
|
+
if (to - from + 1 > READ_PDF_MAX_PAGES_PER_CALL) {
|
|
51
|
+
return {
|
|
52
|
+
error: `Error: pages "${trimmed}" spans ${to - from + 1} pages; max ${READ_PDF_MAX_PAGES_PER_CALL} per request — narrow the range`,
|
|
53
|
+
};
|
|
45
54
|
}
|
|
46
55
|
return { filter: { from, to } };
|
|
47
56
|
}
|
|
48
57
|
|
|
49
|
-
// PDF.js text extraction
|
|
50
|
-
//
|
|
51
|
-
// document block can't be page-filtered, so a narrowed read keeps using text).
|
|
58
|
+
// PDF.js text extraction for a file without the %PDF- signature: pdf.js
|
|
59
|
+
// surfaces the parse error as text instead of a document block.
|
|
52
60
|
async function extractPdfTextBody(fullPath, pageFilter, maxOutputBytes) {
|
|
53
61
|
const buf = await readFile(fullPath);
|
|
54
62
|
const result = await inspectPdfBuffer(buf, {
|
|
@@ -60,79 +68,240 @@ async function extractPdfTextBody(fullPath, pageFilter, maxOutputBytes) {
|
|
|
60
68
|
return result.text || '(no text content extracted from PDF)';
|
|
61
69
|
}
|
|
62
70
|
|
|
71
|
+
// Bounded UTF-8 cut for the text a paged read returns.
|
|
72
|
+
function capPdfText(text, maxOutputBytes) {
|
|
73
|
+
const bounded = boundedUtf8(text, maxOutputBytes);
|
|
74
|
+
if (!bounded.truncated) return text;
|
|
75
|
+
return `${bounded.text}\n\n${capMarker({ limit: maxOutputBytes, shown: 'PDF text cut', narrow: 'read a narrower pages range' })}`;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async function renderPdfPageImages(fullPath, pages, signal) {
|
|
79
|
+
const { renderPdfPages } = await import('../../../../office/pdf/pdf-render.mjs');
|
|
80
|
+
const out = [];
|
|
81
|
+
for (let offset = 0; offset < pages.length; offset += PDF_RENDER_BATCH) {
|
|
82
|
+
const rendered = await renderPdfPages(fullPath, {
|
|
83
|
+
pages: pages.slice(offset, offset + PDF_RENDER_BATCH),
|
|
84
|
+
maxWidth: PDF_PAGE_IMAGE_WIDTH,
|
|
85
|
+
signal,
|
|
86
|
+
writeImages: false,
|
|
87
|
+
});
|
|
88
|
+
out.push(...rendered.images);
|
|
89
|
+
}
|
|
90
|
+
return out;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async function recognizePdfPages(fullPath, pages, signal) {
|
|
94
|
+
const [{ recognizePdfPageTexts }, { defaultOfficeDataDir }] = await Promise.all([
|
|
95
|
+
import('../../../../office/pdf/pdf-ocr.mjs'),
|
|
96
|
+
import('../../../../office/core/journal.mjs'),
|
|
97
|
+
]);
|
|
98
|
+
return recognizePdfPageTexts(fullPath, pages, { dataDir: defaultOfficeDataDir(), signal });
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function pageList(pages) {
|
|
102
|
+
return pages.length > 6 ? `${pages.slice(0, 6).join(', ')}, …` : pages.join(', ');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function errorText(error) {
|
|
106
|
+
return error instanceof Error ? error.message : String(error);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function pdfFileLabel(fullPath) {
|
|
110
|
+
return fullPath.split(/[\\/]/).pop();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// The scanned pages of one paged read, as page images or OCR text. A page the
|
|
114
|
+
// renderer or the OCR engine cannot produce says so instead of failing the
|
|
115
|
+
// whole read: the text pages are still returned.
|
|
116
|
+
async function scannedPageBodies(fullPath, scanned, { pageMode, signal, renderPages, recognizePages }) {
|
|
117
|
+
const bodies = new Map();
|
|
118
|
+
if (!scanned.length) return bodies;
|
|
119
|
+
if (pageMode === 'text') {
|
|
120
|
+
for (const page of scanned) {
|
|
121
|
+
bodies.set(page, {
|
|
122
|
+
text: `(no text layer: a scanned page. Read this PDF on its own with pages="${page}" to see it as an image or OCR text.)`,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
return bodies;
|
|
126
|
+
}
|
|
127
|
+
if (pageMode === 'image') {
|
|
128
|
+
try {
|
|
129
|
+
for (const image of await renderPages(fullPath, scanned, signal)) {
|
|
130
|
+
bodies.set(image.page, { image });
|
|
131
|
+
}
|
|
132
|
+
} catch (error) {
|
|
133
|
+
for (const page of scanned) {
|
|
134
|
+
bodies.set(page, { text: `(no text layer, and the page image could not be rendered: ${errorText(error)})` });
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return bodies;
|
|
138
|
+
}
|
|
139
|
+
try {
|
|
140
|
+
for (const { page, text } of await recognizePages(fullPath, scanned, signal)) {
|
|
141
|
+
bodies.set(page, { ocr: text || '(OCR found no text on this page)' });
|
|
142
|
+
}
|
|
143
|
+
} catch (error) {
|
|
144
|
+
const note =
|
|
145
|
+
`(no text layer, and OCR is unavailable here: ${errorText(error)}. OCR language data downloads on first ` +
|
|
146
|
+
'use, so this needs a network connection once; after that it works offline. A model that accepts images ' +
|
|
147
|
+
'can read these pages as page images instead.)';
|
|
148
|
+
for (const page of scanned) bodies.set(page, { text: note });
|
|
149
|
+
}
|
|
150
|
+
return bodies;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// One page range: text pages as their text, scanned pages (no text layer) as
|
|
154
|
+
// page images for a model that reads images, OCR text for a text-only model,
|
|
155
|
+
// or a note in a flat-text context. Order follows the pages.
|
|
156
|
+
async function readPdfPageRange(fullPath, buf, pageCount, range, options) {
|
|
157
|
+
const { maxOutputBytes, note = '' } = options;
|
|
158
|
+
const extracted = await inspectPdfBuffer(buf, {
|
|
159
|
+
extractText: true,
|
|
160
|
+
maxPages: Infinity,
|
|
161
|
+
maxOutputBytes,
|
|
162
|
+
pageRange: range,
|
|
163
|
+
});
|
|
164
|
+
const scanned = extracted.pages.filter((entry) => !pageHasTextLayer(entry.text)).map((entry) => entry.page);
|
|
165
|
+
const bodies = await scannedPageBodies(fullPath, scanned, options);
|
|
166
|
+
const label = pdfFileLabel(fullPath);
|
|
167
|
+
const textPages = extracted.pages.length - scanned.length;
|
|
168
|
+
const header =
|
|
169
|
+
`[PDF ${label}: pages ${range.from}-${range.to} of ${pageCount}; ` +
|
|
170
|
+
`${textPages === extracted.pages.length ? 'every page has a text layer' : `scanned (no text layer): ${pageList(scanned)}`}` +
|
|
171
|
+
`${note ? `. ${note}` : ''}]`;
|
|
172
|
+
const blocks = [];
|
|
173
|
+
let text = header;
|
|
174
|
+
let textBytes = 0;
|
|
175
|
+
const flush = () => {
|
|
176
|
+
if (!text) return;
|
|
177
|
+
blocks.push({ type: 'text', text: capPdfText(text, Math.max(1, maxOutputBytes - textBytes)) });
|
|
178
|
+
textBytes += Buffer.byteLength(text, 'utf8');
|
|
179
|
+
text = '';
|
|
180
|
+
};
|
|
181
|
+
for (const entry of extracted.pages) {
|
|
182
|
+
const body = bodies.get(entry.page);
|
|
183
|
+
if (body?.image) {
|
|
184
|
+
text += `${text ? '\n\n' : ''}--- Page ${entry.page} (scanned; page image follows) ---`;
|
|
185
|
+
flush();
|
|
186
|
+
const { data, mimeType, width, height } = body.image;
|
|
187
|
+
blocks.push({ type: 'image', data, mimeType, width, height });
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
let pageText = entry.text;
|
|
191
|
+
let title = `--- Page ${entry.page} ---`;
|
|
192
|
+
if (body?.ocr) {
|
|
193
|
+
title = `--- Page ${entry.page} (OCR of a scanned page) ---`;
|
|
194
|
+
pageText = body.ocr;
|
|
195
|
+
} else if (body?.text) {
|
|
196
|
+
pageText = body.text;
|
|
197
|
+
}
|
|
198
|
+
text += `${text ? '\n\n' : ''}${title}\n${pageText}`;
|
|
199
|
+
}
|
|
200
|
+
flush();
|
|
201
|
+
if (blocks.every((block) => block.type === 'text')) return blocks.map((block) => block.text).join('\n\n');
|
|
202
|
+
return { content: blocks };
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function pdfInspectError(fullPath, error) {
|
|
206
|
+
const label = pdfFileLabel(fullPath);
|
|
207
|
+
const protectedPdf = error?.name === 'PasswordException' || /password/i.test(String(error?.message || ''));
|
|
208
|
+
return protectedPdf
|
|
209
|
+
? `Error: this PDF is password-protected and cannot be read (${label}).`
|
|
210
|
+
: `Error: ${label} is not a valid PDF (${errorText(error)}).`;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Reads a PDF: whole for a short one, by page range otherwise.
|
|
215
|
+
*
|
|
216
|
+
* pages "N" / "N-M" (≤20) reads that range: text first; pages without a text
|
|
217
|
+
* layer come back as page images (`pageMode: 'image'`, a model that sees
|
|
218
|
+
* images), OCR text (`pageMode: 'ocr'`, a text-only model) or a note
|
|
219
|
+
* (`textOnly`, a batch that must stay flat text). Without pages a PDF of up to
|
|
220
|
+
* 10 pages is returned whole (a document block when `nativeDocument`, the
|
|
221
|
+
* model reading PDFs natively, else every page through the range path), a
|
|
222
|
+
* longer one as the outline and its first 5 pages. offset/limit do not apply.
|
|
223
|
+
*/
|
|
63
224
|
export async function extractPdfText(
|
|
64
225
|
fullPath,
|
|
65
226
|
pagesArg,
|
|
66
|
-
{
|
|
227
|
+
{
|
|
228
|
+
maxOutputBytes = DEFAULT_READ_MAX_OUTPUT_BYTES,
|
|
229
|
+
textOnly = false,
|
|
230
|
+
pageMode = 'image',
|
|
231
|
+
nativeDocument = true,
|
|
232
|
+
signal = null,
|
|
233
|
+
renderPages = renderPdfPageImages,
|
|
234
|
+
recognizePages = recognizePdfPages,
|
|
235
|
+
} = {}
|
|
67
236
|
) {
|
|
68
237
|
try {
|
|
69
238
|
let pdfStat;
|
|
70
239
|
try {
|
|
71
240
|
pdfStat = await stat(fullPath);
|
|
72
241
|
} catch (e) {
|
|
73
|
-
return `Error: pdf stat failed — ${
|
|
242
|
+
return `Error: pdf stat failed — ${errorText(e)}`;
|
|
74
243
|
}
|
|
75
244
|
|
|
76
245
|
const pages = parsePagesArg(pagesArg);
|
|
77
246
|
if (pages.error) return pages.error;
|
|
78
247
|
|
|
79
|
-
|
|
80
|
-
//
|
|
81
|
-
//
|
|
82
|
-
|
|
83
|
-
//
|
|
84
|
-
//
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
`[PDF has ${pageCount} pages, too many to attach natively (max ${MAX_PDF_PAGES});` +
|
|
110
|
-
`showing extracted text of pages 1-${PDF_TEXT_FALLBACK_PAGES}. Pass \`pages\` (e.g. "21-40", max 20 per request) to read others.]\n` +
|
|
111
|
-
(head.text || '(no text content extracted from PDF)')
|
|
112
|
-
);
|
|
248
|
+
const buf = await readFile(fullPath);
|
|
249
|
+
// Not a real PDF (mislabelled extension) — pdf.js surfaces a clean parse
|
|
250
|
+
// error rather than a bad document block entering history.
|
|
251
|
+
if (!isPdfBuffer(buf)) return await extractPdfTextBody(fullPath, pages.filter, maxOutputBytes);
|
|
252
|
+
// A password-protected or invalid PDF is rejected by every provider and
|
|
253
|
+
// would wedge the session if it entered history as a block.
|
|
254
|
+
let pageCount;
|
|
255
|
+
try {
|
|
256
|
+
({ pageCount } = await inspectPdfBuffer(buf, { maxPages: Infinity }));
|
|
257
|
+
} catch (inspectErr) {
|
|
258
|
+
return pdfInspectError(fullPath, inspectErr);
|
|
259
|
+
}
|
|
260
|
+
const mode = textOnly ? 'text' : pageMode;
|
|
261
|
+
const rangeOptions = { maxOutputBytes, pageMode: mode, signal, renderPages, recognizePages };
|
|
262
|
+
if (pages.filter) {
|
|
263
|
+
if (pages.filter.from > pageCount) {
|
|
264
|
+
return `Error: pages "${pagesArg}" out of range — this PDF has ${pageCount} page${pageCount === 1 ? '' : 's'}`;
|
|
265
|
+
}
|
|
266
|
+
const range = { from: pages.filter.from, to: Math.min(pages.filter.to, pageCount) };
|
|
267
|
+
return await readPdfPageRange(fullPath, buf, pageCount, range, rangeOptions);
|
|
268
|
+
}
|
|
269
|
+
if (pageCount > PDF_WHOLE_READ_MAX_PAGES) {
|
|
270
|
+
return await readPdfPageRange(
|
|
271
|
+
fullPath,
|
|
272
|
+
buf,
|
|
273
|
+
pageCount,
|
|
274
|
+
{ from: 1, to: PDF_PREVIEW_PAGES },
|
|
275
|
+
{
|
|
276
|
+
...rangeOptions,
|
|
277
|
+
note: `Showing the first ${PDF_PREVIEW_PAGES} pages; pass pages (e.g. "${PDF_PREVIEW_PAGES + 1}-${Math.min(pageCount, PDF_PREVIEW_PAGES + 10)}", up to ${READ_PDF_MAX_PAGES_PER_CALL} per call) to read the pages the request needs`,
|
|
113
278
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
// A short PDF whole: a model that reads PDFs natively gets the canonical
|
|
282
|
+
// document block (figures, layout, tables) with its page and text-layer
|
|
283
|
+
// counts for the estimate; others get every page through the range path.
|
|
284
|
+
if (mode === 'image' && nativeDocument && pdfStat.size <= MAX_PDF_BYTES) {
|
|
285
|
+
const { textPages, textChars } = await inspectPdfBuffer(buf, { maxPages: Infinity, measureText: true });
|
|
286
|
+
return {
|
|
287
|
+
content: [
|
|
288
|
+
{
|
|
289
|
+
type: 'document',
|
|
290
|
+
source: {
|
|
291
|
+
type: 'base64',
|
|
292
|
+
media_type: 'application/pdf',
|
|
293
|
+
data: buf.toString('base64'),
|
|
124
294
|
},
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
295
|
+
pageCount,
|
|
296
|
+
textPages,
|
|
297
|
+
textChars,
|
|
298
|
+
},
|
|
299
|
+
],
|
|
300
|
+
};
|
|
130
301
|
}
|
|
131
|
-
|
|
132
|
-
// TEXT fallback: >20MB PDFs, page-filtered reads, or non-magic files.
|
|
133
|
-
return await extractPdfTextBody(fullPath, pages.filter, maxOutputBytes);
|
|
302
|
+
return await readPdfPageRange(fullPath, buf, pageCount, { from: 1, to: Math.max(1, pageCount) }, rangeOptions);
|
|
134
303
|
} catch (err) {
|
|
135
|
-
return `Error: PDF extraction failed — ${
|
|
304
|
+
return `Error: PDF extraction failed — ${errorText(err)}`;
|
|
136
305
|
}
|
|
137
306
|
}
|
|
138
307
|
|
|
@@ -197,6 +366,30 @@ function capNotebookText(blocks, maxOutputBytes) {
|
|
|
197
366
|
return capped;
|
|
198
367
|
}
|
|
199
368
|
|
|
369
|
+
// The image blocks (each after its resize note, when it has one) for one
|
|
370
|
+
// cell's image outputs. An image the resize helper cannot take (sharp
|
|
371
|
+
// unavailable) stays the text placeholder already in the cell's text.
|
|
372
|
+
async function notebookImageBlocks(pendingImages, fullPath, cellIndex) {
|
|
373
|
+
const blocks = [];
|
|
374
|
+
for (const img of pendingImages) {
|
|
375
|
+
let buf;
|
|
376
|
+
try {
|
|
377
|
+
buf = Buffer.from(img.b64, 'base64');
|
|
378
|
+
} catch {
|
|
379
|
+
buf = null;
|
|
380
|
+
}
|
|
381
|
+
if (!buf || buf.length === 0) continue;
|
|
382
|
+
const built = await imageBlocksFromBuffer(buf, img.mimeType, {
|
|
383
|
+
sourcePath: `${fullPath} [cell ${cellIndex}]`,
|
|
384
|
+
});
|
|
385
|
+
if (built) {
|
|
386
|
+
if (built.textBlock) blocks.push(built.textBlock);
|
|
387
|
+
blocks.push(built.imageBlock);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
return blocks;
|
|
391
|
+
}
|
|
392
|
+
|
|
200
393
|
export async function extractIpynbText(
|
|
201
394
|
fullPath,
|
|
202
395
|
{ maxOutputBytes = DEFAULT_READ_MAX_OUTPUT_BYTES, hasRangeArgs = false, textOnly = false } = {}
|
|
@@ -214,7 +407,7 @@ export async function extractIpynbText(
|
|
|
214
407
|
try {
|
|
215
408
|
nbStat = await stat(fullPath);
|
|
216
409
|
} catch (e) {
|
|
217
|
-
return `Error: ipynb stat failed — ${
|
|
410
|
+
return `Error: ipynb stat failed — ${errorText(e)}`;
|
|
218
411
|
}
|
|
219
412
|
if (nbStat.size > READ_MAX_SIZE_BYTES) {
|
|
220
413
|
return `Error: notebook too large (size: ${nbStat.size}B, cap: ${READ_MAX_SIZE_BYTES}B) — use a cell range to narrow`;
|
|
@@ -253,23 +446,7 @@ export async function extractIpynbText(
|
|
|
253
446
|
// placeholder in `block` is the only representation.
|
|
254
447
|
if (!textOnly && pendingImages.length > 0) {
|
|
255
448
|
flushText();
|
|
256
|
-
|
|
257
|
-
let buf;
|
|
258
|
-
try {
|
|
259
|
-
buf = Buffer.from(img.b64, 'base64');
|
|
260
|
-
} catch {
|
|
261
|
-
buf = null;
|
|
262
|
-
}
|
|
263
|
-
if (!buf || buf.length === 0) continue;
|
|
264
|
-
const built = await imageBlocksFromBuffer(buf, img.mimeType, {
|
|
265
|
-
sourcePath: `${fullPath} [cell ${cellIndex}]`,
|
|
266
|
-
});
|
|
267
|
-
if (built) {
|
|
268
|
-
if (built.textBlock) blocks.push(built.textBlock);
|
|
269
|
-
blocks.push(built.imageBlock);
|
|
270
|
-
}
|
|
271
|
-
// built === null → sharp unavailable; placeholder text already emitted.
|
|
272
|
-
}
|
|
449
|
+
blocks.push(...(await notebookImageBlocks(pendingImages, fullPath, cellIndex)));
|
|
273
450
|
}
|
|
274
451
|
}
|
|
275
452
|
}
|
|
@@ -293,6 +470,6 @@ export async function extractIpynbText(
|
|
|
293
470
|
}
|
|
294
471
|
return { content: blocks };
|
|
295
472
|
} catch (err) {
|
|
296
|
-
return `Error: ipynb parse failed — ${
|
|
473
|
+
return `Error: ipynb parse failed — ${errorText(err)}`;
|
|
297
474
|
}
|
|
298
475
|
}
|
|
@@ -1255,9 +1255,9 @@ export function buildPowerShellFilterTeePlan(command) {
|
|
|
1255
1255
|
}
|
|
1256
1256
|
|
|
1257
1257
|
// Read the tail of a tee spill file (BOM-sniffed: Windows PowerShell 5.1
|
|
1258
|
-
// writes UTF-16LE, pwsh 7 writes UTF-8)
|
|
1259
|
-
//
|
|
1260
|
-
export function
|
|
1258
|
+
// writes UTF-16LE, pwsh 7 writes UTF-8). Returns null when the file is
|
|
1259
|
+
// missing/empty/unreadable.
|
|
1260
|
+
export function readFilterTeeCapture(teePath, { maxBytes = 16384 } = {}) {
|
|
1261
1261
|
let out = null;
|
|
1262
1262
|
try {
|
|
1263
1263
|
const st = statSync(teePath);
|
|
@@ -1281,10 +1281,16 @@ export function consumeFilterTeeCapture(teePath, { maxBytes = 16384 } = {}) {
|
|
|
1281
1281
|
} catch {
|
|
1282
1282
|
out = null;
|
|
1283
1283
|
}
|
|
1284
|
+
return out?.trim() ? out : null;
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
// The tail of a tee spill file, which is then ALWAYS deleted.
|
|
1288
|
+
export function consumeFilterTeeCapture(teePath, options) {
|
|
1289
|
+
const out = readFilterTeeCapture(teePath, options);
|
|
1284
1290
|
try {
|
|
1285
1291
|
unlinkSync(teePath);
|
|
1286
1292
|
} catch {
|
|
1287
1293
|
/* best-effort cleanup */
|
|
1288
1294
|
}
|
|
1289
|
-
return out
|
|
1295
|
+
return out;
|
|
1290
1296
|
}
|
|
@@ -28,10 +28,8 @@ import {
|
|
|
28
28
|
adoptShellWorkspaceAdmission,
|
|
29
29
|
clearTerminationHold,
|
|
30
30
|
completeShellJobRecord,
|
|
31
|
-
isShellOwnerSessionClosed,
|
|
32
31
|
listShellJobRecords,
|
|
33
32
|
markShellJobTerminationUnconfirmed,
|
|
34
|
-
noteShellJobSurvivors,
|
|
35
33
|
publishShellJobRecord,
|
|
36
34
|
registerTerminationHold,
|
|
37
35
|
retireShellJobRecord,
|
|
@@ -196,8 +194,6 @@ function trackShellJobRecord(task, options) {
|
|
|
196
194
|
sweep: killed,
|
|
197
195
|
onTerminated: finish,
|
|
198
196
|
releaseRequested: () => takeReleaseRequest(jobId),
|
|
199
|
-
ownerClosed: () => isShellOwnerSessionClosed(options?.ownerSessionId),
|
|
200
|
-
onSurvivors: (pids) => void noteShellJobSurvivors(jobId, pids),
|
|
201
197
|
onUnconfirmed: () => {
|
|
202
198
|
void markShellJobTerminationUnconfirmed(jobId);
|
|
203
199
|
registerTerminationHold(jobId, {
|
|
@@ -343,41 +339,58 @@ export function peekShellJob(jobId) {
|
|
|
343
339
|
return attachJobInsights(normalizeShellJobDetail(getNativeTask(jobId)));
|
|
344
340
|
}
|
|
345
341
|
|
|
346
|
-
/**
|
|
342
|
+
/** Run `onTreeSettled` once a job whose native task is terminal is over as a
|
|
343
|
+
* whole: at once for an untracked job, else once every process of its tree is
|
|
344
|
+
* confirmed gone (or the user released it) — a root's exit is not the end of
|
|
345
|
+
* what it started. */
|
|
346
|
+
function afterTreeSettled(jobId, onTreeSettled) {
|
|
347
|
+
const entry = backgroundTrackers.get(jobId);
|
|
348
|
+
if (entry) void entry.terminated.then(onTreeSettled);
|
|
349
|
+
else onTreeSettled();
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/** True while a process of the job is still alive in this daemon. Owners of
|
|
347
353
|
* in-use artifacts consult it before a fallback cleanup fires. */
|
|
348
354
|
export function isShellJobRunning(jobId) {
|
|
349
|
-
|
|
355
|
+
const key = String(jobId || '');
|
|
356
|
+
return getNativeTask(key)?.status === 'running' || backgroundTrackers.has(key);
|
|
350
357
|
}
|
|
351
358
|
|
|
352
|
-
/** Run `onSettled` once the job leaves 'running'
|
|
353
|
-
*
|
|
354
|
-
* cache invalidation) use this instead of
|
|
355
|
-
* under a still-running command or never at
|
|
356
|
-
* null when the job is unknown here — the
|
|
359
|
+
/** Run `onSettled` once the job leaves 'running' and its tree has settled
|
|
360
|
+
* (immediately when both already have). Owners of per-command artifacts
|
|
361
|
+
* (hoisted script files, tee captures, cache invalidation) use this instead of
|
|
362
|
+
* a fixed timer, which either fired under a still-running command or never at
|
|
363
|
+
* all. Returns an unsubscribe, or null when the job is unknown here — the
|
|
364
|
+
* caller then keeps its own fallback. */
|
|
357
365
|
export function subscribeShellJobSettled(jobId, onSettled) {
|
|
358
366
|
const key = String(jobId || '').trim();
|
|
359
367
|
if (!key || typeof onSettled !== 'function') return null;
|
|
360
368
|
const current = getNativeTask(key);
|
|
361
369
|
if (!current) return null;
|
|
362
|
-
|
|
363
|
-
|
|
370
|
+
let done = false;
|
|
371
|
+
const deliver = (task) =>
|
|
372
|
+
afterTreeSettled(key, () => {
|
|
373
|
+
if (done) return;
|
|
374
|
+
done = true;
|
|
364
375
|
try {
|
|
365
|
-
onSettled(
|
|
376
|
+
onSettled(task);
|
|
366
377
|
} catch {}
|
|
367
378
|
});
|
|
368
|
-
|
|
379
|
+
if (current.status !== 'running') {
|
|
380
|
+
queueMicrotask(() => deliver(current));
|
|
381
|
+
return () => {
|
|
382
|
+
done = true;
|
|
383
|
+
};
|
|
369
384
|
}
|
|
370
|
-
let
|
|
385
|
+
let seen = false;
|
|
371
386
|
let unsubscribe = () => {};
|
|
372
387
|
const fire = (task) => {
|
|
373
|
-
if (done || !task || task.status === 'running') return;
|
|
374
|
-
|
|
388
|
+
if (seen || done || !task || task.status === 'running') return;
|
|
389
|
+
seen = true;
|
|
375
390
|
try {
|
|
376
391
|
unsubscribe();
|
|
377
392
|
} catch {}
|
|
378
|
-
|
|
379
|
-
onSettled(task);
|
|
380
|
-
} catch {}
|
|
393
|
+
deliver(task);
|
|
381
394
|
};
|
|
382
395
|
unsubscribe = subscribeNativeTask(key, fire);
|
|
383
396
|
return () => {
|
|
@@ -38,12 +38,16 @@ export const CODE_GRAPH_DISK_MEMORY_MAX_BYTES = Math.max(
|
|
|
38
38
|
// Younger .tmp files may belong to an in-flight persist still holding the sibling .lock;
|
|
39
39
|
// DEFAULT_LOCK_TIMEOUT_MS is 8s — 120s is a safe margin for large graph JSON writes.
|
|
40
40
|
export const ORPHAN_TMP_MIN_AGE_MS = 120_000;
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
// Atomic writer temp name: `<basename>.mixdog-tmp-<16hex>`; the dot-prefixed
|
|
42
|
+
// `.<basename>.<24hex>.tmp` is what older builds left behind.
|
|
43
|
+
export const RE_CACHE_TMP =
|
|
44
|
+
/^(?:\.[0-9a-f]{16}\.json\.[0-9a-f]{24}\.tmp|[0-9a-f]{16}\.json\.mixdog-tmp-[0-9a-f]{16,24})$/i;
|
|
45
|
+
export const RE_MANIFEST_TMP = /^(?:\.manifest\.json\.[0-9a-f]{24}\.tmp|manifest\.json\.mixdog-tmp-[0-9a-f]{16,24})$/i;
|
|
43
46
|
export const RE_CACHE_LOCK = /^[0-9a-f]{16}\.json\.lock$/i;
|
|
44
47
|
// Same debris, for the `<hash>.calls.json` call-site sidecar written beside
|
|
45
|
-
// each entry
|
|
46
|
-
export const RE_CALLS_CACHE_TMP =
|
|
48
|
+
// each entry.
|
|
49
|
+
export const RE_CALLS_CACHE_TMP =
|
|
50
|
+
/^(?:\.[0-9a-f]{16}\.calls\.json\.[0-9a-f]{24}\.tmp|[0-9a-f]{16}\.calls\.json\.mixdog-tmp-[0-9a-f]{16,24})$/i;
|
|
47
51
|
export const RE_CALLS_CACHE_LOCK = /^[0-9a-f]{16}\.calls\.json\.lock$/i;
|
|
48
52
|
export const CODE_GRAPH_MEMORY_MAX_ENTRIES = Math.max(
|
|
49
53
|
1,
|