@reticlehq/server 3.3.0 → 3.5.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/LICENSE-ENTERPRISE +1 -1
- package/README.md +3 -3
- package/SKILL.md +12 -14
- package/dist/chromium-launch-options.d.ts +4 -3
- package/dist/chromium-launch-options.js +6 -2
- package/dist/command/cli/adhoc-suite.d.ts +22 -0
- package/dist/command/cli/adhoc-suite.js +90 -19
- package/dist/command/cli/adhoc-verdict.d.ts +35 -0
- package/dist/command/cli/adhoc-verdict.js +90 -3
- package/dist/command/cli/answers/gate-hook-message.d.ts +6 -0
- package/dist/command/cli/answers/gate-hook-message.js +6 -0
- package/dist/command/cli/auth/cloud-keystore.d.ts +0 -2
- package/dist/command/cli/auth/cloud-keystore.js +0 -3
- package/dist/command/cli/auth/cloud-session.d.ts +6 -2
- package/dist/command/cli/auth/cloud-session.js +2 -0
- package/dist/command/cli/auth/device-flow.d.ts +2 -2
- package/dist/command/cli/cli-doctor.js +7 -5
- package/dist/command/cli/cli-flow-commands.d.ts +3 -6
- package/dist/command/cli/cli-flow-commands.js +67 -4
- package/dist/command/cli/cli-parse.d.ts +8 -1
- package/dist/command/cli/cli-parse.js +24 -4
- package/dist/command/cli/cli-update-commands.js +8 -1
- package/dist/command/cli/cli-verify.d.ts +24 -3
- package/dist/command/cli/cli-verify.js +54 -11
- package/dist/command/cli/cloud-cli.js +64 -2
- package/dist/command/cli/cloud-kit.js +3 -5
- package/dist/command/cli/cloud-login.d.ts +1 -1
- package/dist/command/cli/cloud-login.js +27 -8
- package/dist/command/cli/demo/demo-page.js +6 -3
- package/dist/command/cli/doctor/browser/chromium-hint.d.ts +5 -0
- package/dist/command/cli/doctor/browser/chromium-hint.js +5 -0
- package/dist/command/cli/launch/cli-launch.d.ts +8 -0
- package/dist/command/cli/launch/cli-launch.js +21 -9
- package/dist/command/cli/launch/sdk-version-match.d.ts +84 -0
- package/dist/command/cli/launch/sdk-version-match.js +206 -0
- package/dist/command/cli/lifecycle/daemon-lifecycle.js +2 -16
- package/dist/command/cli/ports/resolve/cli-port.d.ts +25 -0
- package/dist/command/cli/ports/resolve/cli-port.js +125 -15
- package/dist/command/cli/setup-mcp-cli.js +21 -13
- package/dist/command/cli/tutorial.d.ts +2 -2
- package/dist/command/cli/tutorial.js +21 -7
- package/dist/command/cli.js +79 -11
- package/dist/command/daemon/binding/daemon-status-probe.d.ts +14 -2
- package/dist/command/daemon/binding/daemon-status-probe.js +34 -6
- package/dist/command/daemon/daemon-resolve.d.ts +9 -0
- package/dist/command/daemon/daemon-resolve.js +1 -1
- package/dist/command/daemon/lifetime/daemon-usefulness.d.ts +7 -1
- package/dist/command/daemon/lifetime/daemon-usefulness.js +30 -7
- package/dist/command/dev/stale-issue-guard.d.ts +3 -4
- package/dist/command/dev/stale-issue-guard.js +8 -8
- package/dist/command/setup/bringup/connect-lease.d.ts +17 -0
- package/dist/command/setup/bringup/connect-lease.js +38 -0
- package/dist/command/setup/bringup/dev-server-files.d.ts +68 -0
- package/dist/command/setup/bringup/dev-server-files.js +131 -0
- package/dist/command/setup/bringup/dev-server-log-cap.d.ts +35 -0
- package/dist/command/setup/bringup/dev-server-log-cap.js +121 -0
- package/dist/command/setup/bringup/dev-server-wait.d.ts +2 -0
- package/dist/command/setup/bringup/dev-server-wait.js +1 -1
- package/dist/command/setup/bringup/ensure-daemon.d.ts +65 -4
- package/dist/command/setup/bringup/ensure-daemon.js +135 -16
- package/dist/command/setup/bringup/static-page-server.d.ts +35 -0
- package/dist/command/setup/bringup/static-page-server.js +151 -0
- package/dist/command/setup/desktop-shape.d.ts +4 -0
- package/dist/command/setup/desktop-shape.js +18 -2
- package/dist/command/setup/init/init-port.d.ts +15 -0
- package/dist/command/setup/init/init-port.js +44 -0
- package/dist/command/setup/init/init-runtime.d.ts +1 -1
- package/dist/command/setup/init/init-runtime.js +91 -9
- package/dist/command/setup/node-effects.d.ts +27 -14
- package/dist/command/setup/node-effects.js +86 -45
- package/dist/command/setup/probe/loopback-probe-urls.d.ts +7 -0
- package/dist/command/setup/probe/loopback-probe-urls.js +40 -0
- package/dist/command/setup/probe/page-probe.d.ts +13 -2
- package/dist/command/setup/probe/page-probe.js +18 -3
- package/dist/command/setup/run-setup.d.ts +28 -1
- package/dist/command/setup/run-setup.js +103 -15
- package/dist/command/setup/session-pick.d.ts +13 -0
- package/dist/command/setup/session-pick.js +20 -0
- package/dist/command/setup/setup-command.d.ts +2 -14
- package/dist/command/setup/setup-command.js +105 -31
- package/dist/command/setup/setup-mcp.d.ts +2 -0
- package/dist/command/setup/setup-mcp.js +17 -8
- package/dist/command/setup/terminal/wait-progress.d.ts +8 -0
- package/dist/command/setup/terminal/wait-progress.js +17 -0
- package/dist/command/version/peer-announce.d.ts +6 -2
- package/dist/command/version/peer-announce.js +8 -6
- package/dist/command/version/version-nudge.d.ts +17 -3
- package/dist/command/version/version-nudge.js +25 -4
- package/dist/features/crawl/crawl-empty.js +1 -1
- package/dist/features/crawl/crawl-tools.d.ts +6 -0
- package/dist/features/crawl/crawl-tools.js +57 -0
- package/dist/features/crawl/crawl.js +171 -157
- package/dist/features/exhaust/app-coverage.d.ts +33 -0
- package/dist/features/exhaust/app-coverage.js +31 -0
- package/dist/features/exhaust/code-coverage.d.ts +47 -0
- package/dist/features/exhaust/code-coverage.js +77 -0
- package/dist/features/exhaust/exhaust-run.d.ts +33 -0
- package/dist/features/exhaust/exhaust-run.js +82 -0
- package/dist/features/exhaust/explorer.d.ts +103 -0
- package/dist/features/exhaust/explorer.js +195 -0
- package/dist/features/exhaust/failure-branches.d.ts +33 -0
- package/dist/features/exhaust/failure-branches.js +92 -0
- package/dist/features/exhaust/ledger.d.ts +202 -0
- package/dist/features/exhaust/ledger.js +252 -0
- package/dist/features/exhaust/session-fold.d.ts +31 -0
- package/dist/features/exhaust/session-fold.js +79 -0
- package/dist/features/exhaust/session-port.d.ts +36 -0
- package/dist/features/exhaust/session-port.js +102 -0
- package/dist/features/harness/drive-report.d.ts +0 -9
- package/dist/features/harness/drive-report.js +18 -0
- package/dist/features/harness/driver.js +4 -7
- package/dist/features/harness/fill-values.d.ts +10 -0
- package/dist/features/harness/fill-values.js +50 -11
- package/dist/features/harness/jev-driver.js +30 -13
- package/dist/features/harness/openai-driver.d.ts +12 -0
- package/dist/features/harness/openai-driver.js +21 -12
- package/dist/features/harness/platform-config.js +5 -6
- package/dist/features/license/license.d.ts +2 -2
- package/dist/index.d.ts +9 -0
- package/dist/index.js +56 -19
- package/dist/judgement/capsule/capsule-store.d.ts +4 -4
- package/dist/judgement/domain/domain-model.js +3 -1
- package/dist/judgement/oracles/self-instrument.js +4 -2
- package/dist/judgement/runs/artifact/replay-mapping.d.ts +2 -2
- package/dist/judgement/runs/artifact/replay-mapping.js +44 -9
- package/dist/judgement/runs/verification-sync.d.ts +3 -1
- package/dist/judgement/runs/verification-sync.js +1 -1
- package/dist/language/flows/change/affected.d.ts +9 -0
- package/dist/language/flows/change/affected.js +35 -0
- package/dist/language/flows/change/gate.d.ts +20 -0
- package/dist/language/flows/change/gate.js +12 -1
- package/dist/language/flows/flow-author.d.ts +7 -0
- package/dist/language/flows/flow-author.js +19 -0
- package/dist/language/flows/flow-expect-grammar.js +1 -1
- package/dist/language/flows/flow-journey.d.ts +54 -0
- package/dist/language/flows/flow-journey.js +119 -0
- package/dist/language/flows/flow-replay-run.d.ts +2 -3
- package/dist/language/flows/flow-replay-run.js +13 -47
- package/dist/language/flows/flow-replay.d.ts +22 -8
- package/dist/language/flows/flow-replay.js +55 -9
- package/dist/language/flows/flow-result.d.ts +2 -0
- package/dist/language/flows/flow-result.js +25 -0
- package/dist/language/flows/flow-tools.js +17 -5
- package/dist/language/flows/flows.d.ts +10 -1
- package/dist/language/flows/flows.js +90 -14
- package/dist/language/flows/recording/replay-halt.js +3 -1
- package/dist/language/flows/recording/tape/recordings.d.ts +30 -1
- package/dist/language/flows/recording/tape/recordings.js +55 -1
- package/dist/language/flows/replay.d.ts +19 -0
- package/dist/language/flows/replay.js +42 -6
- package/dist/language/flows/verify-change-tools.js +5 -2
- package/dist/launch-chromium.d.ts +80 -0
- package/dist/launch-chromium.js +218 -0
- package/dist/memory/cloud/cloud-config.d.ts +11 -2
- package/dist/memory/cloud/cloud-config.js +47 -7
- package/dist/memory/cloud/cloud-sync.d.ts +4 -1
- package/dist/memory/cloud/cloud-sync.js +6 -9
- package/dist/memory/cloud/harness-offer.js +5 -6
- package/dist/memory/cloud/harness-switch.js +5 -6
- package/dist/memory/cloud/sync-cycle.d.ts +77 -3
- package/dist/memory/cloud/sync-cycle.js +325 -55
- package/dist/memory/cloud/sync-daemon.js +24 -20
- package/dist/memory/cloud/sync-disk.js +16 -2
- package/dist/memory/intent/intent-shard.d.ts +10 -32
- package/dist/memory/intent/intent-shard.js +0 -10
- package/dist/memory/intent/intent-tools.js +8 -3
- package/dist/memory/journal/drive-flow.d.ts +18 -13
- package/dist/memory/journal/drive-flow.js +45 -37
- package/dist/memory/journal/envelope.d.ts +8 -8
- package/dist/memory/journal/journal-query.d.ts +14 -0
- package/dist/memory/journal/journal-query.js +15 -0
- package/dist/memory/journal/on-disk/workspace-tiers.js +2 -0
- package/dist/memory/journal/session-end.d.ts +9 -1
- package/dist/memory/journal/session-end.js +24 -12
- package/dist/memory/project/artifact-root-resolver.d.ts +10 -0
- package/dist/memory/project/artifact-root-resolver.js +46 -19
- package/dist/memory/project/dir/reticle-dir.d.ts +2 -0
- package/dist/memory/project/dir/reticle-dir.js +1 -0
- package/dist/memory/project/project-tools.js +3 -1
- package/dist/portal/bridge/auth-failure-reason.d.ts +1 -1
- package/dist/portal/bridge/auth-failure-reason.js +4 -2
- package/dist/portal/bridge/bridge.d.ts +12 -3
- package/dist/portal/bridge/bridge.js +49 -3
- package/dist/portal/input/js-coverage.d.ts +25 -0
- package/dist/portal/input/js-coverage.js +30 -0
- package/dist/portal/input/real-input.d.ts +11 -0
- package/dist/portal/input/real-input.js +29 -4
- package/dist/portal/input/viewport-tools.js +16 -6
- package/dist/portal/pool/playwright-launcher.d.ts +6 -0
- package/dist/portal/pool/playwright-launcher.js +20 -13
- package/dist/portal/session/dev-server/dev-command.d.ts +2 -2
- package/dist/portal/session/dev-server/dev-command.js +30 -12
- package/dist/portal/session/dev-server/dev-server-probe.js +17 -1
- package/dist/portal/session/dev-server/route-status-probe.d.ts +24 -0
- package/dist/portal/session/dev-server/route-status-probe.js +57 -0
- package/dist/portal/session/facts/observed-state.d.ts +11 -0
- package/dist/portal/session/facts/observed-state.js +34 -0
- package/dist/portal/session/no-session-diagnosis.d.ts +29 -0
- package/dist/portal/session/no-session-diagnosis.js +62 -5
- package/dist/portal/session/no-session-next-action.d.ts +13 -0
- package/dist/portal/session/no-session-next-action.js +48 -9
- package/dist/portal/session/no-session-watch.d.ts +18 -0
- package/dist/portal/session/no-session-watch.js +88 -2
- package/dist/portal/session/session-health.js +3 -2
- package/dist/portal/session/session-manager.d.ts +20 -6
- package/dist/portal/session/session-manager.js +19 -3
- package/dist/portal/session/session-reaper.d.ts +28 -0
- package/dist/portal/session/session-reaper.js +49 -1
- package/dist/portal/session/session.d.ts +10 -8
- package/dist/portal/session/session.js +24 -32
- package/dist/surface/http-server.d.ts +3 -2
- package/dist/surface/http-server.js +8 -5
- package/dist/surface/mcp/mcp-proxy.d.ts +2 -2
- package/dist/surface/mcp/mcp-proxy.js +2 -2
- package/dist/surface/mcp/mcp.js +5 -3
- package/dist/surface/mcp/proxy/proxy-daemon-probe.d.ts +13 -1
- package/dist/surface/mcp/proxy/proxy-daemon-probe.js +27 -2
- package/dist/surface/mcp/proxy/proxy-log.js +3 -5
- package/dist/surface/mcp/server-instructions.js +9 -7
- package/dist/surface/tools/act/after-match.d.ts +17 -0
- package/dist/surface/tools/act/after-match.js +30 -0
- package/dist/surface/tools/act/durable.d.ts +69 -0
- package/dist/surface/tools/act/durable.js +139 -0
- package/dist/surface/tools/act/navigation/navigate-arrival.d.ts +13 -6
- package/dist/surface/tools/act/navigation/navigate-arrival.js +47 -10
- package/dist/surface/tools/act/navigation/navigate-result.d.ts +1 -12
- package/dist/surface/tools/act/navigation/navigate-result.js +21 -3
- package/dist/surface/tools/act/proved-controls.d.ts +9 -0
- package/dist/surface/tools/act/proved-controls.js +11 -0
- package/dist/surface/tools/act/sequence-grade.d.ts +5 -0
- package/dist/surface/tools/act/sequence-grade.js +32 -1
- package/dist/surface/tools/act-capsule.d.ts +7 -0
- package/dist/surface/tools/act-capsule.js +46 -1
- package/dist/surface/tools/act-sequence-tool.js +25 -8
- package/dist/surface/tools/act-tools.js +28 -52
- package/dist/surface/tools/assert/assert-grade.js +12 -1
- package/dist/surface/tools/assert/assert-verdict.js +2 -2
- package/dist/surface/tools/browser-tools.js +14 -2
- package/dist/surface/tools/coverage-tools.js +27 -1
- package/dist/surface/tools/dynamic-tools.js +29 -2
- package/dist/surface/tools/error-recovery.js +5 -6
- package/dist/surface/tools/feedback-invite.js +6 -4
- package/dist/surface/tools/harness-explore.d.ts +3 -2
- package/dist/surface/tools/harness-explore.js +12 -7
- package/dist/surface/tools/invoke-tool.js +12 -8
- package/dist/surface/tools/lease-tools.js +15 -6
- package/dist/surface/tools/live-call-text.js +4 -1
- package/dist/surface/tools/observe-tools.js +9 -1
- package/dist/surface/tools/tool-kit.d.ts +10 -0
- package/dist/surface/tools/tools.js +4 -0
- package/dist/telemetry/argument-shape.d.ts +0 -2
- package/dist/telemetry/argument-shape.js +1 -5
- package/dist/telemetry/error-class.d.ts +2 -3
- package/dist/telemetry/error-class.js +2 -3
- package/dist/telemetry/harness-drive.d.ts +0 -2
- package/dist/telemetry/harness-drive.js +0 -4
- package/dist/telemetry/onboarding-firsts.d.ts +2 -2
- package/dist/telemetry/onboarding-firsts.js +2 -2
- package/dist/wire-journal.d.ts +3 -0
- package/dist/wire-journal.js +4 -0
- package/dist/workspace-packages.d.ts +0 -2
- package/dist/workspace-packages.js +1 -9
- package/docs/README.md +4 -2
- package/docs/actions.mdx +3 -4
- package/docs/agent-cheatsheet.md +4 -4
- package/docs/architecture.md +6 -6
- package/docs/best-practices.mdx +1 -1
- package/docs/capabilities.mdx +32 -1
- package/docs/cli/cloud.mdx +20 -8
- package/docs/cli/doctor.mdx +4 -0
- package/docs/cli/feedback.mdx +2 -2
- package/docs/cli/gate.mdx +9 -4
- package/docs/cli/update.mdx +10 -0
- package/docs/cli.mdx +22 -7
- package/docs/desktop.mdx +14 -10
- package/docs/environment.mdx +87 -0
- package/docs/faq.mdx +18 -16
- package/docs/first-drive.md +5 -13
- package/docs/flows.md +21 -2
- package/docs/frameworks.mdx +29 -10
- package/docs/getting-started.md +15 -5
- package/docs/http-transport.md +6 -3
- package/docs/human-control.md +2 -2
- package/docs/index.mdx +16 -6
- package/docs/install-agentic.mdx +2 -2
- package/docs/install-manual.mdx +1 -1
- package/docs/integration-patterns.md +3 -3
- package/docs/multi-agent-testing.md +1 -1
- package/docs/packages/babel-plugin.mdx +1 -1
- package/docs/packages/browser.mdx +2 -2
- package/docs/packages/next.mdx +3 -0
- package/docs/packages/react.mdx +2 -2
- package/docs/packages/tauri.mdx +1 -1
- package/docs/packages/vite-plugin.mdx +4 -2
- package/docs/packages.mdx +9 -9
- package/docs/predicates.mdx +50 -5
- package/docs/quickstart.mdx +12 -10
- package/docs/recipes.mdx +2 -2
- package/docs/state-management.mdx +2 -2
- package/docs/system-map.md +1 -1
- package/docs/telemetry.md +1 -1
- package/docs/token-efficiency.md +3 -3
- package/docs/tools/act-and-wait.mdx +1 -0
- package/docs/tools/act-sequence.mdx +1 -1
- package/docs/tools/act.mdx +1 -1
- package/docs/tools/console.mdx +1 -1
- package/docs/tools/inspect.mdx +1 -1
- package/docs/tools/network.mdx +1 -1
- package/docs/tools/query.mdx +1 -1
- package/docs/tools/sessions.mdx +1 -1
- package/docs/tools/snapshot.mdx +1 -1
- package/docs/tools/state.mdx +1 -1
- package/docs/tools/wait-for.mdx +1 -1
- package/docs/usage.md +7 -7
- package/docs/use-cases.mdx +157 -0
- package/docs/vs-screenshots.mdx +3 -3
- package/docs/what-is-recorded.md +2 -2
- package/docs/what-is-reticle.mdx +17 -20
- package/docs/why-reticle.mdx +14 -21
- package/package.json +10 -9
- package/dist/judgement/capsule/minimize.d.ts +0 -12
- package/dist/judgement/capsule/minimize.js +0 -22
- package/dist/judgement/phenomena/phenomena.d.ts +0 -27
- package/dist/judgement/phenomena/phenomena.js +0 -101
- package/dist/language/flows/flow-report.d.ts +0 -22
- package/dist/language/flows/flow-report.js +0 -106
|
@@ -21,7 +21,6 @@
|
|
|
21
21
|
*/
|
|
22
22
|
import { z } from 'zod';
|
|
23
23
|
import { type Intent } from '@reticlehq/core/artifacts';
|
|
24
|
-
import type { FileSystemPort } from '../project/fs/fs-port.js';
|
|
25
24
|
/**
|
|
26
25
|
* How settled a record is.
|
|
27
26
|
*
|
|
@@ -52,12 +51,12 @@ declare const IntentRecordSchema: z.ZodObject<{
|
|
|
52
51
|
flow: z.ZodOptional<z.ZodString>;
|
|
53
52
|
files: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
54
53
|
}, "strip", z.ZodTypeAny, {
|
|
55
|
-
flow?: string | undefined;
|
|
56
54
|
route?: string | undefined;
|
|
55
|
+
flow?: string | undefined;
|
|
57
56
|
files?: string[] | undefined;
|
|
58
57
|
}, {
|
|
59
|
-
flow?: string | undefined;
|
|
60
58
|
route?: string | undefined;
|
|
59
|
+
flow?: string | undefined;
|
|
61
60
|
files?: string[] | undefined;
|
|
62
61
|
}>>;
|
|
63
62
|
provenBy: z.ZodOptional<z.ZodObject<{
|
|
@@ -101,8 +100,8 @@ declare const IntentRecordSchema: z.ZodObject<{
|
|
|
101
100
|
statement: string;
|
|
102
101
|
declaredAt: number;
|
|
103
102
|
surface?: {
|
|
104
|
-
flow?: string | undefined;
|
|
105
103
|
route?: string | undefined;
|
|
104
|
+
flow?: string | undefined;
|
|
106
105
|
files?: string[] | undefined;
|
|
107
106
|
} | undefined;
|
|
108
107
|
source?: string | undefined;
|
|
@@ -126,8 +125,8 @@ declare const IntentRecordSchema: z.ZodObject<{
|
|
|
126
125
|
statement: string;
|
|
127
126
|
declaredAt: number;
|
|
128
127
|
surface?: {
|
|
129
|
-
flow?: string | undefined;
|
|
130
128
|
route?: string | undefined;
|
|
129
|
+
flow?: string | undefined;
|
|
131
130
|
files?: string[] | undefined;
|
|
132
131
|
} | undefined;
|
|
133
132
|
source?: string | undefined;
|
|
@@ -159,12 +158,12 @@ export declare const IntentShardSchema: z.ZodObject<{
|
|
|
159
158
|
flow: z.ZodOptional<z.ZodString>;
|
|
160
159
|
files: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
161
160
|
}, "strip", z.ZodTypeAny, {
|
|
162
|
-
flow?: string | undefined;
|
|
163
161
|
route?: string | undefined;
|
|
162
|
+
flow?: string | undefined;
|
|
164
163
|
files?: string[] | undefined;
|
|
165
164
|
}, {
|
|
166
|
-
flow?: string | undefined;
|
|
167
165
|
route?: string | undefined;
|
|
166
|
+
flow?: string | undefined;
|
|
168
167
|
files?: string[] | undefined;
|
|
169
168
|
}>>;
|
|
170
169
|
provenBy: z.ZodOptional<z.ZodObject<{
|
|
@@ -208,8 +207,8 @@ export declare const IntentShardSchema: z.ZodObject<{
|
|
|
208
207
|
statement: string;
|
|
209
208
|
declaredAt: number;
|
|
210
209
|
surface?: {
|
|
211
|
-
flow?: string | undefined;
|
|
212
210
|
route?: string | undefined;
|
|
211
|
+
flow?: string | undefined;
|
|
213
212
|
files?: string[] | undefined;
|
|
214
213
|
} | undefined;
|
|
215
214
|
source?: string | undefined;
|
|
@@ -233,8 +232,8 @@ export declare const IntentShardSchema: z.ZodObject<{
|
|
|
233
232
|
statement: string;
|
|
234
233
|
declaredAt: number;
|
|
235
234
|
surface?: {
|
|
236
|
-
flow?: string | undefined;
|
|
237
235
|
route?: string | undefined;
|
|
236
|
+
flow?: string | undefined;
|
|
238
237
|
files?: string[] | undefined;
|
|
239
238
|
} | undefined;
|
|
240
239
|
source?: string | undefined;
|
|
@@ -262,8 +261,8 @@ export declare const IntentShardSchema: z.ZodObject<{
|
|
|
262
261
|
statement: string;
|
|
263
262
|
declaredAt: number;
|
|
264
263
|
surface?: {
|
|
265
|
-
flow?: string | undefined;
|
|
266
264
|
route?: string | undefined;
|
|
265
|
+
flow?: string | undefined;
|
|
267
266
|
files?: string[] | undefined;
|
|
268
267
|
} | undefined;
|
|
269
268
|
source?: string | undefined;
|
|
@@ -291,8 +290,8 @@ export declare const IntentShardSchema: z.ZodObject<{
|
|
|
291
290
|
statement: string;
|
|
292
291
|
declaredAt: number;
|
|
293
292
|
surface?: {
|
|
294
|
-
flow?: string | undefined;
|
|
295
293
|
route?: string | undefined;
|
|
294
|
+
flow?: string | undefined;
|
|
296
295
|
files?: string[] | undefined;
|
|
297
296
|
} | undefined;
|
|
298
297
|
source?: string | undefined;
|
|
@@ -311,24 +310,6 @@ export declare const IntentShardSchema: z.ZodObject<{
|
|
|
311
310
|
}>;
|
|
312
311
|
}>;
|
|
313
312
|
export type IntentShard = z.infer<typeof IntentShardSchema>;
|
|
314
|
-
/** One line per intent: enough to decide whether to open the shard, and no more. */
|
|
315
|
-
declare const IntentIndexEntrySchema: z.ZodObject<{
|
|
316
|
-
id: z.ZodString;
|
|
317
|
-
subject: z.ZodString;
|
|
318
|
-
statement: z.ZodString;
|
|
319
|
-
status: z.ZodEnum<["proposed", "agreed", "proved", "stale"]>;
|
|
320
|
-
}, "strip", z.ZodTypeAny, {
|
|
321
|
-
status: "proved" | "proposed" | "agreed" | "stale";
|
|
322
|
-
id: string;
|
|
323
|
-
subject: string;
|
|
324
|
-
statement: string;
|
|
325
|
-
}, {
|
|
326
|
-
status: "proved" | "proposed" | "agreed" | "stale";
|
|
327
|
-
id: string;
|
|
328
|
-
subject: string;
|
|
329
|
-
statement: string;
|
|
330
|
-
}>;
|
|
331
|
-
export type IntentIndexEntry = z.infer<typeof IntentIndexEntrySchema>;
|
|
332
313
|
export declare const IntentIndexSchema: z.ZodObject<{
|
|
333
314
|
version: z.ZodLiteral<1>;
|
|
334
315
|
entries: z.ZodArray<z.ZodObject<{
|
|
@@ -366,7 +347,6 @@ export declare const IntentIndexSchema: z.ZodObject<{
|
|
|
366
347
|
}>;
|
|
367
348
|
export type IntentIndex = z.infer<typeof IntentIndexSchema>;
|
|
368
349
|
export declare const emptyShard: (subject: string) => IntentShard;
|
|
369
|
-
export declare const emptyIndex: () => IntentIndex;
|
|
370
350
|
/** The verification lifecycle a legacy record carried, mapped onto the settledness axis. */
|
|
371
351
|
export declare const statusFromState: (state: Intent["state"]) => IntentStatus;
|
|
372
352
|
/**
|
|
@@ -383,6 +363,4 @@ export declare const indexFrom: (shards: readonly IntentShard[]) => IntentIndex;
|
|
|
383
363
|
export declare const shardsFrom: (records: readonly IntentRecord[]) => IntentShard[];
|
|
384
364
|
/** Serialise byte-stably, with keys sorted so a re-save of unchanged data is a no-op diff. */
|
|
385
365
|
export declare const serialise: (value: unknown) => string;
|
|
386
|
-
/** Read + validate one JSON file, failing soft: a hand-merged file with a conflict marker is real. */
|
|
387
|
-
export declare const readJsonFile: <T>(fs: FileSystemPort, path: string, parse: (raw: unknown) => T, fallback: T) => Promise<T>;
|
|
388
366
|
export {};
|
|
@@ -96,7 +96,6 @@ export const emptyShard = (subject) => ({
|
|
|
96
96
|
subject,
|
|
97
97
|
intents: {},
|
|
98
98
|
});
|
|
99
|
-
export const emptyIndex = () => ({ version: INTENT_SHARD_VERSION, entries: [] });
|
|
100
99
|
/** The verification lifecycle a legacy record carried, mapped onto the settledness axis. */
|
|
101
100
|
export const statusFromState = (state) => 'proved' === state ? IntentStatus.PROVED : IntentStatus.AGREED;
|
|
102
101
|
/**
|
|
@@ -142,12 +141,3 @@ const sortKeys = (value) => {
|
|
|
142
141
|
return value;
|
|
143
142
|
return Object.fromEntries(Object.entries(value).sort(([a], [b]) => a.localeCompare(b)));
|
|
144
143
|
};
|
|
145
|
-
/** Read + validate one JSON file, failing soft: a hand-merged file with a conflict marker is real. */
|
|
146
|
-
export const readJsonFile = async (fs, path, parse, fallback) => {
|
|
147
|
-
try {
|
|
148
|
-
return parse(JSON.parse(await fs.readFile(path)));
|
|
149
|
-
}
|
|
150
|
-
catch {
|
|
151
|
-
return fallback;
|
|
152
|
-
}
|
|
153
|
-
};
|
|
@@ -70,6 +70,8 @@ export const INTENT_TOOLS = [
|
|
|
70
70
|
})
|
|
71
71
|
.optional(),
|
|
72
72
|
}))
|
|
73
|
+
// Non-empty in the schema too, so the advertised contract says what `declare` enforces.
|
|
74
|
+
.min(1)
|
|
73
75
|
.optional()
|
|
74
76
|
.describe('declare only. Batchable — declare every intent for a feature in one call.'),
|
|
75
77
|
id: z.string().optional().describe('bind only: which intent the predicate proves.'),
|
|
@@ -153,9 +155,12 @@ export const INTENT_TOOLS = [
|
|
|
153
155
|
return { intents: await store.open() };
|
|
154
156
|
}
|
|
155
157
|
const raw = args['intents'];
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
158
|
+
// Refused, not read as an empty list: `declare` with nothing to declare stored nothing and
|
|
159
|
+
// answered `{ intents: [] }`, which an agent reads as a declaration that worked (#1118).
|
|
160
|
+
if (!Array.isArray(raw) || 0 === raw.length) {
|
|
161
|
+
throw new Error(`${ReticleTool.INTENT} declare: \`intents\` must be a non-empty array of { id, statement }, got ${raw === undefined ? 'nothing' : JSON.stringify(raw)} — nothing was stored`);
|
|
162
|
+
}
|
|
163
|
+
const entries = raw;
|
|
159
164
|
const declared = await store.declare(entries);
|
|
160
165
|
// Said, not swallowed: an agent that declared something and finds nothing stored would
|
|
161
166
|
// otherwise conclude the ledger lost it.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { FlowName, Predicate } from '@reticlehq/core';
|
|
1
|
+
import type { FlowFile, FlowName, Predicate } from '@reticlehq/core';
|
|
2
2
|
/**
|
|
3
3
|
* What this module needs a recorded step to BE, named structurally rather than imported.
|
|
4
4
|
*
|
|
@@ -15,6 +15,10 @@ export interface TapeStep {
|
|
|
15
15
|
expect?: Predicate;
|
|
16
16
|
/** Recorder-internal, and the field journeys are cut on. Never written to the saved flow. */
|
|
17
17
|
route?: string;
|
|
18
|
+
page?: string;
|
|
19
|
+
endPage?: string;
|
|
20
|
+
/** The intent the agent declared on this action. The first one names the journey. */
|
|
21
|
+
intent?: string;
|
|
18
22
|
}
|
|
19
23
|
/** The compiled shape a flow store accepts. Structural, for the same reason as `TapeStep`. */
|
|
20
24
|
export interface DriveProgram {
|
|
@@ -22,6 +26,7 @@ export interface DriveProgram {
|
|
|
22
26
|
version: number;
|
|
23
27
|
steps: TapeStep[];
|
|
24
28
|
startPath?: string;
|
|
29
|
+
author?: FlowFile['author'];
|
|
25
30
|
}
|
|
26
31
|
/**
|
|
27
32
|
* What a session drove, saved as a replayable flow — without anybody asking for it.
|
|
@@ -36,21 +41,21 @@ export interface DriveProgram {
|
|
|
36
41
|
*/
|
|
37
42
|
/** The one condition on saving, and the reason the whole thing is safe. */
|
|
38
43
|
export declare function carriesAnAssertion(steps: readonly TapeStep[]): boolean;
|
|
44
|
+
/** Every auto-saved flow's name starts here, which is what lets a merge tell them from hand-named ones. */
|
|
45
|
+
export declare const DRIVE_FLOW_PREFIX = "drive-";
|
|
39
46
|
/**
|
|
40
|
-
* A
|
|
47
|
+
* A readable name for a journey, led by WHY it was driven.
|
|
41
48
|
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
* because `presenter-controls.ts` renders it straight into the replay button's `textContent` — so
|
|
46
|
-
* the label a person actually sees was `drive-sdc991872-6d66-4adf-8780-f931c62905f9`, twice, for two
|
|
47
|
-
* different journeys. A session id is a fact about the socket, not about the feature.
|
|
49
|
+
* The agent's own `intent` on the action is the best name there is — it is the sentence a person
|
|
50
|
+
* would use — so it leads when one was declared. Without one, what the journey proved names it (the
|
|
51
|
+
* strongest claim first), and the route follows so two journeys stay apart.
|
|
48
52
|
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
53
|
+
* No session id. It used to trail every name so two sessions never overwrote each other, which also
|
|
54
|
+
* made every session's drive of the same journey its own file. Two DIFFERENT journeys that land on
|
|
55
|
+
* one name are now told apart at save by their shape, and two copies of the SAME journey merge —
|
|
56
|
+
* see FlowStore.saveJourney.
|
|
52
57
|
*/
|
|
53
|
-
export declare function driveFlowName(
|
|
58
|
+
export declare function driveFlowName(route?: string, steps?: readonly TapeStep[]): FlowName;
|
|
54
59
|
export interface DriveFlowOutcome {
|
|
55
60
|
/** The flows that were written — one per journey the session contained. */
|
|
56
61
|
readonly saved?: readonly string[];
|
|
@@ -65,7 +70,7 @@ export interface DriveFlowOutcome {
|
|
|
65
70
|
readonly unprovenSteps?: number;
|
|
66
71
|
}
|
|
67
72
|
/** A flow per journey, so two journeys in one session are two regression tests rather than one. */
|
|
68
|
-
export declare function driveFlowsFrom(
|
|
73
|
+
export declare function driveFlowsFrom(tape: {
|
|
69
74
|
steps: readonly TapeStep[];
|
|
70
75
|
startPath?: string;
|
|
71
76
|
} | undefined): {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PredicateKind, REPLAY_PROGRAM_VERSION, asFlowName } from '@reticlehq/core';
|
|
1
|
+
import { ActionType, PredicateKind, QueryBy, REPLAY_PROGRAM_VERSION, asFlowName, defaultIsSensitiveKey, } from '@reticlehq/core';
|
|
2
2
|
import { clauseOfKind } from '@reticlehq/core';
|
|
3
3
|
/**
|
|
4
4
|
* What a session drove, saved as a replayable flow — without anybody asking for it.
|
|
@@ -30,7 +30,7 @@ const segment = (value, max) => value
|
|
|
30
30
|
* DOM-shaped claims are the weakest. Naming a flow after the strongest thing it proves is the same
|
|
31
31
|
* judgement the honesty grade makes, reused where a human reads it.
|
|
32
32
|
*
|
|
33
|
-
* Returns '' when a step claims nothing nameable
|
|
33
|
+
* Returns '' when a step claims nothing nameable.
|
|
34
34
|
*/
|
|
35
35
|
function provedSlug(steps) {
|
|
36
36
|
for (const step of steps) {
|
|
@@ -63,42 +63,29 @@ function provedSlug(steps) {
|
|
|
63
63
|
}
|
|
64
64
|
return '';
|
|
65
65
|
}
|
|
66
|
+
/** Every auto-saved flow's name starts here, which is what lets a merge tell them from hand-named ones. */
|
|
67
|
+
export const DRIVE_FLOW_PREFIX = 'drive-';
|
|
66
68
|
/**
|
|
67
|
-
* A
|
|
69
|
+
* A readable name for a journey, led by WHY it was driven.
|
|
68
70
|
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
* because `presenter-controls.ts` renders it straight into the replay button's `textContent` — so
|
|
73
|
-
* the label a person actually sees was `drive-sdc991872-6d66-4adf-8780-f931c62905f9`, twice, for two
|
|
74
|
-
* different journeys. A session id is a fact about the socket, not about the feature.
|
|
71
|
+
* The agent's own `intent` on the action is the best name there is — it is the sentence a person
|
|
72
|
+
* would use — so it leads when one was declared. Without one, what the journey proved names it (the
|
|
73
|
+
* strongest claim first), and the route follows so two journeys stay apart.
|
|
75
74
|
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
75
|
+
* No session id. It used to trail every name so two sessions never overwrote each other, which also
|
|
76
|
+
* made every session's drive of the same journey its own file. Two DIFFERENT journeys that land on
|
|
77
|
+
* one name are now told apart at save by their shape, and two copies of the SAME journey merge —
|
|
78
|
+
* see FlowStore.saveJourney.
|
|
79
79
|
*/
|
|
80
|
-
export function driveFlowName(
|
|
81
|
-
const
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
// Minted here, not at the call site. This function is where a drive's flow name comes into
|
|
90
|
-
// existence, and every character it can emit has just been forced through the safe-segment
|
|
91
|
-
// replacements above — so this IS the validated boundary the brand is meant to be created at.
|
|
92
|
-
// Returning `string` pushed a cast onto each caller, which is the hole `flowPath`'s parameter
|
|
93
|
-
// type was added to close.
|
|
94
|
-
// Nothing nameable was proved, so keep the name this function has always produced.
|
|
95
|
-
if ('' === proved) {
|
|
96
|
-
return asFlowName(0 === leg.length ? `drive-${safe}` : `drive-${safe}-${leg}`);
|
|
97
|
-
}
|
|
98
|
-
// Enough of the session to keep two of them apart, and little enough to read past.
|
|
99
|
-
const discriminator = safe.replace(/-/g, '').slice(0, 8);
|
|
100
|
-
const parts = ['drive', proved, ...(0 === leg.length ? [] : [leg]), discriminator];
|
|
101
|
-
return asFlowName(parts.join('-'));
|
|
80
|
+
export function driveFlowName(route, steps = []) {
|
|
81
|
+
const intent = steps.find((step) => step.intent !== undefined)?.intent;
|
|
82
|
+
const leg = segment(route ?? '', 24);
|
|
83
|
+
const why = intent === undefined ? '' : segment(intent, 48);
|
|
84
|
+
const lead = '' !== why ? why : provedSlug(steps);
|
|
85
|
+
// Minted here: every character has just been forced through `segment`, so this IS the validated
|
|
86
|
+
// boundary the brand is meant to be created at.
|
|
87
|
+
const parts = [lead, ...('' !== why || 0 === leg.length ? [] : [leg])].filter((p) => '' !== p);
|
|
88
|
+
return asFlowName(`${DRIVE_FLOW_PREFIX}${0 === parts.length ? 'journey' : parts.join('-')}`);
|
|
102
89
|
}
|
|
103
90
|
/**
|
|
104
91
|
* Turn this session's ambient tape into a flow, when it is worth one.
|
|
@@ -125,18 +112,39 @@ export function driveFlowName(sessionId, route, steps = []) {
|
|
|
125
112
|
*/
|
|
126
113
|
function segmentsByRoute(steps) {
|
|
127
114
|
const out = [];
|
|
115
|
+
let signedIn = false;
|
|
128
116
|
for (const step of steps) {
|
|
129
117
|
const last = out.at(-1);
|
|
130
|
-
|
|
118
|
+
const previous = last?.steps.at(-1);
|
|
119
|
+
// The same journey when the route is unchanged, or when the previous ACTION moved the app here
|
|
120
|
+
// — cart → shipping → payment is one flow, and cutting it left three fragments that each
|
|
121
|
+
// proved a page and none proved the journey. Not after a sign-in: that is the cut this exists
|
|
122
|
+
// for, and a login that navigates onward must still not open the next flow.
|
|
123
|
+
const caused = previous !== undefined && step.route !== undefined && previous.endPage === step.route;
|
|
124
|
+
if (last !== undefined && (last.route === step.route || (caused && !signedIn))) {
|
|
131
125
|
last.steps.push(step);
|
|
126
|
+
signedIn ||= typesASecret(step);
|
|
132
127
|
continue;
|
|
133
128
|
}
|
|
129
|
+
signedIn = typesASecret(step);
|
|
134
130
|
out.push({ ...(step.route === undefined ? {} : { route: step.route }), steps: [step] });
|
|
135
131
|
}
|
|
136
132
|
return out;
|
|
137
133
|
}
|
|
134
|
+
/**
|
|
135
|
+
* A fill into a field the network channel would redact — the sign-in a journey must not carry.
|
|
136
|
+
* Read off the recorder's anchor args: a testid names the field by `value`, a role by `name`.
|
|
137
|
+
*/
|
|
138
|
+
function typesASecret(step) {
|
|
139
|
+
const action = step.args['action'];
|
|
140
|
+
if (ActionType.FILL !== action && ActionType.TYPE !== action)
|
|
141
|
+
return false;
|
|
142
|
+
const by = step.args['by'];
|
|
143
|
+
const field = QueryBy.TESTID === by ? step.args['value'] : step.args['name'];
|
|
144
|
+
return 'string' === typeof field && defaultIsSensitiveKey(field);
|
|
145
|
+
}
|
|
138
146
|
/** A flow per journey, so two journeys in one session are two regression tests rather than one. */
|
|
139
|
-
export function driveFlowsFrom(
|
|
147
|
+
export function driveFlowsFrom(tape) {
|
|
140
148
|
if (tape === undefined || 0 === tape.steps.length)
|
|
141
149
|
return { programs: [], outcome: {} };
|
|
142
150
|
const programs = [];
|
|
@@ -148,7 +156,7 @@ export function driveFlowsFrom(sessionId, tape) {
|
|
|
148
156
|
}
|
|
149
157
|
const startPath = segment.route ?? tape.startPath;
|
|
150
158
|
programs.push({
|
|
151
|
-
name: driveFlowName(
|
|
159
|
+
name: driveFlowName(startPath, segment.steps),
|
|
152
160
|
version: REPLAY_PROGRAM_VERSION,
|
|
153
161
|
// The route is recorder-internal and has no business on disk — `startPath` is where the
|
|
154
162
|
// on-disk flow says the same thing, in the field replay actually reads.
|
|
@@ -101,13 +101,13 @@ export declare const RouteEnvelopeSchema: z.ZodObject<{
|
|
|
101
101
|
max: number;
|
|
102
102
|
}>;
|
|
103
103
|
}, "strip", z.ZodTypeAny, {
|
|
104
|
-
|
|
104
|
+
net: {
|
|
105
105
|
count: number;
|
|
106
106
|
mean: number;
|
|
107
107
|
m2: number;
|
|
108
108
|
max: number;
|
|
109
109
|
};
|
|
110
|
-
|
|
110
|
+
durationMs: {
|
|
111
111
|
count: number;
|
|
112
112
|
mean: number;
|
|
113
113
|
m2: number;
|
|
@@ -126,13 +126,13 @@ export declare const RouteEnvelopeSchema: z.ZodObject<{
|
|
|
126
126
|
max: number;
|
|
127
127
|
};
|
|
128
128
|
}, {
|
|
129
|
-
|
|
129
|
+
net: {
|
|
130
130
|
count: number;
|
|
131
131
|
mean: number;
|
|
132
132
|
m2: number;
|
|
133
133
|
max: number;
|
|
134
134
|
};
|
|
135
|
-
|
|
135
|
+
durationMs: {
|
|
136
136
|
count: number;
|
|
137
137
|
mean: number;
|
|
138
138
|
m2: number;
|
|
@@ -155,13 +155,13 @@ export declare const RouteEnvelopeSchema: z.ZodObject<{
|
|
|
155
155
|
route: string;
|
|
156
156
|
samples: number;
|
|
157
157
|
stats: {
|
|
158
|
-
|
|
158
|
+
net: {
|
|
159
159
|
count: number;
|
|
160
160
|
mean: number;
|
|
161
161
|
m2: number;
|
|
162
162
|
max: number;
|
|
163
163
|
};
|
|
164
|
-
|
|
164
|
+
durationMs: {
|
|
165
165
|
count: number;
|
|
166
166
|
mean: number;
|
|
167
167
|
m2: number;
|
|
@@ -184,13 +184,13 @@ export declare const RouteEnvelopeSchema: z.ZodObject<{
|
|
|
184
184
|
route: string;
|
|
185
185
|
samples: number;
|
|
186
186
|
stats: {
|
|
187
|
-
|
|
187
|
+
net: {
|
|
188
188
|
count: number;
|
|
189
189
|
mean: number;
|
|
190
190
|
m2: number;
|
|
191
191
|
max: number;
|
|
192
192
|
};
|
|
193
|
-
|
|
193
|
+
durationMs: {
|
|
194
194
|
count: number;
|
|
195
195
|
mean: number;
|
|
196
196
|
m2: number;
|
|
@@ -62,5 +62,19 @@ export declare function readQueryEvents(reader: JournalReader | undefined, buffe
|
|
|
62
62
|
* "nothing was lost" — see `JournalReader.readWriteLoss`.
|
|
63
63
|
*/
|
|
64
64
|
export declare function readJournalWriteLoss(reader: JournalReader | undefined, buffer: QueryBuffer): Promise<JournalWriteLoss | undefined>;
|
|
65
|
+
/** The slice of the ring buffer `windowLost` reads. `RingBuffer` satisfies it structurally. */
|
|
66
|
+
export interface LossBuffer {
|
|
67
|
+
lostSince(cursor: number): boolean;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Did the EVENT STORE lose scarce evidence from a window opened at `cursor`? The input to whether a
|
|
71
|
+
* verdict's capture was clean — see `RingBuffer.lostSince`, and never the raw drop counter, which
|
|
72
|
+
* moves for the age and churn evictions that every live page produces continuously. The journal is
|
|
73
|
+
* the other half of that store, so a durable read that could not reach back to `cursor` lost
|
|
74
|
+
* exactly what an eviction would have. Both boundaries are INCLUSIVE (`t` is a millisecond many
|
|
75
|
+
* records share) and an absent `lostThroughT` impeaches every window rather than none — the
|
|
76
|
+
* conservative direction, both times.
|
|
77
|
+
*/
|
|
78
|
+
export declare function windowLost(reader: JournalReader | undefined, buffer: LossBuffer, cursor: number): boolean;
|
|
65
79
|
/** Apply since/until/actionId bounds to an event list. */
|
|
66
80
|
export declare function filterEvents(events: readonly ReticleEvent[], options: EventQueryOptions): ReticleEvent[];
|
|
@@ -90,6 +90,21 @@ export async function readJournalWriteLoss(reader, buffer) {
|
|
|
90
90
|
return undefined;
|
|
91
91
|
return (await reader?.readWriteLoss?.()) ?? undefined;
|
|
92
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* Did the EVENT STORE lose scarce evidence from a window opened at `cursor`? The input to whether a
|
|
95
|
+
* verdict's capture was clean — see `RingBuffer.lostSince`, and never the raw drop counter, which
|
|
96
|
+
* moves for the age and churn evictions that every live page produces continuously. The journal is
|
|
97
|
+
* the other half of that store, so a durable read that could not reach back to `cursor` lost
|
|
98
|
+
* exactly what an eviction would have. Both boundaries are INCLUSIVE (`t` is a millisecond many
|
|
99
|
+
* records share) and an absent `lostThroughT` impeaches every window rather than none — the
|
|
100
|
+
* conservative direction, both times.
|
|
101
|
+
*/
|
|
102
|
+
export function windowLost(reader, buffer, cursor) {
|
|
103
|
+
if (buffer.lostSince(cursor))
|
|
104
|
+
return true;
|
|
105
|
+
const lost = reader?.readLoss?.();
|
|
106
|
+
return lost !== undefined && (lost.lostThroughT === undefined || lost.lostThroughT >= cursor);
|
|
107
|
+
}
|
|
93
108
|
/** Apply since/until/actionId bounds to an event list. */
|
|
94
109
|
export function filterEvents(events, options) {
|
|
95
110
|
return events.filter((event) => (options.since === undefined || event.t >= options.since) &&
|
|
@@ -49,6 +49,8 @@ export const WORKSPACE_TIERS = {
|
|
|
49
49
|
[ReticleDir.AMBIENT_FILE]: localFile,
|
|
50
50
|
[ReticleDir.ENVELOPES_FILE]: localFile,
|
|
51
51
|
[ReticleDir.FLAKE_FILE]: localFile,
|
|
52
|
+
// What THIS machine's drives covered, and the best each level reached — the gate's ratchet.
|
|
53
|
+
[ReticleDir.COVERAGE_FILE]: localFile,
|
|
52
54
|
[ReticleDir.TIERS_FILE]: localFile,
|
|
53
55
|
// The user's own record of what Reticle did for them, on THIS machine.
|
|
54
56
|
[ReticleDir.IMPACT_FILE]: localFile,
|
|
@@ -79,8 +79,16 @@ interface SessionEndDeps {
|
|
|
79
79
|
* this handler follows.
|
|
80
80
|
*/
|
|
81
81
|
flows?: {
|
|
82
|
-
|
|
82
|
+
saveJourney: (program: DriveProgram, projectId: ProjectId | undefined, prefix: string) => Promise<unknown>;
|
|
83
83
|
};
|
|
84
|
+
/**
|
|
85
|
+
* The flow store for a session's own `.reticle`. Drive flows used to go to `flows` above — the
|
|
86
|
+
* daemon's launch directory — while replay reads the session's project, so a daemon started
|
|
87
|
+
* anywhere else saved flows nothing could replay. Absent, `flows` is used.
|
|
88
|
+
*/
|
|
89
|
+
flowsAt?: (root: string) => NonNullable<SessionEndDeps['flows']>;
|
|
90
|
+
/** Who is driving — stamped on each saved flow. Injected: teardown does not reach into MCP. */
|
|
91
|
+
author?: () => DriveProgram['author'];
|
|
84
92
|
/**
|
|
85
93
|
* Called when a run artifact is written, so cloud sync can cycle instead of waiting for its timer.
|
|
86
94
|
*
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import { driveFlowsFrom } from './drive-flow.js';
|
|
1
|
+
import { DRIVE_FLOW_PREFIX, driveFlowsFrom, } from './drive-flow.js';
|
|
2
|
+
import { log } from '../../log.js';
|
|
3
|
+
/** What a session's teardown writes to the daemon log when a by-product fails to save. */
|
|
4
|
+
const SESSION_END_LOG = {
|
|
5
|
+
FLOW_SAVE_FAILED: 'reticle_drive_flow_save_failed',
|
|
6
|
+
RUN_RECORD_FAILED: 'reticle_drive_run_record_failed',
|
|
7
|
+
};
|
|
8
|
+
const errorMessage = (error) => error instanceof Error ? error.message : String(error);
|
|
2
9
|
import { journalActionsPath, sessionDirPath } from '../project/dir/reticle-dir.js';
|
|
3
10
|
import { asSessionId } from '@reticlehq/core';
|
|
4
11
|
import { OnboardingPhase, OnboardingStepStatus, } from '@reticlehq/core/telemetry';
|
|
@@ -78,24 +85,27 @@ export function makeSessionEnd(deps) {
|
|
|
78
85
|
// would buy under a minute of latency for a mutable handle held across two wiring sites.
|
|
79
86
|
// A DRIVE IS A REGRESSION TEST, and it used to need somebody to remember to say so.
|
|
80
87
|
//
|
|
81
|
-
// `record{start}` + `flow_save` is the agent-facing route and agents do not take it:
|
|
82
|
-
//
|
|
83
|
-
//
|
|
84
|
-
//
|
|
88
|
+
// `record{start}` + `flow_save` is the agent-facing route and agents do not take it: few saved
|
|
89
|
+
// flows are mutation-testable and few steps declare a consequence, while the engine catches
|
|
90
|
+
// nearly every bug it structurally can. The ceiling is how many flows EXIST that could go red,
|
|
91
|
+
// so a drive that is never saved is a regression test paid for and thrown away.
|
|
85
92
|
//
|
|
86
93
|
// Saved only when the tape declared a consequence — see drive-flow.ts for why the alternative is
|
|
87
94
|
// a false-green factory running once per session.
|
|
88
95
|
try {
|
|
89
96
|
await saveDrivenFlow(deps, session);
|
|
90
97
|
}
|
|
91
|
-
catch {
|
|
92
|
-
//
|
|
98
|
+
catch (error) {
|
|
99
|
+
// A flow is a by-product of the session; failing to write one never fails the teardown. It is
|
|
100
|
+
// logged, because a drive that leaves no flow and no reason is otherwise undiagnosable.
|
|
101
|
+
log(SESSION_END_LOG.FLOW_SAVE_FAILED, { error: errorMessage(error) });
|
|
93
102
|
}
|
|
94
103
|
try {
|
|
95
104
|
await recordDriveRun(deps, session);
|
|
96
105
|
}
|
|
97
|
-
catch {
|
|
98
|
-
//
|
|
106
|
+
catch (error) {
|
|
107
|
+
// An artifact is a report ABOUT the session; failing to write one never fails the teardown.
|
|
108
|
+
log(SESSION_END_LOG.RUN_RECORD_FAILED, { error: errorMessage(error) });
|
|
99
109
|
}
|
|
100
110
|
try {
|
|
101
111
|
// Bound the journal on disk HERE, not only at daemon start.
|
|
@@ -196,13 +206,15 @@ export async function recordDriveRun(deps, session) {
|
|
|
196
206
|
/** Persist what this session drove as a replayable flow, when it declared anything provable. */
|
|
197
207
|
async function saveDrivenFlow(deps, session) {
|
|
198
208
|
const takeTape = deps.takeAmbientTape;
|
|
199
|
-
const flows = deps.flows;
|
|
209
|
+
const flows = deps.flowsAt?.(session.artifactRoot ?? deps.reticleRoot) ?? deps.flows;
|
|
200
210
|
if (takeTape === undefined || flows === undefined)
|
|
201
211
|
return;
|
|
202
212
|
// One flow per journey the session contained, not one flow per session — see drive-flow.ts.
|
|
203
|
-
const { programs, outcome } = driveFlowsFrom(
|
|
213
|
+
const { programs, outcome } = driveFlowsFrom(takeTape());
|
|
214
|
+
const author = deps.author?.();
|
|
204
215
|
for (const program of programs) {
|
|
205
|
-
|
|
216
|
+
// Folded into any saved copy of the same journey, so each session does not add a near-copy.
|
|
217
|
+
await flows.saveJourney(author === undefined ? program : { ...program, author }, session.projectId, DRIVE_FLOW_PREFIX);
|
|
206
218
|
}
|
|
207
219
|
// The funnel step that makes the SECOND run cheap.
|
|
208
220
|
//
|
|
@@ -1,3 +1,13 @@
|
|
|
1
1
|
import { type ProjectId } from '@reticlehq/core';
|
|
2
2
|
import { type ArtifactRoot } from './artifact-root.js';
|
|
3
|
+
/**
|
|
4
|
+
* The directory of the project a page announced, or undefined when this machine does not know it.
|
|
5
|
+
*
|
|
6
|
+
* A daemon is shared: started from `$HOME`, a monorepo root, or another project's MCP client, its
|
|
7
|
+
* cwd is often not the app that sent the HELLO. The version-skew remedy read `package.json` from
|
|
8
|
+
* cwd anyway, so it fell back to the generic sensor package and told a Next.js project to install
|
|
9
|
+
* `@reticlehq/browser` (#1135). This is the same lookup the artifact root uses, keyed by the page's
|
|
10
|
+
* own project id.
|
|
11
|
+
*/
|
|
12
|
+
export declare function projectDirectoryFor(projectId: string | undefined): string | undefined;
|
|
3
13
|
export declare function artifactRootResolver(daemonRoot: string): (projectId: ProjectId | undefined, origin?: string) => ArtifactRoot;
|