@lumenflow/cli 3.9.2 → 3.9.6
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/package.json +8 -8
- package/packs/sidekick/.turbo/turbo-build.log +1 -1
- package/packs/sidekick/manifest.ts +5 -0
- package/packs/sidekick/manifest.yaml +12 -0
- package/packs/sidekick/package.json +2 -1
- package/packs/sidekick/tool-impl/channel-tools.ts +174 -0
- package/packs/sidekick/tool-impl/channel-transports.ts +75 -0
- package/packs/sidekick/tool-impl/index.ts +7 -0
- package/packs/sidekick/tool-impl/runtime-context.ts +24 -0
- package/packs/sidekick/tool-impl/shared.ts +1 -0
- package/packs/sidekick/tool-impl/storage.ts +10 -4
- package/packs/software-delivery/.turbo/turbo-build.log +1 -1
- package/packs/software-delivery/package.json +1 -1
- package/templates/core/LUMENFLOW.md.template +24 -24
- package/templates/core/ai/onboarding/agent-invocation-guide.md.template +9 -0
- package/templates/core/ai/onboarding/quick-ref-commands.md.template +1 -1
- package/templates/core/ai/onboarding/release-process.md.template +51 -34
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lumenflow/cli",
|
|
3
|
-
"version": "3.9.
|
|
3
|
+
"version": "3.9.6",
|
|
4
4
|
"description": "Command-line interface for LumenFlow workflow framework",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"lumenflow",
|
|
@@ -180,13 +180,13 @@
|
|
|
180
180
|
"xstate": "^5.28.0",
|
|
181
181
|
"yaml": "^2.8.2",
|
|
182
182
|
"zod": "^4.3.6",
|
|
183
|
-
"@lumenflow/agent": "3.9.
|
|
184
|
-
"@lumenflow/
|
|
185
|
-
"@lumenflow/
|
|
186
|
-
"@lumenflow/initiatives": "3.9.
|
|
187
|
-
"@lumenflow/memory": "3.9.
|
|
188
|
-
"@lumenflow/
|
|
189
|
-
"@lumenflow/metrics": "3.9.
|
|
183
|
+
"@lumenflow/agent": "3.9.6",
|
|
184
|
+
"@lumenflow/core": "3.9.6",
|
|
185
|
+
"@lumenflow/control-plane-sdk": "3.9.6",
|
|
186
|
+
"@lumenflow/initiatives": "3.9.6",
|
|
187
|
+
"@lumenflow/memory": "3.9.6",
|
|
188
|
+
"@lumenflow/kernel": "3.9.6",
|
|
189
|
+
"@lumenflow/metrics": "3.9.6"
|
|
190
190
|
},
|
|
191
191
|
"devDependencies": {
|
|
192
192
|
"@vitest/coverage-v8": "^4.0.18",
|
|
@@ -182,9 +182,11 @@ const TOOL_INPUT_SCHEMAS: Record<SidekickToolName, Record<string, unknown>> = {
|
|
|
182
182
|
'channel:send': {
|
|
183
183
|
type: 'object',
|
|
184
184
|
properties: {
|
|
185
|
+
provider: { type: 'string' },
|
|
185
186
|
channel: { type: 'string' },
|
|
186
187
|
content: { type: 'string', minLength: 1 },
|
|
187
188
|
sender: { type: 'string' },
|
|
189
|
+
metadata: { type: 'object', additionalProperties: true },
|
|
188
190
|
dry_run: { type: 'boolean' },
|
|
189
191
|
},
|
|
190
192
|
required: ['content'],
|
|
@@ -193,9 +195,12 @@ const TOOL_INPUT_SCHEMAS: Record<SidekickToolName, Record<string, unknown>> = {
|
|
|
193
195
|
'channel:receive': {
|
|
194
196
|
type: 'object',
|
|
195
197
|
properties: {
|
|
198
|
+
provider: { type: 'string' },
|
|
196
199
|
channel: { type: 'string' },
|
|
200
|
+
cursor: { type: 'string' },
|
|
197
201
|
limit: { type: 'integer', minimum: 1 },
|
|
198
202
|
since: { type: 'string' },
|
|
203
|
+
metadata: { type: 'object', additionalProperties: true },
|
|
199
204
|
},
|
|
200
205
|
additionalProperties: false,
|
|
201
206
|
},
|
|
@@ -235,6 +235,8 @@ tools:
|
|
|
235
235
|
input_schema:
|
|
236
236
|
type: object
|
|
237
237
|
properties:
|
|
238
|
+
provider:
|
|
239
|
+
type: string
|
|
238
240
|
channel:
|
|
239
241
|
type: string
|
|
240
242
|
content:
|
|
@@ -242,6 +244,9 @@ tools:
|
|
|
242
244
|
minLength: 1
|
|
243
245
|
sender:
|
|
244
246
|
type: string
|
|
247
|
+
metadata:
|
|
248
|
+
type: object
|
|
249
|
+
additionalProperties: true
|
|
245
250
|
dry_run:
|
|
246
251
|
type: boolean
|
|
247
252
|
required: [content]
|
|
@@ -259,13 +264,20 @@ tools:
|
|
|
259
264
|
input_schema:
|
|
260
265
|
type: object
|
|
261
266
|
properties:
|
|
267
|
+
provider:
|
|
268
|
+
type: string
|
|
262
269
|
channel:
|
|
263
270
|
type: string
|
|
271
|
+
cursor:
|
|
272
|
+
type: string
|
|
264
273
|
limit:
|
|
265
274
|
type: integer
|
|
266
275
|
minimum: 1
|
|
267
276
|
since:
|
|
268
277
|
type: string
|
|
278
|
+
metadata:
|
|
279
|
+
type: object
|
|
280
|
+
additionalProperties: true
|
|
269
281
|
additionalProperties: false
|
|
270
282
|
output_schema:
|
|
271
283
|
type: object
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lumenflow/packs-sidekick",
|
|
3
|
-
"version": "3.9.
|
|
3
|
+
"version": "3.9.6",
|
|
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",
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"./storage": "./dist/tool-impl/storage.js",
|
|
28
28
|
"./tools": "./dist/tools/index.js",
|
|
29
29
|
"./tool-impl": "./dist/tool-impl/index.js",
|
|
30
|
+
"./tool-impl/channel-transports": "./dist/tool-impl/channel-transports.js",
|
|
30
31
|
"./tool-impl/channel-tools": "./dist/tool-impl/channel-tools.js",
|
|
31
32
|
"./tool-impl/memory-tools": "./dist/tool-impl/memory-tools.js",
|
|
32
33
|
"./tool-impl/routine-tools": "./dist/tool-impl/routine-tools.js",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Copyright (c) 2026 Hellmai Ltd
|
|
2
2
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
3
3
|
|
|
4
|
+
import { getChannelTransport } from './channel-transports.js';
|
|
4
5
|
import { getStoragePort, type ChannelMessageRecord, type ChannelRecord } from './storage.js';
|
|
5
6
|
import {
|
|
6
7
|
asInteger,
|
|
@@ -29,6 +30,26 @@ const TOOL_NAMES = {
|
|
|
29
30
|
const OUTBOX_CAP = 100;
|
|
30
31
|
const DEFAULT_CHANNEL_NAME = 'default';
|
|
31
32
|
|
|
33
|
+
function asOptionalRecord(value: unknown): Record<string, unknown> | undefined {
|
|
34
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
return value as Record<string, unknown>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function withTransportMetadata<T extends Record<string, unknown>>(
|
|
41
|
+
data: T,
|
|
42
|
+
metadata: Record<string, unknown> | undefined,
|
|
43
|
+
): T & Record<string, unknown> {
|
|
44
|
+
if (!metadata) {
|
|
45
|
+
return data;
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
...data,
|
|
49
|
+
metadata,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
32
53
|
// ---------------------------------------------------------------------------
|
|
33
54
|
// channel:configure
|
|
34
55
|
// ---------------------------------------------------------------------------
|
|
@@ -99,6 +120,77 @@ async function channelConfigureTool(
|
|
|
99
120
|
// channel:send
|
|
100
121
|
// ---------------------------------------------------------------------------
|
|
101
122
|
|
|
123
|
+
async function channelSendViaTransport(
|
|
124
|
+
input: Record<string, unknown>,
|
|
125
|
+
context: ToolContextLike | undefined,
|
|
126
|
+
provider: string,
|
|
127
|
+
channel: string,
|
|
128
|
+
content: string,
|
|
129
|
+
): Promise<ToolOutput> {
|
|
130
|
+
const workspaceId = asNonEmptyString(context?.workspace_id);
|
|
131
|
+
if (!workspaceId) {
|
|
132
|
+
return failure(
|
|
133
|
+
'WORKSPACE_CONTEXT_REQUIRED',
|
|
134
|
+
'workspace_id is required when provider is specified.',
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const transport = getChannelTransport(provider);
|
|
139
|
+
if (!transport) {
|
|
140
|
+
return failure(
|
|
141
|
+
'INTEGRATION_PROVIDER_NOT_REGISTERED',
|
|
142
|
+
`No channel transport registered for provider: ${provider}`,
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (isDryRun(input)) {
|
|
147
|
+
return success({
|
|
148
|
+
dry_run: true,
|
|
149
|
+
provider,
|
|
150
|
+
capability: 'send',
|
|
151
|
+
channel,
|
|
152
|
+
content,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const transportResult = await transport.send({
|
|
157
|
+
workspaceId,
|
|
158
|
+
provider,
|
|
159
|
+
channel,
|
|
160
|
+
content,
|
|
161
|
+
metadata: asOptionalRecord(input.metadata),
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
const outputData = withTransportMetadata(
|
|
165
|
+
{
|
|
166
|
+
provider,
|
|
167
|
+
capability: 'send',
|
|
168
|
+
channel,
|
|
169
|
+
...(transportResult.externalMessageId !== undefined
|
|
170
|
+
? { externalMessageId: transportResult.externalMessageId }
|
|
171
|
+
: {}),
|
|
172
|
+
...(transportResult.failureClass ? { failureClass: transportResult.failureClass } : {}),
|
|
173
|
+
...(transportResult.retryAfterSeconds !== undefined
|
|
174
|
+
? { retryAfterSeconds: transportResult.retryAfterSeconds }
|
|
175
|
+
: {}),
|
|
176
|
+
},
|
|
177
|
+
transportResult.metadata,
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
if (!transportResult.success) {
|
|
181
|
+
return {
|
|
182
|
+
success: false,
|
|
183
|
+
error: {
|
|
184
|
+
code: 'INTEGRATION_PROVIDER_SEND_FAILED',
|
|
185
|
+
message: transportResult.error ?? `Provider send failed for provider: ${provider}`,
|
|
186
|
+
},
|
|
187
|
+
data: outputData,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return success(outputData);
|
|
192
|
+
}
|
|
193
|
+
|
|
102
194
|
async function channelSendTool(input: unknown, context?: ToolContextLike): Promise<ToolOutput> {
|
|
103
195
|
const parsed = toRecord(input);
|
|
104
196
|
const content = asNonEmptyString(parsed.content);
|
|
@@ -108,6 +200,12 @@ async function channelSendTool(input: unknown, context?: ToolContextLike): Promi
|
|
|
108
200
|
}
|
|
109
201
|
|
|
110
202
|
const channelName = asNonEmptyString(parsed.channel) ?? DEFAULT_CHANNEL_NAME;
|
|
203
|
+
const provider = asNonEmptyString(parsed.provider);
|
|
204
|
+
|
|
205
|
+
if (provider) {
|
|
206
|
+
return channelSendViaTransport(parsed, context, provider, channelName, content);
|
|
207
|
+
}
|
|
208
|
+
|
|
111
209
|
const sender = asNonEmptyString(parsed.sender) ?? 'assistant';
|
|
112
210
|
const now = nowIso();
|
|
113
211
|
|
|
@@ -175,10 +273,86 @@ async function channelSendTool(input: unknown, context?: ToolContextLike): Promi
|
|
|
175
273
|
// channel:receive
|
|
176
274
|
// ---------------------------------------------------------------------------
|
|
177
275
|
|
|
276
|
+
async function channelReceiveViaTransport(
|
|
277
|
+
input: Record<string, unknown>,
|
|
278
|
+
context: ToolContextLike | undefined,
|
|
279
|
+
provider: string,
|
|
280
|
+
channel: string,
|
|
281
|
+
limit: number | null,
|
|
282
|
+
): Promise<ToolOutput> {
|
|
283
|
+
const workspaceId = asNonEmptyString(context?.workspace_id);
|
|
284
|
+
if (!workspaceId) {
|
|
285
|
+
return failure(
|
|
286
|
+
'WORKSPACE_CONTEXT_REQUIRED',
|
|
287
|
+
'workspace_id is required when provider is specified.',
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const transport = getChannelTransport(provider);
|
|
292
|
+
if (!transport) {
|
|
293
|
+
return failure(
|
|
294
|
+
'INTEGRATION_PROVIDER_NOT_REGISTERED',
|
|
295
|
+
`No channel transport registered for provider: ${provider}`,
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const cursor = asNonEmptyString(input.cursor) ?? undefined;
|
|
300
|
+
|
|
301
|
+
const transportResult = await transport.receive({
|
|
302
|
+
workspaceId,
|
|
303
|
+
provider,
|
|
304
|
+
channel,
|
|
305
|
+
cursor,
|
|
306
|
+
limit: limit && limit > 0 ? limit : undefined,
|
|
307
|
+
metadata: asOptionalRecord(input.metadata),
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
const outputData = withTransportMetadata(
|
|
311
|
+
{
|
|
312
|
+
provider,
|
|
313
|
+
capability: 'read',
|
|
314
|
+
channel,
|
|
315
|
+
...(transportResult.records !== undefined ? { records: transportResult.records } : {}),
|
|
316
|
+
...(transportResult.nextCursor !== undefined
|
|
317
|
+
? { nextCursor: transportResult.nextCursor }
|
|
318
|
+
: {}),
|
|
319
|
+
...(transportResult.failureClass ? { failureClass: transportResult.failureClass } : {}),
|
|
320
|
+
...(transportResult.retryAfterSeconds !== undefined
|
|
321
|
+
? { retryAfterSeconds: transportResult.retryAfterSeconds }
|
|
322
|
+
: {}),
|
|
323
|
+
},
|
|
324
|
+
transportResult.metadata,
|
|
325
|
+
);
|
|
326
|
+
|
|
327
|
+
if (!transportResult.success) {
|
|
328
|
+
return {
|
|
329
|
+
success: false,
|
|
330
|
+
error: {
|
|
331
|
+
code: 'INTEGRATION_PROVIDER_RECEIVE_FAILED',
|
|
332
|
+
message: transportResult.error ?? `Provider receive failed for provider: ${provider}`,
|
|
333
|
+
},
|
|
334
|
+
data: outputData,
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
return success(outputData);
|
|
339
|
+
}
|
|
340
|
+
|
|
178
341
|
async function channelReceiveTool(input: unknown, _context?: ToolContextLike): Promise<ToolOutput> {
|
|
179
342
|
const parsed = toRecord(input);
|
|
180
343
|
const channelName = asNonEmptyString(parsed.channel);
|
|
181
344
|
const limit = asInteger(parsed.limit);
|
|
345
|
+
const provider = asNonEmptyString(parsed.provider);
|
|
346
|
+
|
|
347
|
+
if (provider) {
|
|
348
|
+
return channelReceiveViaTransport(
|
|
349
|
+
parsed,
|
|
350
|
+
_context,
|
|
351
|
+
provider,
|
|
352
|
+
channelName ?? DEFAULT_CHANNEL_NAME,
|
|
353
|
+
limit,
|
|
354
|
+
);
|
|
355
|
+
}
|
|
182
356
|
|
|
183
357
|
const storage = getStoragePort();
|
|
184
358
|
const messages = await storage.readStore('messages');
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// Copyright (c) 2026 Hellmai Ltd
|
|
2
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
3
|
+
|
|
4
|
+
import { getSidekickRuntimeContext } from './runtime-context.js';
|
|
5
|
+
|
|
6
|
+
export interface ChannelTransport {
|
|
7
|
+
provider: string;
|
|
8
|
+
send(req: {
|
|
9
|
+
workspaceId: string;
|
|
10
|
+
provider: string;
|
|
11
|
+
channel: string;
|
|
12
|
+
content: string;
|
|
13
|
+
metadata?: Record<string, unknown>;
|
|
14
|
+
}): Promise<{
|
|
15
|
+
success: boolean;
|
|
16
|
+
error?: string;
|
|
17
|
+
externalMessageId?: string;
|
|
18
|
+
failureClass?: 'retryable' | 'terminal';
|
|
19
|
+
retryAfterSeconds?: number;
|
|
20
|
+
metadata?: Record<string, unknown>;
|
|
21
|
+
}>;
|
|
22
|
+
receive(req: {
|
|
23
|
+
workspaceId: string;
|
|
24
|
+
provider: string;
|
|
25
|
+
channel: string;
|
|
26
|
+
cursor?: string;
|
|
27
|
+
limit?: number;
|
|
28
|
+
metadata?: Record<string, unknown>;
|
|
29
|
+
}): Promise<{
|
|
30
|
+
success: boolean;
|
|
31
|
+
error?: string;
|
|
32
|
+
records?: unknown[];
|
|
33
|
+
nextCursor?: string;
|
|
34
|
+
failureClass?: 'retryable' | 'terminal';
|
|
35
|
+
retryAfterSeconds?: number;
|
|
36
|
+
metadata?: Record<string, unknown>;
|
|
37
|
+
}>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function normalizeProvider(provider: string): string {
|
|
41
|
+
return provider.trim().toLowerCase();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function getRegistry(): Map<string, ChannelTransport> | undefined {
|
|
45
|
+
return getSidekickRuntimeContext()?.channelTransports;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function registerChannelTransport(transport: ChannelTransport): void {
|
|
49
|
+
const provider = normalizeProvider(transport.provider);
|
|
50
|
+
if (provider.length === 0) {
|
|
51
|
+
throw new Error('channel transport provider must be a non-empty string.');
|
|
52
|
+
}
|
|
53
|
+
const registry = getRegistry();
|
|
54
|
+
if (!registry) {
|
|
55
|
+
throw new Error('channel transport registry is unavailable outside sidekick runtime context.');
|
|
56
|
+
}
|
|
57
|
+
registry.set(provider, transport);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function getChannelTransport(provider: string): ChannelTransport | undefined {
|
|
61
|
+
const normalized = normalizeProvider(provider);
|
|
62
|
+
if (normalized.length === 0) {
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
const registry = getRegistry();
|
|
66
|
+
return registry?.get(normalized);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function clearChannelTransports(): void {
|
|
70
|
+
const registry = getRegistry();
|
|
71
|
+
if (!registry) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
registry.clear();
|
|
75
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Copyright (c) 2026 Hellmai Ltd
|
|
2
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
3
|
+
|
|
4
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
5
|
+
import type { ChannelTransport } from './channel-transports.js';
|
|
6
|
+
import type { StoragePort } from './storage.js';
|
|
7
|
+
|
|
8
|
+
export interface SidekickRuntimeContext {
|
|
9
|
+
storagePort: StoragePort;
|
|
10
|
+
channelTransports: Map<string, ChannelTransport>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const runtimeContext = new AsyncLocalStorage<SidekickRuntimeContext>();
|
|
14
|
+
|
|
15
|
+
export function getSidekickRuntimeContext(): SidekickRuntimeContext | undefined {
|
|
16
|
+
return runtimeContext.getStore();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export async function runWithSidekickRuntimeContext<T>(
|
|
20
|
+
context: SidekickRuntimeContext,
|
|
21
|
+
fn: () => Promise<T>,
|
|
22
|
+
): Promise<T> {
|
|
23
|
+
return runtimeContext.run(context, fn);
|
|
24
|
+
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// Copyright (c) 2026 Hellmai Ltd
|
|
2
2
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
3
3
|
|
|
4
|
-
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
5
4
|
import { randomBytes } from 'node:crypto';
|
|
6
5
|
import { appendFile, mkdir, open, readFile, rename, rm, stat, writeFile } from 'node:fs/promises';
|
|
7
6
|
import path from 'node:path';
|
|
7
|
+
import { getSidekickRuntimeContext, runWithSidekickRuntimeContext } from './runtime-context.js';
|
|
8
8
|
|
|
9
9
|
// ---------------------------------------------------------------------------
|
|
10
10
|
// Constants
|
|
@@ -299,7 +299,6 @@ export class FsStoragePort implements StoragePort {
|
|
|
299
299
|
// Injection helpers (AsyncLocalStorage-based)
|
|
300
300
|
// ---------------------------------------------------------------------------
|
|
301
301
|
|
|
302
|
-
const storageContext = new AsyncLocalStorage<StoragePort>();
|
|
303
302
|
let defaultStoragePort: StoragePort = new FsStoragePort();
|
|
304
303
|
|
|
305
304
|
export function setDefaultStoragePort(port: StoragePort): void {
|
|
@@ -307,9 +306,16 @@ export function setDefaultStoragePort(port: StoragePort): void {
|
|
|
307
306
|
}
|
|
308
307
|
|
|
309
308
|
export function getStoragePort(): StoragePort {
|
|
310
|
-
return
|
|
309
|
+
return getSidekickRuntimeContext()?.storagePort ?? defaultStoragePort;
|
|
311
310
|
}
|
|
312
311
|
|
|
313
312
|
export async function runWithStoragePort<T>(port: StoragePort, fn: () => Promise<T>): Promise<T> {
|
|
314
|
-
|
|
313
|
+
const existingContext = getSidekickRuntimeContext();
|
|
314
|
+
return runWithSidekickRuntimeContext(
|
|
315
|
+
{
|
|
316
|
+
storagePort: port,
|
|
317
|
+
channelTransports: existingContext?.channelTransports ?? new Map(),
|
|
318
|
+
},
|
|
319
|
+
fn,
|
|
320
|
+
);
|
|
315
321
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
|
|
2
|
-
> @lumenflow/packs-software-delivery@3.9.
|
|
2
|
+
> @lumenflow/packs-software-delivery@3.9.6 build /home/runner/work/lumenflow-dev/lumenflow-dev/packages/@lumenflow/packs/software-delivery
|
|
3
3
|
> tsc
|
|
4
4
|
|
|
@@ -230,30 +230,30 @@ For the full worktree lifecycle (parallel execution, bootstrap, isolation guaran
|
|
|
230
230
|
|
|
231
231
|
> **Complete CLI reference (100+ commands):** See [quick-ref-commands.md](docs/04-operations/_frameworks/lumenflow/agent/onboarding/quick-ref-commands.md). Always run `<command> --help` for the authoritative option list.
|
|
232
232
|
|
|
233
|
-
| Command | Description
|
|
234
|
-
| ------------------------- |
|
|
235
|
-
| `pnpm wu:create` | Create new WU spec
|
|
236
|
-
| `pnpm wu:claim` | Claim WU, update canonical state, create worktree
|
|
237
|
-
| `pnpm wu:prep` | Run gates in worktree, prep for wu:done
|
|
238
|
-
| `pnpm wu:done` | Complete WU (merge, stamp, cleanup)
|
|
239
|
-
| `pnpm wu:status` | Show WU status, location, and valid commands
|
|
240
|
-
| `pnpm wu:recover` | Analyze and fix WU state inconsistencies
|
|
241
|
-
| `pnpm wu:block` | Block WU (transitions to blocked, frees lane)
|
|
242
|
-
| `pnpm wu:unblock` | Unblock WU (transitions to in_progress)
|
|
243
|
-
| `pnpm wu:release` | Release orphaned WU (in_progress to ready for reclaim)
|
|
244
|
-
| `pnpm wu:brief` | Generate handoff prompt + record evidence
|
|
245
|
-
| `pnpm wu:delegate` | Generate prompt + record
|
|
246
|
-
| `pnpm wu:escalate` | Show or resolve WU escalation status
|
|
247
|
-
| `pnpm wu:delete` | Delete WU spec and cleanup
|
|
248
|
-
| `pnpm gates` | Run quality gates (`--docs-only` for docs WUs)
|
|
249
|
-
| `pnpm lumenflow:commands` | List all public commands (primary + alias + legacy)
|
|
250
|
-
| `pnpm docs:generate` | Regenerate CLI/config reference docs from source
|
|
251
|
-
| `pnpm docs:validate` | Verify generated docs are up-to-date
|
|
252
|
-
| `pnpm lane:status` | Show lane lifecycle status + next step
|
|
253
|
-
| `pnpm lane:setup` | Create/update draft lane artifacts
|
|
254
|
-
| `pnpm lane:validate` | Validate lane artifacts before lock
|
|
255
|
-
| `pnpm lane:lock` | Lock lane lifecycle for delivery WUs
|
|
256
|
-
| `pnpm mem:checkpoint` | Save memory checkpoint
|
|
233
|
+
| Command | Description |
|
|
234
|
+
| ------------------------- | --------------------------------------------------------- |
|
|
235
|
+
| `pnpm wu:create` | Create new WU spec |
|
|
236
|
+
| `pnpm wu:claim` | Claim WU, update canonical state, create worktree |
|
|
237
|
+
| `pnpm wu:prep` | Run gates in worktree, prep for wu:done |
|
|
238
|
+
| `pnpm wu:done` | Complete WU (merge, stamp, cleanup) |
|
|
239
|
+
| `pnpm wu:status` | Show WU status, location, and valid commands |
|
|
240
|
+
| `pnpm wu:recover` | Analyze and fix WU state inconsistencies |
|
|
241
|
+
| `pnpm wu:block` | Block WU (transitions to blocked, frees lane) |
|
|
242
|
+
| `pnpm wu:unblock` | Unblock WU (transitions to in_progress) |
|
|
243
|
+
| `pnpm wu:release` | Release orphaned WU (in_progress to ready for reclaim) |
|
|
244
|
+
| `pnpm wu:brief` | Generate handoff prompt + record evidence |
|
|
245
|
+
| `pnpm wu:delegate` | Generate prompt + record lineage + brief hash attestation |
|
|
246
|
+
| `pnpm wu:escalate` | Show or resolve WU escalation status |
|
|
247
|
+
| `pnpm wu:delete` | Delete WU spec and cleanup |
|
|
248
|
+
| `pnpm gates` | Run quality gates (`--docs-only` for docs WUs) |
|
|
249
|
+
| `pnpm lumenflow:commands` | List all public commands (primary + alias + legacy) |
|
|
250
|
+
| `pnpm docs:generate` | Regenerate CLI/config reference docs from source |
|
|
251
|
+
| `pnpm docs:validate` | Verify generated docs are up-to-date |
|
|
252
|
+
| `pnpm lane:status` | Show lane lifecycle status + next step |
|
|
253
|
+
| `pnpm lane:setup` | Create/update draft lane artifacts |
|
|
254
|
+
| `pnpm lane:validate` | Validate lane artifacts before lock |
|
|
255
|
+
| `pnpm lane:lock` | Lock lane lifecycle for delivery WUs |
|
|
256
|
+
| `pnpm mem:checkpoint` | Save memory checkpoint |
|
|
257
257
|
|
|
258
258
|
Commands include **context-aware validation** that checks location, WU status, and git state. When validation fails, commands provide copy-paste ready fix commands. Configure in `workspace.yaml` under `software_delivery.experimental.context_validation`.
|
|
259
259
|
The Starlight CLI reference page is intentionally curated to primary commands; use `pnpm lumenflow:commands` for complete discovery.
|
|
@@ -106,8 +106,17 @@ This is useful when:
|
|
|
106
106
|
|
|
107
107
|
`wu:delegate` records **delegation intent**: that a brief was generated for a target WU with explicit parent lineage.
|
|
108
108
|
|
|
109
|
+
It also stores **brief attestation metadata** (SHA-256 prompt hash, client, timestamp, prompt length) for the generated handoff payload.
|
|
110
|
+
|
|
109
111
|
It does **not** by itself prove pickup or execution. Pickup/execution confirmation comes from lifecycle evidence (claim/completion events, checkpoints, signals, and final `wu:done`).
|
|
110
112
|
|
|
113
|
+
`wu:done` now enforces, for initiative-governed and explicitly delegated WUs:
|
|
114
|
+
|
|
115
|
+
- Delegation lineage exists
|
|
116
|
+
- Claim-time pickup evidence exists
|
|
117
|
+
- Delegation brief attestation exists
|
|
118
|
+
- Attested prompt hash matches recorded `wu:brief` evidence for the target WU
|
|
119
|
+
|
|
111
120
|
---
|
|
112
121
|
|
|
113
122
|
## 2c) Self-Implementation Evidence-Only Flow (WU-2222)
|
|
@@ -133,7 +133,7 @@ files, no manual git, no WU ceremony. Only actual **code changes** need WUs.
|
|
|
133
133
|
| `pnpm wu:brief --id WU-XXX --client <client>` | Generate handoff prompt + record evidence (claimed workspace/branch) |
|
|
134
134
|
| `pnpm wu:brief --id WU-XXX --evidence-only` | Record wu:brief evidence only (self-implementation, no prompt output) |
|
|
135
135
|
| `pnpm wu:brief --id WU-XXX --no-context` | Generate prompt without memory context injection |
|
|
136
|
-
| `pnpm wu:delegate --id WU-XXX --parent-wu <P>` | Generate prompt and
|
|
136
|
+
| `pnpm wu:delegate --id WU-XXX --parent-wu <P>` | Generate prompt, record lineage, and store brief hash attestation |
|
|
137
137
|
| `pnpm wu:sandbox --id WU-XXX -- <cmd>` | Run command through hardened WU sandbox backend |
|
|
138
138
|
|
|
139
139
|
### WU Maintenance
|
|
@@ -10,20 +10,20 @@ This document covers the complete release process for LumenFlow, including versi
|
|
|
10
10
|
|
|
11
11
|
LumenFlow has several components that need to stay in sync:
|
|
12
12
|
|
|
13
|
-
| Component | Location | Deployment
|
|
14
|
-
| -------------- | --------------------------- |
|
|
15
|
-
| npm packages | `packages/@lumenflow/*` | Auto via GitHub Actions on tag push |
|
|
16
|
-
| Starlight docs | `apps/docs/` |
|
|
17
|
-
| Pack registry | `apps/web/` + pack tarballs | Manual deploy + curl publish
|
|
13
|
+
| Component | Location | Deployment |
|
|
14
|
+
| -------------- | --------------------------- | --------------------------------------------------- |
|
|
15
|
+
| npm packages | `packages/@lumenflow/*` | Auto via GitHub Actions on tag push (`publish.yml`) |
|
|
16
|
+
| Starlight docs | `apps/docs/` | Separate GitHub Actions workflow (`docs.yml`) |
|
|
17
|
+
| Pack registry | `apps/web/` + pack tarballs | Manual deploy + curl publish |
|
|
18
18
|
|
|
19
19
|
---
|
|
20
20
|
|
|
21
21
|
## Release Command
|
|
22
22
|
|
|
23
|
-
The `pnpm release` command automates
|
|
23
|
+
The `pnpm release` command automates version bump + tagging, and optionally npm publish:
|
|
24
24
|
|
|
25
25
|
```bash
|
|
26
|
-
pnpm release --release-version 1.3.0
|
|
26
|
+
pnpm release --release-version 1.3.0 --skip-publish
|
|
27
27
|
```
|
|
28
28
|
|
|
29
29
|
### What It Does
|
|
@@ -34,7 +34,7 @@ pnpm release --release-version 1.3.0
|
|
|
34
34
|
4. Bumps all `@lumenflow/*` package versions using micro-worktree isolation
|
|
35
35
|
5. Builds all packages
|
|
36
36
|
6. Creates and pushes git tag `vX.Y.Z`
|
|
37
|
-
7.
|
|
37
|
+
7. Optionally publishes to npm (unless `--skip-publish`)
|
|
38
38
|
|
|
39
39
|
### Options
|
|
40
40
|
|
|
@@ -50,19 +50,16 @@ pnpm release --release-version 1.3.0
|
|
|
50
50
|
### Examples
|
|
51
51
|
|
|
52
52
|
```bash
|
|
53
|
-
# Full release
|
|
54
|
-
pnpm release --release-version 1.3.0
|
|
55
|
-
|
|
56
|
-
# Preview what would happen
|
|
57
|
-
pnpm release --release-version 1.3.0 --dry-run
|
|
58
|
-
|
|
59
53
|
# Version bump and tag only (CI will publish)
|
|
60
54
|
pnpm release --release-version 1.3.0 --skip-publish
|
|
55
|
+
|
|
56
|
+
# Preview what would happen
|
|
57
|
+
pnpm release --release-version 1.3.0 --skip-publish --dry-run
|
|
61
58
|
```
|
|
62
59
|
|
|
63
60
|
### Authentication
|
|
64
61
|
|
|
65
|
-
For npm publish, set one of these environment variables:
|
|
62
|
+
For direct CLI npm publish (without `--skip-publish`), set one of these environment variables:
|
|
66
63
|
|
|
67
64
|
```bash
|
|
68
65
|
export NPM_TOKEN=<your-npm-token>
|
|
@@ -179,8 +176,11 @@ The `.github/workflows/publish.yml` workflow:
|
|
|
179
176
|
|
|
180
177
|
1. Checks out the tagged commit
|
|
181
178
|
2. Installs dependencies
|
|
182
|
-
3. Builds
|
|
183
|
-
4. Publishes to npm with `pnpm -r publish --access public`
|
|
179
|
+
3. Builds publishable workspace packages only (`./packages/**`)
|
|
180
|
+
4. Publishes to npm with `pnpm -r --filter "./packages/**" publish --access public --no-git-checks`
|
|
181
|
+
5. Creates the GitHub release object (`gh release create ... --generate-notes`)
|
|
182
|
+
|
|
183
|
+
`publish.yml` intentionally does **not** build `apps/docs` or `apps/web`, so docs/tooling dependencies cannot block npm publishing.
|
|
184
184
|
|
|
185
185
|
### Required Secrets
|
|
186
186
|
|
|
@@ -226,9 +226,25 @@ The public docs at <https://lumenflow.dev> are built from `apps/docs/`.
|
|
|
226
226
|
|
|
227
227
|
**Automatic Generation:** CLI and config reference docs are automatically generated from code. See [Automatic Docs Generation](./docs-generation.md) for details on the single-source-of-truth pattern.
|
|
228
228
|
|
|
229
|
+
### Build Workflow
|
|
230
|
+
|
|
231
|
+
Starlight docs build in a separate workflow: `.github/workflows/docs.yml`.
|
|
232
|
+
|
|
233
|
+
Trigger:
|
|
234
|
+
|
|
235
|
+
- Tag push (`v*`)
|
|
236
|
+
- Manual run (`workflow_dispatch`)
|
|
237
|
+
|
|
238
|
+
The workflow:
|
|
239
|
+
|
|
240
|
+
1. Installs D2 (`astro-d2` prerequisite)
|
|
241
|
+
2. Builds docs with `pnpm --filter @lumenflow/docs build`
|
|
242
|
+
3. Uploads `apps/docs/dist` as a workflow artifact
|
|
243
|
+
4. Deploys to Vercel when `VERCEL_TOKEN`, `VERCEL_ORG_ID`, and `VERCEL_PROJECT_ID` secrets are configured
|
|
244
|
+
|
|
229
245
|
### Deployment
|
|
230
246
|
|
|
231
|
-
|
|
247
|
+
Production deploy is still manual via Vercel CLI:
|
|
232
248
|
|
|
233
249
|
```bash
|
|
234
250
|
cd apps/docs
|
|
@@ -368,17 +384,17 @@ The `@lumenflow/docs` package requires `d2` (diagramming tool) which is not avai
|
|
|
368
384
|
|
|
369
385
|
### Release Steps (Automated)
|
|
370
386
|
|
|
371
|
-
Use
|
|
387
|
+
Use this as the standard workflow:
|
|
372
388
|
|
|
373
389
|
```bash
|
|
374
390
|
# Preview first
|
|
375
|
-
pnpm release --release-version 1.3.0 --dry-run
|
|
391
|
+
pnpm release --release-version 1.3.0 --skip-publish --dry-run
|
|
376
392
|
|
|
377
|
-
# Execute
|
|
378
|
-
pnpm release --release-version 1.3.0
|
|
393
|
+
# Execute (version bump + tag, publish delegated to GitHub Actions)
|
|
394
|
+
pnpm release --release-version 1.3.0 --skip-publish
|
|
379
395
|
```
|
|
380
396
|
|
|
381
|
-
This handles version bump
|
|
397
|
+
This handles version bump and tag locally; GitHub Actions handles npm publish, docs build, and GitHub release creation.
|
|
382
398
|
|
|
383
399
|
### Release Steps (Manual)
|
|
384
400
|
|
|
@@ -398,21 +414,22 @@ If you need more control:
|
|
|
398
414
|
git push origin v1.3.0
|
|
399
415
|
```
|
|
400
416
|
|
|
401
|
-
3. **Verify npm publish**
|
|
417
|
+
3. **Verify npm publish + GitHub release**
|
|
402
418
|
|
|
403
419
|
```bash
|
|
404
420
|
gh run list --workflow=publish.yml --limit 1
|
|
405
421
|
# Wait for completion, then verify
|
|
406
422
|
npm view @lumenflow/cli version
|
|
423
|
+
gh release view v1.3.0
|
|
407
424
|
```
|
|
408
425
|
|
|
409
|
-
4. **
|
|
426
|
+
4. **Verify docs workflow** (if docs changed)
|
|
410
427
|
|
|
411
428
|
```bash
|
|
412
|
-
gh
|
|
429
|
+
gh run list --workflow=docs.yml --limit 1
|
|
413
430
|
```
|
|
414
431
|
|
|
415
|
-
5. **Deploy Starlight docs** (if content changed)
|
|
432
|
+
5. **Deploy Starlight docs** (if content changed and you are deploying manually)
|
|
416
433
|
|
|
417
434
|
```bash
|
|
418
435
|
cd apps/docs && pnpm build && vercel --prod
|
|
@@ -428,13 +445,13 @@ If you need more control:
|
|
|
428
445
|
|
|
429
446
|
## Keeping Components in Sync
|
|
430
447
|
|
|
431
|
-
| Change Type | npm | Docs
|
|
432
|
-
| ---------------- | ------------- |
|
|
433
|
-
| Bug fix in CLI | Tag + publish | Auto\*
|
|
434
|
-
| New CLI command | Tag + publish | Auto\*
|
|
435
|
-
| Pack changes | Tag + publish | No
|
|
436
|
-
| Docs-only update | No |
|
|
437
|
-
| Full release | Tag + publish |
|
|
448
|
+
| Change Type | npm | Docs | Packs |
|
|
449
|
+
| ---------------- | ------------- | ----------------------- | ------- |
|
|
450
|
+
| Bug fix in CLI | Tag + publish | Auto\* | No |
|
|
451
|
+
| New CLI command | Tag + publish | Auto\* | No |
|
|
452
|
+
| Pack changes | Tag + publish | No | Publish |
|
|
453
|
+
| Docs-only update | No | Build workflow + deploy | No |
|
|
454
|
+
| Full release | Tag + publish | Build workflow + deploy | Publish |
|
|
438
455
|
|
|
439
456
|
\* CLI/config reference docs regenerate automatically during `wu:done` when trigger files change. See [Automatic Docs Generation](./docs-generation.md).
|
|
440
457
|
|