@kindgi/handler-runtime 0.1.4-rc.5 → 0.1.5-rc.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 (44) hide show
  1. package/README.md +1 -0
  2. package/dist/handler-runner.d.ts +21 -3
  3. package/dist/handler-runner.d.ts.map +1 -1
  4. package/dist/handler-runner.js +40 -0
  5. package/dist/handler-runner.js.map +1 -1
  6. package/dist/index.d.ts +2 -2
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +1 -1
  9. package/dist/index.js.map +1 -1
  10. package/dist/kindgi-index.d.ts +50 -10
  11. package/dist/kindgi-index.d.ts.map +1 -1
  12. package/dist/kindgi-index.js +154 -12
  13. package/dist/kindgi-index.js.map +1 -1
  14. package/dist/pack-env.d.ts +3 -3
  15. package/dist/pack-service/main.d.ts +8 -2
  16. package/dist/pack-service/main.d.ts.map +1 -1
  17. package/dist/pack-service/main.js +38 -19
  18. package/dist/pack-service/main.js.map +1 -1
  19. package/dist/pack-service/records.d.ts +45 -0
  20. package/dist/pack-service/records.d.ts.map +1 -0
  21. package/dist/pack-service/records.js +73 -0
  22. package/dist/pack-service/records.js.map +1 -0
  23. package/dist/pack-service/service.d.ts +7 -0
  24. package/dist/pack-service/service.d.ts.map +1 -1
  25. package/dist/pack-service/service.js +75 -13
  26. package/dist/pack-service/service.js.map +1 -1
  27. package/dist/pack-service/supervisor.d.ts +24 -1
  28. package/dist/pack-service/supervisor.d.ts.map +1 -1
  29. package/dist/pack-service/supervisor.js +53 -10
  30. package/dist/pack-service/supervisor.js.map +1 -1
  31. package/dist/protocol.d.ts +7 -2
  32. package/dist/protocol.d.ts.map +1 -1
  33. package/dist/protocol.js +2 -2
  34. package/dist/protocol.js.map +1 -1
  35. package/package.json +9 -7
  36. package/src/handler-runner.ts +62 -4
  37. package/src/index.ts +6 -0
  38. package/src/kindgi-index.ts +192 -17
  39. package/src/pack-env.ts +3 -3
  40. package/src/pack-service/main.ts +39 -20
  41. package/src/pack-service/records.ts +99 -0
  42. package/src/pack-service/service.ts +96 -16
  43. package/src/pack-service/supervisor.ts +69 -12
  44. package/src/protocol.ts +7 -2
@@ -0,0 +1,99 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (C) 2026 Kindgi Inc.
3
+
4
+ /**
5
+ * The pack service's log: `@kindgi/log` records on stderr, subsystem
6
+ * `pack` (the runtime writes the same schema). Two streams share the sink:
7
+ *
8
+ * - **`log`**: calls, and `ctx.log` beneath them (`pack.tool`,
9
+ * `pack.check`). `KINDGI_LOG_LEVEL` / `KINDGI_LOG_LEVELS` apply.
10
+ * - **`event`**: the lifecycle (`listening`, `boot-failed`, …), written
11
+ * whatever the levels, because a supervisor reads it to know the
12
+ * service is up. Its records carry the event's name as `event`, and as
13
+ * `kind` too, so a supervisor from before records (which reads bare
14
+ * `{"kind": …}` lines) still sees `listening`. `kind` stays through
15
+ * 0.1.x.
16
+ *
17
+ * The format is `KINDGI_LOG_FORMAT`'s, but `auto` is pretty only on a
18
+ * terminal: a supervisor pipes stderr, and dev mode (`KINDGI_DEV`) doesn't
19
+ * make it pretty, so a supervisor always gets JSON unless told otherwise.
20
+ */
21
+
22
+ import {
23
+ type LogFields,
24
+ type Logger,
25
+ createLogger,
26
+ loggerFromEnv,
27
+ resolveLogFormat,
28
+ } from '@kindgi/log';
29
+
30
+ /** Lifecycle events: always written, the supervisor's to read. */
31
+ export const PACK_SERVICE_EVENTS = [
32
+ 'listening',
33
+ 'boot-failed',
34
+ 'config-invalid',
35
+ 'draining',
36
+ 'stopped',
37
+ ] as const;
38
+ export type PackServiceEventKind = (typeof PACK_SERVICE_EVENTS)[number];
39
+
40
+ const EVENT_LEVEL = {
41
+ listening: 'info',
42
+ 'boot-failed': 'error',
43
+ 'config-invalid': 'error',
44
+ draining: 'info',
45
+ stopped: 'info',
46
+ } as const satisfies Record<PackServiceEventKind, 'info' | 'error'>;
47
+
48
+ export interface PackServiceLogs {
49
+ /** Calls, and `ctx.log` beneath them; the levels apply. */
50
+ readonly log: Logger;
51
+ /** A lifecycle event, written whatever the levels. */
52
+ event(kind: PackServiceEventKind, message: string, fields?: LogFields): void;
53
+ }
54
+
55
+ export type PackServiceLogsOutcome =
56
+ | { readonly kind: 'ok'; readonly logs: PackServiceLogs; readonly problems: readonly string[] }
57
+ | { readonly kind: 'err'; readonly message: string };
58
+
59
+ /** The pack service's logs from `KINDGI_LOG_*`; a bad setting is an error naming it. */
60
+ export function packServiceLogs(input: {
61
+ readonly env: Readonly<Record<string, string | undefined>>;
62
+ /** One line, no newline. Default stderr. */
63
+ readonly write?: (line: string) => void;
64
+ readonly isTTY?: boolean;
65
+ }): PackServiceLogsOutcome {
66
+ // Dev mode doesn't make the service's records pretty: its supervisor reads them.
67
+ const { KINDGI_DEV: _dev, ...env } = input.env;
68
+ const write = input.write ?? ((line: string) => void process.stderr.write(`${line}\n`));
69
+ const isTTY = input.isTTY ?? false;
70
+ // No unknown-subsystem check: pack code logs under its own names too
71
+ // (`kindgi.log.get_logger("billing")`, its own `@kindgi/log` loggers).
72
+ const built = loggerFromEnv({ env, write, isTTY });
73
+ if (built.kind === 'err') return built;
74
+ const format = resolveLogFormat(env.KINDGI_LOG_FORMAT, { isTTY }) ?? 'json';
75
+ const always = createLogger({
76
+ level: 'trace',
77
+ format,
78
+ color: format === 'pretty' && isTTY && (env.NO_COLOR ?? '') === '',
79
+ write,
80
+ subsystem: 'pack',
81
+ });
82
+ return {
83
+ kind: 'ok',
84
+ problems: built.problems,
85
+ logs: {
86
+ log: built.logger.child({ subsystem: 'pack' }),
87
+ event(kind, message, fields = {}) {
88
+ always[EVENT_LEVEL[kind]](message, { ...fields, event: kind, kind });
89
+ },
90
+ },
91
+ };
92
+ }
93
+
94
+ /** Logs for a caller that passes none: records on stderr, at the defaults. */
95
+ export function defaultPackServiceLogs(): PackServiceLogs {
96
+ const built = packServiceLogs({ env: {}, isTTY: false });
97
+ if (built.kind === 'err') throw new Error(built.message);
98
+ return built.logs;
99
+ }
@@ -29,7 +29,9 @@ import { timingSafeEqual } from 'node:crypto';
29
29
  import type { IncomingMessage, ServerResponse } from 'node:http';
30
30
  import { pathToFileURL } from 'node:url';
31
31
 
32
- import type { HandlerError } from '../handler-runner.js';
32
+ import { type Logger, childSpan, noopLogger, parseTraceparent } from '@kindgi/log';
33
+
34
+ import type { HandlerContext, HandlerError } from '../handler-runner.js';
33
35
  import { runCheck, runHandler } from '../handler-runner.js';
34
36
  import type { Index, IndexedGuardrail, IndexedTool } from '../kindgi-index.js';
35
37
  import { type PackEnvCheck, missingPackEnv } from '../pack-env.js';
@@ -37,6 +39,7 @@ import {
37
39
  type CheckInvokeMessage,
38
40
  PACK_HEADERS,
39
41
  PACK_PROTOCOL_VERSION,
42
+ type PackCallContext,
40
43
  type PackErrorCode,
41
44
  type PackErrorMessage,
42
45
  type PackResponse,
@@ -59,6 +62,12 @@ export interface PackServiceOptions {
59
62
  /** Deadline when a call sends no `kindgi-timeout-ms`. Default 120 s. */
60
63
  readonly defaultTimeoutMs?: number;
61
64
  readonly logger?: (event: PackServiceLogEvent) => void;
65
+ /**
66
+ * Where the service's records go (`@kindgi/log`, subsystem `pack`): a
67
+ * record per call, with the call's ids and trace, and each handler's
68
+ * `ctx.log` beneath it. Default: none. `logger` still gets its events.
69
+ */
70
+ readonly log?: Logger;
62
71
  /**
63
72
  * The process environment the index's `env.required` names are checked
64
73
  * against, once, at creation. Default `process.env`.
@@ -115,6 +124,7 @@ export function createPackService(options: PackServiceOptions): PackService {
115
124
  const maxBodyBytes = options.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES;
116
125
  const defaultTimeoutMs = options.defaultTimeoutMs ?? DEFAULT_TIMEOUT_MS;
117
126
  const logger = options.logger ?? (() => undefined);
127
+ const log = options.log ?? noopLogger;
118
128
  const importModule =
119
129
  options.importModule ?? ((p: string) => import(pathToFileURL(p).href) as Promise<unknown>);
120
130
  const expectedToken = Buffer.from(options.token);
@@ -138,7 +148,13 @@ export function createPackService(options: PackServiceOptions): PackService {
138
148
 
139
149
  const envCheck = options.envCheck ?? 'strict';
140
150
  const missingEnv = missingPackEnv(options.index.env, options.env ?? process.env);
141
- if (missingEnv.length > 0) logger({ kind: 'missing-env', check: envCheck, names: missingEnv });
151
+ if (missingEnv.length > 0) {
152
+ logger({ kind: 'missing-env', check: envCheck, names: missingEnv });
153
+ log.warn(
154
+ `The pack's env.required ${missingEnv.length === 1 ? 'name has' : 'names have'} no value: ${missingEnv.join(', ')}`,
155
+ { event: 'missing-env', kind: 'missing-env', check: envCheck, names: missingEnv },
156
+ );
157
+ }
142
158
 
143
159
  /** Why the service can't take calls now, or `undefined` when it can. */
144
160
  function notReady(): Reply | undefined {
@@ -239,7 +255,11 @@ export function createPackService(options: PackServiceOptions): PackService {
239
255
  };
240
256
  }
241
257
 
242
- function callTool(message: ToolInvokeMessage, signal: AbortSignal): Promise<PackResponse> {
258
+ function callTool(
259
+ message: ToolInvokeMessage,
260
+ signal: AbortSignal,
261
+ callLog: Logger,
262
+ ): Promise<PackResponse> {
243
263
  const found = toolFor(message.tool);
244
264
  if ('refused' in found) return Promise.resolve(found.refused);
245
265
  const { tool } = found;
@@ -251,7 +271,7 @@ export function createPackService(options: PackServiceOptions): PackService {
251
271
  outputSchema: tool.output,
252
272
  },
253
273
  input: message.input,
254
- ctx: { ...message.ctx, abortSignal: signal },
274
+ ctx: handlerContext(message.ctx, signal, callLog),
255
275
  importHandler: (p) => importModule(p) as never,
256
276
  }).then((r) =>
257
277
  r.kind === 'ok'
@@ -270,7 +290,11 @@ export function createPackService(options: PackServiceOptions): PackService {
270
290
  );
271
291
  }
272
292
  return runCheck({
273
- check: { id: message.check.id, modulePath: options.resolveModule(guardrail.checkModulePath) },
293
+ check: {
294
+ id: message.check.id,
295
+ modulePath: options.resolveModule(guardrail.checkModulePath),
296
+ ...(guardrail.configSchema !== undefined && { configSchema: guardrail.configSchema }),
297
+ },
274
298
  config: message.config,
275
299
  trace: message.trace,
276
300
  abortSignal: signal,
@@ -349,24 +373,38 @@ export function createPackService(options: PackServiceOptions): PackService {
349
373
  });
350
374
 
351
375
  const started = Date.now();
376
+ // The call's ids and trace, on its record and on the handler's `ctx.log`.
377
+ const call = log.child({
378
+ ...callIds(message.kind === 'invoke' ? message.ctx : undefined),
379
+ ...callTrace(req.headers[PACK_HEADERS.traceparent]),
380
+ target,
381
+ ...(message.kind === 'invoke' ? { toolId: id } : { checkId: id }),
382
+ });
352
383
  const work =
353
384
  message.kind === 'invoke'
354
- ? callTool(message, controller.signal)
385
+ ? callTool(message, controller.signal, call.child({ subsystem: `pack.${target}` }))
355
386
  : callCheck(message, controller.signal);
356
387
  const outcome = await Promise.race([work, aborted(controller.signal, id, target)]);
357
388
  clearTimeout(timer);
358
389
  if (controller.signal.aborted) {
359
- void work.then(() =>
360
- logger({ kind: 'handler-finished-late', target, id, afterMs: Date.now() - started }),
361
- );
390
+ void work.then(() => {
391
+ const afterMs = Date.now() - started;
392
+ logger({ kind: 'handler-finished-late', target, id, afterMs });
393
+ call.warn(`${target} ${id} finished ${afterMs} ms after its call ended`, {
394
+ event: 'handler-finished-late',
395
+ kind: 'handler-finished-late',
396
+ afterMs,
397
+ });
398
+ });
362
399
  }
363
- logger({
364
- kind: 'call',
365
- target,
366
- id,
367
- durationMs: Date.now() - started,
368
- outcome: outcome.kind === 'error' ? outcome.code : 'ok',
369
- });
400
+ const durationMs = Date.now() - started;
401
+ const result = outcome.kind === 'error' ? outcome.code : 'ok';
402
+ logger({ kind: 'call', target, id, durationMs, outcome: result });
403
+ call[result === 'ok' ? 'info' : 'warn'](
404
+ `${target} ${id} ${result} ${durationMs}ms`,
405
+ { event: 'call', kind: 'call', id, outcome: result, durationMs },
406
+ { inMessage: ['target', 'id', 'outcome', 'durationMs'] },
407
+ );
370
408
  return outcome;
371
409
  }
372
410
 
@@ -509,3 +547,45 @@ function describe(cause: unknown): string {
509
547
  function toolKey(id: string, version: string | undefined): string {
510
548
  return version === undefined ? id : `${id}@${version}`;
511
549
  }
550
+
551
+ /**
552
+ * The context a handler gets: the call's own (`PackCallContext`) and its
553
+ * abort signal, with `log` and `secrets` there but not enumerable, so
554
+ * printing, spreading or serializing a context never shows a secret.
555
+ */
556
+ function handlerContext(wire: PackCallContext, signal: AbortSignal, log: Logger): HandlerContext {
557
+ const { secrets, ...rest } = wire;
558
+ const ctx = { ...rest, abortSignal: signal };
559
+ if (secrets !== undefined) {
560
+ Object.defineProperty(ctx, 'secrets', { value: secrets, enumerable: false });
561
+ }
562
+ Object.defineProperty(ctx, 'log', { value: log, enumerable: false });
563
+ return ctx as HandlerContext;
564
+ }
565
+
566
+ /** The ids a call's records carry, from its context. */
567
+ function callIds(ctx: PackCallContext | undefined): Record<string, string> {
568
+ if (ctx === undefined) return {};
569
+ const ids: Record<string, string> = {};
570
+ for (const key of ['tenantId', 'projectId', 'orgId', 'runId', 'requestId'] as const) {
571
+ const value: unknown = ctx[key];
572
+ if (typeof value === 'string' && value !== '') ids[key] = value;
573
+ }
574
+ return ids;
575
+ }
576
+
577
+ /**
578
+ * The call's trace: the caller's trace id, and a span of this call's own
579
+ * whose parent is the caller's. None when the caller sent no
580
+ * `traceparent` (an older runtime): a fresh trace would join nothing.
581
+ */
582
+ function callTrace(header: string | string[] | undefined): Record<string, string> {
583
+ const incoming = parseTraceparent(Array.isArray(header) ? header[0] : header);
584
+ if (incoming === undefined) return {};
585
+ const span = childSpan({
586
+ traceId: incoming.traceId,
587
+ spanId: incoming.parentSpanId,
588
+ flags: incoming.flags,
589
+ });
590
+ return { traceId: span.traceId, spanId: span.spanId };
591
+ }
@@ -50,6 +50,7 @@ export interface PackServiceSupervisorOptions {
50
50
  *
51
51
  * - Node: `[process.execPath, '--enable-source-maps', <@kindgi/handler-runtime/pack-service-main>]`
52
52
  * - Python: `[<the pack's python>, '-m', 'kindgi.pack', 'serve']`
53
+ * - Java and Scala: `['sh', <kindgi-pack-java>, '-cp', <classpath> (or `@<argfile>`), 'com.kindgi.pack.Main', 'serve']`
53
54
  */
54
55
  readonly command: readonly [string, ...string[]];
55
56
  /** Where the index's module paths resolve — the pack directory. */
@@ -102,8 +103,16 @@ export type PackServiceSupervisorEvent =
102
103
  | { readonly kind: 'exited'; readonly code: number | null; readonly signal: string | null }
103
104
  | { readonly kind: 'restarting'; readonly attempt: number }
104
105
  | { readonly kind: 'gave-up'; readonly attempts: number }
105
- /** One of the service's own JSON log lines (a call, draining, …). */
106
- | { readonly kind: 'log'; readonly event: Readonly<Record<string, unknown>> };
106
+ /**
107
+ * A record the service wrote (a call, draining, an author's `ctx.log`
108
+ * line, …), or a bare event from an older service: `event` as
109
+ * `serviceEvent` reads it, `line` as written.
110
+ */
111
+ | {
112
+ readonly kind: 'log';
113
+ readonly event: Readonly<Record<string, unknown>>;
114
+ readonly line: string;
115
+ };
107
116
 
108
117
  export interface BootFailure {
109
118
  readonly problems: readonly string[];
@@ -122,6 +131,8 @@ export interface PackRelayCall {
122
131
  readonly requestId?: string;
123
132
  /** The caller's protocol version (`kindgi-protocol`), passed on as is. */
124
133
  readonly protocol?: string;
134
+ /** The caller's W3C trace context (`traceparent`), passed on as is, so the child's records carry its trace. */
135
+ readonly traceparent?: string;
125
136
  }
126
137
 
127
138
  /**
@@ -223,7 +234,13 @@ export function createPackServiceSupervisor(
223
234
  let front: { readonly server: Server; readonly url: string; readonly port: number } | undefined;
224
235
 
225
236
  async function boot(indexPath: string): Promise<Result<Child, BootFailure>> {
226
- const env = { ...(await options.env()), KINDGI_PACK_SERVICE_TOKEN: token, PORT: '0' };
237
+ // JSON whatever the pack's env says: this process reads the child's records.
238
+ const env = {
239
+ ...(await options.env()),
240
+ KINDGI_PACK_SERVICE_TOKEN: token,
241
+ PORT: '0',
242
+ KINDGI_LOG_FORMAT: 'json',
243
+ };
227
244
  const [program, ...args] = options.command;
228
245
  const child = spawn(
229
246
  program,
@@ -263,7 +280,7 @@ export function createPackServiceSupervisor(
263
280
  } else if (event.kind === 'boot-failed' || event.kind === 'config-invalid') {
264
281
  problems.push(...problemsOf(event));
265
282
  } else {
266
- emit({ kind: 'log', event });
283
+ emit({ kind: 'log', event, line });
267
284
  }
268
285
  });
269
286
  child.once('error', (cause) => fail([cause.message]));
@@ -485,6 +502,7 @@ function childHeaders(token: string, call: PackRelayCall): Record<string, string
485
502
  ...(call.runId !== undefined && { [PACK_HEADERS.runId]: call.runId }),
486
503
  ...(call.requestId !== undefined && { [PACK_HEADERS.requestId]: call.requestId }),
487
504
  ...(call.protocol !== undefined && { [PACK_HEADERS.protocol]: call.protocol }),
505
+ ...(call.traceparent !== undefined && { [PACK_HEADERS.traceparent]: call.traceparent }),
488
506
  };
489
507
  }
490
508
 
@@ -518,6 +536,7 @@ function relayCall(
518
536
  const runId = header(req, PACK_HEADERS.runId);
519
537
  const requestId = header(req, PACK_HEADERS.requestId);
520
538
  const protocol = header(req, PACK_HEADERS.protocol);
539
+ const traceparent = header(req, PACK_HEADERS.traceparent);
521
540
  return {
522
541
  route,
523
542
  signal,
@@ -526,6 +545,7 @@ function relayCall(
526
545
  ...(runId !== undefined && { runId }),
527
546
  ...(requestId !== undefined && { requestId }),
528
547
  ...(protocol !== undefined && { protocol }),
548
+ ...(traceparent !== undefined && { traceparent }),
529
549
  };
530
550
  }
531
551
 
@@ -620,21 +640,58 @@ function problemsOf(event: Readonly<Record<string, unknown>>): readonly string[]
620
640
  return Array.isArray(event.problems) ? event.problems.map(String) : [];
621
641
  }
622
642
 
623
- /** The service's own log lines are JSON objects with a `kind`. */
624
- function serviceEvent(
643
+ /** What a pack service from before records wrote as bare `{"kind": …}` lines. */
644
+ const LEGACY_EVENTS: ReadonlySet<string> = new Set([
645
+ 'listening',
646
+ 'boot-failed',
647
+ 'config-invalid',
648
+ 'missing-env',
649
+ 'draining',
650
+ 'stopped',
651
+ 'call',
652
+ 'handler-finished-late',
653
+ ]);
654
+
655
+ /**
656
+ * One of the service's own log lines, as an event with a `kind`:
657
+ *
658
+ * - a record (`@kindgi/log`: `time`, `level`, `subsystem`, `message`).
659
+ * The service's own, subsystem `pack`, carry their event as `event`
660
+ * (`listening`, `call`, …), which becomes `kind`. Any other record, an
661
+ * author's `ctx.log` (`pack.tool`) say, comes back with `kind: 'record'`,
662
+ * to show, never to act on;
663
+ * - a bare `{"kind": …}` line from a pack service from before records,
664
+ * for the events it wrote. Any other JSON line is the pack's own
665
+ * output, shown as it is.
666
+ */
667
+ export function serviceEvent(
625
668
  line: string,
626
669
  ): (Record<string, unknown> & { readonly kind: string }) | undefined {
627
670
  if (!line.startsWith('{')) return undefined;
671
+ let parsed: unknown;
628
672
  try {
629
- const parsed = JSON.parse(line) as unknown;
630
- return parsed !== null &&
631
- typeof parsed === 'object' &&
632
- typeof (parsed as { kind?: unknown }).kind === 'string'
633
- ? (parsed as Record<string, unknown> & { readonly kind: string })
634
- : undefined;
673
+ parsed = JSON.parse(line);
635
674
  } catch {
636
675
  return undefined;
637
676
  }
677
+ if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) return undefined;
678
+ const fields = parsed as Record<string, unknown>;
679
+ if (isRecord(fields)) {
680
+ const own = fields.subsystem === 'pack' && typeof fields.event === 'string';
681
+ return { ...fields, kind: own ? (fields.event as string) : 'record' };
682
+ }
683
+ return typeof fields.kind === 'string' && LEGACY_EVENTS.has(fields.kind)
684
+ ? (fields as Record<string, unknown> & { readonly kind: string })
685
+ : undefined;
686
+ }
687
+
688
+ function isRecord(fields: Record<string, unknown>): boolean {
689
+ return (
690
+ typeof fields.time === 'string' &&
691
+ typeof fields.level === 'string' &&
692
+ typeof fields.subsystem === 'string' &&
693
+ typeof fields.message === 'string'
694
+ );
638
695
  }
639
696
 
640
697
  function describe(cause: unknown): string {
package/src/protocol.ts CHANGED
@@ -32,8 +32,13 @@ export interface PackCallContext {
32
32
  readonly orgId?: string;
33
33
  /** The individual call — e.g. the model's tool-call id. */
34
34
  readonly requestId?: string;
35
+ /**
36
+ * The env values the tool declares (`needsSpec.env`), resolved for this
37
+ * call: project, else org, else tenant (protocol 2.5.0). Strings.
38
+ */
35
39
  readonly env?: Readonly<Record<string, unknown>>;
36
40
  readonly secrets?: Readonly<Record<string, unknown>>;
41
+ /** Reserved: no runtime sends it yet. */
37
42
  readonly config?: Readonly<Record<string, unknown>>;
38
43
  /** The calling agent version's settings blocks' values, by block id (protocol 2.4.0). */
39
44
  readonly settings?: Readonly<Record<string, Readonly<Record<string, unknown>>>>;
@@ -219,8 +224,8 @@ export const PACK_HEADERS = {
219
224
  durationMs: 'kindgi-duration-ms',
220
225
  /**
221
226
  * W3C Trace Context: the run's trace, so a pack service's records carry
222
- * the same `traceId` as the runtime's. Sent by the runtime's transport;
223
- * a pack service that doesn't read it ignores it.
227
+ * the same `traceId` as the runtime's. Optional: runtimes from 0.1.5 send
228
+ * it on every call; a pack service that doesn't read it ignores it.
224
229
  */
225
230
  traceparent: 'traceparent',
226
231
  } as const;