@agent-surface/core 0.1.0 → 0.2.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.
package/dist/index.d.ts CHANGED
@@ -17,6 +17,35 @@ interface AgentRouteInfo {
17
17
  path: string;
18
18
  params?: Record<string, string>;
19
19
  }
20
+ /**
21
+ * Concurrency group for an action or procedure reference (D25). Not
22
+ * model-visible: it is runtime behavior, not planning information.
23
+ *
24
+ * - `instance` (default) — every action on the registration shares one FIFO
25
+ * queue. Safest: two actions on the same component can never interleave.
26
+ * - `capability` — one queue per capability, so a slow export does not block
27
+ * closing a drawer.
28
+ * - `key` — one queue per author-chosen key, for actions that contend over
29
+ * the same resource across capabilities.
30
+ * - `parallel` — bounded parallelism; `max` is required and must be ≥ 1.
31
+ *
32
+ * `queueDepth` overrides `limits.actionQueueDepth` for this group only.
33
+ */
34
+ type AgentConcurrency = {
35
+ mode: "instance";
36
+ queueDepth?: number;
37
+ } | {
38
+ mode: "capability";
39
+ queueDepth?: number;
40
+ } | {
41
+ mode: "key";
42
+ key: string;
43
+ queueDepth?: number;
44
+ } | {
45
+ mode: "parallel";
46
+ max: number;
47
+ queueDepth?: number;
48
+ };
20
49
  interface AgentSurfaceLimits {
21
50
  maxComponentDescription: number;
22
51
  maxCapabilityDescription: number;
@@ -423,6 +452,9 @@ interface AgentActionDefinition<TIn extends JsonValue, TOut extends JsonValue |
423
452
  policies?: AgentPolicy[];
424
453
  meta?: Record<string, JsonValue>;
425
454
  timeoutMs?: number;
455
+ /** Concurrency group (D25). Default `{mode:"instance"}` — serialize with
456
+ * every other action on this component instance. */
457
+ concurrency?: AgentConcurrency;
426
458
  }
427
459
  /** Identity helpers that fix generics for record-literal authoring. */
428
460
  declare function observation<TOut extends JsonValue>(def: AgentObservationDefinition<TOut>): AgentObservationDefinition<TOut>;
@@ -468,6 +500,10 @@ interface AgentProcedureBindingRuntimeConfig {
468
500
  /** Contextual description appended to the manifest description. */
469
501
  describe?: () => string;
470
502
  meta?: Record<string, JsonValue>;
503
+ /** Concurrency group (D25). Default: one group per procedure identity per
504
+ * referencing registration — conservative, and it never couples a domain
505
+ * call to unrelated view actions. */
506
+ concurrency?: AgentConcurrency;
471
507
  }
472
508
  interface AgentProcedureBinding<TIn extends object = object, TOut = unknown> {
473
509
  readonly kind: "procedure-binding";
@@ -771,7 +807,23 @@ interface AgentToolsetOptions {
771
807
  * its transport-timeout story, docs/09 §confirmation-topology).
772
808
  */
773
809
  confirmations?: "wait" | "two-phase";
810
+ /**
811
+ * Component-type prefixes this consumer may discover. D27: this is a
812
+ * **floor** — in "meta" mode a model-supplied `scope` can only narrow it
813
+ * further, never widen it. Not an authority boundary: `invoke` does not
814
+ * check scope in either mode (docs/09 §scope-is-discovery-only).
815
+ */
774
816
  scope?: string[];
817
+ /**
818
+ * [Experimental] Snapshot truncation budget for `surface_discover`.
819
+ * "meta" mode only — there the `truncated` marker rides in the payload the
820
+ * model reads. In "direct" mode a budget would silently drop tools with no
821
+ * signal to anyone, so it is rejected rather than half-honored.
822
+ */
823
+ budget?: {
824
+ maxComponents?: number;
825
+ maxBytes?: number;
826
+ };
775
827
  }
776
828
  interface AgentTool {
777
829
  /** Wire-safe name (docs/09 §wire-names), ≤ 64 chars. */
@@ -793,4 +845,4 @@ declare function createAgentToolset(registry: AgentSurfaceRegistry, options: Age
793
845
  /** Deep equality over JsonValue (order-sensitive for arrays, docs/06 rule 2). */
794
846
  declare function jsonDeepEqual(a: JsonValue | undefined, b: JsonValue | undefined): boolean;
795
847
 
796
- export { AGENT_CAPABILITY_ERROR_CODES, type AgentActionContext, type AgentActionDefinition, type AgentActionDescriptor, type AgentAuthorizationContext, type AgentCapabilityDescriptorUnion, type AgentCapabilityErrorCode, type AgentCapabilityErrorPayload, type AgentComponentDefinition, type AgentComponentDescriptor, type AgentConsumer, type AgentEffect, type AgentEnvironment, type AgentErrorRetry, type AgentInvocation, type AgentInvocationPolicyContext, type AgentInvocationResult, type AgentObservationDefinition, type AgentObservationDescriptor, type AgentPlane, type AgentPolicy, type AgentPolicyContext, type AgentProcedureBinding, type AgentProcedureBindingRuntimeConfig, type AgentProcedureDescriptor, type AgentProcedureEffect, type AgentProcedureExecutor, type AgentProcedureRefDescriptor, type AgentReadContext, type AgentRegistrationHandle, type AgentRouteInfo, type AgentSchema, AgentSchemaError, type AgentSchemaIssue, AgentSurfaceDefinitionError, type AgentSurfaceDefinitionErrorCode, AgentSurfaceError, type AgentSurfaceEvent, type AgentSurfaceLimits, type AgentSurfaceRegistry, type AgentSurfaceSnapshot, type AgentTool, type AgentToolset, type AgentToolsetOptions, type AuditEvent, type AuditSink, CONFIRMATION_ESCALATION, type ConfirmationController, type ConfirmationEscalation, DEFAULT_LIMITS, type DiscoveryDecision, type InvokeOptions, type JsonSchema, type JsonValue, MAX_ID_LENGTH, MAX_WIRE_NAME_LENGTH, type ParsedCapabilityId, type PendingConfirmation, type PreconditionFailure, type ProcedureCallInfo, type RegistrationCandidate, type RegistryOptions, type SnapshotContext, type StandardSchemaV1, type Unsubscribe, action, audit, authenticated, composeInvokeChain, consoleAuditSink, createAgentSurfaceRegistry, createAgentToolset, decodeWireName, defineAgentComponent, emptyObjectSchema, encodeWireName, encodeWireNameForInstance, environment, evaluateDiscovery, formatDomainCapabilityId, formatViewCapabilityId, fromJsonSchema, fromStandardSchema, hasPermission, isAgentSurfaceError, isValidCapabilityName, isValidComponentType, isValidInstanceId, jsonDeepEqual, memoryAuditSink, observation, parseCapabilityId, rateLimit, requireConfirmation, tenantBoundary, validateComponentDefinition, validateJsonSchemaDocument, validateValueAgainstSchema };
848
+ export { AGENT_CAPABILITY_ERROR_CODES, type AgentActionContext, type AgentActionDefinition, type AgentActionDescriptor, type AgentAuthorizationContext, type AgentCapabilityDescriptorUnion, type AgentCapabilityErrorCode, type AgentCapabilityErrorPayload, type AgentComponentDefinition, type AgentComponentDescriptor, type AgentConcurrency, type AgentConsumer, type AgentEffect, type AgentEnvironment, type AgentErrorRetry, type AgentInvocation, type AgentInvocationPolicyContext, type AgentInvocationResult, type AgentObservationDefinition, type AgentObservationDescriptor, type AgentPlane, type AgentPolicy, type AgentPolicyContext, type AgentProcedureBinding, type AgentProcedureBindingRuntimeConfig, type AgentProcedureDescriptor, type AgentProcedureEffect, type AgentProcedureExecutor, type AgentProcedureRefDescriptor, type AgentReadContext, type AgentRegistrationHandle, type AgentRouteInfo, type AgentSchema, AgentSchemaError, type AgentSchemaIssue, AgentSurfaceDefinitionError, type AgentSurfaceDefinitionErrorCode, AgentSurfaceError, type AgentSurfaceEvent, type AgentSurfaceLimits, type AgentSurfaceRegistry, type AgentSurfaceSnapshot, type AgentTool, type AgentToolset, type AgentToolsetOptions, type AuditEvent, type AuditSink, CONFIRMATION_ESCALATION, type ConfirmationController, type ConfirmationEscalation, DEFAULT_LIMITS, type DiscoveryDecision, type InvokeOptions, type JsonSchema, type JsonValue, MAX_ID_LENGTH, MAX_WIRE_NAME_LENGTH, type ParsedCapabilityId, type PendingConfirmation, type PreconditionFailure, type ProcedureCallInfo, type RegistrationCandidate, type RegistryOptions, type SnapshotContext, type StandardSchemaV1, type Unsubscribe, action, audit, authenticated, composeInvokeChain, consoleAuditSink, createAgentSurfaceRegistry, createAgentToolset, decodeWireName, defineAgentComponent, emptyObjectSchema, encodeWireName, encodeWireNameForInstance, environment, evaluateDiscovery, formatDomainCapabilityId, formatViewCapabilityId, fromJsonSchema, fromStandardSchema, hasPermission, isAgentSurfaceError, isValidCapabilityName, isValidComponentType, isValidInstanceId, jsonDeepEqual, memoryAuditSink, observation, parseCapabilityId, rateLimit, requireConfirmation, tenantBoundary, validateComponentDefinition, validateJsonSchemaDocument, validateValueAgainstSchema };
package/dist/index.js CHANGED
@@ -645,7 +645,8 @@ var ACTION_KEYS = /* @__PURE__ */ new Set([
645
645
  "execute",
646
646
  "policies",
647
647
  "meta",
648
- "timeoutMs"
648
+ "timeoutMs",
649
+ "concurrency"
649
650
  ]);
650
651
  var VIEW_EFFECTS = /* @__PURE__ */ new Set(["local-state", "navigation"]);
651
652
  var SERVER_EFFECTS = /* @__PURE__ */ new Set([
@@ -666,6 +667,29 @@ function checkMeta(meta, where, limits) {
666
667
  fail("LIMIT_EXCEEDED", `${where}: meta exceeds ${limits.maxMetaBytes} bytes`);
667
668
  }
668
669
  }
670
+ function checkConcurrency(concurrency, where) {
671
+ if (concurrency === void 0) return;
672
+ if (typeof concurrency !== "object" || concurrency === null) {
673
+ fail("INVALID_DEFINITION", `${where}: concurrency must be an object`);
674
+ }
675
+ const { mode } = concurrency;
676
+ if (!["instance", "capability", "key", "parallel"].includes(mode)) {
677
+ fail("INVALID_DEFINITION", `${where}: invalid concurrency mode "${String(mode)}"`);
678
+ }
679
+ if (mode === "key" && (typeof concurrency.key !== "string" || concurrency.key.length === 0)) {
680
+ fail("INVALID_DEFINITION", `${where}: concurrency mode "key" requires a non-empty key`);
681
+ }
682
+ if (mode === "parallel" && (typeof concurrency.max !== "number" || !Number.isInteger(concurrency.max) || concurrency.max < 1)) {
683
+ fail(
684
+ "INVALID_DEFINITION",
685
+ `${where}: concurrency mode "parallel" requires an integer max \u2265 1 (unbounded parallelism is not offered)`
686
+ );
687
+ }
688
+ const depth = concurrency.queueDepth;
689
+ if (depth !== void 0 && (!Number.isInteger(depth) || depth < 0)) {
690
+ fail("INVALID_DEFINITION", `${where}: concurrency queueDepth must be a non-negative integer`);
691
+ }
692
+ }
669
693
  function checkSchema(schema, where, limits) {
670
694
  if (schema === void 0) return;
671
695
  if (typeof schema !== "object" || schema === null || typeof schema.parse !== "function" || typeof schema.jsonSchema !== "object") {
@@ -774,6 +798,7 @@ function validateComponentDefinition(def, limits, opts) {
774
798
  checkSchema(act.input, `${where} input`, limits);
775
799
  checkSchema(act.output, `${where} output`, limits);
776
800
  checkMeta(act.meta, where, limits);
801
+ checkConcurrency(act.concurrency, where);
777
802
  }
778
803
  const procedures = def.procedures ?? [];
779
804
  if (procedures.length > 0 && !opts.hasProcedureExecutor) {
@@ -803,6 +828,7 @@ function validateComponentDefinition(def, limits, opts) {
803
828
  fail("INVALID_DEFINITION", `procedure "${ref.path}": invalid confirmation escalation`);
804
829
  }
805
830
  checkMeta(binding.config.meta, `procedure "${ref.path}"`, limits);
831
+ checkConcurrency(binding.config.concurrency, `procedure "${ref.path}"`);
806
832
  }
807
833
  }
808
834
 
@@ -1354,7 +1380,8 @@ function normalizeRegistration(def, id) {
1354
1380
  auditLevel: act.audit ?? "metadata",
1355
1381
  meta: act.meta ? jsonClone(act.meta) : void 0,
1356
1382
  timeoutMs: act.timeoutMs,
1357
- policies: [...act.policies ?? []]
1383
+ policies: [...act.policies ?? []],
1384
+ concurrency: act.concurrency
1358
1385
  });
1359
1386
  }
1360
1387
  const hasView = observations.size > 0 || actions.size > 0;
@@ -1386,7 +1413,8 @@ function normalizeRegistration(def, id) {
1386
1413
  auditLevel: defaultAuditFor(effect),
1387
1414
  meta: binding.config.meta ? jsonClone(binding.config.meta) : void 0,
1388
1415
  policies: [...binding.config.policies ?? []],
1389
- contextLink: binding.contextLink ?? (hasView ? { type: def.type, instanceId } : void 0)
1416
+ contextLink: binding.contextLink ?? (hasView ? { type: def.type, instanceId } : void 0),
1417
+ concurrency: binding.config.concurrency
1390
1418
  };
1391
1419
  });
1392
1420
  return {
@@ -1410,9 +1438,27 @@ function normalizeRegistration(def, id) {
1410
1438
  enabled: def.enabled !== false,
1411
1439
  availabilityOverrides: /* @__PURE__ */ new Map(),
1412
1440
  inFlight: /* @__PURE__ */ new Set(),
1413
- actionQueue: { running: false, waiting: [] }
1441
+ concurrencyGroups: /* @__PURE__ */ new Map()
1414
1442
  };
1415
1443
  }
1444
+ function concurrencyGroupFor(cap, limits) {
1445
+ const declared = cap.kind === "action" ? cap.concurrency : cap.concurrency;
1446
+ const fallbackDepth = limits.actionQueueDepth;
1447
+ if (declared === void 0) {
1448
+ return cap.kind === "action" ? { key: "instance", max: 1, depth: fallbackDepth } : { key: `proc:${cap.capabilityId}`, max: 1, depth: fallbackDepth };
1449
+ }
1450
+ const depth = declared.queueDepth ?? fallbackDepth;
1451
+ switch (declared.mode) {
1452
+ case "instance":
1453
+ return { key: "instance", max: 1, depth };
1454
+ case "capability":
1455
+ return { key: `cap:${cap.capabilityId}`, max: 1, depth };
1456
+ case "key":
1457
+ return { key: `key:${declared.key}`, max: 1, depth };
1458
+ case "parallel":
1459
+ return { key: `par:${cap.capabilityId}`, max: declared.max, depth };
1460
+ }
1461
+ }
1416
1462
  function liveAvailabilityHooks(reg, cap) {
1417
1463
  if (cap.kind === "observation") {
1418
1464
  const live = reg.definition.observations?.[cap.name];
@@ -2000,7 +2046,7 @@ async function executeAction(internals, args, cap) {
2000
2046
  }
2001
2047
  }
2002
2048
  const queueStart = internals.now();
2003
- const slot = await acquireActionSlot(internals, reg);
2049
+ const slot = await acquireActionSlot(internals, reg, cap);
2004
2050
  args.setTimings({ queueWaitMs: internals.now() - queueStart });
2005
2051
  if (slot === "overflow") {
2006
2052
  return finalize({ status: "error", error: queueFull(250) });
@@ -2034,7 +2080,7 @@ async function executeAction(internals, args, cap) {
2034
2080
  args.setAuditPayload(void 0, output.value);
2035
2081
  return finalize({ status: "ok", output: output.value });
2036
2082
  } finally {
2037
- releaseActionSlot(reg);
2083
+ releaseActionSlot(internals, reg, cap);
2038
2084
  }
2039
2085
  };
2040
2086
  return runInvokePolicies(args, parsedInput, run);
@@ -2102,40 +2148,50 @@ async function executeProcedure(internals, args, cap) {
2102
2148
  effect: cap.effect
2103
2149
  });
2104
2150
  if ("error" in confirmation) return finalize({ status: "error", error: confirmation.error });
2105
- const executor = internals.executor;
2106
- if (!executor) {
2107
- return finalize({ status: "error", error: executionFailed("transport") });
2151
+ const queueStart = internals.now();
2152
+ const slot = await acquireActionSlot(internals, reg, cap);
2153
+ args.setTimings({ queueWaitMs: internals.now() - queueStart });
2154
+ if (slot === "overflow") {
2155
+ return finalize({ status: "error", error: queueFull(250) });
2156
+ }
2157
+ try {
2158
+ const executor = internals.executor;
2159
+ if (!executor) {
2160
+ return finalize({ status: "error", error: executionFailed("transport") });
2161
+ }
2162
+ const timeoutMs = options?.timeoutMs ?? internals.limits.procedureTimeoutMs;
2163
+ const executeStart = internals.now();
2164
+ const outcome = await executeWithGuards(internals, reg, {
2165
+ invocationId,
2166
+ capabilityId: cap.capabilityId,
2167
+ timeoutMs,
2168
+ externalSignal: options?.signal,
2169
+ idempotent: cap.idempotent,
2170
+ run: (signal) => executor.execute({
2171
+ path: cap.path,
2172
+ input: effective,
2173
+ info: {
2174
+ invocationId,
2175
+ consumer,
2176
+ signal,
2177
+ ...confirmation.evidence ? { confirmation: confirmation.evidence } : {}
2178
+ }
2179
+ }),
2180
+ procedureErrors: true
2181
+ });
2182
+ args.setTimings({ executionMs: internals.now() - executeStart });
2183
+ if (!outcome.ok) return finalize({ status: "error", error: outcome.payload });
2184
+ const output = settleOutput(
2185
+ internals,
2186
+ outcome.value,
2187
+ cap.outputJsonSchema ? fromJsonSchema(cap.outputJsonSchema) : void 0
2188
+ );
2189
+ if ("error" in output) return finalize({ status: "error", error: output.error });
2190
+ args.setAuditPayload(void 0, output.value);
2191
+ return finalize({ status: "ok", output: output.value });
2192
+ } finally {
2193
+ releaseActionSlot(internals, reg, cap);
2108
2194
  }
2109
- const timeoutMs = options?.timeoutMs ?? internals.limits.procedureTimeoutMs;
2110
- const executeStart = internals.now();
2111
- const outcome = await executeWithGuards(internals, reg, {
2112
- invocationId,
2113
- capabilityId: cap.capabilityId,
2114
- timeoutMs,
2115
- externalSignal: options?.signal,
2116
- idempotent: cap.idempotent,
2117
- run: (signal) => executor.execute({
2118
- path: cap.path,
2119
- input: effective,
2120
- info: {
2121
- invocationId,
2122
- consumer,
2123
- signal,
2124
- ...confirmation.evidence ? { confirmation: confirmation.evidence } : {}
2125
- }
2126
- }),
2127
- procedureErrors: true
2128
- });
2129
- args.setTimings({ executionMs: internals.now() - executeStart });
2130
- if (!outcome.ok) return finalize({ status: "error", error: outcome.payload });
2131
- const output = settleOutput(
2132
- internals,
2133
- outcome.value,
2134
- cap.outputJsonSchema ? fromJsonSchema(cap.outputJsonSchema) : void 0
2135
- );
2136
- if ("error" in output) return finalize({ status: "error", error: output.error });
2137
- args.setAuditPayload(void 0, output.value);
2138
- return finalize({ status: "ok", output: output.value });
2139
2195
  };
2140
2196
  return runInvokePolicies(args, effective, run);
2141
2197
  }
@@ -2384,21 +2440,32 @@ function executeWithGuards(internals, reg, opts) {
2384
2440
  }
2385
2441
  });
2386
2442
  }
2387
- async function acquireActionSlot(internals, reg) {
2388
- if (!reg.actionQueue.running) {
2389
- reg.actionQueue.running = true;
2443
+ async function acquireActionSlot(internals, reg, cap) {
2444
+ const { key, max, depth } = concurrencyGroupFor(cap, internals.limits);
2445
+ let group = reg.concurrencyGroups.get(key);
2446
+ if (!group) {
2447
+ group = { running: 0, max, depth, waiting: [] };
2448
+ reg.concurrencyGroups.set(key, group);
2449
+ }
2450
+ if (group.running < group.max) {
2451
+ group.running += 1;
2390
2452
  return "ok";
2391
2453
  }
2392
- if (reg.actionQueue.waiting.length >= internals.limits.actionQueueDepth) {
2454
+ if (group.waiting.length >= group.depth) {
2455
+ if (group.running === 0 && group.waiting.length === 0) reg.concurrencyGroups.delete(key);
2393
2456
  return "overflow";
2394
2457
  }
2395
- await new Promise((resolve) => reg.actionQueue.waiting.push(resolve));
2458
+ await new Promise((resolve) => group.waiting.push(resolve));
2396
2459
  return "ok";
2397
2460
  }
2398
- function releaseActionSlot(reg) {
2399
- const next = reg.actionQueue.waiting.shift();
2400
- if (next) next();
2401
- else reg.actionQueue.running = false;
2461
+ function releaseActionSlot(internals, reg, cap) {
2462
+ const { key } = concurrencyGroupFor(cap, internals.limits);
2463
+ const group = reg.concurrencyGroups.get(key);
2464
+ if (!group) return;
2465
+ const next = group.waiting.shift();
2466
+ if (!next) group.running -= 1;
2467
+ else next();
2468
+ if (group.running === 0 && group.waiting.length === 0) reg.concurrencyGroups.delete(key);
2402
2469
  }
2403
2470
  function acquireObservationSlot(internals, consumerKey) {
2404
2471
  const adm = internals.observationAdmission;
@@ -2943,6 +3010,11 @@ function createAgentToolset(registry, options) {
2943
3010
  "createAgentToolset: declare a topology ('embedded' | 'remote') or an explicit confirmations mode ('wait' | 'two-phase'). Embedded loops default to 'wait', remote loops to 'two-phase' (docs/09 \xA7confirmation-topology)."
2944
3011
  );
2945
3012
  }
3013
+ if (options.budget !== void 0 && mode !== "meta") {
3014
+ throw new Error(
3015
+ "createAgentToolset: `budget` applies to mode 'meta' only \u2014 in 'direct' mode it would silently drop tools. Pass a `scope` to bound a direct catalog instead (docs/09 \xA7meta-tools-mode)."
3016
+ );
3017
+ }
2946
3018
  const confirmationsMode = options.confirmations ?? (options.topology === "remote" ? "two-phase" : "wait");
2947
3019
  const listeners = /* @__PURE__ */ new Set();
2948
3020
  const pendingWaits = /* @__PURE__ */ new Set();
@@ -2960,15 +3032,16 @@ function createAgentToolset(registry, options) {
2960
3032
  pendingWaits.delete(controller);
2961
3033
  }
2962
3034
  }
2963
- async function invokeThroughSurface(entry, input, toolCallId) {
2964
- const invocationId = toolCallId ?? `inv_${randomBase62(12)}`;
3035
+ async function invokeThroughSurface(entry, input, toolCallId, overrides) {
3036
+ const invocationId = overrides?.invocationId ?? toolCallId ?? `inv_${randomBase62(12)}`;
2965
3037
  const base = {
2966
3038
  invocationId,
2967
3039
  capabilityId: entry.capabilityId,
2968
3040
  ...entry.instanceId !== void 0 ? { instanceId: entry.instanceId } : {},
2969
- registrationId: entry.registrationId,
3041
+ ...entry.registrationId !== void 0 ? { registrationId: entry.registrationId } : {},
2970
3042
  surfaceVersion: entry.surfaceVersion,
2971
- ...input !== void 0 ? { input } : {}
3043
+ ...input !== void 0 ? { input } : {},
3044
+ ...overrides?.confirmationId !== void 0 ? { confirmationId: overrides.confirmationId } : {}
2972
3045
  };
2973
3046
  let result = await registry.invoke(base, { consumer: options.consumer });
2974
3047
  if (confirmationsMode === "wait" && result.status === "error" && result.error.code === "CONFIRMATION_REQUIRED") {
@@ -3065,16 +3138,19 @@ function createAgentToolset(registry, options) {
3065
3138
  additionalProperties: false
3066
3139
  },
3067
3140
  async execute(input) {
3068
- const scope = input?.scope;
3141
+ const requested = input?.scope;
3142
+ const effective = intersectScope(options.scope, requested);
3069
3143
  const snapshot = registry.snapshot({
3070
3144
  consumer: options.consumer,
3071
- ...scope ? { scope } : options.scope ? { scope: options.scope } : {}
3145
+ ...effective.scope ? { scope: effective.scope } : {},
3146
+ ...options.budget ? { budget: options.budget } : {}
3072
3147
  });
3148
+ const projected = effective.empty ? { ...snapshot, components: [], procedures: [] } : snapshot;
3073
3149
  return {
3074
3150
  status: "ok",
3075
3151
  invocationId: `inv_${randomBase62(12)}`,
3076
3152
  capabilityId: "meta:surface.discover",
3077
- output: JSON.parse(JSON.stringify(snapshot)),
3153
+ output: JSON.parse(JSON.stringify(projected)),
3078
3154
  surfaceVersion: snapshot.surfaceVersion
3079
3155
  };
3080
3156
  }
@@ -3094,10 +3170,12 @@ function createAgentToolset(registry, options) {
3094
3170
  async execute(input, call) {
3095
3171
  const req = input;
3096
3172
  const snapshot = snapshotFor();
3173
+ const registrationId = findRegistrationId(snapshot, req.capabilityId, req.instanceId);
3097
3174
  return invokeThroughSurface(
3098
3175
  {
3099
3176
  capabilityId: req.capabilityId,
3100
- registrationId: findRegistrationId(snapshot, req.capabilityId, req.instanceId) ?? "",
3177
+ // Unresolved let the registry answer (AS-ADAPTER-003).
3178
+ ...registrationId !== void 0 ? { registrationId } : {},
3101
3179
  ...req.instanceId !== void 0 ? { instanceId: req.instanceId } : {},
3102
3180
  surfaceVersion: snapshot.surfaceVersion,
3103
3181
  kind: "observation"
@@ -3125,36 +3203,22 @@ function createAgentToolset(registry, options) {
3125
3203
  async execute(input, call) {
3126
3204
  const req = input;
3127
3205
  const snapshot = snapshotFor();
3128
- const invocationId = req.invocationId ?? call.toolCallId ?? `inv_${randomBase62(12)}`;
3129
- let result = await registry.invoke(
3206
+ const registrationId = findRegistrationId(snapshot, req.capabilityId, req.instanceId);
3207
+ return invokeThroughSurface(
3130
3208
  {
3131
- invocationId,
3132
3209
  capabilityId: req.capabilityId,
3210
+ ...registrationId !== void 0 ? { registrationId } : {},
3133
3211
  ...req.instanceId !== void 0 ? { instanceId: req.instanceId } : {},
3134
- ...findRegistrationId(snapshot, req.capabilityId, req.instanceId) ? { registrationId: findRegistrationId(snapshot, req.capabilityId, req.instanceId) } : {},
3135
3212
  surfaceVersion: snapshot.surfaceVersion,
3136
- ...req.input !== void 0 ? { input: req.input } : {},
3137
- ...req.confirmationId !== void 0 ? { confirmationId: req.confirmationId } : {}
3213
+ kind: "action"
3138
3214
  },
3139
- { consumer: options.consumer }
3215
+ req.input,
3216
+ call.toolCallId,
3217
+ {
3218
+ ...req.invocationId !== void 0 ? { invocationId: req.invocationId } : {},
3219
+ ...req.confirmationId !== void 0 ? { confirmationId: req.confirmationId } : {}
3220
+ }
3140
3221
  );
3141
- if (confirmationsMode === "wait" && result.status === "error" && result.error.code === "CONFIRMATION_REQUIRED" && typeof result.error.details?.confirmationId === "string") {
3142
- const confirmationId = result.error.details.confirmationId;
3143
- await waitForConfirmation(confirmationId);
3144
- if (disposed) return result;
3145
- result = await registry.invoke(
3146
- {
3147
- invocationId,
3148
- capabilityId: req.capabilityId,
3149
- ...req.instanceId !== void 0 ? { instanceId: req.instanceId } : {},
3150
- surfaceVersion: snapshot.surfaceVersion,
3151
- ...req.input !== void 0 ? { input: req.input } : {},
3152
- confirmationId
3153
- },
3154
- { consumer: options.consumer }
3155
- );
3156
- }
3157
- return result;
3158
3222
  }
3159
3223
  }
3160
3224
  ];
@@ -3178,6 +3242,7 @@ function createAgentToolset(registry, options) {
3178
3242
  const unsubscribe = registry.subscribe((event) => {
3179
3243
  if (disposed || event.type !== "surface-changed") return;
3180
3244
  cachedVersion = void 0;
3245
+ if (mode === "meta") return;
3181
3246
  const tools = computeTools();
3182
3247
  const signature = signatureOf(tools);
3183
3248
  if (signature === cachedSignature) return;
@@ -3210,6 +3275,21 @@ function createAgentToolset(registry, options) {
3210
3275
  }
3211
3276
  };
3212
3277
  }
3278
+ function intersectScope(floor, requested) {
3279
+ const hasFloor = floor !== void 0 && floor.length > 0;
3280
+ if (requested === void 0 || requested.length === 0) {
3281
+ return hasFloor ? { scope: floor, empty: false } : { empty: false };
3282
+ }
3283
+ if (!hasFloor) return { scope: requested, empty: false };
3284
+ const out = /* @__PURE__ */ new Set();
3285
+ for (const f of floor) {
3286
+ for (const r of requested) {
3287
+ if (r === f || r.startsWith(`${f}.`)) out.add(r);
3288
+ else if (f.startsWith(`${r}.`)) out.add(f);
3289
+ }
3290
+ }
3291
+ return out.size > 0 ? { scope: [...out], empty: false } : { empty: true };
3292
+ }
3213
3293
  function findRegistrationId(snapshot, capabilityId, instanceId) {
3214
3294
  const matches = [];
3215
3295
  for (const component of snapshot.components) {