@lumenflow/cli 4.24.0 → 5.0.1
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/README.md +54 -52
- package/dist/agent-issues-query.js +10 -2
- package/dist/agent-issues-query.js.map +1 -1
- package/dist/agent-runtime-enrollment-events.js +44 -0
- package/dist/agent-runtime-enrollment-events.js.map +1 -0
- package/dist/agent-session-end.js +47 -0
- package/dist/agent-session-end.js.map +1 -1
- package/dist/agent-session-heartbeat.js +250 -0
- package/dist/agent-session-heartbeat.js.map +1 -0
- package/dist/agent-session.js +299 -5
- package/dist/agent-session.js.map +1 -1
- package/dist/capacity-snapshot-emitter.js +73 -0
- package/dist/capacity-snapshot-emitter.js.map +1 -0
- package/dist/claim-queue.js +276 -0
- package/dist/claim-queue.js.map +1 -0
- package/dist/config-set.js +22 -3
- package/dist/config-set.js.map +1 -1
- package/dist/control-plane-sidecar-runner.js +145 -0
- package/dist/control-plane-sidecar-runner.js.map +1 -0
- package/dist/delegation-list.js +160 -1
- package/dist/delegation-list.js.map +1 -1
- package/dist/delegation-role-resolver.js +69 -0
- package/dist/delegation-role-resolver.js.map +1 -0
- package/dist/docs-generate-pack-reference.js +500 -0
- package/dist/docs-generate-pack-reference.js.map +1 -0
- package/dist/docs-sync.js +116 -1
- package/dist/docs-sync.js.map +1 -1
- package/dist/file-edit.js +28 -8
- package/dist/file-edit.js.map +1 -1
- package/dist/file-write.js +29 -5
- package/dist/file-write.js.map +1 -1
- package/dist/gate-co-change.js +25 -7
- package/dist/gate-co-change.js.map +1 -1
- package/dist/gate-conditional.js +19 -7
- package/dist/gate-conditional.js.map +1 -1
- package/dist/gates-runners.js +42 -33
- package/dist/gates-runners.js.map +1 -1
- package/dist/gates-utils.js +34 -20
- package/dist/gates-utils.js.map +1 -1
- package/dist/gates.js +79 -7
- package/dist/gates.js.map +1 -1
- package/dist/hooks/config-resolver.js +10 -1
- package/dist/hooks/config-resolver.js.map +1 -1
- package/dist/init-package-config.js +1 -1
- package/dist/init-package-config.js.map +1 -1
- package/dist/init-scaffolding.js +5 -1
- package/dist/init-scaffolding.js.map +1 -1
- package/dist/init-templates.js +10 -0
- package/dist/init-templates.js.map +1 -1
- package/dist/init.js +1 -1
- package/dist/init.js.map +1 -1
- package/dist/initiative-create.js +17 -0
- package/dist/initiative-create.js.map +1 -1
- package/dist/initiative-remove-wu.js +17 -3
- package/dist/initiative-remove-wu.js.map +1 -1
- package/dist/kernel-event-sync/emitters.js +104 -0
- package/dist/kernel-event-sync/emitters.js.map +1 -0
- package/dist/kernel-event-sync/index.js +13 -0
- package/dist/kernel-event-sync/index.js.map +1 -0
- package/dist/kernel-event-sync/lifecycle-emitters.js +160 -0
- package/dist/kernel-event-sync/lifecycle-emitters.js.map +1 -0
- package/dist/kernel-event-sync/narrow-emissions.js +89 -0
- package/dist/kernel-event-sync/narrow-emissions.js.map +1 -0
- package/dist/kernel-event-sync/software-delivery-emitters.js +297 -0
- package/dist/kernel-event-sync/software-delivery-emitters.js.map +1 -0
- package/dist/lane-lock.js +14 -1
- package/dist/lane-lock.js.map +1 -1
- package/dist/lane-suggest.js +21 -0
- package/dist/lane-suggest.js.map +1 -1
- package/dist/lumenflow-upgrade.js +7 -5
- package/dist/lumenflow-upgrade.js.map +1 -1
- package/dist/mem-context.js +145 -0
- package/dist/mem-context.js.map +1 -1
- package/dist/mem-create.js +39 -6
- package/dist/mem-create.js.map +1 -1
- package/dist/mem-inbox.js +16 -0
- package/dist/mem-inbox.js.map +1 -1
- package/dist/mem-roster.js +95 -0
- package/dist/mem-roster.js.map +1 -0
- package/dist/mem-signal.js +97 -2
- package/dist/mem-signal.js.map +1 -1
- package/dist/metrics-snapshot.js +3 -2
- package/dist/metrics-snapshot.js.map +1 -1
- package/dist/orchestrate-init-status.js +117 -2
- package/dist/orchestrate-init-status.js.map +1 -1
- package/dist/orchestrate-initiative.js +83 -10
- package/dist/orchestrate-initiative.js.map +1 -1
- package/dist/orchestrate-monitor-quality.js +289 -0
- package/dist/orchestrate-monitor-quality.js.map +1 -0
- package/dist/orchestrate-monitor.js +85 -0
- package/dist/orchestrate-monitor.js.map +1 -1
- package/dist/pack-validate.js +127 -2
- package/dist/pack-validate.js.map +1 -1
- package/dist/plan-create.js +18 -0
- package/dist/plan-create.js.map +1 -1
- package/dist/plan-link.js +13 -0
- package/dist/plan-link.js.map +1 -1
- package/dist/plan-promote.js +14 -0
- package/dist/plan-promote.js.map +1 -1
- package/dist/pre-commit-check.js +4 -3
- package/dist/pre-commit-check.js.map +1 -1
- package/dist/public-manifest.js +17 -3
- package/dist/public-manifest.js.map +1 -1
- package/dist/release.js +28 -10
- package/dist/release.js.map +1 -1
- package/dist/session-cross-link.js +139 -0
- package/dist/session-cross-link.js.map +1 -0
- package/dist/sidecar-manager.js +208 -0
- package/dist/sidecar-manager.js.map +1 -0
- package/dist/state-path-resolvers.js +18 -0
- package/dist/state-path-resolvers.js.map +1 -1
- package/dist/stream-heartbeat.js +151 -0
- package/dist/stream-heartbeat.js.map +1 -0
- package/dist/sync-templates.js +56 -2
- package/dist/sync-templates.js.map +1 -1
- package/dist/wu-block.js +47 -5
- package/dist/wu-block.js.map +1 -1
- package/dist/wu-claim-branch.js +8 -4
- package/dist/wu-claim-branch.js.map +1 -1
- package/dist/wu-claim-state.js +5 -3
- package/dist/wu-claim-state.js.map +1 -1
- package/dist/wu-claim-worktree.js +5 -3
- package/dist/wu-claim-worktree.js.map +1 -1
- package/dist/wu-claim.js +261 -9
- package/dist/wu-claim.js.map +1 -1
- package/dist/wu-done-auto-cleanup.js +3 -2
- package/dist/wu-done-auto-cleanup.js.map +1 -1
- package/dist/wu-done-git-ops.js +12 -8
- package/dist/wu-done-git-ops.js.map +1 -1
- package/dist/wu-done-preflight.js +3 -3
- package/dist/wu-done-preflight.js.map +1 -1
- package/dist/wu-done.js +46 -10
- package/dist/wu-done.js.map +1 -1
- package/dist/wu-lifecycle-sync/gate-scope-resolver.js +16 -0
- package/dist/wu-lifecycle-sync/gate-scope-resolver.js.map +1 -0
- package/dist/wu-lifecycle-sync/kernel-event-sync-shim.js +10 -0
- package/dist/wu-lifecycle-sync/kernel-event-sync-shim.js.map +1 -0
- package/dist/wu-prep.js +363 -22
- package/dist/wu-prep.js.map +1 -1
- package/dist/wu-prune.js +68 -27
- package/dist/wu-prune.js.map +1 -1
- package/dist/wu-release.js +34 -3
- package/dist/wu-release.js.map +1 -1
- package/dist/wu-review.js +167 -0
- package/dist/wu-review.js.map +1 -0
- package/dist/wu-spawn-prompt-builders.js +296 -40
- package/dist/wu-spawn-prompt-builders.js.map +1 -1
- package/dist/wu-spawn-strategy-resolver.js +126 -14
- package/dist/wu-spawn-strategy-resolver.js.map +1 -1
- package/dist/wu-unblock.js +52 -22
- package/dist/wu-unblock.js.map +1 -1
- package/package.json +13 -8
- package/packs/agent-runtime/agent-heartbeat.ts +163 -0
- package/packs/agent-runtime/auto-session-integration.ts +874 -0
- package/packs/agent-runtime/delegation-registry-schema.ts +220 -0
- package/packs/agent-runtime/delegation-registry-store.ts +269 -0
- package/packs/agent-runtime/delegation-tree.ts +328 -0
- package/packs/agent-runtime/index.ts +9 -0
- package/packs/agent-runtime/manifest.ts +109 -19
- package/packs/agent-runtime/manifest.yaml +150 -0
- package/packs/agent-runtime/memory-coordination-contract.ts +86 -0
- package/packs/agent-runtime/memory.d.ts +19 -0
- package/packs/agent-runtime/orchestration.ts +238 -23
- package/packs/agent-runtime/package.json +11 -2
- package/packs/agent-runtime/remote-controls/index.ts +7 -0
- package/packs/agent-runtime/remote-controls/operations.ts +399 -0
- package/packs/agent-runtime/remote-controls/port.ts +48 -0
- package/packs/agent-runtime/remote-controls/state-store.ts +258 -0
- package/packs/agent-runtime/remote-controls/types.ts +105 -0
- package/packs/agent-runtime/session-schema.ts +423 -0
- package/packs/agent-runtime/tool-impl/index.ts +1 -0
- package/packs/agent-runtime/tool-impl/remote-controls.mock.ts +252 -0
- package/packs/agent-runtime/tool-impl/remote-controls.ts +273 -0
- package/packs/agent-runtime/tsconfig.json +1 -1
- package/packs/agent-runtime/turn-lifecycle-events.ts +501 -0
- package/packs/sidekick/channel-ingress.ts +137 -0
- package/packs/sidekick/manifest.ts +74 -0
- package/packs/sidekick/manifest.yaml +88 -0
- package/packs/sidekick/package.json +3 -1
- package/packs/sidekick/sidekick-events.ts +517 -0
- package/packs/sidekick/src/adapters/cloud-queue.ts +101 -0
- package/packs/sidekick/src/adapters/control-plane-bridge.adapter.ts +378 -0
- package/packs/sidekick/src/adapters/filesystem-bridge.adapter.ts +224 -0
- package/packs/sidekick/src/domain/channel.types.ts +84 -0
- package/packs/sidekick/src/ports/channel-bridge.port.ts +75 -0
- package/packs/sidekick/src/routines/commit.ts +74 -0
- package/packs/sidekick/tool-impl/channel-tools.ts +47 -0
- package/packs/sidekick/tool-impl/memory-tools.ts +17 -0
- package/packs/sidekick/tool-impl/routine-commit.ts +102 -0
- package/packs/sidekick/tool-impl/routine-tools.ts +67 -7
- package/packs/sidekick/tool-impl/runtime-context.ts +4 -0
- package/packs/sidekick/tool-impl/storage.ts +3 -0
- package/packs/sidekick/tool-impl/system-tools.ts +7 -0
- package/packs/sidekick/tool-impl/task-tools.ts +46 -0
- package/packs/sidekick/tsconfig.json +1 -1
- package/packs/software-delivery/manifest-schema.ts +30 -0
- package/packs/software-delivery/manifest.ts +160 -11
- package/packs/software-delivery/manifest.yaml +210 -230
- package/packs/software-delivery/package.json +88 -3
- package/packs/software-delivery/src/commands/index.ts +5 -0
- package/packs/software-delivery/src/config/delivery-review-contract.ts +20 -0
- package/packs/software-delivery/src/config/env-accessors.ts +19 -0
- package/packs/software-delivery/src/config/index.ts +8 -0
- package/packs/software-delivery/src/config/normalize-config-keys.ts +19 -0
- package/packs/software-delivery/src/config/schemas/lumenflow-config-schema-types.ts +436 -0
- package/packs/software-delivery/src/config/workspace-reader.ts +310 -0
- package/packs/software-delivery/src/constants/backlog-patterns.ts +31 -0
- package/packs/software-delivery/src/constants/client-ids.ts +19 -0
- package/packs/software-delivery/src/constants/config-contract.ts +7 -0
- package/packs/software-delivery/src/constants/docs-layout-presets.ts +50 -0
- package/packs/software-delivery/src/constants/duration-constants.ts +20 -0
- package/packs/software-delivery/src/constants/gate-constants.ts +32 -0
- package/packs/software-delivery/src/constants/index.ts +29 -0
- package/packs/software-delivery/src/constants/lock-constants.ts +35 -0
- package/packs/software-delivery/src/constants/object-guards.ts +12 -0
- package/packs/software-delivery/src/constants/section-headings.ts +107 -0
- package/packs/software-delivery/src/constants/wu-cli-constants.ts +485 -0
- package/packs/software-delivery/src/constants/wu-domain-constants.ts +466 -0
- package/packs/software-delivery/src/constants/wu-git-constants.ts +7 -0
- package/packs/software-delivery/src/constants/wu-id-format.ts +327 -0
- package/packs/software-delivery/src/constants/wu-paths-constants.ts +358 -0
- package/packs/software-delivery/src/constants/wu-statuses.ts +287 -0
- package/packs/software-delivery/src/constants/wu-type-helpers.ts +67 -0
- package/packs/software-delivery/src/constants/wu-ui-constants.ts +267 -0
- package/packs/software-delivery/src/constants/wu-validation-constants.ts +73 -0
- package/packs/software-delivery/src/domain/index.ts +5 -0
- package/packs/software-delivery/src/domain/orchestration.constants.ts +168 -0
- package/packs/software-delivery/src/domain/orchestration.schemas.ts +239 -0
- package/packs/software-delivery/src/domain/orchestration.types.ts +178 -0
- package/packs/software-delivery/src/methodology/incremental-test.ts +90 -0
- package/packs/software-delivery/src/methodology/index.ts +6 -0
- package/packs/software-delivery/src/methodology/manual-test-validator.ts +292 -0
- package/packs/software-delivery/src/policy/coverage-gate.ts +270 -0
- package/packs/software-delivery/src/policy/gates-agent-mode.ts +223 -0
- package/packs/software-delivery/src/policy/gates-config-internal.ts +121 -0
- package/packs/software-delivery/src/policy/gates-config.ts +293 -0
- package/packs/software-delivery/src/policy/gates-coverage.ts +247 -0
- package/packs/software-delivery/src/policy/gates-presets.ts +134 -0
- package/packs/software-delivery/src/policy/gates-schemas.ts +173 -0
- package/packs/software-delivery/src/policy/index.ts +22 -0
- package/packs/software-delivery/src/policy/package-manager-resolver.ts +319 -0
- package/packs/software-delivery/src/policy/resolve-policy.ts +518 -0
- package/packs/software-delivery/src/ports/config.ports.ts +90 -0
- package/packs/software-delivery/src/ports/dashboard-renderer.port.ts +125 -0
- package/packs/software-delivery/src/ports/index.ts +10 -0
- package/packs/software-delivery/src/ports/sync-validator.ports.ts +59 -0
- package/packs/software-delivery/src/ports/wu-helpers.ports.ts +168 -0
- package/packs/software-delivery/src/ports/wu-state.ports.ts +241 -0
- package/packs/software-delivery/src/primitives/index.ts +5 -0
- package/packs/software-delivery/src/runtime/index.ts +6 -0
- package/packs/software-delivery/src/runtime/work-classifier.ts +561 -0
- package/packs/software-delivery/src/sandbox/index.ts +10 -0
- package/packs/software-delivery/src/sandbox/sandbox-allowlist.ts +118 -0
- package/packs/software-delivery/src/sandbox/sandbox-backend-linux.ts +88 -0
- package/packs/software-delivery/src/sandbox/sandbox-backend-macos.ts +154 -0
- package/packs/software-delivery/src/sandbox/sandbox-backend-windows.ts +47 -0
- package/packs/software-delivery/src/sandbox/sandbox-profile.ts +153 -0
- package/packs/software-delivery/src/schemas/index.ts +5 -0
- package/packs/software-delivery/src/state/date-utils.ts +158 -0
- package/packs/software-delivery/src/state/index.ts +15 -0
- package/packs/software-delivery/src/state/state-machine.ts +119 -0
- package/packs/software-delivery/src/state/wu-doc-types.ts +51 -0
- package/packs/software-delivery/src/state/wu-paths.ts +381 -0
- package/packs/software-delivery/src/state/wu-schema.ts +1139 -0
- package/packs/software-delivery/src/state/wu-state-schema.ts +255 -0
- package/packs/software-delivery/src/state/wu-yaml.ts +338 -0
- package/packs/software-delivery/src/types.d.ts +16 -0
- package/packs/software-delivery/tool-impl/wu-lifecycle-tools.ts +18 -0
- package/packs/software-delivery/tsconfig.json +28 -2
- package/templates/core/AGENTS.md.template +76 -17
- package/templates/core/LUMENFLOW.md.template +265 -66
- package/templates/core/_frameworks/lumenflow/wu-sizing-guide.md.template +180 -116
- package/templates/core/ai/onboarding/agent-invocation-guide.md.template +26 -8
- package/templates/core/ai/onboarding/existing-project-bootstrap.md.template +171 -0
- package/templates/core/ai/onboarding/first-15-mins.md.template +3 -1
- package/templates/core/ai/onboarding/first-wu-mistakes.md.template +1 -1
- package/templates/core/ai/onboarding/initiative-orchestration.md.template +46 -30
- package/templates/core/ai/onboarding/quick-ref-commands.md.template +36 -33
- package/templates/core/ai/onboarding/release-process.md.template +8 -7
- package/templates/core/ai/onboarding/starting-prompt.md.template +2 -0
- package/templates/core/ai/onboarding/troubleshooting-wu-done.md.template +62 -0
- package/templates/vendors/claude/.claude/CLAUDE.md.template +29 -54
- package/templates/vendors/cursor/.cursor/rules/lumenflow.md.template +24 -52
- package/templates/vendors/windsurf/.windsurf/rules/lumenflow.md.template +24 -52
- package/packs/agent-runtime/.turbo/turbo-build.log +0 -4
- package/packs/sidekick/.turbo/turbo-build.log +0 -4
- package/packs/software-delivery/.turbo/turbo-build.log +0 -4
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// Copyright (c) 2026 Hellmai Ltd
|
|
2
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* WU-2735 (INIT-060 WU-7a, ADR-013 §ChannelBridge):
|
|
6
|
+
* ChannelBridge port.
|
|
7
|
+
*
|
|
8
|
+
* Port contract — load-bearing across adapters (ADR-013 §ChannelBridge):
|
|
9
|
+
*
|
|
10
|
+
* - `send` is at-least-once for `queue` envelopes, fail-silent for
|
|
11
|
+
* `ephemeral` envelopes (ADR-013 §4 backpressure split).
|
|
12
|
+
* - `receive` yields envelopes in emit order on a single channel (§3). No
|
|
13
|
+
* cross-channel ordering guarantee.
|
|
14
|
+
* - `register` is idempotent on `BridgeConfig` identity — same
|
|
15
|
+
* `(provider, name)` returns the same `ChannelId`.
|
|
16
|
+
* - `disconnect` flushes queued envelopes before closing; in-flight ephemerals
|
|
17
|
+
* may drop.
|
|
18
|
+
*
|
|
19
|
+
* The port MUST NOT leak filesystem- or network-specific types. Both the
|
|
20
|
+
* filesystem adapter (this WU) and the cloud adapter (WU-2737) implement this
|
|
21
|
+
* interface unmodified.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import type {
|
|
25
|
+
BridgeConfig,
|
|
26
|
+
ChannelEnvelope,
|
|
27
|
+
ChannelId,
|
|
28
|
+
SendResult,
|
|
29
|
+
} from '../domain/channel.types.js';
|
|
30
|
+
|
|
31
|
+
export interface ChannelBridge {
|
|
32
|
+
/**
|
|
33
|
+
* Send an envelope on a registered channel.
|
|
34
|
+
*
|
|
35
|
+
* @throws if `channelId` is not registered, or has been disconnected.
|
|
36
|
+
*/
|
|
37
|
+
send(channelId: ChannelId, envelope: ChannelEnvelope): Promise<SendResult>;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Stream envelopes on the given channel in emit order. The iterator
|
|
41
|
+
* terminates after `disconnect(channelId)`; otherwise it is long-lived.
|
|
42
|
+
*/
|
|
43
|
+
receive(channelId: ChannelId): AsyncIterable<ChannelEnvelope>;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Register a channel and return its id. Idempotent on `(provider, name)`.
|
|
47
|
+
*/
|
|
48
|
+
register(bridgeConfig: BridgeConfig): Promise<ChannelId>;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Close the channel. Queued envelopes flush before this resolves; ephemerals
|
|
52
|
+
* in flight may be dropped.
|
|
53
|
+
*/
|
|
54
|
+
disconnect(channelId: ChannelId): Promise<void>;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Structural type guard. Useful for adapter tests and runtime payload
|
|
59
|
+
* validation at the port boundary.
|
|
60
|
+
*/
|
|
61
|
+
export function isChannelEnvelope(value: unknown): value is ChannelEnvelope {
|
|
62
|
+
if (!value || typeof value !== 'object') {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
const v = value as Record<string, unknown>;
|
|
66
|
+
return (
|
|
67
|
+
typeof v.id === 'string' &&
|
|
68
|
+
(v.kind === 'ephemeral' || v.kind === 'queue') &&
|
|
69
|
+
typeof v.content_type === 'string' &&
|
|
70
|
+
'body' in v &&
|
|
71
|
+
typeof v.emitted_at === 'string'
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export type { BridgeConfig, ChannelEnvelope, ChannelId, SendResult };
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// Copyright (c) 2026 Hellmai Ltd
|
|
2
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* WU-2738 (INIT-060, ADR-013 §6 governance): plan-to-commit envelope +
|
|
6
|
+
* content-hash helpers for the governed sidekick:routine:commit_plan
|
|
7
|
+
* tool. The commit envelope is what an agent attestation carries; the
|
|
8
|
+
* content-hash is what lands on the audit trail so cloud can correlate
|
|
9
|
+
* a `sidekick:routine_committed` event with the originating envelope
|
|
10
|
+
* without the PII-bearing body ever leaving the local workspace.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { createHash } from 'node:crypto';
|
|
14
|
+
import type { RoutineRecord } from '../../tool-impl/storage.js';
|
|
15
|
+
|
|
16
|
+
const CONTENT_HASH_ALGORITHM = 'sha256';
|
|
17
|
+
const CONTENT_HASH_ENCODING = 'hex';
|
|
18
|
+
|
|
19
|
+
export interface RoutineCommitAttestation {
|
|
20
|
+
actor: string;
|
|
21
|
+
reason: string;
|
|
22
|
+
// Intentionally untyped extension slot: attestation may carry arbitrary
|
|
23
|
+
// operator-supplied metadata which can contain PII. The envelope hash
|
|
24
|
+
// covers the entire attestation; the body never appears on the audit
|
|
25
|
+
// event surface.
|
|
26
|
+
[key: string]: unknown;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface RoutineCommitEnvelope {
|
|
30
|
+
plan_id: string;
|
|
31
|
+
routine_name: string;
|
|
32
|
+
step_count: number;
|
|
33
|
+
committed_at: string;
|
|
34
|
+
attestation: RoutineCommitAttestation;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface BuildRoutineCommitEnvelopeInput {
|
|
38
|
+
routine: RoutineRecord;
|
|
39
|
+
attestation: RoutineCommitAttestation;
|
|
40
|
+
committedAt: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function buildRoutineCommitEnvelope(
|
|
44
|
+
input: BuildRoutineCommitEnvelopeInput,
|
|
45
|
+
): RoutineCommitEnvelope {
|
|
46
|
+
return {
|
|
47
|
+
plan_id: input.routine.id,
|
|
48
|
+
routine_name: input.routine.name,
|
|
49
|
+
step_count: input.routine.steps.length,
|
|
50
|
+
committed_at: input.committedAt,
|
|
51
|
+
attestation: { ...input.attestation },
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function canonicalize(value: unknown): unknown {
|
|
56
|
+
if (Array.isArray(value)) {
|
|
57
|
+
return value.map((entry) => canonicalize(entry));
|
|
58
|
+
}
|
|
59
|
+
if (value && typeof value === 'object') {
|
|
60
|
+
const record = value as Record<string, unknown>;
|
|
61
|
+
const sortedKeys = Object.keys(record).sort();
|
|
62
|
+
const result: Record<string, unknown> = {};
|
|
63
|
+
for (const key of sortedKeys) {
|
|
64
|
+
result[key] = canonicalize(record[key]);
|
|
65
|
+
}
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
68
|
+
return value;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function hashRoutineCommitEnvelope(envelope: RoutineCommitEnvelope): string {
|
|
72
|
+
const canonical = JSON.stringify(canonicalize(envelope));
|
|
73
|
+
return createHash(CONTENT_HASH_ALGORITHM).update(canonical).digest(CONTENT_HASH_ENCODING);
|
|
74
|
+
}
|
|
@@ -16,6 +16,14 @@ import {
|
|
|
16
16
|
type ToolContextLike,
|
|
17
17
|
type ToolOutput,
|
|
18
18
|
} from './shared.js';
|
|
19
|
+
import {
|
|
20
|
+
buildChannelBridgeConnectedEvent,
|
|
21
|
+
buildChannelBridgeDisconnectedEvent,
|
|
22
|
+
buildChannelMessageReceivedEvent,
|
|
23
|
+
buildChannelMessageSentEvent,
|
|
24
|
+
emitSidekickEvent,
|
|
25
|
+
serializeLocalChannelMessage,
|
|
26
|
+
} from '../sidekick-events.js';
|
|
19
27
|
|
|
20
28
|
// ---------------------------------------------------------------------------
|
|
21
29
|
// Constants
|
|
@@ -115,6 +123,8 @@ async function channelConfigureTool(
|
|
|
115
123
|
}
|
|
116
124
|
});
|
|
117
125
|
|
|
126
|
+
await emitSidekickEvent(buildChannelBridgeConnectedEvent(resolvedChannel));
|
|
127
|
+
|
|
118
128
|
return success({ channel: resolvedChannel as unknown as Record<string, unknown> });
|
|
119
129
|
}
|
|
120
130
|
|
|
@@ -205,6 +215,13 @@ async function channelDeleteTool(input: unknown, context?: ToolContextLike): Pro
|
|
|
205
215
|
);
|
|
206
216
|
});
|
|
207
217
|
|
|
218
|
+
await emitSidekickEvent(
|
|
219
|
+
buildChannelBridgeDisconnectedEvent({
|
|
220
|
+
bridge_id: id,
|
|
221
|
+
deleted_message_count: deletedMessages.length,
|
|
222
|
+
}),
|
|
223
|
+
);
|
|
224
|
+
|
|
208
225
|
return success({
|
|
209
226
|
deleted_id: id,
|
|
210
227
|
deleted_message_count: deletedMessages.length,
|
|
@@ -283,6 +300,17 @@ async function channelSendViaTransport(
|
|
|
283
300
|
};
|
|
284
301
|
}
|
|
285
302
|
|
|
303
|
+
await emitSidekickEvent(
|
|
304
|
+
buildChannelMessageSentEvent({
|
|
305
|
+
provider,
|
|
306
|
+
channel,
|
|
307
|
+
content,
|
|
308
|
+
...(transportResult.externalMessageId !== undefined
|
|
309
|
+
? { external_message_id: transportResult.externalMessageId }
|
|
310
|
+
: {}),
|
|
311
|
+
}),
|
|
312
|
+
);
|
|
313
|
+
|
|
286
314
|
return success(outputData);
|
|
287
315
|
}
|
|
288
316
|
|
|
@@ -361,6 +389,10 @@ async function channelSendTool(input: unknown, context?: ToolContextLike): Promi
|
|
|
361
389
|
);
|
|
362
390
|
});
|
|
363
391
|
|
|
392
|
+
await emitSidekickEvent(
|
|
393
|
+
buildChannelMessageSentEvent(serializeLocalChannelMessage(resolvedMessage, channelName)),
|
|
394
|
+
);
|
|
395
|
+
|
|
364
396
|
return success({ message: resolvedMessage as unknown as Record<string, unknown> });
|
|
365
397
|
}
|
|
366
398
|
|
|
@@ -430,6 +462,14 @@ async function channelReceiveViaTransport(
|
|
|
430
462
|
};
|
|
431
463
|
}
|
|
432
464
|
|
|
465
|
+
await emitSidekickEvent(
|
|
466
|
+
buildChannelMessageReceivedEvent({
|
|
467
|
+
provider,
|
|
468
|
+
channel,
|
|
469
|
+
count: transportResult.records?.length ?? 0,
|
|
470
|
+
}),
|
|
471
|
+
);
|
|
472
|
+
|
|
433
473
|
return success(outputData);
|
|
434
474
|
}
|
|
435
475
|
|
|
@@ -468,6 +508,13 @@ async function channelReceiveTool(input: unknown, _context?: ToolContextLike): P
|
|
|
468
508
|
|
|
469
509
|
const items = limit && limit > 0 ? sorted.slice(-limit) : sorted;
|
|
470
510
|
|
|
511
|
+
await emitSidekickEvent(
|
|
512
|
+
buildChannelMessageReceivedEvent({
|
|
513
|
+
channel: channelName ?? DEFAULT_CHANNEL_NAME,
|
|
514
|
+
count: items.length,
|
|
515
|
+
}),
|
|
516
|
+
);
|
|
517
|
+
|
|
471
518
|
return success({
|
|
472
519
|
items: items as unknown as Record<string, unknown>,
|
|
473
520
|
count: items.length,
|
|
@@ -18,6 +18,12 @@ import {
|
|
|
18
18
|
type ToolContextLike,
|
|
19
19
|
type ToolOutput,
|
|
20
20
|
} from './shared.js';
|
|
21
|
+
import {
|
|
22
|
+
buildMemoryForgottenEvent,
|
|
23
|
+
buildMemoryRecalledEvent,
|
|
24
|
+
buildMemoryStoredEvent,
|
|
25
|
+
emitSidekickEvent,
|
|
26
|
+
} from '../sidekick-events.js';
|
|
21
27
|
|
|
22
28
|
// ---------------------------------------------------------------------------
|
|
23
29
|
// Constants
|
|
@@ -87,6 +93,8 @@ async function memoryStoreTool(input: unknown, context?: ToolContextLike): Promi
|
|
|
87
93
|
);
|
|
88
94
|
});
|
|
89
95
|
|
|
96
|
+
await emitSidekickEvent(buildMemoryStoredEvent(memory));
|
|
97
|
+
|
|
90
98
|
return success({ memory: memory as unknown as Record<string, unknown> });
|
|
91
99
|
}
|
|
92
100
|
|
|
@@ -121,6 +129,13 @@ async function memoryRecallTool(input: unknown, _context?: ToolContextLike): Pro
|
|
|
121
129
|
|
|
122
130
|
const items = limit && limit > 0 ? sorted.slice(0, limit) : sorted;
|
|
123
131
|
|
|
132
|
+
await emitSidekickEvent(
|
|
133
|
+
buildMemoryRecalledEvent({
|
|
134
|
+
query,
|
|
135
|
+
memories: items,
|
|
136
|
+
}),
|
|
137
|
+
);
|
|
138
|
+
|
|
124
139
|
return success({
|
|
125
140
|
items: items as unknown as Record<string, unknown>,
|
|
126
141
|
count: items.length,
|
|
@@ -247,6 +262,8 @@ async function memoryForgetTool(input: unknown, context?: ToolContextLike): Prom
|
|
|
247
262
|
);
|
|
248
263
|
});
|
|
249
264
|
|
|
265
|
+
await emitSidekickEvent(buildMemoryForgottenEvent(id));
|
|
266
|
+
|
|
250
267
|
return success({ deleted_id: id });
|
|
251
268
|
}
|
|
252
269
|
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// Copyright (c) 2026 Hellmai Ltd
|
|
2
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* WU-2738 (INIT-060, ADR-013 §6 governance): `sidekick:routine:commit_plan`
|
|
6
|
+
* is the only path from a planned routine to a committed one. It is
|
|
7
|
+
* registered as a runtime-callable tool (not a top-level surface) so the
|
|
8
|
+
* kernel's governed dispatch emits `agent-runtime:tool_called` inside a
|
|
9
|
+
* turn, and the commit itself emits `sidekick:routine_committed` carrying
|
|
10
|
+
* a content-hash of the commit envelope. No fast-path bypasses this tool.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
buildRoutineCommitEnvelope,
|
|
15
|
+
hashRoutineCommitEnvelope,
|
|
16
|
+
type RoutineCommitAttestation,
|
|
17
|
+
} from '../src/routines/commit.js';
|
|
18
|
+
import { buildRoutineCommittedEvent, emitSidekickEvent } from '../sidekick-events.js';
|
|
19
|
+
import { getStoragePort } from './storage.js';
|
|
20
|
+
import {
|
|
21
|
+
asNonEmptyString,
|
|
22
|
+
buildAuditEvent,
|
|
23
|
+
failure,
|
|
24
|
+
nowIso,
|
|
25
|
+
success,
|
|
26
|
+
toRecord,
|
|
27
|
+
type ToolContextLike,
|
|
28
|
+
type ToolOutput,
|
|
29
|
+
} from './shared.js';
|
|
30
|
+
|
|
31
|
+
const ROUTINE_COMMIT_PLAN_TOOL_NAME = 'sidekick:routine:commit_plan';
|
|
32
|
+
|
|
33
|
+
function readAttestation(value: unknown): RoutineCommitAttestation | null {
|
|
34
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
const record = value as Record<string, unknown>;
|
|
38
|
+
const actor = asNonEmptyString(record.actor);
|
|
39
|
+
const reason = asNonEmptyString(record.reason);
|
|
40
|
+
if (!actor || !reason) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
...record,
|
|
45
|
+
actor,
|
|
46
|
+
reason,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export default async function routineCommitPlanTool(
|
|
51
|
+
input: unknown,
|
|
52
|
+
context?: ToolContextLike,
|
|
53
|
+
): Promise<ToolOutput> {
|
|
54
|
+
const parsed = toRecord(input);
|
|
55
|
+
const planId = asNonEmptyString(parsed.plan_id);
|
|
56
|
+
const attestation = readAttestation(parsed.attestation);
|
|
57
|
+
|
|
58
|
+
if (!planId) {
|
|
59
|
+
return failure('INVALID_INPUT', 'plan_id is required.');
|
|
60
|
+
}
|
|
61
|
+
if (!attestation) {
|
|
62
|
+
return failure(
|
|
63
|
+
'INVALID_INPUT',
|
|
64
|
+
'attestation must be an object with non-empty actor and reason fields.',
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const storage = getStoragePort();
|
|
69
|
+
const routines = await storage.readStore('routines');
|
|
70
|
+
const routine = routines.find((entry) => entry.id === planId);
|
|
71
|
+
if (!routine) {
|
|
72
|
+
return failure('NOT_FOUND', `routine ${planId} was not found.`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const committedAt = nowIso();
|
|
76
|
+
const envelope = buildRoutineCommitEnvelope({ routine, attestation, committedAt });
|
|
77
|
+
const envelopeContentHash = hashRoutineCommitEnvelope(envelope);
|
|
78
|
+
|
|
79
|
+
await storage.appendAudit(
|
|
80
|
+
buildAuditEvent({
|
|
81
|
+
tool: ROUTINE_COMMIT_PLAN_TOOL_NAME,
|
|
82
|
+
op: 'execute',
|
|
83
|
+
context,
|
|
84
|
+
ids: [routine.id],
|
|
85
|
+
details: {
|
|
86
|
+
plan_id: routine.id,
|
|
87
|
+
envelope_content_hash: envelopeContentHash,
|
|
88
|
+
},
|
|
89
|
+
}),
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
await emitSidekickEvent(
|
|
93
|
+
buildRoutineCommittedEvent(routine, { envelope_content_hash: envelopeContentHash }),
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
return success({
|
|
97
|
+
plan_id: routine.id,
|
|
98
|
+
routine_id: routine.id,
|
|
99
|
+
envelope_content_hash: envelopeContentHash,
|
|
100
|
+
committed_at: committedAt,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
@@ -15,6 +15,13 @@ import {
|
|
|
15
15
|
type ToolContextLike,
|
|
16
16
|
type ToolOutput,
|
|
17
17
|
} from './shared.js';
|
|
18
|
+
import {
|
|
19
|
+
buildRoutineCommittedEvent,
|
|
20
|
+
buildRoutineExecutedEvent,
|
|
21
|
+
buildRoutinePlannedEvent,
|
|
22
|
+
buildRoutineStepFailedEvent,
|
|
23
|
+
emitSidekickEvent,
|
|
24
|
+
} from '../sidekick-events.js';
|
|
18
25
|
|
|
19
26
|
// ---------------------------------------------------------------------------
|
|
20
27
|
// Constants
|
|
@@ -36,15 +43,17 @@ const ROUTINE_ID_REQUIRED_MESSAGE = 'id is required.';
|
|
|
36
43
|
interface NormalizeStepsResult {
|
|
37
44
|
steps: RoutineStepRecord[];
|
|
38
45
|
warnings: string[];
|
|
46
|
+
failed_steps: Array<{ step_index: number; reason: string }>;
|
|
39
47
|
}
|
|
40
48
|
|
|
41
49
|
function normalizeSteps(value: unknown): NormalizeStepsResult {
|
|
42
50
|
if (!Array.isArray(value)) {
|
|
43
|
-
return { steps: [], warnings: [] };
|
|
51
|
+
return { steps: [], warnings: [], failed_steps: [] };
|
|
44
52
|
}
|
|
45
53
|
|
|
46
54
|
const steps: RoutineStepRecord[] = [];
|
|
47
55
|
const warnings: string[] = [];
|
|
56
|
+
const failedSteps: Array<{ step_index: number; reason: string }> = [];
|
|
48
57
|
|
|
49
58
|
for (let i = 0; i < value.length; i++) {
|
|
50
59
|
const candidate = value[i];
|
|
@@ -55,20 +64,26 @@ function normalizeSteps(value: unknown): NormalizeStepsResult {
|
|
|
55
64
|
if (tool) {
|
|
56
65
|
steps.push({ tool, input: {} });
|
|
57
66
|
} else {
|
|
58
|
-
|
|
67
|
+
const reason = `steps[${i}] invalid: expected non-empty string or object with "tool".`;
|
|
68
|
+
warnings.push(reason);
|
|
69
|
+
failedSteps.push({ step_index: i, reason });
|
|
59
70
|
}
|
|
60
71
|
continue;
|
|
61
72
|
}
|
|
62
73
|
|
|
63
74
|
if (!candidate || typeof candidate !== 'object' || Array.isArray(candidate)) {
|
|
64
|
-
|
|
75
|
+
const reason = `steps[${i}] invalid: expected object with "tool" or a tool name string.`;
|
|
76
|
+
warnings.push(reason);
|
|
77
|
+
failedSteps.push({ step_index: i, reason });
|
|
65
78
|
continue;
|
|
66
79
|
}
|
|
67
80
|
|
|
68
81
|
const record = candidate as Record<string, unknown>;
|
|
69
82
|
const tool = asNonEmptyString(record.tool);
|
|
70
83
|
if (!tool) {
|
|
71
|
-
|
|
84
|
+
const reason = `steps[${i}] invalid: missing or empty "tool" property.`;
|
|
85
|
+
warnings.push(reason);
|
|
86
|
+
failedSteps.push({ step_index: i, reason });
|
|
72
87
|
continue;
|
|
73
88
|
}
|
|
74
89
|
|
|
@@ -80,7 +95,7 @@ function normalizeSteps(value: unknown): NormalizeStepsResult {
|
|
|
80
95
|
steps.push({ tool, input });
|
|
81
96
|
}
|
|
82
97
|
|
|
83
|
-
return { steps, warnings };
|
|
98
|
+
return { steps, warnings, failed_steps: failedSteps };
|
|
84
99
|
}
|
|
85
100
|
|
|
86
101
|
function asOptionalBoolean(value: unknown): boolean | null {
|
|
@@ -90,6 +105,22 @@ function asOptionalBoolean(value: unknown): boolean | null {
|
|
|
90
105
|
return null;
|
|
91
106
|
}
|
|
92
107
|
|
|
108
|
+
async function emitRoutineStepFailures(
|
|
109
|
+
failedSteps: Array<{ step_index: number; reason: string }>,
|
|
110
|
+
metadata: { routine_id?: string; routine_name?: string },
|
|
111
|
+
): Promise<void> {
|
|
112
|
+
for (const failedStep of failedSteps) {
|
|
113
|
+
await emitSidekickEvent(
|
|
114
|
+
buildRoutineStepFailedEvent({
|
|
115
|
+
step_index: failedStep.step_index,
|
|
116
|
+
reason: failedStep.reason,
|
|
117
|
+
...(metadata.routine_id ? { routine_id: metadata.routine_id } : {}),
|
|
118
|
+
...(metadata.routine_name ? { routine_name: metadata.routine_name } : {}),
|
|
119
|
+
}),
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
93
124
|
// ---------------------------------------------------------------------------
|
|
94
125
|
// routine:create
|
|
95
126
|
// ---------------------------------------------------------------------------
|
|
@@ -97,12 +128,15 @@ function asOptionalBoolean(value: unknown): boolean | null {
|
|
|
97
128
|
async function routineCreateTool(input: unknown, context?: ToolContextLike): Promise<ToolOutput> {
|
|
98
129
|
const parsed = toRecord(input);
|
|
99
130
|
const name = asNonEmptyString(parsed.name);
|
|
100
|
-
const { steps, warnings } = normalizeSteps(parsed.steps);
|
|
131
|
+
const { steps, warnings, failed_steps } = normalizeSteps(parsed.steps);
|
|
101
132
|
|
|
102
133
|
if (!name) {
|
|
103
134
|
return failure('INVALID_INPUT', 'name is required.');
|
|
104
135
|
}
|
|
105
136
|
if (steps.length === 0) {
|
|
137
|
+
if (!isDryRun(parsed)) {
|
|
138
|
+
await emitRoutineStepFailures(failed_steps, { routine_name: name ?? undefined });
|
|
139
|
+
}
|
|
106
140
|
const detail =
|
|
107
141
|
warnings.length > 0
|
|
108
142
|
? `steps must include at least one tool step. Issues: ${warnings.join('; ')}`
|
|
@@ -144,6 +178,12 @@ async function routineCreateTool(input: unknown, context?: ToolContextLike): Pro
|
|
|
144
178
|
);
|
|
145
179
|
});
|
|
146
180
|
|
|
181
|
+
await emitRoutineStepFailures(failed_steps, {
|
|
182
|
+
routine_id: routine.id,
|
|
183
|
+
routine_name: routine.name,
|
|
184
|
+
});
|
|
185
|
+
await emitSidekickEvent(buildRoutinePlannedEvent(routine));
|
|
186
|
+
|
|
147
187
|
return success({
|
|
148
188
|
routine: routine as unknown as Record<string, unknown>,
|
|
149
189
|
...(warnings.length > 0 ? { warnings } : {}),
|
|
@@ -180,6 +220,7 @@ async function routineListTool(input: unknown, _context?: ToolContextLike): Prom
|
|
|
180
220
|
async function routineUpdateTool(input: unknown, context?: ToolContextLike): Promise<ToolOutput> {
|
|
181
221
|
const parsed = toRecord(input);
|
|
182
222
|
const id = asNonEmptyString(parsed.id);
|
|
223
|
+
let patchFailedSteps: Array<{ step_index: number; reason: string }> = [];
|
|
183
224
|
|
|
184
225
|
if (!id) {
|
|
185
226
|
return failure('INVALID_INPUT', ROUTINE_ID_REQUIRED_MESSAGE);
|
|
@@ -201,8 +242,12 @@ async function routineUpdateTool(input: unknown, context?: ToolContextLike): Pro
|
|
|
201
242
|
patch.enabled = enabled;
|
|
202
243
|
}
|
|
203
244
|
if (parsed.steps !== undefined) {
|
|
204
|
-
const { steps, warnings } = normalizeSteps(parsed.steps);
|
|
245
|
+
const { steps, warnings, failed_steps } = normalizeSteps(parsed.steps);
|
|
246
|
+
patchFailedSteps = failed_steps;
|
|
205
247
|
if (steps.length === 0) {
|
|
248
|
+
if (!isDryRun(parsed)) {
|
|
249
|
+
await emitRoutineStepFailures(failed_steps, { routine_id: id });
|
|
250
|
+
}
|
|
206
251
|
const detail =
|
|
207
252
|
warnings.length > 0
|
|
208
253
|
? `steps must include at least one tool step. Issues: ${warnings.join('; ')}`
|
|
@@ -260,6 +305,13 @@ async function routineUpdateTool(input: unknown, context?: ToolContextLike): Pro
|
|
|
260
305
|
|
|
261
306
|
const updated = await storage.readStore('routines');
|
|
262
307
|
const updatedRoutine = updated.find((entry) => entry.id === id);
|
|
308
|
+
await emitRoutineStepFailures(patchFailedSteps, {
|
|
309
|
+
routine_id: id,
|
|
310
|
+
routine_name: updatedRoutine?.name,
|
|
311
|
+
});
|
|
312
|
+
if (updatedRoutine) {
|
|
313
|
+
await emitSidekickEvent(buildRoutineCommittedEvent(updatedRoutine));
|
|
314
|
+
}
|
|
263
315
|
return success({ routine: updatedRoutine as unknown as Record<string, unknown> });
|
|
264
316
|
}
|
|
265
317
|
|
|
@@ -339,6 +391,14 @@ async function routineRunTool(input: unknown, context?: ToolContextLike): Promis
|
|
|
339
391
|
}),
|
|
340
392
|
);
|
|
341
393
|
|
|
394
|
+
await emitSidekickEvent(
|
|
395
|
+
buildRoutineExecutedEvent({
|
|
396
|
+
routine_id: routine.id,
|
|
397
|
+
name: routine.name,
|
|
398
|
+
step_count: routine.steps.length,
|
|
399
|
+
}),
|
|
400
|
+
);
|
|
401
|
+
|
|
342
402
|
return success({
|
|
343
403
|
routine_id: routine.id,
|
|
344
404
|
name: routine.name,
|
|
@@ -4,10 +4,14 @@
|
|
|
4
4
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
5
5
|
import type { ChannelTransport } from './channel-transports.js';
|
|
6
6
|
import type { StoragePort } from './storage.js';
|
|
7
|
+
import type { SidekickEvent } from '../sidekick-events.js';
|
|
7
8
|
|
|
8
9
|
export interface SidekickRuntimeContext {
|
|
9
10
|
storagePort: StoragePort;
|
|
10
11
|
channelTransports: Map<string, ChannelTransport>;
|
|
12
|
+
workspaceRoot?: string;
|
|
13
|
+
workspaceConfig?: unknown;
|
|
14
|
+
eventSink?: (event: SidekickEvent) => void | Promise<void>;
|
|
11
15
|
}
|
|
12
16
|
|
|
13
17
|
const runtimeContext = new AsyncLocalStorage<SidekickRuntimeContext>();
|
|
@@ -316,6 +316,9 @@ export async function runWithStoragePort<T>(port: StoragePort, fn: () => Promise
|
|
|
316
316
|
{
|
|
317
317
|
storagePort: port,
|
|
318
318
|
channelTransports: existingContext?.channelTransports ?? new Map(),
|
|
319
|
+
workspaceRoot: existingContext?.workspaceRoot,
|
|
320
|
+
workspaceConfig: existingContext?.workspaceConfig,
|
|
321
|
+
eventSink: existingContext?.eventSink,
|
|
319
322
|
},
|
|
320
323
|
fn,
|
|
321
324
|
);
|
|
@@ -12,6 +12,11 @@ import {
|
|
|
12
12
|
type ToolContextLike,
|
|
13
13
|
type ToolOutput,
|
|
14
14
|
} from './shared.js';
|
|
15
|
+
import {
|
|
16
|
+
buildStateRehydratedEvent,
|
|
17
|
+
emitSidekickEvent,
|
|
18
|
+
snapshotSidekickState,
|
|
19
|
+
} from '../sidekick-events.js';
|
|
15
20
|
|
|
16
21
|
// ---------------------------------------------------------------------------
|
|
17
22
|
// Constants
|
|
@@ -46,6 +51,8 @@ async function initTool(context?: ToolContextLike): Promise<ToolOutput> {
|
|
|
46
51
|
}),
|
|
47
52
|
);
|
|
48
53
|
|
|
54
|
+
await emitSidekickEvent(buildStateRehydratedEvent(await snapshotSidekickState()));
|
|
55
|
+
|
|
49
56
|
return success({
|
|
50
57
|
initialized: true,
|
|
51
58
|
root_dir: storage.getRootDir(),
|