@lumenflow/cli 5.0.3 → 5.2.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/capacity-snapshot-emitter.js +10 -12
- package/dist/capacity-snapshot-emitter.js.map +1 -1
- package/dist/commands/integrate.js +9 -0
- package/dist/commands/integrate.js.map +1 -1
- package/dist/delegation-role-resolver.js +1 -1
- package/dist/delegation-role-resolver.js.map +1 -1
- package/dist/docs-generate-pack-reference.js +57 -15
- package/dist/docs-generate-pack-reference.js.map +1 -1
- package/dist/docs-sync.js +7 -3
- package/dist/docs-sync.js.map +1 -1
- package/dist/init-docs-scaffolder.js +18 -3
- package/dist/init-docs-scaffolder.js.map +1 -1
- package/dist/init-templates.js +307 -0
- package/dist/init-templates.js.map +1 -1
- package/dist/init.js +26 -6
- package/dist/init.js.map +1 -1
- package/dist/kernel-event-sync/lifecycle-emitters.js +13 -14
- package/dist/kernel-event-sync/lifecycle-emitters.js.map +1 -1
- package/dist/kernel-event-sync/narrow-emissions.js +7 -4
- package/dist/kernel-event-sync/narrow-emissions.js.map +1 -1
- package/dist/kernel-event-sync/software-delivery-emitters.js +91 -0
- package/dist/kernel-event-sync/software-delivery-emitters.js.map +1 -1
- package/dist/lumenflow-upgrade.js +86 -23
- package/dist/lumenflow-upgrade.js.map +1 -1
- package/dist/orchestrate-init-status.js +24 -11
- package/dist/orchestrate-init-status.js.map +1 -1
- package/dist/pack-install.js +27 -20
- package/dist/pack-install.js.map +1 -1
- package/dist/pack-publish.js +34 -26
- package/dist/pack-publish.js.map +1 -1
- package/dist/release.js +293 -146
- package/dist/release.js.map +1 -1
- package/dist/skills-projection.js +84 -0
- package/dist/skills-projection.js.map +1 -0
- package/dist/sync-templates.js +4 -4
- package/dist/sync-templates.js.map +1 -1
- package/dist/temp-dir-cleanup.js +112 -0
- package/dist/temp-dir-cleanup.js.map +1 -0
- package/dist/validate-agent-skills.js +4 -4
- package/dist/validate-agent-skills.js.map +1 -1
- package/dist/validate-agent-sync.js +109 -45
- package/dist/validate-agent-sync.js.map +1 -1
- package/dist/validate-skills-spec.js +2 -2
- package/dist/wu-done.js +1 -1
- package/dist/wu-done.js.map +1 -1
- package/dist/wu-spawn-prompt-builders.js +11 -34
- package/dist/wu-spawn-prompt-builders.js.map +1 -1
- package/dist/wu-spawn-strategy-resolver.js +4 -0
- package/dist/wu-spawn-strategy-resolver.js.map +1 -1
- package/package.json +11 -11
- package/packs/agent-runtime/manifest.ts +55 -4
- package/packs/agent-runtime/manifest.yaml +16 -6
- package/packs/agent-runtime/orchestration.ts +26 -1
- package/packs/agent-runtime/package.json +1 -1
- package/packs/agent-runtime/remote-controls/operations.ts +6 -0
- package/packs/agent-runtime/tool-impl/remote-controls.mock.ts +4 -0
- package/packs/agent-runtime/turn-lifecycle-events.ts +90 -1
- package/packs/sidekick/manifest.yaml +6 -0
- package/packs/sidekick/package.json +2 -1
- package/packs/sidekick/sidekick-events.ts +195 -18
- package/packs/sidekick/src/adapters/control-plane-bridge.adapter.ts +18 -10
- package/packs/sidekick/src/adapters/filesystem-bridge.adapter.ts +4 -0
- package/packs/sidekick/src/domain/channel.types.ts +34 -54
- package/packs/sidekick/src/ports/channel-bridge.port.ts +29 -12
- package/packs/sidekick/tool-impl/channel-tools.ts +47 -16
- package/packs/sidekick/tool-impl/system-tools.ts +4 -6
- package/packs/software-delivery/manifest.ts +94 -7
- package/packs/software-delivery/manifest.yaml +42 -5
- package/packs/software-delivery/package.json +1 -1
- package/packs/software-delivery/src/config/schemas/lumenflow-config-schema-types.ts +26 -2
- package/packs/software-delivery/src/config/workspace-reader.ts +67 -2
- package/packs/software-delivery/src/constants/wu-paths-constants.ts +26 -0
- package/packs/software-delivery/src/domain/orchestration.constants.ts +7 -9
- package/packs/software-delivery/src/domain/orchestration.schemas.ts +1 -2
- package/packs/software-delivery/src/domain/orchestration.types.ts +0 -2
- package/packs/software-delivery/src/state/wu-paths.ts +1 -1
- package/packs/software-delivery/tool-impl/wu-lifecycle-tools.ts +30 -0
- package/templates/core/AGENTS.md.template +12 -8
- package/templates/core/LUMENFLOW.md.template +14 -9
- package/templates/core/ai/onboarding/agent-invocation-guide.md.template +5 -0
|
@@ -69,6 +69,62 @@ export interface AgentRuntimeTurnCostBreakdown {
|
|
|
69
69
|
|
|
70
70
|
export type AgentRuntimeCleanupStatus = 'clean' | 'partial' | 'needs_recovery';
|
|
71
71
|
|
|
72
|
+
/**
|
|
73
|
+
* WU-2829 (INIT-062 WU-C): narrowed TurnCompletedEvent.status so cloud
|
|
74
|
+
* can resolve the turn discriminant without runtime string checks. The
|
|
75
|
+
* four values describe the lifecycle outcome of a turn; the internal
|
|
76
|
+
* per-turn semantic status (`reply | tool_request | complete | escalate`)
|
|
77
|
+
* remains on AgentRuntimeExecuteTurnOutput — it is NOT the same concept.
|
|
78
|
+
*/
|
|
79
|
+
export const AGENT_RUNTIME_TURN_COMPLETED_STATUSES = {
|
|
80
|
+
SUCCESS: 'success',
|
|
81
|
+
ERROR: 'error',
|
|
82
|
+
ABORTED: 'aborted',
|
|
83
|
+
PAUSED: 'paused',
|
|
84
|
+
} as const;
|
|
85
|
+
|
|
86
|
+
export const AGENT_RUNTIME_TURN_COMPLETED_STATUS_VALUES = [
|
|
87
|
+
AGENT_RUNTIME_TURN_COMPLETED_STATUSES.SUCCESS,
|
|
88
|
+
AGENT_RUNTIME_TURN_COMPLETED_STATUSES.ERROR,
|
|
89
|
+
AGENT_RUNTIME_TURN_COMPLETED_STATUSES.ABORTED,
|
|
90
|
+
AGENT_RUNTIME_TURN_COMPLETED_STATUSES.PAUSED,
|
|
91
|
+
] as const;
|
|
92
|
+
|
|
93
|
+
export type AgentRuntimeTurnCompletedStatus =
|
|
94
|
+
(typeof AGENT_RUNTIME_TURN_COMPLETED_STATUS_VALUES)[number];
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* WU-2829 (INIT-062 WU-C): budget threshold enrichment. Cloud previously
|
|
98
|
+
* had to reconstruct `level` from `threshold`/`observed_value`; we now
|
|
99
|
+
* supply the discriminant directly alongside `remaining_usd`, `scope`,
|
|
100
|
+
* and `scope_id` so dashboards render without post-hoc inference.
|
|
101
|
+
*/
|
|
102
|
+
export const AGENT_RUNTIME_BUDGET_LEVELS = {
|
|
103
|
+
WARNING: 'warning',
|
|
104
|
+
EXCEEDED: 'exceeded',
|
|
105
|
+
} as const;
|
|
106
|
+
|
|
107
|
+
export const AGENT_RUNTIME_BUDGET_LEVEL_VALUES = [
|
|
108
|
+
AGENT_RUNTIME_BUDGET_LEVELS.WARNING,
|
|
109
|
+
AGENT_RUNTIME_BUDGET_LEVELS.EXCEEDED,
|
|
110
|
+
] as const;
|
|
111
|
+
|
|
112
|
+
export type AgentRuntimeBudgetLevel = (typeof AGENT_RUNTIME_BUDGET_LEVEL_VALUES)[number];
|
|
113
|
+
|
|
114
|
+
export const AGENT_RUNTIME_BUDGET_SCOPES = {
|
|
115
|
+
SESSION: 'session',
|
|
116
|
+
WORKSPACE: 'workspace',
|
|
117
|
+
ORG: 'org',
|
|
118
|
+
} as const;
|
|
119
|
+
|
|
120
|
+
export const AGENT_RUNTIME_BUDGET_SCOPE_VALUES = [
|
|
121
|
+
AGENT_RUNTIME_BUDGET_SCOPES.SESSION,
|
|
122
|
+
AGENT_RUNTIME_BUDGET_SCOPES.WORKSPACE,
|
|
123
|
+
AGENT_RUNTIME_BUDGET_SCOPES.ORG,
|
|
124
|
+
] as const;
|
|
125
|
+
|
|
126
|
+
export type AgentRuntimeBudgetScope = (typeof AGENT_RUNTIME_BUDGET_SCOPE_VALUES)[number];
|
|
127
|
+
|
|
72
128
|
export interface TurnStartedEvent extends AgentRuntimeEventEnvelope {
|
|
73
129
|
kind: typeof AGENT_RUNTIME_EVENT_KINDS.TURN_STARTED;
|
|
74
130
|
session_id: string;
|
|
@@ -80,7 +136,9 @@ export interface TurnCompletedEvent extends AgentRuntimeEventEnvelope {
|
|
|
80
136
|
kind: typeof AGENT_RUNTIME_EVENT_KINDS.TURN_COMPLETED;
|
|
81
137
|
session_id: string;
|
|
82
138
|
turn_index: number;
|
|
83
|
-
|
|
139
|
+
// WU-2829: narrowed from `string` to the four lifecycle outcomes so
|
|
140
|
+
// cloud gets TS exhaustiveness on the discriminated union.
|
|
141
|
+
status: AgentRuntimeTurnCompletedStatus;
|
|
84
142
|
cost_breakdown: AgentRuntimeTurnCostBreakdown;
|
|
85
143
|
}
|
|
86
144
|
|
|
@@ -99,6 +157,15 @@ export interface ToolCalledEvent extends AgentRuntimeEventEnvelope {
|
|
|
99
157
|
turn_index: number;
|
|
100
158
|
tool_name: string;
|
|
101
159
|
tool_call_id: string;
|
|
160
|
+
/**
|
|
161
|
+
* WU-2829 (INIT-062 WU-C): approval discriminant so cloud no longer
|
|
162
|
+
* has to cross-reference APPROVAL_REQUIRED events to tell which tool
|
|
163
|
+
* calls were gated. `true` = call proceeded (either no approval
|
|
164
|
+
* required, or approval was satisfied); `false` = call blocked
|
|
165
|
+
* pending an approval (paired with a subsequent APPROVAL_REQUIRED
|
|
166
|
+
* event carrying the request_id).
|
|
167
|
+
*/
|
|
168
|
+
approved: boolean;
|
|
102
169
|
}
|
|
103
170
|
|
|
104
171
|
export interface ApprovalRequiredEvent extends AgentRuntimeEventEnvelope {
|
|
@@ -149,6 +216,28 @@ export interface BudgetThresholdEvent extends AgentRuntimeEventEnvelope {
|
|
|
149
216
|
budget_name: string;
|
|
150
217
|
threshold: number;
|
|
151
218
|
observed_value: number;
|
|
219
|
+
/**
|
|
220
|
+
* WU-2829 (INIT-062 WU-C): discriminant for cloud dashboards so the
|
|
221
|
+
* level is no longer inferred from threshold/observed_value.
|
|
222
|
+
*/
|
|
223
|
+
level: AgentRuntimeBudgetLevel;
|
|
224
|
+
/**
|
|
225
|
+
* WU-2829 (INIT-062 WU-C): remaining USD headroom for the budget at
|
|
226
|
+
* emission time. Negative values are permitted when the budget is
|
|
227
|
+
* already exceeded.
|
|
228
|
+
*/
|
|
229
|
+
remaining_usd: number;
|
|
230
|
+
/**
|
|
231
|
+
* WU-2829 (INIT-062 WU-C): budget scope so cloud can route the signal
|
|
232
|
+
* to the correct dashboard (session / workspace / org).
|
|
233
|
+
*/
|
|
234
|
+
scope: AgentRuntimeBudgetScope;
|
|
235
|
+
/**
|
|
236
|
+
* WU-2829 (INIT-062 WU-C): identifier of the scope object (session id,
|
|
237
|
+
* workspace id, or org id) — paired with `scope` to pin the emission
|
|
238
|
+
* to a specific subject.
|
|
239
|
+
*/
|
|
240
|
+
scope_id: string;
|
|
152
241
|
}
|
|
153
242
|
|
|
154
243
|
export interface AgentSessionEnrolledEvent extends AgentRuntimeEventEnvelope {
|
|
@@ -678,6 +678,9 @@ emitted_event_kinds:
|
|
|
678
678
|
- sidekick:routine_executed
|
|
679
679
|
- sidekick:routine_step_failed
|
|
680
680
|
- sidekick:state_rehydrated
|
|
681
|
+
- sidekick:state_rehydration_started
|
|
682
|
+
- sidekick:state_rehydration_chunk
|
|
683
|
+
- sidekick:state_rehydration_completed
|
|
681
684
|
backpressure_policy:
|
|
682
685
|
sidekick:task_created: queue-with-replay
|
|
683
686
|
sidekick:task_completed: queue-with-replay
|
|
@@ -695,6 +698,9 @@ backpressure_policy:
|
|
|
695
698
|
sidekick:routine_executed: queue-with-replay
|
|
696
699
|
sidekick:routine_step_failed: queue-with-replay
|
|
697
700
|
sidekick:state_rehydrated: queue-with-replay
|
|
701
|
+
sidekick:state_rehydration_started: queue-with-replay
|
|
702
|
+
sidekick:state_rehydration_chunk: queue-with-replay
|
|
703
|
+
sidekick:state_rehydration_completed: queue-with-replay
|
|
698
704
|
state_aliases: {}
|
|
699
705
|
lane_templates: []
|
|
700
706
|
# WU-2735 (INIT-060 WU-7a, ADR-013 §ChannelBridge): the sidekick pack requires
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lumenflow/packs-sidekick",
|
|
3
|
-
"version": "5.0
|
|
3
|
+
"version": "5.2.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",
|
|
@@ -53,6 +53,7 @@
|
|
|
53
53
|
"typecheck": "tsc --noEmit"
|
|
54
54
|
},
|
|
55
55
|
"dependencies": {
|
|
56
|
+
"@lumenflow/conductor-sdk": "workspace:^",
|
|
56
57
|
"@lumenflow/kernel": "workspace:^"
|
|
57
58
|
},
|
|
58
59
|
"devDependencies": {
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
type MemoryRecord,
|
|
13
13
|
type RoutineRecord,
|
|
14
14
|
type SidekickStores,
|
|
15
|
+
type StoreName,
|
|
15
16
|
type TaskRecord,
|
|
16
17
|
} from './tool-impl/storage.js';
|
|
17
18
|
|
|
@@ -45,7 +46,17 @@ export const SIDEKICK_EVENT_KINDS = {
|
|
|
45
46
|
ROUTINE_COMMITTED: 'sidekick:routine_committed',
|
|
46
47
|
ROUTINE_EXECUTED: 'sidekick:routine_executed',
|
|
47
48
|
ROUTINE_STEP_FAILED: 'sidekick:routine_step_failed',
|
|
49
|
+
/**
|
|
50
|
+
* @deprecated WU-2830 (INIT-062 WU-D): `sidekick:state_rehydrated` is
|
|
51
|
+
* unbounded and cannot stream. New emitters MUST use the chunked trio
|
|
52
|
+
* (`STATE_REHYDRATION_STARTED`, `STATE_REHYDRATION_CHUNK`,
|
|
53
|
+
* `STATE_REHYDRATION_COMPLETED`). The constant and type are retained for
|
|
54
|
+
* backwards compatibility only.
|
|
55
|
+
*/
|
|
48
56
|
STATE_REHYDRATED: 'sidekick:state_rehydrated',
|
|
57
|
+
STATE_REHYDRATION_STARTED: 'sidekick:state_rehydration_started',
|
|
58
|
+
STATE_REHYDRATION_CHUNK: 'sidekick:state_rehydration_chunk',
|
|
59
|
+
STATE_REHYDRATION_COMPLETED: 'sidekick:state_rehydration_completed',
|
|
49
60
|
} as const;
|
|
50
61
|
|
|
51
62
|
export const SIDEKICK_EVENT_KIND_VALUES = [
|
|
@@ -65,6 +76,9 @@ export const SIDEKICK_EVENT_KIND_VALUES = [
|
|
|
65
76
|
SIDEKICK_EVENT_KINDS.ROUTINE_EXECUTED,
|
|
66
77
|
SIDEKICK_EVENT_KINDS.ROUTINE_STEP_FAILED,
|
|
67
78
|
SIDEKICK_EVENT_KINDS.STATE_REHYDRATED,
|
|
79
|
+
SIDEKICK_EVENT_KINDS.STATE_REHYDRATION_STARTED,
|
|
80
|
+
SIDEKICK_EVENT_KINDS.STATE_REHYDRATION_CHUNK,
|
|
81
|
+
SIDEKICK_EVENT_KINDS.STATE_REHYDRATION_COMPLETED,
|
|
68
82
|
] as const;
|
|
69
83
|
|
|
70
84
|
export type SidekickEventKind = (typeof SIDEKICK_EVENT_KIND_VALUES)[number];
|
|
@@ -93,6 +107,9 @@ export const SIDEKICK_EVENT_BACKPRESSURE_POLICY: Record<
|
|
|
93
107
|
[SIDEKICK_EVENT_KINDS.ROUTINE_EXECUTED]: QUEUE_WITH_REPLAY,
|
|
94
108
|
[SIDEKICK_EVENT_KINDS.ROUTINE_STEP_FAILED]: QUEUE_WITH_REPLAY,
|
|
95
109
|
[SIDEKICK_EVENT_KINDS.STATE_REHYDRATED]: QUEUE_WITH_REPLAY,
|
|
110
|
+
[SIDEKICK_EVENT_KINDS.STATE_REHYDRATION_STARTED]: QUEUE_WITH_REPLAY,
|
|
111
|
+
[SIDEKICK_EVENT_KINDS.STATE_REHYDRATION_CHUNK]: QUEUE_WITH_REPLAY,
|
|
112
|
+
[SIDEKICK_EVENT_KINDS.STATE_REHYDRATION_COMPLETED]: QUEUE_WITH_REPLAY,
|
|
96
113
|
};
|
|
97
114
|
|
|
98
115
|
interface SidekickEventEnvelope {
|
|
@@ -144,13 +161,23 @@ export interface MemoryForgottenEvent extends SidekickEventEnvelope {
|
|
|
144
161
|
|
|
145
162
|
export interface ChannelMessageSentEvent extends SidekickEventEnvelope {
|
|
146
163
|
kind: typeof SIDEKICK_EVENT_KINDS.CHANNEL_MESSAGE_SENT;
|
|
147
|
-
|
|
164
|
+
/**
|
|
165
|
+
* WU-2830 (INIT-062 WU-D): typed as {@link ChannelMessageRecord} for
|
|
166
|
+
* symmetry with {@link ChannelMessageReceivedEvent.messages} and to enable
|
|
167
|
+
* static analysis cloud-side.
|
|
168
|
+
*/
|
|
169
|
+
message: ChannelMessageRecord;
|
|
148
170
|
}
|
|
149
171
|
|
|
150
172
|
export interface ChannelMessageReceivedEvent extends SidekickEventEnvelope {
|
|
151
173
|
kind: typeof SIDEKICK_EVENT_KINDS.CHANNEL_MESSAGE_RECEIVED;
|
|
152
174
|
channel: string;
|
|
153
|
-
|
|
175
|
+
/**
|
|
176
|
+
* WU-2830 (INIT-062 WU-D): full {@link ChannelMessageRecord} payloads
|
|
177
|
+
* replace the previous bare `count` field. Subscribers count via
|
|
178
|
+
* `messages.length`; symmetric with {@link ChannelMessageSentEvent.message}.
|
|
179
|
+
*/
|
|
180
|
+
messages: ChannelMessageRecord[];
|
|
154
181
|
provider?: string;
|
|
155
182
|
}
|
|
156
183
|
|
|
@@ -199,11 +226,50 @@ export interface RoutineStepFailedEvent extends SidekickEventEnvelope {
|
|
|
199
226
|
routine_name?: string;
|
|
200
227
|
}
|
|
201
228
|
|
|
229
|
+
/**
|
|
230
|
+
* @deprecated WU-2830 (INIT-062 WU-D): unbounded snapshots cannot stream.
|
|
231
|
+
* Emit {@link StateRehydrationStartedEvent} +
|
|
232
|
+
* {@link StateRehydrationChunkEvent} (one per store) +
|
|
233
|
+
* {@link StateRehydrationCompletedEvent} instead. Retained for parity with
|
|
234
|
+
* existing consumers and the compile-time parity tests.
|
|
235
|
+
*/
|
|
202
236
|
export interface StateRehydratedEvent extends SidekickEventEnvelope {
|
|
203
237
|
kind: typeof SIDEKICK_EVENT_KINDS.STATE_REHYDRATED;
|
|
204
238
|
snapshot: SidekickStores;
|
|
205
239
|
}
|
|
206
240
|
|
|
241
|
+
export interface StateRehydrationStartedEvent extends SidekickEventEnvelope {
|
|
242
|
+
kind: typeof SIDEKICK_EVENT_KINDS.STATE_REHYDRATION_STARTED;
|
|
243
|
+
/** Correlation id shared by the started/chunk/completed trio. */
|
|
244
|
+
rehydration_id: string;
|
|
245
|
+
/** Total chunks the receiver should expect. */
|
|
246
|
+
total_chunks: number;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export interface StateRehydrationChunkEvent extends SidekickEventEnvelope {
|
|
250
|
+
kind: typeof SIDEKICK_EVENT_KINDS.STATE_REHYDRATION_CHUNK;
|
|
251
|
+
/** Correlation id matching the {@link StateRehydrationStartedEvent}. */
|
|
252
|
+
rehydration_id: string;
|
|
253
|
+
/**
|
|
254
|
+
* Monotonic zero-based index identifying this chunk's position within the
|
|
255
|
+
* stream. Receivers reassemble ordered by `cursor`.
|
|
256
|
+
*/
|
|
257
|
+
cursor: number;
|
|
258
|
+
/** True for the final chunk in the stream (cursor === total_chunks - 1). */
|
|
259
|
+
last_chunk: boolean;
|
|
260
|
+
/**
|
|
261
|
+
* Subset of {@link SidekickStores} keys carried by this chunk. Receivers
|
|
262
|
+
* merge `stores` into the accumulating snapshot keyed on store name.
|
|
263
|
+
*/
|
|
264
|
+
stores: Partial<SidekickStores>;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export interface StateRehydrationCompletedEvent extends SidekickEventEnvelope {
|
|
268
|
+
kind: typeof SIDEKICK_EVENT_KINDS.STATE_REHYDRATION_COMPLETED;
|
|
269
|
+
rehydration_id: string;
|
|
270
|
+
chunk_count: number;
|
|
271
|
+
}
|
|
272
|
+
|
|
207
273
|
export type SidekickEvent =
|
|
208
274
|
| TaskCreatedEvent
|
|
209
275
|
| TaskCompletedEvent
|
|
@@ -220,7 +286,10 @@ export type SidekickEvent =
|
|
|
220
286
|
| RoutineCommittedEvent
|
|
221
287
|
| RoutineExecutedEvent
|
|
222
288
|
| RoutineStepFailedEvent
|
|
223
|
-
| StateRehydratedEvent
|
|
289
|
+
| StateRehydratedEvent
|
|
290
|
+
| StateRehydrationStartedEvent
|
|
291
|
+
| StateRehydrationChunkEvent
|
|
292
|
+
| StateRehydrationCompletedEvent;
|
|
224
293
|
|
|
225
294
|
const channelSeqCounters = new Map<string, number>();
|
|
226
295
|
|
|
@@ -406,7 +475,7 @@ export function buildMemoryForgottenEvent(memory_id: string): MemoryForgottenEve
|
|
|
406
475
|
}
|
|
407
476
|
|
|
408
477
|
export function buildChannelMessageSentEvent(
|
|
409
|
-
message:
|
|
478
|
+
message: ChannelMessageRecord,
|
|
410
479
|
): ChannelMessageSentEvent {
|
|
411
480
|
return stampSidekickEvent<ChannelMessageSentEvent>({
|
|
412
481
|
kind: SIDEKICK_EVENT_KINDS.CHANNEL_MESSAGE_SENT,
|
|
@@ -416,13 +485,13 @@ export function buildChannelMessageSentEvent(
|
|
|
416
485
|
|
|
417
486
|
export function buildChannelMessageReceivedEvent(input: {
|
|
418
487
|
channel: string;
|
|
419
|
-
|
|
488
|
+
messages: ChannelMessageRecord[];
|
|
420
489
|
provider?: string;
|
|
421
490
|
}): ChannelMessageReceivedEvent {
|
|
422
491
|
return stampSidekickEvent<ChannelMessageReceivedEvent>({
|
|
423
492
|
kind: SIDEKICK_EVENT_KINDS.CHANNEL_MESSAGE_RECEIVED,
|
|
424
493
|
channel: input.channel,
|
|
425
|
-
|
|
494
|
+
messages: input.messages,
|
|
426
495
|
...(input.provider ? { provider: input.provider } : {}),
|
|
427
496
|
});
|
|
428
497
|
}
|
|
@@ -495,6 +564,11 @@ export function buildRoutineStepFailedEvent(input: {
|
|
|
495
564
|
});
|
|
496
565
|
}
|
|
497
566
|
|
|
567
|
+
/**
|
|
568
|
+
* @deprecated WU-2830 (INIT-062 WU-D): prefer
|
|
569
|
+
* {@link buildStateRehydrationChunkEvents} which emits the bounded
|
|
570
|
+
* started/chunk/completed trio. Retained for backwards compatibility.
|
|
571
|
+
*/
|
|
498
572
|
export function buildStateRehydratedEvent(snapshot: SidekickStores): StateRehydratedEvent {
|
|
499
573
|
return stampSidekickEvent<StateRehydratedEvent>({
|
|
500
574
|
kind: SIDEKICK_EVENT_KINDS.STATE_REHYDRATED,
|
|
@@ -502,16 +576,119 @@ export function buildStateRehydratedEvent(snapshot: SidekickStores): StateRehydr
|
|
|
502
576
|
});
|
|
503
577
|
}
|
|
504
578
|
|
|
505
|
-
export function
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
):
|
|
509
|
-
return {
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
579
|
+
export function buildStateRehydrationStartedEvent(input: {
|
|
580
|
+
rehydration_id: string;
|
|
581
|
+
total_chunks: number;
|
|
582
|
+
}): StateRehydrationStartedEvent {
|
|
583
|
+
return stampSidekickEvent<StateRehydrationStartedEvent>({
|
|
584
|
+
kind: SIDEKICK_EVENT_KINDS.STATE_REHYDRATION_STARTED,
|
|
585
|
+
rehydration_id: input.rehydration_id,
|
|
586
|
+
total_chunks: input.total_chunks,
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
export function buildStateRehydrationChunkEvent(input: {
|
|
591
|
+
rehydration_id: string;
|
|
592
|
+
cursor: number;
|
|
593
|
+
last_chunk: boolean;
|
|
594
|
+
stores: Partial<SidekickStores>;
|
|
595
|
+
}): StateRehydrationChunkEvent {
|
|
596
|
+
return stampSidekickEvent<StateRehydrationChunkEvent>({
|
|
597
|
+
kind: SIDEKICK_EVENT_KINDS.STATE_REHYDRATION_CHUNK,
|
|
598
|
+
rehydration_id: input.rehydration_id,
|
|
599
|
+
cursor: input.cursor,
|
|
600
|
+
last_chunk: input.last_chunk,
|
|
601
|
+
stores: input.stores,
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
export function buildStateRehydrationCompletedEvent(input: {
|
|
606
|
+
rehydration_id: string;
|
|
607
|
+
chunk_count: number;
|
|
608
|
+
}): StateRehydrationCompletedEvent {
|
|
609
|
+
return stampSidekickEvent<StateRehydrationCompletedEvent>({
|
|
610
|
+
kind: SIDEKICK_EVENT_KINDS.STATE_REHYDRATION_COMPLETED,
|
|
611
|
+
rehydration_id: input.rehydration_id,
|
|
612
|
+
chunk_count: input.chunk_count,
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
/**
|
|
617
|
+
* WU-2830 (INIT-062 WU-D): the store order used by the chunked rehydration
|
|
618
|
+
* emitter. Exposed so receivers that care about deterministic reassembly
|
|
619
|
+
* can cross-check cursor semantics.
|
|
620
|
+
*/
|
|
621
|
+
export const SIDEKICK_REHYDRATION_STORE_ORDER = [
|
|
622
|
+
'tasks',
|
|
623
|
+
'memories',
|
|
624
|
+
'channels',
|
|
625
|
+
'messages',
|
|
626
|
+
'routines',
|
|
627
|
+
] as const satisfies readonly StoreName[];
|
|
628
|
+
|
|
629
|
+
export type SidekickRehydrationStore = (typeof SIDEKICK_REHYDRATION_STORE_ORDER)[number];
|
|
630
|
+
|
|
631
|
+
/**
|
|
632
|
+
* WU-2830 (INIT-062 WU-D): emit the chunked rehydration stream.
|
|
633
|
+
*
|
|
634
|
+
* Emission order: `state_rehydration_started` → one
|
|
635
|
+
* `state_rehydration_chunk` per store in
|
|
636
|
+
* {@link SIDEKICK_REHYDRATION_STORE_ORDER} → `state_rehydration_completed`.
|
|
637
|
+
* `cursor` is a zero-based monotonic index; `last_chunk` is true on the final
|
|
638
|
+
* chunk so streaming receivers can close the reassembly window without
|
|
639
|
+
* waiting for the completion event.
|
|
640
|
+
*/
|
|
641
|
+
export async function emitSidekickStateRehydration(snapshot: SidekickStores): Promise<{
|
|
642
|
+
rehydration_id: string;
|
|
643
|
+
chunk_count: number;
|
|
644
|
+
}> {
|
|
645
|
+
const rehydration_id = randomUUID();
|
|
646
|
+
const stores = SIDEKICK_REHYDRATION_STORE_ORDER;
|
|
647
|
+
const total_chunks = stores.length;
|
|
648
|
+
|
|
649
|
+
await emitSidekickEvent(buildStateRehydrationStartedEvent({ rehydration_id, total_chunks }));
|
|
650
|
+
|
|
651
|
+
for (let cursor = 0; cursor < stores.length; cursor++) {
|
|
652
|
+
const store = stores[cursor];
|
|
653
|
+
if (store === undefined) {
|
|
654
|
+
continue;
|
|
655
|
+
}
|
|
656
|
+
const last_chunk = cursor === stores.length - 1;
|
|
657
|
+
const chunkStores: Partial<SidekickStores> = {};
|
|
658
|
+
// Narrow the union so TS keeps the store-value type aligned with the key.
|
|
659
|
+
switch (store) {
|
|
660
|
+
case 'tasks':
|
|
661
|
+
chunkStores.tasks = snapshot.tasks;
|
|
662
|
+
break;
|
|
663
|
+
case 'memories':
|
|
664
|
+
chunkStores.memories = snapshot.memories;
|
|
665
|
+
break;
|
|
666
|
+
case 'channels':
|
|
667
|
+
chunkStores.channels = snapshot.channels;
|
|
668
|
+
break;
|
|
669
|
+
case 'messages':
|
|
670
|
+
chunkStores.messages = snapshot.messages;
|
|
671
|
+
break;
|
|
672
|
+
case 'routines':
|
|
673
|
+
chunkStores.routines = snapshot.routines;
|
|
674
|
+
break;
|
|
675
|
+
}
|
|
676
|
+
await emitSidekickEvent(
|
|
677
|
+
buildStateRehydrationChunkEvent({
|
|
678
|
+
rehydration_id,
|
|
679
|
+
cursor,
|
|
680
|
+
last_chunk,
|
|
681
|
+
stores: chunkStores,
|
|
682
|
+
}),
|
|
683
|
+
);
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
await emitSidekickEvent(
|
|
687
|
+
buildStateRehydrationCompletedEvent({
|
|
688
|
+
rehydration_id,
|
|
689
|
+
chunk_count: total_chunks,
|
|
690
|
+
}),
|
|
691
|
+
);
|
|
692
|
+
|
|
693
|
+
return { rehydration_id, chunk_count: total_chunks };
|
|
517
694
|
}
|
|
@@ -294,18 +294,26 @@ export function createControlPlaneChannelBridge(
|
|
|
294
294
|
// Port methods
|
|
295
295
|
// -------------------------------------------------------------------------
|
|
296
296
|
|
|
297
|
+
async function registerInternal(bridgeConfig: BridgeConfig): Promise<ChannelId> {
|
|
298
|
+
const key = `${bridgeConfig.provider}::${bridgeConfig.name}::${hashOptions(
|
|
299
|
+
bridgeConfig.options as Record<string, unknown> | undefined,
|
|
300
|
+
)}`;
|
|
301
|
+
const existing = registry.get(key);
|
|
302
|
+
if (existing) {
|
|
303
|
+
return existing;
|
|
304
|
+
}
|
|
305
|
+
const id = mintChannelId(bridgeConfig);
|
|
306
|
+
registry.set(key, id);
|
|
307
|
+
return id;
|
|
308
|
+
}
|
|
309
|
+
|
|
297
310
|
return {
|
|
311
|
+
async connect(bridgeConfig: BridgeConfig): Promise<ChannelId> {
|
|
312
|
+
return registerInternal(bridgeConfig);
|
|
313
|
+
},
|
|
314
|
+
|
|
298
315
|
async register(bridgeConfig: BridgeConfig): Promise<ChannelId> {
|
|
299
|
-
|
|
300
|
-
bridgeConfig.options as Record<string, unknown> | undefined,
|
|
301
|
-
)}`;
|
|
302
|
-
const existing = registry.get(key);
|
|
303
|
-
if (existing) {
|
|
304
|
-
return existing;
|
|
305
|
-
}
|
|
306
|
-
const id = mintChannelId(bridgeConfig);
|
|
307
|
-
registry.set(key, id);
|
|
308
|
-
return id;
|
|
316
|
+
return registerInternal(bridgeConfig);
|
|
309
317
|
},
|
|
310
318
|
|
|
311
319
|
async send(channelId: ChannelId, envelope: ChannelEnvelope): Promise<SendResult> {
|
|
@@ -180,6 +180,10 @@ export function createFilesystemChannelBridge(
|
|
|
180
180
|
}
|
|
181
181
|
|
|
182
182
|
return {
|
|
183
|
+
async connect(bridgeConfig: BridgeConfig): Promise<ChannelId> {
|
|
184
|
+
return ensureRegistered(bridgeConfig);
|
|
185
|
+
},
|
|
186
|
+
|
|
183
187
|
async register(bridgeConfig: BridgeConfig): Promise<ChannelId> {
|
|
184
188
|
return ensureRegistered(bridgeConfig);
|
|
185
189
|
},
|
|
@@ -2,23 +2,37 @@
|
|
|
2
2
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* WU-2735 (INIT-060 WU-7a
|
|
6
|
-
*
|
|
5
|
+
* WU-2735 (INIT-060 WU-7a) + WU-2831 (INIT-062 WU-E):
|
|
6
|
+
* Domain types for the sidekick ChannelBridge adapters.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
8
|
+
* As of WU-2831 the port-level identity, config, and send-result shapes are
|
|
9
|
+
* canonical in @lumenflow/conductor-sdk (Apache-2.0). This file re-exports
|
|
10
|
+
* those types so sidekick adapters implement the neutral port without
|
|
11
|
+
* parallel contract drift.
|
|
12
|
+
*
|
|
13
|
+
* The internal `EnvelopeKind` + `ChannelEnvelope` shape below remains the
|
|
14
|
+
* sidekick-pack wire format: it predates the canonical port and is retained
|
|
15
|
+
* for on-disk/control-plane compatibility of existing adapters. New
|
|
16
|
+
* implementers (e.g. the cloud phone bridge) SHOULD consume
|
|
17
|
+
* `@lumenflow/conductor-sdk`'s `ChannelEnvelope` instead.
|
|
12
18
|
*/
|
|
13
19
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
20
|
+
// Re-export canonical port types from the Apache-2.0 conductor SDK.
|
|
21
|
+
export type {
|
|
22
|
+
BackpressurePolicy,
|
|
23
|
+
BridgeConfig,
|
|
24
|
+
ChannelId,
|
|
25
|
+
ChannelIdentity,
|
|
26
|
+
DeliveryGuarantee,
|
|
27
|
+
IdentityResolver,
|
|
28
|
+
SendResult,
|
|
29
|
+
} from '@lumenflow/conductor-sdk';
|
|
19
30
|
|
|
20
31
|
/**
|
|
21
|
-
* Envelope dispatch classification
|
|
32
|
+
* Envelope dispatch classification — sidekick-internal wire format.
|
|
33
|
+
*
|
|
34
|
+
* Predates the canonical `BackpressurePolicy` enum in conductor-sdk.
|
|
35
|
+
* Maps: `ephemeral` ↔ `ephemeral`, `queue` ↔ `buffered` (conductor-sdk).
|
|
22
36
|
*
|
|
23
37
|
* - `ephemeral` — observational; may fail-silent when transport is down.
|
|
24
38
|
* - `queue` — commands / approvals; must be queued and replayed on reconnect.
|
|
@@ -26,14 +40,18 @@ export type ChannelId = string & { readonly __brand: 'sidekick.ChannelId' };
|
|
|
26
40
|
export type EnvelopeKind = 'ephemeral' | 'queue';
|
|
27
41
|
|
|
28
42
|
/**
|
|
29
|
-
* A channel envelope
|
|
30
|
-
*
|
|
31
|
-
* without snooping the body.
|
|
43
|
+
* A channel envelope (sidekick-internal wire format).
|
|
44
|
+
* Transport-agnostic: `body` is an opaque JSON-serialisable payload,
|
|
45
|
+
* `content_type` labels it so receivers can dispatch without snooping the body.
|
|
46
|
+
*
|
|
47
|
+
* Note: the canonical port-level envelope is
|
|
48
|
+
* `@lumenflow/conductor-sdk`'s `ChannelEnvelope` (uses `policy` field).
|
|
49
|
+
* Adapters translate between the two when bridging across the Apache boundary.
|
|
32
50
|
*/
|
|
33
51
|
export interface ChannelEnvelope {
|
|
34
52
|
/** Unique id per envelope (content-hash or uuid — chosen by the emitter). */
|
|
35
53
|
id: string;
|
|
36
|
-
/**
|
|
54
|
+
/** Pack-internal backpressure classification. */
|
|
37
55
|
kind: EnvelopeKind;
|
|
38
56
|
/** MIME-like content descriptor, e.g. `application/json`, `text/plain`. */
|
|
39
57
|
content_type: string;
|
|
@@ -44,41 +62,3 @@ export interface ChannelEnvelope {
|
|
|
44
62
|
/** Optional emitter-supplied metadata (trace ids, correlation keys). */
|
|
45
63
|
metadata?: Readonly<Record<string, unknown>>;
|
|
46
64
|
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Bridge registration config — identity under which a channel is opened.
|
|
50
|
-
*
|
|
51
|
-
* The (`provider`, `name`) pair is the canonical identity: re-registering the
|
|
52
|
-
* same pair returns the same `ChannelId` (ADR-013 §ChannelBridge contract
|
|
53
|
-
* rule #3 — idempotent register).
|
|
54
|
-
*
|
|
55
|
-
* `options` is provider-specific bag. The port does NOT inspect it; adapters
|
|
56
|
-
* interpret.
|
|
57
|
-
*/
|
|
58
|
-
export interface BridgeConfig {
|
|
59
|
-
/** Provider slug — e.g. `filesystem`, `control-plane`. */
|
|
60
|
-
provider: string;
|
|
61
|
-
/** Human-readable channel name scoped within the provider. */
|
|
62
|
-
name: string;
|
|
63
|
-
/** Provider-specific options (paths, tokens, etc.). Opaque to the port. */
|
|
64
|
-
options?: Readonly<Record<string, unknown>>;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Result of a `send` attempt. `accepted` reflects the port contract: for
|
|
69
|
-
* ephemeral envelopes with an unreachable transport, adapters may return
|
|
70
|
-
* `{ accepted: false, reason: '...' }` rather than throwing (§4 fail-silent).
|
|
71
|
-
*/
|
|
72
|
-
export interface SendResult {
|
|
73
|
-
accepted: boolean;
|
|
74
|
-
/** Transport-assigned id when the sink confirms receipt (optional). */
|
|
75
|
-
delivery_id?: string;
|
|
76
|
-
/** Reason when `accepted === false` (not meant for programmatic handling). */
|
|
77
|
-
reason?: string;
|
|
78
|
-
/**
|
|
79
|
-
* `true` when the sink recognised `envelope.id` on a registered channel as
|
|
80
|
-
* an at-least-once replay (WU-2737 §Idempotency; cloud contract surface).
|
|
81
|
-
* Adapters that cannot observe dedup SHOULD omit this field.
|
|
82
|
-
*/
|
|
83
|
-
deduped?: boolean;
|
|
84
|
-
}
|