@agent-plan/core 0.2.23 → 0.2.24
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/dist/handoff-context.d.ts +60 -0
- package/dist/handoff-context.d.ts.map +1 -0
- package/dist/handoff-context.js +150 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/plan-store.d.ts +28 -0
- package/dist/plan-store.d.ts.map +1 -1
- package/dist/plan-store.js +142 -0
- package/dist/planner-rules.js +2 -2
- package/dist/read-tracking.d.ts +60 -23
- package/dist/read-tracking.d.ts.map +1 -1
- package/dist/read-tracking.js +131 -45
- package/dist/schema.d.ts +324 -0
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +12 -0
- package/dist/task-start-outcome.d.ts +23 -0
- package/dist/task-start-outcome.d.ts.map +1 -0
- package/dist/task-start-outcome.js +23 -0
- package/package.json +1 -1
package/dist/read-tracking.d.ts
CHANGED
|
@@ -1,49 +1,86 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Session-scoped, ordered context-read enforcement for agent lifecycle operations.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* feature with
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* Linked requirements remain a separate explicit-read gate. They are recorded
|
|
10
|
-
* only through the requirement list tool and never by an entity read.
|
|
4
|
+
* The first complete read for a task must be task(full) → phase(full) →
|
|
5
|
+
* feature(full), with linked requirements read independently. A persisted
|
|
6
|
+
* sessionInfo attestation may satisfy later checks in the same session while
|
|
7
|
+
* every entity's updatedAt remains at or before its attestation timestamp.
|
|
11
8
|
*/
|
|
9
|
+
type SessionInfoEntry = {
|
|
10
|
+
sessionId: string;
|
|
11
|
+
createdAt: string;
|
|
12
|
+
};
|
|
13
|
+
type ReadTrackedEntity = {
|
|
14
|
+
updatedAt: string;
|
|
15
|
+
sessionInfo?: SessionInfoEntry[];
|
|
16
|
+
};
|
|
12
17
|
export type ContextReadEligibility = {
|
|
13
18
|
eligible: boolean;
|
|
14
19
|
reason: string;
|
|
15
20
|
};
|
|
16
|
-
|
|
21
|
+
export interface SessionContextReadInput {
|
|
22
|
+
sessionId: string;
|
|
23
|
+
taskId: string;
|
|
24
|
+
phaseId: string;
|
|
25
|
+
featureId?: string;
|
|
26
|
+
task?: ReadTrackedEntity;
|
|
27
|
+
phase?: ReadTrackedEntity;
|
|
28
|
+
feature?: ReadTrackedEntity;
|
|
29
|
+
requirements?: Array<ReadTrackedEntity & {
|
|
30
|
+
id: string;
|
|
31
|
+
}>;
|
|
32
|
+
requirementIds?: string[];
|
|
33
|
+
}
|
|
34
|
+
/** Record a full feature read in the default compatibility session. */
|
|
17
35
|
export declare function markFeatureRead(featureId: string): void;
|
|
18
|
-
/** Record a full
|
|
36
|
+
/** Record a full feature read for an explicit harness session. */
|
|
37
|
+
export declare function markFeatureReadForSessionId(sessionId: string, featureId: string): void;
|
|
38
|
+
/** Record a full phase read in the default compatibility session. */
|
|
19
39
|
export declare function markPhaseRead(phaseId: string, _featureId?: string): void;
|
|
20
|
-
/** Record a full
|
|
40
|
+
/** Record a full phase read for an explicit harness session. */
|
|
41
|
+
export declare function markPhaseReadForSessionId(sessionId: string, phaseId: string): void;
|
|
42
|
+
/** Record a full task read in the default compatibility session. */
|
|
21
43
|
export declare function markTaskRead(taskId: string, _phaseId?: string, _featureId?: string): void;
|
|
22
|
-
/** Record
|
|
44
|
+
/** Record a full task read for an explicit harness session. */
|
|
45
|
+
export declare function markTaskReadForSessionId(sessionId: string, taskId: string): void;
|
|
46
|
+
/** Record that a requirement was explicitly read in the default session. */
|
|
23
47
|
export declare function markRequirementRead(requirementId: string): void;
|
|
48
|
+
/** Record that a requirement was explicitly read for an explicit session. */
|
|
49
|
+
export declare function markRequirementReadForSessionId(sessionId: string, requirementId: string): void;
|
|
24
50
|
/**
|
|
25
|
-
*
|
|
26
|
-
*
|
|
51
|
+
* Evaluate exact in-memory read ordering first, then a persisted attestation
|
|
52
|
+
* for the same session and current entity revisions. A previously persisted
|
|
53
|
+
* but now stale attestation always wins over stale in-memory ordering.
|
|
27
54
|
*/
|
|
55
|
+
export declare function contextReadEligibilityForSession(input: SessionContextReadInput): ContextReadEligibility;
|
|
56
|
+
/** Return true only when the persisted attestation covers the current revisions. */
|
|
57
|
+
export declare function hasValidSessionAttestation(input: SessionContextReadInput): boolean;
|
|
58
|
+
/** Compatibility check for the default session and in-memory reads only. */
|
|
28
59
|
export declare function contextReadEligibility(taskId: string, phaseId: string, featureId?: string): ContextReadEligibility;
|
|
60
|
+
/** Legacy parent-read check retained for callers that only need independent parent state. */
|
|
61
|
+
export declare function hasReadParents(featureId: string | undefined, phaseId: string): boolean;
|
|
29
62
|
/**
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* contextReadEligibility so the task and ordering cannot be bypassed.
|
|
63
|
+
* Whether linked requirements are read in memory or have valid persisted
|
|
64
|
+
* attestations for the current session and entity revisions.
|
|
33
65
|
*/
|
|
34
|
-
export declare function
|
|
35
|
-
|
|
66
|
+
export declare function hasReadRequirementsForSession(sessionId: string, requirementIds: string[], requirements?: Array<ReadTrackedEntity & {
|
|
67
|
+
id: string;
|
|
68
|
+
}>): boolean;
|
|
69
|
+
/** Legacy requirement check for the default compatibility session. */
|
|
36
70
|
export declare function hasReadRequirements(requirementIds: string[]): boolean;
|
|
37
|
-
/** Clear
|
|
38
|
-
export declare function invalidateReads(): void;
|
|
71
|
+
/** Clear one session's read state, or all state for legacy callers. */
|
|
72
|
+
export declare function invalidateReads(sessionId?: string): void;
|
|
73
|
+
/** Initialize an explicit session without clearing other harness sessions. */
|
|
74
|
+
export declare function startReadSession(sessionId: string): void;
|
|
39
75
|
/** Compatibility advisory for non-lifecycle callers. */
|
|
40
76
|
export declare function parentReadAdvisory(featureId: string | undefined, phaseId: string): string;
|
|
41
77
|
/** Advisory text for the separate linked-requirements gate. */
|
|
42
78
|
export declare function requirementReadAdvisory(requirementIds: string[]): string;
|
|
43
|
-
/** Snapshot for diagnostics
|
|
44
|
-
export declare function readTrackingSnapshot(): {
|
|
79
|
+
/** Snapshot for diagnostics. */
|
|
80
|
+
export declare function readTrackingSnapshot(sessionId?: string): {
|
|
45
81
|
features: string[];
|
|
46
82
|
phases: string[];
|
|
47
83
|
requirements: string[];
|
|
48
84
|
};
|
|
85
|
+
export {};
|
|
49
86
|
//# sourceMappingURL=read-tracking.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"read-tracking.d.ts","sourceRoot":"","sources":["../src/read-tracking.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"read-tracking.d.ts","sourceRoot":"","sources":["../src/read-tracking.ts"],"names":[],"mappings":"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"}
|
package/dist/read-tracking.js
CHANGED
|
@@ -1,46 +1,82 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Session-scoped, ordered context-read enforcement for agent lifecycle operations.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* feature with
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* Linked requirements remain a separate explicit-read gate. They are recorded
|
|
10
|
-
* only through the requirement list tool and never by an entity read.
|
|
4
|
+
* The first complete read for a task must be task(full) → phase(full) →
|
|
5
|
+
* feature(full), with linked requirements read independently. A persisted
|
|
6
|
+
* sessionInfo attestation may satisfy later checks in the same session while
|
|
7
|
+
* every entity's updatedAt remains at or before its attestation timestamp.
|
|
11
8
|
*/
|
|
12
|
-
|
|
9
|
+
const DEFAULT_SESSION_ID = "__default__";
|
|
10
|
+
const newState = () => ({
|
|
13
11
|
tasks: new Map(),
|
|
14
12
|
phases: new Map(),
|
|
15
13
|
features: new Map(),
|
|
16
14
|
requirements: new Set(),
|
|
17
15
|
nextSequence: 0,
|
|
18
|
-
};
|
|
19
|
-
|
|
16
|
+
});
|
|
17
|
+
const states = new Map([[DEFAULT_SESSION_ID, newState()]]);
|
|
18
|
+
function normalizeSessionId(sessionId) {
|
|
19
|
+
return sessionId?.trim() || DEFAULT_SESSION_ID;
|
|
20
|
+
}
|
|
21
|
+
function stateFor(sessionId) {
|
|
22
|
+
const key = normalizeSessionId(sessionId);
|
|
23
|
+
let state = states.get(key);
|
|
24
|
+
if (!state) {
|
|
25
|
+
state = newState();
|
|
26
|
+
states.set(key, state);
|
|
27
|
+
}
|
|
28
|
+
return state;
|
|
29
|
+
}
|
|
30
|
+
function record(map, id, state) {
|
|
20
31
|
state.nextSequence += 1;
|
|
21
32
|
map.set(id, state.nextSequence);
|
|
22
33
|
}
|
|
23
|
-
|
|
34
|
+
function markTaskReadForSession(sessionId, taskId) {
|
|
35
|
+
const state = stateFor(sessionId);
|
|
36
|
+
record(state.tasks, taskId, state);
|
|
37
|
+
}
|
|
38
|
+
function markPhaseReadForSession(sessionId, phaseId) {
|
|
39
|
+
const state = stateFor(sessionId);
|
|
40
|
+
record(state.phases, phaseId, state);
|
|
41
|
+
}
|
|
42
|
+
function markFeatureReadForSession(sessionId, featureId) {
|
|
43
|
+
const state = stateFor(sessionId);
|
|
44
|
+
record(state.features, featureId, state);
|
|
45
|
+
}
|
|
46
|
+
/** Record a full feature read in the default compatibility session. */
|
|
24
47
|
export function markFeatureRead(featureId) {
|
|
25
|
-
|
|
48
|
+
markFeatureReadForSession(DEFAULT_SESSION_ID, featureId);
|
|
49
|
+
}
|
|
50
|
+
/** Record a full feature read for an explicit harness session. */
|
|
51
|
+
export function markFeatureReadForSessionId(sessionId, featureId) {
|
|
52
|
+
markFeatureReadForSession(sessionId, featureId);
|
|
26
53
|
}
|
|
27
|
-
/** Record a full phase read
|
|
54
|
+
/** Record a full phase read in the default compatibility session. */
|
|
28
55
|
export function markPhaseRead(phaseId, _featureId) {
|
|
29
|
-
|
|
56
|
+
markPhaseReadForSession(DEFAULT_SESSION_ID, phaseId);
|
|
30
57
|
}
|
|
31
|
-
/** Record a full
|
|
58
|
+
/** Record a full phase read for an explicit harness session. */
|
|
59
|
+
export function markPhaseReadForSessionId(sessionId, phaseId) {
|
|
60
|
+
markPhaseReadForSession(sessionId, phaseId);
|
|
61
|
+
}
|
|
62
|
+
/** Record a full task read in the default compatibility session. */
|
|
32
63
|
export function markTaskRead(taskId, _phaseId, _featureId) {
|
|
33
|
-
|
|
64
|
+
markTaskReadForSession(DEFAULT_SESSION_ID, taskId);
|
|
65
|
+
}
|
|
66
|
+
/** Record a full task read for an explicit harness session. */
|
|
67
|
+
export function markTaskReadForSessionId(sessionId, taskId) {
|
|
68
|
+
markTaskReadForSession(sessionId, taskId);
|
|
34
69
|
}
|
|
35
|
-
/** Record that a requirement was explicitly read
|
|
70
|
+
/** Record that a requirement was explicitly read in the default session. */
|
|
36
71
|
export function markRequirementRead(requirementId) {
|
|
37
|
-
|
|
72
|
+
stateFor(DEFAULT_SESSION_ID).requirements.add(requirementId);
|
|
38
73
|
}
|
|
39
|
-
/**
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
74
|
+
/** Record that a requirement was explicitly read for an explicit session. */
|
|
75
|
+
export function markRequirementReadForSessionId(sessionId, requirementId) {
|
|
76
|
+
stateFor(sessionId).requirements.add(requirementId);
|
|
77
|
+
}
|
|
78
|
+
function orderedEligibility(sessionId, taskId, phaseId, featureId) {
|
|
79
|
+
const state = stateFor(sessionId);
|
|
44
80
|
const taskSequence = state.tasks.get(taskId);
|
|
45
81
|
if (taskSequence === undefined) {
|
|
46
82
|
return { eligible: false, reason: "Read this exact task with full=true first." };
|
|
@@ -57,38 +93,87 @@ export function contextReadEligibility(taskId, phaseId, featureId) {
|
|
|
57
93
|
}
|
|
58
94
|
return { eligible: true, reason: "" };
|
|
59
95
|
}
|
|
96
|
+
function validSessionInfo(entity, sessionId) {
|
|
97
|
+
const entry = entity?.sessionInfo?.find((candidate) => candidate.sessionId === sessionId);
|
|
98
|
+
return Boolean(entity && entry && entity.updatedAt <= entry.createdAt);
|
|
99
|
+
}
|
|
100
|
+
function persistedEligibility(input) {
|
|
101
|
+
if (!validSessionInfo(input.task, input.sessionId) || !validSessionInfo(input.phase, input.sessionId))
|
|
102
|
+
return false;
|
|
103
|
+
if (input.featureId && !validSessionInfo(input.feature, input.sessionId))
|
|
104
|
+
return false;
|
|
105
|
+
const requirementIds = input.requirementIds ?? [];
|
|
106
|
+
return requirementIds.every((id) => validSessionInfo(input.requirements?.find((requirement) => requirement.id === id), input.sessionId));
|
|
107
|
+
}
|
|
108
|
+
function hasStoredSessionAttestation(input) {
|
|
109
|
+
const entities = [input.task, input.phase, input.feature, ...(input.requirements ?? [])];
|
|
110
|
+
return entities.some((entity) => entity?.sessionInfo?.some((entry) => entry.sessionId === input.sessionId));
|
|
111
|
+
}
|
|
60
112
|
/**
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
113
|
+
* Evaluate exact in-memory read ordering first, then a persisted attestation
|
|
114
|
+
* for the same session and current entity revisions. A previously persisted
|
|
115
|
+
* but now stale attestation always wins over stale in-memory ordering.
|
|
64
116
|
*/
|
|
117
|
+
export function contextReadEligibilityForSession(input) {
|
|
118
|
+
const ordered = orderedEligibility(input.sessionId, input.taskId, input.phaseId, input.featureId);
|
|
119
|
+
if (ordered.eligible)
|
|
120
|
+
return ordered;
|
|
121
|
+
if (input.task && input.phase && persistedEligibility(input))
|
|
122
|
+
return { eligible: true, reason: "" };
|
|
123
|
+
if (input.task && input.phase && hasStoredSessionAttestation(input)) {
|
|
124
|
+
return { eligible: false, reason: "Context changed since the last session read; reread the exact task, phase, feature, and linked requirements." };
|
|
125
|
+
}
|
|
126
|
+
return ordered;
|
|
127
|
+
}
|
|
128
|
+
/** Return true only when the persisted attestation covers the current revisions. */
|
|
129
|
+
export function hasValidSessionAttestation(input) {
|
|
130
|
+
return persistedEligibility(input);
|
|
131
|
+
}
|
|
132
|
+
/** Compatibility check for the default session and in-memory reads only. */
|
|
133
|
+
export function contextReadEligibility(taskId, phaseId, featureId) {
|
|
134
|
+
return orderedEligibility(DEFAULT_SESSION_ID, taskId, phaseId, featureId);
|
|
135
|
+
}
|
|
136
|
+
/** Legacy parent-read check retained for callers that only need independent parent state. */
|
|
65
137
|
export function hasReadParents(featureId, phaseId) {
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
return phaseOk && featureOk;
|
|
138
|
+
const state = stateFor(DEFAULT_SESSION_ID);
|
|
139
|
+
return state.phases.has(phaseId) && (!featureId || state.features.has(featureId));
|
|
69
140
|
}
|
|
70
|
-
/**
|
|
141
|
+
/**
|
|
142
|
+
* Whether linked requirements are read in memory or have valid persisted
|
|
143
|
+
* attestations for the current session and entity revisions.
|
|
144
|
+
*/
|
|
145
|
+
export function hasReadRequirementsForSession(sessionId, requirementIds, requirements = []) {
|
|
146
|
+
const state = stateFor(sessionId);
|
|
147
|
+
return requirementIds.every((id) => state.requirements.has(id) || validSessionInfo(requirements.find((requirement) => requirement.id === id), sessionId));
|
|
148
|
+
}
|
|
149
|
+
/** Legacy requirement check for the default compatibility session. */
|
|
71
150
|
export function hasReadRequirements(requirementIds) {
|
|
72
|
-
return requirementIds.every((id) =>
|
|
73
|
-
}
|
|
74
|
-
/** Clear
|
|
75
|
-
export function invalidateReads() {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
151
|
+
return requirementIds.every((id) => stateFor(DEFAULT_SESSION_ID).requirements.has(id));
|
|
152
|
+
}
|
|
153
|
+
/** Clear one session's read state, or all state for legacy callers. */
|
|
154
|
+
export function invalidateReads(sessionId) {
|
|
155
|
+
if (sessionId) {
|
|
156
|
+
states.delete(normalizeSessionId(sessionId));
|
|
157
|
+
stateFor(sessionId);
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
states.clear();
|
|
161
|
+
states.set(DEFAULT_SESSION_ID, newState());
|
|
162
|
+
}
|
|
163
|
+
/** Initialize an explicit session without clearing other harness sessions. */
|
|
164
|
+
export function startReadSession(sessionId) {
|
|
165
|
+
stateFor(sessionId);
|
|
83
166
|
}
|
|
84
167
|
/** Compatibility advisory for non-lifecycle callers. */
|
|
85
168
|
export function parentReadAdvisory(featureId, phaseId) {
|
|
86
|
-
|
|
169
|
+
const state = stateFor(DEFAULT_SESSION_ID);
|
|
170
|
+
if (state.phases.has(phaseId) && (!featureId || state.features.has(featureId)))
|
|
87
171
|
return "";
|
|
88
172
|
return "\n\n⚠️ READ REQUIRED before proceeding: read the parent phase and feature with full=true.";
|
|
89
173
|
}
|
|
90
174
|
/** Advisory text for the separate linked-requirements gate. */
|
|
91
175
|
export function requirementReadAdvisory(requirementIds) {
|
|
176
|
+
const state = stateFor(DEFAULT_SESSION_ID);
|
|
92
177
|
if (requirementIds.length === 0)
|
|
93
178
|
return "";
|
|
94
179
|
const unread = requirementIds.filter((id) => !state.requirements.has(id));
|
|
@@ -96,8 +181,9 @@ export function requirementReadAdvisory(requirementIds) {
|
|
|
96
181
|
return "";
|
|
97
182
|
return "\n\n⚠️ REQUIREMENTS READ REQUIRED before proceeding: read the requirements linked to this phase and feature.";
|
|
98
183
|
}
|
|
99
|
-
/** Snapshot for diagnostics
|
|
100
|
-
export function readTrackingSnapshot() {
|
|
184
|
+
/** Snapshot for diagnostics. */
|
|
185
|
+
export function readTrackingSnapshot(sessionId) {
|
|
186
|
+
const state = stateFor(sessionId);
|
|
101
187
|
return {
|
|
102
188
|
features: [...state.features.keys()],
|
|
103
189
|
phases: [...state.phases.keys()],
|