@lumenflow/cli 3.18.1 → 3.20.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 (175) hide show
  1. package/dist/docs-sync.js +123 -6
  2. package/dist/docs-sync.js.map +1 -1
  3. package/dist/gate-co-change.js +23 -4
  4. package/dist/gate-co-change.js.map +1 -1
  5. package/dist/gates-runners.js +113 -16
  6. package/dist/gates-runners.js.map +1 -1
  7. package/dist/gates-utils.js +71 -0
  8. package/dist/gates-utils.js.map +1 -1
  9. package/dist/lumenflow-upgrade.js +1 -0
  10. package/dist/lumenflow-upgrade.js.map +1 -1
  11. package/dist/public-manifest.js +1 -1
  12. package/dist/public-manifest.js.map +1 -1
  13. package/dist/sync-templates.js +13 -0
  14. package/dist/sync-templates.js.map +1 -1
  15. package/dist/wu-block.js +10 -0
  16. package/dist/wu-block.js.map +1 -1
  17. package/dist/wu-claim-validation.js +3 -1
  18. package/dist/wu-claim-validation.js.map +1 -1
  19. package/dist/wu-claim.js +3 -1
  20. package/dist/wu-claim.js.map +1 -1
  21. package/dist/wu-done-memory-telemetry.js +5 -1
  22. package/dist/wu-done-memory-telemetry.js.map +1 -1
  23. package/dist/wu-done-ownership.js +6 -0
  24. package/dist/wu-done-ownership.js.map +1 -1
  25. package/dist/wu-edit-operations.js +4 -4
  26. package/dist/wu-edit-operations.js.map +1 -1
  27. package/dist/wu-prep.js +88 -13
  28. package/dist/wu-prep.js.map +1 -1
  29. package/dist/wu-prune.js +2 -2
  30. package/dist/wu-prune.js.map +1 -1
  31. package/dist/wu-recover.js +15 -0
  32. package/dist/wu-recover.js.map +1 -1
  33. package/dist/wu-release.js +10 -1
  34. package/dist/wu-release.js.map +1 -1
  35. package/dist/wu-spawn-prompt-builders.js +27 -2
  36. package/dist/wu-spawn-prompt-builders.js.map +1 -1
  37. package/dist/wu-state-mutation-ownership.js +136 -0
  38. package/dist/wu-state-mutation-ownership.js.map +1 -0
  39. package/dist/wu-unblock.js +10 -0
  40. package/dist/wu-unblock.js.map +1 -1
  41. package/dist/wu-verify.js +22 -17
  42. package/dist/wu-verify.js.map +1 -1
  43. package/package.json +111 -110
  44. package/packs/agent-runtime/.turbo/turbo-build.log +1 -1
  45. package/packs/agent-runtime/package.json +1 -1
  46. package/packs/sidekick/.turbo/turbo-build.log +1 -1
  47. package/packs/sidekick/README.md +118 -113
  48. package/packs/sidekick/manifest-schema.ts +15 -228
  49. package/packs/sidekick/manifest.ts +107 -7
  50. package/packs/sidekick/manifest.yaml +199 -1
  51. package/packs/sidekick/package.json +4 -1
  52. package/packs/sidekick/policy-factory.ts +38 -0
  53. package/packs/sidekick/tool-impl/channel-tools.ts +99 -0
  54. package/packs/sidekick/tool-impl/memory-tools.ts +86 -1
  55. package/packs/sidekick/tool-impl/routine-tools.ts +156 -2
  56. package/packs/sidekick/tool-impl/storage.ts +6 -5
  57. package/packs/sidekick/tool-impl/task-tools.ts +186 -4
  58. package/packs/software-delivery/.turbo/turbo-build.log +1 -1
  59. package/packs/software-delivery/package.json +1 -1
  60. package/templates/core/AGENTS.md.template +157 -32
  61. package/templates/core/LUMENFLOW.md.template +44 -29
  62. package/templates/core/_frameworks/lumenflow/wu-sizing-guide.md.template +644 -0
  63. package/templates/core/ai/onboarding/agent-invocation-guide.md.template +5 -5
  64. package/templates/core/ai/onboarding/agent-safety-card.md.template +1 -0
  65. package/templates/core/ai/onboarding/docs-generation.md.template +94 -4
  66. package/templates/core/ai/onboarding/first-15-mins.md.template +1 -1
  67. package/templates/core/ai/onboarding/first-wu-mistakes.md.template +2 -1
  68. package/templates/core/ai/onboarding/initiative-orchestration.md.template +21 -21
  69. package/templates/core/ai/onboarding/quick-ref-commands.md.template +126 -109
  70. package/templates/core/ai/onboarding/release-process.md.template +12 -12
  71. package/templates/core/ai/onboarding/starting-prompt.md.template +33 -32
  72. package/templates/vendors/claude/.claude/skills/initiative-management/SKILL.md.template +2 -2
  73. package/templates/vendors/claude/.claude/skills/multi-agent-coordination/SKILL.md.template +2 -2
  74. package/templates/vendors/claude/.claude/skills/orchestration/SKILL.md.template +3 -3
  75. package/dist/chunk-2D2VOCA4.js +0 -37
  76. package/dist/chunk-2D5KFYGX.js +0 -284
  77. package/dist/chunk-2GXVIN57.js +0 -14072
  78. package/dist/chunk-2MQ7HZWZ.js +0 -26
  79. package/dist/chunk-2UFQ3A3C.js +0 -643
  80. package/dist/chunk-3RG5ZIWI.js +0 -10
  81. package/dist/chunk-4N74J3UT.js +0 -15
  82. package/dist/chunk-5GTOXFYR.js +0 -392
  83. package/dist/chunk-5VY6MQMC.js +0 -240
  84. package/dist/chunk-67XVPMRY.js +0 -1297
  85. package/dist/chunk-6HO4GWJE.js +0 -164
  86. package/dist/chunk-6W5XHWYV.js +0 -1890
  87. package/dist/chunk-6X4EMYJQ.js +0 -64
  88. package/dist/chunk-6XYXI2NQ.js +0 -772
  89. package/dist/chunk-7ANSOV6Q.js +0 -285
  90. package/dist/chunk-A624LFLB.js +0 -1380
  91. package/dist/chunk-ADN5NHG4.js +0 -126
  92. package/dist/chunk-B7YJYJKG.js +0 -33
  93. package/dist/chunk-CCLHCPKG.js +0 -210
  94. package/dist/chunk-CK36VROC.js +0 -1584
  95. package/dist/chunk-D3UOFRSB.js +0 -81
  96. package/dist/chunk-DFR4DJBM.js +0 -230
  97. package/dist/chunk-DSYBDHYH.js +0 -79
  98. package/dist/chunk-DWMLTXKQ.js +0 -1176
  99. package/dist/chunk-E3REJTAJ.js +0 -28
  100. package/dist/chunk-EA3IVO64.js +0 -633
  101. package/dist/chunk-EK2AKZKD.js +0 -55
  102. package/dist/chunk-ELD7JTTT.js +0 -343
  103. package/dist/chunk-EX6TT2XI.js +0 -195
  104. package/dist/chunk-EXINSFZE.js +0 -82
  105. package/dist/chunk-EZ6ZBYBM.js +0 -510
  106. package/dist/chunk-FBKAPTJ2.js +0 -16
  107. package/dist/chunk-FVLV5RYH.js +0 -1118
  108. package/dist/chunk-GDNSBQVK.js +0 -2485
  109. package/dist/chunk-GPQHMBNN.js +0 -278
  110. package/dist/chunk-GTFJB67L.js +0 -68
  111. package/dist/chunk-HANJXVKW.js +0 -1127
  112. package/dist/chunk-HEVS5YLD.js +0 -269
  113. package/dist/chunk-HMEVZKPQ.js +0 -9
  114. package/dist/chunk-HRGSYNLM.js +0 -3511
  115. package/dist/chunk-ISZR5N4K.js +0 -60
  116. package/dist/chunk-J6SUPR2C.js +0 -226
  117. package/dist/chunk-JERYVEIZ.js +0 -244
  118. package/dist/chunk-JHHWGL2N.js +0 -87
  119. package/dist/chunk-JONWQUB5.js +0 -775
  120. package/dist/chunk-K2DIWWDM.js +0 -1766
  121. package/dist/chunk-KY4PGL5V.js +0 -969
  122. package/dist/chunk-L737LQ4C.js +0 -1285
  123. package/dist/chunk-LFTWYIB2.js +0 -497
  124. package/dist/chunk-LV47RFNJ.js +0 -41
  125. package/dist/chunk-MKSAITI7.js +0 -15
  126. package/dist/chunk-MZ7RKIX4.js +0 -212
  127. package/dist/chunk-NAP6CFSO.js +0 -84
  128. package/dist/chunk-ND6MY37M.js +0 -16
  129. package/dist/chunk-NMG736UR.js +0 -683
  130. package/dist/chunk-NRAXROED.js +0 -32
  131. package/dist/chunk-NRIZR3A7.js +0 -690
  132. package/dist/chunk-NX43BG3M.js +0 -233
  133. package/dist/chunk-O645XLSI.js +0 -297
  134. package/dist/chunk-OMJD6A3S.js +0 -235
  135. package/dist/chunk-QB6SJD4T.js +0 -430
  136. package/dist/chunk-QFSTL4J3.js +0 -276
  137. package/dist/chunk-QLGDFMFX.js +0 -212
  138. package/dist/chunk-RIAAGL2E.js +0 -13
  139. package/dist/chunk-RWO5XMZ6.js +0 -86
  140. package/dist/chunk-RXRKBBSM.js +0 -149
  141. package/dist/chunk-RZOZMML6.js +0 -363
  142. package/dist/chunk-U7I7FS7T.js +0 -113
  143. package/dist/chunk-UI42RODY.js +0 -717
  144. package/dist/chunk-UTVMVSCO.js +0 -519
  145. package/dist/chunk-V6OJGLBA.js +0 -1746
  146. package/dist/chunk-W2JHVH7D.js +0 -152
  147. package/dist/chunk-WD3Y7VQN.js +0 -280
  148. package/dist/chunk-WOCTQ5MS.js +0 -303
  149. package/dist/chunk-WZR3ZUNN.js +0 -696
  150. package/dist/chunk-XGI665H7.js +0 -150
  151. package/dist/chunk-XKY65P2T.js +0 -304
  152. package/dist/chunk-Y4CQZY65.js +0 -57
  153. package/dist/chunk-YFEXKLVE.js +0 -194
  154. package/dist/chunk-YHO3HS5X.js +0 -287
  155. package/dist/chunk-YLS7AZSX.js +0 -738
  156. package/dist/chunk-ZE473AO6.js +0 -49
  157. package/dist/chunk-ZF747T3O.js +0 -644
  158. package/dist/chunk-ZHCZHZH3.js +0 -43
  159. package/dist/chunk-ZZNZX2XY.js +0 -87
  160. package/dist/constants-7QAP3VQ4.js +0 -23
  161. package/dist/dist-IY3UUMWK.js +0 -33
  162. package/dist/invariants-runner-W5RGHCSU.js +0 -27
  163. package/dist/lane-lock-6J36HD5O.js +0 -35
  164. package/dist/mem-checkpoint-core-EANG2GVN.js +0 -14
  165. package/dist/mem-signal-core-2LZ2WYHW.js +0 -19
  166. package/dist/memory-store-OLB5FO7K.js +0 -18
  167. package/dist/service-6BYCOCO5.js +0 -13
  168. package/dist/spawn-policy-resolver-NTSZYQ6R.js +0 -17
  169. package/dist/spawn-task-builder-R4E2BHSW.js +0 -22
  170. package/dist/wu-done-pr-WLFFFEPJ.js +0 -25
  171. package/dist/wu-done-validation-3J5E36FE.js +0 -30
  172. package/dist/wu-duplicate-id-detector-5S7JHELK.js +0 -232
  173. package/packs/sidekick/.turbo/turbo-test.log +0 -12
  174. package/packs/sidekick/.turbo/turbo-typecheck.log +0 -4
  175. package/packs/software-delivery/.turbo/turbo-typecheck.log +0 -4
@@ -1,6 +1,7 @@
1
1
  id: sidekick
2
2
  version: 0.1.0
3
3
  config_key: sidekick
4
+ policy_factory: policy-factory.ts#createSidekickPolicyFactory
4
5
  task_types:
5
6
  - sidekick
6
7
  tools:
@@ -49,7 +50,7 @@ tools:
49
50
  properties:
50
51
  status:
51
52
  type: string
52
- enum: [pending, done]
53
+ enum: [pending, done, canceled]
53
54
  priority:
54
55
  type: string
55
56
  enum: [P0, P1, P2, P3]
@@ -67,6 +68,67 @@ tools:
67
68
  output_schema:
68
69
  type: object
69
70
  additionalProperties: true
71
+ - name: task:update
72
+ entry: tool-impl/task-tools.ts
73
+ permission: write
74
+ required_scopes:
75
+ - type: path
76
+ pattern: .sidekick/**
77
+ access: read
78
+ - type: path
79
+ pattern: .sidekick/**
80
+ access: write
81
+ input_schema:
82
+ type: object
83
+ properties:
84
+ id:
85
+ type: string
86
+ minLength: 1
87
+ title:
88
+ type: string
89
+ minLength: 1
90
+ description:
91
+ type: string
92
+ priority:
93
+ type: string
94
+ enum: [P0, P1, P2, P3]
95
+ tags:
96
+ type: array
97
+ items: { type: string }
98
+ due_at:
99
+ type: string
100
+ cron:
101
+ type: string
102
+ dry_run:
103
+ type: boolean
104
+ required: [id]
105
+ additionalProperties: false
106
+ output_schema:
107
+ type: object
108
+ additionalProperties: true
109
+ - name: task:cancel
110
+ entry: tool-impl/task-tools.ts
111
+ permission: admin
112
+ required_scopes:
113
+ - type: path
114
+ pattern: .sidekick/**
115
+ access: read
116
+ - type: path
117
+ pattern: .sidekick/**
118
+ access: write
119
+ input_schema:
120
+ type: object
121
+ properties:
122
+ id:
123
+ type: string
124
+ minLength: 1
125
+ dry_run:
126
+ type: boolean
127
+ required: [id]
128
+ additionalProperties: false
129
+ output_schema:
130
+ type: object
131
+ additionalProperties: true
70
132
  - name: task:complete
71
133
  entry: tool-impl/task-tools.ts
72
134
  permission: write
@@ -174,6 +236,29 @@ tools:
174
236
  type: object
175
237
  additionalProperties: true
176
238
  - name: memory:forget
239
+ entry: tool-impl/memory-tools.ts
240
+ permission: admin
241
+ required_scopes:
242
+ - type: path
243
+ pattern: .sidekick/**
244
+ access: read
245
+ - type: path
246
+ pattern: .sidekick/**
247
+ access: write
248
+ input_schema:
249
+ type: object
250
+ properties:
251
+ id:
252
+ type: string
253
+ minLength: 1
254
+ dry_run:
255
+ type: boolean
256
+ required: [id]
257
+ additionalProperties: false
258
+ output_schema:
259
+ type: object
260
+ additionalProperties: true
261
+ - name: memory:update
177
262
  entry: tool-impl/memory-tools.ts
178
263
  permission: write
179
264
  required_scopes:
@@ -189,6 +274,15 @@ tools:
189
274
  id:
190
275
  type: string
191
276
  minLength: 1
277
+ type:
278
+ type: string
279
+ enum: [fact, preference, note, snippet]
280
+ content:
281
+ type: string
282
+ minLength: 1
283
+ tags:
284
+ type: array
285
+ items: { type: string }
192
286
  dry_run:
193
287
  type: boolean
194
288
  required: [id]
@@ -222,6 +316,43 @@ tools:
222
316
  output_schema:
223
317
  type: object
224
318
  additionalProperties: true
319
+ - name: channel:list
320
+ entry: tool-impl/channel-tools.ts
321
+ permission: read
322
+ required_scopes:
323
+ - type: path
324
+ pattern: .sidekick/**
325
+ access: read
326
+ input_schema:
327
+ type: object
328
+ properties: {}
329
+ additionalProperties: false
330
+ output_schema:
331
+ type: object
332
+ additionalProperties: true
333
+ - name: channel:delete
334
+ entry: tool-impl/channel-tools.ts
335
+ permission: admin
336
+ required_scopes:
337
+ - type: path
338
+ pattern: .sidekick/**
339
+ access: read
340
+ - type: path
341
+ pattern: .sidekick/**
342
+ access: write
343
+ input_schema:
344
+ type: object
345
+ properties:
346
+ id:
347
+ type: string
348
+ minLength: 1
349
+ dry_run:
350
+ type: boolean
351
+ required: [id]
352
+ additionalProperties: false
353
+ output_schema:
354
+ type: object
355
+ additionalProperties: true
225
356
  - name: channel:send
226
357
  entry: tool-impl/channel-tools.ts
227
358
  permission: write
@@ -342,6 +473,73 @@ tools:
342
473
  output_schema:
343
474
  type: object
344
475
  additionalProperties: true
476
+ - name: routine:update
477
+ entry: tool-impl/routine-tools.ts
478
+ permission: write
479
+ required_scopes:
480
+ - type: path
481
+ pattern: .sidekick/**
482
+ access: read
483
+ - type: path
484
+ pattern: .sidekick/**
485
+ access: write
486
+ input_schema:
487
+ type: object
488
+ properties:
489
+ id:
490
+ type: string
491
+ minLength: 1
492
+ name:
493
+ type: string
494
+ minLength: 1
495
+ steps:
496
+ type: array
497
+ minItems: 1
498
+ items:
499
+ type: object
500
+ properties:
501
+ tool:
502
+ type: string
503
+ minLength: 1
504
+ input:
505
+ type: object
506
+ additionalProperties: true
507
+ required: [tool]
508
+ additionalProperties: false
509
+ cron:
510
+ type: string
511
+ enabled:
512
+ type: boolean
513
+ dry_run:
514
+ type: boolean
515
+ required: [id]
516
+ additionalProperties: false
517
+ output_schema:
518
+ type: object
519
+ additionalProperties: true
520
+ - name: routine:delete
521
+ entry: tool-impl/routine-tools.ts
522
+ permission: admin
523
+ required_scopes:
524
+ - type: path
525
+ pattern: .sidekick/**
526
+ access: read
527
+ - type: path
528
+ pattern: .sidekick/**
529
+ access: write
530
+ input_schema:
531
+ type: object
532
+ properties:
533
+ id:
534
+ type: string
535
+ minLength: 1
536
+ dry_run:
537
+ type: boolean
538
+ required: [id]
539
+ additionalProperties: false
540
+ output_schema:
541
+ type: object
542
+ additionalProperties: true
345
543
  - name: routine:run
346
544
  entry: tool-impl/routine-tools.ts
347
545
  permission: read
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lumenflow/packs-sidekick",
3
- "version": "3.18.1",
3
+ "version": "3.20.0",
4
4
  "description": "Sidekick personal assistant pack for LumenFlow — 16 tools for task management, typed memory, channels, routines, and audit",
5
5
  "keywords": [
6
6
  "lumenflow",
@@ -50,6 +50,9 @@
50
50
  "test": "vitest run",
51
51
  "typecheck": "tsc --noEmit"
52
52
  },
53
+ "dependencies": {
54
+ "@lumenflow/kernel": "workspace:^"
55
+ },
53
56
  "devDependencies": {
54
57
  "typescript": "^5.9.3",
55
58
  "vitest": "^4.0.18"
@@ -0,0 +1,38 @@
1
+ // Copyright (c) 2026 Hellmai Ltd
2
+ // SPDX-License-Identifier: AGPL-3.0-only
3
+
4
+ import { POLICY_TRIGGERS, type PackPolicyFactory, type PolicyRule } from '@lumenflow/kernel';
5
+ import { SIDEKICK_POLICY_ID_PREFIX } from './constants.js';
6
+
7
+ export const SIDEKICK_APPROVAL_REQUIRED_TOOL_NAMES = [
8
+ 'task:cancel',
9
+ 'memory:forget',
10
+ 'channel:delete',
11
+ 'routine:delete',
12
+ ] as const;
13
+
14
+ const SIDEKICK_APPROVAL_REQUIRED_TOOL_NAME_SET = new Set<string>(
15
+ SIDEKICK_APPROVAL_REQUIRED_TOOL_NAMES,
16
+ );
17
+
18
+ export function isSidekickApprovalRequiredToolName(toolName: string): boolean {
19
+ return SIDEKICK_APPROVAL_REQUIRED_TOOL_NAME_SET.has(toolName);
20
+ }
21
+
22
+ export const createSidekickPolicyFactory: PackPolicyFactory = async () => {
23
+ if (SIDEKICK_APPROVAL_REQUIRED_TOOL_NAME_SET.size === 0) {
24
+ return [];
25
+ }
26
+
27
+ const approvalRule: PolicyRule = {
28
+ id: `${SIDEKICK_POLICY_ID_PREFIX}.destructive-approval`,
29
+ trigger: POLICY_TRIGGERS.ON_TOOL_REQUEST,
30
+ decision: 'approval_required',
31
+ reason: 'Destructive Sidekick tools require explicit approval before execution.',
32
+ when: (context) =>
33
+ typeof context.tool_name === 'string' &&
34
+ isSidekickApprovalRequiredToolName(context.tool_name.trim()),
35
+ };
36
+
37
+ return [approvalRule];
38
+ };
@@ -23,6 +23,8 @@ import {
23
23
 
24
24
  const TOOL_NAMES = {
25
25
  CONFIGURE: 'channel:configure',
26
+ LIST: 'channel:list',
27
+ DELETE: 'channel:delete',
26
28
  SEND: 'channel:send',
27
29
  RECEIVE: 'channel:receive',
28
30
  } as const;
@@ -116,6 +118,99 @@ async function channelConfigureTool(
116
118
  return success({ channel: resolvedChannel as unknown as Record<string, unknown> });
117
119
  }
118
120
 
121
+ // ---------------------------------------------------------------------------
122
+ // channel:list
123
+ // ---------------------------------------------------------------------------
124
+
125
+ async function channelListTool(_input: unknown, _context?: ToolContextLike): Promise<ToolOutput> {
126
+ const storage = getStoragePort();
127
+ const channels = await storage.readStore('channels');
128
+ const messages = await storage.readStore('messages');
129
+
130
+ const items = channels
131
+ .map((channel) => {
132
+ const channelMessages = messages.filter((message) => message.channel_id === channel.id);
133
+ const lastMessage = channelMessages.toSorted(
134
+ (left, right) => Date.parse(right.created_at) - Date.parse(left.created_at),
135
+ )[0];
136
+
137
+ return {
138
+ ...channel,
139
+ message_count: channelMessages.length,
140
+ last_message_at: lastMessage?.created_at,
141
+ };
142
+ })
143
+ .toSorted((left, right) => {
144
+ const leftTs = left.last_message_at ?? left.updated_at;
145
+ const rightTs = right.last_message_at ?? right.updated_at;
146
+ return Date.parse(rightTs) - Date.parse(leftTs);
147
+ });
148
+
149
+ return success({
150
+ items: items as unknown as Record<string, unknown>,
151
+ count: items.length,
152
+ });
153
+ }
154
+
155
+ // ---------------------------------------------------------------------------
156
+ // channel:delete
157
+ // ---------------------------------------------------------------------------
158
+
159
+ async function channelDeleteTool(input: unknown, context?: ToolContextLike): Promise<ToolOutput> {
160
+ const parsed = toRecord(input);
161
+ const id = asNonEmptyString(parsed.id);
162
+
163
+ if (!id) {
164
+ return failure('INVALID_INPUT', 'id is required.');
165
+ }
166
+
167
+ const storage = getStoragePort();
168
+ const channels = await storage.readStore('channels');
169
+ const channel = channels.find((entry) => entry.id === id);
170
+
171
+ if (!channel) {
172
+ return failure('NOT_FOUND', `channel ${id} was not found.`);
173
+ }
174
+
175
+ const messages = await storage.readStore('messages');
176
+ const deletedMessages = messages.filter((message) => message.channel_id === id);
177
+
178
+ if (isDryRun(parsed)) {
179
+ return success({
180
+ dry_run: true,
181
+ deleted_id: id,
182
+ deleted_message_count: deletedMessages.length,
183
+ });
184
+ }
185
+
186
+ await storage.withLock(async () => {
187
+ const latestChannels = await storage.readStore('channels');
188
+ const latestMessages = await storage.readStore('messages');
189
+ await storage.writeStore(
190
+ 'channels',
191
+ latestChannels.filter((entry) => entry.id !== id),
192
+ );
193
+ await storage.writeStore(
194
+ 'messages',
195
+ latestMessages.filter((message) => message.channel_id !== id),
196
+ );
197
+ await storage.appendAudit(
198
+ buildAuditEvent({
199
+ tool: TOOL_NAMES.DELETE,
200
+ op: 'delete',
201
+ context,
202
+ ids: [id],
203
+ details: { deleted_message_count: deletedMessages.length },
204
+ }),
205
+ );
206
+ });
207
+
208
+ return success({
209
+ deleted_id: id,
210
+ deleted_message_count: deletedMessages.length,
211
+ });
212
+ }
213
+
119
214
  // ---------------------------------------------------------------------------
120
215
  // channel:send
121
216
  // ---------------------------------------------------------------------------
@@ -390,6 +485,10 @@ export default async function channelTools(
390
485
  switch (context?.tool_name) {
391
486
  case TOOL_NAMES.CONFIGURE:
392
487
  return channelConfigureTool(input, context);
488
+ case TOOL_NAMES.LIST:
489
+ return channelListTool(input, context);
490
+ case TOOL_NAMES.DELETE:
491
+ return channelDeleteTool(input, context);
393
492
  case TOOL_NAMES.SEND:
394
493
  return channelSendTool(input, context);
395
494
  case TOOL_NAMES.RECEIVE:
@@ -26,10 +26,11 @@ import {
26
26
  const TOOL_NAMES = {
27
27
  STORE: 'memory:store',
28
28
  RECALL: 'memory:recall',
29
+ UPDATE: 'memory:update',
29
30
  FORGET: 'memory:forget',
30
31
  } as const;
31
32
 
32
- const VALID_MEMORY_TYPES: MemoryType[] = ['fact', 'preference', 'note'];
33
+ const VALID_MEMORY_TYPES: MemoryType[] = ['fact', 'preference', 'note', 'snippet'];
33
34
 
34
35
  // ---------------------------------------------------------------------------
35
36
  // Helpers
@@ -126,6 +127,88 @@ async function memoryRecallTool(input: unknown, _context?: ToolContextLike): Pro
126
127
  });
127
128
  }
128
129
 
130
+ // ---------------------------------------------------------------------------
131
+ // memory:update
132
+ // ---------------------------------------------------------------------------
133
+
134
+ async function memoryUpdateTool(input: unknown, context?: ToolContextLike): Promise<ToolOutput> {
135
+ const parsed = toRecord(input);
136
+ const id = asNonEmptyString(parsed.id);
137
+
138
+ if (!id) {
139
+ return failure('INVALID_INPUT', 'id is required.');
140
+ }
141
+
142
+ const patch: Partial<MemoryRecord> = {};
143
+ if (parsed.type !== undefined) {
144
+ const type = asMemoryType(parsed.type);
145
+ if (!type) {
146
+ return failure('INVALID_INPUT', 'type must be one of fact, preference, note, snippet.');
147
+ }
148
+ patch.type = type;
149
+ }
150
+ if (parsed.content !== undefined) {
151
+ const content = asNonEmptyString(parsed.content);
152
+ if (!content) {
153
+ return failure('INVALID_INPUT', 'content must be a non-empty string when provided.');
154
+ }
155
+ patch.content = content;
156
+ }
157
+ if (parsed.tags !== undefined) {
158
+ patch.tags = asStringArray(parsed.tags);
159
+ }
160
+
161
+ if (Object.keys(patch).length === 0) {
162
+ return failure(
163
+ 'INVALID_INPUT',
164
+ 'memory:update requires at least one of type, content, or tags.',
165
+ );
166
+ }
167
+
168
+ const storage = getStoragePort();
169
+ const memories = await storage.readStore('memories');
170
+ const memory = memories.find((entry) => entry.id === id);
171
+
172
+ if (!memory) {
173
+ return failure('NOT_FOUND', `memory ${id} was not found.`);
174
+ }
175
+
176
+ const preview: MemoryRecord = {
177
+ ...memory,
178
+ ...patch,
179
+ updated_at: nowIso(),
180
+ };
181
+
182
+ if (isDryRun(parsed)) {
183
+ return success({
184
+ dry_run: true,
185
+ memory: preview as unknown as Record<string, unknown>,
186
+ });
187
+ }
188
+
189
+ await storage.withLock(async () => {
190
+ const latest = await storage.readStore('memories');
191
+ const target = latest.find((entry) => entry.id === id);
192
+ if (!target) {
193
+ return;
194
+ }
195
+ Object.assign(target, preview);
196
+ await storage.writeStore('memories', latest);
197
+ await storage.appendAudit(
198
+ buildAuditEvent({
199
+ tool: TOOL_NAMES.UPDATE,
200
+ op: 'update',
201
+ context,
202
+ ids: [id],
203
+ }),
204
+ );
205
+ });
206
+
207
+ const updated = await storage.readStore('memories');
208
+ const updatedMemory = updated.find((entry) => entry.id === id);
209
+ return success({ memory: updatedMemory as unknown as Record<string, unknown> });
210
+ }
211
+
129
212
  // ---------------------------------------------------------------------------
130
213
  // memory:forget
131
214
  // ---------------------------------------------------------------------------
@@ -180,6 +263,8 @@ export default async function memoryTools(
180
263
  return memoryStoreTool(input, context);
181
264
  case TOOL_NAMES.RECALL:
182
265
  return memoryRecallTool(input, context);
266
+ case TOOL_NAMES.UPDATE:
267
+ return memoryUpdateTool(input, context);
183
268
  case TOOL_NAMES.FORGET:
184
269
  return memoryForgetTool(input, context);
185
270
  default: