@namzu/sdk 61.3.0 → 62.0.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 (307) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/dist/agents/QueryAgent.d.ts.map +1 -1
  3. package/dist/agents/QueryAgent.js +2 -0
  4. package/dist/agents/QueryAgent.js.map +1 -1
  5. package/dist/bridge/acp/permission.d.ts +8 -0
  6. package/dist/bridge/acp/permission.d.ts.map +1 -1
  7. package/dist/bridge/acp/permission.js.map +1 -1
  8. package/dist/bridge/acp/server.d.ts.map +1 -1
  9. package/dist/bridge/acp/server.js +8 -1
  10. package/dist/bridge/acp/server.js.map +1 -1
  11. package/dist/constants/tools/index.d.ts +2 -0
  12. package/dist/constants/tools/index.d.ts.map +1 -1
  13. package/dist/constants/tools/index.js +23 -0
  14. package/dist/constants/tools/index.js.map +1 -1
  15. package/dist/eval/harness-protection.js +1 -1
  16. package/dist/eval/harness-protection.js.map +1 -1
  17. package/dist/learning/cycle.d.ts +5 -3
  18. package/dist/learning/cycle.d.ts.map +1 -1
  19. package/dist/learning/cycle.js +11 -13
  20. package/dist/learning/cycle.js.map +1 -1
  21. package/dist/learning/guidance.d.ts +74 -0
  22. package/dist/learning/guidance.d.ts.map +1 -0
  23. package/dist/learning/guidance.js +259 -0
  24. package/dist/learning/guidance.js.map +1 -0
  25. package/dist/learning/skill.d.ts +2 -2
  26. package/dist/learning/skill.d.ts.map +1 -1
  27. package/dist/learning/skill.js +6 -6
  28. package/dist/learning/skill.js.map +1 -1
  29. package/dist/learning/store.d.ts.map +1 -1
  30. package/dist/learning/store.js +6 -6
  31. package/dist/learning/store.js.map +1 -1
  32. package/dist/pals/communication/ingress-schema.d.ts +8 -8
  33. package/dist/pals/communication/schema.d.ts +2 -2
  34. package/dist/peers/client.d.ts +2 -3
  35. package/dist/peers/client.d.ts.map +1 -1
  36. package/dist/peers/client.js +2 -3
  37. package/dist/peers/client.js.map +1 -1
  38. package/dist/peers/endpoint.d.ts +1 -2
  39. package/dist/peers/endpoint.d.ts.map +1 -1
  40. package/dist/peers/endpoint.js +1 -2
  41. package/dist/peers/endpoint.js.map +1 -1
  42. package/dist/peers/protocol.d.ts.map +1 -1
  43. package/dist/peers/protocol.js +1 -0
  44. package/dist/peers/protocol.js.map +1 -1
  45. package/dist/prompt/coding-agent-doctrine.d.ts +4 -4
  46. package/dist/prompt/coding-agent-doctrine.d.ts.map +1 -1
  47. package/dist/prompt/coding-agent-doctrine.js +5 -3
  48. package/dist/prompt/coding-agent-doctrine.js.map +1 -1
  49. package/dist/prompt/index.d.ts +0 -4
  50. package/dist/prompt/index.d.ts.map +1 -1
  51. package/dist/prompt/index.js +0 -2
  52. package/dist/prompt/index.js.map +1 -1
  53. package/dist/provider/fallback.d.ts.map +1 -1
  54. package/dist/provider/fallback.js +3 -0
  55. package/dist/provider/fallback.js.map +1 -1
  56. package/dist/provider/idle-timeout.d.ts.map +1 -1
  57. package/dist/provider/idle-timeout.js +5 -0
  58. package/dist/provider/idle-timeout.js.map +1 -1
  59. package/dist/provider/model-window-suffix.d.ts.map +1 -1
  60. package/dist/provider/model-window-suffix.js +1 -0
  61. package/dist/provider/model-window-suffix.js.map +1 -1
  62. package/dist/provider/retry.d.ts.map +1 -1
  63. package/dist/provider/retry.js +5 -0
  64. package/dist/provider/retry.js.map +1 -1
  65. package/dist/provider/step-stopped.d.ts +19 -0
  66. package/dist/provider/step-stopped.d.ts.map +1 -0
  67. package/dist/provider/step-stopped.js +25 -0
  68. package/dist/provider/step-stopped.js.map +1 -0
  69. package/dist/provider/token-budget.d.ts.map +1 -1
  70. package/dist/provider/token-budget.js +6 -1
  71. package/dist/provider/token-budget.js.map +1 -1
  72. package/dist/public-runtime.d.ts +4 -15
  73. package/dist/public-runtime.d.ts.map +1 -1
  74. package/dist/public-runtime.js +3 -14
  75. package/dist/public-runtime.js.map +1 -1
  76. package/dist/public-tools.d.ts +0 -2
  77. package/dist/public-tools.d.ts.map +1 -1
  78. package/dist/public-tools.js +0 -2
  79. package/dist/public-tools.js.map +1 -1
  80. package/dist/public-types.d.ts +3 -21
  81. package/dist/public-types.d.ts.map +1 -1
  82. package/dist/runtime/query/iteration/index.d.ts +17 -0
  83. package/dist/runtime/query/iteration/index.d.ts.map +1 -1
  84. package/dist/runtime/query/iteration/index.js +168 -5
  85. package/dist/runtime/query/iteration/index.js.map +1 -1
  86. package/dist/runtime/query/iteration/phases/per-call-review.d.ts.map +1 -1
  87. package/dist/runtime/query/iteration/phases/per-call-review.js +4 -5
  88. package/dist/runtime/query/iteration/phases/per-call-review.js.map +1 -1
  89. package/dist/runtime/query/iteration/phases/tool-review.d.ts.map +1 -1
  90. package/dist/runtime/query/iteration/phases/tool-review.js +6 -3
  91. package/dist/runtime/query/iteration/phases/tool-review.js.map +1 -1
  92. package/dist/runtime/query/iteration/stream-turn.d.ts +9 -1
  93. package/dist/runtime/query/iteration/stream-turn.d.ts.map +1 -1
  94. package/dist/runtime/query/iteration/stream-turn.js +48 -2
  95. package/dist/runtime/query/iteration/stream-turn.js.map +1 -1
  96. package/dist/runtime/query/resume-pending.d.ts.map +1 -1
  97. package/dist/runtime/query/resume-pending.js +7 -4
  98. package/dist/runtime/query/resume-pending.js.map +1 -1
  99. package/dist/runtime/query/review-policy.d.ts +40 -8
  100. package/dist/runtime/query/review-policy.d.ts.map +1 -1
  101. package/dist/runtime/query/review-policy.js +139 -24
  102. package/dist/runtime/query/review-policy.js.map +1 -1
  103. package/dist/runtime/query/turn/completion-audit.d.ts +32 -0
  104. package/dist/runtime/query/turn/completion-audit.d.ts.map +1 -0
  105. package/dist/runtime/query/turn/completion-audit.js +56 -0
  106. package/dist/runtime/query/turn/completion-audit.js.map +1 -0
  107. package/dist/runtime/query/turn/stall-monitor.d.ts +78 -0
  108. package/dist/runtime/query/turn/stall-monitor.d.ts.map +1 -0
  109. package/dist/runtime/query/turn/stall-monitor.js +101 -0
  110. package/dist/runtime/query/turn/stall-monitor.js.map +1 -0
  111. package/dist/session/paths.d.ts +1 -2
  112. package/dist/session/paths.d.ts.map +1 -1
  113. package/dist/session/paths.js +1 -4
  114. package/dist/session/paths.js.map +1 -1
  115. package/dist/tools/builtins/bash.d.ts +2 -1
  116. package/dist/tools/builtins/bash.d.ts.map +1 -1
  117. package/dist/tools/builtins/bash.js +5 -2
  118. package/dist/tools/builtins/bash.js.map +1 -1
  119. package/dist/tools/builtins/computer-use.d.ts +80 -0
  120. package/dist/tools/builtins/computer-use.d.ts.map +1 -1
  121. package/dist/tools/builtins/computer-use.js +172 -9
  122. package/dist/tools/builtins/computer-use.js.map +1 -1
  123. package/dist/tools/builtins/content-fingerprint.d.ts +13 -0
  124. package/dist/tools/builtins/content-fingerprint.d.ts.map +1 -1
  125. package/dist/tools/builtins/content-fingerprint.js +25 -0
  126. package/dist/tools/builtins/content-fingerprint.js.map +1 -1
  127. package/dist/tools/builtins/edit.d.ts.map +1 -1
  128. package/dist/tools/builtins/edit.js +7 -2
  129. package/dist/tools/builtins/edit.js.map +1 -1
  130. package/dist/tools/builtins/shell-read-credit.d.ts +15 -0
  131. package/dist/tools/builtins/shell-read-credit.d.ts.map +1 -0
  132. package/dist/tools/builtins/shell-read-credit.js +161 -0
  133. package/dist/tools/builtins/shell-read-credit.js.map +1 -0
  134. package/dist/tools/paths.d.ts +15 -2
  135. package/dist/tools/paths.d.ts.map +1 -1
  136. package/dist/tools/paths.js +17 -2
  137. package/dist/tools/paths.js.map +1 -1
  138. package/dist/turn/evidence-recall.d.ts +1 -1
  139. package/dist/turn/evidence-recall.d.ts.map +1 -1
  140. package/dist/turn/evidence-recall.js +5 -14
  141. package/dist/turn/evidence-recall.js.map +1 -1
  142. package/dist/types/acp/index.d.ts +6 -0
  143. package/dist/types/acp/index.d.ts.map +1 -1
  144. package/dist/types/agent/base.d.ts +4 -0
  145. package/dist/types/agent/base.d.ts.map +1 -1
  146. package/dist/types/hitl/index.d.ts +8 -0
  147. package/dist/types/hitl/index.d.ts.map +1 -1
  148. package/dist/types/hitl/index.js.map +1 -1
  149. package/dist/types/ids/index.d.ts +1 -1
  150. package/dist/types/message/index.d.ts +1 -1
  151. package/dist/types/message/index.d.ts.map +1 -1
  152. package/dist/types/message/index.js +1 -0
  153. package/dist/types/message/index.js.map +1 -1
  154. package/dist/types/provider/interface.d.ts +12 -0
  155. package/dist/types/provider/interface.d.ts.map +1 -1
  156. package/dist/types/session/config.d.ts +25 -0
  157. package/dist/types/session/config.d.ts.map +1 -1
  158. package/dist/types/session/records.d.ts.map +1 -1
  159. package/dist/types/session/records.js +2 -0
  160. package/dist/types/session/records.js.map +1 -1
  161. package/dist/types/session/turn.d.ts +1 -1
  162. package/dist/types/session/turn.d.ts.map +1 -1
  163. package/dist/types/session/turn.js.map +1 -1
  164. package/package.json +1 -1
  165. package/src/agents/QueryAgent.ts +2 -0
  166. package/src/bridge/acp/permission.ts +8 -0
  167. package/src/bridge/acp/server.ts +7 -1
  168. package/src/constants/tools/index.ts +27 -0
  169. package/src/eval/harness-protection.ts +1 -1
  170. package/src/learning/cycle.ts +15 -16
  171. package/src/learning/guidance.ts +339 -0
  172. package/src/learning/skill.ts +7 -7
  173. package/src/learning/store.ts +5 -6
  174. package/src/peers/client.ts +2 -3
  175. package/src/peers/endpoint.ts +1 -2
  176. package/src/peers/protocol.ts +1 -0
  177. package/src/prompt/coding-agent-doctrine.ts +5 -3
  178. package/src/prompt/index.ts +0 -4
  179. package/src/provider/fallback.ts +7 -0
  180. package/src/provider/idle-timeout.ts +8 -0
  181. package/src/provider/model-window-suffix.ts +1 -0
  182. package/src/provider/retry.ts +8 -0
  183. package/src/provider/step-stopped.ts +27 -0
  184. package/src/provider/token-budget.ts +6 -1
  185. package/src/public-runtime.ts +10 -28
  186. package/src/public-tools.ts +0 -3
  187. package/src/public-types.ts +5 -137
  188. package/src/runtime/query/iteration/index.ts +207 -5
  189. package/src/runtime/query/iteration/phases/per-call-review.ts +7 -5
  190. package/src/runtime/query/iteration/phases/tool-review.ts +5 -3
  191. package/src/runtime/query/iteration/stream-turn.ts +51 -1
  192. package/src/runtime/query/resume-pending.ts +11 -4
  193. package/src/runtime/query/review-policy.ts +157 -24
  194. package/src/runtime/query/turn/completion-audit.ts +71 -0
  195. package/src/runtime/query/turn/stall-monitor.ts +158 -0
  196. package/src/session/paths.ts +2 -6
  197. package/src/tools/builtins/bash.ts +6 -2
  198. package/src/tools/builtins/computer-use.ts +245 -7
  199. package/src/tools/builtins/content-fingerprint.ts +25 -0
  200. package/src/tools/builtins/edit.ts +11 -2
  201. package/src/tools/builtins/shell-read-credit.ts +159 -0
  202. package/src/tools/paths.ts +18 -2
  203. package/src/turn/evidence-recall.ts +7 -15
  204. package/src/types/acp/index.ts +6 -0
  205. package/src/types/agent/base.ts +4 -0
  206. package/src/types/hitl/index.ts +8 -0
  207. package/src/types/ids/index.ts +1 -1
  208. package/src/types/message/index.ts +1 -0
  209. package/src/types/provider/interface.ts +13 -0
  210. package/src/types/session/config.ts +27 -0
  211. package/src/types/session/records.ts +2 -0
  212. package/src/types/session/turn.ts +2 -1
  213. package/dist/learning/deprecated.d.ts +0 -86
  214. package/dist/learning/deprecated.d.ts.map +0 -1
  215. package/dist/learning/deprecated.js +0 -22
  216. package/dist/learning/deprecated.js.map +0 -1
  217. package/dist/manager/resident/activity.d.ts +0 -74
  218. package/dist/manager/resident/activity.d.ts.map +0 -1
  219. package/dist/manager/resident/activity.js +0 -125
  220. package/dist/manager/resident/activity.js.map +0 -1
  221. package/dist/manager/resident/agenda.d.ts +0 -167
  222. package/dist/manager/resident/agenda.d.ts.map +0 -1
  223. package/dist/manager/resident/agenda.js +0 -588
  224. package/dist/manager/resident/agenda.js.map +0 -1
  225. package/dist/manager/resident/consumption.d.ts +0 -98
  226. package/dist/manager/resident/consumption.d.ts.map +0 -1
  227. package/dist/manager/resident/consumption.js +0 -237
  228. package/dist/manager/resident/consumption.js.map +0 -1
  229. package/dist/manager/resident/delivery-window.d.ts +0 -28
  230. package/dist/manager/resident/delivery-window.d.ts.map +0 -1
  231. package/dist/manager/resident/delivery-window.js +0 -80
  232. package/dist/manager/resident/delivery-window.js.map +0 -1
  233. package/dist/manager/resident/evidence-recall.d.ts +0 -30
  234. package/dist/manager/resident/evidence-recall.d.ts.map +0 -1
  235. package/dist/manager/resident/evidence-recall.js +0 -297
  236. package/dist/manager/resident/evidence-recall.js.map +0 -1
  237. package/dist/manager/resident/history-disk.d.ts +0 -10
  238. package/dist/manager/resident/history-disk.d.ts.map +0 -1
  239. package/dist/manager/resident/history-disk.js +0 -50
  240. package/dist/manager/resident/history-disk.js.map +0 -1
  241. package/dist/manager/resident/history.d.ts +0 -115
  242. package/dist/manager/resident/history.d.ts.map +0 -1
  243. package/dist/manager/resident/history.js +0 -203
  244. package/dist/manager/resident/history.js.map +0 -1
  245. package/dist/manager/resident/host.d.ts +0 -112
  246. package/dist/manager/resident/host.d.ts.map +0 -1
  247. package/dist/manager/resident/host.js +0 -254
  248. package/dist/manager/resident/host.js.map +0 -1
  249. package/dist/manager/resident/initiative.d.ts +0 -86
  250. package/dist/manager/resident/initiative.d.ts.map +0 -1
  251. package/dist/manager/resident/initiative.js +0 -131
  252. package/dist/manager/resident/initiative.js.map +0 -1
  253. package/dist/manager/resident/loop.d.ts +0 -49
  254. package/dist/manager/resident/loop.d.ts.map +0 -1
  255. package/dist/manager/resident/loop.js +0 -75
  256. package/dist/manager/resident/loop.js.map +0 -1
  257. package/dist/manager/resident/outbox.d.ts +0 -124
  258. package/dist/manager/resident/outbox.d.ts.map +0 -1
  259. package/dist/manager/resident/outbox.js +0 -216
  260. package/dist/manager/resident/outbox.js.map +0 -1
  261. package/dist/manager/resident/proposal.d.ts +0 -65
  262. package/dist/manager/resident/proposal.d.ts.map +0 -1
  263. package/dist/manager/resident/proposal.js +0 -77
  264. package/dist/manager/resident/proposal.js.map +0 -1
  265. package/dist/manager/resident/store.d.ts +0 -108
  266. package/dist/manager/resident/store.d.ts.map +0 -1
  267. package/dist/manager/resident/store.js +0 -205
  268. package/dist/manager/resident/store.js.map +0 -1
  269. package/dist/manager/resident/tool-evidence.d.ts +0 -112
  270. package/dist/manager/resident/tool-evidence.d.ts.map +0 -1
  271. package/dist/manager/resident/tool-evidence.js +0 -289
  272. package/dist/manager/resident/tool-evidence.js.map +0 -1
  273. package/dist/prompt/resident-learning.d.ts +0 -29
  274. package/dist/prompt/resident-learning.d.ts.map +0 -1
  275. package/dist/prompt/resident-learning.js +0 -126
  276. package/dist/prompt/resident-learning.js.map +0 -1
  277. package/dist/prompt/resident-step.d.ts +0 -42
  278. package/dist/prompt/resident-step.d.ts.map +0 -1
  279. package/dist/prompt/resident-step.js +0 -137
  280. package/dist/prompt/resident-step.js.map +0 -1
  281. package/dist/tools/resident-history.d.ts +0 -12
  282. package/dist/tools/resident-history.d.ts.map +0 -1
  283. package/dist/tools/resident-history.js +0 -82
  284. package/dist/tools/resident-history.js.map +0 -1
  285. package/dist/tools/resident-tool-evidence.d.ts +0 -9
  286. package/dist/tools/resident-tool-evidence.d.ts.map +0 -1
  287. package/dist/tools/resident-tool-evidence.js +0 -61
  288. package/dist/tools/resident-tool-evidence.js.map +0 -1
  289. package/src/learning/deprecated.ts +0 -124
  290. package/src/manager/resident/activity.ts +0 -211
  291. package/src/manager/resident/agenda.ts +0 -937
  292. package/src/manager/resident/consumption.ts +0 -335
  293. package/src/manager/resident/delivery-window.ts +0 -99
  294. package/src/manager/resident/evidence-recall.ts +0 -369
  295. package/src/manager/resident/history-disk.ts +0 -63
  296. package/src/manager/resident/history.ts +0 -348
  297. package/src/manager/resident/host.ts +0 -390
  298. package/src/manager/resident/initiative.ts +0 -231
  299. package/src/manager/resident/loop.ts +0 -119
  300. package/src/manager/resident/outbox.ts +0 -341
  301. package/src/manager/resident/proposal.ts +0 -119
  302. package/src/manager/resident/store.ts +0 -279
  303. package/src/manager/resident/tool-evidence.ts +0 -455
  304. package/src/prompt/resident-learning.ts +0 -152
  305. package/src/prompt/resident-step.ts +0 -182
  306. package/src/tools/resident-history.ts +0 -88
  307. package/src/tools/resident-tool-evidence.ts +0 -72
@@ -1,937 +0,0 @@
1
- import { randomUUID } from 'node:crypto'
2
- import { join, resolve } from 'node:path'
3
- import { isDeepStrictEqual } from 'node:util'
4
- import { z } from 'zod'
5
- import { DiskRecordStore } from '../../store/kv/record-store.js'
6
- import { DiskRevisionRecordStore } from '../../store/kv/revision-record-store.js'
7
- import { defineSchema, migrate } from '../../store/schema.js'
8
- import type { TenantId } from '../../types/ids/index.js'
9
- import { asTenantId } from '../../utils/id.js'
10
- import {
11
- ResidentConflictError,
12
- type ResidentDecision,
13
- type ResidentExecutionStore,
14
- type ResidentState,
15
- claimResidentState,
16
- residentDecisionSchema,
17
- residentKeySegment,
18
- residentStateSchema,
19
- settleResidentState,
20
- wakeResidentState,
21
- } from './store.js'
22
-
23
- import {
24
- type LearningEvidence,
25
- type LearningProfileUpdate,
26
- type LearningSkillCandidate,
27
- type LearningSkillEvaluation,
28
- type LearningState,
29
- freezeLearning,
30
- learningEvidenceSchema,
31
- learningSchema,
32
- promoteLearningSkill,
33
- restoreLearningSkill,
34
- reviseLearningProfile,
35
- } from '../../learning/skill.js'
36
- import { type ResidentActivitySource, createResidentActivitySource } from './activity.js'
37
- import { readResidentHistoryRecord } from './history-disk.js'
38
- import { type ResidentHistorySource, createResidentHistorySource } from './history.js'
39
- import {
40
- type ResidentFeedback,
41
- type ResidentObservation,
42
- freezeResidentFeedback,
43
- observeResidentStep,
44
- residentFeedbackSchema,
45
- residentObservationSchema,
46
- } from './initiative.js'
47
- import {
48
- type ResidentDeliveryOutcome,
49
- type ResidentMessageInput,
50
- type ResidentOutboxMessage,
51
- appendResidentMessage,
52
- claimResidentOutboxMessage,
53
- residentDeliveryOutcomeSchema,
54
- residentMessageInputSchema,
55
- residentOutboxMessageSchema,
56
- settleResidentOutboxMessage,
57
- } from './outbox.js'
58
- import {
59
- type ResidentProposal,
60
- type ResidentProposalLimits,
61
- type ResidentProposalOrigin,
62
- residentProposalLimitsSchema,
63
- residentProposalOriginSchema,
64
- residentProposalSchema,
65
- validateResidentProposal,
66
- } from './proposal.js'
67
-
68
- const agendaSchema = z
69
- .object({
70
- tenantId: z.string().uuid(),
71
- agentKey: z.string().min(1).max(200),
72
- identity: z.string().trim().min(1).max(8_000),
73
- revision: z.number().int().positive().safe(),
74
- paused: z.boolean(),
75
- pauseGeneration: z.number().int().nonnegative().safe().optional(),
76
- archiveHead: z.number().int().min(2).safe().optional(),
77
- learning: learningSchema.optional(),
78
- outbox: z.array(residentOutboxMessageSchema).max(128).optional(),
79
- pursuits: z
80
- .array(
81
- z.object({
82
- id: z.string().uuid(),
83
- state: residentStateSchema,
84
- feedback: residentFeedbackSchema.optional(),
85
- origin: residentProposalOriginSchema.optional(),
86
- retiredChildren: z.number().int().min(0).max(8).optional(),
87
- }),
88
- )
89
- .max(32),
90
- })
91
- .superRefine((agenda, context) => {
92
- const origins = agenda.pursuits.flatMap((p) => (p.origin ? [p.origin] : []))
93
- const outbox = agenda.outbox ?? []
94
- const invalid =
95
- (agenda.archiveHead !== undefined && agenda.archiveHead > agenda.revision) ||
96
- new Set(outbox.map((message) => message.id)).size !== outbox.length ||
97
- outbox.filter((message) => message.phase === 'sending').length > 1 ||
98
- outbox.some(
99
- (message) =>
100
- message.tenantId !== agenda.tenantId ||
101
- message.agentKey !== agenda.agentKey ||
102
- !agenda.pursuits.some((pursuit) => pursuit.id === message.pursuitId),
103
- ) ||
104
- new Set(agenda.pursuits.map((p) => p.id)).size !== agenda.pursuits.length ||
105
- new Set(origins.map((origin) => origin.proposalId)).size !== origins.length ||
106
- agenda.pursuits.filter((p) => p.state.phase === 'running').length > 1 ||
107
- agenda.pursuits.some((p) => {
108
- if (!p.origin) return false
109
- const parent = agenda.pursuits.find((candidate) => candidate.id === p.origin?.parentId)
110
- return (
111
- !parent ||
112
- parent.id === p.id ||
113
- p.origin.parentRevision > parent.state.revision ||
114
- p.origin.depth !== (parent.origin?.depth ?? 0) + 1
115
- )
116
- }) ||
117
- agenda.pursuits.some((p) =>
118
- p.feedback?.observations.some(
119
- (item, index, all) =>
120
- item.step > p.state.stepsAdmitted ||
121
- (index > 0 && item.step <= (all[index - 1]?.step ?? 0)),
122
- ),
123
- ) ||
124
- agenda.pursuits.some(
125
- ({ id, state }) =>
126
- state.pursuitId !== id ||
127
- state.tenantId !== agenda.tenantId ||
128
- state.agentKey !== agenda.agentKey ||
129
- state.identity !== agenda.identity,
130
- )
131
- if (invalid)
132
- context.addIssue({
133
- code: z.ZodIssueCode.custom,
134
- message:
135
- 'Invalid agenda identity, pursuit ancestry, observation order, outbox or overlapping claims.',
136
- })
137
- })
138
-
139
- /**
140
- * @experimental One pursuit shares its agent's identity, but has its own lifecycle.
141
- *
142
- * @deprecated Residents are deprecated and removed in the next major release. Learning (`runLearningCycle` and the other `Learning*` names) keeps working, but `runLearningCycle` still takes a resident agenda until learning gets its own activation target in the next major release; for long-running work use scheduled tasks (`LocalTaskScheduler`), session goals and background agents.
143
- */
144
- export interface ResidentPursuit {
145
- readonly id: string
146
- readonly state: ResidentState
147
- readonly feedback?: ResidentFeedback
148
- readonly origin?: ResidentProposalOrigin
149
- /** Archived direct children still consume the parent's lifetime admission bound. */
150
- readonly retiredChildren?: number
151
- }
152
-
153
- /**
154
- * @experimental Bounded shared admission record; at most one pursuit may be running.
155
- *
156
- * @deprecated Residents are deprecated and removed in the next major release. Learning (`runLearningCycle` and the other `Learning*` names) keeps working, but `runLearningCycle` still takes a resident agenda until learning gets its own activation target in the next major release; for long-running work use scheduled tasks (`LocalTaskScheduler`), session goals and background agents.
157
- */
158
- export interface ResidentAgendaState {
159
- readonly tenantId: string
160
- readonly agentKey: string
161
- readonly identity: string
162
- readonly revision: number
163
- readonly paused: boolean
164
- /** Durable pause requests; absent means zero. Resuming never resets this generation. */
165
- readonly pauseGeneration?: number
166
- readonly pursuits: readonly ResidentPursuit[]
167
- readonly outbox?: readonly ResidentOutboxMessage[]
168
- readonly archiveHead?: number
169
- readonly learning?: LearningState
170
- }
171
-
172
- /**
173
- * @experimental Explicit terminal entries to retire from active capacity together.
174
- *
175
- * @deprecated Residents are deprecated and removed in the next major release. Learning (`runLearningCycle` and the other `Learning*` names) keeps working, but `runLearningCycle` still takes a resident agenda until learning gets its own activation target in the next major release; for long-running work use scheduled tasks (`LocalTaskScheduler`), session goals and background agents.
176
- */
177
- export interface ResidentArchiveRequest {
178
- readonly pursuitIds?: readonly string[]
179
- readonly messageIds?: readonly string[]
180
- }
181
-
182
- /**
183
- * @experimental A committed removal, whose immutable predecessor preserves the full records.
184
- *
185
- * @deprecated Residents are deprecated and removed in the next major release. Learning (`runLearningCycle` and the other `Learning*` names) keeps working, but `runLearningCycle` still takes a resident agenda until learning gets its own activation target in the next major release; for long-running work use scheduled tasks (`LocalTaskScheduler`), session goals and background agents.
186
- */
187
- export interface ResidentArchiveEntry {
188
- readonly revision: number
189
- readonly pursuits: readonly ResidentPursuit[]
190
- readonly messages: readonly ResidentOutboxMessage[]
191
- }
192
-
193
- /**
194
- * @experimental Pages count archive events; each event contains at most 32 pursuits and 128 messages.
195
- *
196
- * @deprecated Residents are deprecated and removed in the next major release. Learning (`runLearningCycle` and the other `Learning*` names) keeps working, but `runLearningCycle` still takes a resident agenda until learning gets its own activation target in the next major release; for long-running work use scheduled tasks (`LocalTaskScheduler`), session goals and background agents.
197
- */
198
- export interface ResidentArchivePage {
199
- readonly entries: readonly ResidentArchiveEntry[]
200
- readonly nextBeforeRevision: number | null
201
- }
202
-
203
- /**
204
- * @experimental The cursor is the next archive-event revision returned by a previous page.
205
- *
206
- * @deprecated Residents are deprecated and removed in the next major release. Learning (`runLearningCycle` and the other `Learning*` names) keeps working, but `runLearningCycle` still takes a resident agenda until learning gets its own activation target in the next major release; for long-running work use scheduled tasks (`LocalTaskScheduler`), session goals and background agents.
207
- */
208
- export interface ResidentArchiveListOptions {
209
- readonly beforeRevision?: number
210
- /** Number of archive events to inspect; defaults to 8 and cannot exceed 32. */
211
- readonly limit?: number
212
- }
213
-
214
- const agendaRecordSchema = defineSchema({
215
- kind: 'resident-agenda',
216
- current: 6,
217
- migrations: {
218
- 1: (record) => record,
219
- 2: (record) => record,
220
- 3: (record) => record,
221
- 4: (record) => record,
222
- 5: (record) => record,
223
- },
224
- })
225
-
226
- const archiveRequestSchema = z.object({
227
- pursuitIds: z.array(z.string().uuid()).max(32).default([]),
228
- messageIds: z.array(z.string().uuid()).max(128).default([]),
229
- })
230
-
231
- /**
232
- * @experimental Atomic whole-agent admission plus independently addressable pursuits.
233
- *
234
- * @deprecated Residents are deprecated and removed in the next major release. Learning (`runLearningCycle` and the other `Learning*` names) keeps working, but `runLearningCycle` still takes a resident agenda until learning gets its own activation target in the next major release; for long-running work use scheduled tasks (`LocalTaskScheduler`), session goals and background agents.
235
- */
236
- export interface ResidentAgendaStore {
237
- read(): Promise<ResidentAgendaState | null>
238
- create(identity: string): Promise<ResidentAgendaState>
239
- add(expected: ResidentAgendaState, objective: string): Promise<ResidentPursuit>
240
- /** Each committed true request advances pauseGeneration, even when already paused. */
241
- setPaused(expected: ResidentAgendaState, paused: boolean): Promise<ResidentAgendaState>
242
- wake(id: string, expected: ResidentState, reason: string, now: number): Promise<ResidentState>
243
- execution(id: string): ResidentExecutionStore
244
- /** Optional atomic extensions required by a configured resident initiative host. */
245
- executionAt?(id: string, expected: ResidentAgendaState): ResidentExecutionStore
246
- settleObserved?(
247
- id: string,
248
- expected: ResidentState,
249
- decision: ResidentDecision,
250
- observation: ResidentObservation,
251
- now: number,
252
- ): Promise<ResidentState>
253
- /** Optional atomic settlement and outbound intent; no transport runs during persistence. */
254
- settleWithMessage?(
255
- id: string,
256
- expected: ResidentState,
257
- decision: ResidentDecision,
258
- message: ResidentMessageInput,
259
- now: number,
260
- observation?: ResidentObservation,
261
- ): Promise<ResidentState>
262
- enqueueMessage?(
263
- expected: ResidentAgendaState,
264
- input: ResidentMessageInput,
265
- ): Promise<ResidentOutboxMessage>
266
- claimMessage?(
267
- expected: ResidentAgendaState,
268
- id: string,
269
- now: number,
270
- ): Promise<ResidentOutboxMessage>
271
- settleMessage?(
272
- expected: ResidentOutboxMessage,
273
- outcome: ResidentDeliveryOutcome,
274
- now: number,
275
- ): Promise<ResidentOutboxMessage>
276
- }
277
-
278
- /**
279
- * @experimental One immutable revision contains identity, pause state and all
280
- * pursuits. Admission therefore serializes this agent even across processes
281
- * choosing different pursuits. Trusted local filesystems only; no lease expiry.
282
- *
283
- * @deprecated Residents are deprecated and removed in the next major release. Learning (`runLearningCycle` and the other `Learning*` names) keeps working, but `runLearningCycle` still takes a resident agenda until learning gets its own activation target in the next major release; for long-running work use scheduled tasks (`LocalTaskScheduler`), session goals and background agents.
284
- */
285
- export class DiskResidentAgenda implements ResidentAgendaStore {
286
- private readonly records = new DiskRevisionRecordStore<ResidentAgendaState>(
287
- agendaRecordSchema,
288
- 'resident agenda',
289
- (record) => record.revision,
290
- )
291
- private readonly historyRecords = new DiskRecordStore<ResidentAgendaState>(agendaRecordSchema)
292
- private readonly location
293
- private readonly tenantId: TenantId
294
- private readonly agentKey: string
295
- private readonly historyRoot: string
296
- private readonly historyRevisions: string
297
-
298
- constructor(root: string, scope: { tenantId: TenantId; agentKey: string }) {
299
- this.tenantId = asTenantId(scope.tenantId)
300
- this.agentKey = z.string().min(1).max(200).parse(scope.agentKey)
301
- const directory = join(root, this.tenantId, residentKeySegment(this.agentKey), 'agenda')
302
- this.location = {
303
- legacyPath: join(directory, 'state.json'),
304
- revisionsDir: join(directory, 'revisions'),
305
- publishLegacyProjection: false,
306
- }
307
- this.historyRoot = resolve(root)
308
- this.historyRevisions = resolve(this.location.revisionsDir)
309
- }
310
-
311
- private checked(record: ResidentAgendaState): ResidentAgendaState {
312
- const agenda = agendaSchema.parse(record)
313
- if (agenda.tenantId !== this.tenantId || agenda.agentKey !== this.agentKey)
314
- throw new Error('Agenda does not match the bound tenant and agent.')
315
- return Object.freeze({
316
- ...agenda,
317
- ...(agenda.learning ? { learning: freezeLearning(agenda.learning) } : {}),
318
- ...(agenda.outbox
319
- ? { outbox: Object.freeze(agenda.outbox.map((message) => Object.freeze(message))) }
320
- : {}),
321
- pursuits: Object.freeze(
322
- agenda.pursuits.map((p) =>
323
- Object.freeze({
324
- ...p,
325
- state: Object.freeze(p.state),
326
- ...(p.feedback ? { feedback: freezeResidentFeedback(p.feedback) } : {}),
327
- ...(p.origin ? { origin: Object.freeze(p.origin) } : {}),
328
- }),
329
- ),
330
- ),
331
- })
332
- }
333
-
334
- async read(): Promise<ResidentAgendaState | null> {
335
- const record = await this.records.read(this.location)
336
- return record === null ? null : this.checked(record)
337
- }
338
-
339
- /** Read one authoritative immutable commit, never the compatibility projection. */
340
- async readRevision(revision: number): Promise<ResidentAgendaState | null> {
341
- z.number().int().positive().safe().parse(revision)
342
- const record = await this.historyRecords.read(
343
- join(this.location.revisionsDir, `${revision}.json`),
344
- )
345
- if (record === null) return null
346
- const state = this.checked(record)
347
- if (state.revision !== revision) throw new Error('Agenda revision filename and body disagree.')
348
- return state
349
- }
350
-
351
- /**
352
- * Bind bounded historical reads to this pursuit and a host-selected upper
353
- * agenda revision (normally ResidentStepContext.agendaRevision). No I/O or
354
- * inference occurs until search/read; the boundary never advances itself.
355
- */
356
- history(pursuit: ResidentState, throughRevision: number): ResidentHistorySource {
357
- const state = residentStateSchema.parse(pursuit)
358
- if (state.tenantId !== this.tenantId || state.agentKey !== this.agentKey || !state.pursuitId)
359
- throw new Error('History requires a pursuit belonging to this resident agenda.')
360
- z.number().int().positive().safe().parse(throughRevision)
361
- return createResidentHistorySource(
362
- {
363
- tenantId: this.tenantId,
364
- agentKey: this.agentKey,
365
- pursuitId: state.pursuitId,
366
- throughRevision,
367
- },
368
- state,
369
- async (revision, budget) => {
370
- const raw = await readResidentHistoryRecord(
371
- this.historyRoot,
372
- join(this.historyRevisions, `${revision}.json`),
373
- budget,
374
- )
375
- return this.checked(migrate<ResidentAgendaState>(agendaRecordSchema, raw))
376
- },
377
- )
378
- }
379
-
380
- /** @experimental Bounded admission/settlement history, including archived pursuits. */
381
- activity(throughRevision: number): ResidentActivitySource {
382
- return createResidentActivitySource(
383
- { tenantId: this.tenantId, agentKey: this.agentKey, throughRevision },
384
- async (revision, budget) => {
385
- const raw = await readResidentHistoryRecord(
386
- this.historyRoot,
387
- join(this.historyRevisions, `${revision}.json`),
388
- budget,
389
- )
390
- return this.checked(migrate<ResidentAgendaState>(agendaRecordSchema, raw))
391
- },
392
- )
393
- }
394
-
395
- private retire(state: ResidentAgendaState, input: ResidentArchiveRequest): ResidentAgendaState {
396
- const request = archiveRequestSchema.parse(input)
397
- const pursuits = new Set(request.pursuitIds)
398
- const messages = new Set(request.messageIds)
399
- if (
400
- pursuits.size + messages.size === 0 ||
401
- pursuits.size !== request.pursuitIds.length ||
402
- messages.size !== request.messageIds.length
403
- )
404
- throw new Error('Archive requires a nonempty set of unique entry IDs.')
405
- const removed = state.pursuits.filter((pursuit) => pursuits.has(pursuit.id))
406
- const removedMessages = (state.outbox ?? []).filter((message) => messages.has(message.id))
407
- if (removed.length !== pursuits.size || removedMessages.length !== messages.size)
408
- throw new Error('Unknown active entry in resident archive request.')
409
- if (removed.some((p) => p.state.phase !== 'complete' && p.state.phase !== 'blocked'))
410
- throw new Error('Only terminal pursuits can be archived.')
411
- if (removedMessages.some((m) => m.phase !== 'acknowledged' && m.phase !== 'cancelled'))
412
- throw new Error('Only terminal messages can be archived.')
413
- const remaining = state.pursuits.filter((pursuit) => !pursuits.has(pursuit.id))
414
- const remainingMessages = state.outbox?.filter((message) => !messages.has(message.id))
415
- if (remaining.some((p) => p.origin && pursuits.has(p.origin.parentId)))
416
- throw new Error('Archive must retain the parents of remaining pursuits.')
417
- if (remainingMessages?.some((message) => pursuits.has(message.pursuitId)))
418
- throw new Error('Archive must retain pursuits referenced by remaining messages.')
419
- return {
420
- ...state,
421
- archiveHead: state.revision + 1,
422
- pursuits: remaining.map((pursuit) => {
423
- const retired = removed.filter((child) => child.origin?.parentId === pursuit.id).length
424
- return retired
425
- ? { ...pursuit, retiredChildren: (pursuit.retiredChildren ?? 0) + retired }
426
- : pursuit
427
- }),
428
- ...(remainingMessages ? { outbox: remainingMessages } : {}),
429
- }
430
- }
431
-
432
- /** Free active slots while preserving full records in the immutable predecessor commit. */
433
- async archive(
434
- expected: ResidentAgendaState,
435
- input: ResidentArchiveRequest,
436
- ): Promise<ResidentAgendaState> {
437
- const request = archiveRequestSchema.parse(input)
438
- return this.change(expected, (state) => this.retire(state, request))
439
- }
440
-
441
- private async archiveEntry(revision: number): Promise<{
442
- entry: ResidentArchiveEntry
443
- previous: number | null
444
- }> {
445
- const after = await this.readRevision(revision)
446
- const before = await this.readRevision(revision - 1)
447
- if (
448
- !before ||
449
- !after ||
450
- after.archiveHead !== revision ||
451
- (before.archiveHead !== undefined && before.archiveHead >= revision)
452
- )
453
- throw new Error('Resident archive history is missing or its chain is invalid.')
454
- const activePursuits = new Set(after.pursuits.map((p) => p.id))
455
- const activeMessages = new Set((after.outbox ?? []).map((m) => m.id))
456
- const pursuits = before.pursuits.filter((p) => !activePursuits.has(p.id))
457
- const messages = (before.outbox ?? []).filter((m) => !activeMessages.has(m.id))
458
- const derived = this.checked({
459
- ...this.retire(before, {
460
- pursuitIds: pursuits.map((p) => p.id),
461
- messageIds: messages.map((m) => m.id),
462
- }),
463
- revision,
464
- })
465
- if (!isDeepStrictEqual(derived, after))
466
- throw new Error('Resident archive commit does not match its immutable predecessor.')
467
- return {
468
- entry: Object.freeze({
469
- revision,
470
- pursuits: Object.freeze(pursuits),
471
- messages: Object.freeze(messages),
472
- }),
473
- previous: before.archiveHead ?? null,
474
- }
475
- }
476
-
477
- /** Bounded archive-event pages; exact-ID deduplication may inspect the complete chain. */
478
- async listArchived(options: ResidentArchiveListOptions = {}): Promise<ResidentArchivePage> {
479
- const request = z
480
- .object({
481
- beforeRevision: z.number().int().min(2).safe().optional(),
482
- limit: z.number().int().min(1).max(32).default(8),
483
- })
484
- .parse(options)
485
- const state = await this.read()
486
- if (!state) throw new Error('Create the resident agenda first.')
487
- let revision = request.beforeRevision ?? state.archiveHead ?? null
488
- if (revision !== null && (state.archiveHead === undefined || revision > state.archiveHead))
489
- throw new Error('Archive cursor is ahead of committed archive history.')
490
- const entries: ResidentArchiveEntry[] = []
491
- while (revision !== null && entries.length < request.limit) {
492
- const archived = await this.archiveEntry(revision)
493
- entries.push(archived.entry)
494
- revision = archived.previous
495
- }
496
- return Object.freeze({ entries: Object.freeze(entries), nextBeforeRevision: revision })
497
- }
498
-
499
- private async archivedMatch<T>(
500
- state: ResidentAgendaState,
501
- match: (entry: ResidentArchiveEntry) => T | undefined,
502
- ): Promise<T | undefined> {
503
- let revision = state.archiveHead ?? null
504
- while (revision !== null) {
505
- const archived = await this.archiveEntry(revision)
506
- const found = match(archived.entry)
507
- if (found !== undefined) return found
508
- revision = archived.previous
509
- }
510
- return undefined
511
- }
512
-
513
- private assertArchivedIntent(
514
- message: ResidentOutboxMessage,
515
- input: ResidentMessageInput,
516
- sourceClaimId: string | null,
517
- ): void {
518
- if (
519
- message.pursuitId !== input.pursuitId ||
520
- message.destination !== input.destination ||
521
- message.body !== input.body ||
522
- message.notBefore !== input.notBefore ||
523
- message.sourceClaimId !== sourceClaimId
524
- )
525
- throw new Error('Outbox message ID already names a different immutable archived intent.')
526
- }
527
-
528
- async create(identity: string): Promise<ResidentAgendaState> {
529
- const record = this.checked({
530
- tenantId: this.tenantId,
531
- agentKey: this.agentKey,
532
- identity,
533
- revision: 1,
534
- paused: false,
535
- pursuits: [],
536
- })
537
- return this.records.transact(this.location, (current) => {
538
- if (current !== null) throw new ResidentConflictError()
539
- return { record, result: record }
540
- })
541
- }
542
-
543
- private async change(
544
- expected: ResidentAgendaState,
545
- update: (state: ResidentAgendaState) => ResidentAgendaState,
546
- ): Promise<ResidentAgendaState> {
547
- const snapshot = this.checked(expected)
548
- return this.records.transact(this.location, (record) => {
549
- if (record === null) throw new ResidentConflictError()
550
- const current = this.checked(record)
551
- if (current.revision !== snapshot.revision) throw new ResidentConflictError()
552
- const next = this.checked({ ...update(current), revision: current.revision + 1 })
553
- return { record: next, result: next }
554
- })
555
- }
556
-
557
- private pursuit(identity: string, objective: string): ResidentPursuit {
558
- const id = randomUUID()
559
- const pursuit = Object.freeze({
560
- id,
561
- state: Object.freeze(
562
- residentStateSchema.parse({
563
- tenantId: this.tenantId,
564
- agentKey: this.agentKey,
565
- pursuitId: id,
566
- identity,
567
- objective,
568
- revision: 1,
569
- stepsAdmitted: 0,
570
- phase: 'waiting',
571
- wakeAt: 0,
572
- reason: 'Initial pursuit',
573
- summary: null,
574
- claimId: null,
575
- }),
576
- ),
577
- })
578
- return pursuit
579
- }
580
-
581
- async add(expected: ResidentAgendaState, objective: string): Promise<ResidentPursuit> {
582
- const pursuit = this.pursuit(expected.identity, objective)
583
- await this.change(expected, (state) => ({ ...state, pursuits: [...state.pursuits, pursuit] }))
584
- return pursuit
585
- }
586
-
587
- /** Host-approved proposal admission is atomic and inert until a future invocation. */
588
- async admitProposal(
589
- expected: ResidentAgendaState,
590
- input: ResidentProposal,
591
- limits: ResidentProposalLimits,
592
- ): Promise<ResidentPursuit> {
593
- const snapshot = this.checked(expected)
594
- const proposal = residentProposalSchema.parse(input)
595
- const policy = residentProposalLimitsSchema.parse(limits)
596
- const current = await this.read()
597
- if (!current || current.revision !== snapshot.revision) throw new ResidentConflictError()
598
- validateResidentProposal(current, proposal, policy)
599
- if (
600
- await this.archivedMatch(current, (entry) =>
601
- entry.pursuits.find((p) => p.origin?.proposalId === proposal.id),
602
- )
603
- )
604
- throw new Error('Resident proposal has already been admitted and archived.')
605
- let admitted: ResidentPursuit | undefined
606
- await this.change(snapshot, (state) => {
607
- const origin = validateResidentProposal(state, proposal, policy)
608
- admitted = Object.freeze({
609
- ...this.pursuit(state.identity, proposal.objective),
610
- origin: Object.freeze(origin),
611
- })
612
- return { ...state, pursuits: [...state.pursuits, admitted] }
613
- })
614
- if (!admitted) throw new Error('Resident proposal was not admitted.')
615
- return admitted
616
- }
617
-
618
- async setPaused(expected: ResidentAgendaState, paused: boolean): Promise<ResidentAgendaState> {
619
- z.boolean().parse(paused)
620
- return this.change(expected, (state) => ({
621
- ...state,
622
- paused,
623
- ...(paused ? { pauseGeneration: (state.pauseGeneration ?? 0) + 1 } : {}),
624
- }))
625
- }
626
-
627
- private async learn(
628
- expected: ResidentAgendaState,
629
- learning: LearningState,
630
- ): Promise<ResidentAgendaState> {
631
- return this.change(expected, (state) => {
632
- if (state.pursuits.some((pursuit) => pursuit.state.phase === 'running'))
633
- throw new Error('Resident learning cannot change while a pursuit is running.')
634
- if (!isDeepStrictEqual(state.learning, expected.learning)) throw new ResidentConflictError()
635
- return { ...state, learning }
636
- })
637
- }
638
-
639
- /** Append a host-evidenced correction only while pursuit admission is idle. */
640
- async updateProfile(
641
- expected: ResidentAgendaState,
642
- input: LearningProfileUpdate,
643
- ): Promise<ResidentAgendaState> {
644
- const snapshot = this.checked(expected)
645
- const learning = reviseLearningProfile(snapshot.learning, input)
646
- return this.learn(snapshot, learning)
647
- }
648
-
649
- /** Bind evaluated guidance to the current learning version and an idle agenda. */
650
- async promoteSkill(
651
- expected: ResidentAgendaState,
652
- candidate: LearningSkillCandidate,
653
- evaluation: LearningSkillEvaluation,
654
- evidence: LearningEvidence,
655
- ): Promise<ResidentAgendaState> {
656
- const snapshot = this.checked(expected)
657
- const learning = promoteLearningSkill(snapshot.learning, candidate, evaluation, evidence)
658
- return this.learn(snapshot, learning)
659
- }
660
-
661
- /** Restore one skill from an immutable agenda revision while retaining the current profile. */
662
- async rollbackSkill(
663
- expected: ResidentAgendaState,
664
- name: string,
665
- fromRevision: number,
666
- evidence: LearningEvidence,
667
- ): Promise<ResidentAgendaState> {
668
- const snapshot = this.checked(expected)
669
- const checkedEvidence = learningEvidenceSchema.parse(evidence)
670
- z.number().int().positive().safe().max(snapshot.revision).parse(fromRevision)
671
- z.string()
672
- .regex(/^[a-z0-9][a-z0-9_-]{0,63}$/)
673
- .parse(name)
674
- const historical = await this.readRevision(fromRevision)
675
- if (!historical) throw new Error('Resident skill rollback revision is missing.')
676
- const learning = restoreLearningSkill(
677
- snapshot.learning,
678
- historical.learning,
679
- name,
680
- checkedEvidence,
681
- )
682
- return this.learn(snapshot, learning)
683
- }
684
-
685
- private async updatePursuit(
686
- id: string,
687
- expected: ResidentState,
688
- update: (state: ResidentState) => ResidentState,
689
- admission = false,
690
- expectedAgendaRevision?: number,
691
- observation?: ResidentObservation,
692
- message?: ResidentMessageInput,
693
- ): Promise<ResidentState> {
694
- const validate = (state: ResidentAgendaState): ResidentState => {
695
- const pursuit = state.pursuits.find((p) => p.id === id)
696
- if (!pursuit) throw new Error('Unknown resident pursuit.')
697
- const current = pursuit.state
698
- if (
699
- expected.pursuitId !== id ||
700
- expected.tenantId !== state.tenantId ||
701
- expected.agentKey !== state.agentKey ||
702
- current.revision !== expected.revision ||
703
- current.claimId !== expected.claimId
704
- )
705
- throw new ResidentConflictError()
706
- if (admission && (state.paused || state.pursuits.some((p) => p.state.phase === 'running')))
707
- throw new ResidentConflictError()
708
- return current
709
- }
710
- // Retry persistence contention only. The caller's exact pursuit revision
711
- // and claim must survive every retry; a callback is never re-executed here.
712
- for (let attempt = 0; attempt < 8; attempt++) {
713
- const agenda = await this.read()
714
- if (agenda === null) throw new Error('Create the resident agenda first.')
715
- if (expectedAgendaRevision !== undefined && agenda.revision !== expectedAgendaRevision)
716
- throw new ResidentConflictError()
717
- const current = validate(agenda)
718
- const archived =
719
- message && !agenda.outbox?.some((item) => item.id === message.id)
720
- ? await this.archivedMatch(agenda, (entry) =>
721
- entry.messages.find((item) => item.id === message.id),
722
- )
723
- : undefined
724
- if (message && archived) this.assertArchivedIntent(archived, message, current.claimId)
725
- try {
726
- const next = await this.change(agenda, (state) => {
727
- const current = validate(state)
728
- const updated = residentStateSchema.parse({
729
- ...update(current),
730
- revision: current.revision + 1,
731
- })
732
- return {
733
- ...state,
734
- ...(message && !archived
735
- ? { outbox: appendResidentMessage(state, message, current.claimId) }
736
- : {}),
737
- pursuits: state.pursuits.map((p) =>
738
- p.id === id
739
- ? {
740
- ...p,
741
- state: updated,
742
- ...(observation
743
- ? {
744
- feedback: observeResidentStep(
745
- p.feedback,
746
- observation,
747
- current.stepsAdmitted,
748
- ),
749
- }
750
- : {}),
751
- }
752
- : p,
753
- ),
754
- }
755
- })
756
- const result = next.pursuits.find((p) => p.id === id)
757
- if (!result) throw new Error('Committed pursuit is missing.')
758
- return result.state
759
- } catch (error) {
760
- if (!(error instanceof ResidentConflictError) || attempt === 7) throw error
761
- }
762
- }
763
- throw new ResidentConflictError()
764
- }
765
-
766
- async wake(
767
- id: string,
768
- expected: ResidentState,
769
- reason: string,
770
- now: number,
771
- ): Promise<ResidentState> {
772
- return this.updatePursuit(id, expected, (state) => wakeResidentState(state, reason, now))
773
- }
774
-
775
- async settleObserved(
776
- id: string,
777
- expected: ResidentState,
778
- decision: ResidentDecision,
779
- observation: ResidentObservation,
780
- now: number,
781
- ): Promise<ResidentState> {
782
- const checked = residentObservationSchema.parse(observation)
783
- return this.updatePursuit(
784
- id,
785
- expected,
786
- (state) => settleResidentState(state, decision, now),
787
- false,
788
- undefined,
789
- checked,
790
- )
791
- }
792
-
793
- /** Commit the step, observation and message intent together; delivery is separate. */
794
- async settleWithMessage(
795
- id: string,
796
- expected: ResidentState,
797
- decision: ResidentDecision,
798
- message: ResidentMessageInput,
799
- now: number,
800
- observation?: ResidentObservation,
801
- ): Promise<ResidentState> {
802
- const input = residentMessageInputSchema.parse(message)
803
- const disposition = residentDecisionSchema.parse(decision)
804
- if (input.pursuitId !== id) throw new Error('Message must belong to the settling pursuit.')
805
- const current = residentStateSchema.parse(expected)
806
- const checked =
807
- observation === undefined ? undefined : residentObservationSchema.parse(observation)
808
- return this.updatePursuit(
809
- id,
810
- current,
811
- (state) => settleResidentState(state, disposition, now),
812
- false,
813
- undefined,
814
- checked,
815
- input,
816
- )
817
- }
818
-
819
- /** Enqueue host-authorized intent without invoking a transport or changing pursuit state. */
820
- async enqueueMessage(
821
- expected: ResidentAgendaState,
822
- input: ResidentMessageInput,
823
- ): Promise<ResidentOutboxMessage> {
824
- const snapshot = this.checked(expected)
825
- const message = residentMessageInputSchema.parse(input)
826
- const current = await this.read()
827
- if (!current || current.revision !== snapshot.revision) throw new ResidentConflictError()
828
- const archived = current.outbox?.some((item) => item.id === message.id)
829
- ? undefined
830
- : await this.archivedMatch(current, (entry) =>
831
- entry.messages.find((item) => item.id === message.id),
832
- )
833
- if (archived) this.assertArchivedIntent(archived, message, null)
834
- const next = await this.change(snapshot, (state) => ({
835
- ...state,
836
- ...(!archived ? { outbox: appendResidentMessage(state, message) } : {}),
837
- }))
838
- const saved = archived ?? next.outbox?.find((candidate) => candidate.id === message.id)
839
- if (!saved) throw new Error('Committed resident message is missing.')
840
- return saved
841
- }
842
-
843
- /** Admit one delivery against the same agenda snapshot used by the host's gate. */
844
- async claimMessage(
845
- expected: ResidentAgendaState,
846
- id: string,
847
- now: number,
848
- ): Promise<ResidentOutboxMessage> {
849
- z.string().uuid().parse(id)
850
- const next = await this.change(expected, (state) => {
851
- const outbox = state.outbox ?? []
852
- if (state.paused || outbox.some((message) => message.phase === 'sending'))
853
- throw new ResidentConflictError()
854
- const message = outbox.find((candidate) => candidate.id === id)
855
- if (!message) throw new Error('Unknown resident message.')
856
- const admitted = claimResidentOutboxMessage(message, now)
857
- return {
858
- ...state,
859
- outbox: outbox.map((candidate) => (candidate.id === id ? admitted : candidate)),
860
- }
861
- })
862
- const claim = next.outbox?.find((message) => message.id === id)
863
- if (!claim) throw new Error('Claimed resident message is missing.')
864
- return claim
865
- }
866
-
867
- /** Settle the exact delivery claim; unrelated agenda writes never repeat the transport. */
868
- async settleMessage(
869
- expected: ResidentOutboxMessage,
870
- outcome: ResidentDeliveryOutcome,
871
- now: number,
872
- ): Promise<ResidentOutboxMessage> {
873
- const claim = residentOutboxMessageSchema.parse(expected)
874
- const checked = residentDeliveryOutcomeSchema.parse(outcome)
875
- if (claim.tenantId !== this.tenantId || claim.agentKey !== this.agentKey)
876
- throw new ResidentConflictError()
877
- const validate = (state: ResidentAgendaState): ResidentOutboxMessage => {
878
- const message = state.outbox?.find((candidate) => candidate.id === claim.id)
879
- if (!message || message.revision !== claim.revision || message.claimId !== claim.claimId)
880
- throw new ResidentConflictError()
881
- return message
882
- }
883
- for (let attempt = 0; attempt < 8; attempt++) {
884
- const agenda = await this.read()
885
- if (agenda === null) throw new Error('Create the resident agenda first.')
886
- validate(agenda)
887
- try {
888
- const next = await this.change(agenda, (state) => {
889
- const current = validate(state)
890
- const settled = settleResidentOutboxMessage(current, checked, now)
891
- return {
892
- ...state,
893
- outbox: (state.outbox ?? []).map((message) =>
894
- message.id === claim.id ? settled : message,
895
- ),
896
- }
897
- })
898
- const settled = next.outbox?.find((message) => message.id === claim.id)
899
- if (!settled) throw new Error('Settled resident message is missing.')
900
- return settled
901
- } catch (error) {
902
- if (!(error instanceof ResidentConflictError) || attempt === 7) throw error
903
- }
904
- }
905
- throw new ResidentConflictError()
906
- }
907
-
908
- executionAt(id: string, expected: ResidentAgendaState): ResidentExecutionStore {
909
- const state = this.checked(expected)
910
- const pursuit = state.pursuits.find((p) => p.id === id)
911
- if (!pursuit) throw new Error('Unknown resident pursuit.')
912
- const execution = this.execution(id)
913
- return {
914
- read: async () => pursuit.state,
915
- settle: (...args) => execution.settle(...args),
916
- claim: (current, now) =>
917
- this.updatePursuit(
918
- id,
919
- current,
920
- (saved) => claimResidentState(saved, now),
921
- true,
922
- state.revision,
923
- ),
924
- }
925
- }
926
-
927
- execution(id: string): ResidentExecutionStore {
928
- z.string().uuid().parse(id)
929
- return {
930
- read: async () => (await this.read())?.pursuits.find((p) => p.id === id)?.state ?? null,
931
- claim: (expected, now) =>
932
- this.updatePursuit(id, expected, (state) => claimResidentState(state, now), true),
933
- settle: (expected, decision: ResidentDecision, now) =>
934
- this.updatePursuit(id, expected, (state) => settleResidentState(state, decision, now)),
935
- }
936
- }
937
- }