@xemahq/agent-session-runtime 0.1.1 → 0.3.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.
@@ -31,13 +31,19 @@ import {
31
31
  ManifestSurface,
32
32
  OutputSurfaceKind,
33
33
  type AgentRunRole,
34
+ type CompiledManifestCredential,
35
+ type CompiledManifestPermissions,
36
+ type ModelRef,
34
37
  } from '@xemahq/kernel-contracts/workflow';
38
+ import type { PermissionMap } from '@xemahq/kernel-contracts/agent-permission';
35
39
  import {
36
40
  compileManifest,
37
41
  type CompiledWorkspaceManifest,
42
+ type ManifestCredential,
38
43
  type ManifestInputDeclaration,
39
44
  type ManifestMountsBlock,
40
45
  type ManifestOutputSurface,
46
+ type ManifestPermissions,
41
47
  type ManifestSeedFile,
42
48
  type ManifestSubAgent,
43
49
  type WorkspaceManifest as DslWorkspaceManifest,
@@ -104,10 +110,23 @@ export interface CompositionMountLayoutInput {
104
110
  /**
105
111
  * A single manifest-declared sub-agent binding the composition resolved.
106
112
  * Mirrors the binding subset the manifest DSL's `ManifestSubAgent` needs.
113
+ *
114
+ * Carries the {@link NodeOverlayFragment} overlay trio (`modelOverride` /
115
+ * `instructions` / `permission`) so a resolved sub-agent's per-node
116
+ * specialization survives the manifest reconstruction. Without these the
117
+ * worker silently DROPPED a delegate's `modelOverride` (the root fix for the
118
+ * worker drift). All optional ⇒ backward-compatible with callers that only
119
+ * supply `slug` / `alias`.
107
120
  */
108
121
  export interface CompositionSubAgentBindingInput {
109
122
  readonly slug: string;
110
123
  readonly alias?: string;
124
+ /** Per-delegate model override — OVERRIDE on the referenced agent's model. */
125
+ readonly modelOverride?: ModelRef;
126
+ /** Per-delegate instructions — APPENDED to the referenced agent's prompt. */
127
+ readonly instructions?: string;
128
+ /** Per-delegate permission — RESTRICT-merged over the agent's permission. */
129
+ readonly permission?: PermissionMap;
111
130
  }
112
131
 
113
132
  /**
@@ -160,6 +179,32 @@ export interface CompositionManifestSource {
160
179
  * `{ kind: 'none' }` default.
161
180
  */
162
181
  readonly outputSurface?: CompositionOutputSurfaceInput;
182
+ /**
183
+ * The composition's `workspace.credentials` block. Threaded into the
184
+ * reconstructed manifest's `spec.credentials` so the compiled manifest's
185
+ * `credentials` matches the source-of-truth — the EnvironmentResolver
186
+ * reads the same `{name, kind, sourceRef, required}` entries the
187
+ * authored composition declared (BUG-1 carry-through: authored
188
+ * `spec.credentials` were silently dropped on reconstruction). Carried
189
+ * as the kernel `CompiledManifestCredential[]` because that is the shape
190
+ * `composition.workspace.credentials` holds (the seeder projects the
191
+ * authored entries through the manifest compiler). `undefined` when the
192
+ * composition declared none — the DSL compile then yields `[]`.
193
+ */
194
+ readonly credentials?: readonly CompiledManifestCredential[];
195
+ /**
196
+ * The composition's `workspace.permissions` block. Threaded into the
197
+ * reconstructed manifest's `spec.permissions` so the compiled manifest's
198
+ * `permissions` matches the source-of-truth — the EnvironmentResolver
199
+ * reads the same `{tools: {allow, deny}}` the authored composition
200
+ * declared (BUG-1 carry-through: authored `spec.permissions` were
201
+ * silently dropped on reconstruction). Carried as the kernel
202
+ * `CompiledManifestPermissions` because that is the shape
203
+ * `composition.workspace.permissions` holds. `undefined` when the
204
+ * composition declared none — the DSL compile then yields
205
+ * `{ tools: { allow: [], deny: [] } }`.
206
+ */
207
+ readonly permissions?: CompiledManifestPermissions;
163
208
  }
164
209
 
165
210
  /**
@@ -227,6 +272,20 @@ export function compileCompositionWorkspaceManifest(
227
272
  (binding) => ({
228
273
  slug: binding.slug,
229
274
  ...(binding.alias !== undefined ? { alias: binding.alias } : {}),
275
+ // Pass the overlay trio through to the manifest agent block so a
276
+ // resolved delegate's per-node `modelOverride` / `instructions` /
277
+ // `permission` are NOT dropped on reconstruction (the worker bug).
278
+ // `ManifestSubAgent.defaultModel` is the manifest DSL's name for the
279
+ // kernel `modelOverride` slot.
280
+ ...(binding.modelOverride !== undefined
281
+ ? { defaultModel: binding.modelOverride }
282
+ : {}),
283
+ ...(binding.instructions !== undefined
284
+ ? { instructions: binding.instructions }
285
+ : {}),
286
+ ...(binding.permission !== undefined
287
+ ? { permission: binding.permission }
288
+ : {}),
230
289
  }),
231
290
  );
232
291
 
@@ -236,6 +295,8 @@ export function compileCompositionWorkspaceManifest(
236
295
  // `WorkspaceManifestSchema`, so the casts here are checked downstream,
237
296
  // not silently trusted.
238
297
  const outputSurface = projectOutputSurface(source.outputSurface);
298
+ const credentials = projectCredentials(source.credentials);
299
+ const permissions = projectPermissions(source.permissions);
239
300
  const spec: WorkspaceManifestSpec = {
240
301
  mounts: mountLayout.mounts as unknown as ManifestMountsBlock,
241
302
  agent: {
@@ -262,6 +323,8 @@ export function compileCompositionWorkspaceManifest(
262
323
  }
263
324
  : {}),
264
325
  ...(outputSurface === undefined ? {} : { outputSurface }),
326
+ ...(credentials === undefined ? {} : { credentials }),
327
+ ...(permissions === undefined ? {} : { permissions }),
265
328
  };
266
329
 
267
330
  const envelope: DslWorkspaceManifest = {
@@ -309,7 +372,9 @@ function projectOutputSurface(
309
372
  return {
310
373
  kind: surface.kind as OutputSurfaceKind,
311
374
  ...(surface.port === undefined ? {} : { port: surface.port }),
312
- ...(surface.healthPath === undefined ? {} : { healthPath: surface.healthPath }),
375
+ ...(surface.healthPath === undefined
376
+ ? {}
377
+ : { healthPath: surface.healthPath }),
313
378
  ...(surface.autoOpen === undefined ? {} : { autoOpen: surface.autoOpen }),
314
379
  ...(surface.mode === undefined ? {} : { mode: surface.mode }),
315
380
  ...(surface.root === undefined ? {} : { root: surface.root }),
@@ -319,6 +384,66 @@ function projectOutputSurface(
319
384
  };
320
385
  }
321
386
 
387
+ /**
388
+ * Project the composition's `workspace.credentials` block (carried on the
389
+ * runtime-agnostic `CompositionManifestSource` as the kernel
390
+ * `CompiledManifestCredential[]`) into the manifest DSL's authored
391
+ * `ManifestCredential[]` shape so the reconstructed envelope's
392
+ * `spec.credentials` carries the same entries as the source.
393
+ *
394
+ * The compiled shape is a structural superset of the authored shape
395
+ * (`{name, kind, sourceRef}` plus a now-required `required` boolean), so the
396
+ * projection is a 1:1 field copy — `compileManifest`'s Zod schema below
397
+ * re-validates each entry and re-defaults `required` idempotently. The map
398
+ * is explicit (not a pass-through cast) so a malformed entry fails fast at
399
+ * the manifest compile boundary, mirroring {@link projectOutputSurface}.
400
+ *
401
+ * Returns `undefined` when the composition declared no credentials — the DSL
402
+ * `compileManifest` then defaults the compiled `credentials` to `[]`.
403
+ */
404
+ function projectCredentials(
405
+ credentials: readonly CompiledManifestCredential[] | undefined,
406
+ ): readonly ManifestCredential[] | undefined {
407
+ if (credentials === undefined) return undefined;
408
+ return credentials.map((credential) => ({
409
+ name: credential.name,
410
+ kind: credential.kind,
411
+ sourceRef: credential.sourceRef,
412
+ required: credential.required,
413
+ }));
414
+ }
415
+
416
+ /**
417
+ * Project the composition's `workspace.permissions` block (carried on the
418
+ * runtime-agnostic `CompositionManifestSource` as the kernel
419
+ * `CompiledManifestPermissions`) into the manifest DSL's authored
420
+ * `ManifestPermissions` shape so the reconstructed envelope's
421
+ * `spec.permissions` carries the same tool allow/deny lists as the source.
422
+ *
423
+ * The compiled shape is a structural superset of the authored shape (the
424
+ * compiled `tools.allow` / `tools.deny` are always present; the authored
425
+ * ones are optional), so the projection is a 1:1 field copy —
426
+ * `compileManifest`'s Zod schema below re-validates and re-defaults the
427
+ * lists idempotently. Explicit (not a pass-through cast) so a malformed
428
+ * block fails fast at the manifest compile boundary, mirroring
429
+ * {@link projectOutputSurface}.
430
+ *
431
+ * Returns `undefined` when the composition declared no permissions — the DSL
432
+ * `compileManifest` then defaults the compiled `permissions` to
433
+ * `{ tools: { allow: [], deny: [] } }`.
434
+ */
435
+ function projectPermissions(
436
+ permissions: CompiledManifestPermissions | undefined,
437
+ ): ManifestPermissions | undefined {
438
+ if (permissions === undefined) return undefined;
439
+ return {
440
+ tools: {
441
+ allow: permissions.tools.allow,
442
+ deny: permissions.tools.deny,
443
+ },
444
+ };
445
+ }
446
+
322
447
  /**
323
448
  * Build the manifest bind-input bag from the caller's explicit input bag
324
449
  * and the implicit inputs, validated against the composition's declared