@lostgradient/weft 0.18.0 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. package/README.md +3 -1
  2. package/dist/cli/conformance.js +14 -18
  3. package/dist/cli/generated/operation-catalog.snapshot.json +1597 -21
  4. package/dist/cli/generated/operation-client.generated.d.ts +37 -6
  5. package/dist/cli/generated/operation-client.generated.js +6 -0
  6. package/dist/core/context/child-workflow-pipe.d.ts +1 -1
  7. package/dist/core/context/child-workflow-pipe.js +1 -1
  8. package/dist/core/engine/construction.d.ts +2 -0
  9. package/dist/core/engine/construction.js +3 -0
  10. package/dist/core/engine/index.js +1 -0
  11. package/dist/core/engine/listing.d.ts +1 -0
  12. package/dist/core/engine/listing.js +6 -4
  13. package/dist/core/weft-error.d.ts +1 -1
  14. package/dist/core/weft-error.js +2 -1
  15. package/dist/core/worker-execution-dispatcher.d.ts +8 -0
  16. package/dist/core/worker-execution-dispatcher.js +4 -0
  17. package/dist/core/worker-execution-strategy-options.d.ts +16 -0
  18. package/dist/core/worker-execution-strategy.js +71 -89
  19. package/dist/core/worker-fault-handling.d.ts +45 -0
  20. package/dist/core/worker-fault-handling.js +88 -0
  21. package/dist/core/worker-protocol.d.ts +18 -1
  22. package/dist/core/worker-protocol.js +1 -1
  23. package/dist/core/worker-realm-readiness.d.ts +78 -0
  24. package/dist/core/worker-realm-readiness.js +127 -0
  25. package/dist/index.d.ts +3 -1
  26. package/dist/index.js +28 -1
  27. package/dist/json-schema.js +1 -1
  28. package/dist/runtime/portable.d.ts +14 -0
  29. package/dist/runtime/portable.js +10 -0
  30. package/dist/server/index.d.ts +71 -8
  31. package/dist/server/index.js +7 -3
  32. package/dist/server/operations/get-task-detail-schema.d.ts +341 -0
  33. package/dist/server/operations/get-task-detail-schema.js +118 -0
  34. package/dist/server/operations/get-task-detail.d.ts +346 -0
  35. package/dist/server/operations/get-task-detail.js +215 -0
  36. package/dist/server/operations/get-task-diagnostics.d.ts +43 -18
  37. package/dist/server/operations/get-task-diagnostics.js +87 -126
  38. package/dist/server/operations/get-worker-diagnostics.d.ts +177 -0
  39. package/dist/server/operations/get-worker-diagnostics.js +117 -0
  40. package/dist/server/operations/list-worker-registration-rejections.d.ts +79 -0
  41. package/dist/server/operations/list-worker-registration-rejections.js +60 -0
  42. package/dist/server/operations/list-workers.js +1 -3
  43. package/dist/server/operations/static-registrations.js +8 -20
  44. package/dist/server/rest-bindings.js +31 -2
  45. package/dist/server/runtime/authentication-bridge.js +6 -14
  46. package/dist/server/runtime/context.d.ts +23 -0
  47. package/dist/server/runtime/task-dispatch.d.ts +7 -1
  48. package/dist/server/runtime/task-dispatch.js +126 -55
  49. package/dist/server/runtime/task-ledger-completion.d.ts +68 -0
  50. package/dist/server/runtime/task-ledger-completion.js +72 -0
  51. package/dist/server/runtime/task-ledger-recovery.d.ts +55 -0
  52. package/dist/server/runtime/task-ledger-recovery.js +73 -0
  53. package/dist/server/runtime/task-ledger-runtime.d.ts +64 -0
  54. package/dist/server/runtime/task-ledger-runtime.js +34 -0
  55. package/dist/server/runtime/task-metrics.d.ts +6 -4
  56. package/dist/server/runtime/task-polling.d.ts +14 -12
  57. package/dist/server/runtime/task-polling.js +106 -82
  58. package/dist/server/runtime/task-reconciliation.d.ts +35 -8
  59. package/dist/server/runtime/task-reconciliation.js +75 -74
  60. package/dist/server/runtime/task-result-resolution.d.ts +2 -10
  61. package/dist/server/runtime/task-result-resolution.js +0 -67
  62. package/dist/server/runtime/task-result-view.d.ts +82 -0
  63. package/dist/server/runtime/task-result-view.js +48 -0
  64. package/dist/server/runtime/websocket-worker-messaging.d.ts +16 -0
  65. package/dist/server/runtime/websocket-worker-messaging.js +32 -0
  66. package/dist/server/runtime/websocket-worker-registration.d.ts +41 -0
  67. package/dist/server/runtime/websocket-worker-registration.js +134 -0
  68. package/dist/server/runtime/websocket-worker.d.ts +0 -1
  69. package/dist/server/runtime/websocket-worker.js +64 -116
  70. package/dist/server/serve-internals.d.ts +9 -6
  71. package/dist/server/serve-internals.js +14 -40
  72. package/dist/server/task-ledger-codec.d.ts +40 -0
  73. package/dist/server/task-ledger-codec.js +195 -0
  74. package/dist/server/task-ledger-keys.d.ts +13 -0
  75. package/dist/server/task-ledger-keys.js +5 -0
  76. package/dist/server/task-ledger-limits.d.ts +34 -0
  77. package/dist/server/task-ledger-limits.js +5 -0
  78. package/dist/server/task-ledger-transition-helpers.d.ts +38 -0
  79. package/dist/server/task-ledger-transition-helpers.js +42 -0
  80. package/dist/server/task-ledger-transitions-cancellation.d.ts +31 -0
  81. package/dist/server/task-ledger-transitions-cancellation.js +73 -0
  82. package/dist/server/task-ledger-transitions.d.ts +129 -0
  83. package/dist/server/task-ledger-transitions.js +162 -0
  84. package/dist/server/task-ledger-types.d.ts +220 -0
  85. package/dist/server/task-ledger-types.js +1 -0
  86. package/dist/server/task-ledger.d.ts +17 -0
  87. package/dist/server/task-ledger.js +24 -0
  88. package/dist/server/task-state.d.ts +25 -152
  89. package/dist/server/task-state.js +0 -188
  90. package/dist/server/worker-admission-policy.d.ts +70 -0
  91. package/dist/server/worker-admission-policy.js +0 -0
  92. package/dist/storage/compressed-storage.js +1 -1
  93. package/dist/storage/index.d.ts +0 -2
  94. package/dist/storage/interface.d.ts +0 -2
  95. package/dist/storage/interface.js +1 -1
  96. package/dist/storage/lmdb.js +1 -1
  97. package/dist/storage/memory.js +1 -1
  98. package/dist/storage/neon.js +1 -1
  99. package/dist/storage/postgres.js +1 -1
  100. package/dist/storage/resolve.js +1 -1
  101. package/dist/storage/scoped-storage.js +1 -1
  102. package/dist/storage/testing.js +1 -1
  103. package/dist/storage/turso.js +1 -1
  104. package/dist/storage/web-extension.js +2 -2
  105. package/dist/version.d.ts +1 -1
  106. package/dist/version.js +1 -1
  107. package/dist/web-extension.js +1 -1
  108. package/dist/worker/index.d.ts +2 -0
  109. package/dist/worker/index.js +30 -4
  110. package/dist/worker/manifest/canonical-json.d.ts +20 -0
  111. package/dist/worker/manifest/canonical-json.js +13 -0
  112. package/dist/worker/manifest/capabilities.d.ts +24 -0
  113. package/dist/worker/manifest/capabilities.js +74 -0
  114. package/dist/worker/manifest/content-digest.d.ts +20 -0
  115. package/dist/worker/manifest/content-digest.js +13 -0
  116. package/dist/worker/manifest/declared-shape-digest.d.ts +14 -0
  117. package/dist/worker/manifest/declared-shape-digest.js +4 -0
  118. package/dist/worker/manifest/digest.d.ts +82 -0
  119. package/dist/worker/manifest/digest.js +9 -0
  120. package/dist/worker/manifest/execution-identity.d.ts +92 -0
  121. package/dist/worker/manifest/execution-identity.js +36 -0
  122. package/dist/worker/manifest/failure.d.ts +59 -0
  123. package/dist/worker/manifest/failure.js +4 -0
  124. package/dist/worker/manifest/index.d.ts +22 -0
  125. package/dist/worker/manifest/index.js +23 -0
  126. package/dist/worker/manifest/internal-realm.d.ts +50 -0
  127. package/dist/worker/manifest/internal-realm.js +39 -0
  128. package/dist/worker/manifest/is-record.d.ts +12 -0
  129. package/dist/worker/manifest/is-record.js +6 -0
  130. package/dist/worker/manifest/json-scan.d.ts +26 -0
  131. package/dist/worker/manifest/json-scan.js +88 -0
  132. package/dist/worker/manifest/limits.d.ts +138 -0
  133. package/dist/worker/manifest/limits.js +1 -0
  134. package/dist/worker/manifest/normalize.d.ts +63 -0
  135. package/dist/worker/manifest/normalize.js +73 -0
  136. package/dist/worker/manifest/parse-json.d.ts +24 -0
  137. package/dist/worker/manifest/parse-json.js +15 -0
  138. package/dist/worker/manifest/parse.d.ts +86 -0
  139. package/dist/worker/manifest/parse.js +188 -0
  140. package/dist/worker/manifest/registry-contract-builder.d.ts +120 -0
  141. package/dist/worker/manifest/registry-contract-builder.js +65 -0
  142. package/dist/worker/manifest/types.d.ts +263 -0
  143. package/dist/worker/manifest/types.js +1 -0
  144. package/dist/worker/manifest/utf8.d.ts +13 -0
  145. package/dist/worker/manifest/utf8.js +7 -0
  146. package/dist/worker/options.d.ts +55 -8
  147. package/dist/worker/options.js +65 -9
  148. package/dist/worker/protocol-internals.d.ts +1 -2
  149. package/dist/worker/protocol-internals.js +0 -5
  150. package/dist/worker/protocol-messages.d.ts +33 -17
  151. package/dist/worker/protocol-schemas.d.ts +36 -74
  152. package/dist/worker/protocol-schemas.js +25 -18
  153. package/dist/worker/protocol-version.d.ts +18 -14
  154. package/dist/worker/protocol-version.js +1 -1
  155. package/dist/worker/protocol.js +1 -1
  156. package/dist/worker/registry/deployment-consistency.d.ts +78 -0
  157. package/dist/worker/registry/deployment-consistency.js +27 -0
  158. package/dist/worker/registry/drain.d.ts +14 -0
  159. package/dist/worker/registry/drain.js +24 -0
  160. package/dist/worker/registry/rejections.d.ts +31 -0
  161. package/dist/worker/registry/rejections.js +10 -0
  162. package/dist/worker/registry/summary.d.ts +0 -3
  163. package/dist/worker/registry/summary.js +2 -5
  164. package/dist/worker/registry/types.d.ts +10 -3
  165. package/dist/worker/registry.d.ts +47 -2
  166. package/dist/worker/registry.js +45 -32
  167. package/dist/workers/workflow-worker-entry.d.ts +12 -7
  168. package/dist/workers/workflow-worker-entry.js +9 -2
  169. package/package.json +3 -3
  170. package/dist/server/task-resolved-record.d.ts +0 -12
  171. package/dist/server/task-resolved-record.js +0 -35
@@ -0,0 +1,188 @@
1
+ import { validateWorkflowOrActivityName } from "../../core/types/name-grammar.js";
2
+ import { parseManifestCapabilities } from "./capabilities.js";
3
+ import { manifestFailure } from "./failure.js";
4
+ import { isRecord } from "./is-record.js";
5
+ import {
6
+ MAX_MANIFEST_ACTIVITY_COUNT,
7
+ MAX_MANIFEST_IDENTIFIER_BYTES,
8
+ MAX_MANIFEST_WORKFLOW_COUNT,
9
+ MAX_NORMALIZED_MANIFEST_BYTES
10
+ } from "./limits.js";
11
+ import { canonicalWorkerManifestJson, normalizeWorkerManifest } from "./normalize.js";
12
+ import { WORKER_MANIFEST_VERSION } from "./types.js";
13
+ import { utf8ByteLength } from "./utf8.js";
14
+ function parseIdentifier(value, path) {
15
+ if (typeof value !== "string" || value.length === 0)
16
+ return manifestFailure("invalid_field", "must be a non-empty string", path);
17
+ const bytes = utf8ByteLength(value);
18
+ if (bytes > MAX_MANIFEST_IDENTIFIER_BYTES)
19
+ return manifestFailure("identifier_too_long", `is ${bytes} bytes, exceeding the maximum identifier size of ${MAX_MANIFEST_IDENTIFIER_BYTES}`, path);
20
+ return { ok: !0, value };
21
+ }
22
+ function checkKey(key, kind, path) {
23
+ if (key.length === 0)
24
+ return manifestFailure("invalid_field", "must not be an empty string", path);
25
+ const bytes = utf8ByteLength(key);
26
+ if (bytes > MAX_MANIFEST_IDENTIFIER_BYTES)
27
+ return manifestFailure("identifier_too_long", `is ${bytes} bytes, exceeding the maximum identifier size of ${MAX_MANIFEST_IDENTIFIER_BYTES}`, path);
28
+ try {
29
+ validateWorkflowOrActivityName(key, kind);
30
+ } catch (error) {
31
+ return manifestFailure("invalid_field", error instanceof Error ? error.message : "is not a wire-safe name", path);
32
+ }
33
+ return;
34
+ }
35
+ function parseProtocolVersion(value) {
36
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 1)
37
+ return manifestFailure("invalid_field", "must be a positive safe integer", "manifest.protocolVersion");
38
+ return { ok: !0, value };
39
+ }
40
+ function parseRuntime(value, path) {
41
+ if (!isRecord(value))
42
+ return manifestFailure("invalid_field", "must be a JSON object", path);
43
+ const name = parseIdentifier(value.name, `${path}.name`);
44
+ if (!name.ok)
45
+ return name;
46
+ const rawVersion = value.version;
47
+ if (typeof rawVersion !== "string")
48
+ return manifestFailure("invalid_field", "must be a string", `${path}.version`);
49
+ const versionBytes = utf8ByteLength(rawVersion);
50
+ if (versionBytes > MAX_MANIFEST_IDENTIFIER_BYTES)
51
+ return manifestFailure("identifier_too_long", `is ${versionBytes} bytes, exceeding the maximum identifier size of ${MAX_MANIFEST_IDENTIFIER_BYTES}`, `${path}.version`);
52
+ return { ok: !0, runtime: { name: name.value, version: rawVersion } };
53
+ }
54
+ function parseDeployment(value, path) {
55
+ if (!isRecord(value))
56
+ return manifestFailure("invalid_field", "must be a JSON object", path);
57
+ const name = parseIdentifier(value.name, `${path}.name`);
58
+ if (!name.ok)
59
+ return name;
60
+ const buildId = parseIdentifier(value.buildId, `${path}.buildId`);
61
+ if (!buildId.ok)
62
+ return buildId;
63
+ const artifactDigest = parseIdentifier(value.artifactDigest, `${path}.artifactDigest`);
64
+ if (!artifactDigest.ok)
65
+ return artifactDigest;
66
+ return {
67
+ ok: !0,
68
+ deployment: {
69
+ name: name.value,
70
+ buildId: buildId.value,
71
+ artifactDigest: artifactDigest.value
72
+ }
73
+ };
74
+ }
75
+ function parseActivity(value, path) {
76
+ if (!isRecord(value))
77
+ return manifestFailure("invalid_field", "must be a JSON object", path);
78
+ const contractHash = parseIdentifier(value.contractHash, `${path}.contractHash`);
79
+ if (!contractHash.ok)
80
+ return contractHash;
81
+ const implementationRevision = parseIdentifier(value.implementationRevision, `${path}.implementationRevision`);
82
+ if (!implementationRevision.ok)
83
+ return implementationRevision;
84
+ return {
85
+ ok: !0,
86
+ activity: {
87
+ contractHash: contractHash.value,
88
+ implementationRevision: implementationRevision.value
89
+ }
90
+ };
91
+ }
92
+ function parseActivities(value, path) {
93
+ if (!isRecord(value))
94
+ return manifestFailure("invalid_field", "must be a JSON object", path);
95
+ const names = Object.keys(value);
96
+ if (names.length > MAX_MANIFEST_ACTIVITY_COUNT)
97
+ return manifestFailure("too_many_activities", `declares ${names.length} activities, exceeding the maximum of ${MAX_MANIFEST_ACTIVITY_COUNT}`, path);
98
+ const activities = Object.create(null);
99
+ for (const name of names) {
100
+ const keyFailure = checkKey(name, "activity", `${path} key ${JSON.stringify(name)}`);
101
+ if (keyFailure !== void 0)
102
+ return keyFailure;
103
+ const activity = parseActivity(value[name], `${path}.${name}`);
104
+ if (!activity.ok)
105
+ return activity;
106
+ activities[name] = activity.activity;
107
+ }
108
+ return { ok: !0, activities };
109
+ }
110
+ function parseWorkflow(value, path) {
111
+ if (!isRecord(value))
112
+ return manifestFailure("invalid_field", "must be a JSON object", path);
113
+ const workflowVersion = parseIdentifier(value.workflowVersion, `${path}.workflowVersion`);
114
+ if (!workflowVersion.ok)
115
+ return workflowVersion;
116
+ const workflowRevision = parseIdentifier(value.workflowRevision, `${path}.workflowRevision`);
117
+ if (!workflowRevision.ok)
118
+ return workflowRevision;
119
+ const contractHash = parseIdentifier(value.contractHash, `${path}.contractHash`);
120
+ if (!contractHash.ok)
121
+ return contractHash;
122
+ const activities = parseActivities(value.activities, `${path}.activities`);
123
+ if (!activities.ok)
124
+ return activities;
125
+ return {
126
+ ok: !0,
127
+ workflow: {
128
+ workflowVersion: workflowVersion.value,
129
+ workflowRevision: workflowRevision.value,
130
+ contractHash: contractHash.value,
131
+ activities: activities.activities
132
+ }
133
+ };
134
+ }
135
+ function parseWorkflows(value, path) {
136
+ if (!isRecord(value))
137
+ return manifestFailure("invalid_field", "must be a JSON object", path);
138
+ const names = Object.keys(value);
139
+ if (names.length > MAX_MANIFEST_WORKFLOW_COUNT)
140
+ return manifestFailure("too_many_workflows", `declares ${names.length} workflows, exceeding the maximum of ${MAX_MANIFEST_WORKFLOW_COUNT}`, path);
141
+ const workflows = Object.create(null);
142
+ for (const name of names) {
143
+ const keyFailure = checkKey(name, "workflow", `${path} key ${JSON.stringify(name)}`);
144
+ if (keyFailure !== void 0)
145
+ return keyFailure;
146
+ const workflow = parseWorkflow(value[name], `${path}.${name}`);
147
+ if (!workflow.ok)
148
+ return workflow;
149
+ workflows[name] = workflow.workflow;
150
+ }
151
+ return { ok: !0, workflows };
152
+ }
153
+ export function parseWorkerManifest(value) {
154
+ if (!isRecord(value))
155
+ return manifestFailure("not_an_object", "manifest must be a JSON object");
156
+ if (value.manifestVersion !== WORKER_MANIFEST_VERSION)
157
+ return manifestFailure("unsupported_manifest_version", `manifest.manifestVersion must be ${String(WORKER_MANIFEST_VERSION)}`);
158
+ const protocolVersion = parseProtocolVersion(value.protocolVersion);
159
+ if (!protocolVersion.ok)
160
+ return protocolVersion;
161
+ const sdkVersion = parseIdentifier(value.sdkVersion, "manifest.sdkVersion");
162
+ if (!sdkVersion.ok)
163
+ return sdkVersion;
164
+ const runtime = parseRuntime(value.runtime, "manifest.runtime");
165
+ if (!runtime.ok)
166
+ return runtime;
167
+ const deployment = parseDeployment(value.deployment, "manifest.deployment");
168
+ if (!deployment.ok)
169
+ return deployment;
170
+ const workflows = parseWorkflows(value.workflows, "manifest.workflows");
171
+ if (!workflows.ok)
172
+ return workflows;
173
+ const capabilities = parseManifestCapabilities(value.capabilities, "manifest.capabilities");
174
+ if (!capabilities.ok)
175
+ return capabilities;
176
+ const manifest = normalizeWorkerManifest({
177
+ manifestVersion: WORKER_MANIFEST_VERSION,
178
+ protocolVersion: protocolVersion.value,
179
+ sdkVersion: sdkVersion.value,
180
+ runtime: runtime.runtime,
181
+ deployment: deployment.deployment,
182
+ workflows: workflows.workflows,
183
+ capabilities: capabilities.capabilities
184
+ }), canonicalJson = canonicalWorkerManifestJson(manifest), canonicalBytes = utf8ByteLength(canonicalJson);
185
+ if (canonicalBytes > MAX_NORMALIZED_MANIFEST_BYTES)
186
+ return manifestFailure("manifest_too_large", `normalizes to ${canonicalBytes} bytes, exceeding the maximum of ${MAX_NORMALIZED_MANIFEST_BYTES}`, "manifest");
187
+ return { ok: !0, manifest, canonicalJson };
188
+ }
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Real worker manifest production from an engine's canonical workflow
3
+ * registry (WFT-29).
4
+ *
5
+ * `RemoteWorkerOptions` and the internal Worker realm both fall back to
6
+ * `declared-shape:` placeholders when no real build tooling supplies a
7
+ * manifest — see {@link declaredShapeDigest}. This module is that build
8
+ * tooling: it derives `WorkerWorkflowContract`/`WorkerActivityContract`
9
+ * values from the *same* normalized representation `buildRegistrySnapshot`
10
+ * produces for `GET /v1/registry` and `weft codegen`, hashed with the
11
+ * collision-resistant {@link sha256Hex} rather than the cache-key-quality
12
+ * FNV-1a scheme those placeholders use.
13
+ *
14
+ * Intended use is a build script: construct an `Engine` with every workflow
15
+ * the artifact bundles registered (never started), call
16
+ * {@link buildWorkerManifestFromRegistry}, and pass the result as
17
+ * `RemoteWorkerOptions.manifest`.
18
+ *
19
+ * @module worker/manifest/registry-contract-builder
20
+ */
21
+ import type { Engine } from '../../core/engine.ts';
22
+ import { WeftError } from '../../core/weft-error.ts';
23
+ import type { WorkerDeploymentIdentity, WorkerManifest, WorkerRuntimeIdentity } from './types.ts';
24
+ /**
25
+ * Thrown when {@link buildWorkerManifestFromRegistry} is asked to advertise a
26
+ * workflow or activity name that the source `Engine` has not registered.
27
+ *
28
+ * This is a build-time tooling error, thrown before any manifest reaches the
29
+ * wire — analogous to the engine's own `RegistrySchemaConversionError`.
30
+ *
31
+ * @example
32
+ * ```ts
33
+ * import { WorkerManifestBuildError } from '@lostgradient/weft';
34
+ *
35
+ * try {
36
+ * throw new WorkerManifestBuildError('workflow "checkout" is not registered');
37
+ * } catch (error) {
38
+ * console.log(error instanceof WorkerManifestBuildError); // true
39
+ * }
40
+ * ```
41
+ */
42
+ export declare class WorkerManifestBuildError extends WeftError<'WorkerManifestBuildError'> {
43
+ constructor(message: string);
44
+ }
45
+ /**
46
+ * Options accepted by {@link buildWorkerManifestFromRegistry}.
47
+ *
48
+ * @example
49
+ * ```ts
50
+ * import type { WorkerManifestFromRegistryOptions } from '@lostgradient/weft';
51
+ *
52
+ * const options: WorkerManifestFromRegistryOptions = {
53
+ * workflows: { checkout: ['charge'] },
54
+ * deployment: { name: 'billing', buildId: '2026.08.18-3', artifactDigest: 'sha256:41d0e2' },
55
+ * runtime: { name: 'bun', version: '1.3.14' },
56
+ * };
57
+ * console.log(options.workflows['checkout']);
58
+ * ```
59
+ */
60
+ export interface WorkerManifestFromRegistryOptions {
61
+ /**
62
+ * Workflow types this worker instance can execute, each mapped to the
63
+ * activity names it invokes. Every key must name a workflow the source
64
+ * `Engine` has registered, and every activity name must be one the same
65
+ * `Engine` has registered — the engine's activity registry is a flat
66
+ * namespace, not partitioned per workflow, so this association is the one
67
+ * thing the registry genuinely cannot supply on its own.
68
+ */
69
+ workflows: Readonly<Record<string, readonly string[]>>;
70
+ /** Immutable deployment artifact identity. Required — never derived. */
71
+ deployment: WorkerDeploymentIdentity;
72
+ /**
73
+ * Runtime that will execute the deployed artifact. Required rather than
74
+ * defaulted: a build script's own process (often a CI runner) is not
75
+ * necessarily the runtime the artifact deploys to, so live detection here
76
+ * would silently assert the wrong identity.
77
+ */
78
+ runtime: WorkerRuntimeIdentity;
79
+ /** Weft worker SDK release the deployed artifact bundles. Defaults to this package's `VERSION`. */
80
+ sdkVersion?: string;
81
+ /** RemoteWorker wire protocol version. Defaults to `REMOTE_WORKER_PROTOCOL_VERSION`. */
82
+ protocolVersion?: number;
83
+ /** Bounded descriptive capability data. Defaults to `{}`. */
84
+ capabilities?: WorkerManifest['capabilities'];
85
+ }
86
+ /**
87
+ * Build a real worker manifest from an engine's canonical workflow and
88
+ * activity registrations.
89
+ *
90
+ * `implementationRevision` on every activity contract is set to
91
+ * `options.deployment.buildId`: a schema identifies the *contract*, not the
92
+ * code behind it, so there is no honest schema-derived source for "which
93
+ * implementation" — the build that produced this artifact is the closest
94
+ * available honest answer.
95
+ *
96
+ * Throws {@link WorkerManifestBuildError} if `options.workflows` names a
97
+ * workflow or activity the source `Engine` has not registered, and a
98
+ * (non-public) schema-conversion error if a registered schema cannot be
99
+ * converted to JSON Schema.
100
+ *
101
+ * @example
102
+ * ```ts
103
+ * import { buildWorkerManifestFromRegistry, Engine, workflow } from '@lostgradient/weft';
104
+ *
105
+ * const engine = new Engine();
106
+ * engine.register(
107
+ * workflow({ name: 'checkout', version: '2.1.0' }).execute(async function* () {}),
108
+ * );
109
+ *
110
+ * const manifest = await buildWorkerManifestFromRegistry(engine, {
111
+ * workflows: { checkout: [] },
112
+ * deployment: { name: 'billing', buildId: '2026.08.18-3', artifactDigest: 'sha256:41d0e2' },
113
+ * runtime: { name: 'bun', version: '1.3.14' },
114
+ * });
115
+ *
116
+ * console.log(manifest.workflows['checkout']?.workflowVersion); // '2.1.0'
117
+ * engine[Symbol.dispose]();
118
+ * ```
119
+ */
120
+ export declare function buildWorkerManifestFromRegistry(engine: Engine, options: WorkerManifestFromRegistryOptions): Promise<WorkerManifest>;
@@ -0,0 +1,65 @@
1
+ import {
2
+ buildRegistrySnapshot
3
+ } from "../../core/registry-snapshot.js";
4
+ import { WeftError } from "../../core/weft-error.js";
5
+ import { VERSION } from "../../version.js";
6
+ import { REMOTE_WORKER_PROTOCOL_VERSION } from "../protocol.js";
7
+ import { canonicalJsonStringify } from "./canonical-json.js";
8
+ import { sha256Hex } from "./content-digest.js";
9
+ import { WORKER_MANIFEST_VERSION } from "./types.js";
10
+
11
+ export class WorkerManifestBuildError extends WeftError {
12
+ constructor(message) {
13
+ super("WorkerManifestBuildError", message);
14
+ }
15
+ }
16
+ function findWorkflowEntry(snapshot, workflowType) {
17
+ const entry = snapshot.workflows[workflowType];
18
+ if (entry === void 0)
19
+ throw new WorkerManifestBuildError(`Cannot build a worker manifest: workflow type "${workflowType}" is not registered on the source Engine.`);
20
+ return entry;
21
+ }
22
+ function findActivityEntry(snapshot, workflowType, activityName) {
23
+ const entry = snapshot.activities[activityName];
24
+ if (entry === void 0)
25
+ throw new WorkerManifestBuildError(`Cannot build a worker manifest: activity "${activityName}" declared under workflow "${workflowType}" is not registered on the source Engine.`);
26
+ return entry;
27
+ }
28
+ function workflowContractPayload(entry) {
29
+ return {
30
+ inputSchema: entry.inputSchema,
31
+ outputSchema: entry.outputSchema,
32
+ signals: entry.signals,
33
+ updates: entry.updates,
34
+ queries: entry.queries
35
+ };
36
+ }
37
+ function activityContractPayload(entry) {
38
+ return { inputSchema: entry.inputSchema, outputSchema: entry.outputSchema };
39
+ }
40
+ async function buildActivityContract(snapshot, workflowType, activityName, implementationRevision) {
41
+ const entry = findActivityEntry(snapshot, workflowType, activityName);
42
+ return { contractHash: await sha256Hex(canonicalJsonStringify(activityContractPayload(entry))), implementationRevision };
43
+ }
44
+ async function buildWorkflowContract(snapshot, workflowVersionsByType, workflowType, activityNames, implementationRevision) {
45
+ const entry = findWorkflowEntry(snapshot, workflowType), workflowVersion = workflowVersionsByType.get(workflowType), contractHash = await sha256Hex(canonicalJsonStringify(workflowContractPayload(entry))), workflowRevision = await sha256Hex(canonicalJsonStringify({ ...entry, version: workflowVersion })), sortedActivityNames = [...activityNames].toSorted(), activityContracts = await Promise.all(sortedActivityNames.map((activityName) => buildActivityContract(snapshot, workflowType, activityName, implementationRevision))), activities = {};
46
+ sortedActivityNames.forEach((activityName, index) => {
47
+ activities[activityName] = activityContracts[index];
48
+ });
49
+ return { workflowVersion, workflowRevision, contractHash, activities };
50
+ }
51
+ export async function buildWorkerManifestFromRegistry(engine, options) {
52
+ const snapshot = buildRegistrySnapshot(engine), workflowVersionsByType = new Map(engine.listWorkflowDefinitions().map((definition) => [definition.type, definition.version])), sortedWorkflowTypes = Object.keys(options.workflows).toSorted(), workflowContracts = await Promise.all(sortedWorkflowTypes.map((workflowType) => buildWorkflowContract(snapshot, workflowVersionsByType, workflowType, options.workflows[workflowType] ?? [], options.deployment.buildId))), workflows = {};
53
+ sortedWorkflowTypes.forEach((workflowType, index) => {
54
+ workflows[workflowType] = workflowContracts[index];
55
+ });
56
+ return {
57
+ manifestVersion: WORKER_MANIFEST_VERSION,
58
+ protocolVersion: options.protocolVersion ?? REMOTE_WORKER_PROTOCOL_VERSION,
59
+ sdkVersion: options.sdkVersion ?? VERSION,
60
+ runtime: options.runtime,
61
+ deployment: options.deployment,
62
+ workflows,
63
+ capabilities: options.capabilities ?? {}
64
+ };
65
+ }
@@ -0,0 +1,263 @@
1
+ /**
2
+ * Canonical worker manifest and execution-identity vocabulary.
3
+ *
4
+ * One manifest answers every identity question about a worker at once, with
5
+ * each question in its own field:
6
+ *
7
+ * | Field | Question answered | Stability |
8
+ * | ----------------- | ------------------------------------------------ | ------------------------------- |
9
+ * | `manifestVersion` | Can the host parse this manifest shape? | Changes with manifest schema |
10
+ * | `protocolVersion` | Can these peers exchange wire messages? | Changes with wire semantics |
11
+ * | `sdkVersion` | Which Weft worker SDK produced this worker? | Package release |
12
+ * | `runtime` | Which runtime executes the worker? | Runtime deployment |
13
+ * | `deployment.name` | Which logical service owns this worker? | Stable service identity |
14
+ * | `buildId` | Which operator-visible release is this? | Immutable within a deployment |
15
+ * | `artifactDigest` | Which exact executable bytes are loaded? | Content-addressed and immutable |
16
+ * | `workflowVersion` | Which replay compatibility boundary is declared? | Workflow author controlled |
17
+ * | `contractHash` | Which public payload contract is implemented? | Deterministic contract identity |
18
+ *
19
+ * A Git SHA is deliberately absent from that table: repositories may be dirty,
20
+ * builds may inject configuration, and one commit may produce several
21
+ * artifacts, so a commit is metadata rather than an executable identity.
22
+ *
23
+ * @module worker/manifest/types
24
+ */
25
+ import type { JSONValue } from '../../core/json.ts';
26
+ /**
27
+ * Current worker manifest schema version.
28
+ *
29
+ * This is a different axis from the RemoteWorker wire protocol version:
30
+ * `manifestVersion` answers "can the host parse this shape", while
31
+ * `protocolVersion` answers "can these peers exchange messages". Bump this
32
+ * only when the manifest's own structure changes incompatibly; unknown
33
+ * versions are rejected before registration rather than best-effort parsed.
34
+ *
35
+ * @example
36
+ * ```ts
37
+ * import { WORKER_MANIFEST_VERSION } from '@lostgradient/weft';
38
+ *
39
+ * const canParse = (received: number): boolean => received === WORKER_MANIFEST_VERSION;
40
+ * console.log(canParse(1));
41
+ * ```
42
+ */
43
+ export declare const WORKER_MANIFEST_VERSION = 1;
44
+ /**
45
+ * Deterministic identity of one activity implementation inside a workflow.
46
+ *
47
+ * `contractHash` identifies the public payload contract — what callers may
48
+ * send and expect back. `implementationRevision` identifies the code behind
49
+ * that contract, which may change without changing the contract.
50
+ *
51
+ * @example
52
+ * ```ts
53
+ * import type { WorkerActivityContract } from '@lostgradient/weft';
54
+ *
55
+ * const charge: WorkerActivityContract = {
56
+ * contractHash: 'sha256:2b1f0c9d',
57
+ * implementationRevision: 'rev-41',
58
+ * };
59
+ * console.log(charge.contractHash);
60
+ * ```
61
+ */
62
+ export type WorkerActivityContract = Readonly<{
63
+ /** Deterministic identity of this activity's public payload contract. */
64
+ contractHash: string;
65
+ /** Identity of the implementation currently bound to that contract. */
66
+ implementationRevision: string;
67
+ }>;
68
+ /**
69
+ * Deterministic identity of one workflow an artifact can execute, including
70
+ * every activity it exposes.
71
+ *
72
+ * Activity keys are canonical runtime activity names. An activity is qualified
73
+ * structurally by the workflow that contains it rather than only through a
74
+ * dotted string, so the same activity name may appear under two workflows
75
+ * without collision.
76
+ *
77
+ * @example
78
+ * ```ts
79
+ * import type { WorkerWorkflowContract } from '@lostgradient/weft';
80
+ *
81
+ * const checkout: WorkerWorkflowContract = {
82
+ * workflowVersion: '2.1.0',
83
+ * workflowRevision: 'rev-88',
84
+ * contractHash: 'sha256:9ab3',
85
+ * activities: { charge: { contractHash: 'sha256:2b1f', implementationRevision: 'rev-41' } },
86
+ * };
87
+ * console.log(Object.keys(checkout.activities));
88
+ * ```
89
+ */
90
+ export type WorkerWorkflowContract = Readonly<{
91
+ /** Semantic replay-compatibility boundary declared by the workflow author. */
92
+ workflowVersion: string;
93
+ /** Identity of the exact workflow definition loaded from this artifact. */
94
+ workflowRevision: string;
95
+ /** Deterministic identity of the workflow's public payload contract. */
96
+ contractHash: string;
97
+ /** Activities this workflow exposes, keyed by canonical activity name. */
98
+ activities: Readonly<Record<string, WorkerActivityContract>>;
99
+ }>;
100
+ /**
101
+ * Immutable deployment artifact identity.
102
+ *
103
+ * `(name, buildId)` identifies exactly one `artifactDigest` within one server
104
+ * scope. Shipping different bytes means choosing a new `buildId` — reusing one
105
+ * is a registration conflict, not an update.
106
+ *
107
+ * @example
108
+ * ```ts
109
+ * import type { WorkerDeploymentIdentity } from '@lostgradient/weft';
110
+ *
111
+ * const deployment: WorkerDeploymentIdentity = {
112
+ * name: 'billing',
113
+ * buildId: '2026.08.18-3',
114
+ * artifactDigest: 'sha256:41d0e2',
115
+ * };
116
+ * console.log(deployment.buildId);
117
+ * ```
118
+ */
119
+ export type WorkerDeploymentIdentity = Readonly<{
120
+ /** Logical service that owns this worker. */
121
+ name: string;
122
+ /** Operator-visible release identity, immutable within the deployment. */
123
+ buildId: string;
124
+ /** Content-addressed digest of the complete executable artifact. */
125
+ artifactDigest: string;
126
+ }>;
127
+ /**
128
+ * Runtime that executes a worker process.
129
+ *
130
+ * `version` is an empty string on runtimes that expose none — a browser or
131
+ * edge worker — rather than being omitted, so the field is always answerable.
132
+ *
133
+ * @example
134
+ * ```ts
135
+ * import type { WorkerRuntimeIdentity } from '@lostgradient/weft';
136
+ *
137
+ * const runtime: WorkerRuntimeIdentity = { name: 'bun', version: '1.3.14' };
138
+ * console.log(`${runtime.name} ${runtime.version}`);
139
+ * ```
140
+ */
141
+ export type WorkerRuntimeIdentity = Readonly<{
142
+ /** Runtime name, such as `bun` or `node`. */
143
+ name: string;
144
+ /** Runtime version, or an empty string where the runtime exposes none. */
145
+ version: string;
146
+ }>;
147
+ /**
148
+ * Everything a worker asserts about itself, validated by the host before the
149
+ * worker becomes routing-eligible.
150
+ *
151
+ * Worker readiness means the server accepted a validated manifest — not merely
152
+ * that a socket opened. Digests and contract hashes carried here are checked
153
+ * against trusted records rather than believed because the worker asserted
154
+ * them, and `capabilities` never grants authorization on its own.
155
+ *
156
+ * @example
157
+ * ```ts
158
+ * import { WORKER_MANIFEST_VERSION, type WorkerManifest } from '@lostgradient/weft';
159
+ *
160
+ * const manifest: WorkerManifest = {
161
+ * manifestVersion: WORKER_MANIFEST_VERSION,
162
+ * protocolVersion: 2,
163
+ * sdkVersion: '0.18.0',
164
+ * runtime: { name: 'bun', version: '1.3.14' },
165
+ * deployment: { name: 'billing', buildId: '2026.08.18-3', artifactDigest: 'sha256:41d0e2' },
166
+ * workflows: {},
167
+ * capabilities: {},
168
+ * };
169
+ * console.log(manifest.deployment.name);
170
+ * ```
171
+ */
172
+ export type WorkerManifest = Readonly<{
173
+ /** Manifest schema version; unknown values are rejected, not tolerated. */
174
+ manifestVersion: typeof WORKER_MANIFEST_VERSION;
175
+ /** RemoteWorker wire protocol version these peers will speak. */
176
+ protocolVersion: number;
177
+ /** Weft worker SDK release that produced this worker. */
178
+ sdkVersion: string;
179
+ /** Runtime executing the worker process. */
180
+ runtime: WorkerRuntimeIdentity;
181
+ /** Immutable deployment artifact identity. */
182
+ deployment: WorkerDeploymentIdentity;
183
+ /** Workflows this artifact can execute, keyed by canonical workflow name. */
184
+ workflows: Readonly<Record<string, WorkerWorkflowContract>>;
185
+ /** Bounded descriptive capability data; never an authorization claim. */
186
+ capabilities: Readonly<Record<string, JSONValue>>;
187
+ }>;
188
+ /**
189
+ * Routing input: the constraints a task places on the worker that may execute
190
+ * it.
191
+ *
192
+ * An omitted field lets policy choose any eligible value. It is *not* an
193
+ * empty-string wildcard, and it is not a claim that the field does not matter
194
+ * once the task is leased — see {@link WorkerExecutionIdentity}, which is
195
+ * always complete.
196
+ *
197
+ * @example
198
+ * ```ts
199
+ * import type { WorkerExecutionRequirement } from '@lostgradient/weft';
200
+ *
201
+ * // Pin the deployment, let policy pick any eligible build within it.
202
+ * const requirement: WorkerExecutionRequirement = { deploymentName: 'billing' };
203
+ * console.log(requirement.buildId === undefined);
204
+ * ```
205
+ */
206
+ export type WorkerExecutionRequirement = Readonly<{
207
+ /** Required logical service, when the task pins one. */
208
+ deploymentName?: string;
209
+ /** Required operator-visible release, when the task pins one. */
210
+ buildId?: string;
211
+ /** Required executable artifact, when the task pins exact bytes. */
212
+ artifactDigest?: string;
213
+ /** Required workflow definition revision, when the task pins one. */
214
+ workflowRevision?: string;
215
+ /** Required activity contract identity, when the task pins one. */
216
+ activityContractHash?: string;
217
+ }>;
218
+ /**
219
+ * Observed execution: the complete identity of the worker that actually holds
220
+ * a lease on an attempt.
221
+ *
222
+ * Every field is populated from the accepted manifest plus the live session.
223
+ * A worker cannot self-report a different execution identity in its result,
224
+ * which is what makes this safe to persist as provenance.
225
+ *
226
+ * @example
227
+ * ```ts
228
+ * import type { WorkerExecutionIdentity } from '@lostgradient/weft';
229
+ *
230
+ * function describe(identity: WorkerExecutionIdentity): string {
231
+ * return `${identity.deploymentName}@${identity.buildId} (${identity.workerId})`;
232
+ * }
233
+ * console.log(typeof describe);
234
+ * ```
235
+ */
236
+ export type WorkerExecutionIdentity = Readonly<{
237
+ /** Live process instance that holds the lease. */
238
+ workerId: string;
239
+ /** Digest of the manifest the server accepted from that instance. */
240
+ manifestDigest: string;
241
+ /** Wire protocol version negotiated with that instance. */
242
+ protocolVersion: number;
243
+ /** Weft worker SDK release that produced the instance. */
244
+ sdkVersion: string;
245
+ /** Runtime name executing the instance. */
246
+ runtimeName: string;
247
+ /** Runtime version executing the instance. */
248
+ runtimeVersion: string;
249
+ /** Logical service that owns the instance. */
250
+ deploymentName: string;
251
+ /** Operator-visible release the instance is running. */
252
+ buildId: string;
253
+ /** Executable artifact the instance loaded. */
254
+ artifactDigest: string;
255
+ /** Workflow that executed the attempt. */
256
+ workflowType: string;
257
+ /** Workflow definition revision that executed the attempt. */
258
+ workflowRevision: string;
259
+ /** Activity that executed the attempt. */
260
+ activityName: string;
261
+ /** Contract identity of that activity. */
262
+ activityContractHash: string;
263
+ }>;
@@ -0,0 +1 @@
1
+ export const WORKER_MANIFEST_VERSION = 1;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * UTF-8 byte measurement for manifest bounds.
3
+ *
4
+ * Manifest limits are expressed in bytes rather than code units so a worker
5
+ * cannot smuggle several times the intended payload through astral-plane
6
+ * characters, each of which costs one UTF-16 `.length` but four UTF-8 bytes.
7
+ *
8
+ * @module worker/manifest/utf8
9
+ */
10
+ /** UTF-8 byte length of a string. */
11
+ export declare function utf8ByteLength(value: string): number;
12
+ /** Encode a string to UTF-8 bytes. */
13
+ export declare function utf8Encode(value: string): Uint8Array;
@@ -0,0 +1,7 @@
1
+ const encoder = new TextEncoder;
2
+ export function utf8ByteLength(value) {
3
+ return encoder.encode(value).byteLength;
4
+ }
5
+ export function utf8Encode(value) {
6
+ return encoder.encode(value);
7
+ }