@agent-surface/core 0.1.0 → 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.
- package/README.md +2 -1
- package/dist/index.d.ts +156 -8
- package/dist/index.js +308 -125
- package/dist/index.js.map +1 -1
- package/package.json +3 -1
package/dist/index.js
CHANGED
|
@@ -102,29 +102,81 @@ function parseCapabilityId(id) {
|
|
|
102
102
|
return void 0;
|
|
103
103
|
}
|
|
104
104
|
var MAX_WIRE_NAME_LENGTH = 64;
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
105
|
+
var SHORTENED_MARKER = "_0_";
|
|
106
|
+
var INSTANCE_MARKER = "_at_";
|
|
107
|
+
function hash36(input, length) {
|
|
108
|
+
let out = "";
|
|
109
|
+
for (let round = 0; out.length < length; round++) {
|
|
110
|
+
let hash = (2166136261 ^ round) >>> 0;
|
|
111
|
+
for (let i = 0; i < input.length; i++) {
|
|
112
|
+
hash ^= input.charCodeAt(i);
|
|
113
|
+
hash = Math.imul(hash, 16777619) >>> 0;
|
|
114
|
+
}
|
|
115
|
+
out += hash.toString(36).padStart(7, "0");
|
|
110
116
|
}
|
|
111
|
-
return
|
|
117
|
+
return out.slice(0, length);
|
|
118
|
+
}
|
|
119
|
+
function rawWireName(id, instanceId) {
|
|
120
|
+
const encoded = id.replace(":", "_").replaceAll(".", "__");
|
|
121
|
+
return instanceId ? `${encoded}${INSTANCE_MARKER}${instanceId}` : encoded;
|
|
112
122
|
}
|
|
113
123
|
function encodeWireName(id) {
|
|
114
124
|
return encodeWireNameForInstance(id);
|
|
115
125
|
}
|
|
116
|
-
function encodeWireNameForInstance(id, instanceId) {
|
|
117
|
-
const raw = id
|
|
118
|
-
if (raw.length <= MAX_WIRE_NAME_LENGTH) return raw;
|
|
119
|
-
|
|
126
|
+
function encodeWireNameForInstance(id, instanceId, level = 0) {
|
|
127
|
+
const raw = rawWireName(id, instanceId);
|
|
128
|
+
if (level === 0 && raw.length <= MAX_WIRE_NAME_LENGTH) return raw;
|
|
129
|
+
const hashLength = 7 + level * 2;
|
|
130
|
+
const keep = MAX_WIRE_NAME_LENGTH - SHORTENED_MARKER.length - hashLength;
|
|
131
|
+
const hash = hash36(`${id}#${instanceId ?? ""}#${level}`, hashLength);
|
|
132
|
+
return `${raw.slice(0, keep)}${SHORTENED_MARKER}${hash}`;
|
|
133
|
+
}
|
|
134
|
+
function assignWireNames(entries) {
|
|
135
|
+
const keyOf = (e) => `${e.id}#${e.instanceId ?? ""}`;
|
|
136
|
+
const level = /* @__PURE__ */ new Map();
|
|
137
|
+
const MAX_LEVEL = 3;
|
|
138
|
+
let names = entries.map((e) => encodeWireNameForInstance(e.id, e.instanceId));
|
|
139
|
+
for (let round = 0; round <= MAX_LEVEL; round++) {
|
|
140
|
+
const byName2 = /* @__PURE__ */ new Map();
|
|
141
|
+
entries.forEach((entry, i) => {
|
|
142
|
+
const set = byName2.get(names[i]) ?? /* @__PURE__ */ new Set();
|
|
143
|
+
set.add(keyOf(entry));
|
|
144
|
+
byName2.set(names[i], set);
|
|
145
|
+
});
|
|
146
|
+
const colliding = /* @__PURE__ */ new Set();
|
|
147
|
+
for (const [, keys] of byName2) {
|
|
148
|
+
if (keys.size > 1) for (const key of keys) colliding.add(key);
|
|
149
|
+
}
|
|
150
|
+
if (colliding.size === 0) break;
|
|
151
|
+
if (round === MAX_LEVEL) {
|
|
152
|
+
const ranked = [...colliding].sort();
|
|
153
|
+
names = entries.map((entry, i) => {
|
|
154
|
+
const rank = ranked.indexOf(keyOf(entry));
|
|
155
|
+
if (rank < 0) return names[i];
|
|
156
|
+
const suffix = `${SHORTENED_MARKER}${rank}`;
|
|
157
|
+
const base = encodeWireNameForInstance(entry.id, entry.instanceId, MAX_LEVEL);
|
|
158
|
+
return `${base.slice(0, MAX_WIRE_NAME_LENGTH - suffix.length)}${suffix}`;
|
|
159
|
+
});
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
for (const key of colliding) level.set(key, (level.get(key) ?? 0) + 1);
|
|
163
|
+
names = entries.map(
|
|
164
|
+
(entry) => encodeWireNameForInstance(entry.id, entry.instanceId, level.get(keyOf(entry)) ?? 0)
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
const byName = /* @__PURE__ */ new Map();
|
|
168
|
+
entries.forEach((entry, i) => byName.set(names[i], entry.id));
|
|
169
|
+
return { names, byName };
|
|
120
170
|
}
|
|
121
171
|
function decodeWireName(name) {
|
|
172
|
+
if (name.includes(SHORTENED_MARKER) || name.includes(INSTANCE_MARKER)) return void 0;
|
|
122
173
|
const planeEnd = name.indexOf("_");
|
|
123
174
|
if (planeEnd <= 0) return void 0;
|
|
124
175
|
const plane = name.slice(0, planeEnd);
|
|
125
176
|
if (plane !== "view" && plane !== "domain") return void 0;
|
|
126
|
-
const
|
|
127
|
-
return
|
|
177
|
+
const id = `${plane}:${name.slice(planeEnd + 1).replaceAll("__", ".")}`;
|
|
178
|
+
if (id.includes("_") || !parseCapabilityId(id) || encodeWireName(id) !== name) return void 0;
|
|
179
|
+
return id;
|
|
128
180
|
}
|
|
129
181
|
|
|
130
182
|
// src/utils.ts
|
|
@@ -645,7 +697,8 @@ var ACTION_KEYS = /* @__PURE__ */ new Set([
|
|
|
645
697
|
"execute",
|
|
646
698
|
"policies",
|
|
647
699
|
"meta",
|
|
648
|
-
"timeoutMs"
|
|
700
|
+
"timeoutMs",
|
|
701
|
+
"concurrency"
|
|
649
702
|
]);
|
|
650
703
|
var VIEW_EFFECTS = /* @__PURE__ */ new Set(["local-state", "navigation"]);
|
|
651
704
|
var SERVER_EFFECTS = /* @__PURE__ */ new Set([
|
|
@@ -666,6 +719,29 @@ function checkMeta(meta, where, limits) {
|
|
|
666
719
|
fail("LIMIT_EXCEEDED", `${where}: meta exceeds ${limits.maxMetaBytes} bytes`);
|
|
667
720
|
}
|
|
668
721
|
}
|
|
722
|
+
function checkConcurrency(concurrency, where) {
|
|
723
|
+
if (concurrency === void 0) return;
|
|
724
|
+
if (typeof concurrency !== "object" || concurrency === null) {
|
|
725
|
+
fail("INVALID_DEFINITION", `${where}: concurrency must be an object`);
|
|
726
|
+
}
|
|
727
|
+
const { mode } = concurrency;
|
|
728
|
+
if (!["instance", "capability", "key", "parallel"].includes(mode)) {
|
|
729
|
+
fail("INVALID_DEFINITION", `${where}: invalid concurrency mode "${String(mode)}"`);
|
|
730
|
+
}
|
|
731
|
+
if (mode === "key" && (typeof concurrency.key !== "string" || concurrency.key.length === 0)) {
|
|
732
|
+
fail("INVALID_DEFINITION", `${where}: concurrency mode "key" requires a non-empty key`);
|
|
733
|
+
}
|
|
734
|
+
if (mode === "parallel" && (typeof concurrency.max !== "number" || !Number.isInteger(concurrency.max) || concurrency.max < 1)) {
|
|
735
|
+
fail(
|
|
736
|
+
"INVALID_DEFINITION",
|
|
737
|
+
`${where}: concurrency mode "parallel" requires an integer max \u2265 1 (unbounded parallelism is not offered)`
|
|
738
|
+
);
|
|
739
|
+
}
|
|
740
|
+
const depth = concurrency.queueDepth;
|
|
741
|
+
if (depth !== void 0 && (!Number.isInteger(depth) || depth < 0)) {
|
|
742
|
+
fail("INVALID_DEFINITION", `${where}: concurrency queueDepth must be a non-negative integer`);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
669
745
|
function checkSchema(schema, where, limits) {
|
|
670
746
|
if (schema === void 0) return;
|
|
671
747
|
if (typeof schema !== "object" || schema === null || typeof schema.parse !== "function" || typeof schema.jsonSchema !== "object") {
|
|
@@ -774,6 +850,7 @@ function validateComponentDefinition(def, limits, opts) {
|
|
|
774
850
|
checkSchema(act.input, `${where} input`, limits);
|
|
775
851
|
checkSchema(act.output, `${where} output`, limits);
|
|
776
852
|
checkMeta(act.meta, where, limits);
|
|
853
|
+
checkConcurrency(act.concurrency, where);
|
|
777
854
|
}
|
|
778
855
|
const procedures = def.procedures ?? [];
|
|
779
856
|
if (procedures.length > 0 && !opts.hasProcedureExecutor) {
|
|
@@ -803,6 +880,7 @@ function validateComponentDefinition(def, limits, opts) {
|
|
|
803
880
|
fail("INVALID_DEFINITION", `procedure "${ref.path}": invalid confirmation escalation`);
|
|
804
881
|
}
|
|
805
882
|
checkMeta(binding.config.meta, `procedure "${ref.path}"`, limits);
|
|
883
|
+
checkConcurrency(binding.config.concurrency, `procedure "${ref.path}"`);
|
|
806
884
|
}
|
|
807
885
|
}
|
|
808
886
|
|
|
@@ -1354,7 +1432,8 @@ function normalizeRegistration(def, id) {
|
|
|
1354
1432
|
auditLevel: act.audit ?? "metadata",
|
|
1355
1433
|
meta: act.meta ? jsonClone(act.meta) : void 0,
|
|
1356
1434
|
timeoutMs: act.timeoutMs,
|
|
1357
|
-
policies: [...act.policies ?? []]
|
|
1435
|
+
policies: [...act.policies ?? []],
|
|
1436
|
+
concurrency: act.concurrency
|
|
1358
1437
|
});
|
|
1359
1438
|
}
|
|
1360
1439
|
const hasView = observations.size > 0 || actions.size > 0;
|
|
@@ -1386,7 +1465,8 @@ function normalizeRegistration(def, id) {
|
|
|
1386
1465
|
auditLevel: defaultAuditFor(effect),
|
|
1387
1466
|
meta: binding.config.meta ? jsonClone(binding.config.meta) : void 0,
|
|
1388
1467
|
policies: [...binding.config.policies ?? []],
|
|
1389
|
-
contextLink: binding.contextLink ?? (hasView ? { type: def.type, instanceId } : void 0)
|
|
1468
|
+
contextLink: binding.contextLink ?? (hasView ? { type: def.type, instanceId } : void 0),
|
|
1469
|
+
concurrency: binding.config.concurrency
|
|
1390
1470
|
};
|
|
1391
1471
|
});
|
|
1392
1472
|
return {
|
|
@@ -1410,9 +1490,27 @@ function normalizeRegistration(def, id) {
|
|
|
1410
1490
|
enabled: def.enabled !== false,
|
|
1411
1491
|
availabilityOverrides: /* @__PURE__ */ new Map(),
|
|
1412
1492
|
inFlight: /* @__PURE__ */ new Set(),
|
|
1413
|
-
|
|
1493
|
+
concurrencyGroups: /* @__PURE__ */ new Map()
|
|
1414
1494
|
};
|
|
1415
1495
|
}
|
|
1496
|
+
function concurrencyGroupFor(cap, limits) {
|
|
1497
|
+
const declared = cap.kind === "action" ? cap.concurrency : cap.concurrency;
|
|
1498
|
+
const fallbackDepth = limits.actionQueueDepth;
|
|
1499
|
+
if (declared === void 0) {
|
|
1500
|
+
return cap.kind === "action" ? { key: "instance", max: 1, depth: fallbackDepth } : { key: `proc:${cap.capabilityId}`, max: 1, depth: fallbackDepth };
|
|
1501
|
+
}
|
|
1502
|
+
const depth = declared.queueDepth ?? fallbackDepth;
|
|
1503
|
+
switch (declared.mode) {
|
|
1504
|
+
case "instance":
|
|
1505
|
+
return { key: "instance", max: 1, depth };
|
|
1506
|
+
case "capability":
|
|
1507
|
+
return { key: `cap:${cap.capabilityId}`, max: 1, depth };
|
|
1508
|
+
case "key":
|
|
1509
|
+
return { key: `key:${declared.key}`, max: 1, depth };
|
|
1510
|
+
case "parallel":
|
|
1511
|
+
return { key: `par:${cap.capabilityId}`, max: declared.max, depth };
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1416
1514
|
function liveAvailabilityHooks(reg, cap) {
|
|
1417
1515
|
if (cap.kind === "observation") {
|
|
1418
1516
|
const live = reg.definition.observations?.[cap.name];
|
|
@@ -2000,7 +2098,7 @@ async function executeAction(internals, args, cap) {
|
|
|
2000
2098
|
}
|
|
2001
2099
|
}
|
|
2002
2100
|
const queueStart = internals.now();
|
|
2003
|
-
const slot = await acquireActionSlot(internals, reg);
|
|
2101
|
+
const slot = await acquireActionSlot(internals, reg, cap);
|
|
2004
2102
|
args.setTimings({ queueWaitMs: internals.now() - queueStart });
|
|
2005
2103
|
if (slot === "overflow") {
|
|
2006
2104
|
return finalize({ status: "error", error: queueFull(250) });
|
|
@@ -2034,7 +2132,7 @@ async function executeAction(internals, args, cap) {
|
|
|
2034
2132
|
args.setAuditPayload(void 0, output.value);
|
|
2035
2133
|
return finalize({ status: "ok", output: output.value });
|
|
2036
2134
|
} finally {
|
|
2037
|
-
releaseActionSlot(reg);
|
|
2135
|
+
releaseActionSlot(internals, reg, cap);
|
|
2038
2136
|
}
|
|
2039
2137
|
};
|
|
2040
2138
|
return runInvokePolicies(args, parsedInput, run);
|
|
@@ -2102,40 +2200,50 @@ async function executeProcedure(internals, args, cap) {
|
|
|
2102
2200
|
effect: cap.effect
|
|
2103
2201
|
});
|
|
2104
2202
|
if ("error" in confirmation) return finalize({ status: "error", error: confirmation.error });
|
|
2105
|
-
const
|
|
2106
|
-
|
|
2107
|
-
|
|
2203
|
+
const queueStart = internals.now();
|
|
2204
|
+
const slot = await acquireActionSlot(internals, reg, cap);
|
|
2205
|
+
args.setTimings({ queueWaitMs: internals.now() - queueStart });
|
|
2206
|
+
if (slot === "overflow") {
|
|
2207
|
+
return finalize({ status: "error", error: queueFull(250) });
|
|
2208
|
+
}
|
|
2209
|
+
try {
|
|
2210
|
+
const executor = internals.executor;
|
|
2211
|
+
if (!executor) {
|
|
2212
|
+
return finalize({ status: "error", error: executionFailed("transport") });
|
|
2213
|
+
}
|
|
2214
|
+
const timeoutMs = options?.timeoutMs ?? internals.limits.procedureTimeoutMs;
|
|
2215
|
+
const executeStart = internals.now();
|
|
2216
|
+
const outcome = await executeWithGuards(internals, reg, {
|
|
2217
|
+
invocationId,
|
|
2218
|
+
capabilityId: cap.capabilityId,
|
|
2219
|
+
timeoutMs,
|
|
2220
|
+
externalSignal: options?.signal,
|
|
2221
|
+
idempotent: cap.idempotent,
|
|
2222
|
+
run: (signal) => executor.execute({
|
|
2223
|
+
path: cap.path,
|
|
2224
|
+
input: effective,
|
|
2225
|
+
info: {
|
|
2226
|
+
invocationId,
|
|
2227
|
+
consumer,
|
|
2228
|
+
signal,
|
|
2229
|
+
...confirmation.evidence ? { confirmation: confirmation.evidence } : {}
|
|
2230
|
+
}
|
|
2231
|
+
}),
|
|
2232
|
+
procedureErrors: true
|
|
2233
|
+
});
|
|
2234
|
+
args.setTimings({ executionMs: internals.now() - executeStart });
|
|
2235
|
+
if (!outcome.ok) return finalize({ status: "error", error: outcome.payload });
|
|
2236
|
+
const output = settleOutput(
|
|
2237
|
+
internals,
|
|
2238
|
+
outcome.value,
|
|
2239
|
+
cap.outputJsonSchema ? fromJsonSchema(cap.outputJsonSchema) : void 0
|
|
2240
|
+
);
|
|
2241
|
+
if ("error" in output) return finalize({ status: "error", error: output.error });
|
|
2242
|
+
args.setAuditPayload(void 0, output.value);
|
|
2243
|
+
return finalize({ status: "ok", output: output.value });
|
|
2244
|
+
} finally {
|
|
2245
|
+
releaseActionSlot(internals, reg, cap);
|
|
2108
2246
|
}
|
|
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
2247
|
};
|
|
2140
2248
|
return runInvokePolicies(args, effective, run);
|
|
2141
2249
|
}
|
|
@@ -2384,21 +2492,32 @@ function executeWithGuards(internals, reg, opts) {
|
|
|
2384
2492
|
}
|
|
2385
2493
|
});
|
|
2386
2494
|
}
|
|
2387
|
-
async function acquireActionSlot(internals, reg) {
|
|
2388
|
-
|
|
2389
|
-
|
|
2495
|
+
async function acquireActionSlot(internals, reg, cap) {
|
|
2496
|
+
const { key, max, depth } = concurrencyGroupFor(cap, internals.limits);
|
|
2497
|
+
let group = reg.concurrencyGroups.get(key);
|
|
2498
|
+
if (!group) {
|
|
2499
|
+
group = { running: 0, max, depth, waiting: [] };
|
|
2500
|
+
reg.concurrencyGroups.set(key, group);
|
|
2501
|
+
}
|
|
2502
|
+
if (group.running < group.max) {
|
|
2503
|
+
group.running += 1;
|
|
2390
2504
|
return "ok";
|
|
2391
2505
|
}
|
|
2392
|
-
if (
|
|
2506
|
+
if (group.waiting.length >= group.depth) {
|
|
2507
|
+
if (group.running === 0 && group.waiting.length === 0) reg.concurrencyGroups.delete(key);
|
|
2393
2508
|
return "overflow";
|
|
2394
2509
|
}
|
|
2395
|
-
await new Promise((resolve) =>
|
|
2510
|
+
await new Promise((resolve) => group.waiting.push(resolve));
|
|
2396
2511
|
return "ok";
|
|
2397
2512
|
}
|
|
2398
|
-
function releaseActionSlot(reg) {
|
|
2399
|
-
const
|
|
2400
|
-
|
|
2401
|
-
|
|
2513
|
+
function releaseActionSlot(internals, reg, cap) {
|
|
2514
|
+
const { key } = concurrencyGroupFor(cap, internals.limits);
|
|
2515
|
+
const group = reg.concurrencyGroups.get(key);
|
|
2516
|
+
if (!group) return;
|
|
2517
|
+
const next = group.waiting.shift();
|
|
2518
|
+
if (!next) group.running -= 1;
|
|
2519
|
+
else next();
|
|
2520
|
+
if (group.running === 0 && group.waiting.length === 0) reg.concurrencyGroups.delete(key);
|
|
2402
2521
|
}
|
|
2403
2522
|
function acquireObservationSlot(internals, consumerKey) {
|
|
2404
2523
|
const adm = internals.observationAdmission;
|
|
@@ -2453,6 +2572,12 @@ function drainObservationQueues(internals) {
|
|
|
2453
2572
|
|
|
2454
2573
|
// src/snapshot.ts
|
|
2455
2574
|
var DEFAULT_CONSUMER2 = { id: "anonymous", kind: "embedded" };
|
|
2575
|
+
function stableDescriptionOf(descriptor) {
|
|
2576
|
+
const note = descriptor.contextualNote;
|
|
2577
|
+
if (!note) return descriptor.description;
|
|
2578
|
+
if (descriptor.description === note) return "";
|
|
2579
|
+
return descriptor.description.endsWith(` ${note}`) ? descriptor.description.slice(0, -(note.length + 1)) : descriptor.description;
|
|
2580
|
+
}
|
|
2456
2581
|
function matchesScope(type, scope) {
|
|
2457
2582
|
if (!scope || scope.length === 0) return true;
|
|
2458
2583
|
return scope.some((prefix) => type === prefix || type.startsWith(`${prefix}.`));
|
|
@@ -2558,18 +2683,20 @@ function createSnapshot(internals, ctx) {
|
|
|
2558
2683
|
const available = availability.available && decision.decision === "expose";
|
|
2559
2684
|
const reason = decision.decision === "disable" ? decision.reason : availability.reason;
|
|
2560
2685
|
if (!available && !includeUnavailable) continue;
|
|
2561
|
-
let
|
|
2686
|
+
let contextualNote;
|
|
2562
2687
|
const describe = proc.binding.config.describe;
|
|
2563
2688
|
if (describe) {
|
|
2564
2689
|
try {
|
|
2565
2690
|
const contextual = describe();
|
|
2566
|
-
if (contextual)
|
|
2691
|
+
if (contextual) contextualNote = contextual;
|
|
2567
2692
|
} catch {
|
|
2568
2693
|
}
|
|
2569
2694
|
}
|
|
2695
|
+
const description = contextualNote && internals.mergesContextualNote ? `${proc.baseDescription} ${contextualNote}`.trim() : proc.baseDescription;
|
|
2570
2696
|
procedures.push({
|
|
2571
2697
|
procedureId: proc.capabilityId,
|
|
2572
2698
|
description,
|
|
2699
|
+
...contextualNote !== void 0 ? { contextualNote } : {},
|
|
2573
2700
|
inputSchema: proc.reducedInputSchema,
|
|
2574
2701
|
...proc.outputJsonSchema ? { outputSchema: proc.outputJsonSchema } : {},
|
|
2575
2702
|
effect: proc.effect,
|
|
@@ -2636,6 +2763,7 @@ function createAgentSurfaceRegistry(options) {
|
|
|
2636
2763
|
const internals = {
|
|
2637
2764
|
environment: environment2,
|
|
2638
2765
|
limits,
|
|
2766
|
+
mergesContextualNote: options?.snapshotMergesContextualNote ?? true,
|
|
2639
2767
|
surfaceId: `srf_${randomBase62(22)}`,
|
|
2640
2768
|
version: 0,
|
|
2641
2769
|
registrations: /* @__PURE__ */ new Map(),
|
|
@@ -2927,14 +3055,22 @@ var EMPTY_INPUT_SCHEMA = {
|
|
|
2927
3055
|
properties: {},
|
|
2928
3056
|
additionalProperties: false
|
|
2929
3057
|
};
|
|
2930
|
-
function describePrefix(plane, effect, confirmation
|
|
3058
|
+
function describePrefix(plane, effect, confirmation) {
|
|
2931
3059
|
const parts = [plane, effect];
|
|
2932
3060
|
if (confirmation === "required") parts.push("requires confirmation");
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
}
|
|
2937
|
-
|
|
3061
|
+
return `[${parts.join(" \xB7 ")}]`;
|
|
3062
|
+
}
|
|
3063
|
+
function legacyDescription(prefix, description, state) {
|
|
3064
|
+
const unavailable = state.available ? "" : ` [currently unavailable${state.unavailableReason ? `: ${state.unavailableReason}` : ""}]`;
|
|
3065
|
+
const note = state.note ? ` ${state.note}` : "";
|
|
3066
|
+
return `${prefix}${unavailable} ${description}${note}`;
|
|
3067
|
+
}
|
|
3068
|
+
function availabilityState(descriptor) {
|
|
3069
|
+
return {
|
|
3070
|
+
available: descriptor.available,
|
|
3071
|
+
...descriptor.unavailableReason !== void 0 ? { unavailableReason: descriptor.unavailableReason } : {},
|
|
3072
|
+
...descriptor.contextualNote !== void 0 ? { note: descriptor.contextualNote } : {}
|
|
3073
|
+
};
|
|
2938
3074
|
}
|
|
2939
3075
|
function createAgentToolset(registry, options) {
|
|
2940
3076
|
const mode = options.mode ?? "direct";
|
|
@@ -2943,12 +3079,19 @@ function createAgentToolset(registry, options) {
|
|
|
2943
3079
|
"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
3080
|
);
|
|
2945
3081
|
}
|
|
3082
|
+
if (options.budget !== void 0 && mode !== "meta") {
|
|
3083
|
+
throw new Error(
|
|
3084
|
+
"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)."
|
|
3085
|
+
);
|
|
3086
|
+
}
|
|
2946
3087
|
const confirmationsMode = options.confirmations ?? (options.topology === "remote" ? "two-phase" : "wait");
|
|
3088
|
+
const descriptionIncludesState = options.descriptionIncludesState ?? true;
|
|
2947
3089
|
const listeners = /* @__PURE__ */ new Set();
|
|
2948
3090
|
const pendingWaits = /* @__PURE__ */ new Set();
|
|
2949
3091
|
let disposed = false;
|
|
2950
3092
|
let cachedVersion;
|
|
2951
3093
|
let cachedTools;
|
|
3094
|
+
let cachedWireNames = /* @__PURE__ */ new Map();
|
|
2952
3095
|
let cachedSignature;
|
|
2953
3096
|
async function waitForConfirmation(confirmationId) {
|
|
2954
3097
|
if (disposed) return;
|
|
@@ -2960,15 +3103,16 @@ function createAgentToolset(registry, options) {
|
|
|
2960
3103
|
pendingWaits.delete(controller);
|
|
2961
3104
|
}
|
|
2962
3105
|
}
|
|
2963
|
-
async function invokeThroughSurface(entry, input, toolCallId) {
|
|
2964
|
-
const invocationId = toolCallId ?? `inv_${randomBase62(12)}`;
|
|
3106
|
+
async function invokeThroughSurface(entry, input, toolCallId, overrides) {
|
|
3107
|
+
const invocationId = overrides?.invocationId ?? toolCallId ?? `inv_${randomBase62(12)}`;
|
|
2965
3108
|
const base = {
|
|
2966
3109
|
invocationId,
|
|
2967
3110
|
capabilityId: entry.capabilityId,
|
|
2968
3111
|
...entry.instanceId !== void 0 ? { instanceId: entry.instanceId } : {},
|
|
2969
|
-
registrationId: entry.registrationId,
|
|
3112
|
+
...entry.registrationId !== void 0 ? { registrationId: entry.registrationId } : {},
|
|
2970
3113
|
surfaceVersion: entry.surfaceVersion,
|
|
2971
|
-
...input !== void 0 ? { input } : {}
|
|
3114
|
+
...input !== void 0 ? { input } : {},
|
|
3115
|
+
...overrides?.confirmationId !== void 0 ? { confirmationId: overrides.confirmationId } : {}
|
|
2972
3116
|
};
|
|
2973
3117
|
let result = await registry.invoke(base, { consumer: options.consumer });
|
|
2974
3118
|
if (confirmationsMode === "wait" && result.status === "error" && result.error.code === "CONFIRMATION_REQUIRED") {
|
|
@@ -2990,26 +3134,32 @@ function createAgentToolset(registry, options) {
|
|
|
2990
3134
|
...options.scope ? { scope: options.scope } : {},
|
|
2991
3135
|
includeUnavailable: true
|
|
2992
3136
|
});
|
|
2993
|
-
const
|
|
2994
|
-
const push = (capabilityId, kind, registrationId, instanceId, description, inputSchema, nameSuffix) => {
|
|
2995
|
-
const
|
|
2996
|
-
|
|
2997
|
-
registrationId,
|
|
2998
|
-
...instanceId !== void 0 ? { instanceId } : {},
|
|
2999
|
-
surfaceVersion: snapshot.surfaceVersion,
|
|
3000
|
-
kind
|
|
3001
|
-
};
|
|
3002
|
-
tools.push({
|
|
3137
|
+
const pending = [];
|
|
3138
|
+
const push = (capabilityId, kind, registrationId, instanceId, prefix, description, inputSchema, state, nameSuffix) => {
|
|
3139
|
+
const suffix = nameSuffix ?? instanceId;
|
|
3140
|
+
pending.push({
|
|
3003
3141
|
// Providers require unique tool names: multi-instance capabilities
|
|
3004
3142
|
// are disambiguated with an `_at_<instance>` suffix (docs/09).
|
|
3005
|
-
|
|
3143
|
+
wire: { id: capabilityId, ...suffix !== void 0 ? { instanceId: suffix } : {} },
|
|
3144
|
+
entry: {
|
|
3145
|
+
capabilityId,
|
|
3146
|
+
registrationId,
|
|
3147
|
+
...instanceId !== void 0 ? { instanceId } : {},
|
|
3148
|
+
surfaceVersion: snapshot.surfaceVersion,
|
|
3149
|
+
kind
|
|
3150
|
+
},
|
|
3151
|
+
prefix,
|
|
3006
3152
|
description,
|
|
3007
3153
|
inputSchema,
|
|
3008
|
-
|
|
3154
|
+
state
|
|
3009
3155
|
});
|
|
3010
3156
|
};
|
|
3157
|
+
const typeCounts = /* @__PURE__ */ new Map();
|
|
3158
|
+
for (const component of snapshot.components) {
|
|
3159
|
+
typeCounts.set(component.type, (typeCounts.get(component.type) ?? 0) + 1);
|
|
3160
|
+
}
|
|
3011
3161
|
for (const component of snapshot.components) {
|
|
3012
|
-
const multiInstance =
|
|
3162
|
+
const multiInstance = (typeCounts.get(component.type) ?? 0) > 1;
|
|
3013
3163
|
const instanceId = multiInstance ? component.instanceId : void 0;
|
|
3014
3164
|
for (const obs of component.observations) {
|
|
3015
3165
|
push(
|
|
@@ -3017,8 +3167,10 @@ function createAgentToolset(registry, options) {
|
|
|
3017
3167
|
"observation",
|
|
3018
3168
|
component.registrationId,
|
|
3019
3169
|
instanceId,
|
|
3020
|
-
|
|
3021
|
-
|
|
3170
|
+
describePrefix("view", "read", "never"),
|
|
3171
|
+
obs.description,
|
|
3172
|
+
EMPTY_INPUT_SCHEMA,
|
|
3173
|
+
availabilityState(obs)
|
|
3022
3174
|
);
|
|
3023
3175
|
}
|
|
3024
3176
|
for (const act of component.actions) {
|
|
@@ -3027,8 +3179,10 @@ function createAgentToolset(registry, options) {
|
|
|
3027
3179
|
"action",
|
|
3028
3180
|
component.registrationId,
|
|
3029
3181
|
instanceId,
|
|
3030
|
-
|
|
3031
|
-
act.
|
|
3182
|
+
describePrefix("view", act.effect, act.confirmation),
|
|
3183
|
+
act.description,
|
|
3184
|
+
act.inputSchema,
|
|
3185
|
+
availabilityState(act)
|
|
3032
3186
|
);
|
|
3033
3187
|
}
|
|
3034
3188
|
}
|
|
@@ -3043,19 +3197,30 @@ function createAgentToolset(registry, options) {
|
|
|
3043
3197
|
"procedure",
|
|
3044
3198
|
proc.registrationId,
|
|
3045
3199
|
void 0,
|
|
3046
|
-
|
|
3200
|
+
describePrefix("domain", proc.effect, proc.confirmation),
|
|
3201
|
+
// The stable half only: a contextual note travels in `state.note`.
|
|
3202
|
+
stableDescriptionOf(proc),
|
|
3047
3203
|
proc.inputSchema,
|
|
3204
|
+
availabilityState(proc),
|
|
3048
3205
|
needsSuffix ? proc.context?.instanceId ?? proc.registrationId.replace(/[^A-Za-z0-9_-]/g, "") : void 0
|
|
3049
3206
|
);
|
|
3050
3207
|
}
|
|
3051
|
-
|
|
3208
|
+
const assignment = assignWireNames(pending.map((p) => p.wire));
|
|
3209
|
+
const tools = pending.map((p, i) => ({
|
|
3210
|
+
name: assignment.names[i],
|
|
3211
|
+
description: descriptionIncludesState ? legacyDescription(p.prefix, p.description, p.state) : `${p.prefix} ${p.description}`,
|
|
3212
|
+
inputSchema: p.inputSchema,
|
|
3213
|
+
state: p.state,
|
|
3214
|
+
execute: (input, call) => invokeThroughSurface(p.entry, input, call.toolCallId)
|
|
3215
|
+
}));
|
|
3216
|
+
return { tools, wireNames: assignment.byName };
|
|
3052
3217
|
}
|
|
3053
3218
|
function buildMetaTools() {
|
|
3054
3219
|
const snapshotFor = () => registry.snapshot({
|
|
3055
3220
|
consumer: options.consumer,
|
|
3056
3221
|
...options.scope ? { scope: options.scope } : {}
|
|
3057
3222
|
});
|
|
3058
|
-
|
|
3223
|
+
const verbs = [
|
|
3059
3224
|
{
|
|
3060
3225
|
name: "surface_discover",
|
|
3061
3226
|
description: "[meta] Discover the current agent surface: components, capabilities, procedures, availability, schemas.",
|
|
@@ -3065,16 +3230,19 @@ function createAgentToolset(registry, options) {
|
|
|
3065
3230
|
additionalProperties: false
|
|
3066
3231
|
},
|
|
3067
3232
|
async execute(input) {
|
|
3068
|
-
const
|
|
3233
|
+
const requested = input?.scope;
|
|
3234
|
+
const effective = intersectScope(options.scope, requested);
|
|
3069
3235
|
const snapshot = registry.snapshot({
|
|
3070
3236
|
consumer: options.consumer,
|
|
3071
|
-
...
|
|
3237
|
+
...effective.scope ? { scope: effective.scope } : {},
|
|
3238
|
+
...options.budget ? { budget: options.budget } : {}
|
|
3072
3239
|
});
|
|
3240
|
+
const projected = effective.empty ? { ...snapshot, components: [], procedures: [] } : snapshot;
|
|
3073
3241
|
return {
|
|
3074
3242
|
status: "ok",
|
|
3075
3243
|
invocationId: `inv_${randomBase62(12)}`,
|
|
3076
3244
|
capabilityId: "meta:surface.discover",
|
|
3077
|
-
output: JSON.parse(JSON.stringify(
|
|
3245
|
+
output: JSON.parse(JSON.stringify(projected)),
|
|
3078
3246
|
surfaceVersion: snapshot.surfaceVersion
|
|
3079
3247
|
};
|
|
3080
3248
|
}
|
|
@@ -3094,10 +3262,12 @@ function createAgentToolset(registry, options) {
|
|
|
3094
3262
|
async execute(input, call) {
|
|
3095
3263
|
const req = input;
|
|
3096
3264
|
const snapshot = snapshotFor();
|
|
3265
|
+
const registrationId = findRegistrationId(snapshot, req.capabilityId, req.instanceId);
|
|
3097
3266
|
return invokeThroughSurface(
|
|
3098
3267
|
{
|
|
3099
3268
|
capabilityId: req.capabilityId,
|
|
3100
|
-
|
|
3269
|
+
// Unresolved → let the registry answer (AS-ADAPTER-003).
|
|
3270
|
+
...registrationId !== void 0 ? { registrationId } : {},
|
|
3101
3271
|
...req.instanceId !== void 0 ? { instanceId: req.instanceId } : {},
|
|
3102
3272
|
surfaceVersion: snapshot.surfaceVersion,
|
|
3103
3273
|
kind: "observation"
|
|
@@ -3109,7 +3279,7 @@ function createAgentToolset(registry, options) {
|
|
|
3109
3279
|
},
|
|
3110
3280
|
{
|
|
3111
3281
|
name: "surface_act",
|
|
3112
|
-
description: "[meta] Invoke an action or procedure by capabilityId.",
|
|
3282
|
+
description: "[meta] Invoke an action or procedure by capabilityId. Echo the surfaceVersion you discovered so a surface that changed underneath a destructive plan is rejected rather than executed.",
|
|
3113
3283
|
inputSchema: {
|
|
3114
3284
|
type: "object",
|
|
3115
3285
|
properties: {
|
|
@@ -3117,7 +3287,8 @@ function createAgentToolset(registry, options) {
|
|
|
3117
3287
|
instanceId: { type: "string" },
|
|
3118
3288
|
input: {},
|
|
3119
3289
|
invocationId: { type: "string" },
|
|
3120
|
-
confirmationId: { type: "string" }
|
|
3290
|
+
confirmationId: { type: "string" },
|
|
3291
|
+
surfaceVersion: { type: "string" }
|
|
3121
3292
|
},
|
|
3122
3293
|
required: ["capabilityId"],
|
|
3123
3294
|
additionalProperties: false
|
|
@@ -3125,39 +3296,26 @@ function createAgentToolset(registry, options) {
|
|
|
3125
3296
|
async execute(input, call) {
|
|
3126
3297
|
const req = input;
|
|
3127
3298
|
const snapshot = snapshotFor();
|
|
3128
|
-
const
|
|
3129
|
-
|
|
3299
|
+
const registrationId = findRegistrationId(snapshot, req.capabilityId, req.instanceId);
|
|
3300
|
+
return invokeThroughSurface(
|
|
3130
3301
|
{
|
|
3131
|
-
invocationId,
|
|
3132
3302
|
capabilityId: req.capabilityId,
|
|
3303
|
+
...registrationId !== void 0 ? { registrationId } : {},
|
|
3133
3304
|
...req.instanceId !== void 0 ? { instanceId: req.instanceId } : {},
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
...req.input !== void 0 ? { input: req.input } : {},
|
|
3137
|
-
...req.confirmationId !== void 0 ? { confirmationId: req.confirmationId } : {}
|
|
3305
|
+
surfaceVersion: req.surfaceVersion ?? snapshot.surfaceVersion,
|
|
3306
|
+
kind: "action"
|
|
3138
3307
|
},
|
|
3139
|
-
|
|
3308
|
+
req.input,
|
|
3309
|
+
call.toolCallId,
|
|
3310
|
+
{
|
|
3311
|
+
...req.invocationId !== void 0 ? { invocationId: req.invocationId } : {},
|
|
3312
|
+
...req.confirmationId !== void 0 ? { confirmationId: req.confirmationId } : {}
|
|
3313
|
+
}
|
|
3140
3314
|
);
|
|
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
3315
|
}
|
|
3159
3316
|
}
|
|
3160
3317
|
];
|
|
3318
|
+
return verbs.map((verb) => ({ ...verb, state: { available: true } }));
|
|
3161
3319
|
}
|
|
3162
3320
|
function computeTools() {
|
|
3163
3321
|
if (mode === "meta") {
|
|
@@ -3166,18 +3324,21 @@ function createAgentToolset(registry, options) {
|
|
|
3166
3324
|
}
|
|
3167
3325
|
const version = registry.getVersion();
|
|
3168
3326
|
if (cachedTools && cachedVersion === version) return cachedTools;
|
|
3169
|
-
|
|
3327
|
+
const built = buildDirectTools();
|
|
3328
|
+
cachedTools = built.tools;
|
|
3329
|
+
cachedWireNames = built.wireNames;
|
|
3170
3330
|
cachedVersion = version;
|
|
3171
3331
|
return cachedTools;
|
|
3172
3332
|
}
|
|
3173
3333
|
function signatureOf(tools) {
|
|
3174
3334
|
return JSON.stringify(
|
|
3175
|
-
tools.map((t) => [t.name, t.description, t.inputSchema])
|
|
3335
|
+
tools.map((t) => [t.name, t.description, t.inputSchema, t.state])
|
|
3176
3336
|
);
|
|
3177
3337
|
}
|
|
3178
3338
|
const unsubscribe = registry.subscribe((event) => {
|
|
3179
3339
|
if (disposed || event.type !== "surface-changed") return;
|
|
3180
3340
|
cachedVersion = void 0;
|
|
3341
|
+
if (mode === "meta") return;
|
|
3181
3342
|
const tools = computeTools();
|
|
3182
3343
|
const signature = signatureOf(tools);
|
|
3183
3344
|
if (signature === cachedSignature) return;
|
|
@@ -3195,6 +3356,11 @@ function createAgentToolset(registry, options) {
|
|
|
3195
3356
|
cachedSignature ??= signatureOf(tools);
|
|
3196
3357
|
return tools;
|
|
3197
3358
|
},
|
|
3359
|
+
wireNameMap() {
|
|
3360
|
+
if (mode === "meta") return /* @__PURE__ */ new Map();
|
|
3361
|
+
computeTools();
|
|
3362
|
+
return cachedWireNames;
|
|
3363
|
+
},
|
|
3198
3364
|
subscribe(listener) {
|
|
3199
3365
|
listeners.add(listener);
|
|
3200
3366
|
return () => {
|
|
@@ -3210,6 +3376,21 @@ function createAgentToolset(registry, options) {
|
|
|
3210
3376
|
}
|
|
3211
3377
|
};
|
|
3212
3378
|
}
|
|
3379
|
+
function intersectScope(floor, requested) {
|
|
3380
|
+
const hasFloor = floor !== void 0 && floor.length > 0;
|
|
3381
|
+
if (requested === void 0 || requested.length === 0) {
|
|
3382
|
+
return hasFloor ? { scope: floor, empty: false } : { empty: false };
|
|
3383
|
+
}
|
|
3384
|
+
if (!hasFloor) return { scope: requested, empty: false };
|
|
3385
|
+
const out = /* @__PURE__ */ new Set();
|
|
3386
|
+
for (const f of floor) {
|
|
3387
|
+
for (const r of requested) {
|
|
3388
|
+
if (r === f || r.startsWith(`${f}.`)) out.add(r);
|
|
3389
|
+
else if (f.startsWith(`${r}.`)) out.add(f);
|
|
3390
|
+
}
|
|
3391
|
+
}
|
|
3392
|
+
return out.size > 0 ? { scope: [...out], empty: false } : { empty: true };
|
|
3393
|
+
}
|
|
3213
3394
|
function findRegistrationId(snapshot, capabilityId, instanceId) {
|
|
3214
3395
|
const matches = [];
|
|
3215
3396
|
for (const component of snapshot.components) {
|
|
@@ -3235,6 +3416,7 @@ export {
|
|
|
3235
3416
|
MAX_ID_LENGTH,
|
|
3236
3417
|
MAX_WIRE_NAME_LENGTH,
|
|
3237
3418
|
action,
|
|
3419
|
+
assignWireNames,
|
|
3238
3420
|
audit,
|
|
3239
3421
|
authenticated,
|
|
3240
3422
|
composeInvokeChain,
|
|
@@ -3263,6 +3445,7 @@ export {
|
|
|
3263
3445
|
parseCapabilityId,
|
|
3264
3446
|
rateLimit,
|
|
3265
3447
|
requireConfirmation,
|
|
3448
|
+
stableDescriptionOf,
|
|
3266
3449
|
tenantBoundary,
|
|
3267
3450
|
validateComponentDefinition,
|
|
3268
3451
|
validateJsonSchemaDocument,
|