@reticlehq/server 2.11.0 → 2.12.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 +26 -2
- package/dist/bridge/bridge.d.ts +0 -13
- package/dist/bridge/bridge.js +1 -1
- package/dist/bridge/bridge.test-harness.d.ts +2 -0
- package/dist/bridge/bridge.test-harness.js +8 -1
- package/dist/capsule/capsule-store.d.ts +2 -1
- package/dist/capsule/capsule-store.js +1 -1
- package/dist/capsule/causal-summary.d.ts +2 -2
- package/dist/chromium-launch-options.d.ts +28 -0
- package/dist/chromium-launch-options.js +29 -0
- package/dist/cli/cli-doctor.js +8 -0
- 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-parse.d.ts +3 -1
- package/dist/cli/cli-parse.js +29 -7
- package/dist/cli/cli-update-commands.js +20 -1
- package/dist/cli/cli-verify.d.ts +1 -1
- package/dist/cli/cli-verify.js +2 -2
- package/dist/cli/cloud-cli.js +12 -6
- package/dist/cli/config-discovery.d.ts +2 -1
- package/dist/cli/doctor-rows.d.ts +3 -1
- package/dist/cli/doctor-rows.js +3 -1
- 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 +143 -0
- package/dist/cli/sibling-ports.d.ts +37 -0
- package/dist/cli/sibling-ports.js +56 -0
- package/dist/cli.js +37 -110
- package/dist/cloud/cloud-config.d.ts +2 -1
- package/dist/cloud/cloud-sync.d.ts +11 -0
- package/dist/cloud/cloud-sync.js +32 -0
- package/dist/crawl/crawl.d.ts +9 -13
- package/dist/crawl/crawl.js +20 -3
- package/dist/daemon/daemon-resolve.d.ts +79 -0
- package/dist/daemon/daemon-resolve.js +118 -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/dev/stale-issue-guard.d.ts +0 -2
- package/dist/dev/stale-issue-guard.js +1 -1
- package/dist/events/contradictions.d.ts +41 -0
- package/dist/events/contradictions.js +125 -12
- package/dist/events/declared.d.ts +14 -0
- package/dist/events/declared.js +33 -0
- package/dist/events/echo-mismatch.js +14 -0
- 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 +64 -22
- package/dist/events/predicate-parse.d.ts +0 -6
- package/dist/events/predicate-parse.js +43 -3
- package/dist/events/predicate-schema.d.ts +44 -0
- package/dist/events/predicate-schema.js +126 -23
- package/dist/events/predicate.d.ts +15 -0
- package/dist/events/predicate.js +41 -2
- 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 +0 -2
- 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-replay-run.js +27 -4
- package/dist/flows/flow-success.js +15 -3
- package/dist/flows/flow-tools.js +32 -4
- package/dist/flows/flows.d.ts +6 -1
- package/dist/flows/flows.js +34 -22
- package/dist/flows/predicate-to-expect.js +6 -3
- package/dist/flows/server-verify.js +16 -2
- package/dist/flows/verify-change-tools.js +4 -1
- package/dist/honesty/blind-spots.d.ts +0 -17
- package/dist/honesty/blind-spots.js +1 -1
- package/dist/honesty/feature-capture.d.ts +150 -0
- package/dist/honesty/feature-capture.js +233 -0
- package/dist/honesty/instrumentation-gaps.d.ts +30 -6
- package/dist/honesty/instrumentation-gaps.js +61 -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 +15 -0
- package/dist/honesty/verified.js +68 -4
- package/dist/http-server.d.ts +0 -1
- package/dist/http-server.js +1 -1
- package/dist/hunt/hunt-report.d.ts +0 -2
- package/dist/hunt/hunt-report.js +1 -1
- package/dist/impact/impact-store.d.ts +0 -5
- package/dist/impact/impact-store.js +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/init/agent-rules.d.ts +17 -0
- package/dist/init/agent-rules.js +31 -1
- package/dist/init/capabilities.d.ts +0 -2
- package/dist/init/capabilities.js +1 -1
- package/dist/init/closing-hint.d.ts +36 -0
- package/dist/init/closing-hint.js +120 -0
- package/dist/init/confirm.d.ts +0 -25
- package/dist/init/confirm.js +3 -3
- package/dist/init/cra.d.ts +14 -1
- package/dist/init/cra.js +43 -12
- package/dist/init/dev-script.d.ts +48 -0
- package/dist/init/dev-script.js +63 -0
- package/dist/init/mcp.d.ts +16 -5
- package/dist/init/mcp.js +26 -10
- package/dist/init/plan-framework.d.ts +0 -5
- package/dist/init/plan-framework.js +83 -9
- package/dist/init/plan.d.ts +10 -30
- package/dist/init/plan.js +30 -15
- package/dist/init/refresh-rules.d.ts +39 -0
- package/dist/init/refresh-rules.js +51 -0
- package/dist/init/run.d.ts +0 -26
- package/dist/init/run.js +14 -87
- package/dist/init/snippets.js +53 -10
- package/dist/input/real-input.d.ts +2 -0
- package/dist/input/real-input.js +3 -2
- package/dist/input/scroll-find.d.ts +7 -4
- package/dist/input/scroll-find.js +63 -25
- package/dist/intent/inline-intent.d.ts +46 -0
- package/dist/intent/inline-intent.js +103 -0
- package/dist/intent/intent-store.d.ts +37 -0
- package/dist/intent/intent-store.js +112 -0
- package/dist/intent/intent-tools.d.ts +2 -0
- package/dist/intent/intent-tools.js +80 -0
- package/dist/intent/open-intents.d.ts +21 -0
- package/dist/intent/open-intents.js +25 -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/mcp/attach-memory.d.ts +67 -0
- package/dist/mcp/attach-memory.js +142 -0
- package/dist/mcp/mcp-proxy.d.ts +0 -7
- package/dist/mcp/mcp-proxy.js +14 -2
- package/dist/mcp/mcp.d.ts +2 -0
- package/dist/mcp/mcp.js +45 -23
- package/dist/mcp/server-instructions.js +45 -3
- package/dist/mcp/tool-catalog-cache.d.ts +8 -0
- package/dist/mcp/tool-catalog-cache.js +10 -0
- package/dist/oracles/flow-instrument-gaps.d.ts +4 -4
- package/dist/oracles/flow-instrument-gaps.js +4 -5
- package/dist/oracles/self-instrument.d.ts +9 -9
- package/dist/oracles/self-instrument.js +19 -13
- package/dist/pool/browser-pool.d.ts +12 -1
- package/dist/pool/browser-pool.js +31 -1
- package/dist/pool/playwright-launcher.js +3 -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 +3 -32
- package/dist/project/reticle-dir.d.ts +2 -0
- package/dist/project/reticle-dir.js +1 -0
- 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/verification-sync.js +2 -2
- 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/live-control-tools.js +1 -42
- package/dist/session/no-session-diagnosis.d.ts +10 -0
- package/dist/session/no-session-diagnosis.js +24 -7
- package/dist/session/no-session-next-action.d.ts +5 -0
- package/dist/session/no-session-next-action.js +35 -3
- package/dist/session/no-session-watch.d.ts +8 -0
- package/dist/session/no-session-watch.js +82 -64
- package/dist/session/output-budget.js +4 -1
- package/dist/session/session-health.d.ts +9 -0
- package/dist/session/session-health.js +23 -0
- package/dist/session/session-manager.js +50 -1
- package/dist/session/session-successor.d.ts +37 -0
- package/dist/session/session-successor.js +78 -0
- package/dist/session/session.d.ts +31 -1
- package/dist/session/session.js +75 -2
- package/dist/telemetry/automation-hint.d.ts +2 -1
- package/dist/telemetry/feedback-outbox.d.ts +2 -1
- package/dist/telemetry/project-profile.d.ts +0 -8
- package/dist/telemetry/project-profile.js +1 -1
- package/dist/telemetry/run-telemetry.d.ts +0 -2
- package/dist/telemetry/run-telemetry.js +1 -1
- package/dist/telemetry/verification-of.js +2 -2
- package/dist/tools/act-tools.js +126 -21
- 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 +36 -4
- package/dist/tools/browser-tools.js +0 -20
- 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 +29 -8
- package/dist/tools/error-recovery.d.ts +24 -14
- package/dist/tools/error-recovery.js +70 -9
- package/dist/tools/feedback-tools.d.ts +1 -1
- package/dist/tools/feedback-tools.js +2 -2
- package/dist/tools/invoke-tool.js +62 -2
- package/dist/tools/lease-hint.d.ts +10 -0
- package/dist/tools/lease-hint.js +53 -4
- package/dist/tools/lease-tools.d.ts +5 -1
- package/dist/tools/lease-tools.js +22 -8
- package/dist/tools/navigate-arrival.d.ts +0 -2
- package/dist/tools/navigate-arrival.js +1 -1
- package/dist/tools/observe-tools.js +63 -26
- package/dist/tools/read-tools.js +5 -3
- package/dist/tools/real-input-attempt.d.ts +22 -0
- package/dist/tools/real-input-attempt.js +153 -1
- package/dist/tools/reconcile-tools.js +18 -4
- 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 +12 -0
- package/dist/tools/tool-kit.js +16 -0
- package/dist/tools/tool-names.d.ts +3 -0
- package/dist/tools/tool-names.js +3 -0
- package/dist/tools/tool-surface.d.ts +85 -6
- package/dist/tools/tool-surface.js +122 -2
- package/dist/tools/tools.js +22 -4
- 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/update-nudge.d.ts +7 -10
- package/dist/update/update-nudge.js +12 -3
- package/dist/update/updater.d.ts +1 -1
- package/docs/README.md +2 -1
- package/docs/cli/cloud.mdx +1 -1
- package/docs/cli/doctor.mdx +1 -0
- package/docs/cli/verify.mdx +4 -5
- package/docs/desktop.mdx +1 -1
- package/docs/docs.json +2 -2
- package/docs/gates.md +1 -1
- 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/tools-tools-and-run.mdx +2 -4
- package/docs/troubleshooting.mdx +9 -0
- package/docs/usage.md +3 -2
- package/package.json +2 -2
- package/dist/session/session-readiness.d.ts +0 -34
- package/dist/session/session-readiness.js +0 -36
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The session that took over after a full-document navigation.
|
|
3
|
+
*
|
|
4
|
+
* Reload keeps the id (sessionStorage). A click that loads a new document often does not: the SDK
|
|
5
|
+
* is torn down with the page and HELLO's back under a new id, while the agent still holds the old
|
|
6
|
+
* one. `navigate` already waits for that HELLO (`awaitArrival`) and returns the new id.
|
|
7
|
+
* `act_and_wait` and `assert` did not, so an MPA drive ended at the first link.
|
|
8
|
+
*
|
|
9
|
+
* Unique at the departed origin (and project, when known). Two live tabs at that origin is still a
|
|
10
|
+
* guess, and a guess here would drive the wrong app.
|
|
11
|
+
*/
|
|
12
|
+
import type { Session } from './session.js';
|
|
13
|
+
export interface SessionIdentity {
|
|
14
|
+
id: string;
|
|
15
|
+
url: string;
|
|
16
|
+
projectId?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface SuccessorClock {
|
|
19
|
+
now: () => number;
|
|
20
|
+
sleep: (ms: number) => Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The one live session that can honestly take over from `departed`, or undefined when none or more
|
|
24
|
+
* than one could.
|
|
25
|
+
*
|
|
26
|
+
* A same-id reconnect is a successor: the id survived in sessionStorage, the document did not.
|
|
27
|
+
*/
|
|
28
|
+
export declare function pickDocumentSuccessor(live: readonly SessionIdentity[], departed: SessionIdentity): SessionIdentity | undefined;
|
|
29
|
+
export interface SuccessorRegistry {
|
|
30
|
+
get(id: string): Session | undefined;
|
|
31
|
+
all(): readonly Session[];
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Poll until a successor of `departed` is live, or the budget runs out. Null on timeout — never
|
|
35
|
+
* throws, because failing to follow is a legitimate `observation_lost`, not an error.
|
|
36
|
+
*/
|
|
37
|
+
export declare function awaitDocumentSuccessor(sessions: SuccessorRegistry, departed: Session, timeoutMs: number, clock?: SuccessorClock): Promise<Session | null>;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The session that took over after a full-document navigation.
|
|
3
|
+
*
|
|
4
|
+
* Reload keeps the id (sessionStorage). A click that loads a new document often does not: the SDK
|
|
5
|
+
* is torn down with the page and HELLO's back under a new id, while the agent still holds the old
|
|
6
|
+
* one. `navigate` already waits for that HELLO (`awaitArrival`) and returns the new id.
|
|
7
|
+
* `act_and_wait` and `assert` did not, so an MPA drive ended at the first link.
|
|
8
|
+
*
|
|
9
|
+
* Unique at the departed origin (and project, when known). Two live tabs at that origin is still a
|
|
10
|
+
* guess, and a guess here would drive the wrong app.
|
|
11
|
+
*/
|
|
12
|
+
const POLL_MS = 25;
|
|
13
|
+
const REAL_CLOCK = {
|
|
14
|
+
now: () => Date.now(),
|
|
15
|
+
sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
|
|
16
|
+
};
|
|
17
|
+
function originOf(url) {
|
|
18
|
+
try {
|
|
19
|
+
return new URL(url).origin;
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function identityOf(session) {
|
|
26
|
+
return {
|
|
27
|
+
id: session.id,
|
|
28
|
+
url: session.url,
|
|
29
|
+
...(session.projectId === undefined ? {} : { projectId: session.projectId }),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The one live session that can honestly take over from `departed`, or undefined when none or more
|
|
34
|
+
* than one could.
|
|
35
|
+
*
|
|
36
|
+
* A same-id reconnect is a successor: the id survived in sessionStorage, the document did not.
|
|
37
|
+
*/
|
|
38
|
+
export function pickDocumentSuccessor(live, departed) {
|
|
39
|
+
const origin = originOf(departed.url);
|
|
40
|
+
if (origin === undefined)
|
|
41
|
+
return undefined;
|
|
42
|
+
const atOrigin = live.filter((s) => originOf(s.url) === origin);
|
|
43
|
+
const sameId = atOrigin.find((s) => s.id === departed.id);
|
|
44
|
+
if (sameId !== undefined)
|
|
45
|
+
return sameId;
|
|
46
|
+
const others = atOrigin.filter((s) => s.id !== departed.id &&
|
|
47
|
+
(departed.projectId === undefined || s.projectId === departed.projectId));
|
|
48
|
+
if (1 !== others.length)
|
|
49
|
+
return undefined;
|
|
50
|
+
return others[0];
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Poll until a successor of `departed` is live, or the budget runs out. Null on timeout — never
|
|
54
|
+
* throws, because failing to follow is a legitimate `observation_lost`, not an error.
|
|
55
|
+
*/
|
|
56
|
+
export async function awaitDocumentSuccessor(sessions, departed, timeoutMs, clock = REAL_CLOCK) {
|
|
57
|
+
const deadline = clock.now() + timeoutMs;
|
|
58
|
+
const departedIdentity = identityOf(departed);
|
|
59
|
+
for (;;) {
|
|
60
|
+
const sameId = sessions.get(departed.id);
|
|
61
|
+
if (sameId !== undefined && sameId !== departed)
|
|
62
|
+
return sameId;
|
|
63
|
+
// The departed session often still sits in the registry until the socket close lands. Leave it
|
|
64
|
+
// out of the pick, or same-id-first would keep returning the page that just unloaded.
|
|
65
|
+
const picked = pickDocumentSuccessor(sessions
|
|
66
|
+
.all()
|
|
67
|
+
.filter((s) => s !== departed)
|
|
68
|
+
.map(identityOf), departedIdentity);
|
|
69
|
+
if (picked !== undefined) {
|
|
70
|
+
const live = sessions.get(picked.id);
|
|
71
|
+
if (live !== undefined && live !== departed)
|
|
72
|
+
return live;
|
|
73
|
+
}
|
|
74
|
+
if (clock.now() >= deadline)
|
|
75
|
+
return null;
|
|
76
|
+
await clock.sleep(POLL_MS);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -2,10 +2,11 @@ import type { WebSocket } from 'ws';
|
|
|
2
2
|
import type { ImpactSnapshot } from '@reticlehq/core';
|
|
3
3
|
import { LastAct } from './last-act.js';
|
|
4
4
|
import { GapLedger } from '../honesty/gap-ledger.js';
|
|
5
|
+
import { CaptureLedger } from '../honesty/feature-capture.js';
|
|
5
6
|
import { type PageRuntime } from './command-timeout.js';
|
|
6
7
|
import { type SessionHealth } from './session-health.js';
|
|
7
8
|
export type { SessionHealth };
|
|
8
|
-
import { PresenterTone, SessionState, type BrowserBrand, type CommandResult, type HelloMessage, type HumanControlData, type ReticleEvent } from '@reticlehq/core';
|
|
9
|
+
import { PresenterTone, SessionState, type BrowserBrand, type CommandResult, type JournalAction, type HelloMessage, type HumanControlData, type ReticleEvent } from '@reticlehq/core';
|
|
9
10
|
import type { JournalReader, JournalRecorder } from '../journal/journal-recorder.js';
|
|
10
11
|
import { type EventQueryOptions } from '../journal/journal-query.js';
|
|
11
12
|
import { type AmbientCounts } from '../journal/ambient.js';
|
|
@@ -63,6 +64,10 @@ export declare class Session {
|
|
|
63
64
|
info(): SessionInfo;
|
|
64
65
|
/** Wall-clock age of the session in milliseconds. */
|
|
65
66
|
staleMs(): number;
|
|
67
|
+
/** The document the most recent stamped event was observed under. See `#documentId`. */
|
|
68
|
+
get currentDocumentId(): string | undefined;
|
|
69
|
+
/** The edit epoch the most recent stamped event was observed under. See `#editEpoch`. */
|
|
70
|
+
get currentEditEpoch(): number | undefined;
|
|
66
71
|
/** Re-stamp an incoming event with server-relative time, buffer it, and fan out. */
|
|
67
72
|
pushEvent(event: ReticleEvent, byteSize?: number): void;
|
|
68
73
|
recordActedRef(ref: string): void;
|
|
@@ -100,8 +105,28 @@ export declare class Session {
|
|
|
100
105
|
* action id. Ids are minted independently of command correlation ids so the journal is self-contained.
|
|
101
106
|
*/
|
|
102
107
|
beginAction(tool: string, args: Record<string, unknown>): string;
|
|
108
|
+
/**
|
|
109
|
+
* Record one action WITHOUT opening an attribution window — for a tool that returns a verdict but
|
|
110
|
+
* drives nothing, so no event it observes was caused by it. See `JournalRecorder.recordAction`.
|
|
111
|
+
*/
|
|
112
|
+
recordAction(tool: string, args: Record<string, unknown>, effect?: unknown): string;
|
|
113
|
+
/**
|
|
114
|
+
* How many actions this session has dispatched.
|
|
115
|
+
*
|
|
116
|
+
* The counter that mints journal action ids, not a second one — so it equals the journal's own
|
|
117
|
+
* length, and `reticle_context`'s `step`, whenever this session journals at all.
|
|
118
|
+
*/
|
|
119
|
+
get actionCount(): number;
|
|
103
120
|
/** Close the active action window, persisting its action record with the settle outcome. */
|
|
104
121
|
finishAction(effect?: unknown, settled?: boolean, settledInMs?: number): void;
|
|
122
|
+
/**
|
|
123
|
+
* The durable action ledger for this session — the ledger the run context is folded out of.
|
|
124
|
+
*
|
|
125
|
+
* `[]` when nothing journals this session (journaling disabled, or an id that is not a safe path
|
|
126
|
+
* segment). An absent ledger reads as an empty run, never as a missing one: the fold simply has
|
|
127
|
+
* nothing to say, which is the honest answer.
|
|
128
|
+
*/
|
|
129
|
+
readJournalActions(): Promise<JournalAction[]>;
|
|
105
130
|
/** Flush any buffered journal events to disk (call on session end). */
|
|
106
131
|
flushJournal(): Promise<void>;
|
|
107
132
|
eventsSince(cursor: number): ReticleEvent[];
|
|
@@ -120,6 +145,11 @@ export declare class Session {
|
|
|
120
145
|
* end of it.
|
|
121
146
|
*/
|
|
122
147
|
readonly gaps: GapLedger;
|
|
148
|
+
/**
|
|
149
|
+
* The read-only calls the journal does not keep, so "was `reticle_context` ever called" has
|
|
150
|
+
* something to fold. See honesty/feature-capture.ts — it is the only state that instrument adds.
|
|
151
|
+
*/
|
|
152
|
+
readonly capture: CaptureLedger;
|
|
123
153
|
/**
|
|
124
154
|
* Stamp the wall-clock of the latest AGENT command (called whenever a tool resolves this session).
|
|
125
155
|
* If the reaper had auto-ended the session, a fresh command means the agent is alive again, so the
|
package/dist/session/session.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { recordImpact } from '../impact/impact-recorder.js';
|
|
2
2
|
import { LastAct } from './last-act.js';
|
|
3
3
|
import { GapLedger } from '../honesty/gap-ledger.js';
|
|
4
|
+
import { CaptureLedger } from '../honesty/feature-capture.js';
|
|
4
5
|
import { commandTimeoutMessage } from './command-timeout.js';
|
|
5
6
|
import { readHealthEvent } from './session-health.js';
|
|
6
7
|
/** The HUD does not need 200 frames to watch a counter climb; the last state always lands. */
|
|
@@ -107,6 +108,30 @@ export class Session {
|
|
|
107
108
|
#journalReader;
|
|
108
109
|
/** What this session has learned by watching its own stream: ambient churn + blind-spot levels. */
|
|
109
110
|
#observed = new ObservedState();
|
|
111
|
+
/**
|
|
112
|
+
* Which document is on screen right now — the one the most recent stamped event was observed under.
|
|
113
|
+
*
|
|
114
|
+
* DERIVED, never announced. The SDK mints a document id once per real document and stamps it on
|
|
115
|
+
* every event, so the stream already carries the answer; asking the page for it separately would be
|
|
116
|
+
* a second source of truth that can disagree with the evidence it is supposed to scope. A full
|
|
117
|
+
* navigation or a reload builds a new document, mints a new id, and the first event carrying it
|
|
118
|
+
* moves this forward — which is exactly the moment the previous document's evidence stops being
|
|
119
|
+
* about the world. An SPA route change keeps the same document and so keeps the same id, which is
|
|
120
|
+
* correct rather than a limitation: same JavaScript context, same in-flight requests, same evidence.
|
|
121
|
+
*
|
|
122
|
+
* An UNSTAMPED event never clears this. An SDK older than the field stamps nothing, and letting one
|
|
123
|
+
* such event blank the current document would make every later comparison vacuous.
|
|
124
|
+
*/
|
|
125
|
+
#documentId;
|
|
126
|
+
/**
|
|
127
|
+
* The edit epoch of the most recent stamped event. DERIVED for the same reason `#documentId` is:
|
|
128
|
+
* the stream already carries it, and asking the page separately would be a second source of truth.
|
|
129
|
+
*
|
|
130
|
+
* A hot update advances it INSIDE the same document, which is the case `#documentId` structurally
|
|
131
|
+
* cannot see — no navigation, same page, replaced code. An unstamped event never clears it: most
|
|
132
|
+
* pages have no hot-update channel at all, so absence is "unknown", never "back to no edits".
|
|
133
|
+
*/
|
|
134
|
+
#editEpoch;
|
|
110
135
|
constructor(hello, socket, clock) {
|
|
111
136
|
this.id = hello.sessionId;
|
|
112
137
|
this.projectId = hello.projectId;
|
|
@@ -201,8 +226,20 @@ export class Session {
|
|
|
201
226
|
staleMs() {
|
|
202
227
|
return this.#clock() - this.#startedAt;
|
|
203
228
|
}
|
|
229
|
+
/** The document the most recent stamped event was observed under. See `#documentId`. */
|
|
230
|
+
get currentDocumentId() {
|
|
231
|
+
return this.#documentId;
|
|
232
|
+
}
|
|
233
|
+
/** The edit epoch the most recent stamped event was observed under. See `#editEpoch`. */
|
|
234
|
+
get currentEditEpoch() {
|
|
235
|
+
return this.#editEpoch;
|
|
236
|
+
}
|
|
204
237
|
/** Re-stamp an incoming event with server-relative time, buffer it, and fan out. */
|
|
205
238
|
pushEvent(event, byteSize) {
|
|
239
|
+
if (event.documentId !== undefined)
|
|
240
|
+
this.#documentId = event.documentId;
|
|
241
|
+
if (event.editEpoch !== undefined)
|
|
242
|
+
this.#editEpoch = event.editEpoch;
|
|
206
243
|
if (event.type === EventType.PAGE_HEALTH) {
|
|
207
244
|
const r = readHealthEvent(event.data);
|
|
208
245
|
this.applyHealth(r.hidden ?? this.#hidden, r.focused ?? this.#focused, r.runtime, r.engine, r.brand);
|
|
@@ -344,19 +381,50 @@ export class Session {
|
|
|
344
381
|
* action id. Ids are minted independently of command correlation ids so the journal is self-contained.
|
|
345
382
|
*/
|
|
346
383
|
beginAction(tool, args) {
|
|
347
|
-
this.#
|
|
348
|
-
const actionId = `${ACTION_ID_PREFIX}${String(this.#actionSeq)}`;
|
|
384
|
+
const actionId = this.#mintActionId();
|
|
349
385
|
// Held here, not only in the journal: with the journal off every event was unattributed, and
|
|
350
386
|
// pushEvent reads that as ambient churn the settle oracle then ignores. See action-attribution test.
|
|
351
387
|
this.#activeActionId = actionId;
|
|
352
388
|
this.#journal?.beginAction(actionId, tool, args);
|
|
353
389
|
return actionId;
|
|
354
390
|
}
|
|
391
|
+
/**
|
|
392
|
+
* Record one action WITHOUT opening an attribution window — for a tool that returns a verdict but
|
|
393
|
+
* drives nothing, so no event it observes was caused by it. See `JournalRecorder.recordAction`.
|
|
394
|
+
*/
|
|
395
|
+
recordAction(tool, args, effect) {
|
|
396
|
+
const actionId = this.#mintActionId();
|
|
397
|
+
this.#journal?.recordAction(actionId, tool, args, effect);
|
|
398
|
+
return actionId;
|
|
399
|
+
}
|
|
400
|
+
#mintActionId() {
|
|
401
|
+
this.#actionSeq += 1;
|
|
402
|
+
return `${ACTION_ID_PREFIX}${String(this.#actionSeq)}`;
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* How many actions this session has dispatched.
|
|
406
|
+
*
|
|
407
|
+
* The counter that mints journal action ids, not a second one — so it equals the journal's own
|
|
408
|
+
* length, and `reticle_context`'s `step`, whenever this session journals at all.
|
|
409
|
+
*/
|
|
410
|
+
get actionCount() {
|
|
411
|
+
return this.#actionSeq;
|
|
412
|
+
}
|
|
355
413
|
/** Close the active action window, persisting its action record with the settle outcome. */
|
|
356
414
|
finishAction(effect, settled, settledInMs) {
|
|
357
415
|
this.#activeActionId = undefined;
|
|
358
416
|
this.#journal?.finishAction(effect, settled, settledInMs);
|
|
359
417
|
}
|
|
418
|
+
/**
|
|
419
|
+
* The durable action ledger for this session — the ledger the run context is folded out of.
|
|
420
|
+
*
|
|
421
|
+
* `[]` when nothing journals this session (journaling disabled, or an id that is not a safe path
|
|
422
|
+
* segment). An absent ledger reads as an empty run, never as a missing one: the fold simply has
|
|
423
|
+
* nothing to say, which is the honest answer.
|
|
424
|
+
*/
|
|
425
|
+
async readJournalActions() {
|
|
426
|
+
return (await this.#journalReader?.readActions?.()) ?? [];
|
|
427
|
+
}
|
|
360
428
|
/** Flush any buffered journal events to disk (call on session end). */
|
|
361
429
|
async flushJournal() {
|
|
362
430
|
await this.#journal?.flush();
|
|
@@ -379,6 +447,11 @@ export class Session {
|
|
|
379
447
|
* end of it.
|
|
380
448
|
*/
|
|
381
449
|
gaps = new GapLedger();
|
|
450
|
+
/**
|
|
451
|
+
* The read-only calls the journal does not keep, so "was `reticle_context` ever called" has
|
|
452
|
+
* something to fold. See honesty/feature-capture.ts — it is the only state that instrument adds.
|
|
453
|
+
*/
|
|
454
|
+
capture = new CaptureLedger();
|
|
382
455
|
// ── Server-authoritative liveness (immune to browser-tab throttling) ──────────────
|
|
383
456
|
/**
|
|
384
457
|
* Stamp the wall-clock of the latest AGENT command (called whenever a tool resolves this session).
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AutomationHint } from '@reticlehq/core';
|
|
2
|
-
|
|
2
|
+
interface AutomationInput {
|
|
3
3
|
env: NodeJS.ProcessEnv;
|
|
4
4
|
fileExists: (path: string) => boolean;
|
|
5
5
|
hasTty: boolean;
|
|
@@ -15,3 +15,4 @@ export interface AutomationInput {
|
|
|
15
15
|
export declare function resolveAutomationHint(input: AutomationInput): AutomationHint | undefined;
|
|
16
16
|
/** The real reading, taken once per process. */
|
|
17
17
|
export declare function currentAutomationHint(env: NodeJS.ProcessEnv): AutomationHint | undefined;
|
|
18
|
+
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** One queued report: the payload, plus what is needed to find and remove it again. */
|
|
2
|
-
|
|
2
|
+
interface OutboxEntry {
|
|
3
3
|
id: string;
|
|
4
4
|
t: string;
|
|
5
5
|
payload: unknown;
|
|
@@ -20,3 +20,4 @@ export declare function readOutbox(home?: string): OutboxEntry[];
|
|
|
20
20
|
* Volume is a handful of lines a week, so the cost is irrelevant.
|
|
21
21
|
*/
|
|
22
22
|
export declare function markDelivered(id: string | null, home?: string): void;
|
|
23
|
+
export {};
|
|
@@ -13,9 +13,6 @@ export declare const FeatureFamily: {
|
|
|
13
13
|
readonly BUG_CAPSULES: "bug_capsules";
|
|
14
14
|
readonly CROSS_RUN_HISTORY: "cross_run_history";
|
|
15
15
|
};
|
|
16
|
-
export type FeatureFamily = (typeof FeatureFamily)[keyof typeof FeatureFamily];
|
|
17
|
-
/** Count source files, stopping at the cap (the cap itself lands in the largest bucket, correctly). */
|
|
18
|
-
export declare function countSourceFiles(root: string, readDir?: DirReader): number;
|
|
19
16
|
export declare function sizeBucket(sourceFiles: number): ProjectSize;
|
|
20
17
|
/**
|
|
21
18
|
* Project age in whole WEEKS, from the repo's first commit.
|
|
@@ -30,11 +27,6 @@ export declare function sizeBucket(sourceFiles: number): ProjectSize;
|
|
|
30
27
|
export declare function projectAgeWeeks(root: string, now: number, read?: FileReader): number | undefined;
|
|
31
28
|
/** True when package.json declares workspaces — monorepos exercise very different code paths. */
|
|
32
29
|
export declare function isMonorepo(root: string, read?: FileReader): boolean;
|
|
33
|
-
interface DirEntry {
|
|
34
|
-
name: string;
|
|
35
|
-
isDirectory: boolean;
|
|
36
|
-
}
|
|
37
|
-
type DirReader = (dir: string) => DirEntry[];
|
|
38
30
|
type FileReader = (path: string) => string;
|
|
39
31
|
/**
|
|
40
32
|
* Build the full profile. Best-effort throughout: an unreadable project yields a profile of zeroes
|
|
@@ -45,7 +45,7 @@ const MAX_FILES_SCANNED = 6000;
|
|
|
45
45
|
const MAX_WALK_DEPTH = 8;
|
|
46
46
|
const MS_PER_WEEK = 7 * 24 * 60 * 60 * 1000;
|
|
47
47
|
/** Count source files, stopping at the cap (the cap itself lands in the largest bucket, correctly). */
|
|
48
|
-
|
|
48
|
+
function countSourceFiles(root, readDir = defaultReadDir) {
|
|
49
49
|
let count = 0;
|
|
50
50
|
const walk = (dir, depth) => {
|
|
51
51
|
if (depth > MAX_WALK_DEPTH || count >= MAX_FILES_SCANNED)
|
|
@@ -11,8 +11,6 @@
|
|
|
11
11
|
* how a replay works.
|
|
12
12
|
*/
|
|
13
13
|
import { type ReticleVerificationRun } from '@reticlehq/core';
|
|
14
|
-
/** The tool name reported for this path, so CI runs are distinguishable from agent-driven ones. */
|
|
15
|
-
export declare const VERIFY_SURFACE = "reticle_verify";
|
|
16
14
|
/**
|
|
17
15
|
* Report one completed verification run: the verdict, and one bug per failing flow.
|
|
18
16
|
*
|
|
@@ -14,7 +14,7 @@ import { BugAttribution, BugSource, RunFlowStatus, TelemetryActor, TelemetryEven
|
|
|
14
14
|
import { getSessionMetrics } from './session-metrics.js';
|
|
15
15
|
import { getTelemetry } from './telemetry.js';
|
|
16
16
|
/** The tool name reported for this path, so CI runs are distinguishable from agent-driven ones. */
|
|
17
|
-
|
|
17
|
+
const VERIFY_SURFACE = 'reticle_verify';
|
|
18
18
|
/**
|
|
19
19
|
* Report one completed verification run: the verdict, and one bug per failing flow.
|
|
20
20
|
*
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* be readable and testable on its own rather than inferred from a ternary inside a dispatcher.
|
|
14
14
|
*/
|
|
15
15
|
import { CaptureLoss, VerifiedReason } from '@reticlehq/core';
|
|
16
|
-
import {
|
|
16
|
+
import { VERDICT_TOOLS } from '../tools/feedback-tools.js';
|
|
17
17
|
import { getBrowserMode } from './browser-mode.js';
|
|
18
18
|
/**
|
|
19
19
|
* The deciding clause, read off the result `decideVerified` wrote it onto.
|
|
@@ -92,7 +92,7 @@ function passedOf(result, status) {
|
|
|
92
92
|
* it verified. That is the product's thesis reduced to a boolean.
|
|
93
93
|
*/
|
|
94
94
|
export function verificationOf(toolName, result, durationMs, brand) {
|
|
95
|
-
if (!
|
|
95
|
+
if (!VERDICT_TOOLS.has(toolName))
|
|
96
96
|
return undefined;
|
|
97
97
|
// A paused session REFUSES the call — nothing driven, nothing asserted. The refusal carries a
|
|
98
98
|
// verdict field so the agent never reads undefined off it, and that field must not be mistaken
|