@agent-plan/core 0.2.23 → 0.2.25
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.d.ts +3 -11
- package/dist/planner-rules.d.ts.map +1 -1
- package/dist/planner-rules.js +23 -8
- package/dist/read-tracking.d.ts +69 -23
- package/dist/read-tracking.d.ts.map +1 -1
- package/dist/read-tracking.js +170 -45
- package/dist/recap.d.ts.map +1 -1
- package/dist/recap.js +1 -6
- package/dist/schema.d.ts +324 -0
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +12 -0
- package/dist/task-selection.js +2 -2
- 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,95 @@
|
|
|
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
|
+
* the entity's context revision 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
|
+
descriptionUpdatedAt?: string;
|
|
16
|
+
sessionInfo?: SessionInfoEntry[];
|
|
17
|
+
};
|
|
18
|
+
export type ContextReadEntityKind = "task" | "phase" | "feature";
|
|
19
|
+
export type RequiredContextRead = {
|
|
20
|
+
kind: ContextReadEntityKind;
|
|
21
|
+
id: string;
|
|
22
|
+
state: "missing" | "stale" | "out-of-order";
|
|
23
|
+
};
|
|
12
24
|
export type ContextReadEligibility = {
|
|
13
25
|
eligible: boolean;
|
|
14
26
|
reason: string;
|
|
27
|
+
requiredReads?: RequiredContextRead[];
|
|
15
28
|
};
|
|
16
|
-
|
|
29
|
+
export interface SessionContextReadInput {
|
|
30
|
+
sessionId: string;
|
|
31
|
+
taskId: string;
|
|
32
|
+
phaseId: string;
|
|
33
|
+
featureId?: string;
|
|
34
|
+
task?: ReadTrackedEntity;
|
|
35
|
+
phase?: ReadTrackedEntity;
|
|
36
|
+
feature?: ReadTrackedEntity;
|
|
37
|
+
requirements?: Array<ReadTrackedEntity & {
|
|
38
|
+
id: string;
|
|
39
|
+
}>;
|
|
40
|
+
requirementIds?: string[];
|
|
41
|
+
}
|
|
42
|
+
/** Record a full feature read in the default compatibility session. */
|
|
17
43
|
export declare function markFeatureRead(featureId: string): void;
|
|
18
|
-
/** Record a full
|
|
44
|
+
/** Record a full feature read for an explicit harness session. */
|
|
45
|
+
export declare function markFeatureReadForSessionId(sessionId: string, featureId: string): void;
|
|
46
|
+
/** Record a full phase read in the default compatibility session. */
|
|
19
47
|
export declare function markPhaseRead(phaseId: string, _featureId?: string): void;
|
|
20
|
-
/** Record a full
|
|
48
|
+
/** Record a full phase read for an explicit harness session. */
|
|
49
|
+
export declare function markPhaseReadForSessionId(sessionId: string, phaseId: string): void;
|
|
50
|
+
/** Record a full task read in the default compatibility session. */
|
|
21
51
|
export declare function markTaskRead(taskId: string, _phaseId?: string, _featureId?: string): void;
|
|
22
|
-
/** Record
|
|
52
|
+
/** Record a full task read for an explicit harness session. */
|
|
53
|
+
export declare function markTaskReadForSessionId(sessionId: string, taskId: string): void;
|
|
54
|
+
/** Record that a requirement was explicitly read in the default session. */
|
|
23
55
|
export declare function markRequirementRead(requirementId: string): void;
|
|
56
|
+
/** Record that a requirement was explicitly read for an explicit session. */
|
|
57
|
+
export declare function markRequirementReadForSessionId(sessionId: string, requirementId: string): void;
|
|
24
58
|
/**
|
|
25
|
-
*
|
|
26
|
-
*
|
|
59
|
+
* Combine valid persisted attestations with fresh in-memory reads. The initial
|
|
60
|
+
* uncached lineage still requires task → phase → feature ordering, while a
|
|
61
|
+
* stale or missing individual entity can be reread without discarding valid
|
|
62
|
+
* parent attestations from the same harness session.
|
|
27
63
|
*/
|
|
64
|
+
export declare function contextReadEligibilityForSession(input: SessionContextReadInput): ContextReadEligibility;
|
|
65
|
+
/** Return true only when the persisted attestation covers the current revisions. */
|
|
66
|
+
export declare function hasValidSessionAttestation(input: SessionContextReadInput): boolean;
|
|
67
|
+
/** Compatibility check for the default session and in-memory reads only. */
|
|
28
68
|
export declare function contextReadEligibility(taskId: string, phaseId: string, featureId?: string): ContextReadEligibility;
|
|
69
|
+
/** Legacy parent-read check retained for callers that only need independent parent state. */
|
|
70
|
+
export declare function hasReadParents(featureId: string | undefined, phaseId: string): boolean;
|
|
29
71
|
/**
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* contextReadEligibility so the task and ordering cannot be bypassed.
|
|
72
|
+
* Whether linked requirements are read in memory or have valid persisted
|
|
73
|
+
* attestations for the current session and entity revisions.
|
|
33
74
|
*/
|
|
34
|
-
export declare function
|
|
35
|
-
|
|
75
|
+
export declare function hasReadRequirementsForSession(sessionId: string, requirementIds: string[], requirements?: Array<ReadTrackedEntity & {
|
|
76
|
+
id: string;
|
|
77
|
+
}>): boolean;
|
|
78
|
+
/** Legacy requirement check for the default compatibility session. */
|
|
36
79
|
export declare function hasReadRequirements(requirementIds: string[]): boolean;
|
|
37
|
-
/** Clear
|
|
38
|
-
export declare function invalidateReads(): void;
|
|
80
|
+
/** Clear one session's read state, or all state for legacy callers. */
|
|
81
|
+
export declare function invalidateReads(sessionId?: string): void;
|
|
82
|
+
/** Initialize an explicit session without clearing other harness sessions. */
|
|
83
|
+
export declare function startReadSession(sessionId: string): void;
|
|
39
84
|
/** Compatibility advisory for non-lifecycle callers. */
|
|
40
85
|
export declare function parentReadAdvisory(featureId: string | undefined, phaseId: string): string;
|
|
41
86
|
/** Advisory text for the separate linked-requirements gate. */
|
|
42
87
|
export declare function requirementReadAdvisory(requirementIds: string[]): string;
|
|
43
|
-
/** Snapshot for diagnostics
|
|
44
|
-
export declare function readTrackingSnapshot(): {
|
|
88
|
+
/** Snapshot for diagnostics. */
|
|
89
|
+
export declare function readTrackingSnapshot(sessionId?: string): {
|
|
45
90
|
features: string[];
|
|
46
91
|
phases: string[];
|
|
47
92
|
requirements: string[];
|
|
48
93
|
};
|
|
94
|
+
export {};
|
|
49
95
|
//# 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
|
+
* the entity's context revision 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);
|
|
26
49
|
}
|
|
27
|
-
/** Record a full
|
|
50
|
+
/** Record a full feature read for an explicit harness session. */
|
|
51
|
+
export function markFeatureReadForSessionId(sessionId, featureId) {
|
|
52
|
+
markFeatureReadForSession(sessionId, featureId);
|
|
53
|
+
}
|
|
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);
|
|
57
|
+
}
|
|
58
|
+
/** Record a full phase read for an explicit harness session. */
|
|
59
|
+
export function markPhaseReadForSessionId(sessionId, phaseId) {
|
|
60
|
+
markPhaseReadForSession(sessionId, phaseId);
|
|
30
61
|
}
|
|
31
|
-
/** Record a full task read
|
|
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);
|
|
34
65
|
}
|
|
35
|
-
/** Record
|
|
66
|
+
/** Record a full task read for an explicit harness session. */
|
|
67
|
+
export function markTaskReadForSessionId(sessionId, taskId) {
|
|
68
|
+
markTaskReadForSession(sessionId, taskId);
|
|
69
|
+
}
|
|
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,126 @@ export function contextReadEligibility(taskId, phaseId, featureId) {
|
|
|
57
93
|
}
|
|
58
94
|
return { eligible: true, reason: "" };
|
|
59
95
|
}
|
|
96
|
+
function entityRevision(entity, kind) {
|
|
97
|
+
if ((kind === "phase" || kind === "feature") && entity.descriptionUpdatedAt?.trim()) {
|
|
98
|
+
return entity.descriptionUpdatedAt;
|
|
99
|
+
}
|
|
100
|
+
return entity.updatedAt;
|
|
101
|
+
}
|
|
102
|
+
function storedReadState(entity, sessionId, kind) {
|
|
103
|
+
const entry = entity?.sessionInfo?.find((candidate) => candidate.sessionId === sessionId);
|
|
104
|
+
if (!entity || !entry)
|
|
105
|
+
return "missing";
|
|
106
|
+
return entityRevision(entity, kind) <= entry.createdAt ? "valid" : "stale";
|
|
107
|
+
}
|
|
108
|
+
function validSessionInfo(entity, sessionId, kind = "requirement") {
|
|
109
|
+
return storedReadState(entity, sessionId, kind) === "valid";
|
|
110
|
+
}
|
|
111
|
+
function persistedEligibility(input) {
|
|
112
|
+
if (!validSessionInfo(input.task, input.sessionId, "task") || !validSessionInfo(input.phase, input.sessionId, "phase"))
|
|
113
|
+
return false;
|
|
114
|
+
if (input.featureId && !validSessionInfo(input.feature, input.sessionId, "feature"))
|
|
115
|
+
return false;
|
|
116
|
+
const requirementIds = input.requirementIds ?? [];
|
|
117
|
+
return requirementIds.every((id) => validSessionInfo(input.requirements?.find((requirement) => requirement.id === id), input.sessionId));
|
|
118
|
+
}
|
|
119
|
+
function requiredReadReason(requiredReads) {
|
|
120
|
+
const labels = requiredReads.map((read) => `${read.kind} ${read.id} (${read.state})`);
|
|
121
|
+
return `Read required context only for: ${labels.join(", ")}. Follow these reads in the listed order, then retry.`;
|
|
122
|
+
}
|
|
60
123
|
/**
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
124
|
+
* Combine valid persisted attestations with fresh in-memory reads. The initial
|
|
125
|
+
* uncached lineage still requires task → phase → feature ordering, while a
|
|
126
|
+
* stale or missing individual entity can be reread without discarding valid
|
|
127
|
+
* parent attestations from the same harness session.
|
|
64
128
|
*/
|
|
129
|
+
export function contextReadEligibilityForSession(input) {
|
|
130
|
+
const state = stateFor(input.sessionId);
|
|
131
|
+
const requiredReads = [];
|
|
132
|
+
const taskStored = storedReadState(input.task, input.sessionId, "task");
|
|
133
|
+
const taskSequence = state.tasks.get(input.taskId);
|
|
134
|
+
const taskReady = taskStored === "valid" || taskSequence !== undefined;
|
|
135
|
+
if (!taskReady) {
|
|
136
|
+
requiredReads.push({ kind: "task", id: input.taskId, state: taskStored === "stale" ? "stale" : "missing" });
|
|
137
|
+
}
|
|
138
|
+
const phaseStored = storedReadState(input.phase, input.sessionId, "phase");
|
|
139
|
+
const phaseSequence = state.phases.get(input.phaseId);
|
|
140
|
+
const phaseReadInOrder = phaseSequence !== undefined
|
|
141
|
+
&& (taskStored === "valid" || (taskSequence !== undefined && phaseSequence > taskSequence));
|
|
142
|
+
const phaseReady = phaseStored === "valid" || phaseReadInOrder;
|
|
143
|
+
if (!phaseReady) {
|
|
144
|
+
requiredReads.push({
|
|
145
|
+
kind: "phase",
|
|
146
|
+
id: input.phaseId,
|
|
147
|
+
state: phaseStored === "stale" ? "stale" : phaseSequence !== undefined ? "out-of-order" : "missing",
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
if (input.featureId) {
|
|
151
|
+
const featureStored = storedReadState(input.feature, input.sessionId, "feature");
|
|
152
|
+
const featureSequence = state.features.get(input.featureId);
|
|
153
|
+
const featureReadInOrder = featureSequence !== undefined
|
|
154
|
+
&& (phaseStored === "valid" || (phaseReadInOrder && phaseSequence !== undefined && featureSequence > phaseSequence));
|
|
155
|
+
if (featureStored !== "valid" && !featureReadInOrder) {
|
|
156
|
+
requiredReads.push({
|
|
157
|
+
kind: "feature",
|
|
158
|
+
id: input.featureId,
|
|
159
|
+
state: featureStored === "stale" ? "stale" : featureSequence !== undefined ? "out-of-order" : "missing",
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (requiredReads.length === 0)
|
|
164
|
+
return { eligible: true, reason: "" };
|
|
165
|
+
return { eligible: false, reason: requiredReadReason(requiredReads), requiredReads };
|
|
166
|
+
}
|
|
167
|
+
/** Return true only when the persisted attestation covers the current revisions. */
|
|
168
|
+
export function hasValidSessionAttestation(input) {
|
|
169
|
+
return persistedEligibility(input);
|
|
170
|
+
}
|
|
171
|
+
/** Compatibility check for the default session and in-memory reads only. */
|
|
172
|
+
export function contextReadEligibility(taskId, phaseId, featureId) {
|
|
173
|
+
return orderedEligibility(DEFAULT_SESSION_ID, taskId, phaseId, featureId);
|
|
174
|
+
}
|
|
175
|
+
/** Legacy parent-read check retained for callers that only need independent parent state. */
|
|
65
176
|
export function hasReadParents(featureId, phaseId) {
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
177
|
+
const state = stateFor(DEFAULT_SESSION_ID);
|
|
178
|
+
return state.phases.has(phaseId) && (!featureId || state.features.has(featureId));
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Whether linked requirements are read in memory or have valid persisted
|
|
182
|
+
* attestations for the current session and entity revisions.
|
|
183
|
+
*/
|
|
184
|
+
export function hasReadRequirementsForSession(sessionId, requirementIds, requirements = []) {
|
|
185
|
+
const state = stateFor(sessionId);
|
|
186
|
+
return requirementIds.every((id) => state.requirements.has(id) || validSessionInfo(requirements.find((requirement) => requirement.id === id), sessionId));
|
|
69
187
|
}
|
|
70
|
-
/**
|
|
188
|
+
/** Legacy requirement check for the default compatibility session. */
|
|
71
189
|
export function hasReadRequirements(requirementIds) {
|
|
72
|
-
return requirementIds.every((id) =>
|
|
73
|
-
}
|
|
74
|
-
/** Clear
|
|
75
|
-
export function invalidateReads() {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
190
|
+
return requirementIds.every((id) => stateFor(DEFAULT_SESSION_ID).requirements.has(id));
|
|
191
|
+
}
|
|
192
|
+
/** Clear one session's read state, or all state for legacy callers. */
|
|
193
|
+
export function invalidateReads(sessionId) {
|
|
194
|
+
if (sessionId) {
|
|
195
|
+
states.delete(normalizeSessionId(sessionId));
|
|
196
|
+
stateFor(sessionId);
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
states.clear();
|
|
200
|
+
states.set(DEFAULT_SESSION_ID, newState());
|
|
201
|
+
}
|
|
202
|
+
/** Initialize an explicit session without clearing other harness sessions. */
|
|
203
|
+
export function startReadSession(sessionId) {
|
|
204
|
+
stateFor(sessionId);
|
|
83
205
|
}
|
|
84
206
|
/** Compatibility advisory for non-lifecycle callers. */
|
|
85
207
|
export function parentReadAdvisory(featureId, phaseId) {
|
|
86
|
-
|
|
208
|
+
const state = stateFor(DEFAULT_SESSION_ID);
|
|
209
|
+
if (state.phases.has(phaseId) && (!featureId || state.features.has(featureId)))
|
|
87
210
|
return "";
|
|
88
211
|
return "\n\n⚠️ READ REQUIRED before proceeding: read the parent phase and feature with full=true.";
|
|
89
212
|
}
|
|
90
213
|
/** Advisory text for the separate linked-requirements gate. */
|
|
91
214
|
export function requirementReadAdvisory(requirementIds) {
|
|
215
|
+
const state = stateFor(DEFAULT_SESSION_ID);
|
|
92
216
|
if (requirementIds.length === 0)
|
|
93
217
|
return "";
|
|
94
218
|
const unread = requirementIds.filter((id) => !state.requirements.has(id));
|
|
@@ -96,8 +220,9 @@ export function requirementReadAdvisory(requirementIds) {
|
|
|
96
220
|
return "";
|
|
97
221
|
return "\n\n⚠️ REQUIREMENTS READ REQUIRED before proceeding: read the requirements linked to this phase and feature.";
|
|
98
222
|
}
|
|
99
|
-
/** Snapshot for diagnostics
|
|
100
|
-
export function readTrackingSnapshot() {
|
|
223
|
+
/** Snapshot for diagnostics. */
|
|
224
|
+
export function readTrackingSnapshot(sessionId) {
|
|
225
|
+
const state = stateFor(sessionId);
|
|
101
226
|
return {
|
|
102
227
|
features: [...state.features.keys()],
|
|
103
228
|
phases: [...state.phases.keys()],
|
package/dist/recap.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recap.d.ts","sourceRoot":"","sources":["../src/recap.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAGjD;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3B;AAED,yEAAyE;AACzE,MAAM,MAAM,YAAY,GAAG,IAAI,GAAG,KAAK,CAAC;AAExC,MAAM,WAAW,YAAY;IAC3B,OAAO,CAAC,EAAE,YAAY,CAAC;CACxB;AAKD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,GAAG,GAAE,YAAiB,EAAE,IAAI,GAAE,YAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"recap.d.ts","sourceRoot":"","sources":["../src/recap.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAGjD;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3B;AAED,yEAAyE;AACzE,MAAM,MAAM,YAAY,GAAG,IAAI,GAAG,KAAK,CAAC;AAExC,MAAM,WAAW,YAAY;IAC3B,OAAO,CAAC,EAAE,YAAY,CAAC;CACxB;AAKD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,GAAG,GAAE,YAAiB,EAAE,IAAI,GAAE,YAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,CAuOhH"}
|
package/dist/recap.js
CHANGED
|
@@ -63,9 +63,6 @@ export async function buildRecap(st, web = {}, opts = {}) {
|
|
|
63
63
|
const featureAddCmd = cmd("/planner feature add", "planner-feature-add");
|
|
64
64
|
const phaseAddCmd = cmd("/planner phase add", "planner-phase-add");
|
|
65
65
|
const handoffShowCmd = cmd("/planner handoff show", "planner-handoff-show");
|
|
66
|
-
const featureShowCmd = cmd("/planner feature show", "planner-feature-show");
|
|
67
|
-
const phaseShowCmd = cmd("/planner phase show", "planner-phase-show");
|
|
68
|
-
const taskShowCmd = cmd("/planner task show", "planner-task-show");
|
|
69
66
|
const lines = [];
|
|
70
67
|
lines.push(italian ? "## Ripresa planner" : "## Planner recap");
|
|
71
68
|
const name = plan.project.name || "(unnamed project)";
|
|
@@ -78,9 +75,7 @@ export async function buildRecap(st, web = {}, opts = {}) {
|
|
|
78
75
|
const pr = formatPhaseRef(focusPhase.number, focusFeature?.number);
|
|
79
76
|
const tr = tref(focusTask.task.number);
|
|
80
77
|
lines.push(`${italian ? "Focus corrente" : "Current focus"}: ${fr} — ${focusFeature?.name ?? "?"} / ${pr} — ${focusPhase.title} / ${tr} — ${focusTask.task.title} (in-progress)`);
|
|
81
|
-
lines.push("",
|
|
82
|
-
? `Questo task è in-progress (lavoro iniziato in una sessione precedente). Prima di continuare, rileggi il contesto completo: ${taskShowCmd} ${tr} (full=true), ${phaseShowCmd} ${pr} (full=true), ${featureShowCmd} ${fr} (full=true).`
|
|
83
|
-
: `⚠️ This task is in-progress (work started in a previous session). Before continuing, re-read the full context in this order: ${taskShowCmd} ${tr} (full=true), ${phaseShowCmd} ${pr} (full=true), ${featureShowCmd} ${fr} (full=true).`);
|
|
78
|
+
lines.push("", `Continue with ${taskStartCmd} ${tr}. If context reads are required, follow only the missing or stale reads in its nextActions, then retry.`);
|
|
84
79
|
}
|
|
85
80
|
else if (pendingResume) {
|
|
86
81
|
const feature = feats.find((entry) => entry.id === pendingResume.phase.featureId);
|