@lumenflow/cli 3.19.0 → 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.
- package/dist/gates-runners.js +5 -4
- package/dist/gates-runners.js.map +1 -1
- package/dist/gates-utils.js +71 -0
- package/dist/gates-utils.js.map +1 -1
- package/dist/wu-prune.js +2 -2
- package/dist/wu-prune.js.map +1 -1
- package/dist/wu-verify.js +22 -17
- package/dist/wu-verify.js.map +1 -1
- package/package.json +8 -8
- package/packs/agent-runtime/.turbo/turbo-build.log +1 -1
- package/packs/agent-runtime/package.json +1 -1
- package/packs/sidekick/.turbo/turbo-build.log +1 -1
- package/packs/sidekick/README.md +118 -113
- package/packs/sidekick/manifest-schema.ts +15 -228
- package/packs/sidekick/manifest.ts +107 -7
- package/packs/sidekick/manifest.yaml +199 -1
- package/packs/sidekick/package.json +4 -1
- package/packs/sidekick/policy-factory.ts +38 -0
- package/packs/sidekick/tool-impl/channel-tools.ts +99 -0
- package/packs/sidekick/tool-impl/memory-tools.ts +86 -1
- package/packs/sidekick/tool-impl/routine-tools.ts +156 -2
- package/packs/sidekick/tool-impl/storage.ts +6 -5
- package/packs/sidekick/tool-impl/task-tools.ts +186 -4
- package/packs/software-delivery/.turbo/turbo-build.log +1 -1
- package/packs/software-delivery/package.json +1 -1
- package/templates/core/_frameworks/lumenflow/wu-sizing-guide.md.template +2 -2
- package/templates/core/ai/onboarding/quick-ref-commands.md.template +27 -17
- package/templates/core/ai/onboarding/starting-prompt.md.template +2 -1
|
@@ -34,26 +34,33 @@ const SIDEKICK_SCOPE_WRITE: PathScope = {
|
|
|
34
34
|
};
|
|
35
35
|
|
|
36
36
|
// ---------------------------------------------------------------------------
|
|
37
|
-
//
|
|
37
|
+
// Sidekick tool contract
|
|
38
38
|
// ---------------------------------------------------------------------------
|
|
39
39
|
|
|
40
40
|
const TOOL_PERMISSIONS_MAP = {
|
|
41
|
-
// Task tools (
|
|
41
|
+
// Task tools (6)
|
|
42
42
|
'task:create': TOOL_PERMISSIONS.WRITE,
|
|
43
43
|
'task:list': TOOL_PERMISSIONS.READ,
|
|
44
|
+
'task:update': TOOL_PERMISSIONS.WRITE,
|
|
45
|
+
'task:cancel': TOOL_PERMISSIONS.ADMIN,
|
|
44
46
|
'task:complete': TOOL_PERMISSIONS.WRITE,
|
|
45
47
|
'task:schedule': TOOL_PERMISSIONS.WRITE,
|
|
46
|
-
// Memory tools (
|
|
48
|
+
// Memory tools (4)
|
|
47
49
|
'memory:store': TOOL_PERMISSIONS.WRITE,
|
|
48
50
|
'memory:recall': TOOL_PERMISSIONS.READ,
|
|
49
|
-
'memory:
|
|
50
|
-
|
|
51
|
+
'memory:update': TOOL_PERMISSIONS.WRITE,
|
|
52
|
+
'memory:forget': TOOL_PERMISSIONS.ADMIN,
|
|
53
|
+
// Channel tools (5)
|
|
51
54
|
'channel:configure': TOOL_PERMISSIONS.WRITE,
|
|
55
|
+
'channel:list': TOOL_PERMISSIONS.READ,
|
|
56
|
+
'channel:delete': TOOL_PERMISSIONS.ADMIN,
|
|
52
57
|
'channel:send': TOOL_PERMISSIONS.WRITE,
|
|
53
58
|
'channel:receive': TOOL_PERMISSIONS.READ,
|
|
54
|
-
// Routine tools (
|
|
59
|
+
// Routine tools (5)
|
|
55
60
|
'routine:create': TOOL_PERMISSIONS.WRITE,
|
|
56
61
|
'routine:list': TOOL_PERMISSIONS.READ,
|
|
62
|
+
'routine:update': TOOL_PERMISSIONS.WRITE,
|
|
63
|
+
'routine:delete': TOOL_PERMISSIONS.ADMIN,
|
|
57
64
|
'routine:run': TOOL_PERMISSIONS.READ, // plan-only, no execution
|
|
58
65
|
// System tools (3)
|
|
59
66
|
'sidekick:init': TOOL_PERMISSIONS.WRITE,
|
|
@@ -68,20 +75,28 @@ const MEMORY_TOOLS_ENTRY = 'tool-impl/memory-tools.ts';
|
|
|
68
75
|
const CHANNEL_TOOLS_ENTRY = 'tool-impl/channel-tools.ts';
|
|
69
76
|
const ROUTINE_TOOLS_ENTRY = 'tool-impl/routine-tools.ts';
|
|
70
77
|
const SYSTEM_TOOLS_ENTRY = 'tool-impl/system-tools.ts';
|
|
78
|
+
const POLICY_FACTORY_ENTRY = 'policy-factory.ts#createSidekickPolicyFactory';
|
|
71
79
|
|
|
72
80
|
const TOOL_ENTRIES: Record<SidekickToolName, string> = {
|
|
73
81
|
'task:create': TASK_TOOLS_ENTRY,
|
|
74
82
|
'task:list': TASK_TOOLS_ENTRY,
|
|
83
|
+
'task:update': TASK_TOOLS_ENTRY,
|
|
84
|
+
'task:cancel': TASK_TOOLS_ENTRY,
|
|
75
85
|
'task:complete': TASK_TOOLS_ENTRY,
|
|
76
86
|
'task:schedule': TASK_TOOLS_ENTRY,
|
|
77
87
|
'memory:store': MEMORY_TOOLS_ENTRY,
|
|
78
88
|
'memory:recall': MEMORY_TOOLS_ENTRY,
|
|
89
|
+
'memory:update': MEMORY_TOOLS_ENTRY,
|
|
79
90
|
'memory:forget': MEMORY_TOOLS_ENTRY,
|
|
80
91
|
'channel:configure': CHANNEL_TOOLS_ENTRY,
|
|
92
|
+
'channel:list': CHANNEL_TOOLS_ENTRY,
|
|
93
|
+
'channel:delete': CHANNEL_TOOLS_ENTRY,
|
|
81
94
|
'channel:send': CHANNEL_TOOLS_ENTRY,
|
|
82
95
|
'channel:receive': CHANNEL_TOOLS_ENTRY,
|
|
83
96
|
'routine:create': ROUTINE_TOOLS_ENTRY,
|
|
84
97
|
'routine:list': ROUTINE_TOOLS_ENTRY,
|
|
98
|
+
'routine:update': ROUTINE_TOOLS_ENTRY,
|
|
99
|
+
'routine:delete': ROUTINE_TOOLS_ENTRY,
|
|
85
100
|
'routine:run': ROUTINE_TOOLS_ENTRY,
|
|
86
101
|
'sidekick:init': SYSTEM_TOOLS_ENTRY,
|
|
87
102
|
'sidekick:status': SYSTEM_TOOLS_ENTRY,
|
|
@@ -109,7 +124,7 @@ const TOOL_INPUT_SCHEMAS: Record<SidekickToolName, Record<string, unknown>> = {
|
|
|
109
124
|
'task:list': {
|
|
110
125
|
type: 'object',
|
|
111
126
|
properties: {
|
|
112
|
-
status: { type: 'string', enum: ['pending', 'done'] },
|
|
127
|
+
status: { type: 'string', enum: ['pending', 'done', 'canceled'] },
|
|
113
128
|
priority: { type: 'string', enum: ['P0', 'P1', 'P2', 'P3'] },
|
|
114
129
|
tags: { type: 'array', items: { type: 'string' } },
|
|
115
130
|
search: { type: 'string' },
|
|
@@ -118,6 +133,30 @@ const TOOL_INPUT_SCHEMAS: Record<SidekickToolName, Record<string, unknown>> = {
|
|
|
118
133
|
},
|
|
119
134
|
additionalProperties: false,
|
|
120
135
|
},
|
|
136
|
+
'task:update': {
|
|
137
|
+
type: 'object',
|
|
138
|
+
properties: {
|
|
139
|
+
id: { type: 'string', minLength: 1 },
|
|
140
|
+
title: { type: 'string', minLength: 1 },
|
|
141
|
+
description: { type: 'string' },
|
|
142
|
+
priority: { type: 'string', enum: ['P0', 'P1', 'P2', 'P3'] },
|
|
143
|
+
tags: { type: 'array', items: { type: 'string' } },
|
|
144
|
+
due_at: { type: 'string' },
|
|
145
|
+
cron: { type: 'string' },
|
|
146
|
+
dry_run: { type: 'boolean' },
|
|
147
|
+
},
|
|
148
|
+
required: ['id'],
|
|
149
|
+
additionalProperties: false,
|
|
150
|
+
},
|
|
151
|
+
'task:cancel': {
|
|
152
|
+
type: 'object',
|
|
153
|
+
properties: {
|
|
154
|
+
id: { type: 'string', minLength: 1 },
|
|
155
|
+
dry_run: { type: 'boolean' },
|
|
156
|
+
},
|
|
157
|
+
required: ['id'],
|
|
158
|
+
additionalProperties: false,
|
|
159
|
+
},
|
|
121
160
|
'task:complete': {
|
|
122
161
|
type: 'object',
|
|
123
162
|
properties: {
|
|
@@ -169,6 +208,18 @@ const TOOL_INPUT_SCHEMAS: Record<SidekickToolName, Record<string, unknown>> = {
|
|
|
169
208
|
required: ['id'],
|
|
170
209
|
additionalProperties: false,
|
|
171
210
|
},
|
|
211
|
+
'memory:update': {
|
|
212
|
+
type: 'object',
|
|
213
|
+
properties: {
|
|
214
|
+
id: { type: 'string', minLength: 1 },
|
|
215
|
+
type: { type: 'string', enum: ['fact', 'preference', 'note', 'snippet'] },
|
|
216
|
+
content: { type: 'string', minLength: 1 },
|
|
217
|
+
tags: { type: 'array', items: { type: 'string' } },
|
|
218
|
+
dry_run: { type: 'boolean' },
|
|
219
|
+
},
|
|
220
|
+
required: ['id'],
|
|
221
|
+
additionalProperties: false,
|
|
222
|
+
},
|
|
172
223
|
'channel:configure': {
|
|
173
224
|
type: 'object',
|
|
174
225
|
properties: {
|
|
@@ -179,6 +230,20 @@ const TOOL_INPUT_SCHEMAS: Record<SidekickToolName, Record<string, unknown>> = {
|
|
|
179
230
|
required: ['name'],
|
|
180
231
|
additionalProperties: false,
|
|
181
232
|
},
|
|
233
|
+
'channel:list': {
|
|
234
|
+
type: 'object',
|
|
235
|
+
properties: {},
|
|
236
|
+
additionalProperties: false,
|
|
237
|
+
},
|
|
238
|
+
'channel:delete': {
|
|
239
|
+
type: 'object',
|
|
240
|
+
properties: {
|
|
241
|
+
id: { type: 'string', minLength: 1 },
|
|
242
|
+
dry_run: { type: 'boolean' },
|
|
243
|
+
},
|
|
244
|
+
required: ['id'],
|
|
245
|
+
additionalProperties: false,
|
|
246
|
+
},
|
|
182
247
|
'channel:send': {
|
|
183
248
|
type: 'object',
|
|
184
249
|
properties: {
|
|
@@ -236,6 +301,40 @@ const TOOL_INPUT_SCHEMAS: Record<SidekickToolName, Record<string, unknown>> = {
|
|
|
236
301
|
},
|
|
237
302
|
additionalProperties: false,
|
|
238
303
|
},
|
|
304
|
+
'routine:update': {
|
|
305
|
+
type: 'object',
|
|
306
|
+
properties: {
|
|
307
|
+
id: { type: 'string', minLength: 1 },
|
|
308
|
+
name: { type: 'string', minLength: 1 },
|
|
309
|
+
steps: {
|
|
310
|
+
type: 'array',
|
|
311
|
+
minItems: 1,
|
|
312
|
+
items: {
|
|
313
|
+
type: 'object',
|
|
314
|
+
properties: {
|
|
315
|
+
tool: { type: 'string', minLength: 1 },
|
|
316
|
+
input: { type: 'object', additionalProperties: true },
|
|
317
|
+
},
|
|
318
|
+
required: ['tool'],
|
|
319
|
+
additionalProperties: false,
|
|
320
|
+
},
|
|
321
|
+
},
|
|
322
|
+
cron: { type: 'string' },
|
|
323
|
+
enabled: { type: 'boolean' },
|
|
324
|
+
dry_run: { type: 'boolean' },
|
|
325
|
+
},
|
|
326
|
+
required: ['id'],
|
|
327
|
+
additionalProperties: false,
|
|
328
|
+
},
|
|
329
|
+
'routine:delete': {
|
|
330
|
+
type: 'object',
|
|
331
|
+
properties: {
|
|
332
|
+
id: { type: 'string', minLength: 1 },
|
|
333
|
+
dry_run: { type: 'boolean' },
|
|
334
|
+
},
|
|
335
|
+
required: ['id'],
|
|
336
|
+
additionalProperties: false,
|
|
337
|
+
},
|
|
239
338
|
'routine:run': {
|
|
240
339
|
type: 'object',
|
|
241
340
|
properties: {
|
|
@@ -310,6 +409,7 @@ const SIDEKICK_MANIFEST_TEMPLATE = {
|
|
|
310
409
|
id: SIDEKICK_PACK_ID,
|
|
311
410
|
version: SIDEKICK_PACK_VERSION,
|
|
312
411
|
config_key: SIDEKICK_PACK_ID,
|
|
412
|
+
policy_factory: POLICY_FACTORY_ENTRY,
|
|
313
413
|
task_types: ['sidekick'],
|
|
314
414
|
tools: SIDEKICK_TOOL_NAMES.map((name) => buildTool(name)),
|
|
315
415
|
policies: [
|
|
@@ -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.
|
|
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:
|