@agimon-ai/doompi-goal 0.0.1-alpha.0

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 (180) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +25 -0
  3. package/dist/_virtual/_rolldown/runtime.cjs +1 -0
  4. package/dist/adapters/node/historyLock.cjs +2 -0
  5. package/dist/adapters/node/historyLock.cjs.map +1 -0
  6. package/dist/adapters/node/historyLock.mjs +2 -0
  7. package/dist/adapters/node/historyLock.mjs.map +1 -0
  8. package/dist/adapters/node/historyStore.cjs +2 -0
  9. package/dist/adapters/node/historyStore.cjs.map +1 -0
  10. package/dist/adapters/node/historyStore.mjs +2 -0
  11. package/dist/adapters/node/historyStore.mjs.map +1 -0
  12. package/dist/adapters/node/repositoryIdentity.cjs +2 -0
  13. package/dist/adapters/node/repositoryIdentity.cjs.map +1 -0
  14. package/dist/adapters/node/repositoryIdentity.mjs +2 -0
  15. package/dist/adapters/node/repositoryIdentity.mjs.map +1 -0
  16. package/dist/adapters/pi/goalManager.cjs +2 -0
  17. package/dist/adapters/pi/goalManager.cjs.map +1 -0
  18. package/dist/adapters/pi/goalManager.d.cts +86 -0
  19. package/dist/adapters/pi/goalManager.d.cts.map +1 -0
  20. package/dist/adapters/pi/goalManager.d.mts +86 -0
  21. package/dist/adapters/pi/goalManager.d.mts.map +1 -0
  22. package/dist/adapters/pi/goalManager.mjs +2 -0
  23. package/dist/adapters/pi/goalManager.mjs.map +1 -0
  24. package/dist/adapters/pi/toolVisibility.cjs +2 -0
  25. package/dist/adapters/pi/toolVisibility.cjs.map +1 -0
  26. package/dist/adapters/pi/toolVisibility.mjs +2 -0
  27. package/dist/adapters/pi/toolVisibility.mjs.map +1 -0
  28. package/dist/container/index.cjs +2 -0
  29. package/dist/container/index.cjs.map +1 -0
  30. package/dist/container/index.mjs +2 -0
  31. package/dist/container/index.mjs.map +1 -0
  32. package/dist/extensions/doom.cjs +2 -0
  33. package/dist/extensions/doom.cjs.map +1 -0
  34. package/dist/extensions/doom.d.cts +6 -0
  35. package/dist/extensions/doom.d.cts.map +1 -0
  36. package/dist/extensions/doom.d.mts +6 -0
  37. package/dist/extensions/doom.d.mts.map +1 -0
  38. package/dist/extensions/doom.mjs +2 -0
  39. package/dist/extensions/doom.mjs.map +1 -0
  40. package/dist/extensions/pi.cjs +2 -0
  41. package/dist/extensions/pi.cjs.map +1 -0
  42. package/dist/extensions/pi.d.cts +7 -0
  43. package/dist/extensions/pi.d.cts.map +1 -0
  44. package/dist/extensions/pi.d.mts +7 -0
  45. package/dist/extensions/pi.d.mts.map +1 -0
  46. package/dist/extensions/pi.mjs +2 -0
  47. package/dist/extensions/pi.mjs.map +1 -0
  48. package/dist/extensions/runtimeActivation.cjs +2 -0
  49. package/dist/extensions/runtimeActivation.cjs.map +1 -0
  50. package/dist/extensions/runtimeActivation.d.cts +14 -0
  51. package/dist/extensions/runtimeActivation.d.cts.map +1 -0
  52. package/dist/extensions/runtimeActivation.d.mts +14 -0
  53. package/dist/extensions/runtimeActivation.d.mts.map +1 -0
  54. package/dist/extensions/runtimeActivation.mjs +2 -0
  55. package/dist/extensions/runtimeActivation.mjs.map +1 -0
  56. package/dist/index.cjs +1 -0
  57. package/dist/index.d.cts +17 -0
  58. package/dist/index.d.mts +17 -0
  59. package/dist/index.mjs +1 -0
  60. package/dist/services/extensionService.cjs +2 -0
  61. package/dist/services/extensionService.cjs.map +1 -0
  62. package/dist/services/extensionService.d.cts +8 -0
  63. package/dist/services/extensionService.d.cts.map +1 -0
  64. package/dist/services/extensionService.d.mts +8 -0
  65. package/dist/services/extensionService.d.mts.map +1 -0
  66. package/dist/services/extensionService.mjs +2 -0
  67. package/dist/services/extensionService.mjs.map +1 -0
  68. package/dist/services/goal/accounting.cjs +2 -0
  69. package/dist/services/goal/accounting.cjs.map +1 -0
  70. package/dist/services/goal/accounting.d.cts +34 -0
  71. package/dist/services/goal/accounting.d.cts.map +1 -0
  72. package/dist/services/goal/accounting.d.mts +34 -0
  73. package/dist/services/goal/accounting.d.mts.map +1 -0
  74. package/dist/services/goal/accounting.mjs +2 -0
  75. package/dist/services/goal/accounting.mjs.map +1 -0
  76. package/dist/services/goal/parser.cjs +2 -0
  77. package/dist/services/goal/parser.cjs.map +1 -0
  78. package/dist/services/goal/parser.d.cts +24 -0
  79. package/dist/services/goal/parser.d.cts.map +1 -0
  80. package/dist/services/goal/parser.d.mts +24 -0
  81. package/dist/services/goal/parser.d.mts.map +1 -0
  82. package/dist/services/goal/parser.mjs +2 -0
  83. package/dist/services/goal/parser.mjs.map +1 -0
  84. package/dist/services/goal/prompts.cjs +3 -0
  85. package/dist/services/goal/prompts.cjs.map +1 -0
  86. package/dist/services/goal/prompts.d.cts +23 -0
  87. package/dist/services/goal/prompts.d.cts.map +1 -0
  88. package/dist/services/goal/prompts.d.mts +23 -0
  89. package/dist/services/goal/prompts.d.mts.map +1 -0
  90. package/dist/services/goal/prompts.mjs +3 -0
  91. package/dist/services/goal/prompts.mjs.map +1 -0
  92. package/dist/services/goal/queue.cjs +2 -0
  93. package/dist/services/goal/queue.cjs.map +1 -0
  94. package/dist/services/goal/queue.d.cts +19 -0
  95. package/dist/services/goal/queue.d.cts.map +1 -0
  96. package/dist/services/goal/queue.d.mts +19 -0
  97. package/dist/services/goal/queue.d.mts.map +1 -0
  98. package/dist/services/goal/queue.mjs +2 -0
  99. package/dist/services/goal/queue.mjs.map +1 -0
  100. package/dist/services/goal/runtime.cjs +2 -0
  101. package/dist/services/goal/runtime.cjs.map +1 -0
  102. package/dist/services/goal/runtime.d.cts +31 -0
  103. package/dist/services/goal/runtime.d.cts.map +1 -0
  104. package/dist/services/goal/runtime.d.mts +31 -0
  105. package/dist/services/goal/runtime.d.mts.map +1 -0
  106. package/dist/services/goal/runtime.mjs +2 -0
  107. package/dist/services/goal/runtime.mjs.map +1 -0
  108. package/dist/services/goal/safety.cjs +3 -0
  109. package/dist/services/goal/safety.cjs.map +1 -0
  110. package/dist/services/goal/safety.d.cts +18 -0
  111. package/dist/services/goal/safety.d.cts.map +1 -0
  112. package/dist/services/goal/safety.d.mts +18 -0
  113. package/dist/services/goal/safety.d.mts.map +1 -0
  114. package/dist/services/goal/safety.mjs +3 -0
  115. package/dist/services/goal/safety.mjs.map +1 -0
  116. package/dist/services/goal/settings.cjs +2 -0
  117. package/dist/services/goal/settings.cjs.map +1 -0
  118. package/dist/services/goal/settings.d.cts +24 -0
  119. package/dist/services/goal/settings.d.cts.map +1 -0
  120. package/dist/services/goal/settings.d.mts +24 -0
  121. package/dist/services/goal/settings.d.mts.map +1 -0
  122. package/dist/services/goal/settings.mjs +2 -0
  123. package/dist/services/goal/settings.mjs.map +1 -0
  124. package/dist/services/goal/stateCodec.cjs +2 -0
  125. package/dist/services/goal/stateCodec.cjs.map +1 -0
  126. package/dist/services/goal/stateCodec.d.cts +22 -0
  127. package/dist/services/goal/stateCodec.d.cts.map +1 -0
  128. package/dist/services/goal/stateCodec.d.mts +22 -0
  129. package/dist/services/goal/stateCodec.d.mts.map +1 -0
  130. package/dist/services/goal/stateCodec.mjs +2 -0
  131. package/dist/services/goal/stateCodec.mjs.map +1 -0
  132. package/dist/services/goal/stateMachine.cjs +3 -0
  133. package/dist/services/goal/stateMachine.cjs.map +1 -0
  134. package/dist/services/goal/stateMachine.d.cts +25 -0
  135. package/dist/services/goal/stateMachine.d.cts.map +1 -0
  136. package/dist/services/goal/stateMachine.d.mts +25 -0
  137. package/dist/services/goal/stateMachine.d.mts.map +1 -0
  138. package/dist/services/goal/stateMachine.mjs +3 -0
  139. package/dist/services/goal/stateMachine.mjs.map +1 -0
  140. package/dist/services/goal/tools.cjs +2 -0
  141. package/dist/services/goal/tools.cjs.map +1 -0
  142. package/dist/services/goal/tools.d.cts +29 -0
  143. package/dist/services/goal/tools.d.cts.map +1 -0
  144. package/dist/services/goal/tools.d.mts +29 -0
  145. package/dist/services/goal/tools.d.mts.map +1 -0
  146. package/dist/services/goal/tools.mjs +2 -0
  147. package/dist/services/goal/tools.mjs.map +1 -0
  148. package/dist/services/history/historyService.cjs +2 -0
  149. package/dist/services/history/historyService.cjs.map +1 -0
  150. package/dist/services/history/historyService.mjs +2 -0
  151. package/dist/services/history/historyService.mjs.map +1 -0
  152. package/dist/tui/goalHistoryOverlay.cjs +3 -0
  153. package/dist/tui/goalHistoryOverlay.cjs.map +1 -0
  154. package/dist/tui/goalHistoryOverlay.mjs +3 -0
  155. package/dist/tui/goalHistoryOverlay.mjs.map +1 -0
  156. package/dist/types/extension.d.cts +17 -0
  157. package/dist/types/extension.d.cts.map +1 -0
  158. package/dist/types/extension.d.mts +17 -0
  159. package/dist/types/extension.d.mts.map +1 -0
  160. package/dist/types/goal.cjs +2 -0
  161. package/dist/types/goal.cjs.map +1 -0
  162. package/dist/types/goal.d.cts +57 -0
  163. package/dist/types/goal.d.cts.map +1 -0
  164. package/dist/types/goal.d.mts +57 -0
  165. package/dist/types/goal.d.mts.map +1 -0
  166. package/dist/types/goal.mjs +2 -0
  167. package/dist/types/goal.mjs.map +1 -0
  168. package/dist/types/history.d.cts +27 -0
  169. package/dist/types/history.d.cts.map +1 -0
  170. package/dist/types/history.d.mts +27 -0
  171. package/dist/types/history.d.mts.map +1 -0
  172. package/dist/types/ports.d.cts +19 -0
  173. package/dist/types/ports.d.cts.map +1 -0
  174. package/dist/types/ports.d.mts +19 -0
  175. package/dist/types/ports.d.mts.map +1 -0
  176. package/dist/types/settings.d.cts +6 -0
  177. package/dist/types/settings.d.cts.map +1 -0
  178. package/dist/types/settings.d.mts +6 -0
  179. package/dist/types/settings.d.mts.map +1 -0
  180. package/package.json +79 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Vuong Ngo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,25 @@
1
+ # @agimon-ai/doompi-goal
2
+
3
+ Goal execution minor mode for DoomPi
4
+
5
+ ## Development
6
+
7
+ ```bash
8
+ pnpm build
9
+ pnpm typecheck
10
+ pnpm test
11
+ pnpm lint
12
+ ```
13
+
14
+ The Pi extension entrypoint is `@agimon-ai/doompi-goal/extensions/pi`.
15
+
16
+ ## Doom leader keys
17
+
18
+ | Key | Action |
19
+ | --------- | --------------------------------------------------- |
20
+ | `SPC g g` | Show the current Goal and its status |
21
+ | `SPC g s` | Start a Goal by prefilling `/goal ` |
22
+ | `SPC g e` | End and archive the current Goal |
23
+ | `SPC g p` | Open Goal history; Enter restarts the selected Goal |
24
+
25
+ Completing or ending a Goal deactivates Goal mode, removes its tools, and stops adding Goal instructions to later system prompts. Completed Goals remain available only in history so they can be restarted explicitly.
@@ -0,0 +1 @@
1
+ var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,s)=>(s=n==null?{}:e(i(n)),o(r||!n||!n.__esModule||!a.call(n,`default`)?t(s,`default`,{value:n,enumerable:!0}):s,n));exports.__toESM=s;
@@ -0,0 +1,2 @@
1
+ const e=require("../../_virtual/_rolldown/runtime.cjs");let t=require("node:crypto"),n=require("node:fs");n=e.__toESM(n,1);let r=require("node:path");r=e.__toESM(r,1);function i(e,t){if(e===t)return!0;try{return process.kill(e,0),!0}catch(e){return e.code===`EPERM`}}function a(e){try{let t=JSON.parse(n.default.readFileSync(r.default.join(e,`owner.json`),`utf8`));if(!t||typeof t!=`object`)return null;let i=t;if(typeof i.token!=`string`||typeof i.pid!=`number`||!Number.isFinite(i.createdAt))return null;let a=i.token,o=i.pid,s=i.createdAt;return a===void 0||o===void 0||s===void 0?null:{token:a,pid:o,createdAt:s}}catch{return null}}function o(e){return new Promise(t=>setTimeout(t,e))}function s(e,t,r,i){let o=a(e);if(!o)try{return r-n.default.statSync(e).mtimeMs>=t}catch{return!1}return r-o.createdAt>=t||!i(o.pid)}function c(e,r,i,a){if(!s(e,r,i,a))return!1;let o=`${e}.stale-${process.pid}-${(0,t.randomUUID)()}`;try{return n.default.renameSync(e,o),n.default.rmSync(o,{recursive:!0,force:!0}),!0}catch{return!1}}async function l(e,a={}){let s=a.timeoutMs??1e4,l=a.staleMs??3e4,d=a.retryMs??25,f=a.now??Date.now,p=a.pidAlive??(e=>i(e,process.pid)),m=a.random??Math.random,h=f(),g=(0,t.randomUUID)();for(n.default.mkdirSync(r.default.dirname(e),{mode:448,recursive:!0});f()-h<=s;)try{return n.default.mkdirSync(e,{mode:448}),n.default.writeFileSync(r.default.join(e,`owner.json`),`${JSON.stringify({token:g,pid:process.pid,createdAt:f()})}\n`,{mode:384}),{path:e,token:g,release:()=>u(e,g)}}catch(t){if(t.code!==`EEXIST`)throw t;c(e,l,f(),p);let n=s-(f()-h);if(n<=0)break;await o(Math.min(n,d+Math.floor(m()*Math.max(1,d))))}throw Error(`Timed out acquiring Goal history lock '${e}'.`)}function u(e,t){a(e)?.token===t&&n.default.rmSync(e,{recursive:!0,force:!0})}async function d(e,t,n){let r=await l(e,n);try{return await t()}finally{r.release()}}exports.acquireHistoryLock=l,exports.releaseHistoryLock=u,exports.withHistoryLock=d;
2
+ //# sourceMappingURL=historyLock.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"historyLock.cjs","names":["fs","path","randomUUID"],"sources":["../../../src/adapters/node/historyLock.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nconst PRIVATE_FILE_MODE = 0o600;\nconst PRIVATE_DIRECTORY_MODE = 0o700;\nconst DEFAULT_TIMEOUT_MS = 10_000;\nconst DEFAULT_STALE_MS = 30_000;\nconst DEFAULT_RETRY_MS = 25;\n\nexport interface HistoryLockOptions {\n timeoutMs?: number;\n staleMs?: number;\n retryMs?: number;\n now?: () => number;\n pidAlive?: (pid: number) => boolean;\n random?: () => number;\n}\n\ninterface LockOwner {\n token: string;\n pid: number;\n createdAt: number;\n}\n\nexport interface HistoryLock {\n path: string;\n token: string;\n release(): void;\n}\n\nfunction isPidAlive(pid: number, currentPid: number): boolean {\n if (pid === currentPid) return true;\n try {\n process.kill(pid, 0);\n return true;\n } catch (error) {\n return (error as NodeJS.ErrnoException).code === 'EPERM';\n }\n}\n\nfunction readOwner(lockPath: string): LockOwner | null {\n try {\n const parsed: unknown = JSON.parse(fs.readFileSync(path.join(lockPath, 'owner.json'), 'utf8'));\n if (!parsed || typeof parsed !== 'object') return null;\n const owner = parsed as Partial<LockOwner>;\n if (typeof owner.token !== 'string' || typeof owner.pid !== 'number' || !Number.isFinite(owner.createdAt))\n return null;\n const token = owner.token;\n const pid = owner.pid;\n const createdAt = owner.createdAt;\n if (token === undefined || pid === undefined || createdAt === undefined) return null;\n return { token, pid, createdAt };\n } catch {\n return null;\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction isStale(lockPath: string, staleMs: number, now: number, pidAlive: (pid: number) => boolean): boolean {\n const owner = readOwner(lockPath);\n if (!owner) {\n try {\n const stat = fs.statSync(lockPath);\n return now - stat.mtimeMs >= staleMs;\n } catch {\n return false;\n }\n }\n return now - owner.createdAt >= staleMs || !pidAlive(owner.pid);\n}\n\nfunction removeStaleLock(lockPath: string, staleMs: number, now: number, pidAlive: (pid: number) => boolean): boolean {\n if (!isStale(lockPath, staleMs, now, pidAlive)) return false;\n const moved = `${lockPath}.stale-${process.pid}-${randomUUID()}`;\n try {\n fs.renameSync(lockPath, moved);\n fs.rmSync(moved, { recursive: true, force: true });\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function acquireHistoryLock(lockPath: string, options: HistoryLockOptions = {}): Promise<HistoryLock> {\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const staleMs = options.staleMs ?? DEFAULT_STALE_MS;\n const retryMs = options.retryMs ?? DEFAULT_RETRY_MS;\n const now = options.now ?? Date.now;\n const pidAlive = options.pidAlive ?? ((pid: number) => isPidAlive(pid, process.pid));\n const random = options.random ?? Math.random;\n const startedAt = now();\n const token = randomUUID();\n fs.mkdirSync(path.dirname(lockPath), { mode: PRIVATE_DIRECTORY_MODE, recursive: true });\n\n while (now() - startedAt <= timeoutMs) {\n try {\n fs.mkdirSync(lockPath, { mode: PRIVATE_DIRECTORY_MODE });\n fs.writeFileSync(\n path.join(lockPath, 'owner.json'),\n `${JSON.stringify({ token, pid: process.pid, createdAt: now() } satisfies LockOwner)}\\n`,\n { mode: PRIVATE_FILE_MODE },\n );\n return { path: lockPath, token, release: () => releaseHistoryLock(lockPath, token) };\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error;\n removeStaleLock(lockPath, staleMs, now(), pidAlive);\n const remaining = timeoutMs - (now() - startedAt);\n if (remaining <= 0) break;\n await sleep(Math.min(remaining, retryMs + Math.floor(random() * Math.max(1, retryMs))));\n }\n }\n throw new Error(`Timed out acquiring Goal history lock '${lockPath}'.`);\n}\n\nexport function releaseHistoryLock(lockPath: string, token: string): void {\n if (readOwner(lockPath)?.token !== token) return;\n fs.rmSync(lockPath, { recursive: true, force: true });\n}\n\nexport async function withHistoryLock<T>(\n lockPath: string,\n operation: () => T | Promise<T>,\n options?: HistoryLockOptions,\n): Promise<T> {\n const lock = await acquireHistoryLock(lockPath, options);\n try {\n return await operation();\n } finally {\n lock.release();\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import{randomUUID as e}from"node:crypto";import t from"node:fs";import n from"node:path";function r(e,t){if(e===t)return!0;try{return process.kill(e,0),!0}catch(e){return e.code===`EPERM`}}function i(e){try{let r=JSON.parse(t.readFileSync(n.join(e,`owner.json`),`utf8`));if(!r||typeof r!=`object`)return null;let i=r;if(typeof i.token!=`string`||typeof i.pid!=`number`||!Number.isFinite(i.createdAt))return null;let a=i.token,o=i.pid,s=i.createdAt;return a===void 0||o===void 0||s===void 0?null:{token:a,pid:o,createdAt:s}}catch{return null}}function a(e){return new Promise(t=>setTimeout(t,e))}function o(e,n,r,a){let o=i(e);if(!o)try{return r-t.statSync(e).mtimeMs>=n}catch{return!1}return r-o.createdAt>=n||!a(o.pid)}function s(n,r,i,a){if(!o(n,r,i,a))return!1;let s=`${n}.stale-${process.pid}-${e()}`;try{return t.renameSync(n,s),t.rmSync(s,{recursive:!0,force:!0}),!0}catch{return!1}}async function c(i,o={}){let c=o.timeoutMs??1e4,u=o.staleMs??3e4,d=o.retryMs??25,f=o.now??Date.now,p=o.pidAlive??(e=>r(e,process.pid)),m=o.random??Math.random,h=f(),g=e();for(t.mkdirSync(n.dirname(i),{mode:448,recursive:!0});f()-h<=c;)try{return t.mkdirSync(i,{mode:448}),t.writeFileSync(n.join(i,`owner.json`),`${JSON.stringify({token:g,pid:process.pid,createdAt:f()})}\n`,{mode:384}),{path:i,token:g,release:()=>l(i,g)}}catch(e){if(e.code!==`EEXIST`)throw e;s(i,u,f(),p);let t=c-(f()-h);if(t<=0)break;await a(Math.min(t,d+Math.floor(m()*Math.max(1,d))))}throw Error(`Timed out acquiring Goal history lock '${i}'.`)}function l(e,n){i(e)?.token===n&&t.rmSync(e,{recursive:!0,force:!0})}async function u(e,t,n){let r=await c(e,n);try{return await t()}finally{r.release()}}export{c as acquireHistoryLock,l as releaseHistoryLock,u as withHistoryLock};
2
+ //# sourceMappingURL=historyLock.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"historyLock.mjs","names":[],"sources":["../../../src/adapters/node/historyLock.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nconst PRIVATE_FILE_MODE = 0o600;\nconst PRIVATE_DIRECTORY_MODE = 0o700;\nconst DEFAULT_TIMEOUT_MS = 10_000;\nconst DEFAULT_STALE_MS = 30_000;\nconst DEFAULT_RETRY_MS = 25;\n\nexport interface HistoryLockOptions {\n timeoutMs?: number;\n staleMs?: number;\n retryMs?: number;\n now?: () => number;\n pidAlive?: (pid: number) => boolean;\n random?: () => number;\n}\n\ninterface LockOwner {\n token: string;\n pid: number;\n createdAt: number;\n}\n\nexport interface HistoryLock {\n path: string;\n token: string;\n release(): void;\n}\n\nfunction isPidAlive(pid: number, currentPid: number): boolean {\n if (pid === currentPid) return true;\n try {\n process.kill(pid, 0);\n return true;\n } catch (error) {\n return (error as NodeJS.ErrnoException).code === 'EPERM';\n }\n}\n\nfunction readOwner(lockPath: string): LockOwner | null {\n try {\n const parsed: unknown = JSON.parse(fs.readFileSync(path.join(lockPath, 'owner.json'), 'utf8'));\n if (!parsed || typeof parsed !== 'object') return null;\n const owner = parsed as Partial<LockOwner>;\n if (typeof owner.token !== 'string' || typeof owner.pid !== 'number' || !Number.isFinite(owner.createdAt))\n return null;\n const token = owner.token;\n const pid = owner.pid;\n const createdAt = owner.createdAt;\n if (token === undefined || pid === undefined || createdAt === undefined) return null;\n return { token, pid, createdAt };\n } catch {\n return null;\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction isStale(lockPath: string, staleMs: number, now: number, pidAlive: (pid: number) => boolean): boolean {\n const owner = readOwner(lockPath);\n if (!owner) {\n try {\n const stat = fs.statSync(lockPath);\n return now - stat.mtimeMs >= staleMs;\n } catch {\n return false;\n }\n }\n return now - owner.createdAt >= staleMs || !pidAlive(owner.pid);\n}\n\nfunction removeStaleLock(lockPath: string, staleMs: number, now: number, pidAlive: (pid: number) => boolean): boolean {\n if (!isStale(lockPath, staleMs, now, pidAlive)) return false;\n const moved = `${lockPath}.stale-${process.pid}-${randomUUID()}`;\n try {\n fs.renameSync(lockPath, moved);\n fs.rmSync(moved, { recursive: true, force: true });\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function acquireHistoryLock(lockPath: string, options: HistoryLockOptions = {}): Promise<HistoryLock> {\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const staleMs = options.staleMs ?? DEFAULT_STALE_MS;\n const retryMs = options.retryMs ?? DEFAULT_RETRY_MS;\n const now = options.now ?? Date.now;\n const pidAlive = options.pidAlive ?? ((pid: number) => isPidAlive(pid, process.pid));\n const random = options.random ?? Math.random;\n const startedAt = now();\n const token = randomUUID();\n fs.mkdirSync(path.dirname(lockPath), { mode: PRIVATE_DIRECTORY_MODE, recursive: true });\n\n while (now() - startedAt <= timeoutMs) {\n try {\n fs.mkdirSync(lockPath, { mode: PRIVATE_DIRECTORY_MODE });\n fs.writeFileSync(\n path.join(lockPath, 'owner.json'),\n `${JSON.stringify({ token, pid: process.pid, createdAt: now() } satisfies LockOwner)}\\n`,\n { mode: PRIVATE_FILE_MODE },\n );\n return { path: lockPath, token, release: () => releaseHistoryLock(lockPath, token) };\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error;\n removeStaleLock(lockPath, staleMs, now(), pidAlive);\n const remaining = timeoutMs - (now() - startedAt);\n if (remaining <= 0) break;\n await sleep(Math.min(remaining, retryMs + Math.floor(random() * Math.max(1, retryMs))));\n }\n }\n throw new Error(`Timed out acquiring Goal history lock '${lockPath}'.`);\n}\n\nexport function releaseHistoryLock(lockPath: string, token: string): void {\n if (readOwner(lockPath)?.token !== token) return;\n fs.rmSync(lockPath, { recursive: true, force: true });\n}\n\nexport async function withHistoryLock<T>(\n lockPath: string,\n operation: () => T | Promise<T>,\n options?: HistoryLockOptions,\n): Promise<T> {\n const lock = await acquireHistoryLock(lockPath, options);\n try {\n return await operation();\n } finally {\n lock.release();\n }\n}\n"],"mappings":"yFA+BA,SAAS,EAAW,EAAa,EAA6B,CAC5D,GAAI,IAAQ,EAAY,MAAO,GAC/B,GAAI,CAEF,OADA,QAAQ,KAAK,EAAK,CAAC,EACZ,EACT,OAAS,EAAO,CACd,OAAQ,EAAgC,OAAS,OACnD,CACF,CAEA,SAAS,EAAU,EAAoC,CACrD,GAAI,CACF,IAAM,EAAkB,KAAK,MAAM,EAAG,aAAa,EAAK,KAAK,EAAU,YAAY,EAAG,MAAM,CAAC,EAC7F,GAAI,CAAC,GAAU,OAAO,GAAW,SAAU,OAAO,KAClD,IAAM,EAAQ,EACd,GAAI,OAAO,EAAM,OAAU,UAAY,OAAO,EAAM,KAAQ,UAAY,CAAC,OAAO,SAAS,EAAM,SAAS,EACtG,OAAO,KACT,IAAM,EAAQ,EAAM,MACd,EAAM,EAAM,IACZ,EAAY,EAAM,UAExB,OADI,IAAU,IAAA,IAAa,IAAQ,IAAA,IAAa,IAAc,IAAA,GAAkB,KACzE,CAAE,QAAO,MAAK,WAAU,CACjC,MAAQ,CACN,OAAO,IACT,CACF,CAEA,SAAS,EAAM,EAA2B,CACxC,OAAO,IAAI,QAAS,GAAY,WAAW,EAAS,CAAE,CAAC,CACzD,CAEA,SAAS,EAAQ,EAAkB,EAAiB,EAAa,EAA6C,CAC5G,IAAM,EAAQ,EAAU,CAAQ,EAChC,GAAI,CAAC,EACH,GAAI,CAEF,OAAO,EADM,EAAG,SAAS,CACT,CAAC,CAAC,SAAW,CAC/B,MAAQ,CACN,MAAO,EACT,CAEF,OAAO,EAAM,EAAM,WAAa,GAAW,CAAC,EAAS,EAAM,GAAG,CAChE,CAEA,SAAS,EAAgB,EAAkB,EAAiB,EAAa,EAA6C,CACpH,GAAI,CAAC,EAAQ,EAAU,EAAS,EAAK,CAAQ,EAAG,MAAO,GACvD,IAAM,EAAQ,GAAG,EAAS,SAAS,QAAQ,IAAI,GAAG,EAAW,IAC7D,GAAI,CAGF,OAFA,EAAG,WAAW,EAAU,CAAK,EAC7B,EAAG,OAAO,EAAO,CAAE,UAAW,GAAM,MAAO,EAAK,CAAC,EAC1C,EACT,MAAQ,CACN,MAAO,EACT,CACF,CAEA,eAAsB,EAAmB,EAAkB,EAA8B,CAAC,EAAyB,CACjH,IAAM,EAAY,EAAQ,WAAa,IACjC,EAAU,EAAQ,SAAW,IAC7B,EAAU,EAAQ,SAAW,GAC7B,EAAM,EAAQ,KAAO,KAAK,IAC1B,EAAW,EAAQ,WAAc,GAAgB,EAAW,EAAK,QAAQ,GAAG,GAC5E,EAAS,EAAQ,QAAU,KAAK,OAChC,EAAY,EAAI,EAChB,EAAQ,EAAW,EAGzB,IAFA,EAAG,UAAU,EAAK,QAAQ,CAAQ,EAAG,CAAE,KAAM,IAAwB,UAAW,EAAK,CAAC,EAE/E,EAAI,EAAI,GAAa,GAC1B,GAAI,CAOF,OANA,EAAG,UAAU,EAAU,CAAE,KAAM,GAAuB,CAAC,EACvD,EAAG,cACD,EAAK,KAAK,EAAU,YAAY,EAChC,GAAG,KAAK,UAAU,CAAE,QAAO,IAAK,QAAQ,IAAK,UAAW,EAAI,CAAE,CAAqB,EAAE,IACrF,CAAE,KAAM,GAAkB,CAC5B,EACO,CAAE,KAAM,EAAU,QAAO,YAAe,EAAmB,EAAU,CAAK,CAAE,CACrF,OAAS,EAAO,CACd,GAAK,EAAgC,OAAS,SAAU,MAAM,EAC9D,EAAgB,EAAU,EAAS,EAAI,EAAG,CAAQ,EAClD,IAAM,EAAY,GAAa,EAAI,EAAI,GACvC,GAAI,GAAa,EAAG,MACpB,MAAM,EAAM,KAAK,IAAI,EAAW,EAAU,KAAK,MAAM,EAAO,EAAI,KAAK,IAAI,EAAG,CAAO,CAAC,CAAC,CAAC,CACxF,CAEF,MAAU,MAAM,0CAA0C,EAAS,GAAG,CACxE,CAEA,SAAgB,EAAmB,EAAkB,EAAqB,CACpE,EAAU,CAAQ,CAAC,EAAE,QAAU,GACnC,EAAG,OAAO,EAAU,CAAE,UAAW,GAAM,MAAO,EAAK,CAAC,CACtD,CAEA,eAAsB,EACpB,EACA,EACA,EACY,CACZ,IAAM,EAAO,MAAM,EAAmB,EAAU,CAAO,EACvD,GAAI,CACF,OAAO,MAAM,EAAU,CACzB,QAAU,CACR,EAAK,QAAQ,CACf,CACF"}
@@ -0,0 +1,2 @@
1
+ const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("./historyLock.cjs"),n=require("./repositoryIdentity.cjs");let r=require("@earendil-works/pi-coding-agent"),i=require("node:crypto"),a=require("node:fs");a=e.__toESM(a,1);let o=require("node:path");o=e.__toESM(o,1);const s=1048576,c=new Set([`active`,`queued`,`paused`,`blocked`,`usage_limited`,`budget_limited`,`complete`]);function l(e){return{version:1,repositoryIdentity:e.token,revision:0,entries:[],tombstones:[]}}function u(e){if(!e||typeof e!=`object`)return!1;let t=e;return typeof t.id==`string`&&typeof t.objective==`string`&&c.has(t.status)&&typeof t.archivedAt==`string`}function d(e){if(!e||typeof e!=`object`)return!1;let t=e;return typeof t.id==`string`&&typeof t.removedAt==`string`}function f(e,t){if(!e||typeof e!=`object`)throw Error(`Goal history must contain an object.`);let n=e,r=n.revision,i=n.entries,a=n.tombstones;if(n.version!==1||n.repositoryIdentity!==t.token||typeof r!=`number`||!Number.isInteger(r)||r<0||!Array.isArray(i)||!i.every(u)||!Array.isArray(a)||!a.every(d))throw Error(`Goal history document is malformed or belongs to another repository.`);if(r===void 0||i===void 0||a===void 0)throw Error(`Goal history document is incomplete.`);return{version:1,repositoryIdentity:t.token,revision:r,entries:[...i],tombstones:[...a]}}function p(e){return Buffer.byteLength(`${JSON.stringify(e,null,2)}\n`,`utf8`)}function m(e){return[...e].sort((e,t)=>t.archivedAt.localeCompare(e.archivedAt)||t.id.localeCompare(e.id))}function h(e,t){if(e.entries=m(e.entries),e.entries.length>100){let n=e.entries.slice(0,100),r=e.entries.find(e=>e.id===t);r&&!n.some(e=>e.id===t)&&(n[99]=r),e.entries=n}for(e.tombstones=[...e.tombstones].sort((e,t)=>t.removedAt.localeCompare(e.removedAt)||t.id.localeCompare(e.id)).slice(0,100);p(e)>s&&e.entries.length>0;){let n=e.entries.findIndex(e=>e.id!==t);if(n<0)break;e.entries.splice(n,1)}for(;p(e)>s&&e.tombstones.length>0;)e.tombstones.pop()}function g(e,t){let n=o.default.dirname(e);a.default.mkdirSync(n,{mode:448,recursive:!0});let r=`${e}.${process.pid}.${(0,i.randomUUID)()}.tmp`,s;try{s=a.default.openSync(r,`w`,384),a.default.writeFileSync(s,`${JSON.stringify(t,null,2)}\n`,`utf8`),a.default.fsyncSync(s),a.default.closeSync(s),s=void 0,a.default.chmodSync(r,384),a.default.renameSync(r,e);try{let e=a.default.openSync(n,`r`);a.default.fsyncSync(e),a.default.closeSync(e)}catch{}}catch(e){throw s!==void 0&&a.default.closeSync(s),a.default.rmSync(r,{force:!0}),e}}function _(e,t){if(!a.default.existsSync(e))return;a.default.mkdirSync(t,{mode:448,recursive:!0});let n=o.default.join(t,`${o.default.basename(e)}.${Date.now()}.${(0,i.randomUUID)()}.json`);try{a.default.renameSync(e,n)}catch{}}var v=class{identity;historyDirectory;filePath;lockPath;corruptDirectory;lockOptions;now;constructor(e,t={}){this.identity=n.resolveRepositoryIdentity(e,t);let i=t.agentDir??(0,r.getAgentDir)();this.historyDirectory=o.default.join(i,`goal-history`),this.filePath=o.default.join(this.historyDirectory,`${this.identity.hash}.json`),this.lockPath=`${this.filePath}.lock`,this.corruptDirectory=o.default.join(this.historyDirectory,`corrupt`),this.lockOptions=t.lock??{},this.now=t.now??(()=>new Date)}async readDocument(){return t.withHistoryLock(this.lockPath,()=>this.readDocumentUnlocked(),this.lockOptions)}async list(){return m((await this.readDocument()).entries)}async archive(e){if(!u(e))throw Error(`Goal history entries are malformed.`);return t.withHistoryLock(this.lockPath,()=>{let t=this.readDocumentUnlocked();if(t.tombstones.some(t=>t.id===e.id))throw Error(`Goal history entry '${e.id}' was explicitly removed.`);let n=t.entries.find(t=>t.id===e.id);if(n)return n;if(t.entries.push({...e}),t.revision+=1,h(t,e.id),p(t)>s)throw Error(`Goal history exceeds the 1 MiB storage limit.`);return g(this.filePath,t),e},this.lockOptions)}async remove(e){if(!e)throw Error(`Goal history removal requires an id.`);await t.withHistoryLock(this.lockPath,()=>{let t=this.readDocumentUnlocked();t.entries=t.entries.filter(t=>t.id!==e),t.tombstones.some(t=>t.id===e)||t.tombstones.push({id:e,removedAt:this.now().toISOString()}),t.revision+=1,h(t),g(this.filePath,t)},this.lockOptions)}readDocumentUnlocked(){if(!a.default.existsSync(this.filePath))return l(this.identity);try{return f(JSON.parse(a.default.readFileSync(this.filePath,`utf8`)),this.identity)}catch(e){_(this.filePath,this.corruptDirectory);let t=e instanceof Error?e.message:String(e);throw Error(`Goal history was quarantined: ${t}`,{cause:e})}}};function y(e,t){return new v(e,t)}exports.GoalHistoryStore=v,exports.createGoalHistoryStore=y;
2
+ //# sourceMappingURL=historyStore.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"historyStore.cjs","names":["path","randomUUID","fs","resolveRepositoryIdentity","getAgentDir","withHistoryLock"],"sources":["../../../src/adapters/node/historyStore.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { getAgentDir } from '@earendil-works/pi-coding-agent';\nimport type {\n GoalHistoryDocument,\n GoalHistoryEntry,\n GoalHistoryTombstone,\n RepositoryIdentity,\n} from '../../types/history.ts';\nimport { withHistoryLock, type HistoryLockOptions } from './historyLock.ts';\nimport { resolveRepositoryIdentity, type RepositoryIdentityOptions } from './repositoryIdentity.ts';\n\nconst HISTORY_DIRECTORY = 'goal-history';\nconst CORRUPT_DIRECTORY = 'corrupt';\nconst PRIVATE_FILE_MODE = 0o600;\nconst PRIVATE_DIRECTORY_MODE = 0o700;\nconst MAX_ENTRIES = 100;\nconst MAX_TOMBSTONES = 100;\nconst MAX_DOCUMENT_BYTES = 1024 * 1024;\nconst HISTORY_STATUSES = new Set<GoalHistoryEntry['status']>([\n 'active',\n 'queued',\n 'paused',\n 'blocked',\n 'usage_limited',\n 'budget_limited',\n 'complete',\n]);\n\nexport interface HistoryStoreOptions extends RepositoryIdentityOptions {\n agentDir?: string;\n lock?: HistoryLockOptions;\n now?: () => Date;\n}\n\nfunction emptyDocument(identity: RepositoryIdentity): GoalHistoryDocument {\n return { version: 1, repositoryIdentity: identity.token, revision: 0, entries: [], tombstones: [] };\n}\n\nfunction isEntry(value: unknown): value is GoalHistoryEntry {\n if (!value || typeof value !== 'object') return false;\n const entry = value as Partial<GoalHistoryEntry>;\n return (\n typeof entry.id === 'string' &&\n typeof entry.objective === 'string' &&\n HISTORY_STATUSES.has(entry.status as GoalHistoryEntry['status']) &&\n typeof entry.archivedAt === 'string'\n );\n}\n\nfunction isTombstone(value: unknown): value is GoalHistoryTombstone {\n if (!value || typeof value !== 'object') return false;\n const tombstone = value as Partial<GoalHistoryTombstone>;\n return typeof tombstone.id === 'string' && typeof tombstone.removedAt === 'string';\n}\n\nfunction decodeDocument(value: unknown, identity: RepositoryIdentity): GoalHistoryDocument {\n if (!value || typeof value !== 'object') throw new Error('Goal history must contain an object.');\n const candidate = value as Partial<GoalHistoryDocument>;\n const revision = candidate.revision;\n const entries = candidate.entries;\n const tombstones = candidate.tombstones;\n if (\n candidate.version !== 1 ||\n candidate.repositoryIdentity !== identity.token ||\n typeof revision !== 'number' ||\n !Number.isInteger(revision) ||\n revision < 0 ||\n !Array.isArray(entries) ||\n !entries.every(isEntry) ||\n !Array.isArray(tombstones) ||\n !tombstones.every(isTombstone)\n )\n throw new Error('Goal history document is malformed or belongs to another repository.');\n if (revision === undefined || entries === undefined || tombstones === undefined) {\n throw new Error('Goal history document is incomplete.');\n }\n return {\n version: 1,\n repositoryIdentity: identity.token,\n revision,\n entries: [...entries],\n tombstones: [...tombstones],\n };\n}\n\nfunction serializedSize(document: GoalHistoryDocument): number {\n return Buffer.byteLength(`${JSON.stringify(document, null, 2)}\\n`, 'utf8');\n}\n\nfunction sortNewestFirst(entries: GoalHistoryEntry[]): GoalHistoryEntry[] {\n return [...entries].sort(\n (left, right) => right.archivedAt.localeCompare(left.archivedAt) || right.id.localeCompare(left.id),\n );\n}\n\nfunction prune(document: GoalHistoryDocument, protectedId?: string): void {\n document.entries = sortNewestFirst(document.entries);\n if (document.entries.length > MAX_ENTRIES) {\n const retained = document.entries.slice(0, MAX_ENTRIES);\n const protectedEntry = document.entries.find((entry) => entry.id === protectedId);\n if (protectedEntry && !retained.some((entry) => entry.id === protectedId))\n retained[MAX_ENTRIES - 1] = protectedEntry;\n document.entries = retained;\n }\n document.tombstones = [...document.tombstones]\n .sort((left, right) => right.removedAt.localeCompare(left.removedAt) || right.id.localeCompare(left.id))\n .slice(0, MAX_TOMBSTONES);\n while (serializedSize(document) > MAX_DOCUMENT_BYTES && document.entries.length > 0) {\n const removable = document.entries.findIndex((entry) => entry.id !== protectedId);\n if (removable < 0) break;\n document.entries.splice(removable, 1);\n }\n while (serializedSize(document) > MAX_DOCUMENT_BYTES && document.tombstones.length > 0) document.tombstones.pop();\n}\n\nfunction writeAtomic(filePath: string, document: GoalHistoryDocument): void {\n const directory = path.dirname(filePath);\n fs.mkdirSync(directory, { mode: PRIVATE_DIRECTORY_MODE, recursive: true });\n const temporaryPath = `${filePath}.${process.pid}.${randomUUID()}.tmp`;\n let descriptor: number | undefined;\n try {\n descriptor = fs.openSync(temporaryPath, 'w', PRIVATE_FILE_MODE);\n fs.writeFileSync(descriptor, `${JSON.stringify(document, null, 2)}\\n`, 'utf8');\n fs.fsyncSync(descriptor);\n fs.closeSync(descriptor);\n descriptor = undefined;\n fs.chmodSync(temporaryPath, PRIVATE_FILE_MODE);\n fs.renameSync(temporaryPath, filePath);\n try {\n const directoryDescriptor = fs.openSync(directory, 'r');\n fs.fsyncSync(directoryDescriptor);\n fs.closeSync(directoryDescriptor);\n } catch {\n // Some filesystems do not support directory fsync.\n }\n } catch (error) {\n if (descriptor !== undefined) fs.closeSync(descriptor);\n fs.rmSync(temporaryPath, { force: true });\n throw error;\n }\n}\n\nfunction quarantine(filePath: string, corruptDirectory: string): void {\n if (!fs.existsSync(filePath)) return;\n fs.mkdirSync(corruptDirectory, { mode: PRIVATE_DIRECTORY_MODE, recursive: true });\n const destination = path.join(corruptDirectory, `${path.basename(filePath)}.${Date.now()}.${randomUUID()}.json`);\n try {\n fs.renameSync(filePath, destination);\n } catch {\n // A concurrent repair may already have moved the file.\n }\n}\n\nexport class GoalHistoryStore {\n readonly identity: RepositoryIdentity;\n readonly historyDirectory: string;\n readonly filePath: string;\n readonly lockPath: string;\n readonly corruptDirectory: string;\n private readonly lockOptions: HistoryLockOptions;\n private readonly now: () => Date;\n\n constructor(cwd: string, options: HistoryStoreOptions = {}) {\n this.identity = resolveRepositoryIdentity(cwd, options);\n const agentDirectory = options.agentDir ?? getAgentDir();\n this.historyDirectory = path.join(agentDirectory, HISTORY_DIRECTORY);\n this.filePath = path.join(this.historyDirectory, `${this.identity.hash}.json`);\n this.lockPath = `${this.filePath}.lock`;\n this.corruptDirectory = path.join(this.historyDirectory, CORRUPT_DIRECTORY);\n this.lockOptions = options.lock ?? {};\n this.now = options.now ?? (() => new Date());\n }\n\n async readDocument(): Promise<GoalHistoryDocument> {\n return withHistoryLock(this.lockPath, () => this.readDocumentUnlocked(), this.lockOptions);\n }\n\n async list(): Promise<GoalHistoryEntry[]> {\n return sortNewestFirst((await this.readDocument()).entries);\n }\n\n async archive(entry: GoalHistoryEntry): Promise<GoalHistoryEntry> {\n if (!isEntry(entry)) throw new Error('Goal history entries are malformed.');\n return withHistoryLock(\n this.lockPath,\n () => {\n const document = this.readDocumentUnlocked();\n if (document.tombstones.some((tombstone) => tombstone.id === entry.id))\n throw new Error(`Goal history entry '${entry.id}' was explicitly removed.`);\n const existing = document.entries.find((candidate) => candidate.id === entry.id);\n if (existing) return existing;\n document.entries.push({ ...entry });\n document.revision += 1;\n prune(document, entry.id);\n if (serializedSize(document) > MAX_DOCUMENT_BYTES)\n throw new Error('Goal history exceeds the 1 MiB storage limit.');\n writeAtomic(this.filePath, document);\n return entry;\n },\n this.lockOptions,\n );\n }\n\n async remove(id: string): Promise<void> {\n if (!id) throw new Error('Goal history removal requires an id.');\n await withHistoryLock(\n this.lockPath,\n () => {\n const document = this.readDocumentUnlocked();\n document.entries = document.entries.filter((entry) => entry.id !== id);\n if (!document.tombstones.some((tombstone) => tombstone.id === id))\n document.tombstones.push({ id, removedAt: this.now().toISOString() });\n document.revision += 1;\n prune(document);\n writeAtomic(this.filePath, document);\n },\n this.lockOptions,\n );\n }\n\n private readDocumentUnlocked(): GoalHistoryDocument {\n if (!fs.existsSync(this.filePath)) return emptyDocument(this.identity);\n try {\n return decodeDocument(JSON.parse(fs.readFileSync(this.filePath, 'utf8')), this.identity);\n } catch (error) {\n quarantine(this.filePath, this.corruptDirectory);\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(`Goal history was quarantined: ${message}`, { cause: error });\n }\n }\n}\n\nexport function createGoalHistoryStore(cwd: string, options?: HistoryStoreOptions): GoalHistoryStore {\n return new GoalHistoryStore(cwd, options);\n}\n\nexport const GOAL_HISTORY_MAX_ENTRIES = MAX_ENTRIES;\nexport const GOAL_HISTORY_MAX_TOMBSTONES = MAX_TOMBSTONES;\nexport const GOAL_HISTORY_MAX_BYTES = MAX_DOCUMENT_BYTES;\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import{withHistoryLock as e}from"./historyLock.mjs";import{resolveRepositoryIdentity as t}from"./repositoryIdentity.mjs";import{getAgentDir as n}from"@earendil-works/pi-coding-agent";import{randomUUID as r}from"node:crypto";import i from"node:fs";import a from"node:path";const o=1048576,s=new Set([`active`,`queued`,`paused`,`blocked`,`usage_limited`,`budget_limited`,`complete`]);function c(e){return{version:1,repositoryIdentity:e.token,revision:0,entries:[],tombstones:[]}}function l(e){if(!e||typeof e!=`object`)return!1;let t=e;return typeof t.id==`string`&&typeof t.objective==`string`&&s.has(t.status)&&typeof t.archivedAt==`string`}function u(e){if(!e||typeof e!=`object`)return!1;let t=e;return typeof t.id==`string`&&typeof t.removedAt==`string`}function d(e,t){if(!e||typeof e!=`object`)throw Error(`Goal history must contain an object.`);let n=e,r=n.revision,i=n.entries,a=n.tombstones;if(n.version!==1||n.repositoryIdentity!==t.token||typeof r!=`number`||!Number.isInteger(r)||r<0||!Array.isArray(i)||!i.every(l)||!Array.isArray(a)||!a.every(u))throw Error(`Goal history document is malformed or belongs to another repository.`);if(r===void 0||i===void 0||a===void 0)throw Error(`Goal history document is incomplete.`);return{version:1,repositoryIdentity:t.token,revision:r,entries:[...i],tombstones:[...a]}}function f(e){return Buffer.byteLength(`${JSON.stringify(e,null,2)}\n`,`utf8`)}function p(e){return[...e].sort((e,t)=>t.archivedAt.localeCompare(e.archivedAt)||t.id.localeCompare(e.id))}function m(e,t){if(e.entries=p(e.entries),e.entries.length>100){let n=e.entries.slice(0,100),r=e.entries.find(e=>e.id===t);r&&!n.some(e=>e.id===t)&&(n[99]=r),e.entries=n}for(e.tombstones=[...e.tombstones].sort((e,t)=>t.removedAt.localeCompare(e.removedAt)||t.id.localeCompare(e.id)).slice(0,100);f(e)>o&&e.entries.length>0;){let n=e.entries.findIndex(e=>e.id!==t);if(n<0)break;e.entries.splice(n,1)}for(;f(e)>o&&e.tombstones.length>0;)e.tombstones.pop()}function h(e,t){let n=a.dirname(e);i.mkdirSync(n,{mode:448,recursive:!0});let o=`${e}.${process.pid}.${r()}.tmp`,s;try{s=i.openSync(o,`w`,384),i.writeFileSync(s,`${JSON.stringify(t,null,2)}\n`,`utf8`),i.fsyncSync(s),i.closeSync(s),s=void 0,i.chmodSync(o,384),i.renameSync(o,e);try{let e=i.openSync(n,`r`);i.fsyncSync(e),i.closeSync(e)}catch{}}catch(e){throw s!==void 0&&i.closeSync(s),i.rmSync(o,{force:!0}),e}}function g(e,t){if(!i.existsSync(e))return;i.mkdirSync(t,{mode:448,recursive:!0});let n=a.join(t,`${a.basename(e)}.${Date.now()}.${r()}.json`);try{i.renameSync(e,n)}catch{}}var _=class{identity;historyDirectory;filePath;lockPath;corruptDirectory;lockOptions;now;constructor(e,r={}){this.identity=t(e,r);let i=r.agentDir??n();this.historyDirectory=a.join(i,`goal-history`),this.filePath=a.join(this.historyDirectory,`${this.identity.hash}.json`),this.lockPath=`${this.filePath}.lock`,this.corruptDirectory=a.join(this.historyDirectory,`corrupt`),this.lockOptions=r.lock??{},this.now=r.now??(()=>new Date)}async readDocument(){return e(this.lockPath,()=>this.readDocumentUnlocked(),this.lockOptions)}async list(){return p((await this.readDocument()).entries)}async archive(t){if(!l(t))throw Error(`Goal history entries are malformed.`);return e(this.lockPath,()=>{let e=this.readDocumentUnlocked();if(e.tombstones.some(e=>e.id===t.id))throw Error(`Goal history entry '${t.id}' was explicitly removed.`);let n=e.entries.find(e=>e.id===t.id);if(n)return n;if(e.entries.push({...t}),e.revision+=1,m(e,t.id),f(e)>o)throw Error(`Goal history exceeds the 1 MiB storage limit.`);return h(this.filePath,e),t},this.lockOptions)}async remove(t){if(!t)throw Error(`Goal history removal requires an id.`);await e(this.lockPath,()=>{let e=this.readDocumentUnlocked();e.entries=e.entries.filter(e=>e.id!==t),e.tombstones.some(e=>e.id===t)||e.tombstones.push({id:t,removedAt:this.now().toISOString()}),e.revision+=1,m(e),h(this.filePath,e)},this.lockOptions)}readDocumentUnlocked(){if(!i.existsSync(this.filePath))return c(this.identity);try{return d(JSON.parse(i.readFileSync(this.filePath,`utf8`)),this.identity)}catch(e){g(this.filePath,this.corruptDirectory);let t=e instanceof Error?e.message:String(e);throw Error(`Goal history was quarantined: ${t}`,{cause:e})}}};function v(e,t){return new _(e,t)}export{_ as GoalHistoryStore,v as createGoalHistoryStore};
2
+ //# sourceMappingURL=historyStore.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"historyStore.mjs","names":[],"sources":["../../../src/adapters/node/historyStore.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { getAgentDir } from '@earendil-works/pi-coding-agent';\nimport type {\n GoalHistoryDocument,\n GoalHistoryEntry,\n GoalHistoryTombstone,\n RepositoryIdentity,\n} from '../../types/history.ts';\nimport { withHistoryLock, type HistoryLockOptions } from './historyLock.ts';\nimport { resolveRepositoryIdentity, type RepositoryIdentityOptions } from './repositoryIdentity.ts';\n\nconst HISTORY_DIRECTORY = 'goal-history';\nconst CORRUPT_DIRECTORY = 'corrupt';\nconst PRIVATE_FILE_MODE = 0o600;\nconst PRIVATE_DIRECTORY_MODE = 0o700;\nconst MAX_ENTRIES = 100;\nconst MAX_TOMBSTONES = 100;\nconst MAX_DOCUMENT_BYTES = 1024 * 1024;\nconst HISTORY_STATUSES = new Set<GoalHistoryEntry['status']>([\n 'active',\n 'queued',\n 'paused',\n 'blocked',\n 'usage_limited',\n 'budget_limited',\n 'complete',\n]);\n\nexport interface HistoryStoreOptions extends RepositoryIdentityOptions {\n agentDir?: string;\n lock?: HistoryLockOptions;\n now?: () => Date;\n}\n\nfunction emptyDocument(identity: RepositoryIdentity): GoalHistoryDocument {\n return { version: 1, repositoryIdentity: identity.token, revision: 0, entries: [], tombstones: [] };\n}\n\nfunction isEntry(value: unknown): value is GoalHistoryEntry {\n if (!value || typeof value !== 'object') return false;\n const entry = value as Partial<GoalHistoryEntry>;\n return (\n typeof entry.id === 'string' &&\n typeof entry.objective === 'string' &&\n HISTORY_STATUSES.has(entry.status as GoalHistoryEntry['status']) &&\n typeof entry.archivedAt === 'string'\n );\n}\n\nfunction isTombstone(value: unknown): value is GoalHistoryTombstone {\n if (!value || typeof value !== 'object') return false;\n const tombstone = value as Partial<GoalHistoryTombstone>;\n return typeof tombstone.id === 'string' && typeof tombstone.removedAt === 'string';\n}\n\nfunction decodeDocument(value: unknown, identity: RepositoryIdentity): GoalHistoryDocument {\n if (!value || typeof value !== 'object') throw new Error('Goal history must contain an object.');\n const candidate = value as Partial<GoalHistoryDocument>;\n const revision = candidate.revision;\n const entries = candidate.entries;\n const tombstones = candidate.tombstones;\n if (\n candidate.version !== 1 ||\n candidate.repositoryIdentity !== identity.token ||\n typeof revision !== 'number' ||\n !Number.isInteger(revision) ||\n revision < 0 ||\n !Array.isArray(entries) ||\n !entries.every(isEntry) ||\n !Array.isArray(tombstones) ||\n !tombstones.every(isTombstone)\n )\n throw new Error('Goal history document is malformed or belongs to another repository.');\n if (revision === undefined || entries === undefined || tombstones === undefined) {\n throw new Error('Goal history document is incomplete.');\n }\n return {\n version: 1,\n repositoryIdentity: identity.token,\n revision,\n entries: [...entries],\n tombstones: [...tombstones],\n };\n}\n\nfunction serializedSize(document: GoalHistoryDocument): number {\n return Buffer.byteLength(`${JSON.stringify(document, null, 2)}\\n`, 'utf8');\n}\n\nfunction sortNewestFirst(entries: GoalHistoryEntry[]): GoalHistoryEntry[] {\n return [...entries].sort(\n (left, right) => right.archivedAt.localeCompare(left.archivedAt) || right.id.localeCompare(left.id),\n );\n}\n\nfunction prune(document: GoalHistoryDocument, protectedId?: string): void {\n document.entries = sortNewestFirst(document.entries);\n if (document.entries.length > MAX_ENTRIES) {\n const retained = document.entries.slice(0, MAX_ENTRIES);\n const protectedEntry = document.entries.find((entry) => entry.id === protectedId);\n if (protectedEntry && !retained.some((entry) => entry.id === protectedId))\n retained[MAX_ENTRIES - 1] = protectedEntry;\n document.entries = retained;\n }\n document.tombstones = [...document.tombstones]\n .sort((left, right) => right.removedAt.localeCompare(left.removedAt) || right.id.localeCompare(left.id))\n .slice(0, MAX_TOMBSTONES);\n while (serializedSize(document) > MAX_DOCUMENT_BYTES && document.entries.length > 0) {\n const removable = document.entries.findIndex((entry) => entry.id !== protectedId);\n if (removable < 0) break;\n document.entries.splice(removable, 1);\n }\n while (serializedSize(document) > MAX_DOCUMENT_BYTES && document.tombstones.length > 0) document.tombstones.pop();\n}\n\nfunction writeAtomic(filePath: string, document: GoalHistoryDocument): void {\n const directory = path.dirname(filePath);\n fs.mkdirSync(directory, { mode: PRIVATE_DIRECTORY_MODE, recursive: true });\n const temporaryPath = `${filePath}.${process.pid}.${randomUUID()}.tmp`;\n let descriptor: number | undefined;\n try {\n descriptor = fs.openSync(temporaryPath, 'w', PRIVATE_FILE_MODE);\n fs.writeFileSync(descriptor, `${JSON.stringify(document, null, 2)}\\n`, 'utf8');\n fs.fsyncSync(descriptor);\n fs.closeSync(descriptor);\n descriptor = undefined;\n fs.chmodSync(temporaryPath, PRIVATE_FILE_MODE);\n fs.renameSync(temporaryPath, filePath);\n try {\n const directoryDescriptor = fs.openSync(directory, 'r');\n fs.fsyncSync(directoryDescriptor);\n fs.closeSync(directoryDescriptor);\n } catch {\n // Some filesystems do not support directory fsync.\n }\n } catch (error) {\n if (descriptor !== undefined) fs.closeSync(descriptor);\n fs.rmSync(temporaryPath, { force: true });\n throw error;\n }\n}\n\nfunction quarantine(filePath: string, corruptDirectory: string): void {\n if (!fs.existsSync(filePath)) return;\n fs.mkdirSync(corruptDirectory, { mode: PRIVATE_DIRECTORY_MODE, recursive: true });\n const destination = path.join(corruptDirectory, `${path.basename(filePath)}.${Date.now()}.${randomUUID()}.json`);\n try {\n fs.renameSync(filePath, destination);\n } catch {\n // A concurrent repair may already have moved the file.\n }\n}\n\nexport class GoalHistoryStore {\n readonly identity: RepositoryIdentity;\n readonly historyDirectory: string;\n readonly filePath: string;\n readonly lockPath: string;\n readonly corruptDirectory: string;\n private readonly lockOptions: HistoryLockOptions;\n private readonly now: () => Date;\n\n constructor(cwd: string, options: HistoryStoreOptions = {}) {\n this.identity = resolveRepositoryIdentity(cwd, options);\n const agentDirectory = options.agentDir ?? getAgentDir();\n this.historyDirectory = path.join(agentDirectory, HISTORY_DIRECTORY);\n this.filePath = path.join(this.historyDirectory, `${this.identity.hash}.json`);\n this.lockPath = `${this.filePath}.lock`;\n this.corruptDirectory = path.join(this.historyDirectory, CORRUPT_DIRECTORY);\n this.lockOptions = options.lock ?? {};\n this.now = options.now ?? (() => new Date());\n }\n\n async readDocument(): Promise<GoalHistoryDocument> {\n return withHistoryLock(this.lockPath, () => this.readDocumentUnlocked(), this.lockOptions);\n }\n\n async list(): Promise<GoalHistoryEntry[]> {\n return sortNewestFirst((await this.readDocument()).entries);\n }\n\n async archive(entry: GoalHistoryEntry): Promise<GoalHistoryEntry> {\n if (!isEntry(entry)) throw new Error('Goal history entries are malformed.');\n return withHistoryLock(\n this.lockPath,\n () => {\n const document = this.readDocumentUnlocked();\n if (document.tombstones.some((tombstone) => tombstone.id === entry.id))\n throw new Error(`Goal history entry '${entry.id}' was explicitly removed.`);\n const existing = document.entries.find((candidate) => candidate.id === entry.id);\n if (existing) return existing;\n document.entries.push({ ...entry });\n document.revision += 1;\n prune(document, entry.id);\n if (serializedSize(document) > MAX_DOCUMENT_BYTES)\n throw new Error('Goal history exceeds the 1 MiB storage limit.');\n writeAtomic(this.filePath, document);\n return entry;\n },\n this.lockOptions,\n );\n }\n\n async remove(id: string): Promise<void> {\n if (!id) throw new Error('Goal history removal requires an id.');\n await withHistoryLock(\n this.lockPath,\n () => {\n const document = this.readDocumentUnlocked();\n document.entries = document.entries.filter((entry) => entry.id !== id);\n if (!document.tombstones.some((tombstone) => tombstone.id === id))\n document.tombstones.push({ id, removedAt: this.now().toISOString() });\n document.revision += 1;\n prune(document);\n writeAtomic(this.filePath, document);\n },\n this.lockOptions,\n );\n }\n\n private readDocumentUnlocked(): GoalHistoryDocument {\n if (!fs.existsSync(this.filePath)) return emptyDocument(this.identity);\n try {\n return decodeDocument(JSON.parse(fs.readFileSync(this.filePath, 'utf8')), this.identity);\n } catch (error) {\n quarantine(this.filePath, this.corruptDirectory);\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(`Goal history was quarantined: ${message}`, { cause: error });\n }\n }\n}\n\nexport function createGoalHistoryStore(cwd: string, options?: HistoryStoreOptions): GoalHistoryStore {\n return new GoalHistoryStore(cwd, options);\n}\n\nexport const GOAL_HISTORY_MAX_ENTRIES = MAX_ENTRIES;\nexport const GOAL_HISTORY_MAX_TOMBSTONES = MAX_TOMBSTONES;\nexport const GOAL_HISTORY_MAX_BYTES = MAX_DOCUMENT_BYTES;\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ const e=require("../../_virtual/_rolldown/runtime.cjs");let t=require("node:crypto"),n=require("node:fs");n=e.__toESM(n,1);let r=require("node:path");r=e.__toESM(r,1);const i=[`package.json`,`pnpm-workspace.yaml`,`doompi.json`];function a(e,t){let n=r.default.resolve(e),i=n;try{i=t(n)}catch{}let a=r.default.normalize(i);return process.platform===`win32`?a.toLowerCase():a}function o(e){if(!e)return null;try{return n.default.statSync(e).isDirectory()?e:null}catch{return null}}function s(e){let t=[],n=r.default.resolve(e);for(;;){t.push(n);let e=r.default.dirname(n);if(e===n)return t;n=e}}function c(e){let t=r.default.join(e,`.git`);try{let i=n.default.statSync(t);if(i.isDirectory())return t;if(!i.isFile())return null;let a=n.default.readFileSync(t,`utf8`).trim(),o=/^gitdir:\s*(.+)$/im.exec(a);return o?r.default.resolve(e,o[1].trim()):null}catch{return null}}function l(e){try{let t=n.default.readFileSync(r.default.join(e,`commondir`),`utf8`).trim();if(t)return r.default.resolve(e,t)}catch{}return e}function u(e,n){return{token:e,root:n,hash:(0,t.createHash)(`sha256`).update(e,`utf8`).digest(`hex`)}}function d(e,t){for(let n of s(e)){let e=c(n);if(e)return u(`git:${a(l(e),t)}`,a(n,t))}return null}function f(e,t){for(let o of s(e))if(n.default.existsSync(r.default.join(o,`.doom`))||i.some(e=>n.default.existsSync(r.default.join(o,e))))return a(o,t);return null}function p(e,t={}){let r=t.realpath??n.default.realpathSync.native,i=d(e,r);if(i)return i;let s=o(t.doompiRoot??process.env.DOOMPI_ROOT);if(s){let e=a(s,r);return u(`root:${e}`,e)}let c=f(e,r);if(c)return u(`root:${c}`,c);let l=a(e,r);return u(`cwd:${l}`,l)}exports.resolveRepositoryIdentity=p;
2
+ //# sourceMappingURL=repositoryIdentity.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repositoryIdentity.cjs","names":["path","fs","createHash"],"sources":["../../../src/adapters/node/repositoryIdentity.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport type { RepositoryIdentity } from '../../types/history.ts';\n\nconst GIT_DIR = '.git';\nconst DOOM_DIR = '.doom';\nconst PROJECT_MARKERS = ['package.json', 'pnpm-workspace.yaml', 'doompi.json'];\n\nexport interface RepositoryIdentityOptions {\n doompiRoot?: string;\n realpath?: (value: string) => string;\n}\n\nfunction canonicalPath(value: string, realpath: (value: string) => string): string {\n const absolute = path.resolve(value);\n let resolved = absolute;\n try {\n resolved = realpath(absolute);\n } catch {\n // Use a normalized absolute path for a not-yet-created directory.\n }\n const normalized = path.normalize(resolved);\n return process.platform === 'win32' ? normalized.toLowerCase() : normalized;\n}\n\nfunction existingDirectory(value: string | undefined): string | null {\n if (!value) return null;\n try {\n return fs.statSync(value).isDirectory() ? value : null;\n } catch {\n return null;\n }\n}\n\nfunction ancestorDirectories(start: string): string[] {\n const result: string[] = [];\n let current = path.resolve(start);\n while (true) {\n result.push(current);\n const parent = path.dirname(current);\n if (parent === current) return result;\n current = parent;\n }\n}\n\nfunction readGitDirectory(root: string): string | null {\n const gitPath = path.join(root, GIT_DIR);\n try {\n const stat = fs.statSync(gitPath);\n if (stat.isDirectory()) return gitPath;\n if (!stat.isFile()) return null;\n const content = fs.readFileSync(gitPath, 'utf8').trim();\n const match = /^gitdir:\\s*(.+)$/im.exec(content);\n return match ? path.resolve(root, match[1].trim()) : null;\n } catch {\n return null;\n }\n}\n\nfunction resolveCommonDirectory(gitDirectory: string): string {\n try {\n const relativeCommon = fs.readFileSync(path.join(gitDirectory, 'commondir'), 'utf8').trim();\n if (relativeCommon) return path.resolve(gitDirectory, relativeCommon);\n } catch {\n // Normal repositories have no commondir file.\n }\n return gitDirectory;\n}\n\nfunction makeIdentity(token: string, root: string): RepositoryIdentity {\n return { token, root, hash: createHash('sha256').update(token, 'utf8').digest('hex') };\n}\n\nfunction findGitIdentity(cwd: string, realpath: (value: string) => string): RepositoryIdentity | null {\n for (const candidate of ancestorDirectories(cwd)) {\n const gitDirectory = readGitDirectory(candidate);\n if (!gitDirectory) continue;\n const commonDirectory = canonicalPath(resolveCommonDirectory(gitDirectory), realpath);\n return makeIdentity(`git:${commonDirectory}`, canonicalPath(candidate, realpath));\n }\n return null;\n}\n\nfunction findMarkerRoot(cwd: string, realpath: (value: string) => string): string | null {\n for (const candidate of ancestorDirectories(cwd)) {\n if (fs.existsSync(path.join(candidate, DOOM_DIR))) return canonicalPath(candidate, realpath);\n if (PROJECT_MARKERS.some((marker) => fs.existsSync(path.join(candidate, marker)))) {\n return canonicalPath(candidate, realpath);\n }\n }\n return null;\n}\n\n/** Resolve canonical repository identity, sharing linked worktrees. */\nexport function resolveRepositoryIdentity(cwd: string, options: RepositoryIdentityOptions = {}): RepositoryIdentity {\n const realpath = options.realpath ?? fs.realpathSync.native;\n const nearestGit = findGitIdentity(cwd, realpath);\n if (nearestGit) return nearestGit;\n\n const configuredRoot = existingDirectory(options.doompiRoot ?? process.env.DOOMPI_ROOT);\n if (configuredRoot) {\n const root = canonicalPath(configuredRoot, realpath);\n return makeIdentity(`root:${root}`, root);\n }\n\n const markerRoot = findMarkerRoot(cwd, realpath);\n if (markerRoot) return makeIdentity(`root:${markerRoot}`, markerRoot);\n const root = canonicalPath(cwd, realpath);\n return makeIdentity(`cwd:${root}`, root);\n}\n\nexport function historyKeyForRepository(cwd: string, options?: RepositoryIdentityOptions): string {\n return resolveRepositoryIdentity(cwd, options).hash;\n}\n\nexport function canonicalRepositoryPath(value: string): string {\n return canonicalPath(value, fs.realpathSync.native);\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import{createHash as e}from"node:crypto";import t from"node:fs";import n from"node:path";const r=[`package.json`,`pnpm-workspace.yaml`,`doompi.json`];function i(e,t){let r=n.resolve(e),i=r;try{i=t(r)}catch{}let a=n.normalize(i);return process.platform===`win32`?a.toLowerCase():a}function a(e){if(!e)return null;try{return t.statSync(e).isDirectory()?e:null}catch{return null}}function o(e){let t=[],r=n.resolve(e);for(;;){t.push(r);let e=n.dirname(r);if(e===r)return t;r=e}}function s(e){let r=n.join(e,`.git`);try{let i=t.statSync(r);if(i.isDirectory())return r;if(!i.isFile())return null;let a=t.readFileSync(r,`utf8`).trim(),o=/^gitdir:\s*(.+)$/im.exec(a);return o?n.resolve(e,o[1].trim()):null}catch{return null}}function c(e){try{let r=t.readFileSync(n.join(e,`commondir`),`utf8`).trim();if(r)return n.resolve(e,r)}catch{}return e}function l(t,n){return{token:t,root:n,hash:e(`sha256`).update(t,`utf8`).digest(`hex`)}}function u(e,t){for(let n of o(e)){let e=s(n);if(e)return l(`git:${i(c(e),t)}`,i(n,t))}return null}function d(e,a){for(let s of o(e))if(t.existsSync(n.join(s,`.doom`))||r.some(e=>t.existsSync(n.join(s,e))))return i(s,a);return null}function f(e,n={}){let r=n.realpath??t.realpathSync.native,o=u(e,r);if(o)return o;let s=a(n.doompiRoot??process.env.DOOMPI_ROOT);if(s){let e=i(s,r);return l(`root:${e}`,e)}let c=d(e,r);if(c)return l(`root:${c}`,c);let f=i(e,r);return l(`cwd:${f}`,f)}export{f as resolveRepositoryIdentity};
2
+ //# sourceMappingURL=repositoryIdentity.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repositoryIdentity.mjs","names":[],"sources":["../../../src/adapters/node/repositoryIdentity.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport type { RepositoryIdentity } from '../../types/history.ts';\n\nconst GIT_DIR = '.git';\nconst DOOM_DIR = '.doom';\nconst PROJECT_MARKERS = ['package.json', 'pnpm-workspace.yaml', 'doompi.json'];\n\nexport interface RepositoryIdentityOptions {\n doompiRoot?: string;\n realpath?: (value: string) => string;\n}\n\nfunction canonicalPath(value: string, realpath: (value: string) => string): string {\n const absolute = path.resolve(value);\n let resolved = absolute;\n try {\n resolved = realpath(absolute);\n } catch {\n // Use a normalized absolute path for a not-yet-created directory.\n }\n const normalized = path.normalize(resolved);\n return process.platform === 'win32' ? normalized.toLowerCase() : normalized;\n}\n\nfunction existingDirectory(value: string | undefined): string | null {\n if (!value) return null;\n try {\n return fs.statSync(value).isDirectory() ? value : null;\n } catch {\n return null;\n }\n}\n\nfunction ancestorDirectories(start: string): string[] {\n const result: string[] = [];\n let current = path.resolve(start);\n while (true) {\n result.push(current);\n const parent = path.dirname(current);\n if (parent === current) return result;\n current = parent;\n }\n}\n\nfunction readGitDirectory(root: string): string | null {\n const gitPath = path.join(root, GIT_DIR);\n try {\n const stat = fs.statSync(gitPath);\n if (stat.isDirectory()) return gitPath;\n if (!stat.isFile()) return null;\n const content = fs.readFileSync(gitPath, 'utf8').trim();\n const match = /^gitdir:\\s*(.+)$/im.exec(content);\n return match ? path.resolve(root, match[1].trim()) : null;\n } catch {\n return null;\n }\n}\n\nfunction resolveCommonDirectory(gitDirectory: string): string {\n try {\n const relativeCommon = fs.readFileSync(path.join(gitDirectory, 'commondir'), 'utf8').trim();\n if (relativeCommon) return path.resolve(gitDirectory, relativeCommon);\n } catch {\n // Normal repositories have no commondir file.\n }\n return gitDirectory;\n}\n\nfunction makeIdentity(token: string, root: string): RepositoryIdentity {\n return { token, root, hash: createHash('sha256').update(token, 'utf8').digest('hex') };\n}\n\nfunction findGitIdentity(cwd: string, realpath: (value: string) => string): RepositoryIdentity | null {\n for (const candidate of ancestorDirectories(cwd)) {\n const gitDirectory = readGitDirectory(candidate);\n if (!gitDirectory) continue;\n const commonDirectory = canonicalPath(resolveCommonDirectory(gitDirectory), realpath);\n return makeIdentity(`git:${commonDirectory}`, canonicalPath(candidate, realpath));\n }\n return null;\n}\n\nfunction findMarkerRoot(cwd: string, realpath: (value: string) => string): string | null {\n for (const candidate of ancestorDirectories(cwd)) {\n if (fs.existsSync(path.join(candidate, DOOM_DIR))) return canonicalPath(candidate, realpath);\n if (PROJECT_MARKERS.some((marker) => fs.existsSync(path.join(candidate, marker)))) {\n return canonicalPath(candidate, realpath);\n }\n }\n return null;\n}\n\n/** Resolve canonical repository identity, sharing linked worktrees. */\nexport function resolveRepositoryIdentity(cwd: string, options: RepositoryIdentityOptions = {}): RepositoryIdentity {\n const realpath = options.realpath ?? fs.realpathSync.native;\n const nearestGit = findGitIdentity(cwd, realpath);\n if (nearestGit) return nearestGit;\n\n const configuredRoot = existingDirectory(options.doompiRoot ?? process.env.DOOMPI_ROOT);\n if (configuredRoot) {\n const root = canonicalPath(configuredRoot, realpath);\n return makeIdentity(`root:${root}`, root);\n }\n\n const markerRoot = findMarkerRoot(cwd, realpath);\n if (markerRoot) return makeIdentity(`root:${markerRoot}`, markerRoot);\n const root = canonicalPath(cwd, realpath);\n return makeIdentity(`cwd:${root}`, root);\n}\n\nexport function historyKeyForRepository(cwd: string, options?: RepositoryIdentityOptions): string {\n return resolveRepositoryIdentity(cwd, options).hash;\n}\n\nexport function canonicalRepositoryPath(value: string): string {\n return canonicalPath(value, fs.realpathSync.native);\n}\n"],"mappings":"yFAKA,MAEM,EAAkB,CAAC,eAAgB,sBAAuB,aAAa,EAO7E,SAAS,EAAc,EAAe,EAA6C,CACjF,IAAM,EAAW,EAAK,QAAQ,CAAK,EAC/B,EAAW,EACf,GAAI,CACF,EAAW,EAAS,CAAQ,CAC9B,MAAQ,CAER,CACA,IAAM,EAAa,EAAK,UAAU,CAAQ,EAC1C,OAAO,QAAQ,WAAa,QAAU,EAAW,YAAY,EAAI,CACnE,CAEA,SAAS,EAAkB,EAA0C,CACnE,GAAI,CAAC,EAAO,OAAO,KACnB,GAAI,CACF,OAAO,EAAG,SAAS,CAAK,CAAC,CAAC,YAAY,EAAI,EAAQ,IACpD,MAAQ,CACN,OAAO,IACT,CACF,CAEA,SAAS,EAAoB,EAAyB,CACpD,IAAM,EAAmB,CAAC,EACtB,EAAU,EAAK,QAAQ,CAAK,EAChC,OAAa,CACX,EAAO,KAAK,CAAO,EACnB,IAAM,EAAS,EAAK,QAAQ,CAAO,EACnC,GAAI,IAAW,EAAS,OAAO,EAC/B,EAAU,CACZ,CACF,CAEA,SAAS,EAAiB,EAA6B,CACrD,IAAM,EAAU,EAAK,KAAK,EAAM,MAAO,EACvC,GAAI,CACF,IAAM,EAAO,EAAG,SAAS,CAAO,EAChC,GAAI,EAAK,YAAY,EAAG,OAAO,EAC/B,GAAI,CAAC,EAAK,OAAO,EAAG,OAAO,KAC3B,IAAM,EAAU,EAAG,aAAa,EAAS,MAAM,CAAC,CAAC,KAAK,EAChD,EAAQ,qBAAqB,KAAK,CAAO,EAC/C,OAAO,EAAQ,EAAK,QAAQ,EAAM,EAAM,EAAE,CAAC,KAAK,CAAC,EAAI,IACvD,MAAQ,CACN,OAAO,IACT,CACF,CAEA,SAAS,EAAuB,EAA8B,CAC5D,GAAI,CACF,IAAM,EAAiB,EAAG,aAAa,EAAK,KAAK,EAAc,WAAW,EAAG,MAAM,CAAC,CAAC,KAAK,EAC1F,GAAI,EAAgB,OAAO,EAAK,QAAQ,EAAc,CAAc,CACtE,MAAQ,CAER,CACA,OAAO,CACT,CAEA,SAAS,EAAa,EAAe,EAAkC,CACrE,MAAO,CAAE,QAAO,OAAM,KAAM,EAAW,QAAQ,CAAC,CAAC,OAAO,EAAO,MAAM,CAAC,CAAC,OAAO,KAAK,CAAE,CACvF,CAEA,SAAS,EAAgB,EAAa,EAAgE,CACpG,IAAK,IAAM,KAAa,EAAoB,CAAG,EAAG,CAChD,IAAM,EAAe,EAAiB,CAAS,EAC1C,KAEL,OAAO,EAAa,OADI,EAAc,EAAuB,CAAY,EAAG,CACnC,IAAK,EAAc,EAAW,CAAQ,CAAC,CAClF,CACA,OAAO,IACT,CAEA,SAAS,EAAe,EAAa,EAAoD,CACvF,IAAK,IAAM,KAAa,EAAoB,CAAG,EAE7C,GADI,EAAG,WAAW,EAAK,KAAK,EAAW,OAAQ,CAAC,GAC5C,EAAgB,KAAM,GAAW,EAAG,WAAW,EAAK,KAAK,EAAW,CAAM,CAAC,CAAC,EAC9E,OAAO,EAAc,EAAW,CAAQ,EAG5C,OAAO,IACT,CAGA,SAAgB,EAA0B,EAAa,EAAqC,CAAC,EAAuB,CAClH,IAAM,EAAW,EAAQ,UAAY,EAAG,aAAa,OAC/C,EAAa,EAAgB,EAAK,CAAQ,EAChD,GAAI,EAAY,OAAO,EAEvB,IAAM,EAAiB,EAAkB,EAAQ,YAAc,QAAQ,IAAI,WAAW,EACtF,GAAI,EAAgB,CAClB,IAAM,EAAO,EAAc,EAAgB,CAAQ,EACnD,OAAO,EAAa,QAAQ,IAAQ,CAAI,CAC1C,CAEA,IAAM,EAAa,EAAe,EAAK,CAAQ,EAC/C,GAAI,EAAY,OAAO,EAAa,QAAQ,IAAc,CAAU,EACpE,IAAM,EAAO,EAAc,EAAK,CAAQ,EACxC,OAAO,EAAa,OAAO,IAAQ,CAAI,CACzC"}
@@ -0,0 +1,2 @@
1
+ const e=require("../../services/goal/accounting.cjs"),t=require("../../services/goal/parser.cjs"),n=require("../../services/goal/prompts.cjs"),r=require("../../services/goal/stateMachine.cjs"),i=require("../../services/goal/queue.cjs"),a=require("../../services/goal/stateCodec.cjs"),o=require("../../services/goal/runtime.cjs"),s=require("../../services/goal/safety.cjs"),c=require("../../services/goal/settings.cjs"),l=require("../../services/goal/tools.cjs"),u=require("../../services/history/historyService.cjs"),d=require("../node/historyStore.cjs"),f=require("./toolVisibility.cjs");let p=require("@earendil-works/pi-coding-agent");const m=`goal_complete`,h=`goal_blocked`,g={type:`object`,properties:{goal_id:{type:`string`,minLength:1},summary:{type:`string`,minLength:1}},required:[`goal_id`,`summary`],additionalProperties:!1},_={type:`object`,properties:{goal_id:{type:`string`,minLength:1},reason:{type:`string`,minLength:1},evidence:{type:`string`,minLength:1},repeated_turns:{type:`integer`,minimum:3}},required:[`goal_id`,`reason`,`evidence`,`repeated_turns`],additionalProperties:!1};function v(e,t=!1){return{content:[{type:`text`,text:e}],details:{error:t}}}function y(e){return e instanceof Error?e.message:String(e)}function b(e){return e&&typeof e==`object`&&!Array.isArray(e)?e:void 0}var x=class{pi;runtime;dependencies;legacyCommandService;context;sessionId;generation=0;operations=Promise.resolve();settings=c.DEFAULT_GOAL_SETTINGS;history;runGoalId;runOrigin;runExecutionGeneration;pendingRunOrigin=`manual`;lastToolCallGoalId;lastToolCallGeneration;executionGeneration=0;compactState;budgetWrapUpGoalId;lastGoalId;runToolAttempted=!1;disposed=!1;disposers=[];constructor(e,t,n){this.pi=e,this.dependencies=t,this.legacyCommandService=n,this.runtime=new o.GoalRuntimeModel({persist:e=>this.pi.appendEntry(`goal-state`,e)})}snapshot(){return this.runtime.snapshot()}async startFromLeader(e){if(!e.hasUI||e.mode!==`tui`){this.notify(`Goal leader actions require the Doom TUI.`,`warning`);return}e.ui.getEditorText().length>0&&!await e.ui.confirm(`Replace editor draft?`,`Goal start will replace the current draft with /goal .`)||e.ui.setEditorText(`/goal `)}async endFromLeader(e){await this.ensureSession(e),await this.enqueue(()=>this.clearGoal(e))}async showFromLeader(e){await this.ensureSession(e),this.showStatus()}async listHistory(e){return await this.ensureSession(e),await this.history?.list()??[]}async restartFromHistory(e,t){if(await this.ensureSession(t),!this.history){this.notify(`Goal history is unavailable.`,`error`);return}try{let n=await this.history.restart(e);await this.enqueue(()=>this.startGoal(n.objective,n.budget,t,n.goalId))}catch(e){this.notify(`Goal history restart failed: ${y(e)}`,`error`)}}async removeHistory(e,t){if(await this.ensureSession(t),!this.history){this.notify(`Goal history is unavailable.`,`error`);return}try{await this.history.remove(e),this.notify(`Goal history entry removed.`,`info`)}catch(e){this.notify(`Goal history removal failed: ${y(e)}`,`error`)}}async ensureSession(e){this.isCurrent(e)||await this.enqueue(()=>this.startSession(e))}register(){return typeof this.pi.registerTool==`function`&&this.registerTools(),this.pi.registerCommand(`goal`,{description:`Manage persistent goal execution`,handler:(e,t)=>this.enqueue(()=>this.executeCommand(e,t))}),this.disposers.push(this.registerRpc()),this.pi.on(`session_start`,(e,t)=>{this.enqueue(()=>this.startSession(t))}),this.pi.on(`session_shutdown`,()=>{this.fenceExecution(),this.generation+=1,this.context=void 0,this.sessionId=void 0,this.runGoalId=void 0,this.runOrigin=void 0,this.runExecutionGeneration=void 0,this.runToolAttempted=!1,this.compactState=void 0,this.deactivateTools()}),this.pi.on(`before_agent_start`,(e,t)=>{if(!this.isCurrent(t))return;let r=this.runtime.snapshot().goal;if(!r||r.status!==`active`)return;if(!f.canExecuteGoalTools(this.pi,r)){this.pauseForToolPolicyDrift(t,r);return}let i=n.buildGoalSystemPrompt(r);return{systemPrompt:`${e.systemPrompt}\n\n${i}`}}),this.pi.on(`agent_start`,(e,t)=>{if(!this.isCurrent(t))return;let n=this.runtime.snapshot().goal;this.runGoalId=n?.status===`active`?n.id:void 0,this.runOrigin=this.runGoalId?this.pendingRunOrigin:void 0,this.runExecutionGeneration=this.runGoalId?this.executionGeneration:void 0,this.pendingRunOrigin=`manual`,this.runToolAttempted=!1}),this.pi.on(`tool_call`,(e,t)=>{this.isCurrent(t)&&this.noteToolCall(e.toolName)}),this.pi.on(`tool_execution_start`,(e,t)=>{this.isCurrent(t)&&this.noteToolCall(e.toolName)}),this.pi.on(`agent_end`,(e,t)=>{this.enqueue(()=>this.finishAgentRun(e,t))}),this.pi.on(`agent_settled`,(e,t)=>{this.enqueue(async()=>{await this.dispatchPendingQueueAction(t)||await this.continueAfterSettled(t)})}),this.pi.on(`session_before_compact`,(e,t)=>this.beforeCompact(e,t)),this.pi.on(`session_compact`,(e,t)=>{this.enqueue(()=>this.afterCompact(e,t))}),()=>this.dispose()}registerTools(){let e={name:m,label:`Goal complete`,description:`Mark the active Goal complete after verifying every requirement.`,promptSnippet:`Complete an active Goal after authoritative verification.`,parameters:g,execute:async(e,t,n,r,i)=>this.enqueue(()=>this.complete(t,i))},t={name:h,label:`Goal blocked`,description:`Mark the active Goal blocked after the same external blocker recurs with evidence.`,promptSnippet:`Report a repeated external blocker for the active Goal.`,parameters:_,execute:async(e,t,n,r,i)=>this.enqueue(()=>this.blocked(t,i))};this.pi.registerTool(e),this.pi.registerTool(t)}registerRpc(){let e=this.pi.events;if(typeof e.on!=`function`)return()=>void 0;let t=e.on(`pi-goal:rpc:start`,e=>{this.enqueue(()=>this.handleRpcStart(e))}),n=e.on(`pi-goal:rpc:pause`,e=>{this.enqueue(()=>this.handleRpcPause(e))});return()=>{t(),n()}}async startSession(e){this.fenceExecution(),this.generation+=1;let t=this.generation;this.context=e,this.sessionId=e.sessionManager.getSessionId(),this.runGoalId=void 0,this.runOrigin=void 0,this.runExecutionGeneration=void 0,this.runToolAttempted=!1,this.pendingRunOrigin=`manual`,this.budgetWrapUpGoalId=void 0,this.settings=await this.loadSettings(),this.history=this.dependencies?.history??this.createHistory(e.cwd);let n=a.loadGoalStateFromSession({sessionManager:e.sessionManager});this.runtime.load(n),this.deactivateTools();let i=this.runtime.snapshot().goal;n.hasExperimentalQueueState&&!this.settings.experimental.goals&&i?.status===`active`&&(i=r.transitionGoal({...i},`paused`),this.runtime.replaceState({goal:i,queue:[...this.runtime.snapshot().queue],pendingAction:this.runtime.snapshot().pendingAction})),i?.status===`active`&&(this.activateTools(i)||(this.runtime.replaceState({goal:r.transitionGoal(i,`paused`),queue:[...this.runtime.snapshot().queue]}),this.notify(`Goal paused because host policy does not expose both Goal tools.`,`warning`))),t===this.generation&&(this.refreshStatus(),this.emitState())}createHistory(e){try{return new u.GoalHistoryService(d.createGoalHistoryStore(e))}catch{return}}async loadSettings(){try{let e=await import(`node:fs/promises`),t=await import(`node:path`),n=JSON.parse(await e.readFile(t.join((0,p.getAgentDir)(),`pi-goal.json`),`utf8`));return c.normalizeGoalSettings(n)??c.DEFAULT_GOAL_SETTINGS}catch{return c.DEFAULT_GOAL_SETTINGS}}isCurrent(e){return!this.disposed&&this.context===e&&this.sessionId===e.sessionManager.getSessionId()}enqueue(e){let t=this.operations.then(e,e);return this.operations=t.then(()=>void 0,()=>void 0),t}activateTools(e){if(typeof this.pi.getActiveTools!=`function`||typeof this.pi.setActiveTools!=`function`)return!1;let t=this.pi.getActiveTools(),n=f.reconcileGoalTools(this.pi,e);return(e.status===`budget_limited`?[m]:[m,h]).every(e=>n.activeTools.includes(e))?(this.executionGeneration+=1,this.lastToolCallGoalId=void 0,this.lastToolCallGeneration=void 0,!0):(this.pi.setActiveTools(t),!1)}deactivateTools(){typeof this.pi.getActiveTools==`function`&&typeof this.pi.setActiveTools==`function`&&f.removeGoalTools(this.pi),this.executionGeneration+=1,this.lastToolCallGoalId=void 0,this.lastToolCallGeneration=void 0}fenceExecution(){this.executionGeneration+=1,this.runGoalId=void 0,this.runOrigin=void 0,this.runExecutionGeneration=void 0,this.runToolAttempted=!1,this.lastToolCallGoalId=void 0,this.lastToolCallGeneration=void 0,this.compactState=void 0,this.budgetWrapUpGoalId=void 0}noteToolCall(e){if(e!==m&&e!==h)return;this.runToolAttempted=!0;let t=this.runtime.snapshot().goal;t&&(this.lastToolCallGoalId=t.id,this.lastToolCallGeneration=this.executionGeneration)}pauseForToolPolicyDrift(t,n){if(!this.isCurrent(t)||this.runtime.snapshot().goal?.id!==n.id||n.status!==`active`)return;e.updateGoalUsage(n,t,Date.now(),!1),this.fenceExecution();let i=r.transitionGoal({...n},`paused`);this.runtime.replaceState({goal:i,queue:[...this.runtime.snapshot().queue]}),this.deactivateTools(),this.abortCurrentTurn(t),this.refreshStatus(),this.emitState(`required Goal tool unavailable`),this.notify(`Goal tools are unavailable, so the Goal was paused. Restore the tools and run /goal resume.`,`warning`)}beforeCompact(e,t){if(!this.isCurrent(t))return;let n=this.runtime.snapshot().goal;!n||n.status!==`active`||(this.compactState={goalId:n.id,hadAutomaticRun:this.runOrigin===`automatic`&&this.runGoalId===n.id,willRetry:e.willRetry===!0},this.executionGeneration+=1,this.runGoalId=void 0,this.runOrigin=void 0,this.runExecutionGeneration=void 0,this.lastToolCallGoalId=void 0,this.lastToolCallGeneration=void 0)}async afterCompact(e,t){if(!this.isCurrent(t))return;let n=this.compactState;if(!n)return;let r=this.runtime.snapshot().goal;this.compactState=void 0,!(!r||r.id!==n.goalId||r.status!==`active`)&&(this.executionGeneration+=1,this.runToolAttempted=!1,!n.willRetry&&n.hadAutomaticRun&&e.willRetry!==!0&&(this.pendingRunOrigin=`automatic`,this.runGoalId=r.id,this.runOrigin=`automatic`,this.runExecutionGeneration=this.executionGeneration))}refreshStatus(){let e=this.context,t=this.runtime.snapshot();if(!e?.hasUI)return;let n=t.pendingAction!==void 0||!t.goal&&t.queue.length>0;e.ui.setStatus(`goal`,n?`queued`:r.formatStatus(t.goal))}emitState(e,t){let n=this.runtime.snapshot().goal,r=n?.status??`cleared`;this.lastGoalId=n?.id??this.lastGoalId,this.pi.events.emit(`pi-goal:state`,{goalId:n?.id??this.lastGoalId??``,status:r,...e?{reason:e}:{},...t?{summary:t}:{}})}notify(e,t=`info`){this.context?.hasUI&&this.context.ui.notify(e,t)}abortCurrentTurn(e){try{return e.abort(),!0}catch{return!1}}async executeCommand(e,n){let r=Object.hasOwn(n,`sessionManager`);if(!e.trim()&&this.legacyCommandService&&!r){let e=await this.legacyCommandService.execute();n.hasUI&&n.ui.notify(e.message,e.level);return}this.isCurrent(n)||await this.startSession(n);let i=t.parseGoalCommand(e,{experimentalGoals:this.settings.experimental.goals});if(typeof i==`string`){this.notify(i,`warning`);return}switch(i.kind){case`show`:this.showStatus();return;case`start`:await this.startGoal(i.objective??``,i.tokenBudget,n);return;case`pause`:this.pauseGoal(n);return;case`resume`:await this.resumeGoal(n);return;case`clear`:await this.clearGoal(n);return;case`edit`:await this.editGoal(i.objective??``,i.tokenBudget,n);return;case`add`:await this.addGoal(i.objective??``,i.tokenBudget,n);return;case`prioritize`:await this.prioritizeGoal(i.objective??``,i.tokenBudget,n);return;case`drop-last`:this.dropLastGoal(n);return;case`skip`:await this.skipGoal(n);return}}showStatus(){let e=this.runtime.snapshot(),t=e.goal;if(!t){this.notify(`No active goal.`,`info`);return}let n=e.queue.length?`\nQueued: ${e.queue.map(e=>e.text).join(`, `)}`:``;this.notify(`Goal: ${t.text}\nStatus: ${t.status}${n}`,`info`)}async startGoal(i,a,o,s){let c=t.validateObjective(i);if(c){this.notify(c,`warning`);return}let l=this.runtime.snapshot(),u=l.goal&&l.goal.status!==`complete`?l.goal:void 0;if(u){if(!await o.ui.confirm(`Replace goal?`,`Current goal: ${u.text}\n\nNew goal: ${i}`))return;if(!await this.archiveAll([u,...l.queue],`replaced`)){this.notify(`Goal replacement aborted because history archival failed.`,`error`);return}}let d=r.createGoal(i,a,{id:s,baselineTokens:e.currentTokenTotal(o)});if(!this.activateTools(d)){this.notify(`Cannot start /goal: host policy rejected Goal tools.`,`error`);return}this.runtime.replaceState({goal:d,queue:[]}),this.budgetWrapUpGoalId=void 0,this.pendingRunOrigin=`manual`,this.refreshStatus(),this.emitState();try{this.pi.sendUserMessage(n.buildGoalPrompt(d),{deliverAs:`followUp`})}catch(e){this.runtime.replaceState({goal:u,queue:l.queue}),this.fenceExecution(),u||this.deactivateTools(),this.refreshStatus(),this.notify(`Goal kickoff failed: ${y(e)}`,`error`);return}this.notify(u?`Goal replaced: ${i}`:`Goal started: ${i}`)}pauseGoal(t){let n=this.runtime.snapshot().goal;if(!n){this.notify(`No active goal.`);return}if(n.status!==`active`){this.notify(`Goal is ${n.status}; only active goals can be paused.`,`warning`);return}e.updateGoalUsage(n,t,Date.now(),!1),this.fenceExecution();let i=r.transitionGoal({...n},`paused`);this.runtime.replaceState({goal:i,queue:[...this.runtime.snapshot().queue]}),this.deactivateTools(),t.abort(),this.refreshStatus(),this.emitState(`paused`),this.notify(`Goal paused: ${n.text}`)}async resumeGoal(e){let t=this.runtime.snapshot().goal;if(!t){this.notify(`No active goal.`);return}if(!r.isResumableGoalStatus(t.status)){this.notify(`Goal is ${t.status}; it cannot be resumed.`,`warning`);return}if(t.tokenBudget!==void 0&&t.tokensUsed>=t.tokenBudget){this.notify(`Goal token budget is still reached.`,`warning`);return}let i=s.resetGoalSafetyEpoch(r.transitionGoal({...t,updatedAt:Date.now()},`active`));if(!this.activateTools(i)){this.notify(`Cannot resume /goal: host policy rejected Goal tools.`,`error`);return}this.runtime.replaceState({goal:i,queue:[...this.runtime.snapshot().queue]}),this.budgetWrapUpGoalId=void 0,this.pendingRunOrigin=`manual`,this.refreshStatus(),this.emitState();try{this.pi.sendUserMessage(n.buildResumePrompt(i,t.status),{deliverAs:`followUp`})}catch(e){this.fenceExecution(),this.runtime.replaceState({goal:t,queue:[...this.runtime.snapshot().queue]}),this.deactivateTools(),this.refreshStatus(),this.notify(`Goal resume failed: ${y(e)}`,`error`)}}async clearGoal(e){let t=this.runtime.snapshot();if(!await this.archiveAll([...t.goal?[t.goal]:[],...t.queue],`cleared`)){this.notify(`Goal clear aborted because history archival failed.`,`error`);return}this.fenceExecution(),this.runtime.clear(),this.deactivateTools(),e.abort(),this.refreshStatus(),this.emitState(`cleared`),t.goal?this.notify(`Goal cleared: ${t.goal.text}`,`warning`):this.notify(`No active goal.`,`info`)}async editGoal(e,r,i){let a=t.validateObjective(e);if(a){this.notify(a,`warning`);return}let o=this.runtime.snapshot().goal;if(!o){this.notify(`No active goal. Use /goal <objective> to start one.`,`warning`);return}let s={...o,text:e,...r===void 0?{}:{tokenBudget:r},updatedAt:Date.now()};this.runtime.replaceState({goal:s,queue:[...this.runtime.snapshot().queue]}),this.refreshStatus(),this.emitState(),o.status===`active`?this.pi.sendUserMessage(n.buildObjectiveUpdatedPrompt(s),{deliverAs:`followUp`}):this.notify(`Goal updated but remains ${o.status}. Resume explicitly to execute it.`)}async addGoal(e,n,r){let a=t.validateObjective(e);if(a){this.notify(a,`warning`);return}let o=this.runtime.snapshot();if(!o.goal){await this.startGoal(e,n,this.context);return}let s=i.enqueueGoal({goal:o.goal,queue:[...o.queue],pendingAction:o.pendingAction,enabled:this.settings.experimental.goals},e,n);this.runtime.replaceState(s),this.refreshStatus(),this.emitState(),this.notify(`Goal added at position ${s.queue.length+1}: ${e}`)}async prioritizeGoal(e,n,r){let i=t.validateObjective(e);if(i){this.notify(i,`warning`);return}let a=this.runtime.snapshot();if(!a.goal){await this.startGoal(e,n,this.context);return}this.runtime.replaceState({goal:a.goal,queue:[...a.queue],pendingAction:{kind:`prioritize`,objective:e,tokenBudget:n}}),this.notify(`Priority goal queued until the current turn settles: ${e}`)}dropLastGoal(e){let t=this.runtime.snapshot();if(!t.queue.length){this.notify(`No queued goals.`,`info`);return}this.runtime.replaceState({goal:t.goal,queue:[...t.queue].slice(0,-1),pendingAction:t.pendingAction}),this.notify(`Last queued goal dropped.`,`warning`)}async skipGoal(e){let t=this.runtime.snapshot();if(!t.goal){this.notify(`No goals to skip.`,`info`);return}if(!await this.archive(t.goal,`skipped`)){this.notify(`Goal skip aborted because history archival failed.`,`error`);return}this.fenceExecution();let n=i.skipCurrentGoal({goal:t.goal,queue:[...t.queue],pendingAction:t.pendingAction,enabled:this.settings.experimental.goals});n.pendingAction?this.runtime.replaceState(n):this.runtime.clear(),this.deactivateTools(),e.abort(),this.refreshStatus(),this.emitState(`skipped`)}async finishAgentRun(t,n){if(!this.isCurrent(n))return;let i=this.runtime.snapshot(),a=i.goal;if(!a||a.status!==`active`)return;if(this.runGoalId!==a.id||this.runExecutionGeneration!==this.executionGeneration){this.fenceExecution();return}let o=t.messages??[],c=S(o);if(e.updateGoalUsage(a,n,Date.now(),!0),c===`aborted`){this.fenceExecution(),this.runtime.replaceState({goal:a,queue:[...i.queue]}),this.refreshStatus();return}if(c===`usage_limited`||c===`blocked`){this.fenceExecution();let e=r.transitionGoal({...a},c);this.runtime.replaceState({goal:e,queue:[...i.queue]}),this.deactivateTools(),this.abortCurrentTurn(n),this.refreshStatus(),this.emitState(c===`usage_limited`?`provider usage limit`:`provider error`),this.notify(c===`usage_limited`?`Goal paused because the provider usage limit was reached. Run /goal resume after it resets.`:`Goal blocked because the provider reported a terminal error. Resolve it or run /goal resume.`,`warning`);return}if(this.runOrigin===`automatic`){a.automaticModelTurns=Math.min(2**53-1,a.automaticModelTurns+1);let e=s.nextToolFreeRepeatState(a,o,this.runToolAttempted);a.toolFreeRepeatCount=e.toolFreeRepeatCount,a.lastToolFreeOutputFingerprint=e.lastToolFreeOutputFingerprint;let t=s.safetyLimitReached(a,this.settings.continuationLimits);if(t){this.pauseForSafety(n,a,t);return}}if(a.tokenBudget!==void 0&&a.tokensUsed>=a.tokenBudget){this.limitForBudget(n,a,i.queue);return}this.runtime.replaceState({goal:a,queue:[...i.queue]}),this.refreshStatus()}async dispatchPendingQueueAction(t){if(!this.isCurrent(t))return!1;let a=this.runtime.snapshot(),o=a.pendingAction;if(!o)return!1;if(!this.settings.experimental.goals||!t.isIdle()||t.hasPendingMessages())return!0;if(o.kind===`prioritize`){let i=a.goal;if(!i)return this.runtime.replaceState({queue:[...a.queue]}),await this.startGoal(o.objective,o.tokenBudget,t),!0;i.status===`active`&&e.updateGoalUsage(i,t,Date.now(),!1);let s=r.createGoal(o.objective,o.tokenBudget,{baselineTokens:e.currentTokenTotal(t)}),c=i.status===`complete`?[...a.queue]:[r.transitionGoal({...i,activeStartedAt:void 0},`queued`),...a.queue];if(this.deactivateTools(),!this.activateTools(s)){let e=r.transitionGoal(s,`paused`);return this.runtime.replaceState({goal:e,queue:c}),this.deactivateTools(),this.refreshStatus(),this.emitState(`queued goal tools unavailable`),this.notify(`Priority Goal is retained but paused because host policy rejected Goal tools.`,`warning`),!0}this.runtime.replaceState({goal:s,queue:c}),this.pendingRunOrigin=`manual`,this.refreshStatus(),this.emitState(`prioritized`);try{this.pi.sendUserMessage(n.buildGoalPrompt(s),{deliverAs:`followUp`})}catch(e){this.pauseForDeliveryFailure(t,s,`Priority Goal kickoff failed: ${y(e)}`)}return!0}let s=a.goal;if(!s||s.id!==o.goalId||o.reason===`complete`&&s.status!==`complete`)return this.runtime.replaceState({goal:s,queue:[...a.queue]}),this.refreshStatus(),!0;let c=i.promoteNextGoal({goal:s,queue:[...a.queue],pendingAction:o,enabled:!0},Date.now(),e.currentTokenTotal(t)),l=c.goal;if(!l||l.id===s.id)return this.runtime.clear(),this.deactivateTools(),this.refreshStatus(),this.emitState(o.reason),!0;if(!this.activateTools(l)){let e=r.transitionGoal(l,`paused`);return this.runtime.replaceState({goal:e,queue:[...c.queue]}),this.deactivateTools(),this.refreshStatus(),this.emitState(`queued goal tools unavailable`),this.notify(`Next Goal is retained but paused because host policy rejected Goal tools.`,`warning`),!0}this.runtime.replaceState({goal:l,queue:[...c.queue]}),this.pendingRunOrigin=`manual`,this.refreshStatus(),this.emitState(o.reason);try{this.pi.sendUserMessage(n.buildGoalPrompt(l),{deliverAs:`followUp`})}catch(e){this.pauseForDeliveryFailure(t,l,`Queued Goal kickoff failed: ${y(e)}`)}return!0}async continueAfterSettled(e){if(!this.isCurrent(e)||!e.isIdle()||e.hasPendingMessages())return;let t=this.runtime.snapshot(),i=t.goal;if(!i||i.status!==`active`||!this.runGoalId||this.runGoalId!==i.id)return;if(this.runExecutionGeneration!==this.executionGeneration){this.fenceExecution();return}if(!f.canExecuteGoalTools(this.pi,i)){this.pauseForToolPolicyDrift(e,i);return}let a=s.safetyLimitReached(i,this.settings.continuationLimits);if(a){this.pauseForSafety(e,i,a);return}let o=r.incrementGoal({...i});this.runtime.replaceState({goal:o,queue:[...t.queue]});let c=n.buildContinuePrompt(o);this.pendingRunOrigin=`automatic`;try{this.pi.sendUserMessage(c,{deliverAs:`followUp`})}catch(t){this.pauseForDeliveryFailure(e,o,`Goal continuation failed: ${y(t)}`);return}this.runGoalId=void 0,this.runOrigin=void 0,this.runExecutionGeneration=void 0,this.runToolAttempted=!1}pauseForSafety(e,t,n){if(this.runtime.snapshot().goal?.id!==t.id)return;this.fenceExecution();let i=r.transitionGoal({...t,safetyPauseCause:n},`paused`);this.runtime.replaceState({goal:i,queue:[...this.runtime.snapshot().queue]}),this.deactivateTools(),this.abortCurrentTurn(e),this.refreshStatus(),this.emitState(n===`continuation_limit`?`automatic response limit`:`no progress`);let a=n===`continuation_limit`?`automatic response limit`:`no progress`;this.notify(`Goal paused by safety limit: ${a}. Run /goal resume to continue.`,`warning`)}limitForBudget(e,t,n){this.fenceExecution();let i=r.transitionGoal({...t},`budget_limited`);this.runtime.replaceState({goal:i,queue:[...n]}),this.deactivateTools();let a=this.activateTools(i);if(a&&this.budgetWrapUpGoalId!==i.id){this.budgetWrapUpGoalId=i.id;try{this.pi.sendMessage({customType:`goal-budget-wrap-up`,content:`The Goal token budget is exhausted. Stop substantive work, summarize verified progress and blockers, and call goal_complete only if every requirement is already proven.`,display:!0,details:{goalId:i.id}},{deliverAs:`steer`})}catch(e){this.budgetWrapUpGoalId=void 0,this.notify(`Goal budget wrap-up failed: ${y(e)}`,`error`)}}a||this.notify(`Goal budget reached, but host policy rejected goal_complete.`,`warning`),this.abortCurrentTurn(e),this.refreshStatus(),this.emitState(`token budget reached`)}pauseForDeliveryFailure(e,t,n){this.fenceExecution();let i=r.transitionGoal({...t},`paused`);this.runtime.replaceState({goal:i,queue:[...this.runtime.snapshot().queue]}),this.deactivateTools(),this.abortCurrentTurn(e),this.refreshStatus(),this.emitState(`delivery failed`),this.notify(n,`error`)}async complete(e,t){let n=this.runtime.snapshot(),i=n.goal;if(!i||!this.acceptsToolCall(t,i))return v(`Goal completion rejected because this tool call belongs to a stale Goal execution.`,!0);let a=l.validateCompletionInput(i,e);if(!i||i.status!==`active`&&i.status!==`budget_limited`)return v(`Goal completion is not available in the current state.`,!0);if(!a.ok)return v(a.reason??`Goal completion rejected.`,!0);let o=typeof e.summary==`string`?e.summary.trim():``;if(r.isContradictoryCompletionSummary(o))return v(`Completion evidence contradicts completion.`,!0);if(!await this.archive(i,`complete`,o))return v(`Completion aborted because history archival failed.`,!0);this.fenceExecution();let s=r.transitionGoal({...i},`complete`);return n.queue.length&&this.settings.experimental.goals?this.runtime.replaceState({goal:s,queue:[...n.queue],pendingAction:{kind:`advance`,goalId:i.id,reason:`complete`,completedText:i.text}}):this.runtime.clear(),this.deactivateTools(),t.abort(),this.refreshStatus(),this.emitState(void 0,o),v(`Goal complete: ${i.text}`)}async blocked(e,t){let n=this.runtime.snapshot(),i=n.goal;if(!i||!this.acceptsToolCall(t,i))return v(`Goal blocker rejected because this tool call belongs to a stale Goal execution.`,!0);let a=l.validateBlockedInput(i,e);if(!i||i.status!==`active`)return v(`Goal blocking is not available in the current state.`,!0);if(!a.ok)return v(a.reason??`Goal blocker rejected.`,!0);let o=typeof e.reason==`string`?e.reason.trim():`blocked`;this.fenceExecution();let s=r.transitionGoal({...i},`blocked`);return this.runtime.replaceState({goal:s,queue:[...n.queue]}),this.deactivateTools(),t.abort(),this.refreshStatus(),this.emitState(o),v(`Goal blocked: ${o}`)}acceptsToolCall(e,t){return!this.isCurrent(e)||this.runGoalId&&(this.runGoalId!==t.id||this.runExecutionGeneration!==this.executionGeneration)||this.lastToolCallGoalId&&(this.lastToolCallGoalId!==t.id||this.lastToolCallGeneration!==this.executionGeneration)?!1:f.canExecuteGoalTools(this.pi,t)}async handleRpcStart(e){let n=b(e),r=typeof n?.requestId==`string`?n.requestId.trim():``,i=this.pi.events;if(!r)return;let a=e=>i.emit(`pi-goal:rpc:start:reply:${r}`,e),o=typeof n?.objective==`string`?n.objective.trim():``,s=t.validateObjective(o);if(s){a({success:!1,error:s});return}if(this.runtime.snapshot().goal){a({success:!1,error:`a goal already exists`});return}let c=this.context;if(!c){a({success:!1,error:`no active pi-goal session context`});return}try{await this.startGoal(o,typeof n?.tokenBudget==`number`?n.tokenBudget:void 0,c);let e=this.runtime.snapshot().goal;a(e?{success:!0,data:{goalId:e.id,status:e.status}}:{success:!1,error:`goal could not be activated`})}catch(e){a({success:!1,error:`goal start failed: ${y(e)}`})}}async handleRpcPause(e){let t=b(e),n=this.runtime.snapshot().goal;if(!n||n.status!==`active`)return;let r=typeof t?.goalId==`string`?t.goalId:void 0;r&&r!==n.id||this.context&&this.pauseGoal(this.context)}async archiveAll(e,t){let n=new Set;for(let r of e)if(!n.has(r.id)&&(n.add(r.id),!await this.archive(r,t)))return!1;return!0}async archive(e,t,n){if(!this.history)return!0;try{return await this.history.archive({id:e.id,objective:e.text,status:t===`complete`?`complete`:e.status,...n?{reason:n}:{reason:t},...e.tokenBudget===void 0?{}:{budget:e.tokenBudget},archivedAt:new Date().toISOString(),startedAt:new Date(e.startedAt).toISOString(),...t===`complete`?{completedAt:new Date().toISOString()}:{}}),!0}catch(e){return this.notify(`Goal history archival failed: ${y(e)}`,`error`),!1}}dispose(){if(!this.disposed){this.disposed=!0,this.generation+=1;for(let e of this.disposers.splice(0))e();this.deactivateTools(),this.context=void 0}}};function S(e){let t=[...e].reverse().find(e=>C(e));if(!t||typeof t!=`object`)return;let n=t;if(n.stopReason===`aborted`)return`aborted`;if(n.stopReason!==`error`&&n.stopReason!==`length`&&n.errorMessage===void 0)return;let r=[n.errorMessage,w(n)].filter(e=>typeof e==`string`).join(` `).toLowerCase();return n.stopReason===`length`||/(?:rate|quota|usage|token|context|capacity|limit|429|too many|billing|exhaust)/u.test(r)?`usage_limited`:`blocked`}function C(e){return!!(e&&typeof e==`object`&&e.role===`assistant`)}function w(e){return typeof e.text==`string`?e.text:typeof e.content==`string`?e.content:Array.isArray(e.content)?e.content.map(e=>{if(!e||typeof e!=`object`)return``;let t=e.text;return typeof t==`string`?t:``}).join(` `):``}exports.GoalPiManager=x;
2
+ //# sourceMappingURL=goalManager.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"goalManager.cjs","names":["DEFAULT_GOAL_SETTINGS","GoalRuntimeModel","canExecuteGoalTools","buildGoalSystemPrompt","loadGoalStateFromSession","transitionGoal","GoalHistoryService","createGoalHistoryStore","getAgentDir","normalizeGoalSettings","reconcileGoalTools","formatStatus","parseGoalCommand","validateObjective","createGoal","currentTokenTotal","buildGoalPrompt","isResumableGoalStatus","resetGoalSafetyEpoch","buildResumePrompt","buildObjectiveUpdatedPrompt","enqueueGoal","skipCurrentGoal","nextToolFreeRepeatState","safetyLimitReached","promoteNextGoal","incrementGoal","buildContinuePrompt","validateCompletionInput","isContradictoryCompletionSummary","validateBlockedInput"],"sources":["../../../src/adapters/pi/goalManager.ts"],"sourcesContent":["import type {\n AgentToolResult,\n ExtensionAPI,\n ExtensionCommandContext,\n ExtensionContext,\n ToolDefinition,\n} from '@earendil-works/pi-coding-agent';\nimport { getAgentDir } from '@earendil-works/pi-coding-agent';\nimport { currentTokenTotal, updateGoalUsage } from '../../services/goal/accounting.ts';\nimport { parseGoalCommand, validateObjective } from '../../services/goal/parser.ts';\nimport {\n buildContinuePrompt,\n buildGoalPrompt,\n buildGoalSystemPrompt,\n buildObjectiveUpdatedPrompt,\n buildResumePrompt,\n} from '../../services/goal/prompts.ts';\nimport { enqueueGoal, promoteNextGoal, skipCurrentGoal } from '../../services/goal/queue.ts';\nimport { GoalRuntimeModel } from '../../services/goal/runtime.ts';\nimport { nextToolFreeRepeatState, resetGoalSafetyEpoch, safetyLimitReached } from '../../services/goal/safety.ts';\nimport { DEFAULT_GOAL_SETTINGS, normalizeGoalSettings } from '../../services/goal/settings.ts';\nimport { loadGoalStateFromSession } from '../../services/goal/stateCodec.ts';\nimport {\n createGoal,\n formatStatus,\n incrementGoal,\n isContradictoryCompletionSummary,\n isResumableGoalStatus,\n transitionGoal,\n} from '../../services/goal/stateMachine.ts';\nimport { validateBlockedInput, validateCompletionInput } from '../../services/goal/tools.ts';\nimport { GoalHistoryService } from '../../services/history/historyService.ts';\nimport type { GoalExtensionDependencies, GoalExtensionService } from '../../types/extension.ts';\nimport type { ActiveGoal, GoalRuntimeSnapshot, GoalStateData } from '../../types/goal.ts';\nimport type { GoalHistoryEntry, GoalHistoryPort } from '../../types/history.ts';\nimport { createGoalHistoryStore } from '../node/historyStore.ts';\nimport { canExecuteGoalTools, reconcileGoalTools, removeGoalTools } from './toolVisibility.ts';\n\nconst STATE_EVENT = 'pi-goal:state';\nconst RPC_START = 'pi-goal:rpc:start';\nconst RPC_PAUSE = 'pi-goal:rpc:pause';\nconst COMPLETE_TOOL = 'goal_complete';\nconst BLOCKED_TOOL = 'goal_blocked';\nconst STATUS_KEY = 'goal';\nconst SETTINGS_FILE = 'pi-goal.json';\n\nconst COMPLETE_PARAMETERS = {\n type: 'object',\n properties: {\n goal_id: { type: 'string', minLength: 1 },\n summary: { type: 'string', minLength: 1 },\n },\n required: ['goal_id', 'summary'],\n additionalProperties: false,\n} as const;\n\nconst BLOCKED_PARAMETERS = {\n type: 'object',\n properties: {\n goal_id: { type: 'string', minLength: 1 },\n reason: { type: 'string', minLength: 1 },\n evidence: { type: 'string', minLength: 1 },\n repeated_turns: { type: 'integer', minimum: 3 },\n },\n required: ['goal_id', 'reason', 'evidence', 'repeated_turns'],\n additionalProperties: false,\n} as const;\n\ntype CompleteInput = { goal_id?: unknown; summary?: unknown };\ntype BlockedInput = { goal_id?: unknown; reason?: unknown; evidence?: unknown; repeated_turns?: unknown };\ntype ToolResult = AgentToolResult<Record<string, unknown>>;\ntype RunOrigin = 'manual' | 'automatic';\ntype AgentEndEventLike = { messages?: readonly unknown[] };\ntype CompactEventLike = { reason?: string; willRetry?: boolean };\ntype CompactState = { goalId: string; hadAutomaticRun: boolean; willRetry: boolean };\n\ntype EventBus = {\n emit(channel: string, data: unknown): void;\n on(channel: string, handler: (data: unknown) => void): () => void;\n};\n\nfunction messageResult(text: string, isError = false): ToolResult {\n return { content: [{ type: 'text', text }], details: { error: isError } };\n}\n\nfunction errorText(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> | undefined {\n return value && typeof value === 'object' && !Array.isArray(value) ? (value as Record<string, unknown>) : undefined;\n}\n\nexport class GoalPiManager {\n private readonly pi: ExtensionAPI;\n private readonly runtime: GoalRuntimeModel;\n private readonly dependencies?: GoalExtensionDependencies;\n private readonly legacyCommandService?: GoalExtensionService;\n private context?: ExtensionContext;\n private sessionId?: string;\n private generation = 0;\n private operations: Promise<void> = Promise.resolve();\n private settings = DEFAULT_GOAL_SETTINGS;\n private history?: GoalHistoryPort;\n private runGoalId?: string;\n private runOrigin?: RunOrigin;\n private runExecutionGeneration?: number;\n private pendingRunOrigin: RunOrigin = 'manual';\n private lastToolCallGoalId?: string;\n private lastToolCallGeneration?: number;\n private executionGeneration = 0;\n private compactState?: CompactState;\n private budgetWrapUpGoalId?: string;\n private lastGoalId?: string;\n private runToolAttempted = false;\n private disposed = false;\n private readonly disposers: Array<() => void> = [];\n\n constructor(pi: ExtensionAPI, dependencies?: GoalExtensionDependencies, legacyCommandService?: GoalExtensionService) {\n this.pi = pi;\n this.dependencies = dependencies;\n this.legacyCommandService = legacyCommandService;\n this.runtime = new GoalRuntimeModel({\n persist: (state: GoalStateData) => this.pi.appendEntry('goal-state', state),\n });\n }\n\n public snapshot(): GoalRuntimeSnapshot {\n return this.runtime.snapshot();\n }\n\n public async startFromLeader(ctx: ExtensionContext): Promise<void> {\n if (!ctx.hasUI || ctx.mode !== 'tui') {\n this.notify('Goal leader actions require the Doom TUI.', 'warning');\n return;\n }\n const draft = ctx.ui.getEditorText();\n if (draft.length > 0) {\n const accepted = await ctx.ui.confirm(\n 'Replace editor draft?',\n 'Goal start will replace the current draft with /goal .',\n );\n if (!accepted) return;\n }\n ctx.ui.setEditorText('/goal ');\n }\n\n public async endFromLeader(ctx: ExtensionContext): Promise<void> {\n await this.ensureSession(ctx);\n await this.enqueue(() => this.clearGoal(ctx));\n }\n\n public async showFromLeader(ctx: ExtensionContext): Promise<void> {\n await this.ensureSession(ctx);\n this.showStatus();\n }\n\n public async listHistory(ctx: ExtensionContext): Promise<GoalHistoryEntry[]> {\n await this.ensureSession(ctx);\n return (await this.history?.list()) ?? [];\n }\n\n public async restartFromHistory(id: string, ctx: ExtensionContext): Promise<void> {\n await this.ensureSession(ctx);\n if (!this.history) {\n this.notify('Goal history is unavailable.', 'error');\n return;\n }\n try {\n const restart = await this.history.restart(id);\n await this.enqueue(() =>\n this.startGoal(restart.objective, restart.budget, ctx as ExtensionCommandContext, restart.goalId),\n );\n } catch (error) {\n this.notify(`Goal history restart failed: ${errorText(error)}`, 'error');\n }\n }\n\n public async removeHistory(id: string, ctx: ExtensionContext): Promise<void> {\n await this.ensureSession(ctx);\n if (!this.history) {\n this.notify('Goal history is unavailable.', 'error');\n return;\n }\n try {\n await this.history.remove(id);\n this.notify('Goal history entry removed.', 'info');\n } catch (error) {\n this.notify(`Goal history removal failed: ${errorText(error)}`, 'error');\n }\n }\n\n private async ensureSession(ctx: ExtensionContext): Promise<void> {\n if (this.isCurrent(ctx)) return;\n await this.enqueue(() => this.startSession(ctx));\n }\n\n register(): () => void {\n if (typeof this.pi.registerTool === 'function') this.registerTools();\n this.pi.registerCommand('goal', {\n description: 'Manage persistent goal execution',\n handler: (args, ctx) => this.enqueue(() => this.executeCommand(args, ctx)),\n });\n this.disposers.push(this.registerRpc());\n this.pi.on('session_start', (_event, ctx) => {\n void this.enqueue(() => this.startSession(ctx));\n });\n this.pi.on('session_shutdown', () => {\n this.fenceExecution();\n this.generation += 1;\n this.context = undefined;\n this.sessionId = undefined;\n this.runGoalId = undefined;\n this.runOrigin = undefined;\n this.runExecutionGeneration = undefined;\n this.runToolAttempted = false;\n this.compactState = undefined;\n this.deactivateTools();\n });\n this.pi.on('before_agent_start', (event, ctx) => {\n if (!this.isCurrent(ctx)) return undefined;\n const goal = this.runtime.snapshot().goal;\n if (!goal || goal.status !== 'active') return undefined;\n if (!canExecuteGoalTools(this.pi, goal)) {\n this.pauseForToolPolicyDrift(ctx, goal);\n return undefined;\n }\n const prompt = buildGoalSystemPrompt(goal);\n return { systemPrompt: `${event.systemPrompt}\\n\\n${prompt}` };\n });\n this.pi.on('agent_start', (_event, ctx) => {\n if (!this.isCurrent(ctx)) return;\n const goal = this.runtime.snapshot().goal;\n this.runGoalId = goal?.status === 'active' ? goal.id : undefined;\n this.runOrigin = this.runGoalId ? this.pendingRunOrigin : undefined;\n this.runExecutionGeneration = this.runGoalId ? this.executionGeneration : undefined;\n this.pendingRunOrigin = 'manual';\n this.runToolAttempted = false;\n });\n this.pi.on('tool_call', (event, ctx) => {\n if (!this.isCurrent(ctx)) return undefined;\n this.noteToolCall(event.toolName);\n return undefined;\n });\n this.pi.on('tool_execution_start', (event, ctx) => {\n if (!this.isCurrent(ctx)) return;\n this.noteToolCall(event.toolName);\n });\n this.pi.on('agent_end', (event, ctx) => {\n void this.enqueue(() => this.finishAgentRun(event, ctx));\n });\n this.pi.on('agent_settled', (_event, ctx) => {\n void this.enqueue(async () => {\n if (await this.dispatchPendingQueueAction(ctx)) return;\n await this.continueAfterSettled(ctx);\n });\n });\n this.pi.on('session_before_compact', (event, ctx) => this.beforeCompact(event, ctx));\n this.pi.on('session_compact', (event, ctx) => {\n void this.enqueue(() => this.afterCompact(event, ctx));\n });\n return () => this.dispose();\n }\n\n private registerTools(): void {\n const complete = {\n name: COMPLETE_TOOL,\n label: 'Goal complete',\n description: 'Mark the active Goal complete after verifying every requirement.',\n promptSnippet: 'Complete an active Goal after authoritative verification.',\n parameters: COMPLETE_PARAMETERS,\n execute: async (\n _toolCallId: string,\n params: CompleteInput,\n _signal: AbortSignal | undefined,\n _onUpdate: unknown,\n ctx: ExtensionContext,\n ): Promise<ToolResult> => this.enqueue(() => this.complete(params, ctx)),\n } as unknown as ToolDefinition;\n const blocked = {\n name: BLOCKED_TOOL,\n label: 'Goal blocked',\n description: 'Mark the active Goal blocked after the same external blocker recurs with evidence.',\n promptSnippet: 'Report a repeated external blocker for the active Goal.',\n parameters: BLOCKED_PARAMETERS,\n execute: async (\n _toolCallId: string,\n params: BlockedInput,\n _signal: AbortSignal | undefined,\n _onUpdate: unknown,\n ctx: ExtensionContext,\n ): Promise<ToolResult> => this.enqueue(() => this.blocked(params, ctx)),\n } as unknown as ToolDefinition;\n this.pi.registerTool(complete);\n this.pi.registerTool(blocked);\n }\n\n private registerRpc(): () => void {\n const events = this.pi.events as Partial<EventBus>;\n if (typeof events.on !== 'function') return () => undefined;\n const disposeStart = events.on(RPC_START, (data) => {\n void this.enqueue(() => this.handleRpcStart(data));\n });\n const disposePause = events.on(RPC_PAUSE, (data) => {\n void this.enqueue(() => this.handleRpcPause(data));\n });\n return () => {\n disposeStart();\n disposePause();\n };\n }\n\n private async startSession(ctx: ExtensionContext): Promise<void> {\n this.fenceExecution();\n this.generation += 1;\n const activeGeneration = this.generation;\n this.context = ctx;\n this.sessionId = ctx.sessionManager.getSessionId();\n this.runGoalId = undefined;\n this.runOrigin = undefined;\n this.runExecutionGeneration = undefined;\n this.runToolAttempted = false;\n this.pendingRunOrigin = 'manual';\n this.budgetWrapUpGoalId = undefined;\n this.settings = await this.loadSettings();\n this.history = this.dependencies?.history ?? this.createHistory(ctx.cwd);\n const loaded = loadGoalStateFromSession({ sessionManager: ctx.sessionManager });\n this.runtime.load(loaded);\n this.deactivateTools();\n let goal = this.runtime.snapshot().goal;\n if (loaded.hasExperimentalQueueState && !this.settings.experimental.goals && goal?.status === 'active') {\n goal = transitionGoal({ ...goal }, 'paused');\n this.runtime.replaceState({\n goal,\n queue: [...this.runtime.snapshot().queue],\n pendingAction: this.runtime.snapshot().pendingAction,\n });\n }\n if (goal?.status === 'active') {\n const activated = this.activateTools(goal);\n if (!activated) {\n this.runtime.replaceState({ goal: transitionGoal(goal, 'paused'), queue: [...this.runtime.snapshot().queue] });\n this.notify('Goal paused because host policy does not expose both Goal tools.', 'warning');\n }\n }\n if (activeGeneration !== this.generation) return;\n this.refreshStatus();\n this.emitState();\n }\n\n private createHistory(cwd: string): GoalHistoryPort | undefined {\n try {\n return new GoalHistoryService(createGoalHistoryStore(cwd));\n } catch {\n return undefined;\n }\n }\n\n private async loadSettings(): Promise<typeof DEFAULT_GOAL_SETTINGS> {\n try {\n const fs = await import('node:fs/promises');\n const path = await import('node:path');\n const raw = JSON.parse(await fs.readFile(path.join(getAgentDir(), SETTINGS_FILE), 'utf8')) as unknown;\n return normalizeGoalSettings(raw) ?? DEFAULT_GOAL_SETTINGS;\n } catch {\n return DEFAULT_GOAL_SETTINGS;\n }\n }\n\n private isCurrent(ctx: ExtensionContext): boolean {\n return !this.disposed && this.context === ctx && this.sessionId === ctx.sessionManager.getSessionId();\n }\n\n private enqueue<T>(operation: () => Promise<T>): Promise<T> {\n const run = this.operations.then(operation, operation);\n this.operations = run.then(\n () => undefined,\n () => undefined,\n );\n return run;\n }\n\n private activateTools(goal: ActiveGoal): boolean {\n if (typeof this.pi.getActiveTools !== 'function' || typeof this.pi.setActiveTools !== 'function') return false;\n const previous = this.pi.getActiveTools();\n const result = reconcileGoalTools(this.pi, goal);\n const required = goal.status === 'budget_limited' ? [COMPLETE_TOOL] : [COMPLETE_TOOL, BLOCKED_TOOL];\n const available = required.every((name) => result.activeTools.includes(name));\n if (!available) {\n this.pi.setActiveTools(previous);\n return false;\n }\n this.executionGeneration += 1;\n this.lastToolCallGoalId = undefined;\n this.lastToolCallGeneration = undefined;\n return true;\n }\n\n private deactivateTools(): void {\n if (typeof this.pi.getActiveTools === 'function' && typeof this.pi.setActiveTools === 'function') {\n removeGoalTools(this.pi);\n }\n this.executionGeneration += 1;\n this.lastToolCallGoalId = undefined;\n this.lastToolCallGeneration = undefined;\n }\n\n private fenceExecution(): void {\n this.executionGeneration += 1;\n this.runGoalId = undefined;\n this.runOrigin = undefined;\n this.runExecutionGeneration = undefined;\n this.runToolAttempted = false;\n this.lastToolCallGoalId = undefined;\n this.lastToolCallGeneration = undefined;\n this.compactState = undefined;\n this.budgetWrapUpGoalId = undefined;\n }\n\n private noteToolCall(toolName: string): void {\n if (toolName !== COMPLETE_TOOL && toolName !== BLOCKED_TOOL) return;\n this.runToolAttempted = true;\n const goal = this.runtime.snapshot().goal;\n if (!goal) return;\n this.lastToolCallGoalId = goal.id;\n this.lastToolCallGeneration = this.executionGeneration;\n }\n\n private pauseForToolPolicyDrift(ctx: ExtensionContext, goal: ActiveGoal): void {\n if (!this.isCurrent(ctx) || this.runtime.snapshot().goal?.id !== goal.id || goal.status !== 'active') return;\n updateGoalUsage(goal, ctx, Date.now(), false);\n this.fenceExecution();\n const paused = transitionGoal({ ...goal }, 'paused');\n this.runtime.replaceState({ goal: paused, queue: [...this.runtime.snapshot().queue] });\n this.deactivateTools();\n this.abortCurrentTurn(ctx);\n this.refreshStatus();\n this.emitState('required Goal tool unavailable');\n this.notify(\n 'Goal tools are unavailable, so the Goal was paused. Restore the tools and run /goal resume.',\n 'warning',\n );\n }\n\n private beforeCompact(event: CompactEventLike, ctx: ExtensionContext): undefined {\n if (!this.isCurrent(ctx)) return undefined;\n const goal = this.runtime.snapshot().goal;\n if (!goal || goal.status !== 'active') return undefined;\n this.compactState = {\n goalId: goal.id,\n hadAutomaticRun: this.runOrigin === 'automatic' && this.runGoalId === goal.id,\n willRetry: event.willRetry === true,\n };\n // A compaction invalidates the current turn's leases. The persisted Goal remains\n // authoritative, while a retry/settled boundary may establish a new lease.\n this.executionGeneration += 1;\n this.runGoalId = undefined;\n this.runOrigin = undefined;\n this.runExecutionGeneration = undefined;\n this.lastToolCallGoalId = undefined;\n this.lastToolCallGeneration = undefined;\n return undefined;\n }\n\n private async afterCompact(event: CompactEventLike, ctx: ExtensionContext): Promise<void> {\n if (!this.isCurrent(ctx)) return;\n const compact = this.compactState;\n if (!compact) return;\n const goal = this.runtime.snapshot().goal;\n this.compactState = undefined;\n if (!goal || goal.id !== compact.goalId || goal.status !== 'active') return;\n this.executionGeneration += 1;\n this.runToolAttempted = false;\n // Overflow retries belong to Pi's current run and must not receive a duplicate\n // continuation. Manual/threshold compaction keeps one guarded recovery marker.\n if (!compact.willRetry && compact.hadAutomaticRun && event.willRetry !== true) {\n this.pendingRunOrigin = 'automatic';\n this.runGoalId = goal.id;\n this.runOrigin = 'automatic';\n this.runExecutionGeneration = this.executionGeneration;\n }\n }\n\n private refreshStatus(): void {\n const ctx = this.context;\n const snapshot = this.runtime.snapshot();\n if (!ctx?.hasUI) return;\n const queueWaiting = snapshot.pendingAction !== undefined || (!snapshot.goal && snapshot.queue.length > 0);\n ctx.ui.setStatus(STATUS_KEY, queueWaiting ? 'queued' : formatStatus(snapshot.goal));\n }\n\n private emitState(reason?: string, summary?: string): void {\n const goal = this.runtime.snapshot().goal;\n const status = goal?.status ?? 'cleared';\n this.lastGoalId = goal?.id ?? this.lastGoalId;\n (this.pi.events as EventBus).emit(STATE_EVENT, {\n goalId: goal?.id ?? this.lastGoalId ?? '',\n status,\n ...(reason ? { reason } : {}),\n ...(summary ? { summary } : {}),\n });\n }\n\n private notify(message: string, level: 'info' | 'warning' | 'error' = 'info'): void {\n if (this.context?.hasUI) this.context.ui.notify(message, level);\n }\n\n private abortCurrentTurn(ctx: ExtensionContext): boolean {\n try {\n ctx.abort();\n return true;\n } catch {\n return false;\n }\n }\n\n private async executeCommand(args: string, ctx: ExtensionCommandContext): Promise<void> {\n const hasSessionManager = Object.hasOwn(ctx, 'sessionManager');\n if (!args.trim() && this.legacyCommandService && !hasSessionManager) {\n const result = await this.legacyCommandService.execute();\n if (ctx.hasUI) ctx.ui.notify(result.message, result.level);\n return;\n }\n if (!this.isCurrent(ctx)) {\n await this.startSession(ctx);\n }\n const parsed = parseGoalCommand(args, { experimentalGoals: this.settings.experimental.goals });\n if (typeof parsed === 'string') {\n this.notify(parsed, 'warning');\n return;\n }\n switch (parsed.kind) {\n case 'show':\n this.showStatus();\n return;\n case 'start':\n await this.startGoal(parsed.objective ?? '', parsed.tokenBudget, ctx);\n return;\n case 'pause':\n this.pauseGoal(ctx);\n return;\n case 'resume':\n await this.resumeGoal(ctx);\n return;\n case 'clear':\n await this.clearGoal(ctx);\n return;\n case 'edit':\n await this.editGoal(parsed.objective ?? '', parsed.tokenBudget, ctx);\n return;\n case 'add':\n await this.addGoal(parsed.objective ?? '', parsed.tokenBudget, ctx);\n return;\n case 'prioritize':\n await this.prioritizeGoal(parsed.objective ?? '', parsed.tokenBudget, ctx);\n return;\n case 'drop-last':\n this.dropLastGoal(ctx);\n return;\n case 'skip':\n await this.skipGoal(ctx);\n return;\n }\n }\n\n private showStatus(): void {\n const snapshot = this.runtime.snapshot();\n const goal = snapshot.goal;\n if (!goal) {\n this.notify('No active goal.', 'info');\n return;\n }\n const queue = snapshot.queue.length ? `\\nQueued: ${snapshot.queue.map((item) => item.text).join(', ')}` : '';\n this.notify(`Goal: ${goal.text}\\nStatus: ${goal.status}${queue}`, 'info');\n }\n\n private async startGoal(\n objective: string,\n budget: number | undefined,\n ctx: ExtensionCommandContext,\n goalId?: string,\n ): Promise<void> {\n const validation = validateObjective(objective);\n if (validation) {\n this.notify(validation, 'warning');\n return;\n }\n const previous = this.runtime.snapshot();\n const oldGoal = previous.goal && previous.goal.status !== 'complete' ? previous.goal : undefined;\n if (oldGoal) {\n const accepted = await ctx.ui.confirm('Replace goal?', `Current goal: ${oldGoal.text}\\n\\nNew goal: ${objective}`);\n if (!accepted) return;\n if (!(await this.archiveAll([oldGoal, ...previous.queue], 'replaced'))) {\n this.notify('Goal replacement aborted because history archival failed.', 'error');\n return;\n }\n }\n const next = createGoal(objective, budget, { id: goalId, baselineTokens: currentTokenTotal(ctx) });\n if (!this.activateTools(next)) {\n this.notify('Cannot start /goal: host policy rejected Goal tools.', 'error');\n return;\n }\n this.runtime.replaceState({ goal: next, queue: [] });\n this.budgetWrapUpGoalId = undefined;\n this.pendingRunOrigin = 'manual';\n this.refreshStatus();\n this.emitState();\n try {\n this.pi.sendUserMessage(buildGoalPrompt(next), { deliverAs: 'followUp' });\n } catch (error) {\n this.runtime.replaceState({ goal: oldGoal, queue: previous.queue });\n this.fenceExecution();\n if (!oldGoal) this.deactivateTools();\n this.refreshStatus();\n this.notify(`Goal kickoff failed: ${errorText(error)}`, 'error');\n return;\n }\n this.notify(oldGoal ? `Goal replaced: ${objective}` : `Goal started: ${objective}`);\n }\n\n private pauseGoal(ctx: ExtensionContext): void {\n const goal = this.runtime.snapshot().goal;\n if (!goal) {\n this.notify('No active goal.');\n return;\n }\n if (goal.status !== 'active') {\n this.notify(`Goal is ${goal.status}; only active goals can be paused.`, 'warning');\n return;\n }\n updateGoalUsage(goal, ctx, Date.now(), false);\n this.fenceExecution();\n const next = transitionGoal({ ...goal }, 'paused');\n this.runtime.replaceState({ goal: next, queue: [...this.runtime.snapshot().queue] });\n this.deactivateTools();\n ctx.abort();\n this.refreshStatus();\n this.emitState('paused');\n this.notify(`Goal paused: ${goal.text}`);\n }\n\n private async resumeGoal(ctx: ExtensionContext): Promise<void> {\n const goal = this.runtime.snapshot().goal;\n if (!goal) {\n this.notify('No active goal.');\n return;\n }\n if (!isResumableGoalStatus(goal.status)) {\n this.notify(`Goal is ${goal.status}; it cannot be resumed.`, 'warning');\n return;\n }\n void ctx;\n if (goal.tokenBudget !== undefined && goal.tokensUsed >= goal.tokenBudget) {\n this.notify('Goal token budget is still reached.', 'warning');\n return;\n }\n const next = resetGoalSafetyEpoch(transitionGoal({ ...goal, updatedAt: Date.now() }, 'active'));\n if (!this.activateTools(next)) {\n this.notify('Cannot resume /goal: host policy rejected Goal tools.', 'error');\n return;\n }\n this.runtime.replaceState({ goal: next, queue: [...this.runtime.snapshot().queue] });\n this.budgetWrapUpGoalId = undefined;\n this.pendingRunOrigin = 'manual';\n this.refreshStatus();\n this.emitState();\n try {\n this.pi.sendUserMessage(buildResumePrompt(next, goal.status), { deliverAs: 'followUp' });\n } catch (error) {\n this.fenceExecution();\n this.runtime.replaceState({ goal, queue: [...this.runtime.snapshot().queue] });\n this.deactivateTools();\n this.refreshStatus();\n this.notify(`Goal resume failed: ${errorText(error)}`, 'error');\n }\n }\n\n private async clearGoal(ctx: ExtensionContext): Promise<void> {\n const snapshot = this.runtime.snapshot();\n if (!(await this.archiveAll([...(snapshot.goal ? [snapshot.goal] : []), ...snapshot.queue], 'cleared'))) {\n this.notify('Goal clear aborted because history archival failed.', 'error');\n return;\n }\n this.fenceExecution();\n this.runtime.clear();\n this.deactivateTools();\n ctx.abort();\n this.refreshStatus();\n this.emitState('cleared');\n if (snapshot.goal) this.notify(`Goal cleared: ${snapshot.goal.text}`, 'warning');\n else this.notify('No active goal.', 'info');\n }\n\n private async editGoal(objective: string, budget: number | undefined, ctx: ExtensionCommandContext): Promise<void> {\n const validation = validateObjective(objective);\n if (validation) {\n this.notify(validation, 'warning');\n return;\n }\n const goal = this.runtime.snapshot().goal;\n if (!goal) {\n this.notify('No active goal. Use /goal <objective> to start one.', 'warning');\n return;\n }\n const next = {\n ...goal,\n text: objective,\n ...(budget === undefined ? {} : { tokenBudget: budget }),\n updatedAt: Date.now(),\n };\n this.runtime.replaceState({ goal: next, queue: [...this.runtime.snapshot().queue] });\n this.refreshStatus();\n this.emitState();\n if (goal.status === 'active') this.pi.sendUserMessage(buildObjectiveUpdatedPrompt(next), { deliverAs: 'followUp' });\n else this.notify(`Goal updated but remains ${goal.status}. Resume explicitly to execute it.`);\n void ctx;\n }\n\n private async addGoal(objective: string, budget: number | undefined, _ctx: ExtensionContext): Promise<void> {\n const validation = validateObjective(objective);\n if (validation) {\n this.notify(validation, 'warning');\n return;\n }\n const snapshot = this.runtime.snapshot();\n if (!snapshot.goal) {\n await this.startGoal(objective, budget, this.context as ExtensionCommandContext);\n return;\n }\n const state = enqueueGoal(\n {\n goal: snapshot.goal,\n queue: [...snapshot.queue],\n pendingAction: snapshot.pendingAction,\n enabled: this.settings.experimental.goals,\n },\n objective,\n budget,\n );\n this.runtime.replaceState(state);\n this.refreshStatus();\n this.emitState();\n this.notify(`Goal added at position ${state.queue.length + 1}: ${objective}`);\n }\n\n private async prioritizeGoal(objective: string, budget: number | undefined, _ctx: ExtensionContext): Promise<void> {\n const validation = validateObjective(objective);\n if (validation) {\n this.notify(validation, 'warning');\n return;\n }\n const snapshot = this.runtime.snapshot();\n if (!snapshot.goal) {\n await this.startGoal(objective, budget, this.context as ExtensionCommandContext);\n return;\n }\n this.runtime.replaceState({\n goal: snapshot.goal,\n queue: [...snapshot.queue],\n pendingAction: { kind: 'prioritize', objective, tokenBudget: budget },\n });\n this.notify(`Priority goal queued until the current turn settles: ${objective}`);\n }\n\n private dropLastGoal(_ctx: ExtensionContext): void {\n const snapshot = this.runtime.snapshot();\n if (!snapshot.queue.length) {\n this.notify('No queued goals.', 'info');\n return;\n }\n this.runtime.replaceState({\n goal: snapshot.goal,\n queue: [...snapshot.queue].slice(0, -1),\n pendingAction: snapshot.pendingAction,\n });\n this.notify('Last queued goal dropped.', 'warning');\n }\n\n private async skipGoal(ctx: ExtensionContext): Promise<void> {\n const snapshot = this.runtime.snapshot();\n if (!snapshot.goal) {\n this.notify('No goals to skip.', 'info');\n return;\n }\n if (!(await this.archive(snapshot.goal, 'skipped'))) {\n this.notify('Goal skip aborted because history archival failed.', 'error');\n return;\n }\n this.fenceExecution();\n const nextState = skipCurrentGoal({\n goal: snapshot.goal,\n queue: [...snapshot.queue],\n pendingAction: snapshot.pendingAction,\n enabled: this.settings.experimental.goals,\n });\n if (nextState.pendingAction) this.runtime.replaceState(nextState);\n else this.runtime.clear();\n this.deactivateTools();\n ctx.abort();\n this.refreshStatus();\n this.emitState('skipped');\n }\n\n private async finishAgentRun(event: AgentEndEventLike, ctx: ExtensionContext): Promise<void> {\n if (!this.isCurrent(ctx)) return;\n const snapshot = this.runtime.snapshot();\n const goal = snapshot.goal;\n if (!goal || goal.status !== 'active') return;\n if (this.runGoalId !== goal.id || this.runExecutionGeneration !== this.executionGeneration) {\n this.fenceExecution();\n return;\n }\n\n const messages = event.messages ?? [];\n const failure = classifyAgentFailure(messages);\n updateGoalUsage(goal, ctx, Date.now(), true);\n if (failure === 'aborted') {\n // User-initiated aborts and safety aborts are fenced without inventing a\n // provider failure. A later explicit resume establishes a new execution lease.\n this.fenceExecution();\n this.runtime.replaceState({ goal, queue: [...snapshot.queue] });\n this.refreshStatus();\n return;\n }\n if (failure === 'usage_limited' || failure === 'blocked') {\n this.fenceExecution();\n const stopped = transitionGoal({ ...goal }, failure);\n this.runtime.replaceState({ goal: stopped, queue: [...snapshot.queue] });\n this.deactivateTools();\n this.abortCurrentTurn(ctx);\n this.refreshStatus();\n this.emitState(failure === 'usage_limited' ? 'provider usage limit' : 'provider error');\n this.notify(\n failure === 'usage_limited'\n ? 'Goal paused because the provider usage limit was reached. Run /goal resume after it resets.'\n : 'Goal blocked because the provider reported a terminal error. Resolve it or run /goal resume.',\n 'warning',\n );\n return;\n }\n\n if (this.runOrigin === 'automatic') {\n goal.automaticModelTurns = Math.min(Number.MAX_SAFE_INTEGER, goal.automaticModelTurns + 1);\n const progress = nextToolFreeRepeatState(goal, messages, this.runToolAttempted);\n goal.toolFreeRepeatCount = progress.toolFreeRepeatCount;\n goal.lastToolFreeOutputFingerprint = progress.lastToolFreeOutputFingerprint;\n const cause = safetyLimitReached(goal, this.settings.continuationLimits);\n if (cause) {\n this.pauseForSafety(ctx, goal, cause);\n return;\n }\n }\n\n if (goal.tokenBudget !== undefined && goal.tokensUsed >= goal.tokenBudget) {\n this.limitForBudget(ctx, goal, snapshot.queue);\n return;\n }\n this.runtime.replaceState({ goal, queue: [...snapshot.queue] });\n this.refreshStatus();\n }\n\n private async dispatchPendingQueueAction(ctx: ExtensionContext): Promise<boolean> {\n if (!this.isCurrent(ctx)) return false;\n const snapshot = this.runtime.snapshot();\n const pending = snapshot.pendingAction;\n if (!pending) return false;\n if (!this.settings.experimental.goals || !ctx.isIdle() || ctx.hasPendingMessages()) return true;\n\n if (pending.kind === 'prioritize') {\n const current = snapshot.goal;\n if (!current) {\n this.runtime.replaceState({ queue: [...snapshot.queue] });\n await this.startGoal(pending.objective, pending.tokenBudget, ctx as ExtensionCommandContext);\n return true;\n }\n if (current.status === 'active') updateGoalUsage(current, ctx, Date.now(), false);\n const prioritized = createGoal(pending.objective, pending.tokenBudget, {\n baselineTokens: currentTokenTotal(ctx),\n });\n const displacedQueue =\n current.status === 'complete'\n ? [...snapshot.queue]\n : [transitionGoal({ ...current, activeStartedAt: undefined }, 'queued'), ...snapshot.queue];\n this.deactivateTools();\n if (!this.activateTools(prioritized)) {\n const paused = transitionGoal(prioritized, 'paused');\n this.runtime.replaceState({ goal: paused, queue: displacedQueue });\n this.deactivateTools();\n this.refreshStatus();\n this.emitState('queued goal tools unavailable');\n this.notify('Priority Goal is retained but paused because host policy rejected Goal tools.', 'warning');\n return true;\n }\n this.runtime.replaceState({ goal: prioritized, queue: displacedQueue });\n this.pendingRunOrigin = 'manual';\n this.refreshStatus();\n this.emitState('prioritized');\n try {\n this.pi.sendUserMessage(buildGoalPrompt(prioritized), { deliverAs: 'followUp' });\n } catch (error) {\n this.pauseForDeliveryFailure(ctx, prioritized, `Priority Goal kickoff failed: ${errorText(error)}`);\n }\n return true;\n }\n\n const current = snapshot.goal;\n if (!current || current.id !== pending.goalId || (pending.reason === 'complete' && current.status !== 'complete')) {\n this.runtime.replaceState({ goal: current, queue: [...snapshot.queue] });\n this.refreshStatus();\n return true;\n }\n const promoted = promoteNextGoal(\n { goal: current, queue: [...snapshot.queue], pendingAction: pending, enabled: true },\n Date.now(),\n currentTokenTotal(ctx),\n );\n const next = promoted.goal;\n if (!next || next.id === current.id) {\n this.runtime.clear();\n this.deactivateTools();\n this.refreshStatus();\n this.emitState(pending.reason);\n return true;\n }\n if (!this.activateTools(next)) {\n const paused = transitionGoal(next, 'paused');\n this.runtime.replaceState({ goal: paused, queue: [...promoted.queue] });\n this.deactivateTools();\n this.refreshStatus();\n this.emitState('queued goal tools unavailable');\n this.notify('Next Goal is retained but paused because host policy rejected Goal tools.', 'warning');\n return true;\n }\n this.runtime.replaceState({ goal: next, queue: [...promoted.queue] });\n this.pendingRunOrigin = 'manual';\n this.refreshStatus();\n this.emitState(pending.reason);\n try {\n this.pi.sendUserMessage(buildGoalPrompt(next), { deliverAs: 'followUp' });\n } catch (error) {\n this.pauseForDeliveryFailure(ctx, next, `Queued Goal kickoff failed: ${errorText(error)}`);\n }\n return true;\n }\n\n private async continueAfterSettled(ctx: ExtensionContext): Promise<void> {\n if (!this.isCurrent(ctx) || !ctx.isIdle() || ctx.hasPendingMessages()) return;\n const snapshot = this.runtime.snapshot();\n const goal = snapshot.goal;\n if (!goal || goal.status !== 'active' || !this.runGoalId || this.runGoalId !== goal.id) return;\n if (this.runExecutionGeneration !== this.executionGeneration) {\n this.fenceExecution();\n return;\n }\n if (!canExecuteGoalTools(this.pi, goal)) {\n this.pauseForToolPolicyDrift(ctx, goal);\n return;\n }\n const cause = safetyLimitReached(goal, this.settings.continuationLimits);\n if (cause) {\n this.pauseForSafety(ctx, goal, cause);\n return;\n }\n const next = incrementGoal({ ...goal });\n this.runtime.replaceState({ goal: next, queue: [...snapshot.queue] });\n const continuation = buildContinuePrompt(next);\n this.pendingRunOrigin = 'automatic';\n try {\n this.pi.sendUserMessage(continuation, { deliverAs: 'followUp' });\n } catch (error) {\n this.pauseForDeliveryFailure(ctx, next, `Goal continuation failed: ${errorText(error)}`);\n return;\n }\n this.runGoalId = undefined;\n this.runOrigin = undefined;\n this.runExecutionGeneration = undefined;\n this.runToolAttempted = false;\n }\n\n private pauseForSafety(ctx: ExtensionContext, goal: ActiveGoal, cause: 'continuation_limit' | 'no_progress'): void {\n if (this.runtime.snapshot().goal?.id !== goal.id) return;\n this.fenceExecution();\n const paused = transitionGoal({ ...goal, safetyPauseCause: cause }, 'paused');\n this.runtime.replaceState({ goal: paused, queue: [...this.runtime.snapshot().queue] });\n this.deactivateTools();\n this.abortCurrentTurn(ctx);\n this.refreshStatus();\n this.emitState(cause === 'continuation_limit' ? 'automatic response limit' : 'no progress');\n const detail = cause === 'continuation_limit' ? 'automatic response limit' : 'no progress';\n this.notify(`Goal paused by safety limit: ${detail}. Run /goal resume to continue.`, 'warning');\n }\n\n private limitForBudget(ctx: ExtensionContext, goal: ActiveGoal, queue: readonly ActiveGoal[]): void {\n this.fenceExecution();\n const limited = transitionGoal({ ...goal }, 'budget_limited');\n this.runtime.replaceState({ goal: limited, queue: [...queue] });\n this.deactivateTools();\n const canWrap = this.activateTools(limited);\n if (canWrap && this.budgetWrapUpGoalId !== limited.id) {\n this.budgetWrapUpGoalId = limited.id;\n try {\n this.pi.sendMessage(\n {\n customType: 'goal-budget-wrap-up',\n content:\n 'The Goal token budget is exhausted. Stop substantive work, summarize verified progress and blockers, and call goal_complete only if every requirement is already proven.',\n display: true,\n details: { goalId: limited.id },\n },\n { deliverAs: 'steer' },\n );\n } catch (error) {\n this.budgetWrapUpGoalId = undefined;\n this.notify(`Goal budget wrap-up failed: ${errorText(error)}`, 'error');\n }\n }\n if (!canWrap) this.notify('Goal budget reached, but host policy rejected goal_complete.', 'warning');\n this.abortCurrentTurn(ctx);\n this.refreshStatus();\n this.emitState('token budget reached');\n }\n\n private pauseForDeliveryFailure(ctx: ExtensionContext, goal: ActiveGoal, message: string): void {\n this.fenceExecution();\n const paused = transitionGoal({ ...goal }, 'paused');\n this.runtime.replaceState({ goal: paused, queue: [...this.runtime.snapshot().queue] });\n this.deactivateTools();\n this.abortCurrentTurn(ctx);\n this.refreshStatus();\n this.emitState('delivery failed');\n this.notify(message, 'error');\n }\n\n private async complete(params: CompleteInput, ctx: ExtensionContext): Promise<ToolResult> {\n const snapshot = this.runtime.snapshot();\n const goal = snapshot.goal;\n if (!goal || !this.acceptsToolCall(ctx, goal))\n return messageResult('Goal completion rejected because this tool call belongs to a stale Goal execution.', true);\n const validation = validateCompletionInput(goal, params as { goal_id?: string; summary?: string });\n if (!goal || (goal.status !== 'active' && goal.status !== 'budget_limited'))\n return messageResult('Goal completion is not available in the current state.', true);\n if (!validation.ok) return messageResult(validation.reason ?? 'Goal completion rejected.', true);\n const summary = typeof params.summary === 'string' ? params.summary.trim() : '';\n if (isContradictoryCompletionSummary(summary))\n return messageResult('Completion evidence contradicts completion.', true);\n if (!(await this.archive(goal, 'complete', summary)))\n return messageResult('Completion aborted because history archival failed.', true);\n this.fenceExecution();\n const completed = transitionGoal({ ...goal }, 'complete');\n if (snapshot.queue.length && this.settings.experimental.goals) {\n this.runtime.replaceState({\n goal: completed,\n queue: [...snapshot.queue],\n pendingAction: { kind: 'advance', goalId: goal.id, reason: 'complete', completedText: goal.text },\n });\n } else {\n this.runtime.clear();\n }\n this.deactivateTools();\n ctx.abort();\n this.refreshStatus();\n this.emitState(undefined, summary);\n return messageResult(`Goal complete: ${goal.text}`);\n }\n\n private async blocked(params: BlockedInput, ctx: ExtensionContext): Promise<ToolResult> {\n const snapshot = this.runtime.snapshot();\n const goal = snapshot.goal;\n if (!goal || !this.acceptsToolCall(ctx, goal))\n return messageResult('Goal blocker rejected because this tool call belongs to a stale Goal execution.', true);\n const validation = validateBlockedInput(\n goal,\n params as { goal_id?: string; reason?: string; evidence?: string; repeated_turns?: number },\n );\n if (!goal || goal.status !== 'active')\n return messageResult('Goal blocking is not available in the current state.', true);\n if (!validation.ok) return messageResult(validation.reason ?? 'Goal blocker rejected.', true);\n const reason = typeof params.reason === 'string' ? params.reason.trim() : 'blocked';\n this.fenceExecution();\n const blocked = transitionGoal({ ...goal }, 'blocked');\n this.runtime.replaceState({ goal: blocked, queue: [...snapshot.queue] });\n this.deactivateTools();\n ctx.abort();\n this.refreshStatus();\n this.emitState(reason);\n return messageResult(`Goal blocked: ${reason}`);\n }\n\n private acceptsToolCall(ctx: ExtensionContext, goal: ActiveGoal): boolean {\n if (!this.isCurrent(ctx)) return false;\n if (this.runGoalId && (this.runGoalId !== goal.id || this.runExecutionGeneration !== this.executionGeneration))\n return false;\n if (\n this.lastToolCallGoalId &&\n (this.lastToolCallGoalId !== goal.id || this.lastToolCallGeneration !== this.executionGeneration)\n )\n return false;\n return canExecuteGoalTools(this.pi, goal);\n }\n\n private async handleRpcStart(data: unknown): Promise<void> {\n const payload = asRecord(data);\n const requestId = typeof payload?.requestId === 'string' ? payload.requestId.trim() : '';\n const events = this.pi.events as EventBus;\n if (!requestId) return;\n const reply = (value: unknown): void => events.emit(`pi-goal:rpc:start:reply:${requestId}`, value);\n const objective = typeof payload?.objective === 'string' ? payload.objective.trim() : '';\n const validation = validateObjective(objective);\n if (validation) {\n reply({ success: false, error: validation });\n return;\n }\n if (this.runtime.snapshot().goal) {\n reply({ success: false, error: 'a goal already exists' });\n return;\n }\n const ctx = this.context;\n if (!ctx) {\n reply({ success: false, error: 'no active pi-goal session context' });\n return;\n }\n try {\n await this.startGoal(\n objective,\n typeof payload?.tokenBudget === 'number' ? payload.tokenBudget : undefined,\n ctx as ExtensionCommandContext,\n );\n const goal = this.runtime.snapshot().goal;\n if (!goal) reply({ success: false, error: 'goal could not be activated' });\n else reply({ success: true, data: { goalId: goal.id, status: goal.status } });\n } catch (error) {\n reply({ success: false, error: `goal start failed: ${errorText(error)}` });\n }\n }\n\n private async handleRpcPause(data: unknown): Promise<void> {\n const payload = asRecord(data);\n const goal = this.runtime.snapshot().goal;\n if (!goal || goal.status !== 'active') return;\n const requestGoalId = typeof payload?.goalId === 'string' ? payload.goalId : undefined;\n if (requestGoalId && requestGoalId !== goal.id) return;\n if (this.context) this.pauseGoal(this.context);\n }\n\n private async archiveAll(goals: readonly ActiveGoal[], reason: string): Promise<boolean> {\n const seen = new Set<string>();\n for (const goal of goals) {\n if (seen.has(goal.id)) continue;\n seen.add(goal.id);\n if (!(await this.archive(goal, reason))) return false;\n }\n return true;\n }\n\n private async archive(goal: ActiveGoal, reason: string, summary?: string): Promise<boolean> {\n if (!this.history) return true;\n try {\n await this.history.archive({\n id: goal.id,\n objective: goal.text,\n status: reason === 'complete' ? 'complete' : goal.status,\n ...(summary ? { reason: summary } : { reason }),\n ...(goal.tokenBudget === undefined ? {} : { budget: goal.tokenBudget }),\n archivedAt: new Date().toISOString(),\n startedAt: new Date(goal.startedAt).toISOString(),\n ...(reason === 'complete' ? { completedAt: new Date().toISOString() } : {}),\n });\n return true;\n } catch (error) {\n this.notify(`Goal history archival failed: ${errorText(error)}`, 'error');\n return false;\n }\n }\n\n private dispose(): void {\n if (this.disposed) return;\n this.disposed = true;\n this.generation += 1;\n for (const dispose of this.disposers.splice(0)) dispose();\n this.deactivateTools();\n this.context = undefined;\n }\n}\n\nfunction classifyAgentFailure(messages: readonly unknown[]): 'usage_limited' | 'blocked' | 'aborted' | undefined {\n const assistant = [...messages].reverse().find((message) => isAssistantMessage(message));\n if (!assistant || typeof assistant !== 'object') return undefined;\n const candidate = assistant as { stopReason?: unknown; errorMessage?: unknown; content?: unknown; text?: unknown };\n if (candidate.stopReason === 'aborted') return 'aborted';\n if (candidate.stopReason !== 'error' && candidate.stopReason !== 'length' && candidate.errorMessage === undefined)\n return undefined;\n const details = [candidate.errorMessage, extractMessageText(candidate)]\n .filter((value): value is string => typeof value === 'string')\n .join(' ')\n .toLowerCase();\n if (\n candidate.stopReason === 'length' ||\n /(?:rate|quota|usage|token|context|capacity|limit|429|too many|billing|exhaust)/u.test(details)\n )\n return 'usage_limited';\n return 'blocked';\n}\n\nfunction isAssistantMessage(value: unknown): boolean {\n return Boolean(value && typeof value === 'object' && (value as { role?: unknown }).role === 'assistant');\n}\n\nfunction extractMessageText(value: { content?: unknown; text?: unknown }): string {\n if (typeof value.text === 'string') return value.text;\n if (typeof value.content === 'string') return value.content;\n if (!Array.isArray(value.content)) return '';\n return value.content\n .map((part) => {\n if (!part || typeof part !== 'object') return '';\n const text = (part as { text?: unknown }).text;\n return typeof text === 'string' ? text : '';\n })\n .join(' ');\n}\n"],"mappings":"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"}
@@ -0,0 +1,86 @@
1
+ import { GoalHistoryEntry } from "../../types/history.cjs";
2
+ import { GoalExtensionDependencies, GoalExtensionService } from "../../types/extension.cjs";
3
+ import { GoalRuntimeSnapshot } from "../../types/goal.cjs";
4
+ import { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
5
+ //#region src/adapters/pi/goalManager.d.ts
6
+ declare class GoalPiManager {
7
+ private readonly pi;
8
+ private readonly runtime;
9
+ private readonly dependencies?;
10
+ private readonly legacyCommandService?;
11
+ private context?;
12
+ private sessionId?;
13
+ private generation;
14
+ private operations;
15
+ private settings;
16
+ private history?;
17
+ private runGoalId?;
18
+ private runOrigin?;
19
+ private runExecutionGeneration?;
20
+ private pendingRunOrigin;
21
+ private lastToolCallGoalId?;
22
+ private lastToolCallGeneration?;
23
+ private executionGeneration;
24
+ private compactState?;
25
+ private budgetWrapUpGoalId?;
26
+ private lastGoalId?;
27
+ private runToolAttempted;
28
+ private disposed;
29
+ private readonly disposers;
30
+ constructor(pi: ExtensionAPI, dependencies?: GoalExtensionDependencies, legacyCommandService?: GoalExtensionService);
31
+ snapshot(): GoalRuntimeSnapshot;
32
+ startFromLeader(ctx: ExtensionContext): Promise<void>;
33
+ endFromLeader(ctx: ExtensionContext): Promise<void>;
34
+ showFromLeader(ctx: ExtensionContext): Promise<void>;
35
+ listHistory(ctx: ExtensionContext): Promise<GoalHistoryEntry[]>;
36
+ restartFromHistory(id: string, ctx: ExtensionContext): Promise<void>;
37
+ removeHistory(id: string, ctx: ExtensionContext): Promise<void>;
38
+ private ensureSession;
39
+ register(): () => void;
40
+ private registerTools;
41
+ private registerRpc;
42
+ private startSession;
43
+ private createHistory;
44
+ private loadSettings;
45
+ private isCurrent;
46
+ private enqueue;
47
+ private activateTools;
48
+ private deactivateTools;
49
+ private fenceExecution;
50
+ private noteToolCall;
51
+ private pauseForToolPolicyDrift;
52
+ private beforeCompact;
53
+ private afterCompact;
54
+ private refreshStatus;
55
+ private emitState;
56
+ private notify;
57
+ private abortCurrentTurn;
58
+ private executeCommand;
59
+ private showStatus;
60
+ private startGoal;
61
+ private pauseGoal;
62
+ private resumeGoal;
63
+ private clearGoal;
64
+ private editGoal;
65
+ private addGoal;
66
+ private prioritizeGoal;
67
+ private dropLastGoal;
68
+ private skipGoal;
69
+ private finishAgentRun;
70
+ private dispatchPendingQueueAction;
71
+ private continueAfterSettled;
72
+ private pauseForSafety;
73
+ private limitForBudget;
74
+ private pauseForDeliveryFailure;
75
+ private complete;
76
+ private blocked;
77
+ private acceptsToolCall;
78
+ private handleRpcStart;
79
+ private handleRpcPause;
80
+ private archiveAll;
81
+ private archive;
82
+ private dispose;
83
+ }
84
+ //#endregion
85
+ export { GoalPiManager };
86
+ //# sourceMappingURL=goalManager.d.cts.map