create-walle 0.10.0 → 0.10.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/README.md +88 -41
- package/lib/owner-write-safety.js +135 -0
- package/lib/public-runtime.js +13 -5
- package/lib/release-schema.json +3 -2
- package/lib/runtime-closure-policy.json +7 -5
- package/package.json +10 -4
- package/template/bin/deps-doctor.js +1 -1
- package/template/bin/node-bin.sh +11 -0
- package/template/coding-task-manager/api-prompts.js +10 -22
- package/template/coding-task-manager/bin/capture-codex-rollout-records.js +417 -0
- package/template/coding-task-manager/bin/census-codex-lead-history.js +325 -0
- package/template/coding-task-manager/bin/claude-hook.js +7 -3
- package/template/coding-task-manager/bin/claude-table-parity.js +307 -0
- package/template/coding-task-manager/bin/conversation-storage-auto-migration-lib.js +16 -1
- package/template/coding-task-manager/bin/conversation-storage-generation-evidence.js +9 -2
- package/template/coding-task-manager/bin/conversation-storage-service-entry-lib.js +16 -1
- package/template/coding-task-manager/bin/conversation-storage-walle-owner.js +247 -0
- package/template/coding-task-manager/bin/ctm-machine-pair.js +160 -0
- package/template/coding-task-manager/bin/lead-wait-census.js +1348 -0
- package/template/coding-task-manager/bin/repair-foreign-clone.js +564 -0
- package/template/coding-task-manager/bin/repair-resume-echo-titles.js +248 -0
- package/template/coding-task-manager/bin/seam-replay-codex.js +301 -0
- package/template/coding-task-manager/bin/write-lock-bench.js +111 -0
- package/template/coding-task-manager/db.js +705 -286
- package/template/coding-task-manager/git-utils.js +44 -0
- package/template/coding-task-manager/lib/adopted-host-redial.js +4 -0
- package/template/coding-task-manager/lib/agent-presets.js +15 -0
- package/template/coding-task-manager/lib/agent-title-apply.js +88 -0
- package/template/coding-task-manager/lib/agent-title-backfill-read.js +92 -0
- package/template/coding-task-manager/lib/api/host-stats.js +98 -0
- package/template/coding-task-manager/lib/api/ingress.js +35 -12
- package/template/coding-task-manager/lib/api/machine-pairing.js +285 -0
- package/template/coding-task-manager/lib/api/machine-pool.js +307 -0
- package/template/coding-task-manager/lib/api/machines.js +461 -0
- package/template/coding-task-manager/lib/api/misc-ops.js +20 -5
- package/template/coding-task-manager/lib/api/providers.js +53 -6
- package/template/coding-task-manager/lib/api/resume-from-store.js +25 -0
- package/template/coding-task-manager/lib/api/sessions.js +160 -8
- package/template/coding-task-manager/lib/api/transcript-history.js +17 -4
- package/template/coding-task-manager/lib/api/worktrees.js +38 -37
- package/template/coding-task-manager/lib/approval-drift.js +16 -1
- package/template/coding-task-manager/lib/approval-logical-frame.js +5 -1
- package/template/coding-task-manager/lib/approval-scope.js +68 -4
- package/template/coding-task-manager/lib/auth-context.js +32 -2
- package/template/coding-task-manager/lib/auth-rate-limit.js +41 -1
- package/template/coding-task-manager/lib/auth-rules.js +186 -45
- package/template/coding-task-manager/lib/broadcast-serialize.js +2 -1
- package/template/coding-task-manager/lib/browser-session-broker.js +9 -1
- package/template/coding-task-manager/lib/browser-use-adapter.js +33 -2
- package/template/coding-task-manager/lib/claude-markdown-ansi.js +27 -85
- package/template/coding-task-manager/lib/claude-session-title.js +6 -30
- package/template/coding-task-manager/lib/claude-table-layout.js +322 -0
- package/template/coding-task-manager/lib/client-diag-handler.js +25 -0
- package/template/coding-task-manager/lib/codex-config-guard.js +33 -3
- package/template/coding-task-manager/lib/codex-harness-qualification.js +12 -0
- package/template/coding-task-manager/lib/codex-history-terminal-renderer.js +33 -33
- package/template/coding-task-manager/lib/codex-launch-health.js +29 -2
- package/template/coding-task-manager/lib/codex-link-preparation.js +9 -8
- package/template/coding-task-manager/lib/codex-local-links.js +267 -0
- package/template/coding-task-manager/lib/codex-markdown-lists.js +160 -0
- package/template/coding-task-manager/lib/codex-native-history-prefix.js +4 -0
- package/template/coding-task-manager/lib/codex-paths.js +2 -1
- package/template/coding-task-manager/lib/codex-source-handoff.js +186 -0
- package/template/coding-task-manager/lib/coding-agent-models.js +21 -30
- package/template/coding-task-manager/lib/coding-session-model-adapters.js +27 -2
- package/template/coding-task-manager/lib/coding-session-model-projection.js +5 -0
- package/template/coding-task-manager/lib/coding-session-model-runtime.js +1 -2
- package/template/coding-task-manager/lib/coding-session-model-state.js +10 -0
- package/template/coding-task-manager/lib/coding-session-model-sync-state.js +0 -1
- package/template/coding-task-manager/lib/conversation-import/cursor-guard-owner.js +10 -0
- package/template/coding-task-manager/lib/create/resolve-launch-params.js +8 -1
- package/template/coding-task-manager/lib/ctm-boot-lifecycle.js +17 -1
- package/template/coding-task-manager/lib/db-domain-tables.js +7 -0
- package/template/coding-task-manager/lib/db-domains.js +21 -2
- package/template/coding-task-manager/lib/db-owner-op-registry.js +7 -0
- package/template/coding-task-manager/lib/db-owner-worker-client.js +22 -0
- package/template/coding-task-manager/lib/db-schema-source-files.js +1 -0
- package/template/coding-task-manager/lib/feature-manifest.js +1 -3
- package/template/coding-task-manager/lib/fs-watcher.js +3 -2
- package/template/coding-task-manager/lib/harness-history-transition.js +41 -14
- package/template/coding-task-manager/lib/headless-inflight-ops.js +203 -0
- package/template/coding-task-manager/lib/headless-missing-reply.js +42 -0
- package/template/coding-task-manager/lib/headless-term-service.js +60 -23
- package/template/coding-task-manager/lib/history-transition-read-op.js +179 -23
- package/template/coding-task-manager/lib/host-control-dial.js +167 -0
- package/template/coding-task-manager/lib/host-handoff-orchestrator.js +16 -2
- package/template/coding-task-manager/lib/host-handoff.js +51 -13
- package/template/coding-task-manager/lib/host-health-ledger.js +164 -12
- package/template/coding-task-manager/lib/host-self-truth-watchdog.js +4 -0
- package/template/coding-task-manager/lib/host-stats.js +328 -0
- package/template/coding-task-manager/lib/host-threadpool-evacuation.js +160 -0
- package/template/coding-task-manager/lib/host-threadpool-monitor.js +14 -1
- package/template/coding-task-manager/lib/hosted-input-dispatch.js +203 -0
- package/template/coding-task-manager/lib/hosted-sync-worker.js +113 -5
- package/template/coding-task-manager/lib/instance-id-ownership-manifest.js +4 -0
- package/template/coding-task-manager/lib/instance-manifest-runtime.js +3 -0
- package/template/coding-task-manager/lib/instance-manifest.js +13 -1
- package/template/coding-task-manager/lib/instance-sync-status.js +15 -0
- package/template/coding-task-manager/lib/kernel/diag-log.js +12 -0
- package/template/coding-task-manager/lib/launch-model-override.js +160 -1
- package/template/coding-task-manager/lib/launch-plan-worker.js +59 -2
- package/template/coding-task-manager/lib/lead-model-observer.js +17 -2
- package/template/coding-task-manager/lib/lead-model-switch-runtime.js +57 -4
- package/template/coding-task-manager/lib/lead-model-switch.js +11 -2
- package/template/coding-task-manager/lib/lead-model-transition-runtime.js +42 -4
- package/template/coding-task-manager/lib/lead-model-transition-store.js +61 -3
- package/template/coding-task-manager/lib/legacy-draft-cancellation-recovery.js +111 -0
- package/template/coding-task-manager/lib/login-tripwire.js +18 -0
- package/template/coding-task-manager/lib/machine-pairing.js +238 -0
- package/template/coding-task-manager/lib/machine-pool.js +409 -0
- package/template/coding-task-manager/lib/machine-registry.js +749 -0
- package/template/coding-task-manager/lib/machine-self.js +66 -0
- package/template/coding-task-manager/lib/main-thread-lint-exemptions.json +56 -4
- package/template/coding-task-manager/lib/media-journal-control-reader.js +473 -0
- package/template/coding-task-manager/lib/media-journal.js +84 -27
- package/template/coding-task-manager/lib/media-store-options.js +43 -15
- package/template/coding-task-manager/lib/migrations/v56-host-samples-and-machine-pool.js +76 -0
- package/template/coding-task-manager/lib/mirror-feed-sanitize.js +12 -3
- package/template/coding-task-manager/lib/model-execution-recipe-metadata.js +16 -3
- package/template/coding-task-manager/lib/model-proxy/anthropic-face.js +4 -0
- package/template/coding-task-manager/lib/model-proxy/chat-response-translators.js +3 -0
- package/template/coding-task-manager/lib/model-proxy/collaboration-codec.js +94 -0
- package/template/coding-task-manager/lib/model-proxy/handoff.js +494 -0
- package/template/coding-task-manager/lib/model-proxy/lead-worker-picks.js +25 -0
- package/template/coding-task-manager/lib/model-proxy/native-lead-listener.js +61 -7
- package/template/coding-task-manager/lib/model-proxy/proxy-cli.js +200 -0
- package/template/coding-task-manager/lib/model-proxy/proxy-instance-record.js +257 -0
- package/template/coding-task-manager/lib/model-proxy/responses-face.js +24 -2
- package/template/coding-task-manager/lib/model-proxy/stream-accounting.js +205 -0
- package/template/coding-task-manager/lib/model-proxy/tokens.js +78 -0
- package/template/coding-task-manager/lib/model-proxy/upstream-outcome.js +9 -0
- package/template/coding-task-manager/lib/native-history-control.js +62 -14
- package/template/coding-task-manager/lib/native-lead-settings-client.js +2 -1
- package/template/coding-task-manager/lib/native-lead-tls.js +18 -4
- package/template/coding-task-manager/lib/oauth-proxy-supervisor.js +628 -64
- package/template/coding-task-manager/lib/owner-action-manifest-data.json +14 -0
- package/template/coding-task-manager/lib/painted-row-recorder.js +9 -1
- package/template/coding-task-manager/lib/prompt-admission-trace.js +69 -0
- package/template/coding-task-manager/lib/prompt-submission-contract.js +21 -1
- package/template/coding-task-manager/lib/provider-login-broker.js +192 -9
- package/template/coding-task-manager/lib/provider-login-capture.js +153 -0
- package/template/coding-task-manager/lib/queue-draft-status-overlay.js +36 -0
- package/template/coding-task-manager/lib/raw-codex-history-window.js +5 -3
- package/template/coding-task-manager/lib/raw-history-block.js +3 -1
- package/template/coding-task-manager/lib/read-pool-client.js +36 -24
- package/template/coding-task-manager/lib/read-pool-op-registry.js +10 -1
- package/template/coding-task-manager/lib/read-pool-telemetry.js +14 -5
- package/template/coding-task-manager/lib/recent-catalog-query.js +26 -0
- package/template/coding-task-manager/lib/recent-session-catalog-response.js +9 -0
- package/template/coding-task-manager/lib/remote-command-context.js +12 -0
- package/template/coding-task-manager/lib/remote-command-runtime.js +18 -5
- package/template/coding-task-manager/lib/remote-machine-catalog.js +1062 -0
- package/template/coding-task-manager/lib/remote-machine-create.js +693 -0
- package/template/coding-task-manager/lib/remote-machine-link.js +939 -0
- package/template/coding-task-manager/lib/remote-machine-proxy.js +520 -0
- package/template/coding-task-manager/lib/remote-session-forward.js +306 -0
- package/template/coding-task-manager/lib/remote-submission-status.js +6 -0
- package/template/coding-task-manager/lib/restart-outcome-truth.js +20 -3
- package/template/coding-task-manager/lib/restart-preflight.js +9 -2
- package/template/coding-task-manager/lib/restore-backoff-gate.js +10 -2
- package/template/coding-task-manager/lib/restore-prepare-retry.js +12 -5
- package/template/coding-task-manager/lib/restore-resume-batch.js +22 -1
- package/template/coding-task-manager/lib/resume-blocked-wire.js +62 -1
- package/template/coding-task-manager/lib/resume-cwd.js +13 -0
- package/template/coding-task-manager/lib/resume-from-store.js +12 -1
- package/template/coding-task-manager/lib/resume-origin-facts.js +38 -1
- package/template/coding-task-manager/lib/resume-preflight.js +21 -16
- package/template/coding-task-manager/lib/review-repo-admission.js +39 -6
- package/template/coding-task-manager/lib/review-repo-registry.js +17 -0
- package/template/coding-task-manager/lib/root-delivery-runtime.js +72 -15
- package/template/coding-task-manager/lib/root-delivery-selection.js +37 -1
- package/template/coding-task-manager/lib/root-prompt-admission.js +43 -11
- package/template/coding-task-manager/lib/root-prompt-flow.js +175 -38
- package/template/coding-task-manager/lib/scrollback-archive-store.js +7 -4
- package/template/coding-task-manager/lib/server-memory-sampler-runtime.js +10 -0
- package/template/coding-task-manager/lib/server-memory-sampler.js +41 -2
- package/template/coding-task-manager/lib/session-analysis-memory.js +2 -0
- package/template/coding-task-manager/lib/session-create/codex-native-provider-gate.js +218 -0
- package/template/coding-task-manager/lib/session-create/codex-resume-exit-classify.js +52 -0
- package/template/coding-task-manager/lib/session-create/delegate-worker.js +95 -6
- package/template/coding-task-manager/lib/session-create/identity.js +20 -1
- package/template/coding-task-manager/lib/session-create/prepared.js +69 -1
- package/template/coding-task-manager/lib/session-create/provider-launch-context.js +77 -0
- package/template/coding-task-manager/lib/session-create/provider-process.js +38 -39
- package/template/coding-task-manager/lib/session-create/response.js +30 -12
- package/template/coding-task-manager/lib/session-create/transcript.js +13 -4
- package/template/coding-task-manager/lib/session-create/worker-continuation.js +43 -1
- package/template/coding-task-manager/lib/session-create/worker-notices.js +94 -5
- package/template/coding-task-manager/lib/session-create/worker-observability.js +99 -7
- package/template/coding-task-manager/lib/session-create/worker-stop.js +11 -3
- package/template/coding-task-manager/lib/session-create/worker-wait-watcher.js +315 -0
- package/template/coding-task-manager/lib/session-geometry-authority-handler.js +10 -1
- package/template/coding-task-manager/lib/session-host-manager.js +152 -206
- package/template/coding-task-manager/lib/session-jobs.js +17 -9
- package/template/coding-task-manager/lib/session-lifecycle.js +20 -50
- package/template/coding-task-manager/lib/session-model-action-preconditions.js +68 -3
- package/template/coding-task-manager/lib/session-resource-sampler.js +67 -9
- package/template/coding-task-manager/lib/session-standup.js +67 -4
- package/template/coding-task-manager/lib/session-status-ledger.js +42 -3
- package/template/coding-task-manager/lib/session-status-runtime.js +45 -1
- package/template/coding-task-manager/lib/session-status.js +2 -2
- package/template/coding-task-manager/lib/session-title-mirror.js +25 -57
- package/template/coding-task-manager/lib/session-transcript-trash.js +8 -1
- package/template/coding-task-manager/lib/startup-pointer-write.js +57 -0
- package/template/coding-task-manager/lib/state-sync/painted-frame-field.js +11 -0
- package/template/coding-task-manager/lib/storage/coding-session-model-store.js +21 -22
- package/template/coding-task-manager/lib/surviving-owner-bootstrap.js +7 -2
- package/template/coding-task-manager/lib/surviving-owner-recovery.js +2 -1
- package/template/coding-task-manager/lib/terminal-draft-http-status.js +25 -0
- package/template/coding-task-manager/lib/transcript-archiver.js +229 -229
- package/template/coding-task-manager/lib/transcript-frame-cache.js +12 -2
- package/template/coding-task-manager/lib/transcript-guard-job.js +55 -4
- package/template/coding-task-manager/lib/transcript-link.js +77 -0
- package/template/coding-task-manager/lib/transcript-manifest-store.js +8 -4
- package/template/coding-task-manager/lib/transcript-source.js +8 -3
- package/template/coding-task-manager/lib/transcript-store.js +40 -0
- package/template/coding-task-manager/lib/tunnel-lane-governor.js +6 -3
- package/template/coding-task-manager/lib/typing-interaction-trace.js +136 -2
- package/template/coding-task-manager/lib/typing-worker-stages.js +208 -1
- package/template/coding-task-manager/lib/usage-ledger/claude-parse.js +53 -2
- package/template/coding-task-manager/lib/usage-ledger/ingest.js +13 -4
- package/template/coding-task-manager/lib/usage-ledger/read.js +14 -0
- package/template/coding-task-manager/lib/usage-ledger/schema.js +49 -1
- package/template/coding-task-manager/lib/usage-ledger/store.js +337 -6
- package/template/coding-task-manager/lib/usage-ledger/task-read.js +123 -5
- package/template/coding-task-manager/lib/visible-revision-watchdog.js +8 -12
- package/template/coding-task-manager/lib/wake-skip-reasons.js +46 -0
- package/template/coding-task-manager/lib/walle-supervisor.js +98 -14
- package/template/coding-task-manager/lib/worker-failure-classify.js +13 -1
- package/template/coding-task-manager/lib/ws/input.js +1 -0
- package/template/coding-task-manager/lib/ws/state-sync.js +19 -2
- package/template/coding-task-manager/lib/ws/terminal-attach.js +32 -1
- package/template/coding-task-manager/lib/ws-router.js +68 -1
- package/template/coding-task-manager/lib/ws-send-admission.js +22 -4
- package/template/coding-task-manager/oauth-proxy.js +99 -55
- package/template/coding-task-manager/package-lock.json +24 -0
- package/template/coding-task-manager/package.json +3 -0
- package/template/coding-task-manager/protocol/message-table.json +0 -10
- package/template/coding-task-manager/protocol/messages.js +0 -10
- package/template/coding-task-manager/protocol/ws-schema.json +0 -20
- package/template/coding-task-manager/protocol/ws-schema.md +0 -2
- package/template/coding-task-manager/providers/codex-mcp-extract.js +121 -19
- package/template/coding-task-manager/providers/codex-mcp.js +14 -5
- package/template/coding-task-manager/public/css/app.css +279 -2
- package/template/coding-task-manager/public/css/reviews.css +4 -0
- package/template/coding-task-manager/public/index.html +297 -240
- package/template/coding-task-manager/public/js/app/machines-page.js +1252 -0
- package/template/coding-task-manager/public/js/app/models-tab.js +546 -36
- package/template/coding-task-manager/public/js/app/worktrees-panel.js +7 -5
- package/template/coding-task-manager/public/js/background-work.js +13 -7
- package/template/coding-task-manager/public/js/client-health-core.js +81 -0
- package/template/coding-task-manager/public/js/client-health-main.js +61 -0
- package/template/coding-task-manager/public/js/coding-session-model-presentation.js +44 -4
- package/template/coding-task-manager/public/js/core/session-commands.js +676 -0
- package/template/coding-task-manager/public/js/core/session-selectors.js +5 -1
- package/template/coding-task-manager/public/js/history-app-seam.js +26 -2
- package/template/coding-task-manager/public/js/history-painted-rows.js +17 -2
- package/template/coding-task-manager/public/js/history-reader-park.js +13 -1
- package/template/coding-task-manager/public/js/history-scroller.js +187 -9
- package/template/coding-task-manager/public/js/history-transcript-pages.js +5 -5
- package/template/coding-task-manager/public/js/machine-pool-shared.js +436 -0
- package/template/coding-task-manager/public/js/private-state-scope.js +31 -3
- package/template/coding-task-manager/public/js/prompt-submission-client.js +173 -4
- package/template/coding-task-manager/public/js/prompt-submission-tray.js +281 -0
- package/template/coding-task-manager/public/js/protocol.js +0 -10
- package/template/coding-task-manager/public/js/queue-panel-submission-reconcile.js +174 -1
- package/template/coding-task-manager/public/js/reviews.js +7 -3
- package/template/coding-task-manager/public/js/root-draft-lifetime.js +139 -2
- package/template/coding-task-manager/public/js/root-queue-builder-lifetime.js +16 -0
- package/template/coding-task-manager/public/js/row-join.js +18 -3
- package/template/coding-task-manager/public/js/session-model-picker.js +1 -1
- package/template/coding-task-manager/public/js/session-search-utils.js +46 -19
- package/template/coding-task-manager/public/js/session-usage-view.js +1 -0
- package/template/coding-task-manager/public/js/state-sync-client.js +4 -0
- package/template/coding-task-manager/public/js/terminal/runtime-02.js +16 -7
- package/template/coding-task-manager/public/js/terminal/runtime-03.js +41 -0
- package/template/coding-task-manager/public/js/terminal/runtime-04.js +41 -4
- package/template/coding-task-manager/public/js/terminal/runtime-12.js +52 -2
- package/template/coding-task-manager/public/js/terminal/runtime-16.js +12 -12
- package/template/coding-task-manager/public/js/terminal/runtime-18.js +14 -0
- package/template/coding-task-manager/public/js/terminal/runtime-19.js +8 -4
- package/template/coding-task-manager/public/js/terminal/runtime-20.js +210 -73
- package/template/coding-task-manager/public/js/terminal/runtime-21.js +19 -6
- package/template/coding-task-manager/public/js/terminal/runtime-22.js +86 -0
- package/template/coding-task-manager/public/js/terminal-history-viewport.js +1 -3
- package/template/coding-task-manager/public/js/terminal-wheel.js +39 -2
- package/template/coding-task-manager/public/js/walle/chat-conversation.js +11 -3
- package/template/coding-task-manager/public/js/walle-session.js +30 -7
- package/template/coding-task-manager/public/m/app.css +226 -8
- package/template/coding-task-manager/public/m/app.js +502 -9
- package/template/coding-task-manager/public/m/index.html +38 -1
- package/template/coding-task-manager/public/m/machines.js +1313 -0
- package/template/coding-task-manager/public/m/pool-failover.js +391 -0
- package/template/coding-task-manager/public/m/private-queue-actions.js +26 -7
- package/template/coding-task-manager/public/m/sw.js +5 -0
- package/template/coding-task-manager/queue-engine.js +13 -0
- package/template/coding-task-manager/server/approvals.js +5 -6
- package/template/coding-task-manager/server/boot.js +136 -17
- package/template/coding-task-manager/server.js +964 -425
- package/template/coding-task-manager/session-integrity.js +7 -9
- package/template/coding-task-manager/workers/db-owner-worker.js +53 -7
- package/template/coding-task-manager/workers/read-pool-worker.js +17 -6
- package/template/coding-task-manager/workers/session-host-pool-process.js +18 -4
- package/template/coding-task-manager/workers/session-host-process.js +14 -3
- package/template/coding-task-manager/workers/transcript-archive-worker.js +1 -1
- package/template/coding-task-manager/workers/worktree-worker.js +34 -0
- package/template/package-lock.json +2 -2
- package/template/package.json +1 -1
- package/template/scripts/conversation-storage-v2-migration.js +78 -11
- package/template/shared/conversation-storage-v2-contract.js +42 -6
- package/template/shared/fixtures/worker-status-payloads.json +392 -0
- package/template/shared/git-remote-key.js +83 -0
- package/template/shared/host-identity.js +98 -2
- package/template/shared/owner-write-safety.js +135 -0
- package/template/shared/public-runtime.js +13 -5
- package/template/shared/release-schema.json +3 -2
- package/template/shared/report-citations.js +14 -0
- package/template/shared/sqlite-write-lock.js +361 -223
- package/template/shared/storage/contract.js +35 -3
- package/template/wall-e/agent.js +21 -0
- package/template/wall-e/api-walle.js +5 -0
- package/template/wall-e/bin/brain-purge-legacy-ctm.js +3 -2
- package/template/wall-e/bin/walle-mcp-stdio.js +12 -6
- package/template/wall-e/brain/helper-rules-schema.js +129 -0
- package/template/wall-e/brain/migrations.js +51 -22
- package/template/wall-e/brain/model-deployment-facts.js +31 -1
- package/template/wall-e/brain/owner-call-whitelist.js +16 -0
- package/template/wall-e/brain/schema-ensures.js +22 -0
- package/template/wall-e/brain.js +1 -1
- package/template/wall-e/coding/host-capabilities.js +2 -2
- package/template/wall-e/coding/journal-dispatcher.js +51 -0
- package/template/wall-e/coding/session-journal.js +28 -40
- package/template/wall-e/coding-orchestrator.js +23 -26
- package/template/wall-e/fly.toml +1 -1
- package/template/wall-e/http/auth.js +18 -1
- package/template/wall-e/http/blog-views-api.js +318 -0
- package/template/wall-e/http/helper-rules-api.js +321 -0
- package/template/wall-e/http/model-proxy-providers.js +3 -1
- package/template/wall-e/http/telemetry-api.js +12 -0
- package/template/wall-e/lib/blog-view-policy.js +88 -0
- package/template/wall-e/lib/blog-view-store.js +169 -0
- package/template/wall-e/lib/client-config-lock.js +2 -1
- package/template/wall-e/lib/coding-journal-writer-child-client.js +67 -18
- package/template/wall-e/lib/error-kinds.js +10 -1
- package/template/wall-e/lib/feature-manifest.js +1 -1
- package/template/wall-e/lib/helper-rules/cell-state.js +123 -0
- package/template/wall-e/lib/helper-rules/evaluate.js +357 -0
- package/template/wall-e/lib/helper-rules/explore-rubric.md +12 -0
- package/template/wall-e/lib/helper-rules/grade.js +277 -0
- package/template/wall-e/lib/helper-rules/label.js +247 -0
- package/template/wall-e/lib/helper-rules/lead-call-hook.js +522 -0
- package/template/wall-e/lib/helper-rules/store.js +667 -0
- package/template/wall-e/lib/mcp-integration.js +5 -6
- package/template/wall-e/lib/runtime-worker-pool.js +1 -0
- package/template/wall-e/lib/scheduler-worker-jobs.js +6 -0
- package/template/wall-e/lib/telemetry-db-schema.js +37 -0
- package/template/wall-e/lib/telemetry-process-client.js +76 -1
- package/template/wall-e/llm/default-fallback.js +4 -0
- package/template/wall-e/llm/model-catalog.js +13 -4
- package/template/wall-e/llm/model-roles.js +6 -0
- package/template/wall-e/llm/portkey-http.js +150 -0
- package/template/wall-e/llm/portkey-sync.js +15 -145
- package/template/wall-e/llm/worker-mcp-routing.js +61 -0
- package/template/wall-e/llm/worker-route.js +43 -1
- package/template/wall-e/loops/brain-custom-writes.js +45 -0
- package/template/wall-e/mcp-protocol-contract.js +41 -1
- package/template/wall-e/mcp-server.js +13 -2
- package/template/wall-e/mcp-worker-schemas.js +20 -2
- package/template/wall-e/memory/ctm-context-client.js +25 -5
- package/template/wall-e/shared/release-schema.json +3 -2
- package/template/wall-e/shared/sqlite-write-lock.js +361 -223
- package/template/wall-e/shared/storage/contract.js +35 -3
- package/template/wall-e/skills/_templates/delegate-worker/VERSION +1 -1
- package/template/wall-e/skills/_templates/delegate-worker/released-sha256.json +6 -0
- package/template/wall-e/skills/_templates/delegate-worker/templates/claude/SKILL.md +7 -2
- package/template/wall-e/skills/_templates/delegate-worker/templates/codex/SKILL.md +7 -2
- package/template/wall-e/tools/local-tools.js +12 -5
- package/template/wall-e/workers/brain-owner-worker.js +9 -0
- package/template/wall-e/workers/coding-journal-writer-child.js +8 -1
- package/template/wall-e/workers/telemetry-worker.js +8 -0
- package/template/coding-task-manager/lib/giant-freeze-park.js +0 -31
|
@@ -4,6 +4,31 @@
|
|
|
4
4
|
})(typeof globalThis !== 'undefined' ? globalThis : this, function() {
|
|
5
5
|
'use strict';
|
|
6
6
|
|
|
7
|
+
// WP-1 (C3, design §6): the same closed recovery vocabulary the server publishes. Send
|
|
8
|
+
// Next reads `lastAttempt.recoveryAction` directly, so this module normalizes
|
|
9
|
+
// independently of the manager: an unrecognized value never grants a fresh id, and a
|
|
10
|
+
// frame with no usable verdict is conservative (review -- restore + explicit confirm).
|
|
11
|
+
var RECOVERY_ACTIONS = new Set(['retry', 'resend', 'review', 'none']);
|
|
12
|
+
|
|
13
|
+
function normalizeRecoveryAction(value, retryable) {
|
|
14
|
+
var clean = typeof value === 'string' ? value.trim().toLowerCase() : '';
|
|
15
|
+
if (RECOVERY_ACTIONS.has(clean)) return clean;
|
|
16
|
+
return retryable === true ? 'retry' : 'review';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// R7 (AC-1e): the recovery policy a queue item's last failure published, so "Send Next" can
|
|
20
|
+
// never silently repeat an identity the server already refused.
|
|
21
|
+
// retry / none -> keep the admitted identity (a fresh id would be a second submission)
|
|
22
|
+
// resend -> certified no-effect recovery: drop the dead identity, one new attempt
|
|
23
|
+
// review -> uncertain: never dispatched, the user confirms first
|
|
24
|
+
// An item with no recorded attempt is an ordinary unsent draft and keeps the historical
|
|
25
|
+
// same-identity behavior.
|
|
26
|
+
function itemRecoveryPolicy(item) {
|
|
27
|
+
var lastAttempt = item && item.lastAttempt;
|
|
28
|
+
if (!lastAttempt) return 'retry';
|
|
29
|
+
return normalizeRecoveryAction(lastAttempt.recoveryAction, lastAttempt.retryable === true);
|
|
30
|
+
}
|
|
31
|
+
|
|
7
32
|
function positiveTimestamp(value, fallback) {
|
|
8
33
|
var timestamp = Number(value);
|
|
9
34
|
if (Number.isFinite(timestamp) && timestamp > 0) return timestamp;
|
|
@@ -34,13 +59,20 @@
|
|
|
34
59
|
var status = String(message && message.status || '').toLowerCase();
|
|
35
60
|
if (status === 'sent') return { status: 'sent', message: '' };
|
|
36
61
|
if (status === 'blocked' || status === 'failed' || status === 'needs_attention') {
|
|
62
|
+
var retryable = !!(message && message.retryable === true);
|
|
63
|
+
var reason = String((message && (message.reason || message.code)) || '');
|
|
37
64
|
return {
|
|
38
65
|
status: 'pending',
|
|
39
66
|
lastAttempt: {
|
|
40
67
|
at: Number(message && message.observedAt) || Date.now(),
|
|
41
68
|
outcome: message && message.status,
|
|
42
69
|
message: String((message && message.message) || ''),
|
|
43
|
-
retryable:
|
|
70
|
+
retryable: retryable,
|
|
71
|
+
// Additive for the recovery lanes: which action the server granted this
|
|
72
|
+
// failure (`recoveryAction`) and why (`reason`). Always derived, never
|
|
73
|
+
// invented from prose, so Send Next has a policy even for a legacy frame.
|
|
74
|
+
recoveryAction: normalizeRecoveryAction(message && message.recoveryAction, retryable),
|
|
75
|
+
...(reason ? { reason: reason } : {}),
|
|
44
76
|
},
|
|
45
77
|
};
|
|
46
78
|
}
|
|
@@ -135,10 +167,151 @@
|
|
|
135
167
|
return params.toString();
|
|
136
168
|
}
|
|
137
169
|
|
|
170
|
+
// R6 (AC-1d/AC-1f): the ONE route from a tray recovery gesture into the queue. It never mints an
|
|
171
|
+
// identity itself: when the panel still owns the exact submission, the owner-fenced resendItem
|
|
172
|
+
// (passed in as `resendItem`) is the only thing allowed to drop the old ID; otherwise the record
|
|
173
|
+
// stays with the manager, whose own rules decide whether a replacement is permitted at all.
|
|
174
|
+
function routeTrayRecovery(action, record, options, deps) {
|
|
175
|
+
deps = deps || {};
|
|
176
|
+
// Captured once, up front: the gesture is fenced by the manager that owned the record when the
|
|
177
|
+
// gesture started, and its own scope guard applies on completion. Never re-read mid-flight.
|
|
178
|
+
var manager = typeof deps.getManager === 'function' ? deps.getManager() : null;
|
|
179
|
+
var clientMessageId = String(record && record.clientMessageId || '');
|
|
180
|
+
if (action !== 'resend' || !manager || !clientMessageId) return Promise.resolve(false);
|
|
181
|
+
// AC-1e: the failure's own verdict decides whether a fresh attempt is permitted at all.
|
|
182
|
+
// `review` is uncertainty -- only this call being the explicit confirmation may proceed --
|
|
183
|
+
// `resend` is the certified no-effect recovery, and retry/none/unknown can never start a
|
|
184
|
+
// fresh send from here (a legacy frame with no verdict normalizes to `review`).
|
|
185
|
+
var policy = normalizeRecoveryAction(record.recoveryAction, record.retryable === true);
|
|
186
|
+
var confirmed = !!(options && options.confirmUncertain === true);
|
|
187
|
+
if (policy === 'review' ? !confirmed : policy !== 'resend') return Promise.resolve(false);
|
|
188
|
+
var sessionId = String(record && record.sessionId || '');
|
|
189
|
+
// Exact session AND exact item only: a tab switch between the gesture and this call must never
|
|
190
|
+
// touch another session's item, and an item that already moved to a newer identity is not this
|
|
191
|
+
// record's item any more.
|
|
192
|
+
var items = sessionId && typeof deps.getSessionId === 'function' && sessionId === deps.getSessionId()
|
|
193
|
+
&& typeof deps.getItems === 'function' && Array.isArray(deps.getItems())
|
|
194
|
+
? deps.getItems() : [];
|
|
195
|
+
var index = items.findIndex(function(item) {
|
|
196
|
+
return item && String(item._submissionClientMessageId || '') === clientMessageId;
|
|
197
|
+
});
|
|
198
|
+
if (index < 0) {
|
|
199
|
+
// A record the queue panel produced belongs to the panel: with no exact current item there is
|
|
200
|
+
// nothing this route may send, and it must NEVER fall back to a manager resend -- that would
|
|
201
|
+
// mint a fresh identity for a prompt the panel still owns (or send it into another session).
|
|
202
|
+
if (String(record.source || '') === 'queue-panel') return Promise.resolve(false);
|
|
203
|
+
var pending = manager.resend(clientMessageId, {
|
|
204
|
+
confirmUncertain: confirmed,
|
|
205
|
+
});
|
|
206
|
+
// resend() refuses outright, or registers its successor synchronously before returning. The
|
|
207
|
+
// original only disappears once that controlled successor really exists, so a refused
|
|
208
|
+
// preflight leaves the preserved text exactly where it was.
|
|
209
|
+
var superseded = Array.from(manager.records.values()).some(function(candidate) {
|
|
210
|
+
return candidate && String(candidate.supersedesClientMessageId || '') === clientMessageId;
|
|
211
|
+
});
|
|
212
|
+
if (!superseded) return pending;
|
|
213
|
+
manager.dismiss(clientMessageId);
|
|
214
|
+
return Promise.resolve(true);
|
|
215
|
+
}
|
|
216
|
+
return Promise.resolve(deps.resendItem(index)).then(function(dispatched) {
|
|
217
|
+
// The stale manager row retires only after the controlled successor has started.
|
|
218
|
+
if (dispatched) manager.dismiss(clientMessageId);
|
|
219
|
+
return dispatched === true;
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// ---------------------------------------------------------------------------
|
|
224
|
+
// R7 (AC-1e, WP-1 fix): the review surface for a queue item whose last attempt is uncertain.
|
|
225
|
+
// "Send Next" used to open the edit box for such an item, which was a dead end: saving unchanged
|
|
226
|
+
// text does not clear lastAttempt/the dead id, so the next Send Next reopened it forever. The
|
|
227
|
+
// queue-owned review below is the explicit confirmation path instead, and it works for a
|
|
228
|
+
// preserved item after a reload -- the manager may hold no record for it at all.
|
|
229
|
+
// ---------------------------------------------------------------------------
|
|
230
|
+
|
|
231
|
+
var REVIEW_WARNING = 'CTM could not confirm what happened to this prompt.';
|
|
232
|
+
|
|
233
|
+
// The first pending item whose failure verdict is `review`, i.e. the first item Send Next must
|
|
234
|
+
// NOT dispatch. Scanned from the pending set so an auto/multi-item queue POST cannot bypass it.
|
|
235
|
+
function pendingReviewItem(items) {
|
|
236
|
+
if (!Array.isArray(items)) return null;
|
|
237
|
+
for (var index = 0; index < items.length; index += 1) {
|
|
238
|
+
var item = items[index];
|
|
239
|
+
if (!item || item._forceResend === true) continue;
|
|
240
|
+
if (!String(item._submissionClientMessageId || '')) continue;
|
|
241
|
+
if (String(item.status || 'pending').toLowerCase() === 'sent') continue;
|
|
242
|
+
if (itemRecoveryPolicy(item) !== 'review') continue;
|
|
243
|
+
return { index: index, item: item };
|
|
244
|
+
}
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// A READ-ONLY snapshot of the exact live item: the prompt the user typed, its attachments and the
|
|
249
|
+
// server's own warning. Nothing is written back onto the item (no review-authority flag survives a
|
|
250
|
+
// reload) and nothing is dispatched from here.
|
|
251
|
+
function buildReviewRecord(item, deps) {
|
|
252
|
+
var lastAttempt = item && item.lastAttempt ? item.lastAttempt : null;
|
|
253
|
+
return {
|
|
254
|
+
clientMessageId: String(item && item._submissionClientMessageId || ''),
|
|
255
|
+
sessionId: deps && typeof deps.getSessionId === 'function' ? String(deps.getSessionId() || '') : '',
|
|
256
|
+
source: 'queue-panel',
|
|
257
|
+
status: 'needs_attention',
|
|
258
|
+
reason: String((lastAttempt && lastAttempt.reason) || 'review_required'),
|
|
259
|
+
message: String((lastAttempt && lastAttempt.message) || REVIEW_WARNING),
|
|
260
|
+
text: String(item && item.text || ''),
|
|
261
|
+
images: item && Array.isArray(item.images) ? item.images.slice() : [],
|
|
262
|
+
recoveryAction: 'review',
|
|
263
|
+
retryable: false,
|
|
264
|
+
reviewOnly: true,
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// The surface's lifetime: the manager identity, the selected session, the exact items array and
|
|
269
|
+
// the exact original item all have to still be the ones the review was opened for. Captured here,
|
|
270
|
+
// never re-read from live state at click time -- and the id is never authority on its own.
|
|
271
|
+
function createReviewGuard(record, item, deps) {
|
|
272
|
+
var manager = typeof deps.getManager === 'function' ? deps.getManager() : null;
|
|
273
|
+
var items = typeof deps.getItems === 'function' ? deps.getItems() : null;
|
|
274
|
+
var sessionId = typeof deps.getSessionId === 'function' ? String(deps.getSessionId() || '') : '';
|
|
275
|
+
var originalId = String((record && record.clientMessageId) || '');
|
|
276
|
+
return function isCurrent(candidate) {
|
|
277
|
+
if (typeof deps.getManager === 'function' && deps.getManager() !== manager) return false;
|
|
278
|
+
if (typeof deps.getSessionId === 'function' && String(deps.getSessionId() || '') !== sessionId) return false;
|
|
279
|
+
if (typeof deps.getItems === 'function' && deps.getItems() !== items) return false;
|
|
280
|
+
if (String((candidate && candidate.clientMessageId) || '') !== originalId) return false;
|
|
281
|
+
if (!Array.isArray(items) || items.indexOf(item) < 0) return false;
|
|
282
|
+
return String(item._submissionClientMessageId || '') === originalId;
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Opens the explicit review surface for the first review-blocked pending item. `deps.renderReview`
|
|
287
|
+
// is the renderer (the tray module owns the DOM) and receives the read-only record plus the
|
|
288
|
+
// lifetime predicate above; its confirmation travels back through routeTrayRecovery, so the only
|
|
289
|
+
// thing that can drop the dead identity is the existing owner-fenced resendItem.
|
|
290
|
+
function openItemReview(items, deps) {
|
|
291
|
+
deps = deps || {};
|
|
292
|
+
var candidate = pendingReviewItem(items);
|
|
293
|
+
if (!candidate) return false;
|
|
294
|
+
var record = buildReviewRecord(candidate.item, deps);
|
|
295
|
+
var isCurrent = createReviewGuard(record, candidate.item, deps);
|
|
296
|
+
if (!isCurrent(record)) return false;
|
|
297
|
+
if (typeof deps.notify === 'function') {
|
|
298
|
+
deps.notify(String((candidate.item.lastAttempt && candidate.item.lastAttempt.message) || ''));
|
|
299
|
+
}
|
|
300
|
+
if (typeof deps.renderReview === 'function') deps.renderReview(record, isCurrent);
|
|
301
|
+
return true;
|
|
302
|
+
}
|
|
303
|
+
|
|
138
304
|
return {
|
|
139
305
|
buildSubmissionLookupQuery: buildSubmissionLookupQuery,
|
|
306
|
+
buildReviewRecord: buildReviewRecord,
|
|
307
|
+
createReviewGuard: createReviewGuard,
|
|
308
|
+
itemRecoveryPolicy: itemRecoveryPolicy,
|
|
140
309
|
markItemSentById: markItemSentById,
|
|
310
|
+
normalizeRecoveryAction: normalizeRecoveryAction,
|
|
311
|
+
openItemReview: openItemReview,
|
|
312
|
+
pendingReviewItem: pendingReviewItem,
|
|
141
313
|
reconcileSubmissionRows: reconcileSubmissionRows,
|
|
142
314
|
reconcileSubmissionStatus: reconcileSubmissionStatus,
|
|
315
|
+
routeTrayRecovery: routeTrayRecovery,
|
|
143
316
|
};
|
|
144
317
|
});
|
|
@@ -859,6 +859,8 @@ CR.openSessionForProject = function() {
|
|
|
859
859
|
args: [],
|
|
860
860
|
cwd: projectPath,
|
|
861
861
|
label: projectPath.replace(/^.*\//, '') + ' (review)',
|
|
862
|
+
// D-13: a review session's name is composed here, so it is a default (tier 3), not a rename.
|
|
863
|
+
titleIntent: 'default',
|
|
862
864
|
});
|
|
863
865
|
if (typeof window.toast === 'function') {
|
|
864
866
|
window.toast('Starting Claude session at ' + projectPath.replace(/^.*\//, ''), { type: 'success' });
|
|
@@ -1425,9 +1427,10 @@ function _crumbDir(relPath) {
|
|
|
1425
1427
|
|
|
1426
1428
|
// R-5: the review chat is no longer a sidebar — it is the Wall-E chat's review
|
|
1427
1429
|
// thread (`review-<ctmSessionId>`). One button opens it on the WALL-E tab; the
|
|
1428
|
-
// server ensures the thread row on the first message.
|
|
1430
|
+
// server ensures the thread row on the first message. The label is its own span
|
|
1431
|
+
// so the one-line doc header can show the icon alone (reviews.css).
|
|
1429
1432
|
function _chatToggleHTML() {
|
|
1430
|
-
return `<button class="cr-chat-toggle" onclick="CR.toggleChat()" title="Discuss this review in the Wall-E chat (Cmd+Shift+C)" id="cr-chat-toggle-btn">💬
|
|
1433
|
+
return `<button class="cr-chat-toggle" onclick="CR.toggleChat()" title="Discuss this review in the Wall-E chat (Cmd+Shift+C)" aria-label="Discuss in Wall-E chat" id="cr-chat-toggle-btn">💬<span class="cr-chat-toggle-label"> Discuss in Wall-E chat</span></button>`;
|
|
1431
1434
|
}
|
|
1432
1435
|
|
|
1433
1436
|
function renderDocHeader() {
|
|
@@ -3013,7 +3016,8 @@ function _createSessionAndSendReview(prompt) {
|
|
|
3013
3016
|
window.toast('Creating new Claude session...', { type: 'info' });
|
|
3014
3017
|
}
|
|
3015
3018
|
|
|
3016
|
-
|
|
3019
|
+
// D-13: `Code Review:`/`Doc Review:` is CTM-composed — mark it default so it lands tier 3.
|
|
3020
|
+
window.send({ type: 'create', id: sessionId, cmd: 'claude', args: [], cwd, label: label.slice(0, 60), titleIntent: 'default' });
|
|
3017
3021
|
|
|
3018
3022
|
setTimeout(() => {
|
|
3019
3023
|
if (_pendingReviewSend?.sessionId === sessionId) {
|
|
@@ -14,6 +14,10 @@
|
|
|
14
14
|
const requests = new WeakMap();
|
|
15
15
|
const contexts = new WeakMap();
|
|
16
16
|
const imageUploads = new WeakMap();
|
|
17
|
+
const unmatchedPublications = new WeakMap();
|
|
18
|
+
// Bounded like every other retained-evidence store here: newest version per durable attachment
|
|
19
|
+
// id, oldest entry evicted past the cap. A session has at most a handful of drafts in flight.
|
|
20
|
+
const UNMATCHED_PUBLICATION_LIMIT = 16;
|
|
17
21
|
const activeById = new Map();
|
|
18
22
|
|
|
19
23
|
// Same rule as Draft.stateVersionOf (§6: a lifecycle integer is never coerced from a
|
|
@@ -92,6 +96,11 @@
|
|
|
92
96
|
for (const reservation of [...uploads.values()]) disposeImageUpload(reservation);
|
|
93
97
|
uploads.clear();
|
|
94
98
|
}
|
|
99
|
+
// Same rule for a publication retained for an attachment this context had no row for: an
|
|
100
|
+
// in-app retirement is revocation, not a licence to keep applying server truth to a dead
|
|
101
|
+
// draft (the map is keyed by the context, so this also drops the last reference early).
|
|
102
|
+
const retainedUnmatched = unmatchedPublications.get(context);
|
|
103
|
+
if (retainedUnmatched) retainedUnmatched.clear();
|
|
95
104
|
const attachments = context.draft && Array.isArray(context.draft.attachments)
|
|
96
105
|
? context.draft.attachments : [];
|
|
97
106
|
for (const attachment of attachments) {
|
|
@@ -269,6 +278,89 @@
|
|
|
269
278
|
return true;
|
|
270
279
|
}
|
|
271
280
|
|
|
281
|
+
function unmatchedPublicationsOf(context) {
|
|
282
|
+
let map = unmatchedPublications.get(context);
|
|
283
|
+
if (!map) { map = new Map(); unmatchedPublications.set(context, map); }
|
|
284
|
+
return map;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// A client that has never seen this draft before -- the normal shape after a page reload, or a
|
|
288
|
+
// second tab opened on a session with a durable draft -- learns its rows from the restore LIST,
|
|
289
|
+
// but the server starts publishing the moment the row commits. A publication that arrives
|
|
290
|
+
// before that list has been applied therefore has NO row to join (`onTerminalImageDraftState`'s
|
|
291
|
+
// correlation finds nothing), and dropping it there left the restored chip frozen on the list's
|
|
292
|
+
// OLDER state -- typically `delivery_pending` -> 'attaching' -- while the terminal publication
|
|
293
|
+
// had in fact already arrived, so the chip showed "Attached ✓" (from the local client hint) over
|
|
294
|
+
// `data-status="attaching"` forever. Retain it instead, fenced by the CAPTURED context (session)
|
|
295
|
+
// and the publication's own durable attachment id, and consume it exactly once from the restore
|
|
296
|
+
// that creates the row.
|
|
297
|
+
//
|
|
298
|
+
// The list stays the EXISTENCE authority: an attachment the server never lists never gains a
|
|
299
|
+
// row, so a retained publication can never resurrect a chip the user dismissed or one that was
|
|
300
|
+
// retired by a submitted snapshot. Identity is validated here with what is honestly known
|
|
301
|
+
// without a row (session + durable id); the restore re-validates the full identity (owner,
|
|
302
|
+
// and for a local row its request id) before adopting.
|
|
303
|
+
function retainUnmatchedPublication(context, snapshot, hint) {
|
|
304
|
+
if (!current(context) || !snapshot || typeof snapshot !== 'object') return false;
|
|
305
|
+
const version = lifecycleVersion(snapshot.stateVersion);
|
|
306
|
+
if (version === null || !isCanonicalState(String(snapshot.state || ''))) return false;
|
|
307
|
+
const attachmentId = String(snapshot.attachmentId || '');
|
|
308
|
+
if (!attachmentId) return false;
|
|
309
|
+
if (!DraftApi || !DraftApi.validateServerSnapshot(snapshot, {
|
|
310
|
+
sessionId: context.id, attachmentId, ownerHostId: String(snapshot.ownerHostId || ''),
|
|
311
|
+
}).ok) return false;
|
|
312
|
+
const map = unmatchedPublicationsOf(context);
|
|
313
|
+
const retained = map.get(attachmentId);
|
|
314
|
+
if (retained && version < retained.version) return true;
|
|
315
|
+
if (retained && version === retained.version
|
|
316
|
+
&& DraftApi.canonicalSnapshotsEqual(retained.snapshot, snapshot)) return true;
|
|
317
|
+
const copy = {};
|
|
318
|
+
for (const key of Object.keys(snapshot)) {
|
|
319
|
+
if (key === 'hint' || key === 'type' || key === 'timestamp') continue;
|
|
320
|
+
copy[key] = snapshot[key];
|
|
321
|
+
}
|
|
322
|
+
map.delete(attachmentId);
|
|
323
|
+
map.set(attachmentId, {
|
|
324
|
+
snapshot: copy,
|
|
325
|
+
hint: _retainedHint(null, hint, version, String(snapshot.deliveryAttemptId || '')),
|
|
326
|
+
version,
|
|
327
|
+
});
|
|
328
|
+
while (map.size > UNMATCHED_PUBLICATION_LIMIT) {
|
|
329
|
+
const oldest = map.keys().next();
|
|
330
|
+
if (oldest.done) break;
|
|
331
|
+
map.delete(oldest.value);
|
|
332
|
+
}
|
|
333
|
+
return true;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Read-only observability for the bounded-work contract: never allocates, reports 0 for an
|
|
337
|
+
// unknown/retired context.
|
|
338
|
+
function unmatchedPublicationCount(context) {
|
|
339
|
+
if (!current(context)) return 0;
|
|
340
|
+
const map = unmatchedPublications.get(context);
|
|
341
|
+
return map ? map.size : 0;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// The consuming half, called from `mergeServerSnapshot` once the restore list has given the
|
|
345
|
+
// attachment a row: the retained publication is newer server truth for that durable id, so it
|
|
346
|
+
// is ADOPTED (never a local transition) under the restore's own identity envelope. Consumed
|
|
347
|
+
// exactly once -- the entry is dropped whether or not it was newer than the list row -- so a
|
|
348
|
+
// later restore cannot replay it. Returns whether the row changed.
|
|
349
|
+
function applyUnmatchedPublication(context, attachment, identity) {
|
|
350
|
+
if (!current(context) || !attachment) return false;
|
|
351
|
+
const attachmentId = String(attachment.attachmentId || '');
|
|
352
|
+
if (!attachmentId) return false;
|
|
353
|
+
const map = unmatchedPublications.get(context);
|
|
354
|
+
const retained = map && map.get(attachmentId);
|
|
355
|
+
if (!retained) return false;
|
|
356
|
+
map.delete(attachmentId);
|
|
357
|
+
const rowVersion = DraftApi && typeof DraftApi.stateVersionOf === 'function'
|
|
358
|
+
? DraftApi.stateVersionOf(attachment.stateVersion) : null;
|
|
359
|
+
if (rowVersion !== null && retained.version <= rowVersion) return false;
|
|
360
|
+
return DraftApi.adoptServerSnapshot(context.draft, attachment.clientId, retained.snapshot,
|
|
361
|
+
retained.hint, { ...identity, attachmentId }) === true;
|
|
362
|
+
}
|
|
363
|
+
|
|
272
364
|
// §6/AC-3 + review finding 1: choose the newest VALID canonical snapshot among the verified
|
|
273
365
|
// HTTP metadata, the retained publication and the already-adopted watermark; bind the verified
|
|
274
366
|
// durable ID and adopt atomically. Outcomes (callers never guess whether a rejection is the
|
|
@@ -395,6 +487,43 @@
|
|
|
395
487
|
throw lastError || new Error('Request failed');
|
|
396
488
|
}
|
|
397
489
|
|
|
490
|
+
// A DELETE is owned by its captured root. Aborting it does not undo server-side work.
|
|
491
|
+
async function deleteServerDraft({ id, attachment, options = {}, context, token, retry, isCurrent }) {
|
|
492
|
+
const live = () => Boolean(context) && contexts.get(context.draft) === context
|
|
493
|
+
&& current(context) && context.id === id && (typeof isCurrent !== 'function' || isCurrent() === true);
|
|
494
|
+
if (!live()) return false;
|
|
495
|
+
const clientId = String(attachment?.clientId || '');
|
|
496
|
+
const attachmentId = String(attachment?.attachmentId || '');
|
|
497
|
+
const etag = String(attachment?.etag || '');
|
|
498
|
+
const base = '/api/sessions/' + encodeURIComponent(id) + '/image-drafts';
|
|
499
|
+
const requestToken = encodeURIComponent(String(token || ''));
|
|
500
|
+
let url;
|
|
501
|
+
let headers;
|
|
502
|
+
if (!attachmentId) {
|
|
503
|
+
if (!options.cancelRequest || !clientId) return true;
|
|
504
|
+
url = base + '?request_id=' + encodeURIComponent(clientId) + '&purge=1&token=' + requestToken;
|
|
505
|
+
} else {
|
|
506
|
+
url = base + '/' + encodeURIComponent(attachmentId) + '?token=' + requestToken
|
|
507
|
+
+ (options.purge === true ? '&purge=1' : '');
|
|
508
|
+
headers = etag ? { 'If-Match': etag } : {};
|
|
509
|
+
}
|
|
510
|
+
if (typeof retry !== 'function') return false;
|
|
511
|
+
try {
|
|
512
|
+
const result = await request(context, async signal => {
|
|
513
|
+
if (!live()) return false;
|
|
514
|
+
const response = await retry(url, { method: 'DELETE', ...(headers ? { headers } : {}), signal },
|
|
515
|
+
{ attempts: 3, isCurrent: live });
|
|
516
|
+
if (!response || !live()) return false;
|
|
517
|
+
if (!response.ok && response.status !== 404) throw new Error('HTTP ' + response.status);
|
|
518
|
+
return true;
|
|
519
|
+
});
|
|
520
|
+
return result === true && live();
|
|
521
|
+
} catch (error) {
|
|
522
|
+
if (!live()) return false;
|
|
523
|
+
throw error;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
398
527
|
async function completeDelivery({ id, attachment, attemptId, outcome, reason, providerOrdinal, diagnostics,
|
|
399
528
|
context, token, getToken, fetchFn, retry }) {
|
|
400
529
|
const isCurrent = () => attachmentCurrent(context, id, context && context.draft, attachment);
|
|
@@ -528,6 +657,10 @@
|
|
|
528
657
|
Draft.adoptServerSnapshot(draft, existing.clientId, item, null, identity);
|
|
529
658
|
if (existing._serverSnapshotInvalid || existing._serverSnapshotConflict
|
|
530
659
|
|| existing._serverIdentityMismatch || existing._serverSnapshotStale !== undefined) continue;
|
|
660
|
+
// B9: a publication that arrived before this restore gave the attachment a row is
|
|
661
|
+
// NEWER server truth for the same durable id, so it is adopted before the row is
|
|
662
|
+
// decorated/published rather than being lost to the ordering.
|
|
663
|
+
applyUnmatchedPublication(context, existing, identity);
|
|
531
664
|
const rawPreview = existing.canonicalPreviewUrl;
|
|
532
665
|
if (typeof rawPreview === 'string') existing.previewUrl = previewUrlFor({ previewUrl: rawPreview });
|
|
533
666
|
Draft.reactivateWriteProof(existing, { ...identity, deliveryAttemptId: String(existing.deliveryAttemptId || '') });
|
|
@@ -541,6 +674,9 @@
|
|
|
541
674
|
Draft.removeAttachment(draft, added.clientId);
|
|
542
675
|
continue;
|
|
543
676
|
}
|
|
677
|
+
// The restore is the ONLY writer of a `server_` row, and the row now exists: this is
|
|
678
|
+
// where a publication retained while it was missing is consumed (B9).
|
|
679
|
+
applyUnmatchedPublication(context, added, identity);
|
|
544
680
|
added.previewUrl = previewUrlFor({ previewUrl: added.canonicalPreviewUrl });
|
|
545
681
|
byServerId.set(serverId, added);
|
|
546
682
|
if (added.status === 'ready' && item.state === 'ready') attachAfterRestore.push(added);
|
|
@@ -595,9 +731,10 @@
|
|
|
595
731
|
}
|
|
596
732
|
|
|
597
733
|
return Object.freeze({ capture, createDraft, current, attachmentCurrent, retire: dispose, request,
|
|
598
|
-
fetchWithRetry, completeDelivery, readDraftImage, schedule, clear, mergeServerSnapshot,
|
|
734
|
+
fetchWithRetry, completeDelivery, readDraftImage, schedule, clear, mergeServerSnapshot, deleteServerDraft,
|
|
599
735
|
registerRequest, releaseRequest, reserveImageUpload, imageUploadReservation,
|
|
600
|
-
retainImagePublication, finishImageUpload, resetImageHints, disposeImageUpload, imageUploadCount
|
|
736
|
+
retainImagePublication, finishImageUpload, resetImageHints, disposeImageUpload, imageUploadCount,
|
|
737
|
+
retainUnmatchedPublication, applyUnmatchedPublication, unmatchedPublicationCount });
|
|
601
738
|
}
|
|
602
739
|
|
|
603
740
|
return Object.freeze({ createRootDraftLifetime });
|
|
@@ -17,6 +17,14 @@
|
|
|
17
17
|
const leaseRegistries = new WeakMap();
|
|
18
18
|
const cleanupFailures = [];
|
|
19
19
|
let droppedCleanupFailures = 0;
|
|
20
|
+
// WP-1 (C4, AC-1d): exactly ONE replacement gesture per session at a time. Both the
|
|
21
|
+
// row's ↻ and the tray's recovery reach resendItem, and a double click runs it twice
|
|
22
|
+
// before either await returns. The second run must be refused HERE -- before the
|
|
23
|
+
// async verify and before the ID deletion below -- because sendQueueFromPanel's
|
|
24
|
+
// _qpManualSendInFlight is only consulted after the old identity is already gone, so
|
|
25
|
+
// it cannot stop the second gesture from minting a second replacement. A lock whose
|
|
26
|
+
// owner has retired or gone stale is reclaimed instead of blocking forever.
|
|
27
|
+
let resendGesture = null;
|
|
20
28
|
|
|
21
29
|
function rememberCleanupFailure(phase) {
|
|
22
30
|
if (cleanupFailures.length < 8) cleanupFailures.push(phase);
|
|
@@ -482,6 +490,13 @@
|
|
|
482
490
|
});
|
|
483
491
|
if (!captured) return false;
|
|
484
492
|
const { view, action } = captured;
|
|
493
|
+
if (resendGesture && resendGesture.sessionId === sessionId && current(resendGesture.action)) {
|
|
494
|
+
finish(action);
|
|
495
|
+
finish(view);
|
|
496
|
+
return false;
|
|
497
|
+
}
|
|
498
|
+
const gesture = { sessionId, action };
|
|
499
|
+
resendGesture = gesture;
|
|
485
500
|
try {
|
|
486
501
|
if (!await verify(action) || !current(action) || action.loadingAtClick === true
|
|
487
502
|
|| action.visibleSessionAtClick !== sessionId || !Array.isArray(action.items)
|
|
@@ -500,6 +515,7 @@
|
|
|
500
515
|
if (current(action) && dependencies.getVisibleSession?.() === sessionId) dependencies.onError?.();
|
|
501
516
|
return false;
|
|
502
517
|
} finally {
|
|
518
|
+
if (resendGesture === gesture) resendGesture = null;
|
|
503
519
|
finish(action);
|
|
504
520
|
finish(view);
|
|
505
521
|
}
|
|
@@ -24,12 +24,14 @@
|
|
|
24
24
|
// first row in the transcript, never for what gets painted. R-11: a turn-end STATUS row (`✻ Worked for 18s · done 4:35 PM`)
|
|
25
25
|
// carries NO words at all -- its verb is one Claude picks at random (`Worked`, `Brewed`, ...) and its elapsed seconds are the
|
|
26
26
|
// terminal's own clock (observed live `16s` against the transcript's `17s`), so the transcript cannot hold them; only a row
|
|
27
|
-
// that is ENTIRELY that shape is dropped, and every other row keeps its words.
|
|
27
|
+
// that is ENTIRELY that shape is dropped, and every other row keeps its words. A blockquote's bar is decoration too: Claude's
|
|
28
|
+
// live screen draws it `▎` (U+258E) where the transcript's own rendering draws `│` (observed 2026-10-24: a kept `▎` made the
|
|
29
|
+
// quoted rows owe a word the transcript never holds, so the proof refused and 21 live rows stayed duplicated as an overlap).
|
|
28
30
|
function cutWordsOf(text) {
|
|
29
31
|
const plain = stripForCompare(text);
|
|
30
32
|
if (/^\s*[^\s\p{L}\p{N}]\s+\p{L}+\s+for\s+(?:\d+[hms]\s*)+(?:·\s*done\s+\d{1,2}:\d{2}\s*[AP]M)?\s*$/u.test(plain)) return [];
|
|
31
33
|
return plain
|
|
32
|
-
.replace(/[|\u2500\u2501\u2502\u250c\u2510\u2514\u2518\u251c\u2524\u252c\u2534\u253c\u2550\u2026]/g, ' ')
|
|
34
|
+
.replace(/[|\u2500\u2501\u2502\u250c\u2510\u2514\u2518\u251c\u2524\u252c\u2534\u253c\u2550\u2026\u258e]/g, ' ')
|
|
33
35
|
.split(/\s+/)
|
|
34
36
|
.flatMap((token) => token.split(/(?<=[\p{L}\p{N}]-)(?=[\p{L}\p{N}])/u))
|
|
35
37
|
.filter(Boolean);
|
|
@@ -40,6 +42,17 @@
|
|
|
40
42
|
// (R-1), and a prose row such as "Updated 2 tests" must keep owing its words there (R-3).
|
|
41
43
|
const LIVE_CHROME = /^\s*(?:⎿|✻\s|─{4,}|·\s*task notification|⏺\s+(?:MCP task\b|Background command\b|Called\b|Ran\b|Searched\b|Listed\b|Read\b.*\(\d+ lines\)|Edited\b|Wrote\b)|(?:Ran|Searched for|Listed|Called|Read|Fetched|Wrote|Added|Updated)\b[^|]*\b\d|Referenced file\b|Skills restored\b|Compacted\b)/u;
|
|
42
44
|
function isLiveChrome(row) { return LIVE_CHROME.test(stripForCompare(row)); }
|
|
45
|
+
// seam-proof-r3 R-3 (WP-3): Codex's PINNED PROMPT HEADER, the one presentation-only row the TUI draws at the very top of the
|
|
46
|
+
// transcript view when the user's prompt itself has scrolled above the viewport -- `› ` (U+203A, a space) then that prompt,
|
|
47
|
+
// whitespace-collapsed and truncated with `…` (codex-rs/tui/src/transcript_view/prompt_header.rs: "The header is never transcript
|
|
48
|
+
// content, so copying, searching and exporting stay exact"; the TUI pins exactly one row, at the top, and only then). CTM's
|
|
49
|
+
// recorder infers pins from motion and can report `ref.pinned` 0 on such a screen, so the client's slice keeps the row and the
|
|
50
|
+
// join would take it for the live screen's first CONTENT row: it then owes the prompt's words, its coverage walk lands on the
|
|
51
|
+
// transcript's own prompt row, and the loss guard refuses over every row the screen between them never drew (field incident
|
|
52
|
+
// f04c2864, 2026-10-19: none/loss, five rows drawn twice). Live row 0 matching this shape is therefore treated as a BLANK row --
|
|
53
|
+
// index 0 only, live side only, transcript rows untouched -- and a wordless row can never hide content (R-2). Only U+203A marks
|
|
54
|
+
// the header: Claude draws `❯`, so a Claude screen is unaffected, and every other row beginning `› ` keeps its words.
|
|
55
|
+
const CODEX_PROMPT_HEADER = /^› /u;
|
|
43
56
|
// Where the live screen's rows begin in the transcript, by WORDS (R-5..R-7): T = stripped transcript rows, firstT = the twin's
|
|
44
57
|
// transcript index (the live screen's row `liveRow`), liveTop = the stripped live rows [0, liveRow). N = the multiset of the live
|
|
45
58
|
// rows' cut words; walking up from firstT - 1, r0 is the first row whose words (with everything below it) cover N. A row between
|
|
@@ -170,6 +183,7 @@
|
|
|
170
183
|
const paintedRows = ((painted && painted.rows) || []).map(stripForCompare);
|
|
171
184
|
const from = painted ? Number(painted.from) || 0 : 0;
|
|
172
185
|
const live = (liveRows || []).map(stripForCompare);
|
|
186
|
+
if (live.length && CODEX_PROMPT_HEADER.test(live[0])) live[0] = ''; // R-3: Codex's pinned prompt header is presentation-only, never content
|
|
173
187
|
const P = paintedRows.concat(live);
|
|
174
188
|
const top = from + paintedRows.length;
|
|
175
189
|
const allowance = Number.isSafeInteger(appBelow) && appBelow > 0 ? appBelow : 0; // R-F2/R-F5: only a positive measured count opens the window
|
|
@@ -407,6 +421,7 @@
|
|
|
407
421
|
function landingDiag(transcriptRows, liveRows, transcriptKinds) {
|
|
408
422
|
const T = (transcriptRows || []).map(stripForCompare);
|
|
409
423
|
const live = (liveRows || []).map(stripForCompare);
|
|
424
|
+
if (live.length && CODEX_PROMPT_HEADER.test(live[0])) live[0] = ''; // R-3: the same header row is no `dup` and no known row (S-8)
|
|
410
425
|
const kinds = Array.isArray(transcriptKinds) ? transcriptKinds : null;
|
|
411
426
|
const isE = (i) => !kinds || kinds[i] === 'text' || kinds[i] === 'prompt';
|
|
412
427
|
const TW = T.map((row, i) => (isE(i) ? cutWordsOf(row) : []));
|
|
@@ -440,5 +455,5 @@
|
|
|
440
455
|
return { dup, blk: classes.join(','), why: w ? 'cut' : why.rule, lossAt: loss ? T.length - why.at : null, lossCls: loss ? diagClass(T[why.at]) : '' };
|
|
441
456
|
}
|
|
442
457
|
|
|
443
|
-
return Object.freeze({ RUN_ROWS, RUN_NONBLANK, RUN_MIN_CHARS, PARAGRAPH_KEY_MIN_CHARS, stripForCompare, runKeys, findJoin, anchorChain, cutWordsOf, wordCut, tailWordCut, landingDiag, LIVE_CHROME, isLiveChrome });
|
|
458
|
+
return Object.freeze({ RUN_ROWS, RUN_NONBLANK, RUN_MIN_CHARS, PARAGRAPH_KEY_MIN_CHARS, stripForCompare, runKeys, findJoin, anchorChain, cutWordsOf, wordCut, tailWordCut, landingDiag, LIVE_CHROME, isLiveChrome, CODEX_PROMPT_HEADER });
|
|
444
459
|
});
|
|
@@ -801,6 +801,7 @@
|
|
|
801
801
|
}
|
|
802
802
|
|
|
803
803
|
function leadNote(agent) {
|
|
804
|
+
if (agent === 'claude') return 'Same Claude session: your history continues here, and the next completed turn confirms this model.';
|
|
804
805
|
return 'History moves only after source and target compatibility is verified. A native fork uses a new provider thread; the next completed root turn confirms the target.';
|
|
805
806
|
}
|
|
806
807
|
|
|
@@ -1497,7 +1498,6 @@
|
|
|
1497
1498
|
if (typeof row.data.context_window === 'number' && row.data.context_window > 0) {
|
|
1498
1499
|
pane.appendChild(textLine('walle-model-effort-note', 'Context window: ' + row.data.context_window.toLocaleString('en-US') + ' tokens'));
|
|
1499
1500
|
}
|
|
1500
|
-
pane.appendChild(textLine('walle-model-effort-note', 'History is carried forward only after source and target compatibility is verified. A native fork uses a new provider thread.'));
|
|
1501
1501
|
// R-6: the same Route line the Delegate pane has — the routed row's route is choosable.
|
|
1502
1502
|
appendRouteLine(pane, row.data, { onRoutes: lead && lead.onRoutes });
|
|
1503
1503
|
} else {
|
|
@@ -105,6 +105,18 @@
|
|
|
105
105
|
return normalizedValue ? normalizedValue.split(/\s+/).filter(Boolean) : [];
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
+
// D-1/D-3 of the separator-ranking design (docs/session-search-separator-
|
|
109
|
+
// ranking-2026-10-24.html §6): slash and backslash are component boundaries
|
|
110
|
+
// for the non-exact evidence grades, so "resume/hung" contributes whole-word
|
|
111
|
+
// evidence for "resume" instead of one stem-exempt token. Hyphens,
|
|
112
|
+
// underscores, dots and digits stay inside a component. Contract: ordered
|
|
113
|
+
// string[], duplicates and order retained, no empty strings. The raw
|
|
114
|
+
// whitespace representation above is deliberately NOT changed -- it still
|
|
115
|
+
// decides the exact / stem-exact / legacy-command branches.
|
|
116
|
+
function titleComponentTokens(normalizedValue) {
|
|
117
|
+
return String(normalizedValue || '').split(/[\s/\\]+/u).filter(Boolean);
|
|
118
|
+
}
|
|
119
|
+
|
|
108
120
|
const TITLE_GRADE_NONE_RESULT = Object.freeze({
|
|
109
121
|
grade: TITLE_GRADE.NONE,
|
|
110
122
|
rank: TITLE_GRADE_RANK.none,
|
|
@@ -171,48 +183,63 @@
|
|
|
171
183
|
};
|
|
172
184
|
}
|
|
173
185
|
|
|
186
|
+
// D-1/D-3: only after both raw equality branches have failed do the
|
|
187
|
+
// evidence grades run, and they run on separator components (the same
|
|
188
|
+
// helper for title and query) rather than on the raw whitespace tokens.
|
|
189
|
+
// Component arrays keep repetition and order; the score denominator is the
|
|
190
|
+
// component count only, never raw tokens plus components.
|
|
191
|
+
const titleComponents = titleComponentTokens(normalizedTitle);
|
|
192
|
+
const queryComponents = titleComponentTokens(normalizedQuery);
|
|
193
|
+
if (!titleComponents.length || !queryComponents.length) return TITLE_GRADE_NONE_RESULT;
|
|
194
|
+
|
|
195
|
+
const componentCount = titleComponents.length;
|
|
196
|
+
const queryComponentCount = queryComponents.length;
|
|
197
|
+
const titleComponentForms = titleComponents.map(titleTokenForm);
|
|
198
|
+
const queryComponentForms = queryComponents.map(titleTokenForm);
|
|
199
|
+
|
|
174
200
|
const minPrefixLen = TITLE_SCORE_WEIGHTS.MIN_PREFIX_LEN;
|
|
175
|
-
const wordHit = (queryForm) =>
|
|
176
|
-
const prefixHit = (queryTok) => queryTok.length >= minPrefixLen
|
|
177
|
-
|
|
201
|
+
const wordHit = (queryForm) => titleComponentForms.includes(queryForm);
|
|
202
|
+
const prefixHit = (queryTok) => queryTok.length >= minPrefixLen
|
|
203
|
+
&& titleComponents.some(t => t.startsWith(queryTok));
|
|
204
|
+
const containsHit = (queryTok) => titleComponents.some(t => t.includes(queryTok));
|
|
178
205
|
|
|
179
|
-
const wordAll =
|
|
180
|
-
const prefixAll = !wordAll &&
|
|
181
|
-
const containsAll = !wordAll && !prefixAll &&
|
|
206
|
+
const wordAll = queryComponentForms.every(wordHit);
|
|
207
|
+
const prefixAll = !wordAll && queryComponents.every(prefixHit);
|
|
208
|
+
const containsAll = !wordAll && !prefixAll && queryComponents.every(containsHit);
|
|
182
209
|
|
|
183
210
|
let grade;
|
|
184
211
|
let matchTest;
|
|
185
212
|
if (wordAll) {
|
|
186
213
|
grade = TITLE_GRADE.WORD;
|
|
187
|
-
matchTest = (token, form) =>
|
|
214
|
+
matchTest = (token, form) => queryComponentForms.includes(form);
|
|
188
215
|
} else if (prefixAll) {
|
|
189
216
|
grade = TITLE_GRADE.PREFIX;
|
|
190
|
-
matchTest = (token) =>
|
|
217
|
+
matchTest = (token) => queryComponents.some(q => q.length >= minPrefixLen && token.startsWith(q));
|
|
191
218
|
} else if (containsAll) {
|
|
192
219
|
grade = TITLE_GRADE.CONTAINS;
|
|
193
|
-
matchTest = (token) =>
|
|
220
|
+
matchTest = (token) => queryComponents.some(q => token.includes(q));
|
|
194
221
|
} else {
|
|
195
|
-
const anyPartial =
|
|
196
|
-
wordHit(
|
|
222
|
+
const anyPartial = queryComponents.some((queryTok, index) => (
|
|
223
|
+
wordHit(queryComponentForms[index]) || prefixHit(queryTok) || containsHit(queryTok)
|
|
197
224
|
));
|
|
198
225
|
grade = anyPartial ? TITLE_GRADE.PARTIAL : TITLE_GRADE.NONE;
|
|
199
|
-
matchTest = (token, form) =>
|
|
200
|
-
||
|
|
226
|
+
matchTest = (token, form) => queryComponentForms.includes(form)
|
|
227
|
+
|| queryComponents.some(q => (q.length >= minPrefixLen && token.startsWith(q)) || token.includes(q));
|
|
201
228
|
}
|
|
202
229
|
|
|
203
230
|
let matchedTitleTokens = 0;
|
|
204
231
|
let firstMatchedIndex = -1;
|
|
205
232
|
if (grade !== TITLE_GRADE.NONE) {
|
|
206
|
-
|
|
207
|
-
if (matchTest(token,
|
|
233
|
+
titleComponents.forEach((token, index) => {
|
|
234
|
+
if (matchTest(token, titleComponentForms[index])) {
|
|
208
235
|
matchedTitleTokens += 1;
|
|
209
236
|
if (firstMatchedIndex === -1) firstMatchedIndex = index;
|
|
210
237
|
}
|
|
211
238
|
});
|
|
212
239
|
}
|
|
213
240
|
|
|
214
|
-
const coverage =
|
|
215
|
-
const position = firstMatchedIndex === -1 ? 0 : 1 - (firstMatchedIndex /
|
|
241
|
+
const coverage = componentCount ? matchedTitleTokens / componentCount : 0;
|
|
242
|
+
const position = firstMatchedIndex === -1 ? 0 : 1 - (firstMatchedIndex / componentCount);
|
|
216
243
|
const score = TITLE_GRADE_BASE[grade] + TITLE_SCORE_WEIGHTS.COVERAGE * coverage + TITLE_SCORE_WEIGHTS.POSITION * position;
|
|
217
244
|
|
|
218
245
|
return {
|
|
@@ -222,8 +249,8 @@
|
|
|
222
249
|
position,
|
|
223
250
|
score,
|
|
224
251
|
matchedTitleTokens,
|
|
225
|
-
titleTokens:
|
|
226
|
-
queryTokens:
|
|
252
|
+
titleTokens: componentCount,
|
|
253
|
+
queryTokens: queryComponentCount,
|
|
227
254
|
};
|
|
228
255
|
}
|
|
229
256
|
|