reflex-state 0.1.0-alpha.1

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.
Files changed (78) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +255 -0
  3. package/README_ja.md +253 -0
  4. package/dist/cli_io.d.ts +8 -0
  5. package/dist/cli_io.js +45 -0
  6. package/dist/composition.d.ts +3 -0
  7. package/dist/composition.js +8 -0
  8. package/dist/core/config.d.ts +37 -0
  9. package/dist/core/config.js +27 -0
  10. package/dist/core/config_validation.d.ts +6 -0
  11. package/dist/core/config_validation.js +109 -0
  12. package/dist/core/engine.d.ts +28 -0
  13. package/dist/core/engine.js +72 -0
  14. package/dist/core/events.d.ts +7 -0
  15. package/dist/core/events.js +56 -0
  16. package/dist/core/extraction.d.ts +10 -0
  17. package/dist/core/extraction.js +119 -0
  18. package/dist/core/metrics.d.ts +52 -0
  19. package/dist/core/metrics.js +100 -0
  20. package/dist/core/reducer.d.ts +18 -0
  21. package/dist/core/reducer.js +206 -0
  22. package/dist/core/serialization.d.ts +11 -0
  23. package/dist/core/serialization.js +160 -0
  24. package/dist/core/state_view.d.ts +27 -0
  25. package/dist/core/state_view.js +28 -0
  26. package/dist/core/types.d.ts +188 -0
  27. package/dist/core/types.js +1 -0
  28. package/dist/core/updater.d.ts +31 -0
  29. package/dist/core/updater.js +27 -0
  30. package/dist/core/verification.d.ts +20 -0
  31. package/dist/core/verification.js +234 -0
  32. package/dist/export_trace_cli.d.ts +2 -0
  33. package/dist/export_trace_cli.js +45 -0
  34. package/dist/index.d.ts +13 -0
  35. package/dist/index.js +8 -0
  36. package/dist/pi/commands.d.ts +3 -0
  37. package/dist/pi/commands.js +123 -0
  38. package/dist/pi/configuration.d.ts +12 -0
  39. package/dist/pi/configuration.js +48 -0
  40. package/dist/pi/extension.d.ts +3 -0
  41. package/dist/pi/extension.js +113 -0
  42. package/dist/pi/index.d.ts +2 -0
  43. package/dist/pi/index.js +5 -0
  44. package/dist/pi/normalization.d.ts +31 -0
  45. package/dist/pi/normalization.js +72 -0
  46. package/dist/pi/persistence.d.ts +15 -0
  47. package/dist/pi/persistence.js +65 -0
  48. package/dist/pi/projection.d.ts +11 -0
  49. package/dist/pi/projection.js +159 -0
  50. package/dist/pi/runtime.d.ts +35 -0
  51. package/dist/pi/runtime.js +139 -0
  52. package/dist/pi/state_block.d.ts +10 -0
  53. package/dist/pi/state_block.js +123 -0
  54. package/dist/pi/trace.d.ts +14 -0
  55. package/dist/pi/trace.js +106 -0
  56. package/dist/replay/runner.d.ts +50 -0
  57. package/dist/replay/runner.js +21 -0
  58. package/dist/replay/trace.d.ts +2 -0
  59. package/dist/replay/trace.js +7 -0
  60. package/dist/replay_cli.d.ts +2 -0
  61. package/dist/replay_cli.js +71 -0
  62. package/dist/typesafe/client.d.ts +21 -0
  63. package/dist/typesafe/client.js +49 -0
  64. package/dist/typesafe/deadline.d.ts +8 -0
  65. package/dist/typesafe/deadline.js +35 -0
  66. package/dist/typesafe/decisions.d.ts +5 -0
  67. package/dist/typesafe/decisions.js +94 -0
  68. package/dist/typesafe/gating.d.ts +7 -0
  69. package/dist/typesafe/gating.js +48 -0
  70. package/dist/typesafe/input.d.ts +3 -0
  71. package/dist/typesafe/input.js +115 -0
  72. package/dist/typesafe/questions.d.ts +5 -0
  73. package/dist/typesafe/questions.js +50 -0
  74. package/dist/typesafe/request_plan.d.ts +19 -0
  75. package/dist/typesafe/request_plan.js +96 -0
  76. package/dist/typesafe/updater.d.ts +17 -0
  77. package/dist/typesafe/updater.js +82 -0
  78. package/package.json +97 -0
@@ -0,0 +1,206 @@
1
+ export function initialState() {
2
+ return {
3
+ version: 2,
4
+ goal: null,
5
+ phase: "unknown",
6
+ taskStatus: "unknown",
7
+ modifiedFiles: [],
8
+ relevantFiles: [],
9
+ verification: {
10
+ build: { status: "not_run", freshness: "unknown" },
11
+ test: { status: "not_run", freshness: "unknown" },
12
+ lint: { status: "not_run", freshness: "unknown" },
13
+ },
14
+ activeBlockers: [],
15
+ workingSet: [],
16
+ observationGeneration: 0,
17
+ pendingChanges: [],
18
+ stateHealth: "valid",
19
+ cursor: { lastEventId: null, eventCount: 0, turnIndex: 0 },
20
+ lastUpdatedAt: "1970-01-01T00:00:00.000Z",
21
+ };
22
+ }
23
+ export function reduce(context) {
24
+ const { state, event, facts, now } = context;
25
+ const blockers = updatedBlockers(context);
26
+ const generation = (state.observationGeneration ?? 0) + (mutationAdvancesGeneration(state, event, facts) ? 1 : 0);
27
+ const pendingChanges = nextPendingChanges(state.pendingChanges ?? [], event, facts);
28
+ const verification = updatedVerification(context, generation, pendingChanges);
29
+ const completed = canComplete(context, blockers, pendingChanges);
30
+ const next = {
31
+ ...state,
32
+ goal: event.type === "user_prompt" ? event.id : state.goal,
33
+ cursor: {
34
+ lastEventId: event.id,
35
+ eventCount: Number(event.id.slice(1)),
36
+ turnIndex: event.turnIndex,
37
+ },
38
+ lastUpdatedAt: now,
39
+ phase: completed ? "done" : (facts.phaseProposal ?? state.phase),
40
+ verification,
41
+ modifiedFiles: uniqueRecent([...state.modifiedFiles, ...facts.fileChanges], 64).sort(),
42
+ relevantFiles: uniqueRecent([...state.relevantFiles, ...facts.filesRead], 32),
43
+ activeBlockers: blockers,
44
+ workingSet: boundedWorkingSet(context, blockers),
45
+ observationGeneration: generation,
46
+ pendingChanges,
47
+ stateHealth: event.type === "session_resume" ? "valid" : (state.stateHealth ?? "valid"),
48
+ taskStatus: derivedTaskStatus(context, { blocked: blockers.length > 0, completed }),
49
+ };
50
+ const changes = stateChanges(state, next);
51
+ return { state: next, changes };
52
+ }
53
+ function mutationAdvancesGeneration(state, event, facts) {
54
+ if (!facts.mutation)
55
+ return false;
56
+ if (event.type !== "tool_result")
57
+ return true;
58
+ return (!facts.mutation.operationId ||
59
+ !(state.pendingChanges ?? []).includes(facts.mutation.operationId));
60
+ }
61
+ export function admitsEvidence(event, facts) {
62
+ if (facts.fileChanges.length)
63
+ return true;
64
+ if (event.type !== "tool_result" || ["read", "grep", "find", "ls"].includes(event.toolName))
65
+ return false;
66
+ return event.isError || facts.verification !== undefined;
67
+ }
68
+ function updatedBlockers({ state, event, facts, decisions, evidence, }) {
69
+ const blockers = state.activeBlockers.filter((blocker) => {
70
+ if (facts.deterministicallyResolved.includes(blocker.eventId))
71
+ return false;
72
+ if (blocker.origin !== "tool_error" || event.type !== "tool_result" || event.isError)
73
+ return true;
74
+ return !decisions.resolvedBlockers.some((item) => item.eventId === blocker.eventId &&
75
+ evidence.has(blocker.eventId) &&
76
+ accepted(item.decision) === true);
77
+ });
78
+ if (facts.verification?.status === "failed") {
79
+ if (!facts.verification.attributable || !facts.verification.checkKey)
80
+ return blockers;
81
+ blockers.push({
82
+ eventId: event.id,
83
+ origin: "verification",
84
+ kind: facts.verification.kind,
85
+ checkKey: facts.verification.checkKey,
86
+ category: accepted(decisions.failureCategory) ?? "unknown",
87
+ });
88
+ }
89
+ else if (event.type === "tool_result" &&
90
+ event.isError &&
91
+ accepted(decisions.blockerIntroduced) === true) {
92
+ blockers.push({
93
+ eventId: event.id,
94
+ origin: "tool_error",
95
+ category: accepted(decisions.failureCategory) ?? "unknown",
96
+ });
97
+ }
98
+ return blockers;
99
+ }
100
+ function boundedWorkingSet(context, blockers) {
101
+ const { state, event, facts, decisions, config } = context;
102
+ let workingSet = state.workingSet.filter((id) => !facts.supersededInWorkingSet.includes(id));
103
+ if (admitsEvidence(event, facts) && !workingSet.includes(event.id))
104
+ workingSet.push(event.id);
105
+ const cap = config.limits.maxWorkingSetEvents;
106
+ if (workingSet.length <= cap)
107
+ return workingSet;
108
+ const resolved = state.activeBlockers.filter((old) => !blockers.some((blocker) => blocker.eventId === old.eventId));
109
+ workingSet = workingSet.filter((id) => !resolved.some((blocker) => blocker.eventId === id));
110
+ if (workingSet.length <= cap)
111
+ return workingSet;
112
+ workingSet = workingSet.filter((id) => !decisions.relevance.some((item) => item.eventId === id && accepted(item.decision) === false));
113
+ return workingSet.slice(-cap);
114
+ }
115
+ function derivedTaskStatus(context, outcome) {
116
+ if (context.event.type === "user_prompt")
117
+ return "in_progress";
118
+ if (outcome.blocked)
119
+ return "blocked";
120
+ if (outcome.completed)
121
+ return "completed";
122
+ if (context.event.type === "agent_end")
123
+ return "in_progress";
124
+ if (context.state.taskStatus === "completed")
125
+ return "completed";
126
+ return "in_progress";
127
+ }
128
+ function canComplete(context, blockers, pendingChanges) {
129
+ return (context.event.type === "agent_end" &&
130
+ context.event.stopReason === "stop" &&
131
+ blockers.length === 0 &&
132
+ pendingChanges.length === 0 &&
133
+ context.state.stateHealth === "valid" &&
134
+ context.state.observationGeneration !== undefined &&
135
+ context.state.pendingChanges !== undefined &&
136
+ accepted(context.decisions.taskComplete) === true);
137
+ }
138
+ function nextPendingChanges(pending, event, facts) {
139
+ const next = [...pending];
140
+ if (event.type === "tool_call" && facts.mutation && !next.includes(event.id))
141
+ next.push(event.id);
142
+ if (event.type === "tool_result" && facts.mutation?.operationId)
143
+ return next.filter((id) => id !== facts.mutation?.operationId);
144
+ return next;
145
+ }
146
+ function updatedVerification(context, generation, pendingChanges) {
147
+ const { state, event, facts } = context;
148
+ const next = {
149
+ build: { ...state.verification.build },
150
+ test: { ...state.verification.test },
151
+ lint: { ...state.verification.lint },
152
+ };
153
+ if (facts.mutation) {
154
+ for (const kind of ["build", "test", "lint"])
155
+ if (next[kind].status !== "not_run")
156
+ next[kind] = { ...next[kind], freshness: "stale" };
157
+ }
158
+ if (!facts.verification) {
159
+ if (event.type === "session_resume") {
160
+ for (const kind of ["build", "test", "lint"])
161
+ if (next[kind].status !== "not_run")
162
+ next[kind] = { ...next[kind], freshness: "stale" };
163
+ }
164
+ return next;
165
+ }
166
+ const fact = facts.verification;
167
+ const current = next[fact.kind];
168
+ const { checkKey: _previousCheckKey, startedEvent: _previousStartedEvent, unknownReason: _previousUnknownReason, ...stable } = current;
169
+ next[fact.kind] = {
170
+ ...stable,
171
+ status: fact.status,
172
+ freshness: fact.freshness ?? (fact.status === "running" ? "unknown" : "current"),
173
+ evidence: event.id,
174
+ command: fact.command,
175
+ cwd: fact.cwd,
176
+ ...(fact.checkKey ? { checkKey: fact.checkKey } : {}),
177
+ ...(fact.startedEvent ? { startedEvent: fact.startedEvent } : {}),
178
+ ...(fact.observedGeneration !== undefined
179
+ ? { observedGeneration: fact.observedGeneration }
180
+ : event.type === "tool_call"
181
+ ? { observedGeneration: generation, startedEvent: event.id }
182
+ : {}),
183
+ attributable: fact.attributable,
184
+ ...(fact.unknownReason ? { unknownReason: fact.unknownReason } : {}),
185
+ };
186
+ if (event.type === "tool_result" && pendingChanges.length > 0) {
187
+ next[fact.kind] = {
188
+ ...next[fact.kind],
189
+ freshness: fact.attributable ? "stale" : "unknown",
190
+ };
191
+ }
192
+ return next;
193
+ }
194
+ function uniqueRecent(values, cap) {
195
+ return [...new Set([...values].reverse())].slice(0, cap).reverse();
196
+ }
197
+ function stateChanges(before, after) {
198
+ return Object.keys(after)
199
+ .filter((key) => key !== "lastUpdatedAt" &&
200
+ key !== "cursor" &&
201
+ JSON.stringify(before[key]) !== JSON.stringify(after[key]))
202
+ .map((key) => key + ": " + JSON.stringify(before[key]) + " -> " + JSON.stringify(after[key]));
203
+ }
204
+ function accepted(decision) {
205
+ return decision?.gate === "applied" && !decision.shadow ? decision.value : null;
206
+ }
@@ -0,0 +1,11 @@
1
+ import type { AgentEvent, StateTransitionRecord } from "./types.js";
2
+ export declare class InvalidTraceError extends Error {
3
+ constructor(message: string);
4
+ }
5
+ export declare class LegacyTraceError extends Error {
6
+ constructor(message?: string);
7
+ }
8
+ export declare function isRecord(value: unknown): value is Record<string, unknown>;
9
+ export declare function parseJsonLines<T>(text: string, parse: (value: unknown) => T): T[];
10
+ export declare function parseEvent(value: unknown): AgentEvent;
11
+ export declare function parseTransition(value: unknown): StateTransitionRecord;
@@ -0,0 +1,160 @@
1
+ export class InvalidTraceError extends Error {
2
+ constructor(message) {
3
+ super(message);
4
+ this.name = "InvalidTraceError";
5
+ }
6
+ }
7
+ export class LegacyTraceError extends Error {
8
+ constructor(message = "Legacy ReflexState state requires reset") {
9
+ super(message);
10
+ this.name = "LegacyTraceError";
11
+ }
12
+ }
13
+ export function isRecord(value) {
14
+ return value !== null && typeof value === "object" && !Array.isArray(value);
15
+ }
16
+ export function parseJsonLines(text, parse) {
17
+ return text.split("\n").flatMap((line, index) => {
18
+ if (!line.trim())
19
+ return [];
20
+ try {
21
+ return [parse(JSON.parse(line))];
22
+ }
23
+ catch {
24
+ throw new InvalidTraceError("Invalid trace at line " + (index + 1));
25
+ }
26
+ });
27
+ }
28
+ export function parseEvent(value) {
29
+ if (!isRecord(value) ||
30
+ typeof value.id !== "string" ||
31
+ !/^E\d+$/.test(value.id) ||
32
+ typeof value.timestamp !== "string" ||
33
+ !Number.isFinite(Date.parse(value.timestamp)) ||
34
+ !Number.isSafeInteger(value.turnIndex) ||
35
+ Number(value.turnIndex) < 0 ||
36
+ !isRecord(value.source) ||
37
+ !["tool_call", "user_prompt", "assistant_message"].includes(String(value.source.kind)) ||
38
+ typeof value.source.timestamp !== "number" ||
39
+ !Number.isFinite(value.source.timestamp)) {
40
+ throw new InvalidTraceError("Invalid event envelope");
41
+ }
42
+ if (value.type === "user_prompt" && typeof value.text === "string")
43
+ return value;
44
+ if (value.type === "file_change" && strings(value.paths))
45
+ return value;
46
+ if (value.type === "session_resume" && ["resume", "branch_switch"].includes(String(value.reason)))
47
+ return value;
48
+ if (value.type === "agent_end" &&
49
+ excerpt(value.finalText) &&
50
+ ["stop", "length", "error", "aborted"].includes(String(value.stopReason)))
51
+ return value;
52
+ if (typeof value.toolCallId !== "string" || typeof value.toolName !== "string")
53
+ throw new InvalidTraceError("Invalid tool event");
54
+ if (value.type === "tool_call" &&
55
+ isRecord(value.input) &&
56
+ (value.commandTruncated === undefined || typeof value.commandTruncated === "boolean") &&
57
+ (value.cwd === undefined || typeof value.cwd === "string"))
58
+ return value;
59
+ if (value.type === "tool_result" && typeof value.isError === "boolean" && excerpt(value.excerpt))
60
+ return value;
61
+ throw new InvalidTraceError("Unknown or invalid event payload");
62
+ }
63
+ export function parseTransition(value) {
64
+ if (isRecord(value) && isRecord(value.after) && value.after.version === 1)
65
+ throw new LegacyTraceError();
66
+ if (!isRecord(value) ||
67
+ typeof value.id !== "string" ||
68
+ !/^T\d+$/.test(value.id) ||
69
+ typeof value.timestamp !== "string" ||
70
+ !Number.isFinite(Date.parse(value.timestamp)) ||
71
+ typeof value.updater !== "string" ||
72
+ typeof value.cwd !== "string" ||
73
+ !isRecord(value.config) ||
74
+ !strings(value.changes) ||
75
+ !isRecord(value.after) ||
76
+ !Number.isSafeInteger(value.after.version) ||
77
+ !isRecord(value.after.cursor) ||
78
+ !isRecord(value.after.verification) ||
79
+ !strings(value.after.workingSet) ||
80
+ !strings(value.after.modifiedFiles) ||
81
+ !strings(value.after.relevantFiles) ||
82
+ !Array.isArray(value.after.activeBlockers) ||
83
+ !isRecord(value.decisions) ||
84
+ !Array.isArray(value.decisions.resolvedBlockers) ||
85
+ !Array.isArray(value.decisions.relevance) ||
86
+ !isRecord(value.decisions.telemetry) ||
87
+ !strings(value.decisions.telemetry.questionIds)) {
88
+ throw new InvalidTraceError("Invalid or unsupported ReflexState transition");
89
+ }
90
+ if (value.after.version !== 2)
91
+ throw new InvalidTraceError("Unsupported ReflexState state version");
92
+ if (!Number.isSafeInteger(value.after.observationGeneration) ||
93
+ Number(value.after.observationGeneration) < 0 ||
94
+ !strings(value.after.pendingChanges) ||
95
+ !value.after.pendingChanges.every((id) => /^E\d+$/.test(id)) ||
96
+ !["valid", "legacy_state_requires_reset", "invalid"].includes(String(value.after.stateHealth)))
97
+ throw new InvalidTraceError("Invalid ReflexState state metadata");
98
+ if (!validCursor(value.after.cursor))
99
+ throw new InvalidTraceError("Invalid state cursor");
100
+ if (!validBlockers(value.after.activeBlockers))
101
+ throw new InvalidTraceError("Invalid blockers");
102
+ if (!validVerification(value.after.verification))
103
+ throw new InvalidTraceError("Invalid verification freshness");
104
+ parseEvent(value.event);
105
+ return value;
106
+ }
107
+ function strings(value) {
108
+ return Array.isArray(value) && value.every((item) => typeof item === "string");
109
+ }
110
+ function excerpt(value) {
111
+ return (isRecord(value) &&
112
+ typeof value.head === "string" &&
113
+ typeof value.sha256 === "string" &&
114
+ typeof value.truncated === "boolean" &&
115
+ Number.isSafeInteger(value.totalChars) &&
116
+ Number(value.totalChars) >= 0 &&
117
+ (value.tail === undefined || typeof value.tail === "string"));
118
+ }
119
+ function validCursor(value) {
120
+ return ((value.lastEventId === null ||
121
+ (typeof value.lastEventId === "string" && /^E\d+$/.test(value.lastEventId))) &&
122
+ Number.isSafeInteger(value.eventCount) &&
123
+ Number(value.eventCount) >= 0 &&
124
+ Number.isSafeInteger(value.turnIndex) &&
125
+ Number(value.turnIndex) >= 0);
126
+ }
127
+ function validBlockers(value) {
128
+ const categories = new Set([
129
+ "implementation",
130
+ "environment",
131
+ "dependency",
132
+ "test",
133
+ "permissions",
134
+ "network",
135
+ "unknown",
136
+ ]);
137
+ return value.every((item) => isRecord(item) &&
138
+ typeof item.eventId === "string" &&
139
+ /^E\d+$/.test(item.eventId) &&
140
+ typeof item.origin === "string" &&
141
+ (item.origin === "tool_error"
142
+ ? !Object.hasOwn(item, "kind")
143
+ : item.origin === "verification" &&
144
+ ["build", "test", "lint"].includes(String(item.kind)) &&
145
+ typeof item.checkKey === "string") &&
146
+ categories.has(String(item.category)));
147
+ }
148
+ function validVerification(value) {
149
+ return ["build", "test", "lint"].every((kind) => {
150
+ const item = value[kind];
151
+ return (isRecord(item) &&
152
+ ["not_run", "running", "passed", "failed", "unknown"].includes(String(item.status)) &&
153
+ ["current", "stale", "unknown"].includes(String(item.freshness)) &&
154
+ (item.evidence === undefined ||
155
+ (typeof item.evidence === "string" && /^E\d+$/.test(item.evidence))) &&
156
+ (item.checkKey === undefined || typeof item.checkKey === "string") &&
157
+ (item.command === undefined || typeof item.command === "string") &&
158
+ (item.cwd === undefined || typeof item.cwd === "string"));
159
+ });
160
+ }
@@ -0,0 +1,27 @@
1
+ import type { ReflexStateConfig } from "./config.js";
2
+ import type { AgentEvent, Blocker, EventId, HotState } from "./types.js";
3
+ interface BlockerView {
4
+ readonly unresolvedTotal: number;
5
+ readonly shownCount: number;
6
+ readonly omittedCount: number;
7
+ readonly blockers: readonly Blocker[];
8
+ }
9
+ interface WorkingSetView {
10
+ readonly total: number;
11
+ readonly shownCount: number;
12
+ readonly omittedCount: number;
13
+ readonly events: readonly EventId[];
14
+ }
15
+ export declare function blockerView(state: HotState, config: ReflexStateConfig): BlockerView;
16
+ export declare function workingSetView(state: HotState, limit: number): WorkingSetView;
17
+ export declare function stateView(state: HotState, evidence: ReadonlyMap<EventId, AgentEvent>, config: ReflexStateConfig): {
18
+ blockers: BlockerView;
19
+ workingSet: {
20
+ available: `E${string}`[];
21
+ total: number;
22
+ shownCount: number;
23
+ omittedCount: number;
24
+ events: readonly EventId[];
25
+ };
26
+ };
27
+ export {};
@@ -0,0 +1,28 @@
1
+ export function blockerView(state, config) {
2
+ const limit = config.limits.maxProjectedBlockers;
3
+ const blockers = state.activeBlockers.slice(-limit);
4
+ return {
5
+ unresolvedTotal: state.activeBlockers.length,
6
+ shownCount: blockers.length,
7
+ omittedCount: state.activeBlockers.length - blockers.length,
8
+ blockers,
9
+ };
10
+ }
11
+ export function workingSetView(state, limit) {
12
+ const events = state.workingSet.slice(-limit);
13
+ return {
14
+ total: state.workingSet.length,
15
+ shownCount: events.length,
16
+ omittedCount: state.workingSet.length - events.length,
17
+ events,
18
+ };
19
+ }
20
+ export function stateView(state, evidence, config) {
21
+ return {
22
+ blockers: blockerView(state, config),
23
+ workingSet: {
24
+ ...workingSetView(state, config.limits.maxWorkingSetEvents),
25
+ available: state.workingSet.filter((id) => evidence.has(id)),
26
+ },
27
+ };
28
+ }
@@ -0,0 +1,188 @@
1
+ import type { ReflexStateConfig } from "./config.js";
2
+ export type EventId = `E${string}`;
3
+ export type AgentPhase = "planning" | "exploring" | "editing" | "testing" | "debugging" | "done" | "unknown";
4
+ export type TaskStatus = "in_progress" | "blocked" | "completed" | "unknown";
5
+ export type VerificationKind = "build" | "test" | "lint";
6
+ export type VerificationStatus = "not_run" | "running" | "passed" | "failed" | "unknown";
7
+ export type VerificationFreshness = "current" | "stale" | "unknown";
8
+ export type BlockerCategory = "implementation" | "environment" | "dependency" | "test" | "permissions" | "network" | "unknown";
9
+ export interface SourceRef {
10
+ readonly kind: "tool_call" | "user_prompt" | "assistant_message";
11
+ readonly toolCallId?: string;
12
+ readonly timestamp: number;
13
+ }
14
+ export interface Excerpt {
15
+ readonly head: string;
16
+ readonly tail?: string;
17
+ readonly totalChars: number;
18
+ readonly sha256: string;
19
+ readonly truncated: boolean;
20
+ }
21
+ interface BaseEvent {
22
+ readonly id: EventId;
23
+ readonly timestamp: string;
24
+ readonly turnIndex: number;
25
+ readonly source: SourceRef;
26
+ }
27
+ export interface UserPromptEvent extends BaseEvent {
28
+ readonly type: "user_prompt";
29
+ readonly text: string;
30
+ }
31
+ export interface ToolCallEvent extends BaseEvent {
32
+ readonly type: "tool_call";
33
+ readonly toolCallId: string;
34
+ readonly toolName: string;
35
+ readonly input: Readonly<Record<string, unknown>>;
36
+ readonly commandTruncated?: boolean;
37
+ readonly cwd?: string;
38
+ }
39
+ export interface ToolResultEvent extends BaseEvent {
40
+ readonly type: "tool_result";
41
+ readonly toolCallId: string;
42
+ readonly toolName: string;
43
+ readonly isError: boolean;
44
+ readonly excerpt: Excerpt;
45
+ }
46
+ export interface AgentEndEvent extends BaseEvent {
47
+ readonly type: "agent_end";
48
+ readonly finalText: Excerpt;
49
+ readonly stopReason: "stop" | "length" | "error" | "aborted";
50
+ }
51
+ export interface FileChangeEvent extends BaseEvent {
52
+ readonly type: "file_change";
53
+ readonly paths: readonly string[];
54
+ }
55
+ export interface SessionResumeEvent extends BaseEvent {
56
+ readonly type: "session_resume";
57
+ readonly reason: "resume" | "branch_switch";
58
+ }
59
+ export type AgentEvent = UserPromptEvent | ToolCallEvent | ToolResultEvent | AgentEndEvent | FileChangeEvent | SessionResumeEvent;
60
+ export interface VerificationState {
61
+ readonly status: VerificationStatus;
62
+ readonly freshness?: VerificationFreshness;
63
+ readonly evidence?: EventId;
64
+ readonly command?: string;
65
+ readonly cwd?: string;
66
+ readonly checkKey?: string;
67
+ readonly observedGeneration?: number;
68
+ readonly startedEvent?: EventId;
69
+ readonly attributable?: boolean;
70
+ readonly unknownReason?: string;
71
+ }
72
+ export type Blocker = {
73
+ readonly eventId: EventId;
74
+ readonly category: BlockerCategory;
75
+ } & ({
76
+ readonly origin: "verification";
77
+ readonly kind: VerificationKind;
78
+ readonly checkKey?: string;
79
+ } | {
80
+ readonly origin: "tool_error";
81
+ readonly kind?: never;
82
+ });
83
+ export interface HotState {
84
+ readonly version: 2;
85
+ readonly goal: EventId | null;
86
+ readonly phase: AgentPhase;
87
+ readonly taskStatus: TaskStatus;
88
+ readonly modifiedFiles: readonly string[];
89
+ readonly relevantFiles: readonly string[];
90
+ readonly verification: Readonly<Record<VerificationKind, VerificationState>>;
91
+ readonly activeBlockers: readonly Blocker[];
92
+ readonly workingSet: readonly EventId[];
93
+ readonly observationGeneration?: number;
94
+ readonly pendingChanges?: readonly EventId[];
95
+ readonly stateHealth?: "valid" | "legacy_state_requires_reset" | "invalid";
96
+ readonly cursor: {
97
+ readonly lastEventId: EventId | null;
98
+ readonly eventCount: number;
99
+ readonly turnIndex: number;
100
+ };
101
+ readonly lastUpdatedAt: string;
102
+ }
103
+ export interface VerificationFact {
104
+ readonly kind: VerificationKind;
105
+ readonly status: VerificationStatus;
106
+ readonly command: string;
107
+ readonly cwd: string;
108
+ readonly compound: boolean;
109
+ readonly attributable: boolean;
110
+ readonly checkKey?: string;
111
+ readonly unknownReason?: string;
112
+ readonly startedEvent?: EventId;
113
+ readonly observedGeneration?: number;
114
+ readonly freshness?: VerificationFreshness;
115
+ }
116
+ export interface MutationFact {
117
+ readonly operationId?: EventId;
118
+ readonly possible: boolean;
119
+ readonly completed: boolean;
120
+ readonly paths: readonly string[];
121
+ }
122
+ export interface DeterministicFacts {
123
+ readonly fileChanges: readonly string[];
124
+ readonly filesRead: readonly string[];
125
+ readonly exitCode?: number;
126
+ readonly verification?: VerificationFact;
127
+ readonly mutation?: MutationFact;
128
+ readonly phaseProposal: AgentPhase | null;
129
+ readonly deterministicallyResolved: readonly EventId[];
130
+ readonly supersededInWorkingSet: readonly EventId[];
131
+ }
132
+ export type Gate = "applied" | "uncertain" | "skipped" | "error";
133
+ export interface GatedDecision<T> {
134
+ readonly value: T | null;
135
+ readonly gate: Gate;
136
+ readonly probability?: number;
137
+ readonly confidence?: number;
138
+ readonly probabilities?: Readonly<Record<string, number>>;
139
+ readonly shadow?: boolean;
140
+ }
141
+ export interface SemanticDecisions {
142
+ readonly blockerIntroduced?: GatedDecision<boolean>;
143
+ readonly failureCategory?: GatedDecision<BlockerCategory>;
144
+ readonly resolvedBlockers: readonly {
145
+ readonly eventId: EventId;
146
+ readonly decision: GatedDecision<boolean>;
147
+ }[];
148
+ readonly relevance: readonly {
149
+ readonly eventId: EventId;
150
+ readonly decision: GatedDecision<boolean>;
151
+ }[];
152
+ readonly taskComplete?: GatedDecision<boolean>;
153
+ readonly phaseShadow?: GatedDecision<AgentPhase>;
154
+ readonly telemetry: {
155
+ readonly latencyMs?: number;
156
+ readonly inputTokens?: number;
157
+ readonly outputTokens?: number;
158
+ readonly model?: string;
159
+ readonly error?: string;
160
+ readonly responseShape?: Readonly<Record<string, string>>;
161
+ readonly questionsAsked: number;
162
+ readonly questionIds: readonly string[];
163
+ };
164
+ }
165
+ export interface ProjectionMeasurement {
166
+ readonly mode?: "disabled" | "append" | "current-run";
167
+ readonly messagesBefore: number;
168
+ readonly messagesAfter: number;
169
+ readonly messagesOmitted?: number;
170
+ readonly charsBefore: number;
171
+ readonly charsAfter: number;
172
+ readonly stateBlockChars?: number;
173
+ readonly fallback?: string;
174
+ }
175
+ export interface StateTransitionRecord {
176
+ readonly id: string;
177
+ readonly timestamp: string;
178
+ readonly event: AgentEvent;
179
+ readonly after: HotState;
180
+ readonly deterministicPhase?: AgentPhase;
181
+ readonly changes: readonly string[];
182
+ readonly decisions: SemanticDecisions;
183
+ readonly updater: string;
184
+ readonly config: ReflexStateConfig;
185
+ readonly cwd: string;
186
+ readonly projection?: ProjectionMeasurement;
187
+ }
188
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,31 @@
1
+ import type { ReflexStateConfig } from "./config.js";
2
+ import type { StateTransitionRecord } from "./types.js";
3
+ import type { AgentEvent, DeterministicFacts, EventId, HotState, SemanticDecisions } from "./types.js";
4
+ export interface StateUpdateContext {
5
+ readonly state: HotState;
6
+ readonly event: AgentEvent;
7
+ readonly facts: DeterministicFacts;
8
+ readonly evidence: ReadonlyMap<EventId, AgentEvent>;
9
+ readonly config: ReflexStateConfig;
10
+ }
11
+ export interface StateUpdater {
12
+ readonly name: string;
13
+ readonly health?: UpdaterHealth;
14
+ evaluate(context: StateUpdateContext, signal?: AbortSignal): Promise<SemanticDecisions>;
15
+ }
16
+ export interface UpdaterHealth {
17
+ readonly status: "ok" | "degraded" | "disabled";
18
+ readonly circuit: "closed" | "open" | "half_open";
19
+ readonly reason?: string;
20
+ }
21
+ export declare function emptyDecisions(): SemanticDecisions;
22
+ export declare class NoopStateUpdater implements StateUpdater {
23
+ readonly name = "noop";
24
+ evaluate(): Promise<SemanticDecisions>;
25
+ }
26
+ export declare class RecordedDecisionsUpdater implements StateUpdater {
27
+ readonly name = "recorded";
28
+ private readonly records;
29
+ constructor(records: readonly StateTransitionRecord[]);
30
+ evaluate(context: StateUpdateContext): Promise<SemanticDecisions>;
31
+ }