@reticlehq/server 2.11.0 → 2.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SKILL.md +93 -111
- package/dist/bridge/auth-failure-reason.d.ts +6 -2
- package/dist/bridge/auth-failure-reason.js +15 -8
- package/dist/bridge/bridge.d.ts +18 -14
- package/dist/bridge/bridge.js +248 -165
- package/dist/bridge/bridge.test-harness.d.ts +2 -0
- package/dist/bridge/bridge.test-harness.js +8 -1
- package/dist/bridge/pairing-token.d.ts +5 -0
- package/dist/bridge/pairing-token.js +28 -0
- package/dist/bridge/protocol-skew.d.ts +37 -0
- package/dist/bridge/protocol-skew.js +45 -0
- package/dist/capsule/capsule-store.d.ts +8 -7
- package/dist/capsule/capsule-store.js +1 -1
- package/dist/capsule/causal-summary.d.ts +2 -2
- package/dist/capsule/causal-summary.js +7 -2
- package/dist/chromium-launch-options.d.ts +28 -0
- package/dist/chromium-launch-options.js +29 -0
- package/dist/cli/cli-doctor.js +39 -1
- package/dist/cli/cli-flow-commands.d.ts +3 -1
- package/dist/cli/cli-flow-commands.js +34 -4
- package/dist/cli/cli-kill.d.ts +2 -1
- package/dist/cli/cli-launch.d.ts +11 -1
- package/dist/cli/cli-launch.js +59 -10
- package/dist/cli/cli-parse.d.ts +17 -1
- package/dist/cli/cli-parse.js +238 -12
- package/dist/cli/cli-update-commands.js +20 -1
- package/dist/cli/cli-verify.d.ts +2 -1
- package/dist/cli/cli-verify.js +5 -4
- package/dist/cli/cloud-cli.js +428 -253
- package/dist/cli/cloud-keystore.d.ts +37 -0
- package/dist/cli/cloud-keystore.js +97 -0
- package/dist/cli/cloud-kit.d.ts +37 -0
- package/dist/cli/cloud-kit.js +121 -0
- package/dist/cli/cloud-login.d.ts +17 -0
- package/dist/cli/cloud-login.js +183 -0
- package/dist/cli/cloud-session.d.ts +48 -0
- package/dist/cli/cloud-session.js +53 -0
- package/dist/cli/config-discovery.d.ts +2 -1
- package/dist/cli/config-discovery.js +1 -0
- package/dist/cli/daemon-start-options.d.ts +32 -0
- package/dist/cli/daemon-start-options.js +38 -0
- package/dist/cli/device-flow.d.ts +76 -0
- package/dist/cli/device-flow.js +56 -0
- package/dist/cli/doctor-rows.d.ts +12 -1
- package/dist/cli/doctor-rows.js +12 -1
- package/dist/cli/drive-attach.d.ts +13 -0
- package/dist/cli/drive-attach.js +18 -0
- package/dist/cli/drive-command.js +7 -3
- package/dist/cli/gate-exit.d.ts +21 -0
- package/dist/cli/gate-exit.js +21 -0
- package/dist/cli/gate-hook-message.d.ts +33 -0
- package/dist/cli/gate-hook-message.js +46 -0
- package/dist/cli/mcp-command.d.ts +25 -0
- package/dist/cli/mcp-command.js +174 -0
- package/dist/cli/project-name.d.ts +30 -0
- package/dist/cli/project-name.js +44 -0
- package/dist/cli/sibling-ports.d.ts +37 -0
- package/dist/cli/sibling-ports.js +56 -0
- package/dist/cli/status-next-action.d.ts +14 -0
- package/dist/cli/status-next-action.js +4 -4
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +167 -186
- package/dist/cloud/cloud-config.d.ts +24 -2
- package/dist/cloud/cloud-config.js +64 -5
- package/dist/cloud/cloud-sync.d.ts +16 -5
- package/dist/cloud/cloud-sync.js +64 -1
- package/dist/cloud/sync-cycle.d.ts +110 -0
- package/dist/cloud/sync-cycle.js +270 -0
- package/dist/cloud/sync-daemon.d.ts +69 -0
- package/dist/cloud/sync-daemon.js +260 -0
- package/dist/cloud/sync-disk.d.ts +9 -0
- package/dist/cloud/sync-disk.js +191 -0
- package/dist/cloud/sync-hash.d.ts +2 -0
- package/dist/cloud/sync-hash.js +35 -0
- package/dist/crawl/crawl-tools.js +5 -3
- package/dist/crawl/crawl.d.ts +12 -13
- package/dist/crawl/crawl.js +25 -3
- package/dist/daemon/daemon-resolve.d.ts +122 -0
- package/dist/daemon/daemon-resolve.js +187 -0
- package/dist/daemon/dev-servers.d.ts +15 -0
- package/dist/daemon/dev-servers.js +68 -0
- package/dist/daemon/free-port.d.ts +29 -0
- package/dist/daemon/free-port.js +55 -0
- package/dist/daemon/heartbeat.d.ts +0 -4
- package/dist/daemon/heartbeat.js +2 -2
- package/dist/daemon/wake-decision.d.ts +32 -0
- package/dist/daemon/wake-decision.js +38 -0
- package/dist/dev/stale-issue-guard.d.ts +0 -2
- package/dist/dev/stale-issue-guard.js +1 -1
- package/dist/domain/domain-tools.js +2 -2
- package/dist/events/contradictions.d.ts +59 -0
- package/dist/events/contradictions.js +253 -27
- package/dist/events/declared.d.ts +17 -0
- package/dist/events/declared.js +43 -1
- package/dist/events/echo-mismatch.js +65 -1
- package/dist/events/event-filters.d.ts +8 -0
- package/dist/events/event-filters.js +19 -2
- package/dist/events/observed-in-window.d.ts +12 -0
- package/dist/events/observed-in-window.js +17 -0
- package/dist/events/predicate-asks.d.ts +13 -0
- package/dist/events/predicate-asks.js +22 -0
- package/dist/events/predicate-eval.js +117 -27
- package/dist/events/predicate-parse.d.ts +0 -6
- package/dist/events/predicate-parse.js +43 -3
- package/dist/events/predicate-precheck.d.ts +8 -0
- package/dist/events/predicate-precheck.js +41 -0
- package/dist/events/predicate-route.d.ts +14 -0
- package/dist/events/predicate-route.js +36 -8
- package/dist/events/predicate-schema.d.ts +44 -0
- package/dist/events/predicate-schema.js +126 -23
- package/dist/events/predicate.d.ts +20 -0
- package/dist/events/predicate.js +77 -6
- package/dist/events/split-text-miss.d.ts +30 -0
- package/dist/events/split-text-miss.js +44 -0
- package/dist/flows/coverage.d.ts +0 -8
- package/dist/flows/coverage.js +1 -1
- package/dist/flows/decision.d.ts +5 -1
- package/dist/flows/decision.js +33 -7
- package/dist/flows/flake.d.ts +6 -8
- package/dist/flows/flake.js +1 -1
- package/dist/flows/flow-intent.d.ts +52 -0
- package/dist/flows/flow-intent.js +123 -0
- package/dist/flows/flow-memory-consult.d.ts +55 -0
- package/dist/flows/flow-memory-consult.js +38 -0
- package/dist/flows/flow-replay-run.d.ts +18 -7
- package/dist/flows/flow-replay-run.js +263 -23
- package/dist/flows/flow-replay.js +11 -2
- package/dist/flows/flow-step-runners.d.ts +8 -0
- package/dist/flows/flow-step-runners.js +8 -2
- package/dist/flows/flow-store-for-session.d.ts +31 -0
- package/dist/flows/flow-store-for-session.js +33 -0
- package/dist/flows/flow-success.js +15 -3
- package/dist/flows/flow-tools.js +87 -33
- package/dist/flows/flows.d.ts +14 -1
- package/dist/flows/flows.js +502 -24
- package/dist/flows/heal-run.js +5 -2
- package/dist/flows/predicate-to-expect.d.ts +17 -12
- package/dist/flows/predicate-to-expect.js +36 -15
- package/dist/flows/recordings.d.ts +13 -1
- package/dist/flows/recordings.js +17 -2
- package/dist/flows/replay.d.ts +22 -2
- package/dist/flows/replay.js +28 -2
- package/dist/flows/server-verify.js +16 -2
- package/dist/flows/verify-change-tools.js +9 -3
- package/dist/honesty/blind-spots.d.ts +51 -15
- package/dist/honesty/blind-spots.js +64 -4
- package/dist/honesty/feature-capture.d.ts +150 -0
- package/dist/honesty/feature-capture.js +233 -0
- package/dist/honesty/honesty.d.ts +24 -0
- package/dist/honesty/honesty.js +25 -1
- package/dist/honesty/instrumentation-gaps.d.ts +39 -6
- package/dist/honesty/instrumentation-gaps.js +88 -5
- package/dist/honesty/tool-hit-rate.d.ts +86 -0
- package/dist/honesty/tool-hit-rate.js +90 -0
- package/dist/honesty/undeclared-change.d.ts +51 -0
- package/dist/honesty/undeclared-change.js +56 -0
- package/dist/honesty/verified.d.ts +16 -0
- package/dist/honesty/verified.js +84 -5
- package/dist/http-server.d.ts +0 -1
- package/dist/http-server.js +13 -1
- package/dist/hunt/hunt-report.d.ts +0 -2
- package/dist/hunt/hunt-report.js +1 -1
- package/dist/impact/impact-recorder.d.ts +31 -8
- package/dist/impact/impact-recorder.js +104 -21
- package/dist/impact/impact-store.d.ts +13 -4
- package/dist/impact/impact-store.js +44 -3
- package/dist/index.d.ts +4 -1
- package/dist/index.js +113 -7
- package/dist/init/agent-rules.d.ts +30 -6
- package/dist/init/agent-rules.js +54 -13
- package/dist/init/astro-patch.d.ts +16 -0
- package/dist/init/astro-patch.js +28 -0
- package/dist/init/capabilities.d.ts +4 -3
- package/dist/init/capabilities.js +23 -3
- package/dist/init/closing-hint.d.ts +36 -0
- package/dist/init/closing-hint.js +120 -0
- package/dist/init/confirm.d.ts +27 -28
- package/dist/init/confirm.js +51 -11
- package/dist/init/cra.d.ts +34 -5
- package/dist/init/cra.js +96 -25
- package/dist/init/csp-check.js +17 -2
- package/dist/init/csp-doctor.d.ts +8 -1
- package/dist/init/csp-doctor.js +8 -3
- package/dist/init/dev-script.d.ts +50 -0
- package/dist/init/dev-script.js +66 -0
- package/dist/init/format-generated.d.ts +18 -0
- package/dist/init/format-generated.js +39 -0
- package/dist/init/install-hint.d.ts +9 -0
- package/dist/init/install-hint.js +29 -0
- package/dist/init/mcp-clients.d.ts +7 -0
- package/dist/init/mcp-clients.js +7 -0
- package/dist/init/mcp.d.ts +17 -6
- package/dist/init/mcp.js +27 -11
- package/dist/init/node-io.js +11 -1
- package/dist/init/plan-framework.d.ts +0 -5
- package/dist/init/plan-framework.js +147 -37
- package/dist/init/plan.d.ts +30 -37
- package/dist/init/plan.js +50 -60
- package/dist/init/preflight.d.ts +28 -0
- package/dist/init/preflight.js +38 -0
- package/dist/init/refresh-rules.d.ts +39 -0
- package/dist/init/refresh-rules.js +51 -0
- package/dist/init/run.d.ts +32 -35
- package/dist/init/run.js +151 -202
- package/dist/init/slash-command.d.ts +1 -1
- package/dist/init/snippets.js +90 -31
- package/dist/init/vite-config.d.ts +1 -1
- package/dist/init/vite-config.js +39 -25
- package/dist/init/workspace-apps.d.ts +17 -10
- package/dist/init/workspace-apps.js +110 -0
- package/dist/input/network-mock-tools.js +3 -6
- package/dist/input/real-input.d.ts +2 -0
- package/dist/input/real-input.js +28 -7
- package/dist/input/scroll-find.d.ts +7 -4
- package/dist/input/scroll-find.js +63 -25
- package/dist/input/scroll-tools.js +4 -6
- package/dist/input/viewport-tools.js +5 -4
- package/dist/intent/inline-intent.d.ts +37 -0
- package/dist/intent/inline-intent.js +193 -0
- package/dist/intent/intent-shard-store.d.ts +72 -0
- package/dist/intent/intent-shard-store.js +203 -0
- package/dist/intent/intent-shard.d.ts +411 -0
- package/dist/intent/intent-shard.js +176 -0
- package/dist/intent/intent-store.d.ts +49 -0
- package/dist/intent/intent-store.js +133 -0
- package/dist/intent/intent-subject.d.ts +45 -0
- package/dist/intent/intent-subject.js +177 -0
- package/dist/intent/intent-tools.d.ts +2 -0
- package/dist/intent/intent-tools.js +143 -0
- package/dist/intent/open-intents.d.ts +42 -0
- package/dist/intent/open-intents.js +40 -0
- package/dist/journal/envelope.d.ts +26 -26
- package/dist/journal/journal-recorder.d.ts +23 -0
- package/dist/journal/journal-recorder.js +35 -0
- package/dist/license/license-env.d.ts +29 -0
- package/dist/license/license-env.js +148 -0
- package/dist/loopback-agent.d.ts +31 -0
- package/dist/loopback-agent.js +57 -0
- package/dist/mcp/attach-memory.d.ts +83 -0
- package/dist/mcp/attach-memory.js +185 -0
- package/dist/mcp/client-identity.d.ts +31 -0
- package/dist/mcp/client-identity.js +51 -0
- package/dist/mcp/mcp-post-transport.d.ts +26 -0
- package/dist/mcp/mcp-post-transport.js +109 -0
- package/dist/mcp/mcp-proxy.d.ts +2 -8
- package/dist/mcp/mcp-proxy.js +64 -62
- package/dist/mcp/mcp.d.ts +2 -0
- package/dist/mcp/mcp.js +52 -24
- package/dist/mcp/proxy-backoff.d.ts +11 -0
- package/dist/mcp/proxy-backoff.js +13 -0
- package/dist/mcp/proxy-daemon-probe.d.ts +1 -0
- package/dist/mcp/proxy-daemon-probe.js +7 -1
- package/dist/mcp/proxy-handshake.d.ts +23 -0
- package/dist/mcp/proxy-handshake.js +31 -0
- package/dist/mcp/server-instructions.js +45 -3
- package/dist/mcp/tool-catalog-cache.d.ts +16 -0
- package/dist/mcp/tool-catalog-cache.js +22 -0
- package/dist/memory/memory-tools.d.ts +2 -0
- package/dist/memory/memory-tools.js +82 -0
- package/dist/memory/project-memory.d.ts +34 -0
- package/dist/memory/project-memory.js +96 -0
- package/dist/oracles/flow-instrument-gaps.d.ts +4 -4
- package/dist/oracles/flow-instrument-gaps.js +4 -5
- package/dist/oracles/propose-consequences.js +12 -3
- package/dist/oracles/self-instrument.d.ts +9 -9
- package/dist/oracles/self-instrument.js +19 -13
- package/dist/pool/browser-pool.d.ts +75 -5
- package/dist/pool/browser-pool.js +144 -5
- package/dist/pool/playwright-launcher.js +12 -1
- package/dist/project/fs-port.d.ts +3 -0
- package/dist/project/fs-port.js +3 -2
- package/dist/project/memory-fs.js +6 -1
- package/dist/project/project-tools.js +6 -35
- package/dist/project/reticle-dir.d.ts +17 -2
- package/dist/project/reticle-dir.js +22 -4
- package/dist/runs/context-tools.d.ts +2 -0
- package/dist/runs/context-tools.js +89 -0
- package/dist/runs/run-context.d.ts +40 -0
- package/dist/runs/run-context.js +158 -0
- package/dist/runs/run-diff.d.ts +2 -1
- package/dist/runs/run-store.d.ts +10 -0
- package/dist/runs/run-store.js +10 -0
- package/dist/runs/verification-sync.d.ts +0 -11
- package/dist/runs/verification-sync.js +24 -3
- package/dist/runs/verify-server.d.ts +8 -1
- package/dist/runs/verify-server.js +14 -2
- package/dist/session/command-timeout.d.ts +5 -2
- package/dist/session/command-timeout.js +5 -4
- package/dist/session/dev-server-probe.d.ts +0 -8
- package/dist/session/dev-server-probe.js +1 -1
- package/dist/session/last-act.d.ts +7 -1
- package/dist/session/last-act.js +2 -2
- package/dist/session/lease-visibility.d.ts +67 -0
- package/dist/session/lease-visibility.js +76 -0
- package/dist/session/live-control-tools.js +1 -42
- package/dist/session/no-session-diagnosis.d.ts +33 -0
- package/dist/session/no-session-diagnosis.js +82 -24
- package/dist/session/no-session-next-action.d.ts +12 -0
- package/dist/session/no-session-next-action.js +77 -6
- package/dist/session/no-session-watch.d.ts +18 -6
- package/dist/session/no-session-watch.js +125 -75
- package/dist/session/output-budget.js +4 -1
- package/dist/session/pending-commands.d.ts +11 -1
- package/dist/session/pending-commands.js +12 -2
- package/dist/session/resolve-within.d.ts +34 -0
- package/dist/session/resolve-within.js +53 -0
- package/dist/session/session-health.d.ts +9 -0
- package/dist/session/session-health.js +23 -0
- package/dist/session/session-info.d.ts +18 -0
- package/dist/session/session-info.js +11 -0
- package/dist/session/session-manager.d.ts +14 -0
- package/dist/session/session-manager.js +111 -4
- package/dist/session/session-mirror.d.ts +16 -0
- package/dist/session/session-mirror.js +22 -0
- package/dist/session/session-recommendation.d.ts +19 -5
- package/dist/session/session-recommendation.js +25 -9
- package/dist/session/session-successor.d.ts +37 -0
- package/dist/session/session-successor.js +113 -0
- package/dist/session/session-tools.js +2 -2
- package/dist/session/session.d.ts +53 -3
- package/dist/session/session.js +162 -9
- package/dist/session/wall-clock.d.ts +12 -0
- package/dist/session/wall-clock.js +14 -0
- package/dist/setup/agent-configs.d.ts +96 -0
- package/dist/setup/agent-configs.js +333 -0
- package/dist/setup/agent-io.d.ts +9 -0
- package/dist/setup/agent-io.js +16 -0
- package/dist/setup/agent-writer.d.ts +51 -0
- package/dist/setup/agent-writer.js +115 -0
- package/dist/setup/approval-migration.d.ts +47 -0
- package/dist/setup/approval-migration.js +68 -0
- package/dist/setup/auto-approve.d.ts +85 -0
- package/dist/setup/auto-approve.js +150 -0
- package/dist/setup/bridge-port.d.ts +15 -0
- package/dist/setup/bridge-port.js +24 -0
- package/dist/setup/desktop-shape.d.ts +57 -0
- package/dist/setup/desktop-shape.js +63 -0
- package/dist/setup/dev-server-wait.d.ts +60 -0
- package/dist/setup/dev-server-wait.js +65 -0
- package/dist/setup/drive-agent.d.ts +71 -0
- package/dist/setup/drive-agent.js +171 -0
- package/dist/setup/drive-plan.d.ts +68 -0
- package/dist/setup/drive-plan.js +130 -0
- package/dist/setup/ensure-daemon.d.ts +29 -0
- package/dist/setup/ensure-daemon.js +58 -0
- package/dist/setup/init-runtime.d.ts +47 -0
- package/dist/setup/init-runtime.js +160 -0
- package/dist/setup/interrupt.d.ts +25 -0
- package/dist/setup/interrupt.js +40 -0
- package/dist/setup/license-key.d.ts +39 -0
- package/dist/setup/license-key.js +72 -0
- package/dist/setup/listeners.d.ts +37 -0
- package/dist/setup/listeners.js +85 -0
- package/dist/setup/node-effects.d.ts +60 -0
- package/dist/setup/node-effects.js +210 -0
- package/dist/setup/page-probe.d.ts +39 -0
- package/dist/setup/page-probe.js +49 -0
- package/dist/setup/relaunch.d.ts +36 -0
- package/dist/setup/relaunch.js +57 -0
- package/dist/setup/remaining-steps.d.ts +19 -0
- package/dist/setup/remaining-steps.js +31 -0
- package/dist/setup/run-setup.d.ts +99 -0
- package/dist/setup/run-setup.js +187 -0
- package/dist/setup/session-pick.d.ts +47 -0
- package/dist/setup/session-pick.js +51 -0
- package/dist/setup/setup-command.d.ts +45 -0
- package/dist/setup/setup-command.js +199 -0
- package/dist/setup/setup-options.d.ts +74 -0
- package/dist/setup/setup-options.js +67 -0
- package/dist/setup/transcripts.d.ts +16 -0
- package/dist/setup/transcripts.js +43 -0
- package/dist/status-payload.d.ts +14 -1
- package/dist/status-payload.js +23 -1
- package/dist/telemetry/automation-hint.d.ts +2 -1
- package/dist/telemetry/daemon-telemetry.d.ts +8 -1
- package/dist/telemetry/daemon-telemetry.js +52 -5
- package/dist/telemetry/error-class.js +1 -1
- package/dist/telemetry/feedback-context.d.ts +9 -1
- package/dist/telemetry/feedback-context.js +60 -29
- package/dist/telemetry/feedback-local.d.ts +15 -0
- package/dist/telemetry/feedback-local.js +56 -0
- package/dist/telemetry/feedback-outbox.d.ts +2 -1
- package/dist/telemetry/feedback.d.ts +11 -0
- package/dist/telemetry/feedback.js +19 -6
- package/dist/telemetry/init-telemetry.d.ts +2 -0
- package/dist/telemetry/init-telemetry.js +2 -0
- package/dist/telemetry/install-source.d.ts +14 -0
- package/dist/telemetry/install-source.js +30 -0
- package/dist/telemetry/license-activation.d.ts +7 -1
- package/dist/telemetry/license-activation.js +9 -5
- package/dist/telemetry/mcp-connection.js +7 -0
- package/dist/telemetry/project-profile.d.ts +12 -9
- package/dist/telemetry/project-profile.js +12 -7
- package/dist/telemetry/proxy-telemetry.d.ts +3 -0
- package/dist/telemetry/proxy-telemetry.js +21 -0
- package/dist/telemetry/run-telemetry.d.ts +0 -2
- package/dist/telemetry/run-telemetry.js +1 -1
- package/dist/telemetry/session-metrics.d.ts +12 -0
- package/dist/telemetry/session-metrics.js +35 -1
- package/dist/telemetry/telemetry.js +17 -2
- package/dist/telemetry/tool-refused.d.ts +3 -1
- package/dist/telemetry/tool-refused.js +34 -2
- package/dist/telemetry/verification-of.js +2 -2
- package/dist/tools/act-capsule.d.ts +10 -0
- package/dist/tools/act-capsule.js +8 -2
- package/dist/tools/act-danger.d.ts +2 -1
- package/dist/tools/act-danger.js +11 -3
- package/dist/tools/act-observation.d.ts +41 -0
- package/dist/tools/act-observation.js +36 -0
- package/dist/tools/act-preflight.d.ts +20 -0
- package/dist/tools/act-preflight.js +98 -0
- package/dist/tools/act-sequence-retry.d.ts +45 -0
- package/dist/tools/act-sequence-retry.js +58 -0
- package/dist/tools/act-sequence-tool.d.ts +13 -0
- package/dist/tools/act-sequence-tool.js +135 -0
- package/dist/tools/act-target.d.ts +14 -0
- package/dist/tools/act-target.js +47 -0
- package/dist/tools/act-tools.d.ts +21 -1
- package/dist/tools/act-tools.js +196 -204
- package/dist/tools/assert-source.d.ts +22 -0
- package/dist/tools/assert-source.js +53 -0
- package/dist/tools/assert-verdict.d.ts +20 -3
- package/dist/tools/assert-verdict.js +58 -20
- package/dist/tools/browser-tools.js +6 -22
- package/dist/tools/complete-snapshot.d.ts +48 -0
- package/dist/tools/complete-snapshot.js +75 -0
- package/dist/tools/coverage-tools.d.ts +10 -1
- package/dist/tools/coverage-tools.js +123 -69
- package/dist/tools/dynamic-tools.js +53 -13
- package/dist/tools/error-recovery.d.ts +25 -14
- package/dist/tools/error-recovery.js +78 -9
- package/dist/tools/feedback-tools.d.ts +1 -1
- package/dist/tools/feedback-tools.js +2 -2
- package/dist/tools/invoke-tool.js +121 -11
- package/dist/tools/lease-hint.d.ts +10 -0
- package/dist/tools/lease-hint.js +53 -4
- package/dist/tools/lease-tools.d.ts +35 -5
- package/dist/tools/lease-tools.js +191 -13
- package/dist/tools/merged-name-redirect.d.ts +13 -0
- package/dist/tools/merged-name-redirect.js +22 -0
- package/dist/tools/navigate-arrival.d.ts +0 -2
- package/dist/tools/navigate-arrival.js +1 -1
- package/dist/tools/numeric-bounds.d.ts +100 -0
- package/dist/tools/numeric-bounds.js +125 -0
- package/dist/tools/observe-tools.js +109 -65
- package/dist/tools/read-tools.js +31 -8
- package/dist/tools/real-input-attempt.d.ts +32 -0
- package/dist/tools/real-input-attempt.js +212 -2
- package/dist/tools/reconcile-tools.js +22 -8
- package/dist/tools/resolve-target.d.ts +0 -7
- package/dist/tools/sdk-marker-probe.d.ts +29 -0
- package/dist/tools/sdk-marker-probe.js +57 -0
- package/dist/tools/snapshot-delta.d.ts +1 -1
- package/dist/tools/tool-kit.d.ts +24 -0
- package/dist/tools/tool-kit.js +21 -0
- package/dist/tools/tool-names.d.ts +5 -0
- package/dist/tools/tool-names.js +5 -0
- package/dist/tools/tool-surface.d.ts +85 -6
- package/dist/tools/tool-surface.js +122 -2
- package/dist/tools/tools-helpers.d.ts +19 -0
- package/dist/tools/tools-helpers.js +46 -0
- package/dist/tools/tools.d.ts +2 -0
- package/dist/tools/tools.js +78 -12
- package/dist/tools/unadvertised-help.js +4 -4
- package/dist/tools/verify-next-baton.d.ts +53 -0
- package/dist/tools/verify-next-baton.js +52 -0
- package/dist/update/reticle-deps.d.ts +9 -0
- package/dist/update/reticle-deps.js +27 -0
- package/dist/update/sdk-sync.d.ts +1 -2
- package/dist/update/sdk-sync.js +1 -21
- package/dist/update/update-nudge.d.ts +25 -10
- package/dist/update/update-nudge.js +32 -3
- package/dist/update/updater.d.ts +1 -1
- package/dist/version/peer-announce.js +4 -2
- package/dist/version/sdk-fix.d.ts +47 -0
- package/dist/version/sdk-fix.js +127 -0
- package/dist/version/version-nudge.d.ts +14 -0
- package/dist/version/version-nudge.js +21 -0
- package/dist/version/version-skew.d.ts +38 -3
- package/dist/version/version-skew.js +83 -7
- package/dist/visual/visual-store.d.ts +6 -6
- package/dist/visual/visual-store.js +18 -16
- package/dist/visual/visual-tools.js +57 -12
- package/docs/README.md +2 -1
- package/docs/cli/cloud.mdx +1 -1
- package/docs/cli/doctor.mdx +2 -0
- package/docs/cli/serve.mdx +4 -2
- package/docs/cli/verify.mdx +4 -5
- package/docs/cli.mdx +7 -3
- package/docs/desktop.mdx +1 -1
- package/docs/docs.json +2 -2
- package/docs/gates.md +1 -1
- package/docs/getting-started.md +19 -3
- package/docs/index.mdx +9 -6
- package/docs/install-agentic.mdx +38 -12
- package/docs/install-manual.mdx +1 -1
- package/docs/packages/browser.mdx +2 -2
- package/docs/packages/server.mdx +1 -1
- package/docs/packages.mdx +8 -6
- package/docs/platform-integration.md +17 -0
- package/docs/predicates.mdx +8 -0
- package/docs/reticle-manual.md +106 -0
- package/docs/skill-file.mdx +3 -3
- package/docs/telemetry-contract.md +108 -4
- package/docs/tools-act-sequence.mdx +16 -0
- package/docs/tools-tools-and-run.mdx +2 -4
- package/docs/troubleshooting.mdx +9 -0
- package/docs/usage.md +4 -3
- package/package.json +4 -3
- package/dist/init/cursor.d.ts +0 -24
- package/dist/init/cursor.js +0 -80
- package/dist/session/session-readiness.d.ts +0 -34
- package/dist/session/session-readiness.js +0 -36
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The sharded intent store: one small file per subject, plus an index cheap enough to always load.
|
|
3
|
+
*
|
|
4
|
+
* ## Why this exists
|
|
5
|
+
*
|
|
6
|
+
* `.reticle/intent.json` was a single object that reached 141 entries and 109KB. Parsing that is
|
|
7
|
+
* nothing; READING it is the cost, and reading is what this store is for. An agent wanting the two
|
|
8
|
+
* rules about checkout pulled 139 irrelevant ones through its context to find them, and an agent
|
|
9
|
+
* changing one rewrote the whole file — which is both slow to review and a lost write whenever two
|
|
10
|
+
* sessions touch different subjects at once.
|
|
11
|
+
*
|
|
12
|
+
* ## The shape
|
|
13
|
+
*
|
|
14
|
+
* .reticle/intent/index.json every id, its subject and one line of prose
|
|
15
|
+
* .reticle/intent/<subject>.json the full records for that subject
|
|
16
|
+
*
|
|
17
|
+
* The index is the whole point: it answers "what do we know, and where does it live?" without
|
|
18
|
+
* opening anything, and detail is then fetched for the one subject in play. Measured on the real
|
|
19
|
+
* corpus, that is 109KB all-or-nothing becoming an index plus one shard — and the largest shard is
|
|
20
|
+
* under 10KB once the unsorted bucket is set aside.
|
|
21
|
+
*
|
|
22
|
+
* The index is NOT tiny, and measuring beat guessing twice over. With full statements it came to
|
|
23
|
+
* 36KB for 141 entries; summarising them saved 7%, because the bulk is not the prose but the ids —
|
|
24
|
+
* `inline:signing-in-with-the-email-password-created-earli-6ca804e9` is 63 characters before the
|
|
25
|
+
* statement starts. So the honest numbers are: 109KB all-or-nothing becomes a 33KB index plus one
|
|
26
|
+
* ~10KB shard, and the real win is that a READ and a WRITE are both bounded to one subject rather
|
|
27
|
+
* than the whole corpus.
|
|
28
|
+
*
|
|
29
|
+
* The index is therefore the part that does not scale forever. At ten times this size it needs a
|
|
30
|
+
* second tier — subjects and counts, with ids fetched per subject — rather than a longer single
|
|
31
|
+
* read. Recorded here because the next person will hit it, and the shape of the fix is already
|
|
32
|
+
* visible.
|
|
33
|
+
*
|
|
34
|
+
* ## What the records carry that the old ones did not
|
|
35
|
+
*
|
|
36
|
+
* The legacy file was written BY verification, AT verification time, in verification's vocabulary: a
|
|
37
|
+
* statement, a predicate, and whether a verdict discharged it. Of 141 entries, four named a surface.
|
|
38
|
+
* There was nowhere to put the reason a thing must be true, who decided it, or when — so the
|
|
39
|
+
* business case somebody states once and then forgets had no home and was never captured.
|
|
40
|
+
*
|
|
41
|
+
* `why`, `source` and `subject` are that home. They are optional because a migrated record cannot
|
|
42
|
+
* invent them, and a store that rejected incomplete records would simply not be written to.
|
|
43
|
+
*/
|
|
44
|
+
import { z } from 'zod';
|
|
45
|
+
import { IntentSchema } from '@reticlehq/core';
|
|
46
|
+
import { subjectFor, UNSORTED_SUBJECT } from './intent-subject.js';
|
|
47
|
+
export const INTENT_SHARD_VERSION = 1;
|
|
48
|
+
/** Byte-stable writes: an unchanged shard produces no diff, so the ones that DID change stand out. */
|
|
49
|
+
const JSON_INDENT = 2;
|
|
50
|
+
/**
|
|
51
|
+
* How settled a record is.
|
|
52
|
+
*
|
|
53
|
+
* Separate from the legacy `state`, which only ever described the VERIFICATION lifecycle
|
|
54
|
+
* (declared → bound → proved). A rule can be agreed with the customer months before anything can
|
|
55
|
+
* prove it, and collapsing those two axes is what made the old file an assertion log rather than a
|
|
56
|
+
* memory.
|
|
57
|
+
*/
|
|
58
|
+
export const IntentStatus = {
|
|
59
|
+
/** Captured, not yet confirmed by anyone. What an agent writes while building. */
|
|
60
|
+
PROPOSED: 'proposed',
|
|
61
|
+
/** Confirmed as something that must hold. May have no way to prove it yet. */
|
|
62
|
+
AGREED: 'agreed',
|
|
63
|
+
/** A verdict has actually shown it holds. */
|
|
64
|
+
PROVED: 'proved',
|
|
65
|
+
/** Believed out of date — kept, because deleting the record loses the fact it was ever true. */
|
|
66
|
+
STALE: 'stale',
|
|
67
|
+
};
|
|
68
|
+
export const IntentRecordSchema = IntentSchema.extend({
|
|
69
|
+
/** Which shard this lives in. Stored as well as implied, so a file read alone is self-describing. */
|
|
70
|
+
subject: z.string().min(1),
|
|
71
|
+
status: z.enum([
|
|
72
|
+
IntentStatus.PROPOSED,
|
|
73
|
+
IntentStatus.AGREED,
|
|
74
|
+
IntentStatus.PROVED,
|
|
75
|
+
IntentStatus.STALE,
|
|
76
|
+
]),
|
|
77
|
+
/** WHY it must be true. The half the old file had no room for. */
|
|
78
|
+
why: z.string().optional(),
|
|
79
|
+
/** Where it came from — "the user, in conversation" beats an anonymous assertion in six months. */
|
|
80
|
+
source: z.string().optional(),
|
|
81
|
+
/** When it was last touched, so a stale-looking record can be told from an untouched one. */
|
|
82
|
+
updatedAt: z.number().optional(),
|
|
83
|
+
});
|
|
84
|
+
export const IntentShardSchema = z.object({
|
|
85
|
+
version: z.literal(INTENT_SHARD_VERSION),
|
|
86
|
+
subject: z.string().min(1),
|
|
87
|
+
intents: z.record(z.string(), IntentRecordSchema),
|
|
88
|
+
});
|
|
89
|
+
/**
|
|
90
|
+
* How much of a statement the index carries.
|
|
91
|
+
*
|
|
92
|
+
* The index exists to answer WHICH SHARD, and a clause is enough to decide that; the sentence is one
|
|
93
|
+
* file away. Ellipsis included so a truncated line is never mistaken for the whole intent.
|
|
94
|
+
*
|
|
95
|
+
* Worth 7% on the real corpus rather than the large saving expected — the ids dominate — but it also
|
|
96
|
+
* bounds the damage a future 500-character statement can do to a file meant to be read every
|
|
97
|
+
* session, which is the reason to keep it.
|
|
98
|
+
*/
|
|
99
|
+
const SUMMARY_MAX = 72;
|
|
100
|
+
export const summarise = (statement) => statement.length <= SUMMARY_MAX ? statement : `${statement.slice(0, SUMMARY_MAX - 1).trimEnd()}…`;
|
|
101
|
+
/** One line per intent: enough to decide whether to open the shard, and no more. */
|
|
102
|
+
export const IntentIndexEntrySchema = z.object({
|
|
103
|
+
id: z.string().min(1),
|
|
104
|
+
subject: z.string().min(1),
|
|
105
|
+
statement: z.string().min(1),
|
|
106
|
+
status: z.enum([
|
|
107
|
+
IntentStatus.PROPOSED,
|
|
108
|
+
IntentStatus.AGREED,
|
|
109
|
+
IntentStatus.PROVED,
|
|
110
|
+
IntentStatus.STALE,
|
|
111
|
+
]),
|
|
112
|
+
});
|
|
113
|
+
export const IntentIndexSchema = z.object({
|
|
114
|
+
version: z.literal(INTENT_SHARD_VERSION),
|
|
115
|
+
entries: z.array(IntentIndexEntrySchema),
|
|
116
|
+
});
|
|
117
|
+
export const emptyShard = (subject) => ({
|
|
118
|
+
version: INTENT_SHARD_VERSION,
|
|
119
|
+
subject,
|
|
120
|
+
intents: {},
|
|
121
|
+
});
|
|
122
|
+
export const emptyIndex = () => ({ version: INTENT_SHARD_VERSION, entries: [] });
|
|
123
|
+
/** The verification lifecycle a legacy record carried, mapped onto the settledness axis. */
|
|
124
|
+
export const statusFromState = (state) => 'proved' === state ? IntentStatus.PROVED : IntentStatus.AGREED;
|
|
125
|
+
/**
|
|
126
|
+
* Turn a legacy intent into a record, inferring only what can be inferred.
|
|
127
|
+
*
|
|
128
|
+
* `why` and `source` are left ABSENT rather than filled with the statement or a placeholder. A
|
|
129
|
+
* record that claims to carry a reason it does not have is worse than one that admits the gap: the
|
|
130
|
+
* gap is a prompt to write the reason down, and a placeholder is a reason to stop looking.
|
|
131
|
+
*/
|
|
132
|
+
export const recordFromIntent = (intent) => ({
|
|
133
|
+
...intent,
|
|
134
|
+
subject: subjectFor({ surface: intent.surface, binding: intent.binding }),
|
|
135
|
+
status: statusFromState(intent.state),
|
|
136
|
+
});
|
|
137
|
+
/** Build the index from the shards. Derived, never hand-maintained — two sources would disagree. */
|
|
138
|
+
export const indexFrom = (shards) => ({
|
|
139
|
+
version: INTENT_SHARD_VERSION,
|
|
140
|
+
entries: shards
|
|
141
|
+
.flatMap((s) => Object.values(s.intents))
|
|
142
|
+
.map((r) => ({
|
|
143
|
+
id: r.id,
|
|
144
|
+
subject: r.subject,
|
|
145
|
+
statement: summarise(r.statement),
|
|
146
|
+
status: r.status,
|
|
147
|
+
}))
|
|
148
|
+
.sort((a, b) => a.subject.localeCompare(b.subject) || a.id.localeCompare(b.id)),
|
|
149
|
+
});
|
|
150
|
+
/** Group records into shards by their own subject. */
|
|
151
|
+
export const shardsFrom = (records) => {
|
|
152
|
+
const bySubject = new Map();
|
|
153
|
+
for (const r of records) {
|
|
154
|
+
const subject = '' === r.subject ? UNSORTED_SUBJECT : r.subject;
|
|
155
|
+
const shard = bySubject.get(subject) ?? emptyShard(subject);
|
|
156
|
+
shard.intents[r.id] = r;
|
|
157
|
+
bySubject.set(subject, shard);
|
|
158
|
+
}
|
|
159
|
+
return [...bySubject.values()].sort((a, b) => a.subject.localeCompare(b.subject));
|
|
160
|
+
};
|
|
161
|
+
/** Serialise byte-stably, with keys sorted so a re-save of unchanged data is a no-op diff. */
|
|
162
|
+
export const serialise = (value) => `${JSON.stringify(value, (_k, v) => sortKeys(v), JSON_INDENT)}\n`;
|
|
163
|
+
const sortKeys = (value) => {
|
|
164
|
+
if (Array.isArray(value) || null === value || typeof value !== 'object')
|
|
165
|
+
return value;
|
|
166
|
+
return Object.fromEntries(Object.entries(value).sort(([a], [b]) => a.localeCompare(b)));
|
|
167
|
+
};
|
|
168
|
+
/** Read + validate one JSON file, failing soft: a hand-merged file with a conflict marker is real. */
|
|
169
|
+
export const readJsonFile = async (fs, path, parse, fallback) => {
|
|
170
|
+
try {
|
|
171
|
+
return parse(JSON.parse(await fs.readFile(path)));
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
return fallback;
|
|
175
|
+
}
|
|
176
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { type Intent, type IntentSurface } from '@reticlehq/core';
|
|
2
|
+
import type { FileSystemPort } from '../project/fs-port.js';
|
|
3
|
+
export interface Clock {
|
|
4
|
+
now: () => number;
|
|
5
|
+
}
|
|
6
|
+
export declare class IntentStore {
|
|
7
|
+
#private;
|
|
8
|
+
constructor(fs: FileSystemPort, root: string, clock: Clock);
|
|
9
|
+
/** Every intent, in declaration order. */
|
|
10
|
+
read(): Promise<Intent[]>;
|
|
11
|
+
/** Everything not yet proved — what an agent asking "am I done?" still owes. */
|
|
12
|
+
open(): Promise<Intent[]>;
|
|
13
|
+
/**
|
|
14
|
+
* Declare one or more intents.
|
|
15
|
+
*
|
|
16
|
+
* Batched because the marginal cost of the whole mechanism has to stay at one call per feature; an
|
|
17
|
+
* agent that must make five calls to declare five things will make none.
|
|
18
|
+
*/
|
|
19
|
+
declare(entries: readonly {
|
|
20
|
+
id: string;
|
|
21
|
+
statement: string;
|
|
22
|
+
surface?: IntentSurface;
|
|
23
|
+
}[]): Promise<Intent[]>;
|
|
24
|
+
/** Attach the predicate that would prove an intent. False when the id names nothing. */
|
|
25
|
+
/**
|
|
26
|
+
* File a record under where it turned out to be about.
|
|
27
|
+
*
|
|
28
|
+
* Separate from `declare` because the two happen at different MOMENTS and know different things.
|
|
29
|
+
* An inline intent is declared BEFORE the action — deliberately, so a verdict can see it open —
|
|
30
|
+
* and at that instant the agent is still on the page it is leaving. The route that describes what
|
|
31
|
+
* the intent is ABOUT only exists after the consequence lands.
|
|
32
|
+
*
|
|
33
|
+
* Write-once: an existing surface is never overwritten. A record placed by an agent that named its
|
|
34
|
+
* own subject must not be re-filed by a later run that happened to be somewhere else.
|
|
35
|
+
*/
|
|
36
|
+
place(id: string, surface: IntentSurface): Promise<boolean>;
|
|
37
|
+
bind(id: string, binding: unknown): Promise<boolean>;
|
|
38
|
+
/**
|
|
39
|
+
* Record that a verdict proved an intent.
|
|
40
|
+
*
|
|
41
|
+
* False rather than a throw on an unknown or unbound id: discharge runs off the back of a verdict,
|
|
42
|
+
* and must never be the reason one fails to return.
|
|
43
|
+
*/
|
|
44
|
+
discharge(id: string, proof: {
|
|
45
|
+
verdictId: string;
|
|
46
|
+
grade: string;
|
|
47
|
+
at: number;
|
|
48
|
+
}): Promise<boolean>;
|
|
49
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { bindIntent, declareIntent, dischargeIntent, emptyIntentFile, openIntents, parseIntentFile, upsertIntent, } from '@reticlehq/core';
|
|
2
|
+
import { reticleDirPaths } from '../project/reticle-dir.js';
|
|
3
|
+
import { withFileLock } from '../project/file-lock.js';
|
|
4
|
+
/**
|
|
5
|
+
* The intent ledger on disk — `.reticle/intent.json`, git-checked and meant to be read in review.
|
|
6
|
+
*
|
|
7
|
+
* Git-checked deliberately. The one real defence against an agent quietly narrowing what it meant to
|
|
8
|
+
* match what it can already prove is that a human sees the narrowing in a diff, and that only works
|
|
9
|
+
* if the file lives with the code and travels with the PR. It is also why the writes are byte-stable:
|
|
10
|
+
* a ledger that churns on every run is one whose diffs nobody reads.
|
|
11
|
+
*
|
|
12
|
+
* Every operation goes through the same file lock the other stores use, because two sessions driving
|
|
13
|
+
* one project is ordinary and a lost declaration would be silent.
|
|
14
|
+
*/
|
|
15
|
+
const JSON_INDENT = 2;
|
|
16
|
+
export class IntentStore {
|
|
17
|
+
#fs;
|
|
18
|
+
#root;
|
|
19
|
+
#clock;
|
|
20
|
+
constructor(fs, root, clock) {
|
|
21
|
+
this.#fs = fs;
|
|
22
|
+
this.#root = root;
|
|
23
|
+
this.#clock = clock;
|
|
24
|
+
}
|
|
25
|
+
#path() {
|
|
26
|
+
return reticleDirPaths(this.#root).intent;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Load the ledger, failing soft to empty.
|
|
30
|
+
*
|
|
31
|
+
* This is a git-checked file an agent can write and a human can hand-merge, so a malformed one is
|
|
32
|
+
* genuinely reachable — a conflict marker left in place is the obvious case. Throwing here would
|
|
33
|
+
* take down the verdict that was only asking what was still open, which trades a small problem for
|
|
34
|
+
* a large one.
|
|
35
|
+
*/
|
|
36
|
+
async #load() {
|
|
37
|
+
try {
|
|
38
|
+
const raw = await this.#fs.readFile(this.#path());
|
|
39
|
+
return parseIntentFile(JSON.parse(raw));
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return emptyIntentFile();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/** Byte-stable: 2-space indent, one trailing newline. An unchanged ledger produces no diff. */
|
|
46
|
+
async #save(file) {
|
|
47
|
+
await this.#fs.mkdir(reticleDirPaths(this.#root).root);
|
|
48
|
+
await this.#fs.writeFile(this.#path(), `${JSON.stringify(file, null, JSON_INDENT)}\n`);
|
|
49
|
+
}
|
|
50
|
+
/** Every intent, in declaration order. */
|
|
51
|
+
async read() {
|
|
52
|
+
return Object.values((await this.#load()).intents);
|
|
53
|
+
}
|
|
54
|
+
/** Everything not yet proved — what an agent asking "am I done?" still owes. */
|
|
55
|
+
async open() {
|
|
56
|
+
return openIntents(await this.#load());
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Declare one or more intents.
|
|
60
|
+
*
|
|
61
|
+
* Batched because the marginal cost of the whole mechanism has to stay at one call per feature; an
|
|
62
|
+
* agent that must make five calls to declare five things will make none.
|
|
63
|
+
*/
|
|
64
|
+
async declare(entries) {
|
|
65
|
+
if (0 === entries.length)
|
|
66
|
+
return [];
|
|
67
|
+
return withFileLock(this.#path(), async () => {
|
|
68
|
+
let file = await this.#load();
|
|
69
|
+
const now = this.#clock.now();
|
|
70
|
+
const declared = [];
|
|
71
|
+
for (const entry of entries) {
|
|
72
|
+
const intent = declareIntent({ ...entry, now });
|
|
73
|
+
file = upsertIntent(file, intent);
|
|
74
|
+
const stored = file.intents[intent.id];
|
|
75
|
+
if (stored !== undefined)
|
|
76
|
+
declared.push(stored);
|
|
77
|
+
}
|
|
78
|
+
await this.#save(file);
|
|
79
|
+
return declared;
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
/** Attach the predicate that would prove an intent. False when the id names nothing. */
|
|
83
|
+
/**
|
|
84
|
+
* File a record under where it turned out to be about.
|
|
85
|
+
*
|
|
86
|
+
* Separate from `declare` because the two happen at different MOMENTS and know different things.
|
|
87
|
+
* An inline intent is declared BEFORE the action — deliberately, so a verdict can see it open —
|
|
88
|
+
* and at that instant the agent is still on the page it is leaving. The route that describes what
|
|
89
|
+
* the intent is ABOUT only exists after the consequence lands.
|
|
90
|
+
*
|
|
91
|
+
* Write-once: an existing surface is never overwritten. A record placed by an agent that named its
|
|
92
|
+
* own subject must not be re-filed by a later run that happened to be somewhere else.
|
|
93
|
+
*/
|
|
94
|
+
async place(id, surface) {
|
|
95
|
+
return withFileLock(this.#path(), async () => {
|
|
96
|
+
const file = await this.#load();
|
|
97
|
+
const existing = file.intents[id];
|
|
98
|
+
if (existing === undefined || existing.surface !== undefined)
|
|
99
|
+
return false;
|
|
100
|
+
await this.#save(upsertIntent(file, { ...existing, surface }));
|
|
101
|
+
return true;
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
async bind(id, binding) {
|
|
105
|
+
return withFileLock(this.#path(), async () => {
|
|
106
|
+
const file = await this.#load();
|
|
107
|
+
const existing = file.intents[id];
|
|
108
|
+
if (existing === undefined)
|
|
109
|
+
return false;
|
|
110
|
+
await this.#save(upsertIntent(file, bindIntent(existing, binding)));
|
|
111
|
+
return true;
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Record that a verdict proved an intent.
|
|
116
|
+
*
|
|
117
|
+
* False rather than a throw on an unknown or unbound id: discharge runs off the back of a verdict,
|
|
118
|
+
* and must never be the reason one fails to return.
|
|
119
|
+
*/
|
|
120
|
+
async discharge(id, proof) {
|
|
121
|
+
return withFileLock(this.#path(), async () => {
|
|
122
|
+
const file = await this.#load();
|
|
123
|
+
const existing = file.intents[id];
|
|
124
|
+
if (existing === undefined)
|
|
125
|
+
return false;
|
|
126
|
+
const proved = dischargeIntent(existing, proof);
|
|
127
|
+
if (proved === existing)
|
|
128
|
+
return false;
|
|
129
|
+
await this.#save(upsertIntent(file, proved));
|
|
130
|
+
return true;
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What an intent is ABOUT — the key the store shards on, and the thing that makes it retrievable.
|
|
3
|
+
*
|
|
4
|
+
* `.reticle/intent.json` reached 141 entries and 109KB as one object. That is not expensive to
|
|
5
|
+
* parse; it is expensive to READ, which is the operation that matters here. An agent that wants the
|
|
6
|
+
* two rules about checkout has to pull 139 irrelevant ones through its context to find them, and an
|
|
7
|
+
* agent that wants to change one has to rewrite the whole file.
|
|
8
|
+
*
|
|
9
|
+
* Sharding by subject fixes both, but only if the subject is DERIVABLE. A store that requires every
|
|
10
|
+
* caller to invent a category is one where everything lands in whatever the first caller typed. So
|
|
11
|
+
* the subject is inferred from evidence the intent already carries, in falling order of reliability:
|
|
12
|
+
*
|
|
13
|
+
* 1. an explicit subject, when the caller knows one
|
|
14
|
+
* 2. the flow it belongs to — the strongest signal, because a flow IS a feature
|
|
15
|
+
* 3. the route it touches, which is how the product is navigated and therefore how it is discussed
|
|
16
|
+
* 4. the feature directory of the FILE its verdict acted on — the grouping the codebase already
|
|
17
|
+
* states about itself
|
|
18
|
+
* 5. the API path its binding asserts on, which names the domain even when the UI does not
|
|
19
|
+
* 6. failing all of that, a single shared bucket — visibly unsorted rather than silently misfiled
|
|
20
|
+
*
|
|
21
|
+
* Nothing here guesses from prose. A statement is a sentence written for a human, and clustering
|
|
22
|
+
* sentences is exactly the kind of plausible-looking inference that puts "sign-in works" under
|
|
23
|
+
* `settings` and leaves nobody able to explain why.
|
|
24
|
+
*/
|
|
25
|
+
/** Where an intent goes when nothing about it says where it belongs. Deliberately obvious. */
|
|
26
|
+
export declare const UNSORTED_SUBJECT = "unsorted";
|
|
27
|
+
/** Shape enough of an intent to derive a subject, without importing the whole record. */
|
|
28
|
+
export interface SubjectEvidence {
|
|
29
|
+
subject?: string | undefined;
|
|
30
|
+
surface?: {
|
|
31
|
+
route?: string | undefined;
|
|
32
|
+
flow?: string | undefined;
|
|
33
|
+
files?: string[] | undefined;
|
|
34
|
+
} | undefined;
|
|
35
|
+
binding?: unknown;
|
|
36
|
+
}
|
|
37
|
+
/** A filesystem- and URL-safe slug. Same rules as a project id, for the same reasons. */
|
|
38
|
+
export declare const slugifySubject: (raw: string) => string;
|
|
39
|
+
/**
|
|
40
|
+
* The subject for one intent, by the ladder above.
|
|
41
|
+
*
|
|
42
|
+
* Total: it always returns something, because a store that can refuse to place a record is a store
|
|
43
|
+
* that loses records.
|
|
44
|
+
*/
|
|
45
|
+
export declare const subjectFor: (evidence: SubjectEvidence) => string;
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What an intent is ABOUT — the key the store shards on, and the thing that makes it retrievable.
|
|
3
|
+
*
|
|
4
|
+
* `.reticle/intent.json` reached 141 entries and 109KB as one object. That is not expensive to
|
|
5
|
+
* parse; it is expensive to READ, which is the operation that matters here. An agent that wants the
|
|
6
|
+
* two rules about checkout has to pull 139 irrelevant ones through its context to find them, and an
|
|
7
|
+
* agent that wants to change one has to rewrite the whole file.
|
|
8
|
+
*
|
|
9
|
+
* Sharding by subject fixes both, but only if the subject is DERIVABLE. A store that requires every
|
|
10
|
+
* caller to invent a category is one where everything lands in whatever the first caller typed. So
|
|
11
|
+
* the subject is inferred from evidence the intent already carries, in falling order of reliability:
|
|
12
|
+
*
|
|
13
|
+
* 1. an explicit subject, when the caller knows one
|
|
14
|
+
* 2. the flow it belongs to — the strongest signal, because a flow IS a feature
|
|
15
|
+
* 3. the route it touches, which is how the product is navigated and therefore how it is discussed
|
|
16
|
+
* 4. the feature directory of the FILE its verdict acted on — the grouping the codebase already
|
|
17
|
+
* states about itself
|
|
18
|
+
* 5. the API path its binding asserts on, which names the domain even when the UI does not
|
|
19
|
+
* 6. failing all of that, a single shared bucket — visibly unsorted rather than silently misfiled
|
|
20
|
+
*
|
|
21
|
+
* Nothing here guesses from prose. A statement is a sentence written for a human, and clustering
|
|
22
|
+
* sentences is exactly the kind of plausible-looking inference that puts "sign-in works" under
|
|
23
|
+
* `settings` and leaves nobody able to explain why.
|
|
24
|
+
*/
|
|
25
|
+
/** Where an intent goes when nothing about it says where it belongs. Deliberately obvious. */
|
|
26
|
+
export const UNSORTED_SUBJECT = 'unsorted';
|
|
27
|
+
/** The longest a subject may be. It is a filename, and it is read in a directory listing. */
|
|
28
|
+
const MAX_SUBJECT = 40;
|
|
29
|
+
/** A filesystem- and URL-safe slug. Same rules as a project id, for the same reasons. */
|
|
30
|
+
export const slugifySubject = (raw) => {
|
|
31
|
+
const slug = raw
|
|
32
|
+
.toLowerCase()
|
|
33
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
34
|
+
.replace(/^-+|-+$/g, '')
|
|
35
|
+
.slice(0, MAX_SUBJECT)
|
|
36
|
+
.replace(/-+$/g, '');
|
|
37
|
+
return 0 === slug.length ? UNSORTED_SUBJECT : slug;
|
|
38
|
+
};
|
|
39
|
+
/** The first path segment of a route or URL — `/issues?x=1` and `/v1/issues` both give `issues`. */
|
|
40
|
+
const firstSegment = (path) => {
|
|
41
|
+
const clean = path.split(/[?#]/)[0] ?? '';
|
|
42
|
+
for (const part of clean.split('/')) {
|
|
43
|
+
// Skip empties, the API version prefix, and anything that is plainly an id rather than a name.
|
|
44
|
+
if ('' === part || /^v\d+$/i.test(part) || /^\d+$/.test(part))
|
|
45
|
+
continue;
|
|
46
|
+
/*
|
|
47
|
+
* A QUERY FRAGMENT is not a path segment.
|
|
48
|
+
*
|
|
49
|
+
* A `net` predicate's `urlContains` is frequently a filter rather than a URL —
|
|
50
|
+
* `category=vulnerability%2Csevere`, `projectId=storefront` — and with no `?` to split on, the
|
|
51
|
+
* whole thing survived as a "path" and was slugified into a subject. Measured on a real corpus:
|
|
52
|
+
* six of thirty-four subjects were query values, one record each, and together they made the
|
|
53
|
+
* coverage map read as a product with two dozen tiny unrelated areas.
|
|
54
|
+
*
|
|
55
|
+
* Skipped rather than aborting the walk, so a binding that asserts a filter AND a route still
|
|
56
|
+
* finds the route.
|
|
57
|
+
*/
|
|
58
|
+
if (part.includes('=') || part.includes('&'))
|
|
59
|
+
continue;
|
|
60
|
+
return part;
|
|
61
|
+
}
|
|
62
|
+
return undefined;
|
|
63
|
+
};
|
|
64
|
+
/** Walk a binding predicate tree for the first route or URL it mentions. */
|
|
65
|
+
const fromBinding = (binding) => {
|
|
66
|
+
if (typeof binding !== 'object' || null === binding)
|
|
67
|
+
return undefined;
|
|
68
|
+
const node = binding;
|
|
69
|
+
const direct = node['pathname'] ?? node['urlContains'] ?? node['path'];
|
|
70
|
+
if ('string' === typeof direct) {
|
|
71
|
+
const seg = firstSegment(direct);
|
|
72
|
+
if (seg !== undefined)
|
|
73
|
+
return seg;
|
|
74
|
+
}
|
|
75
|
+
const nested = node['predicates'] ?? node['of'];
|
|
76
|
+
if (Array.isArray(nested)) {
|
|
77
|
+
for (const child of nested) {
|
|
78
|
+
const found = fromBinding(child);
|
|
79
|
+
if (found !== undefined)
|
|
80
|
+
return found;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return undefined;
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* Directory names that group CODE rather than name a feature.
|
|
87
|
+
*
|
|
88
|
+
* `src/ui/input.tsx` is every screen's shared control; filing knowledge under `ui` would put
|
|
89
|
+
* sign-in, billing and issues in one bucket named after a folder. Walked past rather than accepted,
|
|
90
|
+
* and if every directory is generic the file yields nothing — which is honest, because it genuinely
|
|
91
|
+
* did not say what the record is about.
|
|
92
|
+
*/
|
|
93
|
+
const GENERIC_DIRS = new Set([
|
|
94
|
+
'src',
|
|
95
|
+
'app',
|
|
96
|
+
'apps',
|
|
97
|
+
'lib',
|
|
98
|
+
'libs',
|
|
99
|
+
'packages',
|
|
100
|
+
'components',
|
|
101
|
+
'ui',
|
|
102
|
+
'utils',
|
|
103
|
+
'util',
|
|
104
|
+
'helpers',
|
|
105
|
+
'shared',
|
|
106
|
+
'common',
|
|
107
|
+
'core',
|
|
108
|
+
'pages',
|
|
109
|
+
'features',
|
|
110
|
+
'modules',
|
|
111
|
+
'screens',
|
|
112
|
+
'views',
|
|
113
|
+
'test',
|
|
114
|
+
'tests',
|
|
115
|
+
'__tests__',
|
|
116
|
+
'dist',
|
|
117
|
+
'build',
|
|
118
|
+
'node_modules',
|
|
119
|
+
]);
|
|
120
|
+
/**
|
|
121
|
+
* The feature a file belongs to: the deepest directory that names something.
|
|
122
|
+
*
|
|
123
|
+
* A codebase already groups itself by feature, and the directory a file sits in is that grouping
|
|
124
|
+
* stated by the people who wrote it — structural evidence, not an inference from prose. Measured
|
|
125
|
+
* against the alternative before being written: 29 unsorted records carried a testid, and using the
|
|
126
|
+
* testid's prefix would have produced `confirm`, `new` and `delete` — verbs rather than areas.
|
|
127
|
+
*/
|
|
128
|
+
const featureOfFile = (file) => {
|
|
129
|
+
// Windows paths arrive from Windows machines; splitting on `/` alone leaves one long segment.
|
|
130
|
+
const parts = file.split(/[/\\]/).filter((p) => p !== '');
|
|
131
|
+
// The last part is the filename, never a feature.
|
|
132
|
+
for (let i = parts.length - 2; i >= 0; i--) {
|
|
133
|
+
const dir = parts[i];
|
|
134
|
+
if (dir === undefined || GENERIC_DIRS.has(dir.toLowerCase()))
|
|
135
|
+
continue;
|
|
136
|
+
return dir;
|
|
137
|
+
}
|
|
138
|
+
return undefined;
|
|
139
|
+
};
|
|
140
|
+
/**
|
|
141
|
+
* The subject for one intent, by the ladder above.
|
|
142
|
+
*
|
|
143
|
+
* Total: it always returns something, because a store that can refuse to place a record is a store
|
|
144
|
+
* that loses records.
|
|
145
|
+
*/
|
|
146
|
+
export const subjectFor = (evidence) => {
|
|
147
|
+
const explicit = evidence.subject;
|
|
148
|
+
if ('string' === typeof explicit && explicit.trim() !== '')
|
|
149
|
+
return slugifySubject(explicit);
|
|
150
|
+
const flow = evidence.surface?.flow;
|
|
151
|
+
if ('string' === typeof flow && flow.trim() !== '')
|
|
152
|
+
return slugifySubject(flow);
|
|
153
|
+
const route = evidence.surface?.route;
|
|
154
|
+
if ('string' === typeof route) {
|
|
155
|
+
const seg = firstSegment(route);
|
|
156
|
+
if (seg !== undefined)
|
|
157
|
+
return slugifySubject(seg);
|
|
158
|
+
}
|
|
159
|
+
/*
|
|
160
|
+
* The file, before the binding. A path names the feature the code lives in; a binding's API path
|
|
161
|
+
* names the domain only indirectly, and its element predicates name controls rather than areas.
|
|
162
|
+
*/
|
|
163
|
+
const files = evidence.surface?.files;
|
|
164
|
+
if (Array.isArray(files)) {
|
|
165
|
+
for (const file of files) {
|
|
166
|
+
if ('string' !== typeof file || '' === file)
|
|
167
|
+
continue;
|
|
168
|
+
const feature = featureOfFile(file);
|
|
169
|
+
if (feature !== undefined)
|
|
170
|
+
return slugifySubject(feature);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
const bound = fromBinding(evidence.binding);
|
|
174
|
+
if (bound !== undefined)
|
|
175
|
+
return slugifySubject(bound);
|
|
176
|
+
return UNSORTED_SUBJECT;
|
|
177
|
+
};
|