@code-yeongyu/senpi 2026.9.4-2 → 2026.9.4-3
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/CHANGELOG.md +23 -0
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +31 -41
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/credential-pool/state-store.d.ts +1 -0
- package/dist/core/credential-pool/state-store.d.ts.map +1 -1
- package/dist/core/credential-pool/state-store.js +22 -2
- package/dist/core/credential-pool/state-store.js.map +1 -1
- package/dist/core/extensions/builtin/goal/store.d.ts.map +1 -1
- package/dist/core/extensions/builtin/goal/store.js +8 -20
- package/dist/core/extensions/builtin/goal/store.js.map +1 -1
- package/dist/core/extensions/builtin/hooks/trust-storage.d.ts.map +1 -1
- package/dist/core/extensions/builtin/hooks/trust-storage.js +3 -1
- package/dist/core/extensions/builtin/hooks/trust-storage.js.map +1 -1
- package/dist/core/extensions/builtin/imagegen/auth.d.ts.map +1 -1
- package/dist/core/extensions/builtin/imagegen/auth.js +10 -2
- package/dist/core/extensions/builtin/imagegen/auth.js.map +1 -1
- package/dist/core/extensions/builtin/loop/store.d.ts +8 -6
- package/dist/core/extensions/builtin/loop/store.d.ts.map +1 -1
- package/dist/core/extensions/builtin/loop/store.js +54 -88
- package/dist/core/extensions/builtin/loop/store.js.map +1 -1
- package/dist/core/extensions/builtin/monitor-state-event.d.ts +2 -0
- package/dist/core/extensions/builtin/monitor-state-event.d.ts.map +1 -1
- package/dist/core/extensions/builtin/monitor-state-event.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/extension.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/extension.js +181 -3
- package/dist/core/extensions/builtin/terminal/extension.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/manager.d.ts +10 -0
- package/dist/core/extensions/builtin/terminal/manager.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/manager.js +21 -0
- package/dist/core/extensions/builtin/terminal/manager.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/manifest-lease.d.ts +24 -0
- package/dist/core/extensions/builtin/terminal/manifest-lease.d.ts.map +1 -0
- package/dist/core/extensions/builtin/terminal/manifest-lease.js +105 -0
- package/dist/core/extensions/builtin/terminal/manifest-lease.js.map +1 -0
- package/dist/core/extensions/builtin/terminal/monitor-registry.d.ts +13 -2
- package/dist/core/extensions/builtin/terminal/monitor-registry.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/monitor-registry.js +24 -2
- package/dist/core/extensions/builtin/terminal/monitor-registry.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/restore.d.ts +43 -0
- package/dist/core/extensions/builtin/terminal/restore.d.ts.map +1 -0
- package/dist/core/extensions/builtin/terminal/restore.js +149 -0
- package/dist/core/extensions/builtin/terminal/restore.js.map +1 -0
- package/dist/core/extensions/builtin/terminal/terminal-manifest.d.ts +110 -0
- package/dist/core/extensions/builtin/terminal/terminal-manifest.d.ts.map +1 -0
- package/dist/core/extensions/builtin/terminal/terminal-manifest.js +158 -0
- package/dist/core/extensions/builtin/terminal/terminal-manifest.js.map +1 -0
- package/dist/core/extensions/builtin/terminal/tools/bash-input.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/bash-input.js +3 -2
- package/dist/core/extensions/builtin/terminal/tools/bash-input.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/bash-output.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/bash-output.js +5 -4
- package/dist/core/extensions/builtin/terminal/tools/bash-output.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/bash-resize.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/bash-resize.js +3 -2
- package/dist/core/extensions/builtin/terminal/tools/bash-resize.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/context.d.ts +8 -0
- package/dist/core/extensions/builtin/terminal/tools/context.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/context.js +10 -0
- package/dist/core/extensions/builtin/terminal/tools/context.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/kill-bash.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/kill-bash.js +5 -4
- package/dist/core/extensions/builtin/terminal/tools/kill-bash.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/monitor.d.ts +4 -1
- package/dist/core/extensions/builtin/terminal/tools/monitor.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/monitor.js +58 -14
- package/dist/core/extensions/builtin/terminal/tools/monitor.js.map +1 -1
- package/dist/core/extensions/builtin/tool-search/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/tool-search/index.js +1 -1
- package/dist/core/extensions/builtin/tool-search/index.js.map +1 -1
- package/dist/core/extensions/builtin/tool-search/native-search.d.ts +9 -4
- package/dist/core/extensions/builtin/tool-search/native-search.d.ts.map +1 -1
- package/dist/core/extensions/builtin/tool-search/native-search.js +11 -6
- package/dist/core/extensions/builtin/tool-search/native-search.js.map +1 -1
- package/dist/core/extensions/builtin/tool-search/native-support.d.ts +13 -0
- package/dist/core/extensions/builtin/tool-search/native-support.d.ts.map +1 -0
- package/dist/core/extensions/builtin/tool-search/native-support.js +45 -0
- package/dist/core/extensions/builtin/tool-search/native-support.js.map +1 -0
- package/dist/core/lockfile-policy.d.ts +11 -0
- package/dist/core/lockfile-policy.d.ts.map +1 -1
- package/dist/core/lockfile-policy.js +19 -0
- package/dist/core/lockfile-policy.js.map +1 -1
- package/dist/core/session-sidecar-store.d.ts +62 -0
- package/dist/core/session-sidecar-store.d.ts.map +1 -0
- package/dist/core/session-sidecar-store.js +149 -0
- package/dist/core/session-sidecar-store.js.map +1 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +11 -15
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/modes/app-server/daemon/process.d.ts +33 -3
- package/dist/modes/app-server/daemon/process.d.ts.map +1 -1
- package/dist/modes/app-server/daemon/process.js +57 -6
- package/dist/modes/app-server/daemon/process.js.map +1 -1
- package/dist/modes/app-server/daemon.js +9 -1
- package/dist/modes/app-server/daemon.js.map +1 -1
- package/dist/modes/rpc/host-ensure.d.ts +2 -0
- package/dist/modes/rpc/host-ensure.d.ts.map +1 -1
- package/dist/modes/rpc/host-ensure.js +77 -18
- package/dist/modes/rpc/host-ensure.js.map +1 -1
- package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +14 -0
- package/node_modules/@code-yeongyu/senpi-codemode/package.json +5 -5
- package/node_modules/@code-yeongyu/senpi-codemode/src/prompt/eval-prompt.ts +1 -1
- package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.js +4 -4
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/retry.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/retry.js +3 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/retry.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/package.json +2 -2
- package/node_modules/@earendil-works/pi-pty/package.json +1 -1
- package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
- package/node_modules/@earendil-works/pi-tui/package.json +1 -1
- package/package.json +7 -7
|
@@ -1,24 +1,12 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { appendFile, mkdir, writeFile } from "node:fs/promises";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
|
+
import { serializeByKey } from "../../../session-sidecar-store.js";
|
|
4
5
|
import { GoalAlreadyExistsError, GoalNotFoundError } from "./errors.js";
|
|
5
6
|
import { encodedThreadId, goalFilePath, readGoalFile, writeGoalFile } from "./persistence.js";
|
|
6
7
|
import { transitionGoalStatus } from "./transitions.js";
|
|
7
8
|
import { resolveTokenBudget, validateObjective, validateTokenBudget } from "./validation.js";
|
|
8
9
|
export { goalFilePath };
|
|
9
|
-
const goalMutationTails = new Map();
|
|
10
|
-
function enqueueGoalMutation(ref, mutation) {
|
|
11
|
-
const key = goalFilePath(ref);
|
|
12
|
-
const previous = goalMutationTails.get(key) ?? Promise.resolve();
|
|
13
|
-
const run = previous.then(mutation);
|
|
14
|
-
const tail = run.then(() => undefined, () => undefined);
|
|
15
|
-
goalMutationTails.set(key, tail);
|
|
16
|
-
void tail.then(() => {
|
|
17
|
-
if (goalMutationTails.get(key) === tail)
|
|
18
|
-
goalMutationTails.delete(key);
|
|
19
|
-
});
|
|
20
|
-
return run;
|
|
21
|
-
}
|
|
22
10
|
export function goalHistoryFilePath(ref) {
|
|
23
11
|
return join(ref.baseDir, `${encodedThreadId(ref)}.history.jsonl`);
|
|
24
12
|
}
|
|
@@ -32,10 +20,10 @@ export async function readGoal(ref) {
|
|
|
32
20
|
return readGoalFile(ref);
|
|
33
21
|
}
|
|
34
22
|
export async function writeGoal(ref, goal) {
|
|
35
|
-
await
|
|
23
|
+
await serializeByKey(goalFilePath(ref), () => writeGoalFile(ref, goal));
|
|
36
24
|
}
|
|
37
25
|
export async function createGoal(ref, objective, tokenBudget) {
|
|
38
|
-
return
|
|
26
|
+
return serializeByKey(goalFilePath(ref), async () => {
|
|
39
27
|
const validatedObjective = validateObjective(objective, objectiveFullTextFileName(ref));
|
|
40
28
|
const current = await readGoalFile(ref);
|
|
41
29
|
if (current !== null && current.status !== "complete") {
|
|
@@ -65,7 +53,7 @@ export async function createGoal(ref, objective, tokenBudget) {
|
|
|
65
53
|
});
|
|
66
54
|
}
|
|
67
55
|
export async function updateGoal(ref, update, source = "model") {
|
|
68
|
-
return
|
|
56
|
+
return serializeByKey(goalFilePath(ref), async () => {
|
|
69
57
|
const current = await readGoalFile(ref);
|
|
70
58
|
if (!current)
|
|
71
59
|
throw new GoalNotFoundError("cannot update goal: no goal exists");
|
|
@@ -131,14 +119,14 @@ async function writeFullObjectiveText(ref, objective) {
|
|
|
131
119
|
await writeFile(filePath, objective, "utf8");
|
|
132
120
|
}
|
|
133
121
|
export async function clearGoal(ref) {
|
|
134
|
-
return
|
|
122
|
+
return serializeByKey(goalFilePath(ref), async () => {
|
|
135
123
|
const hadGoal = (await readGoalFile(ref)) !== null;
|
|
136
124
|
await writeGoalFile(ref, null);
|
|
137
125
|
return hadGoal;
|
|
138
126
|
});
|
|
139
127
|
}
|
|
140
128
|
export async function accountGoalUsage(ref, usage, elapsedSeconds, mode = "active", expectedGoalId) {
|
|
141
|
-
return
|
|
129
|
+
return serializeByKey(goalFilePath(ref), async () => {
|
|
142
130
|
const goal = await readGoalFile(ref);
|
|
143
131
|
if (!goal || (expectedGoalId !== undefined && goal.id !== expectedGoalId) || !canAccountGoalUsage(goal, mode)) {
|
|
144
132
|
return goal;
|
|
@@ -154,7 +142,7 @@ export async function accountGoalUsage(ref, usage, elapsedSeconds, mode = "activ
|
|
|
154
142
|
});
|
|
155
143
|
}
|
|
156
144
|
export async function recordContinuationDelivered(ref, signature, expectedGoalId, options = {}) {
|
|
157
|
-
return
|
|
145
|
+
return serializeByKey(goalFilePath(ref), async () => {
|
|
158
146
|
const goal = await readGoalFile(ref);
|
|
159
147
|
if (!goal || (expectedGoalId !== undefined && goal.id !== expectedGoalId))
|
|
160
148
|
return null;
|
|
@@ -169,7 +157,7 @@ export async function recordContinuationDelivered(ref, signature, expectedGoalId
|
|
|
169
157
|
});
|
|
170
158
|
}
|
|
171
159
|
export async function resetContinuationStreak(ref, options = {}) {
|
|
172
|
-
return
|
|
160
|
+
return serializeByKey(goalFilePath(ref), async () => {
|
|
173
161
|
const goal = await readGoalFile(ref);
|
|
174
162
|
if (!goal)
|
|
175
163
|
return goal;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/goal/store.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { appendFile, mkdir, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { GoalAlreadyExistsError, GoalNotFoundError } from \"./errors.ts\";\nimport { encodedThreadId, goalFilePath, readGoalFile, writeGoalFile } from \"./persistence.ts\";\nimport { transitionGoalStatus } from \"./transitions.ts\";\nimport type {\n\tGoal,\n\tGoalAccountingMode,\n\tGoalStoreRef,\n\tGoalUpdate,\n\tGoalUpdateSource,\n\tTokenUsageSnapshot,\n} from \"./types.ts\";\nimport { resolveTokenBudget, validateObjective, validateTokenBudget } from \"./validation.ts\";\n\nexport { goalFilePath };\n\nconst goalMutationTails = new Map<string, Promise<void>>();\n\nfunction enqueueGoalMutation<T>(ref: GoalStoreRef, mutation: () => Promise<T>): Promise<T> {\n\tconst key = goalFilePath(ref);\n\tconst previous = goalMutationTails.get(key) ?? Promise.resolve();\n\tconst run = previous.then(mutation);\n\tconst tail = run.then(\n\t\t() => undefined,\n\t\t() => undefined,\n\t);\n\tgoalMutationTails.set(key, tail);\n\tvoid tail.then(() => {\n\t\tif (goalMutationTails.get(key) === tail) goalMutationTails.delete(key);\n\t});\n\treturn run;\n}\n\nexport function goalHistoryFilePath(ref: GoalStoreRef): string {\n\treturn join(ref.baseDir, `${encodedThreadId(ref)}.history.jsonl`);\n}\n\nexport function objectiveFullTextFileName(ref: GoalStoreRef): string {\n\treturn `${encodedThreadId(ref)}.objective-full.txt`;\n}\n\nexport function objectiveFullTextFilePath(ref: GoalStoreRef): string {\n\treturn join(ref.baseDir, objectiveFullTextFileName(ref));\n}\n\nexport async function readGoal(ref: GoalStoreRef): Promise<Goal | null> {\n\treturn readGoalFile(ref);\n}\n\nexport async function writeGoal(ref: GoalStoreRef, goal: Goal | null): Promise<void> {\n\tawait enqueueGoalMutation(ref, () => writeGoalFile(ref, goal));\n}\n\nexport async function createGoal(ref: GoalStoreRef, objective: string, tokenBudget?: number): Promise<Goal> {\n\treturn enqueueGoalMutation(ref, async () => {\n\t\tconst validatedObjective = validateObjective(objective, objectiveFullTextFileName(ref));\n\t\tconst current = await readGoalFile(ref);\n\t\tif (current !== null && current.status !== \"complete\") {\n\t\t\tthrow new GoalAlreadyExistsError(\"cannot create a new goal because this thread already has a goal\");\n\t\t}\n\t\tif (validatedObjective.truncated) await writeFullObjectiveText(ref, objective);\n\t\tif (current?.status === \"complete\") await archiveGoal(ref, current);\n\t\tconst now = nowSeconds();\n\t\tconst goal: Goal = {\n\t\t\tid: randomUUID(),\n\t\t\tthreadId: ref.threadId,\n\t\t\tobjective: validatedObjective.objective,\n\t\t\tstatus: \"active\",\n\t\t\ttokensUsed: 0,\n\t\t\ttimeUsedSeconds: 0,\n\t\t\tconsecutiveContinuations: 0,\n\t\t\tunattendedContinuations: 0,\n\t\t\tcreatedAt: now,\n\t\t\tupdatedAt: now,\n\t\t\tlastStartedAt: now,\n\t\t\t...(tokenBudget === undefined ? {} : { tokenBudget: validateTokenBudget(tokenBudget) }),\n\t\t};\n\t\tawait writeGoalFile(ref, goal);\n\t\treturn goal;\n\t});\n}\n\nexport async function updateGoal(\n\tref: GoalStoreRef,\n\tupdate: GoalUpdate,\n\tsource: GoalUpdateSource = \"model\",\n): Promise<Goal> {\n\treturn enqueueGoalMutation(ref, async () => {\n\t\tconst current = await readGoalFile(ref);\n\t\tif (!current) throw new GoalNotFoundError(\"cannot update goal: no goal exists\");\n\n\t\tconst validatedObjective =\n\t\t\tupdate.objective === undefined\n\t\t\t\t? undefined\n\t\t\t\t: validateObjective(update.objective, objectiveFullTextFileName(ref));\n\t\tconst objective = validatedObjective?.objective ?? current.objective;\n\t\tconst tokenBudget = resolveTokenBudget(current.tokenBudget, update.tokenBudget);\n\t\tconst now = nextUpdatedAt(current.updatedAt);\n\t\tconst hasObjectiveUpdate = update.objective !== undefined;\n\t\tconst replacesGoal = hasObjectiveUpdate && (objective !== current.objective || current.status === \"complete\");\n\t\tconst requestedStatus = update.status ?? (hasObjectiveUpdate ? \"active\" : undefined);\n\n\t\tif (replacesGoal) {\n\t\t\tconst status = requestedStatus ?? \"active\";\n\t\t\tif (status === \"blocked\") throw new Error(\"objective replacement cannot create a blocked goal\");\n\t\t\tconst next: Goal = {\n\t\t\t\tid: randomUUID(),\n\t\t\t\tthreadId: ref.threadId,\n\t\t\t\tobjective,\n\t\t\t\tstatus,\n\t\t\t\ttokensUsed: 0,\n\t\t\t\ttimeUsedSeconds: 0,\n\t\t\t\tconsecutiveContinuations: 0,\n\t\t\t\tunattendedContinuations: 0,\n\t\t\t\tcreatedAt: now,\n\t\t\t\tupdatedAt: now,\n\t\t\t\t...(tokenBudget === undefined ? {} : { tokenBudget }),\n\t\t\t};\n\t\t\tif (status === \"active\") next.lastStartedAt = now;\n\t\t\tif (status === \"complete\") next.completedAt = now;\n\t\t\tif (validatedObjective?.truncated) await writeFullObjectiveText(ref, update.objective ?? \"\");\n\t\t\tawait writeGoalFile(ref, next);\n\t\t\treturn next;\n\t\t}\n\n\t\tconst next = transitionGoalStatus(\n\t\t\t{ ...current, objective },\n\t\t\trequestedStatus ?? current.status,\n\t\t\tsource,\n\t\t\tupdate.reason,\n\t\t\tnow,\n\t\t);\n\t\tif (next.status !== current.status) {\n\t\t\tnext.consecutiveContinuations = 0;\n\t\t\tnext.unattendedContinuations = 0;\n\t\t\tdelete next.lastContinuationSignature;\n\t\t}\n\t\tif (tokenBudget === undefined) delete next.tokenBudget;\n\t\telse next.tokenBudget = tokenBudget;\n\t\tif (validatedObjective?.truncated) await writeFullObjectiveText(ref, update.objective ?? \"\");\n\t\tawait writeGoalFile(ref, next);\n\t\treturn next;\n\t});\n}\n\nexport async function archiveGoal(ref: GoalStoreRef, goal: Goal): Promise<void> {\n\tconst filePath = goalHistoryFilePath(ref);\n\tawait mkdir(dirname(filePath), { recursive: true });\n\tawait appendFile(filePath, `${JSON.stringify(goal)}\\n`, \"utf8\");\n}\n\nasync function writeFullObjectiveText(ref: GoalStoreRef, objective: string): Promise<void> {\n\tconst filePath = objectiveFullTextFilePath(ref);\n\tawait mkdir(dirname(filePath), { recursive: true });\n\tawait writeFile(filePath, objective, \"utf8\");\n}\n\nexport async function clearGoal(ref: GoalStoreRef): Promise<boolean> {\n\treturn enqueueGoalMutation(ref, async () => {\n\t\tconst hadGoal = (await readGoalFile(ref)) !== null;\n\t\tawait writeGoalFile(ref, null);\n\t\treturn hadGoal;\n\t});\n}\n\nexport async function accountGoalUsage(\n\tref: GoalStoreRef,\n\tusage: TokenUsageSnapshot,\n\telapsedSeconds: number,\n\tmode: GoalAccountingMode = \"active\",\n\texpectedGoalId?: string,\n): Promise<Goal | null> {\n\treturn enqueueGoalMutation(ref, async () => {\n\t\tconst goal = await readGoalFile(ref);\n\t\tif (!goal || (expectedGoalId !== undefined && goal.id !== expectedGoalId) || !canAccountGoalUsage(goal, mode)) {\n\t\t\treturn goal;\n\t\t}\n\t\tconst next: Goal = {\n\t\t\t...goal,\n\t\t\ttokensUsed: goal.tokensUsed + Math.max(0, usage.input) + Math.max(0, usage.output),\n\t\t\ttimeUsedSeconds: goal.timeUsedSeconds + Math.max(0, Math.trunc(elapsedSeconds)),\n\t\t\tupdatedAt: nextUpdatedAt(goal.updatedAt),\n\t\t};\n\t\tawait writeGoalFile(ref, next);\n\t\treturn next;\n\t});\n}\n\nexport async function recordContinuationDelivered(\n\tref: GoalStoreRef,\n\tsignature: string,\n\texpectedGoalId?: string,\n\toptions: { countUnattended?: boolean } = {},\n): Promise<Goal | null> {\n\treturn enqueueGoalMutation(ref, async () => {\n\t\tconst goal = await readGoalFile(ref);\n\t\tif (!goal || (expectedGoalId !== undefined && goal.id !== expectedGoalId)) return null;\n\t\tconst next: Goal = {\n\t\t\t...goal,\n\t\t\tconsecutiveContinuations: (goal.consecutiveContinuations ?? 0) + 1,\n\t\t\tunattendedContinuations: (goal.unattendedContinuations ?? 0) + (options.countUnattended === false ? 0 : 1),\n\t\t\tlastContinuationSignature: signature,\n\t\t};\n\t\tawait writeGoalFile(ref, next);\n\t\treturn next;\n\t});\n}\n\nexport async function resetContinuationStreak(\n\tref: GoalStoreRef,\n\toptions: { unattended?: boolean } = {},\n): Promise<Goal | null> {\n\treturn enqueueGoalMutation(ref, async () => {\n\t\tconst goal = await readGoalFile(ref);\n\t\tif (!goal) return goal;\n\t\tconst next: Goal = { ...goal, consecutiveContinuations: 0 };\n\t\tif (options.unattended === true) next.unattendedContinuations = 0;\n\t\tdelete next.lastContinuationSignature;\n\t\tawait writeGoalFile(ref, next);\n\t\treturn next;\n\t});\n}\n\nfunction canAccountGoalUsage(goal: Goal, mode: GoalAccountingMode): boolean {\n\tif (mode === \"active\") return goal.status === \"active\";\n\tif (mode === \"activeOrBlocked\") return goal.status === \"active\" || goal.status === \"blocked\";\n\treturn goal.status === \"active\" || goal.status === \"complete\";\n}\n\nfunction nextUpdatedAt(previousUpdatedAt: number): number {\n\treturn Math.max(nowSeconds(), previousUpdatedAt + 1);\n}\n\nfunction nowSeconds(): number {\n\treturn Math.trunc(Date.now() / 1000);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/goal/store.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { appendFile, mkdir, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { serializeByKey } from \"../../../session-sidecar-store.ts\";\nimport { GoalAlreadyExistsError, GoalNotFoundError } from \"./errors.ts\";\nimport { encodedThreadId, goalFilePath, readGoalFile, writeGoalFile } from \"./persistence.ts\";\nimport { transitionGoalStatus } from \"./transitions.ts\";\nimport type {\n\tGoal,\n\tGoalAccountingMode,\n\tGoalStoreRef,\n\tGoalUpdate,\n\tGoalUpdateSource,\n\tTokenUsageSnapshot,\n} from \"./types.ts\";\nimport { resolveTokenBudget, validateObjective, validateTokenBudget } from \"./validation.ts\";\n\nexport { goalFilePath };\n\nexport function goalHistoryFilePath(ref: GoalStoreRef): string {\n\treturn join(ref.baseDir, `${encodedThreadId(ref)}.history.jsonl`);\n}\n\nexport function objectiveFullTextFileName(ref: GoalStoreRef): string {\n\treturn `${encodedThreadId(ref)}.objective-full.txt`;\n}\n\nexport function objectiveFullTextFilePath(ref: GoalStoreRef): string {\n\treturn join(ref.baseDir, objectiveFullTextFileName(ref));\n}\n\nexport async function readGoal(ref: GoalStoreRef): Promise<Goal | null> {\n\treturn readGoalFile(ref);\n}\n\nexport async function writeGoal(ref: GoalStoreRef, goal: Goal | null): Promise<void> {\n\tawait serializeByKey(goalFilePath(ref), () => writeGoalFile(ref, goal));\n}\n\nexport async function createGoal(ref: GoalStoreRef, objective: string, tokenBudget?: number): Promise<Goal> {\n\treturn serializeByKey(goalFilePath(ref), async () => {\n\t\tconst validatedObjective = validateObjective(objective, objectiveFullTextFileName(ref));\n\t\tconst current = await readGoalFile(ref);\n\t\tif (current !== null && current.status !== \"complete\") {\n\t\t\tthrow new GoalAlreadyExistsError(\"cannot create a new goal because this thread already has a goal\");\n\t\t}\n\t\tif (validatedObjective.truncated) await writeFullObjectiveText(ref, objective);\n\t\tif (current?.status === \"complete\") await archiveGoal(ref, current);\n\t\tconst now = nowSeconds();\n\t\tconst goal: Goal = {\n\t\t\tid: randomUUID(),\n\t\t\tthreadId: ref.threadId,\n\t\t\tobjective: validatedObjective.objective,\n\t\t\tstatus: \"active\",\n\t\t\ttokensUsed: 0,\n\t\t\ttimeUsedSeconds: 0,\n\t\t\tconsecutiveContinuations: 0,\n\t\t\tunattendedContinuations: 0,\n\t\t\tcreatedAt: now,\n\t\t\tupdatedAt: now,\n\t\t\tlastStartedAt: now,\n\t\t\t...(tokenBudget === undefined ? {} : { tokenBudget: validateTokenBudget(tokenBudget) }),\n\t\t};\n\t\tawait writeGoalFile(ref, goal);\n\t\treturn goal;\n\t});\n}\n\nexport async function updateGoal(\n\tref: GoalStoreRef,\n\tupdate: GoalUpdate,\n\tsource: GoalUpdateSource = \"model\",\n): Promise<Goal> {\n\treturn serializeByKey(goalFilePath(ref), async () => {\n\t\tconst current = await readGoalFile(ref);\n\t\tif (!current) throw new GoalNotFoundError(\"cannot update goal: no goal exists\");\n\n\t\tconst validatedObjective =\n\t\t\tupdate.objective === undefined\n\t\t\t\t? undefined\n\t\t\t\t: validateObjective(update.objective, objectiveFullTextFileName(ref));\n\t\tconst objective = validatedObjective?.objective ?? current.objective;\n\t\tconst tokenBudget = resolveTokenBudget(current.tokenBudget, update.tokenBudget);\n\t\tconst now = nextUpdatedAt(current.updatedAt);\n\t\tconst hasObjectiveUpdate = update.objective !== undefined;\n\t\tconst replacesGoal = hasObjectiveUpdate && (objective !== current.objective || current.status === \"complete\");\n\t\tconst requestedStatus = update.status ?? (hasObjectiveUpdate ? \"active\" : undefined);\n\n\t\tif (replacesGoal) {\n\t\t\tconst status = requestedStatus ?? \"active\";\n\t\t\tif (status === \"blocked\") throw new Error(\"objective replacement cannot create a blocked goal\");\n\t\t\tconst next: Goal = {\n\t\t\t\tid: randomUUID(),\n\t\t\t\tthreadId: ref.threadId,\n\t\t\t\tobjective,\n\t\t\t\tstatus,\n\t\t\t\ttokensUsed: 0,\n\t\t\t\ttimeUsedSeconds: 0,\n\t\t\t\tconsecutiveContinuations: 0,\n\t\t\t\tunattendedContinuations: 0,\n\t\t\t\tcreatedAt: now,\n\t\t\t\tupdatedAt: now,\n\t\t\t\t...(tokenBudget === undefined ? {} : { tokenBudget }),\n\t\t\t};\n\t\t\tif (status === \"active\") next.lastStartedAt = now;\n\t\t\tif (status === \"complete\") next.completedAt = now;\n\t\t\tif (validatedObjective?.truncated) await writeFullObjectiveText(ref, update.objective ?? \"\");\n\t\t\tawait writeGoalFile(ref, next);\n\t\t\treturn next;\n\t\t}\n\n\t\tconst next = transitionGoalStatus(\n\t\t\t{ ...current, objective },\n\t\t\trequestedStatus ?? current.status,\n\t\t\tsource,\n\t\t\tupdate.reason,\n\t\t\tnow,\n\t\t);\n\t\tif (next.status !== current.status) {\n\t\t\tnext.consecutiveContinuations = 0;\n\t\t\tnext.unattendedContinuations = 0;\n\t\t\tdelete next.lastContinuationSignature;\n\t\t}\n\t\tif (tokenBudget === undefined) delete next.tokenBudget;\n\t\telse next.tokenBudget = tokenBudget;\n\t\tif (validatedObjective?.truncated) await writeFullObjectiveText(ref, update.objective ?? \"\");\n\t\tawait writeGoalFile(ref, next);\n\t\treturn next;\n\t});\n}\n\nexport async function archiveGoal(ref: GoalStoreRef, goal: Goal): Promise<void> {\n\tconst filePath = goalHistoryFilePath(ref);\n\tawait mkdir(dirname(filePath), { recursive: true });\n\tawait appendFile(filePath, `${JSON.stringify(goal)}\\n`, \"utf8\");\n}\n\nasync function writeFullObjectiveText(ref: GoalStoreRef, objective: string): Promise<void> {\n\tconst filePath = objectiveFullTextFilePath(ref);\n\tawait mkdir(dirname(filePath), { recursive: true });\n\tawait writeFile(filePath, objective, \"utf8\");\n}\n\nexport async function clearGoal(ref: GoalStoreRef): Promise<boolean> {\n\treturn serializeByKey(goalFilePath(ref), async () => {\n\t\tconst hadGoal = (await readGoalFile(ref)) !== null;\n\t\tawait writeGoalFile(ref, null);\n\t\treturn hadGoal;\n\t});\n}\n\nexport async function accountGoalUsage(\n\tref: GoalStoreRef,\n\tusage: TokenUsageSnapshot,\n\telapsedSeconds: number,\n\tmode: GoalAccountingMode = \"active\",\n\texpectedGoalId?: string,\n): Promise<Goal | null> {\n\treturn serializeByKey(goalFilePath(ref), async () => {\n\t\tconst goal = await readGoalFile(ref);\n\t\tif (!goal || (expectedGoalId !== undefined && goal.id !== expectedGoalId) || !canAccountGoalUsage(goal, mode)) {\n\t\t\treturn goal;\n\t\t}\n\t\tconst next: Goal = {\n\t\t\t...goal,\n\t\t\ttokensUsed: goal.tokensUsed + Math.max(0, usage.input) + Math.max(0, usage.output),\n\t\t\ttimeUsedSeconds: goal.timeUsedSeconds + Math.max(0, Math.trunc(elapsedSeconds)),\n\t\t\tupdatedAt: nextUpdatedAt(goal.updatedAt),\n\t\t};\n\t\tawait writeGoalFile(ref, next);\n\t\treturn next;\n\t});\n}\n\nexport async function recordContinuationDelivered(\n\tref: GoalStoreRef,\n\tsignature: string,\n\texpectedGoalId?: string,\n\toptions: { countUnattended?: boolean } = {},\n): Promise<Goal | null> {\n\treturn serializeByKey(goalFilePath(ref), async () => {\n\t\tconst goal = await readGoalFile(ref);\n\t\tif (!goal || (expectedGoalId !== undefined && goal.id !== expectedGoalId)) return null;\n\t\tconst next: Goal = {\n\t\t\t...goal,\n\t\t\tconsecutiveContinuations: (goal.consecutiveContinuations ?? 0) + 1,\n\t\t\tunattendedContinuations: (goal.unattendedContinuations ?? 0) + (options.countUnattended === false ? 0 : 1),\n\t\t\tlastContinuationSignature: signature,\n\t\t};\n\t\tawait writeGoalFile(ref, next);\n\t\treturn next;\n\t});\n}\n\nexport async function resetContinuationStreak(\n\tref: GoalStoreRef,\n\toptions: { unattended?: boolean } = {},\n): Promise<Goal | null> {\n\treturn serializeByKey(goalFilePath(ref), async () => {\n\t\tconst goal = await readGoalFile(ref);\n\t\tif (!goal) return goal;\n\t\tconst next: Goal = { ...goal, consecutiveContinuations: 0 };\n\t\tif (options.unattended === true) next.unattendedContinuations = 0;\n\t\tdelete next.lastContinuationSignature;\n\t\tawait writeGoalFile(ref, next);\n\t\treturn next;\n\t});\n}\n\nfunction canAccountGoalUsage(goal: Goal, mode: GoalAccountingMode): boolean {\n\tif (mode === \"active\") return goal.status === \"active\";\n\tif (mode === \"activeOrBlocked\") return goal.status === \"active\" || goal.status === \"blocked\";\n\treturn goal.status === \"active\" || goal.status === \"complete\";\n}\n\nfunction nextUpdatedAt(previousUpdatedAt: number): number {\n\treturn Math.max(nowSeconds(), previousUpdatedAt + 1);\n}\n\nfunction nowSeconds(): number {\n\treturn Math.trunc(Date.now() / 1000);\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trust-storage.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/hooks/trust-storage.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAExD,OAAO,KAAK,EAAkB,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjE,MAAM,WAAW,gBAAgB;IAChC,IAAI,CAAC,KAAK,EAAE,qBAAqB,GAAG,cAAc,CAAC;IACnD,MAAM,CAAC,KAAK,EAAE,qBAAqB,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,cAAc,GAAG,cAAc,CAAC;CAC3G;AAED,MAAM,MAAM,2BAA2B,GAAG;IACzC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF;;;;;GAKG;AACH,qBAAa,oBAAqB,YAAW,gBAAgB;IAC5D,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;gBAE9B,OAAO,EAAE,2BAA2B;IAMhD,IAAI,CAAC,KAAK,EAAE,qBAAqB,GAAG,cAAc;
|
|
1
|
+
{"version":3,"file":"trust-storage.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/hooks/trust-storage.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAExD,OAAO,KAAK,EAAkB,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjE,MAAM,WAAW,gBAAgB;IAChC,IAAI,CAAC,KAAK,EAAE,qBAAqB,GAAG,cAAc,CAAC;IACnD,MAAM,CAAC,KAAK,EAAE,qBAAqB,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,cAAc,GAAG,cAAc,CAAC;CAC3G;AAED,MAAM,MAAM,2BAA2B,GAAG;IACzC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF;;;;;GAKG;AACH,qBAAa,oBAAqB,YAAW,gBAAgB;IAC5D,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;gBAE9B,OAAO,EAAE,2BAA2B;IAMhD,IAAI,CAAC,KAAK,EAAE,qBAAqB,GAAG,cAAc;IAuBlD,MAAM,CAAC,KAAK,EAAE,qBAAqB,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,cAAc,GAAG,cAAc;CAyB1G;AAED,qBAAa,wBAAyB,YAAW,gBAAgB;IAChE,OAAO,CAAC,WAAW,CAAyC;IAC5D,OAAO,CAAC,YAAY,CAAyC;IAE7D,IAAI,CAAC,KAAK,EAAE,qBAAqB,GAAG,cAAc;IAIlD,MAAM,CAAC,KAAK,EAAE,qBAAqB,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,cAAc,GAAG,cAAc;CAS1G"}
|
|
@@ -27,7 +27,9 @@ export class FileHookStateStorage {
|
|
|
27
27
|
release = acquireHookStateLockSync(path);
|
|
28
28
|
}
|
|
29
29
|
catch (error) {
|
|
30
|
-
|
|
30
|
+
const code = errorCode(error);
|
|
31
|
+
// Sandboxed/read-only children cannot mkdir the lock dir; fail open on read.
|
|
32
|
+
if (code === "ELOCKED" || code === "EPERM" || code === "EACCES" || code === "EROFS") {
|
|
31
33
|
return emptyHookTrustState();
|
|
32
34
|
}
|
|
33
35
|
throw error;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trust-storage.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/hooks/trust-storage.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport lockfile from \"proper-lockfile\";\nimport { CONFIG_DIR_NAME, getAgentDir } from \"../../../../config.ts\";\nimport type { HookTrustStorageScope } from \"./trust.ts\";\nimport { emptyHookTrustState, parseHookTrustStateJson, readHookTrustStateJson } from \"./trust-state-json.ts\";\nimport type { HookTrustEntry, HookTrustState } from \"./types.ts\";\n\nexport interface HookStateStorage {\n\tread(scope: HookTrustStorageScope): HookTrustState;\n\tupdate(scope: HookTrustStorageScope, updater: (current: HookTrustState) => HookTrustState): HookTrustState;\n}\n\nexport type FileHookStateStorageOptions = {\n\treadonly agentDir?: string;\n\treadonly cwd: string;\n};\n\n/**\n * Internal same-account application state at `<agentDir>/hooks-state.json` and\n * `<cwd>/.senpi/hooks-state.json`. POSIX files retain their numeric mode and new\n * files use 0600; reassigned ownership, named ACLs, and custom DACLs are outside\n * this storage contract.\n */\nexport class FileHookStateStorage implements HookStateStorage {\n\tprivate readonly globalStatePath: string;\n\tprivate readonly projectStatePath: string;\n\n\tconstructor(options: FileHookStateStorageOptions) {\n\t\tconst agentDir = options.agentDir ?? getAgentDir();\n\t\tthis.globalStatePath = join(agentDir, \"hooks-state.json\");\n\t\tthis.projectStatePath = join(options.cwd, CONFIG_DIR_NAME, \"hooks-state.json\");\n\t}\n\n\tread(scope: HookTrustStorageScope): HookTrustState {\n\t\tconst path = statePathForScope(scope, this.globalStatePath, this.projectStatePath);\n\t\tconst snapshot = parseHookTrustStateJson(existsSync(path) ? readFileSync(path, \"utf-8\") : undefined);\n\t\tif (snapshot !== undefined) {\n\t\t\treturn snapshot;\n\t\t}\n\n\t\tlet release: () => void;\n\t\ttry {\n\t\t\trelease = acquireHookStateLockSync(path);\n\t\t} catch (error) {\n\t\t\tif (errorCode(error) === \"ELOCKED\") {\n\t\t\t\treturn emptyHookTrustState();\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t\treturn runWithHookStateLockRelease(release, () =>\n\t\t\treadHookTrustStateJson(existsSync(path) ? readFileSync(path, \"utf-8\") : undefined),\n\t\t);\n\t}\n\n\tupdate(scope: HookTrustStorageScope, updater: (current: HookTrustState) => HookTrustState): HookTrustState {\n\t\treturn withHookStateFileLock(statePathForScope(scope, this.globalStatePath, this.projectStatePath), (path) => {\n\t\t\tconst stateExists = existsSync(path);\n\t\t\tconst current = readHookTrustStateJson(stateExists ? readFileSync(path, \"utf-8\") : undefined);\n\t\t\tconst mode = stateExists ? statSync(path).mode & 0o777 : 0o600;\n\t\t\tconst next = updater(current);\n\t\t\tconst tempPath = `${path}.${process.pid}.${randomUUID()}.tmp`;\n\t\t\ttry {\n\t\t\t\twriteFileSync(tempPath, serializeHookTrustState(next), { encoding: \"utf-8\", mode });\n\t\t\t\tchmodSync(tempPath, mode);\n\t\t\t\trenameSync(tempPath, path);\n\t\t\t} catch (publicationError) {\n\t\t\t\ttry {\n\t\t\t\t\trmSync(tempPath, { force: true });\n\t\t\t\t} catch (cleanupError) {\n\t\t\t\t\tthrow new AggregateError(\n\t\t\t\t\t\t[publicationError, cleanupError],\n\t\t\t\t\t\t\"Failed to publish and clean up hook trust state snapshot\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tthrow publicationError;\n\t\t\t}\n\t\t\treturn next;\n\t\t});\n\t}\n}\n\nexport class InMemoryHookStateStorage implements HookStateStorage {\n\tprivate globalState: HookTrustState = emptyHookTrustState();\n\tprivate projectState: HookTrustState = emptyHookTrustState();\n\n\tread(scope: HookTrustStorageScope): HookTrustState {\n\t\treturn scope === \"global\" ? this.globalState : this.projectState;\n\t}\n\n\tupdate(scope: HookTrustStorageScope, updater: (current: HookTrustState) => HookTrustState): HookTrustState {\n\t\tconst next = updater(this.read(scope));\n\t\tif (scope === \"global\") {\n\t\t\tthis.globalState = next;\n\t\t} else {\n\t\t\tthis.projectState = next;\n\t\t}\n\t\treturn next;\n\t}\n}\n\nfunction statePathForScope(scope: HookTrustStorageScope, globalStatePath: string, projectStatePath: string): string {\n\treturn scope === \"global\" ? globalStatePath : projectStatePath;\n}\n\nfunction serializeHookTrustState(state: HookTrustState): string {\n\tconst sortedHooks: Record<string, HookTrustEntry> = {};\n\tfor (const key of Object.keys(state.hooks).sort()) {\n\t\tconst entry = state.hooks[key];\n\t\tif (entry !== undefined) {\n\t\t\tsortedHooks[key] = entry;\n\t\t}\n\t}\n\treturn `${JSON.stringify({ version: 1, hooks: sortedHooks }, null, 2)}\\n`;\n}\n\nfunction acquireHookStateLockSync(path: string): () => void {\n\tconst stateDir = dirname(path);\n\tmkdirSync(stateDir, { recursive: true });\n\tconst maxAttempts = 10;\n\tconst delayMs = 20;\n\tlet lastError: unknown;\n\n\tfor (let attempt = 1; attempt <= maxAttempts; attempt++) {\n\t\ttry {\n\t\t\treturn lockfile.lockSync(stateDir, { realpath: false, lockfilePath: `${path}.lock` });\n\t\t} catch (error) {\n\t\t\tconst code = errorCode(error);\n\t\t\tif (code !== \"ELOCKED\" || attempt === maxAttempts) {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tlastError = error;\n\t\t\tconst start = Date.now();\n\t\t\twhile (Date.now() - start < delayMs) {\n\t\t\t\tDate.now();\n\t\t\t}\n\t\t}\n\t}\n\n\tif (lastError instanceof Error) {\n\t\tthrow lastError;\n\t}\n\tthrow new Error(\"Failed to acquire hook state lock\");\n}\n\nfunction errorCode(error: unknown): string | undefined {\n\tif (!isRecord(error)) {\n\t\treturn undefined;\n\t}\n\tconst code = error.code;\n\treturn typeof code === \"string\" ? code : undefined;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction withHookStateFileLock<T>(path: string, fn: (path: string) => T): T {\n\tconst release = acquireHookStateLockSync(path);\n\treturn runWithHookStateLockRelease(release, () => fn(path));\n}\n\nfunction runWithHookStateLockRelease<T>(release: () => void, operation: () => T): T {\n\tlet result: T;\n\ttry {\n\t\tresult = operation();\n\t} catch (operationError) {\n\t\ttry {\n\t\t\trelease();\n\t\t} catch (releaseError) {\n\t\t\tthrow new AggregateError(\n\t\t\t\toperationError instanceof AggregateError\n\t\t\t\t\t? [...operationError.errors, releaseError]\n\t\t\t\t\t: [operationError, releaseError],\n\t\t\t\t\"Hook state operation and lock release both failed\",\n\t\t\t);\n\t\t}\n\t\tthrow operationError;\n\t}\n\trelease();\n\treturn result;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"trust-storage.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/hooks/trust-storage.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport lockfile from \"proper-lockfile\";\nimport { CONFIG_DIR_NAME, getAgentDir } from \"../../../../config.ts\";\nimport type { HookTrustStorageScope } from \"./trust.ts\";\nimport { emptyHookTrustState, parseHookTrustStateJson, readHookTrustStateJson } from \"./trust-state-json.ts\";\nimport type { HookTrustEntry, HookTrustState } from \"./types.ts\";\n\nexport interface HookStateStorage {\n\tread(scope: HookTrustStorageScope): HookTrustState;\n\tupdate(scope: HookTrustStorageScope, updater: (current: HookTrustState) => HookTrustState): HookTrustState;\n}\n\nexport type FileHookStateStorageOptions = {\n\treadonly agentDir?: string;\n\treadonly cwd: string;\n};\n\n/**\n * Internal same-account application state at `<agentDir>/hooks-state.json` and\n * `<cwd>/.senpi/hooks-state.json`. POSIX files retain their numeric mode and new\n * files use 0600; reassigned ownership, named ACLs, and custom DACLs are outside\n * this storage contract.\n */\nexport class FileHookStateStorage implements HookStateStorage {\n\tprivate readonly globalStatePath: string;\n\tprivate readonly projectStatePath: string;\n\n\tconstructor(options: FileHookStateStorageOptions) {\n\t\tconst agentDir = options.agentDir ?? getAgentDir();\n\t\tthis.globalStatePath = join(agentDir, \"hooks-state.json\");\n\t\tthis.projectStatePath = join(options.cwd, CONFIG_DIR_NAME, \"hooks-state.json\");\n\t}\n\n\tread(scope: HookTrustStorageScope): HookTrustState {\n\t\tconst path = statePathForScope(scope, this.globalStatePath, this.projectStatePath);\n\t\tconst snapshot = parseHookTrustStateJson(existsSync(path) ? readFileSync(path, \"utf-8\") : undefined);\n\t\tif (snapshot !== undefined) {\n\t\t\treturn snapshot;\n\t\t}\n\n\t\tlet release: () => void;\n\t\ttry {\n\t\t\trelease = acquireHookStateLockSync(path);\n\t\t} catch (error) {\n\t\t\tconst code = errorCode(error);\n\t\t\t// Sandboxed/read-only children cannot mkdir the lock dir; fail open on read.\n\t\t\tif (code === \"ELOCKED\" || code === \"EPERM\" || code === \"EACCES\" || code === \"EROFS\") {\n\t\t\t\treturn emptyHookTrustState();\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t\treturn runWithHookStateLockRelease(release, () =>\n\t\t\treadHookTrustStateJson(existsSync(path) ? readFileSync(path, \"utf-8\") : undefined),\n\t\t);\n\t}\n\n\tupdate(scope: HookTrustStorageScope, updater: (current: HookTrustState) => HookTrustState): HookTrustState {\n\t\treturn withHookStateFileLock(statePathForScope(scope, this.globalStatePath, this.projectStatePath), (path) => {\n\t\t\tconst stateExists = existsSync(path);\n\t\t\tconst current = readHookTrustStateJson(stateExists ? readFileSync(path, \"utf-8\") : undefined);\n\t\t\tconst mode = stateExists ? statSync(path).mode & 0o777 : 0o600;\n\t\t\tconst next = updater(current);\n\t\t\tconst tempPath = `${path}.${process.pid}.${randomUUID()}.tmp`;\n\t\t\ttry {\n\t\t\t\twriteFileSync(tempPath, serializeHookTrustState(next), { encoding: \"utf-8\", mode });\n\t\t\t\tchmodSync(tempPath, mode);\n\t\t\t\trenameSync(tempPath, path);\n\t\t\t} catch (publicationError) {\n\t\t\t\ttry {\n\t\t\t\t\trmSync(tempPath, { force: true });\n\t\t\t\t} catch (cleanupError) {\n\t\t\t\t\tthrow new AggregateError(\n\t\t\t\t\t\t[publicationError, cleanupError],\n\t\t\t\t\t\t\"Failed to publish and clean up hook trust state snapshot\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tthrow publicationError;\n\t\t\t}\n\t\t\treturn next;\n\t\t});\n\t}\n}\n\nexport class InMemoryHookStateStorage implements HookStateStorage {\n\tprivate globalState: HookTrustState = emptyHookTrustState();\n\tprivate projectState: HookTrustState = emptyHookTrustState();\n\n\tread(scope: HookTrustStorageScope): HookTrustState {\n\t\treturn scope === \"global\" ? this.globalState : this.projectState;\n\t}\n\n\tupdate(scope: HookTrustStorageScope, updater: (current: HookTrustState) => HookTrustState): HookTrustState {\n\t\tconst next = updater(this.read(scope));\n\t\tif (scope === \"global\") {\n\t\t\tthis.globalState = next;\n\t\t} else {\n\t\t\tthis.projectState = next;\n\t\t}\n\t\treturn next;\n\t}\n}\n\nfunction statePathForScope(scope: HookTrustStorageScope, globalStatePath: string, projectStatePath: string): string {\n\treturn scope === \"global\" ? globalStatePath : projectStatePath;\n}\n\nfunction serializeHookTrustState(state: HookTrustState): string {\n\tconst sortedHooks: Record<string, HookTrustEntry> = {};\n\tfor (const key of Object.keys(state.hooks).sort()) {\n\t\tconst entry = state.hooks[key];\n\t\tif (entry !== undefined) {\n\t\t\tsortedHooks[key] = entry;\n\t\t}\n\t}\n\treturn `${JSON.stringify({ version: 1, hooks: sortedHooks }, null, 2)}\\n`;\n}\n\nfunction acquireHookStateLockSync(path: string): () => void {\n\tconst stateDir = dirname(path);\n\tmkdirSync(stateDir, { recursive: true });\n\tconst maxAttempts = 10;\n\tconst delayMs = 20;\n\tlet lastError: unknown;\n\n\tfor (let attempt = 1; attempt <= maxAttempts; attempt++) {\n\t\ttry {\n\t\t\treturn lockfile.lockSync(stateDir, { realpath: false, lockfilePath: `${path}.lock` });\n\t\t} catch (error) {\n\t\t\tconst code = errorCode(error);\n\t\t\tif (code !== \"ELOCKED\" || attempt === maxAttempts) {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tlastError = error;\n\t\t\tconst start = Date.now();\n\t\t\twhile (Date.now() - start < delayMs) {\n\t\t\t\tDate.now();\n\t\t\t}\n\t\t}\n\t}\n\n\tif (lastError instanceof Error) {\n\t\tthrow lastError;\n\t}\n\tthrow new Error(\"Failed to acquire hook state lock\");\n}\n\nfunction errorCode(error: unknown): string | undefined {\n\tif (!isRecord(error)) {\n\t\treturn undefined;\n\t}\n\tconst code = error.code;\n\treturn typeof code === \"string\" ? code : undefined;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction withHookStateFileLock<T>(path: string, fn: (path: string) => T): T {\n\tconst release = acquireHookStateLockSync(path);\n\treturn runWithHookStateLockRelease(release, () => fn(path));\n}\n\nfunction runWithHookStateLockRelease<T>(release: () => void, operation: () => T): T {\n\tlet result: T;\n\ttry {\n\t\tresult = operation();\n\t} catch (operationError) {\n\t\ttry {\n\t\t\trelease();\n\t\t} catch (releaseError) {\n\t\t\tthrow new AggregateError(\n\t\t\t\toperationError instanceof AggregateError\n\t\t\t\t\t? [...operationError.errors, releaseError]\n\t\t\t\t\t: [operationError, releaseError],\n\t\t\t\t\"Hook state operation and lock release both failed\",\n\t\t\t);\n\t\t}\n\t\tthrow operationError;\n\t}\n\trelease();\n\treturn result;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/imagegen/auth.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED,MAAM,MAAM,0BAA0B,GACnC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAA;CAAE,GACtE;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhC,MAAM,WAAW,0BAA0B;IAC1C,IAAI,EAAE;QACL,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC;KACxC,CAAC;CACF;AAED,MAAM,WAAW,oBAAoB,CAAC,MAAM,SAAS,iBAAiB,GAAG,iBAAiB;IACzF,WAAW,EAAE;QACZ,GAAG,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;KAC/B,CAAC;IACF,MAAM,IAAI,MAAM,EAAE,CAAC;IACnB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,0BAA0B,CAAC,CAAC;IACxE,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,0BAA0B,GAAG,SAAS,CAAC,CAAC;CACnF;AAED,MAAM,WAAW,uBAAuB,CAAC,MAAM,SAAS,iBAAiB,GAAG,iBAAiB;IAC5F,aAAa,EAAE,oBAAoB,CAAC,MAAM,CAAC,CAAC;IAC5C,GAAG,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;CACnD;AAED,MAAM,MAAM,sBAAsB,GAC/B;IACA,IAAI,EAAE,eAAe,GAAG,SAAS,CAAC;IAClC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,UAAU,EAAE,OAAO,GAAG,KAAK,GAAG,iBAAiB,CAAC;IAChD,UAAU,CAAC,EAAE,MAAM,CAAC;CACnB,GACD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/imagegen/auth.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED,MAAM,MAAM,0BAA0B,GACnC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAA;CAAE,GACtE;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhC,MAAM,WAAW,0BAA0B;IAC1C,IAAI,EAAE;QACL,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC;KACxC,CAAC;CACF;AAED,MAAM,WAAW,oBAAoB,CAAC,MAAM,SAAS,iBAAiB,GAAG,iBAAiB;IACzF,WAAW,EAAE;QACZ,GAAG,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;KAC/B,CAAC;IACF,MAAM,IAAI,MAAM,EAAE,CAAC;IACnB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,0BAA0B,CAAC,CAAC;IACxE,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,0BAA0B,GAAG,SAAS,CAAC,CAAC;CACnF;AAED,MAAM,WAAW,uBAAuB,CAAC,MAAM,SAAS,iBAAiB,GAAG,iBAAiB;IAC5F,aAAa,EAAE,oBAAoB,CAAC,MAAM,CAAC,CAAC;IAC5C,GAAG,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;CACnD;AAED,MAAM,MAAM,sBAAsB,GAC/B;IACA,IAAI,EAAE,eAAe,GAAG,SAAS,CAAC;IAClC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,UAAU,EAAE,OAAO,GAAG,KAAK,GAAG,iBAAiB,CAAC;IAChD,UAAU,CAAC,EAAE,MAAM,CAAC;CACnB,GACD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AA0HpC,wBAAsB,mBAAmB,CAAC,MAAM,SAAS,iBAAiB,EACzE,IAAI,EAAE,uBAAuB,CAAC,MAAM,CAAC,GACnC,OAAO,CAAC,sBAAsB,CAAC,CAoCjC"}
|
|
@@ -14,9 +14,17 @@ function resolvedHeaders(headers) {
|
|
|
14
14
|
}
|
|
15
15
|
return Object.keys(resolved).length > 0 ? resolved : undefined;
|
|
16
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* Seeded placeholder keys (`SK-SENTINEL-DO-NOT-LOG-*`) are not credentials. Left unfiltered, such a
|
|
19
|
+
* value satisfies the stored-openai branch, pins `api.openai.com`, and every request 401s with
|
|
20
|
+
* `Incorrect API key provided: SK-SENTI***OG-2` while a working gateway sits unused.
|
|
21
|
+
*/
|
|
22
|
+
function isPlaceholderKey(apiKey) {
|
|
23
|
+
return /^SK-SENTINEL-DO-NOT-LOG/i.test(apiKey);
|
|
24
|
+
}
|
|
17
25
|
function credentialParts(auth) {
|
|
18
26
|
const apiKey = nonEmpty(auth?.apiKey);
|
|
19
|
-
if (!apiKey)
|
|
27
|
+
if (!apiKey || isPlaceholderKey(apiKey))
|
|
20
28
|
return undefined;
|
|
21
29
|
const headers = resolvedHeaders(auth?.headers);
|
|
22
30
|
return { apiKey, ...(headers ? { headers } : {}) };
|
|
@@ -124,7 +132,7 @@ export async function resolveImageGenAuth(deps) {
|
|
|
124
132
|
return gateway;
|
|
125
133
|
}
|
|
126
134
|
const envApiKey = nonEmpty(env.OPENAI_API_KEY);
|
|
127
|
-
if (envApiKey) {
|
|
135
|
+
if (envApiKey && !isPlaceholderKey(envApiKey)) {
|
|
128
136
|
return {
|
|
129
137
|
kind: "native-openai",
|
|
130
138
|
apiKey: envApiKey,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/imagegen/auth.ts"],"names":[],"mappings":"AAgDA,MAAM,eAAe,GAAG,2BAA2B,CAAC;AACpD,MAAM,YAAY,GACjB,mNAAmN,CAAC;AAErN,SAAS,QAAQ,CAAC,KAAyB;IAC1C,MAAM,OAAO,GAAG,KAAK,EAAE,IAAI,EAAE,CAAC;IAC9B,OAAO,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;AACtC,CAAC;AAED,SAAS,eAAe,CAAC,OAAkD;IAC1E,IAAI,CAAC,OAAO;QAAE,OAAO,SAAS,CAAC;IAC/B,MAAM,QAAQ,GAA2B,EAAE,CAAC;IAC5C,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACrD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC;YAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC;IAClF,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;AAChE,CAAC;AAED,SAAS,eAAe,CACvB,IAA8E;IAE9E,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACtC,IAAI,CAAC,MAAM;QAAE,OAAO,SAAS,CAAC;IAC9B,MAAM,OAAO,GAAG,eAAe,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC/C,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;AACpD,CAAC;AAED,SAAS,cAAc,CAAC,KAAc;IACrC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,MAAM,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC;AACnG,CAAC;AAED,SAAS,YAAY,CAAC,GAAuB;IAC5C,OAAO,GAAG,KAAK,oBAAoB,IAAI,GAAG,KAAK,kBAAkB,CAAC;AACnE,CAAC;AAED,SAAS,WAAW,CAAC,IAAY,EAAE,KAAa;IAC/C,IAAI,IAAI,GAAG,KAAK;QAAE,OAAO,CAAC,CAAC,CAAC;IAC5B,IAAI,IAAI,GAAG,KAAK;QAAE,OAAO,CAAC,CAAC;IAC3B,OAAO,CAAC,CAAC;AACV,CAAC;AAED,SAAS,kBAAkB,CAAC,IAAY,EAAE,KAAa;IACtD,MAAM,WAAW,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACzC,MAAM,YAAY,GAAG,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC3C,IAAI,WAAW,KAAK,YAAY;QAAE,OAAO,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9D,OAAO,WAAW,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;AACjC,CAAC;AAED,SAAS,kBAAkB,CAAmC,MAAyB;IACtF,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACzC,MAAM,OAAO,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,CAC/B,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,IAAI,WAAW,CAAC,IAAI,CAAC,EAAE,EAAE,KAAK,CAAC,EAAE,CAAC,CAC7F,CAAC;IACF,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,KAAK,CAAC,QAAQ,KAAK,QAAQ,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC;YAAE,SAAS;QAClG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC;YAAE,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IACpE,CAAC;IACD,OAAO,MAAM,CAAC;AACf,CAAC;AAED,KAAK,UAAU,mBAAmB,CACjC,QAAsC;IAEtC,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAAE,OAAO,SAAS,CAAC;IAC1E,IAAI,QAAgD,CAAC;IACrD,IAAI,CAAC;QACJ,QAAQ,GAAG,MAAM,QAAQ,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;IACrD,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,SAAS,CAAC;IAClB,CAAC;IACD,MAAM,WAAW,GAAG,eAAe,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IACpD,IAAI,CAAC,WAAW;QAAE,OAAO,SAAS,CAAC;IACnC,OAAO;QACN,IAAI,EAAE,eAAe;QACrB,GAAG,WAAW;QACd,OAAO,EAAE,eAAe;QACxB,UAAU,EAAE,OAAO;QACnB,UAAU,EAAE,QAAQ;KACpB,CAAC;AACH,CAAC;AAED,KAAK,UAAU,cAAc,CAC5B,UAAkB,EAClB,KAAa,EACb,QAAsC;IAEtC,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACxC,IAAI,CAAC,OAAO;QAAE,OAAO,SAAS,CAAC;IAC/B,IAAI,QAAoC,CAAC;IACzC,IAAI,CAAC;QACJ,QAAQ,GAAG,MAAM,QAAQ,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC;IACtD,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,SAAS,CAAC;IAClB,CAAC;IACD,IAAI,CAAC,QAAQ,CAAC,EAAE;QAAE,OAAO,SAAS,CAAC;IACnC,MAAM,WAAW,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;IAC9C,IAAI,CAAC,WAAW;QAAE,OAAO,SAAS,CAAC;IACnC,OAAO;QACN,IAAI,EAAE,SAAS;QACf,GAAG,WAAW;QACd,OAAO;QACP,UAAU,EAAE,iBAAiB;QAC7B,UAAU;KACV,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACxC,IAAqC;IAErC,MAAM,MAAM,GAAG,MAAM,mBAAmB,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IAC7D,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAE1B,MAAM,MAAM,GAAG,kBAAkB,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC;IAC/D,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC;IACpC,MAAM,cAAc,GAAG,QAAQ,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;IAC3D,IAAI,cAAc,EAAE,CAAC;QACpB,MAAM,WAAW,GAAG,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;QAC/C,IAAI,WAAW,EAAE,CAAC;YACjB,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,cAAc,EAAE,WAAW,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;YACrF,IAAI,MAAM;gBAAE,OAAO,MAAM,CAAC;QAC3B,CAAC;IACF,CAAC;IAED,MAAM,WAAW,GAAG,CAAC,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;SACpC,MAAM,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,KAAK,cAAc,CAAC;SACrD,IAAI,CAAC,kBAAkB,CAAC,CAAC;IAC3B,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE,CAAC;QACtC,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACrC,IAAI,CAAC,KAAK;YAAE,SAAS;QACrB,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,UAAU,EAAE,KAAK,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;QAC5E,IAAI,OAAO;YAAE,OAAO,OAAO,CAAC;IAC7B,CAAC;IAED,MAAM,SAAS,GAAG,QAAQ,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAC/C,IAAI,SAAS,EAAE,CAAC;QACf,OAAO;YACN,IAAI,EAAE,eAAe;YACrB,MAAM,EAAE,SAAS;YACjB,OAAO,EAAE,eAAe;YACxB,UAAU,EAAE,KAAK;SACjB,CAAC;IACH,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC;AAC/C,CAAC","sourcesContent":["export interface ImageGenAuthModel {\n\tprovider: string;\n\tid: string;\n\tbaseUrl: string;\n\tapi?: string;\n}\n\nexport type ImageGenRegistryAuthResult =\n\t| { ok: true; apiKey?: string; headers?: Record<string, string | null> }\n\t| { ok: false; error: string };\n\nexport interface ImageGenProviderAuthResult {\n\tauth: {\n\t\tapiKey?: string;\n\t\theaders?: Record<string, string | null>;\n\t};\n}\n\nexport interface ImageGenAuthRegistry<TModel extends ImageGenAuthModel = ImageGenAuthModel> {\n\tauthStorage: {\n\t\tget(provider: string): unknown;\n\t};\n\tgetAll(): TModel[];\n\tgetApiKeyAndHeaders(model: TModel): Promise<ImageGenRegistryAuthResult>;\n\tgetProviderAuth(provider: string): Promise<ImageGenProviderAuthResult | undefined>;\n}\n\nexport interface ResolveImageGenAuthDeps<TModel extends ImageGenAuthModel = ImageGenAuthModel> {\n\tmodelRegistry: ImageGenAuthRegistry<TModel>;\n\tenv?: Readonly<Record<string, string | undefined>>;\n}\n\nexport type ImageGenAuthResolution =\n\t| {\n\t\t\tkind: \"native-openai\" | \"gateway\";\n\t\t\tapiKey: string | undefined;\n\t\t\tbaseUrl: string;\n\t\t\theaders?: Record<string, string>;\n\t\t\tprovenance: \"store\" | \"env\" | \"provider-config\";\n\t\t\tproviderId?: string;\n\t }\n\t| { kind: \"none\"; reason: string };\n\ninterface CredentialParts {\n\tapiKey: string | undefined;\n\theaders?: Record<string, string>;\n}\n\nconst OPENAI_BASE_URL = \"https://api.openai.com/v1\";\nconst SETUP_REASON =\n\t'Image generation is not configured. Store an OpenAI API key for provider \"openai\", configure an OpenAI-compatible gateway in models.json and optionally pin it with PI_IMAGE_GEN_PROVIDER, or set OPENAI_API_KEY.';\n\nfunction nonEmpty(value: string | undefined): string | undefined {\n\tconst trimmed = value?.trim();\n\treturn trimmed ? trimmed : undefined;\n}\n\nfunction resolvedHeaders(headers: Record<string, string | null> | undefined): Record<string, string> | undefined {\n\tif (!headers) return undefined;\n\tconst resolved: Record<string, string> = {};\n\tfor (const [name, value] of Object.entries(headers)) {\n\t\tif (typeof value === \"string\" && value.trim().length > 0) resolved[name] = value;\n\t}\n\treturn Object.keys(resolved).length > 0 ? resolved : undefined;\n}\n\nfunction credentialParts(\n\tauth: { apiKey?: string; headers?: Record<string, string | null> } | undefined,\n): CredentialParts | undefined {\n\tconst apiKey = nonEmpty(auth?.apiKey);\n\tif (!apiKey) return undefined;\n\tconst headers = resolvedHeaders(auth?.headers);\n\treturn { apiKey, ...(headers ? { headers } : {}) };\n}\n\nfunction isStoredApiKey(value: unknown): boolean {\n\treturn typeof value === \"object\" && value !== null && \"type\" in value && value.type === \"api_key\";\n}\n\nfunction isGatewayApi(api: string | undefined): boolean {\n\treturn api === \"openai-completions\" || api === \"openai-responses\";\n}\n\nfunction compareText(left: string, right: string): number {\n\tif (left < right) return -1;\n\tif (left > right) return 1;\n\treturn 0;\n}\n\nfunction compareProviderIds(left: string, right: string): number {\n\tconst leftMatches = /openai/i.test(left);\n\tconst rightMatches = /openai/i.test(right);\n\tif (leftMatches !== rightMatches) return leftMatches ? -1 : 1;\n\treturn compareText(left, right);\n}\n\nfunction groupGatewayModels<TModel extends ImageGenAuthModel>(models: readonly TModel[]): Map<string, TModel> {\n\tconst groups = new Map<string, TModel>();\n\tconst ordered = [...models].sort(\n\t\t(left, right) => compareText(left.provider, right.provider) || compareText(left.id, right.id),\n\t);\n\tfor (const model of ordered) {\n\t\tif (model.provider === \"openai\" || !isGatewayApi(model.api) || !nonEmpty(model.baseUrl)) continue;\n\t\tif (!groups.has(model.provider)) groups.set(model.provider, model);\n\t}\n\treturn groups;\n}\n\nasync function resolveStoredOpenAi<TModel extends ImageGenAuthModel>(\n\tregistry: ImageGenAuthRegistry<TModel>,\n): Promise<ImageGenAuthResolution | undefined> {\n\tif (!isStoredApiKey(registry.authStorage.get(\"openai\"))) return undefined;\n\tlet resolved: ImageGenProviderAuthResult | undefined;\n\ttry {\n\t\tresolved = await registry.getProviderAuth(\"openai\");\n\t} catch {\n\t\treturn undefined;\n\t}\n\tconst credentials = credentialParts(resolved?.auth);\n\tif (!credentials) return undefined;\n\treturn {\n\t\tkind: \"native-openai\",\n\t\t...credentials,\n\t\tbaseUrl: OPENAI_BASE_URL,\n\t\tprovenance: \"store\",\n\t\tproviderId: \"openai\",\n\t};\n}\n\nasync function resolveGateway<TModel extends ImageGenAuthModel>(\n\tproviderId: string,\n\tmodel: TModel,\n\tregistry: ImageGenAuthRegistry<TModel>,\n): Promise<ImageGenAuthResolution | undefined> {\n\tconst baseUrl = nonEmpty(model.baseUrl);\n\tif (!baseUrl) return undefined;\n\tlet resolved: ImageGenRegistryAuthResult;\n\ttry {\n\t\tresolved = await registry.getApiKeyAndHeaders(model);\n\t} catch {\n\t\treturn undefined;\n\t}\n\tif (!resolved.ok) return undefined;\n\tconst credentials = credentialParts(resolved);\n\tif (!credentials) return undefined;\n\treturn {\n\t\tkind: \"gateway\",\n\t\t...credentials,\n\t\tbaseUrl,\n\t\tprovenance: \"provider-config\",\n\t\tproviderId,\n\t};\n}\n\nexport async function resolveImageGenAuth<TModel extends ImageGenAuthModel>(\n\tdeps: ResolveImageGenAuthDeps<TModel>,\n): Promise<ImageGenAuthResolution> {\n\tconst native = await resolveStoredOpenAi(deps.modelRegistry);\n\tif (native) return native;\n\n\tconst groups = groupGatewayModels(deps.modelRegistry.getAll());\n\tconst env = deps.env ?? process.env;\n\tconst pinnedProvider = nonEmpty(env.PI_IMAGE_GEN_PROVIDER);\n\tif (pinnedProvider) {\n\t\tconst pinnedModel = groups.get(pinnedProvider);\n\t\tif (pinnedModel) {\n\t\t\tconst pinned = await resolveGateway(pinnedProvider, pinnedModel, deps.modelRegistry);\n\t\t\tif (pinned) return pinned;\n\t\t}\n\t}\n\n\tconst providerIds = [...groups.keys()]\n\t\t.filter((providerId) => providerId !== pinnedProvider)\n\t\t.sort(compareProviderIds);\n\tfor (const providerId of providerIds) {\n\t\tconst model = groups.get(providerId);\n\t\tif (!model) continue;\n\t\tconst gateway = await resolveGateway(providerId, model, deps.modelRegistry);\n\t\tif (gateway) return gateway;\n\t}\n\n\tconst envApiKey = nonEmpty(env.OPENAI_API_KEY);\n\tif (envApiKey) {\n\t\treturn {\n\t\t\tkind: \"native-openai\",\n\t\t\tapiKey: envApiKey,\n\t\t\tbaseUrl: OPENAI_BASE_URL,\n\t\t\tprovenance: \"env\",\n\t\t};\n\t}\n\n\treturn { kind: \"none\", reason: SETUP_REASON };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/imagegen/auth.ts"],"names":[],"mappings":"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","sourcesContent":["export interface ImageGenAuthModel {\n\tprovider: string;\n\tid: string;\n\tbaseUrl: string;\n\tapi?: string;\n}\n\nexport type ImageGenRegistryAuthResult =\n\t| { ok: true; apiKey?: string; headers?: Record<string, string | null> }\n\t| { ok: false; error: string };\n\nexport interface ImageGenProviderAuthResult {\n\tauth: {\n\t\tapiKey?: string;\n\t\theaders?: Record<string, string | null>;\n\t};\n}\n\nexport interface ImageGenAuthRegistry<TModel extends ImageGenAuthModel = ImageGenAuthModel> {\n\tauthStorage: {\n\t\tget(provider: string): unknown;\n\t};\n\tgetAll(): TModel[];\n\tgetApiKeyAndHeaders(model: TModel): Promise<ImageGenRegistryAuthResult>;\n\tgetProviderAuth(provider: string): Promise<ImageGenProviderAuthResult | undefined>;\n}\n\nexport interface ResolveImageGenAuthDeps<TModel extends ImageGenAuthModel = ImageGenAuthModel> {\n\tmodelRegistry: ImageGenAuthRegistry<TModel>;\n\tenv?: Readonly<Record<string, string | undefined>>;\n}\n\nexport type ImageGenAuthResolution =\n\t| {\n\t\t\tkind: \"native-openai\" | \"gateway\";\n\t\t\tapiKey: string | undefined;\n\t\t\tbaseUrl: string;\n\t\t\theaders?: Record<string, string>;\n\t\t\tprovenance: \"store\" | \"env\" | \"provider-config\";\n\t\t\tproviderId?: string;\n\t }\n\t| { kind: \"none\"; reason: string };\n\ninterface CredentialParts {\n\tapiKey: string | undefined;\n\theaders?: Record<string, string>;\n}\n\nconst OPENAI_BASE_URL = \"https://api.openai.com/v1\";\nconst SETUP_REASON =\n\t'Image generation is not configured. Store an OpenAI API key for provider \"openai\", configure an OpenAI-compatible gateway in models.json and optionally pin it with PI_IMAGE_GEN_PROVIDER, or set OPENAI_API_KEY.';\n\nfunction nonEmpty(value: string | undefined): string | undefined {\n\tconst trimmed = value?.trim();\n\treturn trimmed ? trimmed : undefined;\n}\n\nfunction resolvedHeaders(headers: Record<string, string | null> | undefined): Record<string, string> | undefined {\n\tif (!headers) return undefined;\n\tconst resolved: Record<string, string> = {};\n\tfor (const [name, value] of Object.entries(headers)) {\n\t\tif (typeof value === \"string\" && value.trim().length > 0) resolved[name] = value;\n\t}\n\treturn Object.keys(resolved).length > 0 ? resolved : undefined;\n}\n\n/**\n * Seeded placeholder keys (`SK-SENTINEL-DO-NOT-LOG-*`) are not credentials. Left unfiltered, such a\n * value satisfies the stored-openai branch, pins `api.openai.com`, and every request 401s with\n * `Incorrect API key provided: SK-SENTI***OG-2` while a working gateway sits unused.\n */\nfunction isPlaceholderKey(apiKey: string): boolean {\n\treturn /^SK-SENTINEL-DO-NOT-LOG/i.test(apiKey);\n}\n\nfunction credentialParts(\n\tauth: { apiKey?: string; headers?: Record<string, string | null> } | undefined,\n): CredentialParts | undefined {\n\tconst apiKey = nonEmpty(auth?.apiKey);\n\tif (!apiKey || isPlaceholderKey(apiKey)) return undefined;\n\tconst headers = resolvedHeaders(auth?.headers);\n\treturn { apiKey, ...(headers ? { headers } : {}) };\n}\n\nfunction isStoredApiKey(value: unknown): boolean {\n\treturn typeof value === \"object\" && value !== null && \"type\" in value && value.type === \"api_key\";\n}\n\nfunction isGatewayApi(api: string | undefined): boolean {\n\treturn api === \"openai-completions\" || api === \"openai-responses\";\n}\n\nfunction compareText(left: string, right: string): number {\n\tif (left < right) return -1;\n\tif (left > right) return 1;\n\treturn 0;\n}\n\nfunction compareProviderIds(left: string, right: string): number {\n\tconst leftMatches = /openai/i.test(left);\n\tconst rightMatches = /openai/i.test(right);\n\tif (leftMatches !== rightMatches) return leftMatches ? -1 : 1;\n\treturn compareText(left, right);\n}\n\nfunction groupGatewayModels<TModel extends ImageGenAuthModel>(models: readonly TModel[]): Map<string, TModel> {\n\tconst groups = new Map<string, TModel>();\n\tconst ordered = [...models].sort(\n\t\t(left, right) => compareText(left.provider, right.provider) || compareText(left.id, right.id),\n\t);\n\tfor (const model of ordered) {\n\t\tif (model.provider === \"openai\" || !isGatewayApi(model.api) || !nonEmpty(model.baseUrl)) continue;\n\t\tif (!groups.has(model.provider)) groups.set(model.provider, model);\n\t}\n\treturn groups;\n}\n\nasync function resolveStoredOpenAi<TModel extends ImageGenAuthModel>(\n\tregistry: ImageGenAuthRegistry<TModel>,\n): Promise<ImageGenAuthResolution | undefined> {\n\tif (!isStoredApiKey(registry.authStorage.get(\"openai\"))) return undefined;\n\tlet resolved: ImageGenProviderAuthResult | undefined;\n\ttry {\n\t\tresolved = await registry.getProviderAuth(\"openai\");\n\t} catch {\n\t\treturn undefined;\n\t}\n\tconst credentials = credentialParts(resolved?.auth);\n\tif (!credentials) return undefined;\n\treturn {\n\t\tkind: \"native-openai\",\n\t\t...credentials,\n\t\tbaseUrl: OPENAI_BASE_URL,\n\t\tprovenance: \"store\",\n\t\tproviderId: \"openai\",\n\t};\n}\n\nasync function resolveGateway<TModel extends ImageGenAuthModel>(\n\tproviderId: string,\n\tmodel: TModel,\n\tregistry: ImageGenAuthRegistry<TModel>,\n): Promise<ImageGenAuthResolution | undefined> {\n\tconst baseUrl = nonEmpty(model.baseUrl);\n\tif (!baseUrl) return undefined;\n\tlet resolved: ImageGenRegistryAuthResult;\n\ttry {\n\t\tresolved = await registry.getApiKeyAndHeaders(model);\n\t} catch {\n\t\treturn undefined;\n\t}\n\tif (!resolved.ok) return undefined;\n\tconst credentials = credentialParts(resolved);\n\tif (!credentials) return undefined;\n\treturn {\n\t\tkind: \"gateway\",\n\t\t...credentials,\n\t\tbaseUrl,\n\t\tprovenance: \"provider-config\",\n\t\tproviderId,\n\t};\n}\n\nexport async function resolveImageGenAuth<TModel extends ImageGenAuthModel>(\n\tdeps: ResolveImageGenAuthDeps<TModel>,\n): Promise<ImageGenAuthResolution> {\n\tconst native = await resolveStoredOpenAi(deps.modelRegistry);\n\tif (native) return native;\n\n\tconst groups = groupGatewayModels(deps.modelRegistry.getAll());\n\tconst env = deps.env ?? process.env;\n\tconst pinnedProvider = nonEmpty(env.PI_IMAGE_GEN_PROVIDER);\n\tif (pinnedProvider) {\n\t\tconst pinnedModel = groups.get(pinnedProvider);\n\t\tif (pinnedModel) {\n\t\t\tconst pinned = await resolveGateway(pinnedProvider, pinnedModel, deps.modelRegistry);\n\t\t\tif (pinned) return pinned;\n\t\t}\n\t}\n\n\tconst providerIds = [...groups.keys()]\n\t\t.filter((providerId) => providerId !== pinnedProvider)\n\t\t.sort(compareProviderIds);\n\tfor (const providerId of providerIds) {\n\t\tconst model = groups.get(providerId);\n\t\tif (!model) continue;\n\t\tconst gateway = await resolveGateway(providerId, model, deps.modelRegistry);\n\t\tif (gateway) return gateway;\n\t}\n\n\tconst envApiKey = nonEmpty(env.OPENAI_API_KEY);\n\tif (envApiKey && !isPlaceholderKey(envApiKey)) {\n\t\treturn {\n\t\t\tkind: \"native-openai\",\n\t\t\tapiKey: envApiKey,\n\t\t\tbaseUrl: OPENAI_BASE_URL,\n\t\t\tprovenance: \"env\",\n\t\t};\n\t}\n\n\treturn { kind: \"none\", reason: SETUP_REASON };\n}\n"]}
|
|
@@ -1,22 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Atomic, versioned, per-session sidecar store for `/loop` state.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* Persistence (temp file + rename, per-file mutation serialization, envelope
|
|
5
|
+
* version/session checks) lives in the shared session sidecar primitive. This
|
|
6
|
+
* module supplies the loop domain parser and keeps the historical exported
|
|
7
|
+
* surface so callers continue to catch {@link InvalidLoopStoreError} /
|
|
8
|
+
* {@link UnsupportedLoopStoreVersionError} by name.
|
|
8
9
|
*
|
|
9
10
|
* Session custom entries are deliberately NOT the authoritative store: they are branch
|
|
10
11
|
* nodes, so a globally scanned "latest" entry can come from an abandoned branch, while
|
|
11
12
|
* loop schedules are session-scoped.
|
|
12
13
|
*/
|
|
14
|
+
import { InvalidSidecarStoreError, UnsupportedSidecarStoreVersionError } from "../../../session-sidecar-store.ts";
|
|
13
15
|
import { type LoopState, type LoopStoreRef } from "./types.ts";
|
|
14
|
-
export declare class InvalidLoopStoreError extends
|
|
16
|
+
export declare class InvalidLoopStoreError extends InvalidSidecarStoreError {
|
|
15
17
|
constructor(message: string, options?: {
|
|
16
18
|
cause?: unknown;
|
|
17
19
|
});
|
|
18
20
|
}
|
|
19
|
-
export declare class UnsupportedLoopStoreVersionError extends
|
|
21
|
+
export declare class UnsupportedLoopStoreVersionError extends UnsupportedSidecarStoreVersionError {
|
|
20
22
|
constructor(message: string);
|
|
21
23
|
}
|
|
22
24
|
/** Mutation callback. May be async; its result becomes the next persisted state. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/loop/store.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/loop/store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAGN,wBAAwB,EAIxB,mCAAmC,EACnC,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAqBN,KAAK,SAAS,EACd,KAAK,YAAY,EAQjB,MAAM,YAAY,CAAC;AAEpB,qBAAa,qBAAsB,SAAQ,wBAAwB;gBACtD,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAI1D;AAED,qBAAa,gCAAiC,SAAQ,mCAAmC;gBAC5E,OAAO,EAAE,MAAM;CAI3B;AAED,oFAAoF;AACpF,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,EAAE,SAAS,KAAK,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;AAIvF,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAE1D;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAE3D;AAED,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,CAE3D;AA8BD;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAMhF;AAED,wFAAwF;AACxF,wBAAsB,aAAa,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC,CAEzE;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,YAAY,GAAG,SAAS,GAAG,SAAS,CAE1E;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,YAAY,EAAE,QAAQ,EAAE,iBAAiB,GAAG,OAAO,CAAC,SAAS,CAAC,CAIlG;AAED,wFAAwF;AACxF,wBAAsB,cAAc,CAAC,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAEvF;AAED,oFAAoF;AACpF,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,YAAY,GAAG,IAAI,CAE9D"}
|
|
@@ -1,60 +1,79 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Atomic, versioned, per-session sidecar store for `/loop` state.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* Persistence (temp file + rename, per-file mutation serialization, envelope
|
|
5
|
+
* version/session checks) lives in the shared session sidecar primitive. This
|
|
6
|
+
* module supplies the loop domain parser and keeps the historical exported
|
|
7
|
+
* surface so callers continue to catch {@link InvalidLoopStoreError} /
|
|
8
|
+
* {@link UnsupportedLoopStoreVersionError} by name.
|
|
8
9
|
*
|
|
9
10
|
* Session custom entries are deliberately NOT the authoritative store: they are branch
|
|
10
11
|
* nodes, so a globally scanned "latest" entry can come from an abandoned branch, while
|
|
11
12
|
* loop schedules are session-scoped.
|
|
12
13
|
*/
|
|
13
|
-
import {
|
|
14
|
-
import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
15
|
-
import { dirname, join } from "node:path";
|
|
14
|
+
import { createSidecarStore, encodedSessionId as encodeSidecarSessionId, InvalidSidecarStoreError, sidecarFilePath, UnsupportedSidecarStoreVersionError, } from "../../../session-sidecar-store.js";
|
|
16
15
|
import { EFFECTIVE_INTERVAL_UNITS, isRecord, LOOP_END_REASONS, LOOP_PHASES, LOOP_SENTINELS, LOOP_STATE_VERSION, LOOP_WAKE_SOURCE_KINDS, REQUESTED_INTERVAL_UNITS, } from "./types.js";
|
|
17
|
-
export class InvalidLoopStoreError extends
|
|
16
|
+
export class InvalidLoopStoreError extends InvalidSidecarStoreError {
|
|
18
17
|
constructor(message, options) {
|
|
19
18
|
super(message, options);
|
|
20
19
|
this.name = "InvalidLoopStoreError";
|
|
21
20
|
}
|
|
22
21
|
}
|
|
23
|
-
export class UnsupportedLoopStoreVersionError extends
|
|
22
|
+
export class UnsupportedLoopStoreVersionError extends UnsupportedSidecarStoreVersionError {
|
|
24
23
|
constructor(message) {
|
|
25
24
|
super(message);
|
|
26
25
|
this.name = "UnsupportedLoopStoreVersionError";
|
|
27
26
|
}
|
|
28
27
|
}
|
|
29
|
-
const
|
|
30
|
-
const snapshots = new Map();
|
|
28
|
+
const stores = new Map();
|
|
31
29
|
export function encodedSessionId(ref) {
|
|
32
|
-
return
|
|
30
|
+
return encodeSidecarSessionId(ref.sessionId);
|
|
33
31
|
}
|
|
34
32
|
export function loopStateFilePath(ref) {
|
|
35
|
-
return
|
|
33
|
+
return sidecarFilePath(ref);
|
|
36
34
|
}
|
|
37
35
|
export function emptyLoopState(sessionId) {
|
|
38
36
|
return { version: LOOP_STATE_VERSION, sessionId, entries: {}, activeDynamicId: null, updatedAt: 0 };
|
|
39
37
|
}
|
|
38
|
+
function storeFor(ref) {
|
|
39
|
+
const filePath = loopStateFilePath(ref);
|
|
40
|
+
const existing = stores.get(filePath);
|
|
41
|
+
if (existing !== undefined)
|
|
42
|
+
return existing;
|
|
43
|
+
const store = createSidecarStore({
|
|
44
|
+
baseDir: ref.baseDir,
|
|
45
|
+
sessionId: ref.sessionId,
|
|
46
|
+
version: LOOP_STATE_VERSION,
|
|
47
|
+
tempPrefix: "loop",
|
|
48
|
+
parse: parseLoopPayload,
|
|
49
|
+
});
|
|
50
|
+
stores.set(filePath, store);
|
|
51
|
+
return store;
|
|
52
|
+
}
|
|
53
|
+
function remapLoopStoreError(error) {
|
|
54
|
+
if (error instanceof InvalidLoopStoreError || error instanceof UnsupportedLoopStoreVersionError) {
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
if (error instanceof UnsupportedSidecarStoreVersionError) {
|
|
58
|
+
throw new UnsupportedLoopStoreVersionError(error.message.replaceAll("sidecar store", "loop store"));
|
|
59
|
+
}
|
|
60
|
+
if (error instanceof InvalidSidecarStoreError) {
|
|
61
|
+
throw new InvalidLoopStoreError(error.message.replaceAll("sidecar store", "loop store"), { cause: error });
|
|
62
|
+
}
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
40
65
|
/**
|
|
41
66
|
* Reads persisted state, or null when this session has never persisted any loop.
|
|
42
67
|
* Throws {@link InvalidLoopStoreError} / {@link UnsupportedLoopStoreVersionError} on
|
|
43
68
|
* unusable state so callers fail closed rather than resetting a user's schedule.
|
|
44
69
|
*/
|
|
45
70
|
export async function readLoopState(ref) {
|
|
46
|
-
let raw;
|
|
47
71
|
try {
|
|
48
|
-
|
|
72
|
+
return await storeFor(ref).read();
|
|
49
73
|
}
|
|
50
74
|
catch (error) {
|
|
51
|
-
|
|
52
|
-
return null;
|
|
53
|
-
throw error;
|
|
75
|
+
remapLoopStoreError(error);
|
|
54
76
|
}
|
|
55
|
-
const state = parseLoopState(raw, ref);
|
|
56
|
-
snapshots.set(loopStateFilePath(ref), state);
|
|
57
|
-
return state;
|
|
58
77
|
}
|
|
59
78
|
/** Like {@link readLoopState}, but returns an empty state when nothing is persisted. */
|
|
60
79
|
export async function loadLoopState(ref) {
|
|
@@ -65,88 +84,38 @@ export async function loadLoopState(ref) {
|
|
|
65
84
|
* (status line, tick attribution). Undefined until the store has been touched.
|
|
66
85
|
*/
|
|
67
86
|
export function snapshotLoopState(ref) {
|
|
68
|
-
return
|
|
87
|
+
return storeFor(ref).snapshot();
|
|
69
88
|
}
|
|
70
89
|
/**
|
|
71
90
|
* Serializes read-modify-write cycles through a per-file promise tail so command,
|
|
72
91
|
* timer, tool, and lifecycle writes cannot interleave and lose an update.
|
|
73
92
|
*/
|
|
74
93
|
export function mutateLoopState(ref, mutation) {
|
|
75
|
-
return
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
await writeLoopState(ref, next);
|
|
79
|
-
return next;
|
|
80
|
-
});
|
|
94
|
+
return storeFor(ref)
|
|
95
|
+
.mutate((current) => mutation(current ?? emptyLoopState(ref.sessionId)))
|
|
96
|
+
.catch((error) => remapLoopStoreError(error));
|
|
81
97
|
}
|
|
82
98
|
/** Persists a complete state atomically. Prefer {@link mutateLoopState} for updates. */
|
|
83
99
|
export async function writeLoopState(ref, state) {
|
|
84
|
-
|
|
85
|
-
await mkdir(dirname(filePath), { recursive: true });
|
|
86
|
-
await writeAtomic(filePath, `${JSON.stringify(state, null, 2)}\n`);
|
|
87
|
-
snapshots.set(filePath, state);
|
|
100
|
+
await storeFor(ref).write(state);
|
|
88
101
|
}
|
|
89
102
|
/** Drops cached snapshots; used when a session ends or tests reset global state. */
|
|
90
103
|
export function clearLoopStateSnapshot(ref) {
|
|
91
|
-
|
|
92
|
-
}
|
|
93
|
-
function enqueue(ref, operation) {
|
|
94
|
-
const key = loopStateFilePath(ref);
|
|
95
|
-
const previous = mutationTails.get(key) ?? Promise.resolve();
|
|
96
|
-
const run = previous.then(operation);
|
|
97
|
-
const tail = run.then(() => undefined, () => undefined);
|
|
98
|
-
mutationTails.set(key, tail);
|
|
99
|
-
void tail.then(() => {
|
|
100
|
-
if (mutationTails.get(key) === tail)
|
|
101
|
-
mutationTails.delete(key);
|
|
102
|
-
});
|
|
103
|
-
return run;
|
|
104
|
+
storeFor(ref).clear();
|
|
104
105
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
try {
|
|
108
|
-
await writeFile(tempPath, contents, { encoding: "utf8", mode: 0o600 });
|
|
109
|
-
await rename(tempPath, filePath);
|
|
110
|
-
}
|
|
111
|
-
catch (error) {
|
|
112
|
-
try {
|
|
113
|
-
await rm(tempPath, { force: true });
|
|
114
|
-
}
|
|
115
|
-
catch (cleanupError) {
|
|
116
|
-
throw new AggregateError([error, cleanupError], "loop store write failed and its temporary file could not be removed");
|
|
117
|
-
}
|
|
118
|
-
throw error;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
function parseLoopState(raw, ref) {
|
|
122
|
-
let parsed;
|
|
123
|
-
try {
|
|
124
|
-
parsed = JSON.parse(raw);
|
|
125
|
-
}
|
|
126
|
-
catch (error) {
|
|
127
|
-
throw new InvalidLoopStoreError("loop store contains unparseable JSON", { cause: error });
|
|
128
|
-
}
|
|
129
|
-
if (!isRecord(parsed))
|
|
106
|
+
function parseLoopPayload(raw, ref) {
|
|
107
|
+
if (!isRecord(raw))
|
|
130
108
|
throw new InvalidLoopStoreError("loop store must be a JSON object");
|
|
131
|
-
if (
|
|
132
|
-
throw new UnsupportedLoopStoreVersionError(`unsupported loop store version: ${JSON.stringify(parsed.version)} (expected ${LOOP_STATE_VERSION})`);
|
|
133
|
-
}
|
|
134
|
-
if (typeof parsed.sessionId !== "string" || parsed.sessionId.length === 0) {
|
|
135
|
-
throw new InvalidLoopStoreError("loop store is missing a sessionId");
|
|
136
|
-
}
|
|
137
|
-
if (parsed.sessionId !== ref.sessionId) {
|
|
138
|
-
throw new InvalidLoopStoreError(`loop store sessionId ${parsed.sessionId} does not match the session it was loaded for`);
|
|
139
|
-
}
|
|
140
|
-
if (!isEpochMs(parsed.updatedAt))
|
|
109
|
+
if (!isEpochMs(raw.updatedAt))
|
|
141
110
|
throw new InvalidLoopStoreError("loop store has an invalid updatedAt");
|
|
142
|
-
if (!isRecord(
|
|
111
|
+
if (!isRecord(raw.entries))
|
|
143
112
|
throw new InvalidLoopStoreError("loop store entries must be an object");
|
|
144
113
|
const entries = {};
|
|
145
|
-
for (const [id, value] of Object.entries(
|
|
114
|
+
for (const [id, value] of Object.entries(raw.entries)) {
|
|
146
115
|
const entry = parseEntry(id, value);
|
|
147
116
|
entries[id] = entry;
|
|
148
117
|
}
|
|
149
|
-
const activeDynamicId =
|
|
118
|
+
const activeDynamicId = raw.activeDynamicId;
|
|
150
119
|
if (activeDynamicId !== null) {
|
|
151
120
|
if (typeof activeDynamicId !== "string") {
|
|
152
121
|
throw new InvalidLoopStoreError("loop store activeDynamicId must be a string or null");
|
|
@@ -158,10 +127,10 @@ function parseLoopState(raw, ref) {
|
|
|
158
127
|
}
|
|
159
128
|
return {
|
|
160
129
|
version: LOOP_STATE_VERSION,
|
|
161
|
-
sessionId:
|
|
130
|
+
sessionId: ref.sessionId,
|
|
162
131
|
entries,
|
|
163
132
|
activeDynamicId,
|
|
164
|
-
updatedAt:
|
|
133
|
+
updatedAt: raw.updatedAt,
|
|
165
134
|
};
|
|
166
135
|
}
|
|
167
136
|
function parseEntry(id, value) {
|
|
@@ -418,7 +387,4 @@ function isRequestedIntervalUnit(value) {
|
|
|
418
387
|
function isEffectiveIntervalUnit(value) {
|
|
419
388
|
return EFFECTIVE_INTERVAL_UNITS.includes(value);
|
|
420
389
|
}
|
|
421
|
-
function isFileSystemError(error, code) {
|
|
422
|
-
return error instanceof Error && "code" in error && error.code === code;
|
|
423
|
-
}
|
|
424
390
|
//# sourceMappingURL=store.js.map
|