cognitive-modules-cli 2.2.5 → 2.2.7

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/CHANGELOG.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  All notable changes to this package are documented in this file.
4
4
 
5
- ## 2.2.5 - 2026-02-06
5
+ ## 2.2.7 - 2026-02-06
6
6
 
7
7
  - Standardized v2.2 runtime behavior and cross-surface error envelope consistency (CLI/HTTP/MCP).
8
8
  - Clarified `compose` output contract: default/pretty output returns full v2.2 envelope, while `compose --trace` returns a debug wrapper object (`result/moduleResults/trace/totalTimeMs`) for diagnostics.
package/README.md CHANGED
@@ -10,12 +10,12 @@ Node.js/TypeScript 版本的 Cognitive Modules CLI,提供 `cog` 命令。
10
10
 
11
11
  ```bash
12
12
  # 全局安装(推荐)
13
- npm install -g cogn@2.2.5
13
+ npm install -g cogn@2.2.7
14
14
  # 或使用完整包名(同样提供 `cog` 命令)
15
- # npm install -g cognitive-modules-cli@2.2.5
15
+ # npm install -g cognitive-modules-cli@2.2.7
16
16
 
17
17
  # 或使用 npx 零安装
18
- npx cogn@2.2.5 --help
18
+ npx cogn@2.2.7 --help
19
19
  ```
20
20
 
21
21
  ## 快速开始
package/dist/cli.js CHANGED
@@ -41,6 +41,7 @@ async function main() {
41
41
  pretty: { type: 'boolean', default: false },
42
42
  verbose: { type: 'boolean', short: 'V', default: false },
43
43
  'no-validate': { type: 'boolean', default: false },
44
+ stream: { type: 'boolean', default: false },
44
45
  // Add/update options
45
46
  name: { type: 'string', short: 'n' },
46
47
  tag: { type: 'string', short: 't' },
@@ -92,6 +93,7 @@ async function main() {
92
93
  noValidate: values['no-validate'],
93
94
  pretty: values.pretty,
94
95
  verbose: values.verbose,
96
+ stream: values.stream,
95
97
  });
96
98
  if (!result.success) {
97
99
  if (result.data) {
@@ -102,7 +104,10 @@ async function main() {
102
104
  }
103
105
  process.exit(1);
104
106
  }
105
- console.log(JSON.stringify(result.data, null, values.pretty ? 2 : 0));
107
+ // Stream mode prints events as NDJSON already.
108
+ if (!values.stream) {
109
+ console.log(JSON.stringify(result.data, null, values.pretty ? 2 : 0));
110
+ }
106
111
  break;
107
112
  }
108
113
  case 'list': {
@@ -9,13 +9,15 @@
9
9
  * cog add ziel-io/cognitive-modules -m code-simplifier
10
10
  * cog add https://github.com/org/repo --module name --tag v1.0.0
11
11
  */
12
- import { createWriteStream, existsSync, mkdirSync, rmSync, readdirSync, statSync, copyFileSync } from 'node:fs';
12
+ import { createWriteStream, existsSync, mkdirSync, rmSync, readdirSync, statSync, copyFileSync, lstatSync } from 'node:fs';
13
13
  import { writeFile, readFile, mkdir } from 'node:fs/promises';
14
- import { pipeline } from 'node:stream/promises';
14
+ import { pipeline, finished } from 'node:stream/promises';
15
15
  import { Readable } from 'node:stream';
16
16
  import { join, basename, dirname, resolve, sep, isAbsolute } from 'node:path';
17
17
  import { homedir, tmpdir } from 'node:os';
18
+ import { createHash } from 'node:crypto';
18
19
  import { RegistryClient } from '../registry/client.js';
20
+ import { extractTarGzFile } from '../registry/tar.js';
19
21
  // Module storage paths
20
22
  const USER_MODULES_DIR = join(homedir(), '.cognitive', 'modules');
21
23
  const INSTALLED_MANIFEST = join(homedir(), '.cognitive', 'installed.json');
@@ -153,7 +155,11 @@ function copyDir(src, dest) {
153
155
  for (const entry of readdirSync(src)) {
154
156
  const srcPath = join(src, entry);
155
157
  const destPath = join(dest, entry);
156
- if (statSync(srcPath).isDirectory()) {
158
+ const st = lstatSync(srcPath);
159
+ if (st.isSymbolicLink()) {
160
+ throw new Error(`Refusing to install module containing symlink: ${srcPath}`);
161
+ }
162
+ if (st.isDirectory()) {
157
163
  copyDir(srcPath, destPath);
158
164
  }
159
165
  else {
@@ -161,6 +167,74 @@ function copyDir(src, dest) {
161
167
  }
162
168
  }
163
169
  }
170
+ async function downloadTarballWithSha256(url, outPath, maxBytes) {
171
+ const controller = new AbortController();
172
+ const timeout = setTimeout(() => controller.abort(), 10_000);
173
+ const hash = createHash('sha256');
174
+ let received = 0;
175
+ const fileStream = createWriteStream(outPath);
176
+ try {
177
+ const response = await fetch(url, {
178
+ headers: { 'User-Agent': 'cognitive-runtime/2.2' },
179
+ signal: controller.signal,
180
+ });
181
+ if (!response.ok) {
182
+ throw new Error(`Failed to download tarball: ${response.status} ${response.statusText}`);
183
+ }
184
+ const contentLengthHeader = response.headers?.get?.('content-length');
185
+ if (contentLengthHeader) {
186
+ const contentLength = Number(contentLengthHeader);
187
+ if (!Number.isNaN(contentLength) && contentLength > maxBytes) {
188
+ throw new Error(`Tarball too large: ${contentLength} bytes (max ${maxBytes})`);
189
+ }
190
+ }
191
+ if (!response.body) {
192
+ throw new Error('Tarball response has no body');
193
+ }
194
+ const reader = response.body.getReader?.();
195
+ if (!reader) {
196
+ // Fallback: stream pipeline without incremental hash.
197
+ await pipeline(Readable.fromWeb(response.body), fileStream);
198
+ const content = await readFile(outPath);
199
+ return createHash('sha256').update(content).digest('hex');
200
+ }
201
+ while (true) {
202
+ const { done, value } = await reader.read();
203
+ if (done)
204
+ break;
205
+ if (value) {
206
+ received += value.byteLength;
207
+ if (received > maxBytes) {
208
+ controller.abort();
209
+ throw new Error(`Tarball too large: ${received} bytes (max ${maxBytes})`);
210
+ }
211
+ const chunk = Buffer.from(value);
212
+ hash.update(chunk);
213
+ if (!fileStream.write(chunk)) {
214
+ await new Promise((resolve) => fileStream.once('drain', () => resolve()));
215
+ }
216
+ }
217
+ }
218
+ fileStream.end();
219
+ await finished(fileStream);
220
+ return hash.digest('hex');
221
+ }
222
+ catch (error) {
223
+ try {
224
+ fileStream.destroy();
225
+ }
226
+ catch {
227
+ // ignore
228
+ }
229
+ if (error instanceof Error && error.name === 'AbortError') {
230
+ throw new Error('Tarball download timed out after 10000ms');
231
+ }
232
+ throw error;
233
+ }
234
+ finally {
235
+ clearTimeout(timeout);
236
+ }
237
+ }
164
238
  /**
165
239
  * Get module version from module.yaml or MODULE.md
166
240
  */
@@ -266,6 +340,7 @@ function parseModuleSpec(spec) {
266
340
  export async function addFromRegistry(moduleSpec, ctx, options = {}) {
267
341
  const { name: moduleName, version: requestedVersion } = parseModuleSpec(moduleSpec);
268
342
  const { name: customName, registry: registryUrl } = options;
343
+ let tempDir;
269
344
  try {
270
345
  const client = new RegistryClient(registryUrl);
271
346
  const moduleInfo = await client.getModule(moduleName);
@@ -325,10 +400,83 @@ export async function addFromRegistry(moduleSpec, ctx, options = {}) {
325
400
  }
326
401
  return result;
327
402
  }
328
- // For tarball sources, download directly (future implementation)
403
+ // Tarball sources: require checksum, verify, and extract safely.
404
+ if (!downloadInfo.url.startsWith('http')) {
405
+ return { success: false, error: `Unsupported registry download URL: ${downloadInfo.url}` };
406
+ }
407
+ if (!downloadInfo.checksum) {
408
+ return {
409
+ success: false,
410
+ error: `Registry tarball missing checksum (required for safe install): ${moduleName}`,
411
+ };
412
+ }
413
+ tempDir = join(tmpdir(), `cog-reg-${Date.now()}`);
414
+ mkdirSync(tempDir, { recursive: true });
415
+ const tarPath = join(tempDir, 'module.tar.gz');
416
+ const MAX_TARBALL_BYTES = 20 * 1024 * 1024; // 20MB
417
+ const actualSha256 = await downloadTarballWithSha256(downloadInfo.url, tarPath, MAX_TARBALL_BYTES);
418
+ const expected = downloadInfo.checksum;
419
+ const checksumMatch = expected.match(/^sha256:([a-f0-9]{64})$/);
420
+ if (!checksumMatch) {
421
+ throw new Error(`Unsupported checksum format (expected sha256:<64hex>): ${expected}`);
422
+ }
423
+ const expectedHash = checksumMatch[1];
424
+ if (actualSha256 !== expectedHash) {
425
+ throw new Error(`Checksum mismatch for ${moduleName}: expected ${expectedHash}, got ${actualSha256}`);
426
+ }
427
+ const extractedRoot = join(tempDir, 'pkg');
428
+ mkdirSync(extractedRoot, { recursive: true });
429
+ await extractTarGzFile(tarPath, extractedRoot, {
430
+ maxFiles: 5_000,
431
+ maxTotalBytes: 50 * 1024 * 1024,
432
+ maxSingleFileBytes: 20 * 1024 * 1024,
433
+ maxTarBytes: 100 * 1024 * 1024,
434
+ });
435
+ // Find module directory inside extractedRoot.
436
+ const rootNames = readdirSync(extractedRoot).filter((e) => e !== '__MACOSX' && e !== '.DS_Store');
437
+ const rootPaths = rootNames.map((e) => join(extractedRoot, e)).filter((p) => existsSync(p));
438
+ const rootDirs = rootPaths.filter((p) => statSync(p).isDirectory());
439
+ const rootFiles = rootPaths.filter((p) => !statSync(p).isDirectory());
440
+ if (rootDirs.length === 0) {
441
+ throw new Error('Tarball extraction produced no root directory');
442
+ }
443
+ if (rootDirs.length !== 1 || rootFiles.length > 0) {
444
+ throw new Error(`Tarball must contain exactly one module root directory and no other top-level entries. ` +
445
+ `dirs=${rootDirs.map((p) => basename(p)).join(',') || '(none)'} files=${rootFiles.map((p) => basename(p)).join(',') || '(none)'}`);
446
+ }
447
+ // Strict mode: require root dir itself to be a valid module.
448
+ const sourcePath = rootDirs[0];
449
+ if (!isValidModule(sourcePath)) {
450
+ throw new Error('Root directory in tarball is not a valid module');
451
+ }
452
+ const installName = (customName || moduleName);
453
+ const safeInstallName = assertSafeModuleName(installName);
454
+ const targetPath = resolveModuleTarget(safeInstallName);
455
+ if (existsSync(targetPath)) {
456
+ rmSync(targetPath, { recursive: true, force: true });
457
+ }
458
+ await mkdir(USER_MODULES_DIR, { recursive: true });
459
+ copyDir(sourcePath, targetPath);
460
+ const version = await getModuleVersion(sourcePath);
461
+ await recordInstall(safeInstallName, {
462
+ source: downloadInfo.url,
463
+ githubUrl: downloadInfo.url,
464
+ version,
465
+ installedAt: targetPath,
466
+ installedTime: new Date().toISOString(),
467
+ registryModule: moduleName,
468
+ registryUrl,
469
+ });
329
470
  return {
330
- success: false,
331
- error: `Tarball downloads not yet supported. Source: ${moduleInfo.source}`,
471
+ success: true,
472
+ data: {
473
+ message: `Added: ${safeInstallName}${version ? ` v${version}` : ''} (registry tarball)`,
474
+ name: safeInstallName,
475
+ version,
476
+ location: targetPath,
477
+ source: 'registry',
478
+ registryModule: moduleName,
479
+ },
332
480
  };
333
481
  }
334
482
  catch (error) {
@@ -337,6 +485,16 @@ export async function addFromRegistry(moduleSpec, ctx, options = {}) {
337
485
  error: error instanceof Error ? error.message : String(error),
338
486
  };
339
487
  }
488
+ finally {
489
+ if (tempDir) {
490
+ try {
491
+ rmSync(tempDir, { recursive: true, force: true });
492
+ }
493
+ catch {
494
+ // ignore
495
+ }
496
+ }
497
+ }
340
498
  }
341
499
  /**
342
500
  * Add a module from GitHub
@@ -9,5 +9,6 @@ export interface RunOptions {
9
9
  noValidate?: boolean;
10
10
  pretty?: boolean;
11
11
  verbose?: boolean;
12
+ stream?: boolean;
12
13
  }
13
14
  export declare function run(moduleName: string, ctx: CommandContext, options?: RunOptions): Promise<CommandResult>;
@@ -2,7 +2,7 @@
2
2
  * cog run - Run a Cognitive Module
3
3
  * Always returns v2.2 envelope format for consistency
4
4
  */
5
- import { findModule, getDefaultSearchPaths, runModule } from '../modules/index.js';
5
+ import { findModule, getDefaultSearchPaths, runModule, runModuleStream } from '../modules/index.js';
6
6
  import { ErrorCodes, attachContext, makeErrorEnvelope } from '../errors/index.js';
7
7
  export async function run(moduleName, ctx, options = {}) {
8
8
  const searchPaths = getDefaultSearchPaths(ctx.cwd);
@@ -40,24 +40,47 @@ export async function run(moduleName, ctx, options = {}) {
40
40
  };
41
41
  }
42
42
  }
43
- // Run module with v2.2 envelope format
44
- const result = await runModule(module, ctx.provider, {
45
- args: options.args,
46
- input: inputData,
47
- verbose: options.verbose || ctx.verbose,
48
- validateInput: !options.noValidate,
49
- validateOutput: !options.noValidate,
50
- useV22: true, // Always use v2.2 envelope
51
- });
52
- const output = attachContext(result, {
53
- module: moduleName,
54
- provider: ctx.provider.name,
55
- });
56
- // Always return full v2.2 envelope
57
- return {
58
- success: result.ok,
59
- data: output,
60
- };
43
+ if (options.stream) {
44
+ // Stream NDJSON events to stdout. Final exit code is determined by the end event.
45
+ let finalOk = null;
46
+ for await (const ev of runModuleStream(module, ctx.provider, {
47
+ args: options.args,
48
+ input: inputData,
49
+ validateInput: !options.noValidate,
50
+ validateOutput: !options.noValidate,
51
+ useV22: true,
52
+ })) {
53
+ // Write each event as one JSON line (NDJSON).
54
+ process.stdout.write(JSON.stringify(ev) + '\n');
55
+ if (ev.type === 'end' && ev.result) {
56
+ finalOk = Boolean(ev.result.ok);
57
+ }
58
+ }
59
+ return {
60
+ success: finalOk === true,
61
+ data: { ok: finalOk === true },
62
+ };
63
+ }
64
+ else {
65
+ // Run module with v2.2 envelope format
66
+ const result = await runModule(module, ctx.provider, {
67
+ args: options.args,
68
+ input: inputData,
69
+ verbose: options.verbose || ctx.verbose,
70
+ validateInput: !options.noValidate,
71
+ validateOutput: !options.noValidate,
72
+ useV22: true, // Always use v2.2 envelope
73
+ });
74
+ const output = attachContext(result, {
75
+ module: moduleName,
76
+ provider: ctx.provider.name,
77
+ });
78
+ // Always return full v2.2 envelope
79
+ return {
80
+ success: result.ok,
81
+ data: output,
82
+ };
83
+ }
61
84
  }
62
85
  catch (e) {
63
86
  const message = e instanceof Error ? e.message : String(e);
@@ -112,6 +112,13 @@ export interface ErrorEnvelopeOptions {
112
112
  */
113
113
  export declare function makeErrorEnvelope(options: ErrorEnvelopeOptions): CognitiveErrorEnvelope;
114
114
  export declare function attachContext<T extends object>(envelope: T, context?: EnvelopeContext): T & EnvelopeContext;
115
+ /**
116
+ * Map a CEP error code to an HTTP status code.
117
+ *
118
+ * This is used to keep HTTP behavior consistent with the error model while
119
+ * allowing callers to attach context without rebuilding envelopes.
120
+ */
121
+ export declare function httpStatusForErrorCode(code: string): number;
115
122
  /**
116
123
  * Create error envelope for HTTP API responses.
117
124
  *
@@ -191,54 +191,47 @@ export function attachContext(envelope, context) {
191
191
  };
192
192
  }
193
193
  /**
194
- * Create error envelope for HTTP API responses.
194
+ * Map a CEP error code to an HTTP status code.
195
195
  *
196
- * @returns Tuple of [statusCode, body]
196
+ * This is used to keep HTTP behavior consistent with the error model while
197
+ * allowing callers to attach context without rebuilding envelopes.
197
198
  */
198
- export function makeHttpError(options) {
199
- const envelope = attachContext(makeErrorEnvelope(options), options);
200
- const code = normalizeErrorCode(options.code);
201
- // Determine HTTP status code
202
- let statusCode;
203
- const category = code.charAt(1);
199
+ export function httpStatusForErrorCode(code) {
200
+ const normalized = normalizeErrorCode(code);
201
+ const category = normalized.charAt(1);
204
202
  switch (category) {
205
203
  case '1': {
206
204
  // Input errors -> Bad Request (with specific overrides)
207
- if (code === ErrorCodes.INPUT_TOO_LARGE) {
208
- statusCode = 413; // Payload Too Large
209
- }
210
- else {
211
- statusCode = 400;
212
- }
213
- break;
205
+ if (normalized === ErrorCodes.INPUT_TOO_LARGE)
206
+ return 413;
207
+ return 400;
214
208
  }
215
- case '2':
216
- statusCode = 422;
217
- break; // Processing errors -> Unprocessable Entity
218
- case '3':
219
- statusCode = 500;
220
- break; // Output errors -> Internal Server Error
209
+ case '2': return 422; // Processing errors -> Unprocessable Entity
210
+ case '3': return 500; // Output errors -> Internal Server Error
221
211
  case '4': {
222
- // Runtime errors - map to appropriate HTTP status
223
- if (code === ErrorCodes.MODULE_NOT_FOUND ||
224
- code === ErrorCodes.ENDPOINT_NOT_FOUND ||
225
- code === ErrorCodes.RESOURCE_NOT_FOUND) {
226
- statusCode = 404; // Not Found
227
- }
228
- else if (code === ErrorCodes.PERMISSION_DENIED) {
229
- statusCode = 403; // Forbidden
230
- }
231
- else if (code === ErrorCodes.RATE_LIMITED) {
232
- statusCode = 429; // Too Many Requests
212
+ // Runtime errors -> map to appropriate HTTP status
213
+ if (normalized === ErrorCodes.MODULE_NOT_FOUND ||
214
+ normalized === ErrorCodes.ENDPOINT_NOT_FOUND ||
215
+ normalized === ErrorCodes.RESOURCE_NOT_FOUND) {
216
+ return 404;
233
217
  }
234
- else {
235
- statusCode = 500; // Internal Server Error
236
- }
237
- break;
218
+ if (normalized === ErrorCodes.PERMISSION_DENIED)
219
+ return 403;
220
+ if (normalized === ErrorCodes.RATE_LIMITED)
221
+ return 429;
222
+ return 500;
238
223
  }
239
- default: statusCode = 500;
224
+ default: return 500;
240
225
  }
241
- // Add HTTP-specific fields
226
+ }
227
+ /**
228
+ * Create error envelope for HTTP API responses.
229
+ *
230
+ * @returns Tuple of [statusCode, body]
231
+ */
232
+ export function makeHttpError(options) {
233
+ const envelope = attachContext(makeErrorEnvelope(options), options);
234
+ const statusCode = httpStatusForErrorCode(String(options.code));
242
235
  return [statusCode, envelope];
243
236
  }
244
237
  /**
@@ -316,16 +309,31 @@ export function shouldRetry(envelope) {
316
309
  * Use this for consistent success responses across HTTP and MCP layers.
317
310
  */
318
311
  export function makeSuccessEnvelope(options) {
312
+ const explain = (options.explain || 'Operation completed successfully').slice(0, 280);
313
+ // Envelope schema requires data to be an object with at least `rationale`.
314
+ // For non-module operations (list/info), we still emit a conforming envelope by
315
+ // injecting a minimal rationale if missing, or wrapping non-objects.
316
+ const normalizedData = (() => {
317
+ const d = options.data;
318
+ const isPlainObject = typeof d === 'object' && d !== null && !Array.isArray(d);
319
+ if (!isPlainObject) {
320
+ return { result: d, rationale: explain };
321
+ }
322
+ const obj = d;
323
+ if (typeof obj.rationale === 'string')
324
+ return options.data;
325
+ return { ...obj, rationale: explain };
326
+ })();
319
327
  return {
320
328
  ok: true,
321
329
  version: options.version || '2.2',
322
330
  meta: {
323
331
  confidence: options.confidence ?? 1.0,
324
332
  risk: options.risk ?? 'none',
325
- explain: (options.explain || 'Operation completed successfully').slice(0, 280),
333
+ explain,
326
334
  trace_id: options.trace_id,
327
335
  },
328
- data: options.data,
336
+ data: normalizedData,
329
337
  };
330
338
  }
331
339
  /**
@@ -369,13 +369,15 @@ export interface RunOptions {
369
369
  }
370
370
  export declare function runModule(module: CognitiveModule, provider: Provider, options?: RunOptions): Promise<ModuleResult>;
371
371
  /** Event types emitted during streaming execution */
372
- export type StreamEventType = 'start' | 'chunk' | 'meta' | 'complete' | 'error';
372
+ export type StreamEventType = 'start' | 'delta' | 'meta' | 'end' | 'error';
373
373
  /** Event emitted during streaming execution */
374
374
  export interface StreamEvent {
375
375
  type: StreamEventType;
376
+ version: string;
376
377
  timestamp_ms: number;
377
- module_name: string;
378
- chunk?: string;
378
+ module: string;
379
+ provider?: string;
380
+ delta?: string;
379
381
  meta?: EnvelopeMeta;
380
382
  result?: EnvelopeResponseV22<unknown>;
381
383
  error?: {
@@ -398,9 +400,9 @@ export interface StreamOptions {
398
400
  *
399
401
  * Yields StreamEvent objects as the module executes:
400
402
  * - type="start": Module execution started
401
- * - type="chunk": Incremental data chunk (if LLM supports streaming)
403
+ * - type="delta": Incremental output delta (provider streaming chunk)
402
404
  * - type="meta": Meta information available early
403
- * - type="complete": Final complete result
405
+ * - type="end": Final result envelope (always emitted)
404
406
  * - type="error": Error occurred
405
407
  *
406
408
  * @example
@@ -1324,9 +1324,9 @@ export async function runModule(module, provider, options = {}) {
1324
1324
  *
1325
1325
  * Yields StreamEvent objects as the module executes:
1326
1326
  * - type="start": Module execution started
1327
- * - type="chunk": Incremental data chunk (if LLM supports streaming)
1327
+ * - type="delta": Incremental output delta (provider streaming chunk)
1328
1328
  * - type="meta": Meta information available early
1329
- * - type="complete": Final complete result
1329
+ * - type="end": Final result envelope (always emitted)
1330
1330
  * - type="error": Error occurred
1331
1331
  *
1332
1332
  * @example
@@ -1342,11 +1342,14 @@ export async function* runModuleStream(module, provider, options = {}) {
1342
1342
  const { input, args, validateInput = true, validateOutput = true, useV22 = true, enableRepair = true, traceId, model } = options;
1343
1343
  const startTime = Date.now();
1344
1344
  const moduleName = module.name;
1345
+ const providerName = provider?.name;
1345
1346
  function makeEvent(type, extra = {}) {
1346
1347
  return {
1347
1348
  type,
1349
+ version: ENVELOPE_VERSION,
1348
1350
  timestamp_ms: Date.now() - startTime,
1349
- module_name: moduleName,
1351
+ module: moduleName,
1352
+ ...(providerName ? { provider: providerName } : {}),
1350
1353
  ...extra,
1351
1354
  };
1352
1355
  }
@@ -1378,7 +1381,7 @@ export async function* runModuleStream(module, provider, options = {}) {
1378
1381
  _invokeErrorHooks(module.name, new Error(inputErrors.join('; ')), null);
1379
1382
  const errorObj = errorResult.error;
1380
1383
  yield makeEvent('error', { error: errorObj });
1381
- yield makeEvent('complete', { result: errorResult });
1384
+ yield makeEvent('end', { result: errorResult });
1382
1385
  return;
1383
1386
  }
1384
1387
  }
@@ -1415,7 +1418,7 @@ export async function* runModuleStream(module, provider, options = {}) {
1415
1418
  let streamResult;
1416
1419
  while (!(streamResult = await stream.next()).done) {
1417
1420
  const chunk = streamResult.value;
1418
- yield makeEvent('chunk', { chunk });
1421
+ yield makeEvent('delta', { delta: chunk });
1419
1422
  }
1420
1423
  // Get the final result (returned from the generator)
1421
1424
  fullContent = streamResult.value.content;
@@ -1429,7 +1432,7 @@ export async function* runModuleStream(module, provider, options = {}) {
1429
1432
  });
1430
1433
  fullContent = result.content;
1431
1434
  // Emit chunk event with full response
1432
- yield makeEvent('chunk', { chunk: result.content });
1435
+ yield makeEvent('delta', { delta: result.content });
1433
1436
  }
1434
1437
  // Parse response
1435
1438
  let parsed;
@@ -1448,7 +1451,7 @@ export async function* runModuleStream(module, provider, options = {}) {
1448
1451
  // errorResult is always an error response from makeErrorResponse
1449
1452
  const errorObj = errorResult.error;
1450
1453
  yield makeEvent('error', { error: errorObj });
1451
- yield makeEvent('complete', { result: errorResult });
1454
+ yield makeEvent('end', { result: errorResult });
1452
1455
  return;
1453
1456
  }
1454
1457
  // Convert to v2.2 envelope
@@ -1502,7 +1505,7 @@ export async function* runModuleStream(module, provider, options = {}) {
1502
1505
  _invokeErrorHooks(module.name, new Error(dataErrors.join('; ')), response.data);
1503
1506
  const errorObj = errorResult.error;
1504
1507
  yield makeEvent('error', { error: errorObj });
1505
- yield makeEvent('complete', { result: errorResult });
1508
+ yield makeEvent('end', { result: errorResult });
1506
1509
  return;
1507
1510
  }
1508
1511
  const overflowErrors = validateOverflowLimits(dataToValidate, module);
@@ -1517,7 +1520,7 @@ export async function* runModuleStream(module, provider, options = {}) {
1517
1520
  _invokeErrorHooks(module.name, new Error(overflowErrors.join('; ')), dataToValidate);
1518
1521
  const errorObj = errorResult.error;
1519
1522
  yield makeEvent('error', { error: errorObj });
1520
- yield makeEvent('complete', { result: errorResult });
1523
+ yield makeEvent('end', { result: errorResult });
1521
1524
  return;
1522
1525
  }
1523
1526
  const enumErrors = validateEnumStrategy(dataToValidate, module);
@@ -1532,7 +1535,7 @@ export async function* runModuleStream(module, provider, options = {}) {
1532
1535
  _invokeErrorHooks(module.name, new Error(enumErrors.join('; ')), dataToValidate);
1533
1536
  const errorObj = errorResult.error;
1534
1537
  yield makeEvent('error', { error: errorObj });
1535
- yield makeEvent('complete', { result: errorResult });
1538
+ yield makeEvent('end', { result: errorResult });
1536
1539
  return;
1537
1540
  }
1538
1541
  }
@@ -1554,7 +1557,7 @@ export async function* runModuleStream(module, provider, options = {}) {
1554
1557
  _invokeErrorHooks(module.name, new Error(metaErrors.join('; ')), response.data);
1555
1558
  const errorObj = errorResult.error;
1556
1559
  yield makeEvent('error', { error: errorObj });
1557
- yield makeEvent('complete', { result: errorResult });
1560
+ yield makeEvent('end', { result: errorResult });
1558
1561
  return;
1559
1562
  }
1560
1563
  }
@@ -1566,8 +1569,8 @@ export async function* runModuleStream(module, provider, options = {}) {
1566
1569
  }
1567
1570
  const finalLatencyMs = Date.now() - startTime;
1568
1571
  _invokeAfterHooks(module.name, response, finalLatencyMs);
1569
- // Emit complete event
1570
- yield makeEvent('complete', { result: response });
1572
+ // Emit end event
1573
+ yield makeEvent('end', { result: response });
1571
1574
  }
1572
1575
  catch (e) {
1573
1576
  _invokeErrorHooks(module.name, e, null);
@@ -1579,7 +1582,7 @@ export async function* runModuleStream(module, provider, options = {}) {
1579
1582
  // errorResult is always an error response from makeErrorResponse
1580
1583
  const errorObj = errorResult.error;
1581
1584
  yield makeEvent('error', { error: errorObj });
1582
- yield makeEvent('complete', { result: errorResult });
1585
+ yield makeEvent('end', { result: errorResult });
1583
1586
  }
1584
1587
  }
1585
1588
  // =============================================================================