@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.
Files changed (115) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/dist/core/auth-storage.d.ts.map +1 -1
  3. package/dist/core/auth-storage.js +31 -41
  4. package/dist/core/auth-storage.js.map +1 -1
  5. package/dist/core/credential-pool/state-store.d.ts +1 -0
  6. package/dist/core/credential-pool/state-store.d.ts.map +1 -1
  7. package/dist/core/credential-pool/state-store.js +22 -2
  8. package/dist/core/credential-pool/state-store.js.map +1 -1
  9. package/dist/core/extensions/builtin/goal/store.d.ts.map +1 -1
  10. package/dist/core/extensions/builtin/goal/store.js +8 -20
  11. package/dist/core/extensions/builtin/goal/store.js.map +1 -1
  12. package/dist/core/extensions/builtin/hooks/trust-storage.d.ts.map +1 -1
  13. package/dist/core/extensions/builtin/hooks/trust-storage.js +3 -1
  14. package/dist/core/extensions/builtin/hooks/trust-storage.js.map +1 -1
  15. package/dist/core/extensions/builtin/imagegen/auth.d.ts.map +1 -1
  16. package/dist/core/extensions/builtin/imagegen/auth.js +10 -2
  17. package/dist/core/extensions/builtin/imagegen/auth.js.map +1 -1
  18. package/dist/core/extensions/builtin/loop/store.d.ts +8 -6
  19. package/dist/core/extensions/builtin/loop/store.d.ts.map +1 -1
  20. package/dist/core/extensions/builtin/loop/store.js +54 -88
  21. package/dist/core/extensions/builtin/loop/store.js.map +1 -1
  22. package/dist/core/extensions/builtin/monitor-state-event.d.ts +2 -0
  23. package/dist/core/extensions/builtin/monitor-state-event.d.ts.map +1 -1
  24. package/dist/core/extensions/builtin/monitor-state-event.js.map +1 -1
  25. package/dist/core/extensions/builtin/terminal/extension.d.ts.map +1 -1
  26. package/dist/core/extensions/builtin/terminal/extension.js +181 -3
  27. package/dist/core/extensions/builtin/terminal/extension.js.map +1 -1
  28. package/dist/core/extensions/builtin/terminal/manager.d.ts +10 -0
  29. package/dist/core/extensions/builtin/terminal/manager.d.ts.map +1 -1
  30. package/dist/core/extensions/builtin/terminal/manager.js +21 -0
  31. package/dist/core/extensions/builtin/terminal/manager.js.map +1 -1
  32. package/dist/core/extensions/builtin/terminal/manifest-lease.d.ts +24 -0
  33. package/dist/core/extensions/builtin/terminal/manifest-lease.d.ts.map +1 -0
  34. package/dist/core/extensions/builtin/terminal/manifest-lease.js +105 -0
  35. package/dist/core/extensions/builtin/terminal/manifest-lease.js.map +1 -0
  36. package/dist/core/extensions/builtin/terminal/monitor-registry.d.ts +13 -2
  37. package/dist/core/extensions/builtin/terminal/monitor-registry.d.ts.map +1 -1
  38. package/dist/core/extensions/builtin/terminal/monitor-registry.js +24 -2
  39. package/dist/core/extensions/builtin/terminal/monitor-registry.js.map +1 -1
  40. package/dist/core/extensions/builtin/terminal/restore.d.ts +43 -0
  41. package/dist/core/extensions/builtin/terminal/restore.d.ts.map +1 -0
  42. package/dist/core/extensions/builtin/terminal/restore.js +149 -0
  43. package/dist/core/extensions/builtin/terminal/restore.js.map +1 -0
  44. package/dist/core/extensions/builtin/terminal/terminal-manifest.d.ts +110 -0
  45. package/dist/core/extensions/builtin/terminal/terminal-manifest.d.ts.map +1 -0
  46. package/dist/core/extensions/builtin/terminal/terminal-manifest.js +158 -0
  47. package/dist/core/extensions/builtin/terminal/terminal-manifest.js.map +1 -0
  48. package/dist/core/extensions/builtin/terminal/tools/bash-input.d.ts.map +1 -1
  49. package/dist/core/extensions/builtin/terminal/tools/bash-input.js +3 -2
  50. package/dist/core/extensions/builtin/terminal/tools/bash-input.js.map +1 -1
  51. package/dist/core/extensions/builtin/terminal/tools/bash-output.d.ts.map +1 -1
  52. package/dist/core/extensions/builtin/terminal/tools/bash-output.js +5 -4
  53. package/dist/core/extensions/builtin/terminal/tools/bash-output.js.map +1 -1
  54. package/dist/core/extensions/builtin/terminal/tools/bash-resize.d.ts.map +1 -1
  55. package/dist/core/extensions/builtin/terminal/tools/bash-resize.js +3 -2
  56. package/dist/core/extensions/builtin/terminal/tools/bash-resize.js.map +1 -1
  57. package/dist/core/extensions/builtin/terminal/tools/context.d.ts +8 -0
  58. package/dist/core/extensions/builtin/terminal/tools/context.d.ts.map +1 -1
  59. package/dist/core/extensions/builtin/terminal/tools/context.js +10 -0
  60. package/dist/core/extensions/builtin/terminal/tools/context.js.map +1 -1
  61. package/dist/core/extensions/builtin/terminal/tools/kill-bash.d.ts.map +1 -1
  62. package/dist/core/extensions/builtin/terminal/tools/kill-bash.js +5 -4
  63. package/dist/core/extensions/builtin/terminal/tools/kill-bash.js.map +1 -1
  64. package/dist/core/extensions/builtin/terminal/tools/monitor.d.ts +4 -1
  65. package/dist/core/extensions/builtin/terminal/tools/monitor.d.ts.map +1 -1
  66. package/dist/core/extensions/builtin/terminal/tools/monitor.js +58 -14
  67. package/dist/core/extensions/builtin/terminal/tools/monitor.js.map +1 -1
  68. package/dist/core/extensions/builtin/tool-search/index.d.ts.map +1 -1
  69. package/dist/core/extensions/builtin/tool-search/index.js +1 -1
  70. package/dist/core/extensions/builtin/tool-search/index.js.map +1 -1
  71. package/dist/core/extensions/builtin/tool-search/native-search.d.ts +9 -4
  72. package/dist/core/extensions/builtin/tool-search/native-search.d.ts.map +1 -1
  73. package/dist/core/extensions/builtin/tool-search/native-search.js +11 -6
  74. package/dist/core/extensions/builtin/tool-search/native-search.js.map +1 -1
  75. package/dist/core/extensions/builtin/tool-search/native-support.d.ts +13 -0
  76. package/dist/core/extensions/builtin/tool-search/native-support.d.ts.map +1 -0
  77. package/dist/core/extensions/builtin/tool-search/native-support.js +45 -0
  78. package/dist/core/extensions/builtin/tool-search/native-support.js.map +1 -0
  79. package/dist/core/lockfile-policy.d.ts +11 -0
  80. package/dist/core/lockfile-policy.d.ts.map +1 -1
  81. package/dist/core/lockfile-policy.js +19 -0
  82. package/dist/core/lockfile-policy.js.map +1 -1
  83. package/dist/core/session-sidecar-store.d.ts +62 -0
  84. package/dist/core/session-sidecar-store.d.ts.map +1 -0
  85. package/dist/core/session-sidecar-store.js +149 -0
  86. package/dist/core/session-sidecar-store.js.map +1 -0
  87. package/dist/core/settings-manager.d.ts.map +1 -1
  88. package/dist/core/settings-manager.js +11 -15
  89. package/dist/core/settings-manager.js.map +1 -1
  90. package/dist/modes/app-server/daemon/process.d.ts +33 -3
  91. package/dist/modes/app-server/daemon/process.d.ts.map +1 -1
  92. package/dist/modes/app-server/daemon/process.js +57 -6
  93. package/dist/modes/app-server/daemon/process.js.map +1 -1
  94. package/dist/modes/app-server/daemon.js +9 -1
  95. package/dist/modes/app-server/daemon.js.map +1 -1
  96. package/dist/modes/rpc/host-ensure.d.ts +2 -0
  97. package/dist/modes/rpc/host-ensure.d.ts.map +1 -1
  98. package/dist/modes/rpc/host-ensure.js +77 -18
  99. package/dist/modes/rpc/host-ensure.js.map +1 -1
  100. package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +14 -0
  101. package/node_modules/@code-yeongyu/senpi-codemode/package.json +5 -5
  102. package/node_modules/@code-yeongyu/senpi-codemode/src/prompt/eval-prompt.ts +1 -1
  103. package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
  104. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.js +4 -4
  105. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.js.map +1 -1
  106. package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
  107. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
  108. package/node_modules/@earendil-works/pi-ai/dist/utils/retry.d.ts.map +1 -1
  109. package/node_modules/@earendil-works/pi-ai/dist/utils/retry.js +3 -0
  110. package/node_modules/@earendil-works/pi-ai/dist/utils/retry.js.map +1 -1
  111. package/node_modules/@earendil-works/pi-ai/package.json +2 -2
  112. package/node_modules/@earendil-works/pi-pty/package.json +1 -1
  113. package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
  114. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  115. 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 enqueueGoalMutation(ref, () => writeGoalFile(ref, goal));
23
+ await serializeByKey(goalFilePath(ref), () => writeGoalFile(ref, goal));
36
24
  }
37
25
  export async function createGoal(ref, objective, tokenBudget) {
38
- return enqueueGoalMutation(ref, async () => {
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 enqueueGoalMutation(ref, async () => {
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 enqueueGoalMutation(ref, async () => {
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 enqueueGoalMutation(ref, async () => {
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 enqueueGoalMutation(ref, async () => {
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 enqueueGoalMutation(ref, async () => {
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;IAqBlD,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"}
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
- if (errorCode(error) === "ELOCKED") {
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;AAiHpC,wBAAsB,mBAAmB,CAAC,MAAM,SAAS,iBAAiB,EACzE,IAAI,EAAE,uBAAuB,CAAC,MAAM,CAAC,GACnC,OAAO,CAAC,sBAAsB,CAAC,CAoCjC"}
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":"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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\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
- * Mirrors the goal store's persistence discipline (temp file + rename, per-file mutation
5
- * serialization, strict versioned parsing) because scheduler state must never be half
6
- * written or silently reset: a corrupt file arms nothing and surfaces a typed error so
7
- * the extension can end affected loops with `error` instead of guessing.
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 Error {
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 Error {
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;;;;;;;;;;;GAWG;AAKH,OAAO,EAqBN,KAAK,SAAS,EACd,KAAK,YAAY,EAQjB,MAAM,YAAY,CAAC;AAEpB,qBAAa,qBAAsB,SAAQ,KAAK;gBACnC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAI1D;AAED,qBAAa,gCAAiC,SAAQ,KAAK;gBAC9C,OAAO,EAAE,MAAM;CAI3B;AAED,oFAAoF;AACpF,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,EAAE,SAAS,KAAK,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;AAKvF,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;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAWhF;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,CAOlG;AAED,wFAAwF;AACxF,wBAAsB,cAAc,CAAC,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAKvF;AAED,oFAAoF;AACpF,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,YAAY,GAAG,IAAI,CAE9D"}
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
- * Mirrors the goal store's persistence discipline (temp file + rename, per-file mutation
5
- * serialization, strict versioned parsing) because scheduler state must never be half
6
- * written or silently reset: a corrupt file arms nothing and surfaces a typed error so
7
- * the extension can end affected loops with `error` instead of guessing.
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 { randomUUID } from "node:crypto";
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 Error {
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 Error {
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 mutationTails = new Map();
30
- const snapshots = new Map();
28
+ const stores = new Map();
31
29
  export function encodedSessionId(ref) {
32
- return encodeURIComponent(ref.sessionId);
30
+ return encodeSidecarSessionId(ref.sessionId);
33
31
  }
34
32
  export function loopStateFilePath(ref) {
35
- return join(ref.baseDir, `${encodedSessionId(ref)}.json`);
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
- raw = await readFile(loopStateFilePath(ref), "utf8");
72
+ return await storeFor(ref).read();
49
73
  }
50
74
  catch (error) {
51
- if (isFileSystemError(error, "ENOENT"))
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 snapshots.get(loopStateFilePath(ref));
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 enqueue(ref, async () => {
76
- const current = await loadLoopState(ref);
77
- const next = await mutation(current);
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
- const filePath = loopStateFilePath(ref);
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
- snapshots.delete(loopStateFilePath(ref));
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
- async function writeAtomic(filePath, contents) {
106
- const tempPath = join(dirname(filePath), `.loop-${randomUUID()}.tmp`);
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 (parsed.version !== LOOP_STATE_VERSION) {
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(parsed.entries))
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(parsed.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 = parsed.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: parsed.sessionId,
130
+ sessionId: ref.sessionId,
162
131
  entries,
163
132
  activeDynamicId,
164
- updatedAt: parsed.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